|
|
import os |
|
|
import base64 |
|
|
from groq import Groq |
|
|
import gradio as gr |
|
|
|
|
|
|
|
|
def get_response(message, history): |
|
|
client = Groq(api_key=os.environ.get("GROQ_API_KEY")) |
|
|
|
|
|
|
|
|
messages = [ |
|
|
{ |
|
|
"role": "system", |
|
|
"content": ( |
|
|
"You are a UX Ops assistant. Help the design team improve processes. You can:\n" |
|
|
"- Collect feedback by topic\n" |
|
|
"- Summarize research transcripts\n" |
|
|
"- Assist with dev handoff checklists\n" |
|
|
"- Ask follow-up questions when needed" |
|
|
) |
|
|
} |
|
|
] |
|
|
|
|
|
|
|
|
clean_history = [ |
|
|
{"role": turn["role"], "content": turn["content"]} |
|
|
for turn in history |
|
|
if isinstance(turn, dict) and "role" in turn and "content" in turn |
|
|
] |
|
|
messages.extend(clean_history) |
|
|
|
|
|
|
|
|
messages.append({"role": "user", "content": message}) |
|
|
|
|
|
|
|
|
chat_completion = client.chat.completions.create( |
|
|
messages=messages, |
|
|
model="meta-llama/llama-4-scout-17b-16e-instruct" |
|
|
) |
|
|
return chat_completion.choices[0].message.content |
|
|
|
|
|
|
|
|
def summarize_file(file): |
|
|
with open(file.name, "r") as f: |
|
|
content = f.read() |
|
|
return get_response(f"Summarize this user interview:\n{content}", []) |
|
|
|
|
|
def validate_handoff(checked_items): |
|
|
msg = "Here’s the developer handoff readiness:\n" |
|
|
status = [] |
|
|
status.append("✅ Components used from design system" if "components" in checked_items else "⚠️ Components not confirmed") |
|
|
status.append("✅ Developer notes added" if "notes" in checked_items else "⚠️ Notes missing") |
|
|
status.append("✅ Dev links and specs included" if "links" in checked_items else "⚠️ Links to specs missing") |
|
|
return "\n".join([msg] + status) |
|
|
|
|
|
def structure_feedback(layout, copy, interaction): |
|
|
return get_response( |
|
|
f"Collect feedback:\n- Layout: {layout}\n- Copy: {copy}\n- Interaction: {interaction}", |
|
|
[] |
|
|
) |
|
|
|
|
|
|
|
|
def provide_design_feedback(image_path): |
|
|
|
|
|
with open(image_path, "rb") as img_f: |
|
|
encoded = base64.b64encode(img_f.read()).decode("utf-8") |
|
|
ext = os.path.splitext(image_path)[1].lstrip(".").lower() |
|
|
data_url = f"data:image/{ext};base64,{encoded}" |
|
|
|
|
|
|
|
|
user_content = [ |
|
|
{"type": "text", "text": ( |
|
|
"Audit the UX of this design—provide feedback on hierarchy, color, typography, " |
|
|
"accessibility, and usability." |
|
|
)}, |
|
|
{"type": "image_url", "image_url": {"url": data_url}} |
|
|
] |
|
|
|
|
|
|
|
|
client = Groq(api_key=os.environ.get("GROQ_API_KEY")) |
|
|
messages = [ |
|
|
{"role": "system", "content": "You are a UX Ops assistant. Give actionable, concise design feedback."}, |
|
|
{"role": "user", "content": user_content} |
|
|
] |
|
|
|
|
|
|
|
|
chat_completion = client.chat.completions.create( |
|
|
model="meta-llama/llama-4-maverick-17b-128e-instruct", |
|
|
messages=messages, |
|
|
temperature=1.0, |
|
|
max_completion_tokens=1024, |
|
|
top_p=1.0 |
|
|
) |
|
|
return chat_completion.choices[0].message.content |
|
|
|
|
|
|
|
|
with gr.Blocks(title="UX Ops Assistant") as demo: |
|
|
gr.Markdown("## 🎯 UX Design Operations Dashboard", elem_classes="centered") |
|
|
|
|
|
|
|
|
with gr.Tab("🧠 Ask Assistant"): |
|
|
chatbot = gr.Chatbot(type="messages", height=400) |
|
|
user_input = gr.Textbox(placeholder="Ask about UX process, feedback, etc.") |
|
|
|
|
|
def submit_chat(msg, history): |
|
|
reply = get_response(msg, history) |
|
|
new_history = history + [ |
|
|
{"role": "user", "content": msg}, |
|
|
{"role": "assistant", "content": reply} |
|
|
] |
|
|
return "", new_history |
|
|
|
|
|
user_input.submit( |
|
|
fn=submit_chat, |
|
|
inputs=[user_input, chatbot], |
|
|
outputs=[user_input, chatbot] |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Tab("📂 Upload Research"): |
|
|
file_input = gr.File(label="Upload transcript (.txt)") |
|
|
summary_output = gr.Textbox(label="Summary", lines=6) |
|
|
file_input.change(fn=summarize_file, inputs=file_input, outputs=summary_output) |
|
|
|
|
|
|
|
|
with gr.Tab("✅ Dev Handoff Checklist"): |
|
|
checklist = gr.CheckboxGroup( |
|
|
choices=["components", "notes", "links"], |
|
|
label="What's included in this design?" |
|
|
) |
|
|
validate_btn = gr.Button("Validate Handoff Readiness") |
|
|
result = gr.Textbox(label="Result", lines=4) |
|
|
validate_btn.click(fn=validate_handoff, inputs=checklist, outputs=result) |
|
|
|
|
|
|
|
|
with gr.Tab("✏️ Collect Design Feedback"): |
|
|
layout_fb = gr.Textbox(label="Layout") |
|
|
copy_fb = gr.Textbox(label="Copy/Wording") |
|
|
interaction_fb = gr.Textbox(label="Interactions") |
|
|
fb_btn = gr.Button("Organize Feedback") |
|
|
fb_output = gr.Textbox(label="Organized Feedback", lines=6) |
|
|
fb_btn.click( |
|
|
fn=structure_feedback, |
|
|
inputs=[layout_fb, copy_fb, interaction_fb], |
|
|
outputs=fb_output |
|
|
) |
|
|
|
|
|
|
|
|
with gr.Tab("🖼️ Upload Design Feedback"): |
|
|
design_image = gr.Image(type="filepath", label="Upload Design (JPG, PNG)") |
|
|
image_preview = gr.Image(interactive=False, label="Preview") |
|
|
feedback_btn2 = gr.Button("Get Design Feedback") |
|
|
design_feedback_output = gr.Textbox(label="Design Feedback", lines=10) |
|
|
|
|
|
def display_and_feedback(image_path): |
|
|
preview = image_path |
|
|
feedback = provide_design_feedback(image_path) |
|
|
return preview, feedback |
|
|
|
|
|
feedback_btn2.click( |
|
|
fn=display_and_feedback, |
|
|
inputs=design_image, |
|
|
outputs=[image_preview, design_feedback_output] |
|
|
) |
|
|
|
|
|
demo.launch() |
|
|
|