acra-cli 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. acra/__init__.py +7 -0
  2. acra/__main__.py +4 -0
  3. acra/agents/__init__.py +0 -0
  4. acra/agents/__pycache__/__init__.cpython-312.pyc +0 -0
  5. acra/agents/__pycache__/llm.cpython-312.pyc +0 -0
  6. acra/agents/__pycache__/llm_utils.cpython-312.pyc +0 -0
  7. acra/agents/coder/__init__.py +0 -0
  8. acra/agents/coder/__pycache__/__init__.cpython-312.pyc +0 -0
  9. acra/agents/coder/__pycache__/coder_agent.cpython-312.pyc +0 -0
  10. acra/agents/coder/__pycache__/coder_chain.cpython-312.pyc +0 -0
  11. acra/agents/coder/coder_agent.py +235 -0
  12. acra/agents/coder/coder_chain.py +204 -0
  13. acra/agents/critic/__init__.py +0 -0
  14. acra/agents/critic/__pycache__/__init__.cpython-312.pyc +0 -0
  15. acra/agents/critic/__pycache__/critic_agent.cpython-312.pyc +0 -0
  16. acra/agents/critic/__pycache__/critic_chain.cpython-312.pyc +0 -0
  17. acra/agents/critic/critic_agent.py +199 -0
  18. acra/agents/critic/critic_chain.py +268 -0
  19. acra/agents/executor/__init__.py +0 -0
  20. acra/agents/executor/__pycache__/__init__.cpython-312.pyc +0 -0
  21. acra/agents/executor/__pycache__/docker_runner.cpython-312.pyc +0 -0
  22. acra/agents/executor/__pycache__/executor_agent.cpython-312.pyc +0 -0
  23. acra/agents/executor/__pycache__/sandbox_runner.cpython-312.pyc +0 -0
  24. acra/agents/executor/docker_runner.py +114 -0
  25. acra/agents/executor/executor_agent.py +142 -0
  26. acra/agents/executor/sandbox_runner.py +144 -0
  27. acra/agents/human/__init__.py +0 -0
  28. acra/agents/human/__pycache__/__init__.cpython-312.pyc +0 -0
  29. acra/agents/human/__pycache__/human_node.cpython-312.pyc +0 -0
  30. acra/agents/human/human_node.py +37 -0
  31. acra/agents/llm.py +263 -0
  32. acra/agents/llm_utils.py +161 -0
  33. acra/agents/memory/__init__.py +0 -0
  34. acra/agents/memory/__pycache__/__init__.cpython-312.pyc +0 -0
  35. acra/agents/memory/__pycache__/memory_agent.cpython-312.pyc +0 -0
  36. acra/agents/memory/__pycache__/memory_manager.cpython-312.pyc +0 -0
  37. acra/agents/memory/__pycache__/retrieval.cpython-312.pyc +0 -0
  38. acra/agents/memory/memory_agent.py +239 -0
  39. acra/agents/memory/memory_manager.py +273 -0
  40. acra/agents/memory/retrieval.py +355 -0
  41. acra/agents/planner/__init__.py +0 -0
  42. acra/agents/planner/__pycache__/__init__.cpython-312.pyc +0 -0
  43. acra/agents/planner/__pycache__/planner_agent.cpython-312.pyc +0 -0
  44. acra/agents/planner/__pycache__/planner_chain.cpython-312.pyc +0 -0
  45. acra/agents/planner/planner_agent.py +97 -0
  46. acra/agents/planner/planner_chain.py +160 -0
  47. acra/agents/researcher/__init__.py +1 -0
  48. acra/agents/researcher/__pycache__/__init__.cpython-312.pyc +0 -0
  49. acra/agents/researcher/__pycache__/researcher_agent.cpython-312.pyc +0 -0
  50. acra/agents/researcher/__pycache__/researcher_chain.cpython-312.pyc +0 -0
  51. acra/agents/researcher/researcher_agent.py +231 -0
  52. acra/agents/researcher/researcher_chain.py +193 -0
  53. acra/brain/__init__.py +6 -0
  54. acra/brain/model_registry.py +19 -0
  55. acra/brain/provider_manager.py +21 -0
  56. acra/cli.py +52 -0
  57. acra/commands/__init__.py +29 -0
  58. acra/commands/ask.py +49 -0
  59. acra/commands/brain.py +58 -0
  60. acra/commands/config.py +25 -0
  61. acra/commands/context.py +30 -0
  62. acra/commands/graph.py +18 -0
  63. acra/commands/keys.py +40 -0
  64. acra/commands/logs.py +13 -0
  65. acra/commands/memory.py +27 -0
  66. acra/commands/plugin.py +18 -0
  67. acra/commands/research.py +65 -0
  68. acra/commands/serve.py +13 -0
  69. acra/commands/session.py +20 -0
  70. acra/commands/utils.py +12 -0
  71. acra/config/__init__.py +36 -0
  72. acra/config/__pycache__/__init__.cpython-312.pyc +0 -0
  73. acra/config/__pycache__/defaults.cpython-312.pyc +0 -0
  74. acra/config/__pycache__/profile_manager.cpython-312.pyc +0 -0
  75. acra/config/defaults.py +88 -0
  76. acra/config/profile_manager.py +87 -0
  77. acra/execution/__init__.py +1 -0
  78. acra/execution/logs/__init__.py +1 -0
  79. acra/execution/logs/error_logs/__init__.py +1 -0
  80. acra/execution/logs/execution_logs/__init__.py +1 -0
  81. acra/execution/sandbox/__init__.py +1 -0
  82. acra/execution/sandbox/docker_executor.py +209 -0
  83. acra/execution/sandbox/isolated_runner.py +148 -0
  84. acra/execution/sandbox/sandbox_manager.py +178 -0
  85. acra/graph/__init__.py +0 -0
  86. acra/graph/checkpoint.py +85 -0
  87. acra/graph/conditional_edges.py +103 -0
  88. acra/graph/edges.py +85 -0
  89. acra/graph/graph_visualizer.py +175 -0
  90. acra/graph/nodes.py +68 -0
  91. acra/graph/router.py +69 -0
  92. acra/graph/state.py +93 -0
  93. acra/graph/workflow.py +117 -0
  94. acra/memory/__init__.py +1 -0
  95. acra/memory/checkpoints/__init__.py +1 -0
  96. acra/memory/checkpoints/data/__init__.py +1 -0
  97. acra/memory/checkpoints/postgres_checkpoint.py +247 -0
  98. acra/memory/checkpoints/sqlite_checkpoint.py +309 -0
  99. acra/memory/chroma_db/__init__.py +1 -0
  100. acra/memory/conversation_memory/__init__.py +1 -0
  101. acra/memory/conversation_memory/long_term.py +229 -0
  102. acra/memory/conversation_memory/short_term.py +158 -0
  103. acra/memory/storage/__init__.py +1 -0
  104. acra/memory/vector_store/__init__.py +0 -0
  105. acra/memory/vector_store/chroma_store.py +260 -0
  106. acra/memory/vector_store/embeddings.py +43 -0
  107. acra/observability/__init__.py +1 -0
  108. acra/observability/langsmith_tracing.py +91 -0
  109. acra/observability/metrics.py +189 -0
  110. acra/observability/monitoring.py +162 -0
  111. acra/observability/token_tracking.py +131 -0
  112. acra/prompts/__init__.py +1 -0
  113. acra/schemas/__init__.py +0 -0
  114. acra/schemas/__pycache__/__init__.cpython-312.pyc +0 -0
  115. acra/schemas/__pycache__/coder_schema.cpython-312.pyc +0 -0
  116. acra/schemas/__pycache__/critic_schema.cpython-312.pyc +0 -0
  117. acra/schemas/__pycache__/execution_schema.cpython-312.pyc +0 -0
  118. acra/schemas/__pycache__/planner_schema.cpython-312.pyc +0 -0
  119. acra/schemas/__pycache__/researcher_schema.cpython-312.pyc +0 -0
  120. acra/schemas/__pycache__/shared_schema.cpython-312.pyc +0 -0
  121. acra/schemas/coder_schema.py +257 -0
  122. acra/schemas/critic_schema.py +177 -0
  123. acra/schemas/execution_schema.py +74 -0
  124. acra/schemas/planner_schema.py +129 -0
  125. acra/schemas/researcher_schema.py +220 -0
  126. acra/schemas/shared_schema.py +329 -0
  127. acra/tools/__init__.py +1 -0
  128. acra/tools/code_tools/__init__.py +1 -0
  129. acra/tools/code_tools/file_reader.py +156 -0
  130. acra/tools/code_tools/file_writer.py +108 -0
  131. acra/tools/code_tools/python_repl.py +72 -0
  132. acra/tools/code_tools/terminal_runner.py +98 -0
  133. acra/tools/github_tools/__init__.py +1 -0
  134. acra/tools/github_tools/commit_generator.py +91 -0
  135. acra/tools/github_tools/github_search.py +113 -0
  136. acra/tools/github_tools/repo_analyzer.py +138 -0
  137. acra/tools/integration_test_helper.py +370 -0
  138. acra/tools/validation_tools/__init__.py +1 -0
  139. acra/tools/validation_tools/dependency_checker.py +101 -0
  140. acra/tools/validation_tools/environment_config_validator.py +353 -0
  141. acra/tools/validation_tools/http_validator.py +336 -0
  142. acra/tools/validation_tools/response_validator.py +407 -0
  143. acra/tools/validation_tools/security_checker.py +93 -0
  144. acra/tools/validation_tools/syntax_checker.py +75 -0
  145. acra/tools/validation_tools/web_framework_validator.py +382 -0
  146. acra/tools/web_tools/__init__.py +1 -0
  147. acra/tools/web_tools/arxiv_search.py +110 -0
  148. acra/tools/web_tools/serpapi_search.py +106 -0
  149. acra/tools/web_tools/tavily_search.py +96 -0
  150. acra/ui/__init__.py +8 -0
  151. acra/ui/__pycache__/__init__.cpython-312.pyc +0 -0
  152. acra/ui/__pycache__/banner.cpython-312.pyc +0 -0
  153. acra/ui/__pycache__/components.cpython-312.pyc +0 -0
  154. acra/ui/__pycache__/shell.cpython-312.pyc +0 -0
  155. acra/ui/__pycache__/theme.cpython-312.pyc +0 -0
  156. acra/ui/banner.py +40 -0
  157. acra/ui/components.py +32 -0
  158. acra/ui/shell.py +91 -0
  159. acra/ui/theme.py +28 -0
  160. acra/utils/__init__.py +21 -0
  161. acra/utils/context_manager.py +20 -0
  162. acra/utils/keyring_manager.py +83 -0
  163. acra/utils/output_formatter.py +18 -0
  164. acra/utils/session_manager.py +59 -0
  165. acra_cli-0.1.1.dist-info/METADATA +616 -0
  166. acra_cli-0.1.1.dist-info/RECORD +169 -0
  167. acra_cli-0.1.1.dist-info/WHEEL +5 -0
  168. acra_cli-0.1.1.dist-info/licenses/LICENSE +661 -0
  169. acra_cli-0.1.1.dist-info/top_level.txt +1 -0
