File size: 7,567 Bytes
a305086
facf100
a305086
50e0854
a305086
 
50e0854
3079b0f
50e0854
 
 
 
3079b0f
a305086
 
50e0854
a305086
50e0854
a305086
 
 
 
 
50e0854
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a7524b6
 
 
50e0854
 
 
facf100
50e0854
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a305086
 
50e0854
 
 
 
 
3079b0f
 
50e0854
 
 
 
3079b0f
 
a305086
50e0854
a305086
50e0854
a305086
 
50e0854
 
 
 
 
a7524b6
 
50e0854
 
a305086
 
50e0854
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a305086
 
a7524b6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
import streamlit as st
import os
import time
import base64
from typing import Dict, List, TypedDict
from langgraph.graph import StateGraph, END
from huggingface_hub import InferenceClient

client = InferenceClient(
    model="mistralai/Mistral-7B-Instruct-v0.2",
    token=st.secrets["HF_TOKEN"]
)

class AgentState(TypedDict):
    messages: List[Dict[str, str]]
    design_specs: str
    html: str
    css: str
    feedback: str
    iteration: int
    done: bool
    timings: Dict[str, float]

PRODUCT_MANAGER_PROMPT = """You're a product manager. Given the user request:
{user_request}
Break it down into clear features and priorities."""

PROJECT_MANAGER_PROMPT = """You're a project manager. Based on these features:
{features}
Draft a quick development plan with key tasks and timeline."""

ARCHITECT_PROMPT = """You're a software architect. Create design specs for:
{user_request}
Include:
1. Color palette (primary, secondary, accent)
2. Font choices
3. Layout structure
4. Component styles
Don't write code - just design guidance."""

ENGINEER_PROMPT = """Create a complete HTML page with embedded CSS for:
{design_specs}
Requirements:
1. Full HTML document with <!DOCTYPE>
2. CSS inside <style> tags in head
3. Mobile-responsive
4. Semantic HTML
5. Ready-to-use (will work when saved as .html)
Output JUST the complete HTML file content:"""

QA_PROMPT = """Review this website:
{html}
Check for:
1. Visual quality
2. Responsiveness
3. Functionality
Reply "APPROVED" if perfect, or suggest improvements."""

def call_model(prompt: str, max_retries=3) -> str:
    for attempt in range(max_retries):
        try:
            return client.text_generation(
                prompt,
                max_new_tokens=3000,
                temperature=0.3,
                return_full_text=False
            )
        except Exception as e:
            st.error(f"Model call failed (attempt {attempt+1}): {str(e)}")
            st.write("\n\n**Full Error:**", e)
            st.stop()  # Force exit to show error
    return "<html><body><h1>Error generating UI</h1></body></html>"

def time_agent(agent_func, state: AgentState, label: str):
    start = time.time()
    result = agent_func(state)
    duration = time.time() - start
    result["timings"] = state["timings"]
    result["timings"][label] = duration
    return result

def product_manager_agent(state: AgentState):
    features = call_model(PRODUCT_MANAGER_PROMPT.format(user_request=state["messages"][-1]["content"]))
    return {"messages": state["messages"] + [{"role": "product_manager", "content": features}]}

def project_manager_agent(state: AgentState):
    features_msg = next((m["content"] for m in state["messages"] if m["role"] == "product_manager"), "")
    plan = call_model(PROJECT_MANAGER_PROMPT.format(features=features_msg))
    return {"messages": state["messages"] + [{"role": "project_manager", "content": plan}]}

def software_architect_agent(state: AgentState):
    specs = call_model(ARCHITECT_PROMPT.format(user_request=state["messages"][-1]["content"]))
    return {"design_specs": specs, "messages": state["messages"] + [{"role": "software_architect", "content": specs}]}

def engineer_agent(state: AgentState):
    html = call_model(ENGINEER_PROMPT.format(design_specs=state["design_specs"]))
    if not html.strip().startswith("<!DOCTYPE"):
        html = f"""<!DOCTYPE html>
<html><head><meta charset='UTF-8'><meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>Generated UI</title></head><body>{html}</body></html>"""
    return {"html": html, "messages": state["messages"] + [{"role": "software_engineer", "content": html}]}

