Rahul-8799 commited on
Commit
b60ae2d
Β·
verified Β·
1 Parent(s): 6678303

Update utils/langgraph_pipeline.py

Browse files
Files changed (1) hide show
  1. utils/langgraph_pipeline.py +100 -84
utils/langgraph_pipeline.py CHANGED
@@ -1,6 +1,4 @@
1
- import uuid
2
- import zipfile
3
- import re
4
  from pathlib import Path
5
  from typing import TypedDict, List, Dict, Any, Tuple
6
 
@@ -13,9 +11,12 @@ from agents import (
13
  project_manager_agent,
14
  software_architect_agent,
15
  software_engineer_agent,
 
16
  )
17
 
18
- # 1) Define the shapes of our state
 
 
19
  class InputState(TypedDict):
20
  messages: List[BaseMessage]
21
  chat_log: List[Dict[str, Any]]
@@ -25,34 +26,33 @@ class OutputState(TypedDict):
25
  proj_output: str
26
  arch_output: str
27
  dev_output: str
 
28
  chat_log: List[Dict[str, Any]]
29
 
30
-
31
- # 2) Helper to wrap any agent so it sees full history
 
32
  def wrap_agent(agent_run, output_key: str):
33
  def node(state: Dict[str, Any]) -> Dict[str, Any]:
34
  history = state["messages"]
35
  log = state["chat_log"]
36
  result = agent_run({"messages": history, "chat_log": log})
37
- # append the AIMessage(s) returned by the agent
38
- new_history = history + result["messages"]
39
  return {
40
- "messages": new_history,
41
- "chat_log": result["chat_log"],
42
- output_key: result[output_key],
43
  }
44
  return node
45
 
46
-
47
- # 3) Seed the chain by turning the user's prompt into a spec for PM
 
48
  def bridge_to_pm(state: Dict[str, Any]) -> Dict[str, Any]:
49
  history = state["messages"]
50
  log = state["chat_log"]
51
- # last message must be the HumanMessage
52
  if not history or not isinstance(history[-1], HumanMessage):
53
- raise ValueError("bridge_to_pm expects a HumanMessage at the end of history")
54
  prompt = history[-1].content
55
-
56
  spec_prompt = (
57
  f"# Stakeholder Prompt\n\n"
58
  f"\"{prompt}\"\n\n"
@@ -67,91 +67,107 @@ def bridge_to_pm(state: Dict[str, Any]) -> Dict[str, Any]:
67
  "chat_log": log + [{"role": "System", "content": spec_prompt}],
68
  }
69
 
70
-
71
- # 4) After architecture, ask the Software Engineer for two files
72
- def bridge_to_code(state: Dict[str, Any]) -> Dict[str, Any]:
73
- history = state["messages"]
74
- log = state["chat_log"]
75
- # Ensure the architect has spoken
76
- if not history or not isinstance(history[-1], AIMessage):
77
- raise ValueError("bridge_to_code expects an AIMessage from the architect")
78
- # Grab the original user prompt for titling
79
- user_prompt = history[0].content
80
- title = user_prompt.title()
81
-
82
- instruction = (
83
- f"Now generate two files for a static website based on the above architecture:\n\n"
84
- f"===index.html===\n"
85
- f"<!DOCTYPE html>\n"
86
- f"<html lang=\"en\">\n"
87
- f"<head>\n"
88
- f" <meta charset=\"UTF-8\">\n"
89
- f" <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n"
90
- f" <title>{title}</title>\n"
91
- f" <link rel=\"stylesheet\" href=\"styles.css\">\n"
92
- f"</head>\n"
93
- f"<body>\n"
94
- f" <!-- Your site UI goes here -->\n"
95
- f"</body>\n"
96
- f"</html>\n\n"
97
- f"===styles.css===\n"
98
- f"/* Your CSS styles go here */\n"
99
- )
100
- return {
101
- "messages": history + [AIMessage(content=instruction)],
102
- "chat_log": log + [{"role": "System", "content": instruction}],
103
- }
104
-
105
-
106
- # 5) Build and compile the LangGraph
107
  graph = StateGraph(input=InputState, output=OutputState)
108
 
109
- # Nodes
110
- graph.add_node("BridgePM", bridge_to_pm)
111
- graph.add_node("ProductManager", wrap_agent(product_manager_agent.run, "pm_output"))
112
- graph.add_node("ProjectManager", wrap_agent(project_manager_agent.run, "proj_output"))
113
- graph.add_node("SoftwareArchitect", wrap_agent(software_architect_agent.run,"arch_output"))
114
- graph.add_node("BridgeCode", bridge_to_code)
115
- graph.add_node("SoftwareEngineer", wrap_agent(software_engineer_agent.run,"dev_output"))
116
 