acra/__init__.py ADDED
@@ -0,0 +1,7 @@
1
+ """acra package root."""
2
+
3
+ __version__ = "0.1.0"
4
+
5
+ from .cli import main
6
+
7
+ __all__ = ["main", "__version__"]
acra/__main__.py ADDED
@@ -0,0 +1,4 @@
1
+ from .cli import app_main
2
+
3
+ if __name__ == "__main__":
4
+ app_main()
File without changes
File without changes
@@ -0,0 +1,235 @@
1
+ from typing import Dict, Optional
2
+ import re
3
+
4
+ from langchain_core.exceptions import OutputParserException
5
+ from langchain_core.messages import AIMessage
6
+
7
+ from acra.graph.state import AgentState
8
+ from acra.agents.coder.coder_chain import create_coder_chain
9
+ from acra.schemas.coder_schema import CoderOutput
10
+
11
+
12
+ def _build_fallback_response(project_name: str, entry_point: str, generated_files: Dict) -> CoderOutput:
13
+ """Create a safe fallback coder response when structured output parsing fails."""
14
+ return CoderOutput(
15
+ generated_files=generated_files or {},
16
+ explanation="Generated project files from the latest workflow state.",
17
+ coding_status="completed",
18
+ next_agent="executor",
19
+ project_name=project_name or "current_project",
20
+ entry_point=entry_point or "app.py",
21
+ )
22
+
23
+
24
+ def _extract_project_name(user_request: str) -> str:
25
+ """
26
+ Extract project name from user request.
27
+
28
+ Tries to extract from:
29
+ 1. Quoted names: "project 'name'" or "create 'name'"
30
+ 2. Filenames: "calculator.html" -> "calculator"
31
+ 3. First few words of request
32
+ """
33
+
34
+ # Try quoted names: 'project_name' or "project_name"
35
+ quoted_match = re.search(r"['\"]([^'\"]+)['\"]", user_request)
36
+ if quoted_match:
37
+ name = quoted_match.group(1)
38
+ # Remove file extension if present
39
+ name = name.rsplit('.', 1)[0]
40
+ # Sanitize name (remove spaces, special chars)
41
+ name = re.sub(r'[^\w-]', '_', name)
42
+ if name:
43
+ return name
44
+
45
+ # Fallback: use first few words (max 2)
46
+ words = user_request.split()[:2]
47
+ project_name = '_'.join(words).lower()
48
+ project_name = re.sub(r'[^\w-]', '_', project_name)
49
+
50
+ return project_name or "generated_project"
51
+
52
+
53
+ def coder_agent(state: AgentState) -> Dict:
54
+ """
55
+ Coding Agent
56
+
57
+ Responsibilities:
58
+ - Generate source code
59
+ - Create project files
60
+ - Fix execution errors
61
+ - Update generated codebase
62
+ - Prepare output for execution
63
+ """
64
+
65
+ #extract state
66
+
67
+ user_request = state.get("user_request", "")
68
+
69
+ # Extract or preserve project name
70
+ project_name = state.get("project_name", "")
71
+ if not project_name:
72
+ project_name = _extract_project_name(user_request)
73
+
74
+ current_step = state.get("current_step", "")
75
+
76
+ plan = state.get("plan", [])
77
+
78
+ generated_files = state.get(
79
+ "generated_files",
80
+ {}
81
+ )
82
+
83
+ interactive = state.get("interactive", False)
84
+
85
+ error_message = state.get(
86
+ "error_message",
87
+ ""
88
+ )
89
+
90
+ entry_point = state.get("entry_point", "app.py") or "app.py"
91
+
92
+ retry_count = state.get(
93
+ "retry_count",
94
+ 0
95
+ )
96
+
97
+ critic_feedback = state.get(
98
+ "critic_feedback",
99
+ ""
100
+ )
101
+
102
+ research_data = state.get(
103
+ "research_data",
104
+ []
105
+ )
106
+
107
+
108
+ #create coder chain
109
+
110
+ coder_chain = create_coder_chain()
111
+
112
+
113
+ #invoke coder chain
114
+
115
+ used_fallback = False
116
+ try:
117
+ response = coder_chain.invoke({
118
+
119
+ "user_request": user_request,
120
+
121
+ "current_step": current_step,
122
+
123
+ "plan": plan,
124
+
125
+ "generated_files": generated_files,
126
+
127
+ "error_message": error_message,
128
+
129
+ "retry_count": retry_count,
130
+
131
+ "critic_feedback": critic_feedback,
132
+
133
+ "research_data": research_data,
134
+ })
135
+
136
+ #extract structured output
137
+
138
+ updated_files = response.generated_files
139
+ explanation = response.explanation
140
+ coding_status = response.coding_status
141
+ entry_point = getattr(response, "entry_point", entry_point) or entry_point
142
+ next_agent = response.next_agent
143
+
144
+ except (OutputParserException, AttributeError, TypeError, ValueError) as exc:
145
+ response = _build_fallback_response(
146
+ project_name=project_name,
147
+ entry_point=entry_point,
148
+ generated_files=generated_files,
149
+ )
150
+ updated_files = response.generated_files
151
+ explanation = response.explanation
152
+ coding_status = response.coding_status
153
+ entry_point = response.entry_point
154
+ next_agent = response.next_agent
155
+ used_fallback = True
156
+ if error_message:
157
+ explanation = f"{explanation} Fallback due to parser error: {exc}"
158
+
159
+ if interactive and getattr(response, "requires_human_approval", False):
160
+ next_agent = "human"
161
+
162
+
163
+ #coder workflow logic
164
+
165
+ # first genration
166
+ if used_fallback:
167
+ coder_message = (
168
+ "⚠️ Coding Agent: "
169
+ "The model returned an incomplete structured response. "
170
+ "Using a safe fallback structure to continue the workflow."
171
+ )
172
+ elif retry_count == 0 and not error_message:
173
+
174
+ coder_message = (
175
+ "💻 Coding Agent: "
176
+ "Generating project files and implementation..."
177
+ )
178
+
179
+ # error fioxing mode
180
+ elif error_message:
181
+
182
+ coder_message = (
183
+ "🛠️ Coding Agent: "
184
+ "Execution failure detected.\n\n"
185
+ f"Error:\n{error_message}\n\n"
186
+ "Attempting autonomous repair..."
187
+ )
188
+
189
+ # critic revision mode
190
+ elif critic_feedback:
191
+
192
+ coder_message = (
193
+ "🧪 Coding Agent: "
194
+ "Critic feedback received.\n\n"
195
+ "Improving implementation quality..."
196
+ )
197
+
198
+ # general update
199
+ else:
200
+
201
+ coder_message = (
202
+ "⚡ Coding Agent: "
203
+ "Updating implementation..."
204
+ )
205
+
206
+
207
+ #return update state
208
+
209
+ return {
210
+
211
+ # convarsation update
212
+ "messages": [
213
+ AIMessage(content=coder_message)
214
+ ],
215
+
216
+ # generate codebase
217
+ "generated_files": updated_files,
218
+ "project_name": project_name,
219
+ "entry_point": entry_point,
220
+
221
+ # coding metadata
222
+ "coding_status": coding_status,
223
+ "coding_explanation": explanation,
224
+
225
+ # workflow
226
+ "next_agent": next_agent,
227
+ "current_agent": "coder",
228
+
229
+ # reset execution state before rerun
230
+ "execution_success": False,
231
+ "execution_output": "",
232
+
233
+ # clear previous errors after regeneration
234
+ "error_message": "",
235
+ }
@@ -0,0 +1,204 @@
1
+ from langchain_core.prompts import ChatPromptTemplate
2
+ from acra.agents.llm import llm
3
+ from acra.schemas.coder_schema import CoderOutput
4
+
5
+
6
+ # create coder chain
7
+
8
+ def create_coder_chain():
9
+ """
10
+ Creates the Coding Agent chain.
11
+
12
+ Responsibilities:
13
+ - Generate source code
14
+ - Create project files
15
+ - Fix runtime errors
16
+ - Improve existing code
17
+ - Respond to critic feedback
18
+ """
19
+
20
+ model = llm()
21
+
22
+ coder_prompt = ChatPromptTemplate.from_messages([
23
+
24
+ (
25
+ "system",
26
+
27
+ """
28
+ You are the Coding Agent inside an autonomous AI platform called AgentForge.
29
+
30
+ You are an expert AI software engineer.
31
+
32
+ Your responsibilities:
33
+ - Generate production-quality code
34
+ - Create complete project files
35
+ - Fix runtime errors
36
+ - Improve architecture
37
+ - Modify existing implementations
38
+ - Respond to critic feedback
39
+ - Ensure clean and executable code
40
+
41
+ ==================================================
42
+ YOUR CAPABILITIES
43
+ ==================================================
44
+
45
+ You can:
46
+ - generate Python applications
47
+ - create APIs
48
+ - write backend systems
49
+ - implement ML pipelines
50
+ - create tests
51
+ - generate configuration files
52
+ - fix broken code
53
+ - improve code quality
54
+
55
+ ==================================================
56
+ IMPORTANT RULES
57
+ ==================================================
58
+
59
+ 1. ALWAYS generate executable code
60
+ 2. NEVER generate incomplete placeholders
61
+ 3. NEVER leave TODO comments
62
+ 4. ALWAYS return complete file contents
63
+ 5. ALWAYS preserve working code unless fixing issues
64
+ 6. Keep architecture modular and clean
65
+ 7. Prefer readability and maintainability
66
+ 8. Handle runtime errors carefully
67
+ 9. Generate minimal but functional implementations
68
+ 10. Return ONLY structured output
69
+ 11. YOU MUST INCLUDE EVERY REQUIRED FIELD IN THE RESPONSE
70
+ 12. DO NOT OMIT explanation, coding_status, next_agent, entry_point, or generated_files
71
+ 13. If a field is not applicable, still provide a safe default value rather than omitting it
72
+ 14. generated_files must be a dictionary mapping filenames to full file contents
73
+ 15. If you are unsure, return a conservative working implementation instead of an empty response
74
+
75
+ ==================================================
76
+ CURRENT WORKFLOW STATE
77
+ ==================================================
78
+
79
+ USER REQUEST:
80
+ {user_request}
81
+
82
+ CURRENT TASK:
83
+ {current_step}
84
+
85
+ FULL EXECUTION PLAN:
86
+ {plan}
87
+
88
+ PREVIOUSLY GENERATED FILES:
89
+ {generated_files}
90
+
91
+ EXECUTION ERROR:
92
+ {error_message}
93
+
94
+ RETRY COUNT:
95
+ {retry_count}
96
+
97
+ CRITIC FEEDBACK:
98
+ {critic_feedback}
99
+
100
+ RESEARCH DATA:
101
+ {research_data}
102
+
103
+ ==================================================
104
+ YOUR TASK
105
+ ==================================================
106
+
107
+ Analyze the workflow state carefully.
108
+
109
+ Then:
110
+
111
+ 1. Generate or update project files
112
+ 2. Fix runtime issues if errors exist
113
+ 3. Improve implementation if critic feedback exists
114
+ 4. Maintain consistency across all files
115
+ 5. Prepare project for execution
116
+
117
+ ==================================================
118
+ OUTPUT REQUIREMENTS
119
+ ==================================================
120
+
121
+ You MUST return ALL of the following fields and they must be present in every response:
122
+
123
+ 1. generated_files
124
+ - dictionary of filename → file content
125
+ - must not be empty unless the task is impossible
126
+
127
+ 2. explanation
128
+ - short explanation of changes
129
+ - never omit this field
130
+
131
+ 3. coding_status
132
+ - one of: generating, updating, fixing, improving, completed, failed
133
+ - never omit this field
134
+
135
+ 4. next_agent
136
+ - one of: executor, critic, human
137
+ - never omit this field
138
+
139
+ 5. entry_point
140
+ - the file that should be executed to start the project, for example "app.py" or "main.py"
141
+ - never omit this field
142
+
143
+ 6. project_name
144
+ - a valid project name string
145
+ - never omit this field
146
+
147
+ Before responding, verify that every required field exists and is non-empty.
148
+ If any field is missing in your draft, fill it with a safe default before finalizing.
149
+
150
+ ==================================================
151
+ CODING STATUS OPTIONS
152
+ ==================================================
153
+
154
+ - generating
155
+ - updating
156
+ - fixing
157
+ - improving
158
+ - completed
159
+
160
+ ==================================================
161
+ NEXT AGENT OPTIONS
162
+ ==================================================
163
+
164
+ - executor
165
+ - critic
166
+
167
+ ==================================================
168
+ IMPORTANT
169
+ ==================================================
170
+
171
+ - If execution errors exist:
172
+ focus on fixing the errors.
173
+
174
+ - If critic feedback exists:
175
+ improve code quality and architecture.
176
+
177
+ - If no files exist:
178
+ generate the initial implementation.
179
+
180
+ - Always return FULL updated file contents.
181
+ """
182
+ ),
183
+
184
+ (
185
+ "human",
186
+
187
+ """
188
+ Generate or update the project implementation based on the workflow state.
189
+ """
190
+ )
191
+
192
+ ])
193
+
194
+
195
+ # structured output
196
+
197
+ structured_llm = model.with_structured_output(
198
+ CoderOutput
199
+ )
200
+
201
+
202
+ #return chain
203
+
204
+ return coder_prompt | structured_llm
File without changes
@@ -0,0 +1,199 @@
1
+ from typing import Dict
2
+
3
+ from langchain_core.messages import AIMessage
4
+
5
+ from acra.graph.state import AgentState
6
+
7
+ from acra.agents.critic.critic_chain import (
8
+ create_critic_chain
9
+ )
10
+
11
+
12
+ def critic_agent(state: AgentState) -> Dict:
13
+ """
14
+ Critic Agent
15
+
16
+ Responsibilities:
17
+ - Evaluate generated code
18
+ - Review execution results
19
+ - Detect vulnerabilities
20
+ - Analyze architecture quality
21
+ - Decide whether code is acceptable
22
+ """
23
+
24
+ #extract state
25
+
26
+ user_request = state.get(
27
+ "user_request",
28
+ ""
29
+ )
30
+
31
+ current_step = state.get(
32
+ "current_step",
33
+ ""
34
+ )
35
+
36
+ generated_files = state.get(
37
+ "generated_files",
38
+ {}
39
+ )
40
+
41
+ interactive = state.get("interactive", False)
42
+
43
+ execution_success = state.get(
44
+ "execution_success",
45
+ False
46
+ )
47
+
48
+ execution_logs = state.get(
49
+ "execution_logs",
50
+ ""
51
+ )
52
+
53
+ execution_output = state.get(
54
+ "execution_output",
55
+ ""
56
+ )
57
+
58
+ error_message = state.get(
59
+ "error_message",
60
+ ""
61
+ )
62
+
63
+ retry_count = state.get(
64
+ "retry_count",
65
+ 0
66
+ )
67
+
68
+
69
+ #create chain
70
+
71
+ critic_chain = create_critic_chain()
72
+
73
+
74
+ #invoke critic agent chain
75
+
76
+ response = critic_chain.invoke({
77
+
78
+ "user_request": user_request,
79
+
80
+ "current_step": current_step,
81
+
82
+ "generated_files": generated_files,
83
+
84
+ "execution_success": execution_success,
85
+
86
+ "execution_logs": execution_logs,
87
+
88
+ "execution_output": execution_output,
89
+
90
+ "error_message": error_message,
91
+
92
+ "retry_count": retry_count,
93
+ })
94
+
95
+
96
+ #extract structured response
97
+
98
+ review_status = response.review_status
99
+
100
+ quality_score = response.quality_score
101
+
102
+ summary = response.summary
103
+
104
+ feedback = response.feedback
105
+
106
+ next_agent = response.next_agent
107
+
108
+ if interactive and getattr(response, "requires_human_approval", False):
109
+ next_agent = "human"
110
+
111
+ issues = response.issues or []
112
+
113
+ security_issues = (
114
+ response.security_issues or []
115
+ )
116
+
117
+ improvement_suggestions = (
118
+ response.improvement_suggestions or []
119
+ )
120
+
121
+
122
+ #review success
123
+
124
+ if review_status == "approved":
125
+
126
+ critic_message = (
127
+ "Critic Agent: "
128
+ "Implementation approved.\n\n"
129
+ f"Quality Score: {quality_score}/10\n\n"
130
+ f"Summary:\n{summary}"
131
+ )
132
+
133
+ #review failed
134
+
135
+ elif review_status in [
136
+ "needs_improvement",
137
+ "failed"
138
+ ]:
139
+
140
+ critic_message = (
141
+ "Critic Agent: "
142
+ "Issues detected in implementation.\n\n"
143
+ f"Quality Score: {quality_score}/10\n\n"
144
+ f"Feedback:\n{feedback}"
145
+ )
146
+
147
+ #security failure
148
+
149
+ elif review_status == "unsafe":
150
+
151
+ critic_message = (
152
+ "Critic Agent: "
153
+ "Security risks detected.\n\n"
154
+ f"Security Issues:\n"
155
+ f"{security_issues}"
156
+ )
157
+
158
+ #feedback
159
+
160
+ else:
161
+
162
+ critic_message = (
163
+ "Critic Agent: "
164
+ "Unexpected review state encountered."
165
+ )
166
+
167
+
168
+ #return update state
169
+
170
+ return {
171
+
172
+ "messages": [
173
+ AIMessage(content=critic_message)
174
+ ],
175
+
176
+ # Review Results
177
+ "review_status": review_status,
178
+
179
+ "quality_score": quality_score,
180
+
181
+ "critic_feedback": feedback,
182
+
183
+ "critic_summary": summary,
184
+
185
+ "review_issues": issues,
186
+
187
+ "security_issues": security_issues,
188
+
189
+ "improvement_suggestions": (
190
+ improvement_suggestions
191
+ ),
192
+
193
+ # Workflow
194
+ "next_agent": next_agent,
195
+ "current_agent": "critic",
196
+
197
+ # Reset retry count on approval so future workflow phases start fresh
198
+ "retry_count": 0 if review_status == "approved" else state.get("retry_count", 0),
199
+ }