def qa_agent(state: AgentState, max_iter: int):
    feedback = call_model(QA_PROMPT.format(html=state["html"]))
    done = "APPROVED" in feedback or state["iteration"] >= max_iter
    return {"feedback": feedback, "done": done, "iteration": state["iteration"] + 1,
            "messages": state["messages"] + [{"role": "qa", "content": feedback}]}

def generate_ui(user_request: str, max_iter: int):
    state = {"messages": [{"role": "user", "content": user_request}], "design_specs": "", "html": "",
             "css": "", "feedback": "", "iteration": 0, "done": False, "timings": {}}

    workflow = StateGraph(AgentState)
    workflow.add_node("product_manager", lambda s: time_agent(product_manager_agent, s, "product_manager"))
    workflow.add_node("project_manager", lambda s: time_agent(project_manager_agent, s, "project_manager"))
    workflow.add_node("software_architect", lambda s: time_agent(software_architect_agent, s, "software_architect"))
    workflow.add_node("software_engineer", lambda s: time_agent(engineer_agent, s, "software_engineer"))
    workflow.add_node("qa", lambda s: time_agent(lambda x: qa_agent(x, max_iter), s, "qa"))

    workflow.add_edge("product_manager", "project_manager")
    workflow.add_edge("project_manager", "software_architect")
    workflow.add_edge("software_architect", "software_engineer")
    workflow.add_edge("software_engineer", "qa")
    workflow.add_conditional_edges("qa", lambda s: END if s["done"] else "software_engineer")
    workflow.set_entry_point("product_manager")

    app = workflow.compile()
    total_start = time.time()
    final_state = app.invoke(state)
    return final_state["html"], final_state, time.time() - total_start

def main():
    st.set_page_config(page_title="Multi-Agent Collaboration", layout="wide")
    st.title("🀝 Multi-Agent Collaboration")
    with st.sidebar:
        max_iter = st.slider("Max QA Iterations", 1, 5, 2)

    st.write("\nπŸ” HF_TOKEN found:", st.secrets.get("HF_TOKEN", "❌ Missing!"))

    prompt = st.text_area("πŸ“ Describe the UI you want:", "A coffee shop landing page with hero, menu, and contact form.", height=150)

    if st.button("πŸš€ Generate UI"):
        with st.spinner("Agents working..."):
            html, final_state, total_time = generate_ui(prompt, max_iter)
            st.success("βœ… UI Generated Successfully!")
            st.components.v1.html(html, height=600, scrolling=True)

            st.subheader("πŸ“… Download HTML")
            b64 = base64.b64encode(html.encode()).decode()
            st.markdown(f'<a href="data:file/html;base64,{b64}" download="ui.html">Download HTML</a>', unsafe_allow_html=True)

            st.subheader("🧐 Agent Communication Log")
            history_text = ""
            for msg in final_state["messages"]:
                role = msg["role"].replace("_", " ").title()
                content = msg["content"]
                history_text += f"---\n{role}:\n{content}\n\n"
            st.text_area("Agent Dialogue", value=history_text, height=300)

            b64_hist = base64.b64encode(history_text.encode()).decode()
            st.markdown(
                f'<a href="data:file/txt;base64,{b64_hist}" download="agent_communication.txt" '
                'style="padding: 0.4em 1em; background: #4CAF50; color: white; border-radius: 0.3em; text-decoration: none;">'
                'πŸ“… Download Communication Log</a>',
                unsafe_allow_html=True
            )

            st.subheader("πŸ“Š Performance")
            st.write(f"⏱️ Total Time: {total_time:.2f} seconds")
            st.write(f"πŸ” Iterations: {final_state['iteration']}")
            for stage in ["product_manager", "project_manager", "software_architect", "software_engineer", "qa"]:
                st.write(f"πŸ€– {stage.title().replace('_', ' ')} Time: {final_state['timings'].get(stage, 0):.2f}s")

if __name__ == "__main__":
    main()