117
- # Flow
118
  graph.set_entry_point("BridgePM")
119
- graph.add_edge("BridgePM", "ProductManager")
120
- graph.add_edge("ProductManager", "ProjectManager")
121
- graph.add_edge("ProjectManager", "SoftwareArchitect")
122
- graph.add_edge("SoftwareArchitect", "BridgeCode")
123
- graph.add_edge("BridgeCode", "SoftwareEngineer")
124
- graph.add_edge("SoftwareEngineer", END)
125
 
126
  compiled_graph = graph.compile()
127
 
128
-
129
- # 6) Run the pipeline, parse out the two files, write & zip them
 
 
 
 
 
 
 
 
 
 
 
 
130
  def run_pipeline_and_save(prompt: str) -> Tuple[List[Dict[str, Any]], str]:
131
- # a) Run through all agents
132
  initial_state = {"messages": [HumanMessage(content=prompt)], "chat_log": []}
133
  final_state = compiled_graph.invoke(initial_state)
134
 
135
- chat_log = final_state["chat_log"]
136
- code_blob = final_state["dev_output"]
137
-
138
- # b) Extract HTML and CSS between our markers
139
- html_match = re.search(r"===index\.html===\s*(.*?)\s*===styles\.css===", code_blob, re.S)
140
- css_match = re.search(r"===styles\.css===\s*(.*)", code_blob, re.S)
141
-
142
- html_code = html_match.group(1).strip() if html_match else ""
143
- css_code = css_match.group(1).strip() if css_match else ""
144
-
145
- # c) Write files to disk
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  site_id = uuid.uuid4().hex
147
- site_dir = Path("output") / f"site_{site_id}"
 
148
  site_dir.mkdir(parents=True, exist_ok=True)
149
 
150
  (site_dir / "index.html").write_text(html_code, encoding="utf-8")
151
  (site_dir / "styles.css").write_text(css_code, encoding="utf-8")
152
 
153
- # d) Zip them up
154
- zip_path = Path("output") / f"site_{site_id}.zip"
155
  with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
156
  for f in site_dir.iterdir():
157
  zf.write(f, arcname=f.name)
 
1
+ import uuid, zipfile, re
 
 
2
  from pathlib import Path
3
  from typing import TypedDict, List, Dict, Any, Tuple
4
 
 
11
  project_manager_agent,
12
  software_architect_agent,
13
  software_engineer_agent,
14
+ quality_assurance_agent,
15
  )
16
 
17
+ # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
18
+ # 1) State definitions
19
+ # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
20
  class InputState(TypedDict):
21
  messages: List[BaseMessage]
22
  chat_log: List[Dict[str, Any]]
 
26
  proj_output: str
27
  arch_output: str
28
  dev_output: str
29
+ qa_output: str
30
  chat_log: List[Dict[str, Any]]
31
 
32
+ # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
33
+ # 2) Wrap agents so they see full history
34
+ # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
35
  def wrap_agent(agent_run, output_key: str):
36
  def node(state: Dict[str, Any]) -> Dict[str, Any]:
37
  history = state["messages"]
38
  log = state["chat_log"]
39
  result = agent_run({"messages": history, "chat_log": log})
 
 
40
  return {
41
+ "messages": history + result["messages"],
42
+ "chat_log": result["chat_log"],
43
+ output_key: result[output_key],
44
  }
45
  return node
46
 
47
+ # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
48
+ # 3) Bridge β†’ ProductManager
49
+ # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
50
  def bridge_to_pm(state: Dict[str, Any]) -> Dict[str, Any]:
51
  history = state["messages"]
52
  log = state["chat_log"]
 
53
  if not history or not isinstance(history[-1], HumanMessage):
54
+ raise ValueError("bridge_to_pm expected a HumanMessage at history end")
55
  prompt = history[-1].content
 
56
  spec_prompt = (
57
  f"# Stakeholder Prompt\n\n"
58
  f"\"{prompt}\"\n\n"
 
67
  "chat_log": log + [{"role": "System", "content": spec_prompt}],
68
  }
69
 
70
+ # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
71
+ # 4) Build & compile the LangGraph
72
+ # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  graph = StateGraph(input=InputState, output=OutputState)
74
 
75
+ graph.add_node("BridgePM", bridge_to_pm)
76
+ graph.add_node("ProductManager", wrap_agent(product_manager_agent.run, "pm_output"))
77
+ graph.add_node("ProjectManager", wrap_agent(project_manager_agent.run, "proj_output"))
78
+ graph.add_node("SoftwareArchitect",wrap_agent(software_architect_agent.run, "arch_output"))
79
+ graph.add_node("SoftwareEngineer", wrap_agent(software_engineer_agent.run, "dev_output"))
80
+ graph.add_node("QualityAssurance", wrap_agent(quality_assurance_agent.run, "qa_output"))
 
