1- # 🛡️ DevGuardian Project — Core Module
2- """
3- 🤖 DevGuardian Agent Swarm
4- ===========================
5- A 3-agent LangGraph pipeline that tackles tasks as a team:
6- 1. 🖊️ Coder — Writes or fixes the code
7- 2. 🧪 Tester — Identifies test scenarios and potential breakage
8- 3. 🔍 Reviewer — Enforces project conventions and security
9-
10- The final output is the Reviewer's verdict and the Coder's finished code.
11- """
12-
131import os
14- from typing import TypedDict , Annotated , List
2+ from typing import TypedDict , Annotated , List , Literal
153from typing_extensions import Required
164
175from langchain_google_genai import ChatGoogleGenerativeAI
186from langchain_core .messages import BaseMessage , HumanMessage , SystemMessage , AIMessage
197from langgraph .graph import StateGraph , END
208
219from devguardian .utils .file_reader import build_project_context
10+ from devguardian .utils .memory import ProjectMemory
11+ from devguardian .utils .executor import verify_code_logic
2212
2313
2414# ---------------------------------------------------------------------------
2717class SwarmState (TypedDict ):
2818 task : Required [str ]
2919 project_path : Required [str ]
30- project_dna : str # enriched context (loaded once)
31- code_draft : str # Coder's output
32- test_report : str # Tester's output
33- final_verdict : str # Reviewer's output
20+ project_dna : str
21+ memory_context : str
22+ code_draft : str
23+ test_report : str
24+ reviewer_feedback : str # Used for iterative loops
25+ iteration_count : int
26+ final_verdict : str
3427 messages : Annotated [List [BaseMessage ], "trace" ]
3528
3629
3730# ---------------------------------------------------------------------------
38- # Shared LLM factory (lazy — created once per swarm run)
31+ # LLM & Memory Helpers
3932# ---------------------------------------------------------------------------
4033def _get_llm (temperature : float = 0.2 ) -> ChatGoogleGenerativeAI :
4134 return ChatGoogleGenerativeAI (
@@ -45,171 +38,192 @@ def _get_llm(temperature: float = 0.2) -> ChatGoogleGenerativeAI:
4538 )
4639
4740
48- # ----------------------------------------------------------------及ひ-------------------------------------
49- # Node: Load Project DNA (runs once at start)
5041# ---------------------------------------------------------------------------
51- def load_dna (state : SwarmState ) -> SwarmState :
42+ # Nodes
43+ # ---------------------------------------------------------------------------
44+
45+ def load_context (state : SwarmState ) -> SwarmState :
46+ """Initialize project DNA and Semantic Memory."""
5247 dna = build_project_context (state ["project_path" ])
53- return {** state , "project_dna" : dna , "messages" : state .get ("messages" , [])}
48+ mem = ProjectMemory (state ["project_path" ])
49+
50+ return {
51+ ** state ,
52+ "project_dna" : dna ,
53+ "memory_context" : mem .get_context_string (),
54+ "iteration_count" : 1 ,
55+ "reviewer_feedback" : "" ,
56+ "messages" : state .get ("messages" , [])
57+ }
5458
5559
56- # ---------------------------------------------------------------------------
57- # Node: Coder Agent
58- # ---------------------------------------------------------------------------
5960def coder_agent (state : SwarmState ) -> SwarmState :
61+ """Writes code, considering memory and previous reviewer rejections."""
6062 llm = _get_llm (temperature = 0.3 )
63+
64+ # Pillar 1: Adversarial Loop
65+ is_fix = len (state ["reviewer_feedback" ]) > 0
66+ mode_instruction = (
67+ f"FIX the following code based on reviewer feedback:\n { state ['reviewer_feedback' ]} "
68+ if is_fix else "Write the implementation from scratch."
69+ )
6170
6271 system = SystemMessage (
6372 content = (
64- "You are an expert Python Coder Agent in the DevGuardian Swarm. "
65- "Your sole job is to write or fix code. "
66- "Given a task and the project context, produce complete, correct Python code. "
67- "Return ONLY code. No explanations, no markdown fences."
73+ "You are an expert Python Coder. "
74+ "Follow project style preferences and past lessons learned. "
75+ "Return ONLY raw Python code. No markdown fences."
6876 )
6977 )
7078 user = HumanMessage (
7179 content = (
72- f"## Project DNA\n { state ['project_dna' ][:3000 ]} \n \n "
80+ f"## Project Context\n { state ['project_dna' ][:3000 ]} \n "
81+ f"## Semantic Memory (Style & Lessons)\n { state ['memory_context' ]} \n \n "
7382 f"## Task\n { state ['task' ]} \n \n "
74- "Write the implementation now."
83+ f"## Current Effort (Iteration { state ['iteration_count' ]} )\n "
84+ f"{ mode_instruction } \n \n "
85+ f"{ '## Source Code to Fix:' + state ['code_draft' ] if is_fix else '' } "
7586 )
7687 )
7788
7889 response : AIMessage = llm .invoke ([system , user ])
7990 code = response .content .strip ()
80-
81- # Strip markdown fences if model adds them
91+
8292 if "```" in code :
83- lines = [l for l in code .splitlines () if not l .strip ().startswith ("```" )]
84- code = "\n " .join (lines ).strip ()
93+ code = "\n " .join ([l for l in code .splitlines () if not l .strip ().startswith ("```" )]).strip ()
8594
8695 return {
8796 ** state ,
8897 "code_draft" : code ,
89- "messages" : state .get ("messages" , []) + [HumanMessage (content = "[Coder produced draft code] " )],
98+ "messages" : state .get ("messages" , []) + [HumanMessage (content = f "[Coder produced draft v { state [ 'iteration_count' ] } ] " )]
9099 }
91100
92101
93- # ---------------------------------------------------------------------------
94- # Node: Tester Agent
95- # ---------------------------------------------------------------------------
96102def tester_agent (state : SwarmState ) -> SwarmState :
103+ """Audits code and RUNS it in a sandbox to catch real crashes."""
97104 llm = _get_llm (temperature = 0.4 )
105+
106+ # Pillar 3: Sandbox Execution
107+ execution_result = verify_code_logic (state ["code_draft" ])
98108
99109 system = SystemMessage (
100110 content = (
101- "You are a meticulous Tester Agent in the DevGuardian Swarm. "
102- "You audit code written by the Coder for bugs, edge cases, and missing validation. "
103- "Report your findings clearly with bullet points. "
104- "Do NOT rewrite the code — only surface issues and test scenarios."
111+ "You are a meticulous Tester. Identify bugs and edge cases. "
112+ "Consider the sandbox execution result provided below."
105113 )
106114 )
107115 user = HumanMessage (
108116 content = (
109- f"## Coder's Draft \n ```python\n { state ['code_draft' ]} \n ```\n \n "
110- f"## Original Task \n { state [ 'task' ] } \n \n "
111- "List all bugs, missing edge cases, and test scenarios you would write . "
112- "Be specific — cite exact function names or line contexts ."
117+ f"## Code to Audit \n ```python\n { state ['code_draft' ]} \n ```\n \n "
118+ f"## Sandbox Execution Output \n { execution_result } \n \n "
119+ "List bugs, missing edge cases, and logic flaws . "
120+ "If the sandbox failed, explain why based on the code ."
113121 )
114122 )
115123
116124 response : AIMessage = llm .invoke ([system , user ])
117125 return {
118126 ** state ,
119- "test_report" : response .content .strip (),
120- "messages" : state .get ("messages" , []) + [HumanMessage (content = "[Tester produced report]" )],
127+ "test_report" : f"### Sandbox Result \n { execution_result } \n \n ### Audit Notes \n { response .content .strip ()} " ,
128+ "messages" : state .get ("messages" , []) + [HumanMessage (content = "[Tester produced report]" )]
121129 }
122130
123131
124- # ---------------------------------------------------------------------------
125- # Node: Reviewer Agent (also incorporates Tester feedback into final code)
126- # ---------------------------------------------------------------------------
127132def reviewer_agent (state : SwarmState ) -> SwarmState :
128- llm = _get_llm (temperature = 0.2 )
133+ """Decides if the code is production-ready or needs another pass (Adversarial)."""
134+ llm = _get_llm (temperature = 0.1 )
129135
130136 system = SystemMessage (
131137 content = (
132- "You are a senior Code Reviewer Agent in the DevGuardian Swarm. "
133- "You receive the Coder's draft and the Tester's report. "
134- "Your job: produce the FINAL, production-ready version of the code, "
135- "incorporating all Tester feedback and ensuring it matches project conventions. "
136- "Format your response as:\n "
137- "## Verdict\n <your assessment>\n \n ## Final Code\n <complete code>\n \n ## Changes Made\n <bullet list>"
138+ "You are the Final Gatekeeper. You must either ACCEPT or REJECT the code. "
139+ "REJECT if there are logic bugs, security leaks, or it fails sandbox tests. "
140+ "If REJECTED, provide clear instructions for the Coder. "
141+ "If ACCEPTED, format response as:\n "
142+ "## Verdict\n ACCEPTED\n \n ## Final Code\n <code>\n \n ## Summary\n <notes>"
138143 )
139144 )
140145 user = HumanMessage (
141146 content = (
142- f"## Project DNA\n { state ['project_dna' ][:2000 ]} \n \n "
143147 f"## Task\n { state ['task' ]} \n \n "
144- f"## Coder's Draft\n ```python\n { state ['code_draft' ]} \n ```\n \n "
145- f"## Tester's Report \n { state ['test_report' ]} \n \n "
146- "Produce the final verdict and production-ready code. "
148+ f"## Draft Code \n ```python\n { state ['code_draft' ]} \n ```\n \n "
149+ f"## Tester Feedback \n { state ['test_report' ]} \n \n "
150+ "Decide: Is this production-ready? (Iteration: " + str ( state [ 'iteration_count' ]) + ") "
147151 )
148152 )
149153
150154 response : AIMessage = llm .invoke ([system , user ])
155+ verdict_text = response .content .strip ()
156+
157+ is_rejected = "REJECT" in verdict_text .upper () and state ["iteration_count" ] < 3
158+
151159 return {
152160 ** state ,
153- "final_verdict" : response .content .strip (),
154- "messages" : state .get ("messages" , []) + [HumanMessage (content = "[Reviewer produced final verdict]" )],
161+ "reviewer_feedback" : verdict_text if is_rejected else "" ,
162+ "final_verdict" : verdict_text ,
163+ "iteration_count" : state ["iteration_count" ] + 1 ,
164+ "messages" : state .get ("messages" , []) + [HumanMessage (content = "[Reviewer verdict]" )]
155165 }
156166
157167
158168# ---------------------------------------------------------------------------
159- # Graph builder
169+ # Router
170+ # ---------------------------------------------------------------------------
171+ def router (state : SwarmState ) -> Literal ["coder" , "end" ]:
172+ """Determines if we should loop back or finish."""
173+ if state ["reviewer_feedback" ] and state ["iteration_count" ] <= 3 :
174+ return "coder"
175+
176+ # Update Semantic Memory on success
177+ if "ACCEPTED" in state ["final_verdict" ].upper ():
178+ mem = ProjectMemory (state ["project_path" ])
179+ mem .add_lesson (state ["task" ], "Code passed sandbox and adversarial review." )
180+
181+ return "end"
182+
183+
184+ # ---------------------------------------------------------------------------
185+ # Graph Builder
160186# ---------------------------------------------------------------------------
161187def create_swarm_graph ():
162188 workflow = StateGraph (SwarmState )
163189
164- workflow .add_node ("load_dna " , load_dna )
190+ workflow .add_node ("load_context " , load_context )
165191 workflow .add_node ("coder" , coder_agent )
166192 workflow .add_node ("tester" , tester_agent )
167193 workflow .add_node ("reviewer" , reviewer_agent )
168194
169- workflow .set_entry_point ("load_dna " )
170- workflow .add_edge ("load_dna " , "coder" )
195+ workflow .set_entry_point ("load_context " )
196+ workflow .add_edge ("load_context " , "coder" )
171197 workflow .add_edge ("coder" , "tester" )
172198 workflow .add_edge ("tester" , "reviewer" )
173- workflow .add_edge ("reviewer" , END )
199+
200+ workflow .add_conditional_edges (
201+ "reviewer" ,
202+ router ,
203+ {
204+ "coder" : "coder" ,
205+ "end" : END
206+ }
207+ )
174208
175209 return workflow .compile ()
176210
177211
178- # ---------------------------------------------------------------------------
179- # Public entry point
180- # ---------------------------------------------------------------------------
181212async def run_swarm (task : str , project_path : str ) -> str :
182- """
183- Run the 3-agent swarm on a task.
184-
185- Args:
186- task: What to build or fix.
187- project_path: Absolute path to the project root for context.
188-
189- Returns:
190- The Reviewer's final verdict + production-ready code.
191- """
192213 graph = create_swarm_graph ()
193-
194- initial_state : SwarmState = {
214+ result = await graph .ainvoke ({
195215 "task" : task ,
196216 "project_path" : project_path ,
197- "project_dna" : "" ,
198- "code_draft" : "" ,
199- "test_report" : "" ,
200- "final_verdict" : "" ,
201217 "messages" : [],
202- }
203-
204- result = await graph .ainvoke (initial_state )
205- final = result ["final_verdict" ]
206- test_notes = result ["test_report" ]
218+ "iteration_count" : 0
219+ })
207220
208221 return (
209- f"# 🤖 DevGuardian Agent Swarm Report\n \n "
210- f"**Task:** { task } \n \n "
211- f"---\n \n "
212- f"## 🧪 Tester's Notes\n { test_notes } \n \n "
222+ f"# 🤖 DevGuardian v3 Swarm Report\n \n "
223+ f"**Task:** { task } \n "
224+ f"**Persistence:** Semantic Memory Updated ✅\n "
225+ f"**Validation:** Sandbox Execution Confirmed ✅\n "
226+ f"**Total Passes:** { result ['iteration_count' ] - 1 if result ['iteration_count' ] > 0 else 1 } \n \n "
213227 f"---\n \n "
214- f"{ final } "
228+ f"{ result [ 'final_verdict' ] } "
215229 )
0 commit comments