81
 
 
82
  graph.set_entry_point("BridgePM")
83
+ graph.add_edge("BridgePM", "ProductManager")
84
+ graph.add_edge("ProductManager", "ProjectManager")
85
+ graph.add_edge("ProjectManager", "SoftwareArchitect")
86
+ graph.add_edge("SoftwareArchitect","SoftwareEngineer")
87
+ graph.add_edge("SoftwareEngineer", "QualityAssurance")
88
+ graph.add_edge("QualityAssurance", END)
89
 
90
  compiled_graph = graph.compile()
91
 
92
+ # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
93
+ # 5) Parse spec into sections
94
+ # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
95
+ def parse_spec(spec: str) -> Dict[str, List[str]]:
96
+ sections: Dict[str, List[str]] = {}
97
+ for m in re.finditer(r"##\s*(.+?)\n((?:- .+\n?)+)", spec):
98
+ name = m.group(1).strip()
99
+ items = [line[2:].strip() for line in m.group(2).splitlines() if line.startswith("- ")]
100
+ sections[name] = items
101
+ return sections
102
+
103
+ # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
104
+ # 6) Run pipeline, generate site, zip, return (chat_log, zip_path)
105
+ # β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”
106
  def run_pipeline_and_save(prompt: str) -> Tuple[List[Dict[str, Any]], str]:
107
+ # a) invoke agents
108
  initial_state = {"messages": [HumanMessage(content=prompt)], "chat_log": []}
109
  final_state = compiled_graph.invoke(initial_state)
110
 
111
+ chat_log = final_state["chat_log"]
112
+ qa_output = final_state["qa_output"]
113
+
114
+ # b) parse spec
115
+ spec = parse_spec(qa_output)
116
+ features = spec.get("Key features", [])
117
+ testimonials = spec.get("User stories", [])
118
+
119
+ # c) build HTML
120
+ title = prompt.title()
121
+ domain = prompt.replace(" ", "").lower() + ".com"
122
+ cards_html = "\n".join(f"<div class='card'><h3>{f}</h3></div>" for f in features)
123
+ test_html = "\n".join(f"<blockquote>{t}</blockquote>" for t in testimonials)
124
+
125
+ html_code = f"""<!DOCTYPE html>
126
+ <html lang="en">
127
+ <head>
128
+ <meta charset="UTF-8">
129
+ <meta name="viewport" content="width=device-width,initial-scale=1">
130
+ <title>{title}</title>
131
+ <link rel="stylesheet" href="styles.css">
132
+ </head>
133
+ <body>
134
+ <header><h1>{title}</h1></header>
135
+ <section id="features">
136
+ <h2>Features</h2>
137
+ <div class="cards">
138
+ {cards_html}
139
+ </div>
140
+ </section>
141
+ <section id="testimonials">
142
+ <h2>Testimonials</h2>
143
+ {test_html or '<p>No testimonials provided.</p>'}
144
+ </section>
145
+ <section id="contact">
146
+ <h2>Contact Us</h2>
147
+ <p>Email: info@{domain}</p>
148
+ </section>
149
+ </body>
150
+ </html>"""
151
+
152
+ # d) basic CSS
153
+ css_code = """
154
+ body { font-family: Arial, sans-serif; margin: 1em; line-height: 1.5; }
155
+ header { text-align: center; margin-bottom: 2em; }
156
+ .cards { display: grid; grid-template-columns: repeat(auto-fit,minmax(150px,1fr)); gap: 1em; }
157
+ .card { background: #f9f9f9; padding: 1em; border-radius: 8px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); text-align: center; }
158
+ blockquote { font-style: italic; margin: 1em; padding: 0.5em; background: #eef; border-left: 4px solid #99f; }
159
+ """
160
+
161
+ # e) write & zip
162
  site_id = uuid.uuid4().hex
163
+ out_dir = Path("output")
164
+ site_dir = out_dir / f"site_{site_id}"
165
  site_dir.mkdir(parents=True, exist_ok=True)
166
 
167
  (site_dir / "index.html").write_text(html_code, encoding="utf-8")
168
  (site_dir / "styles.css").write_text(css_code, encoding="utf-8")
169
 
170
+ zip_path = out_dir / f"site_{site_id}.zip"
 
171
  with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
172
  for f in site_dir.iterdir():
173
  zf.write(f, arcname=f.name)