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
@@ -0,0 +1,268 @@
1
+ from langchain_core.prompts import ChatPromptTemplate
2
+ from acra.agents.llm import llm
3
+
4
+ from acra.schemas.critic_schema import CriticOutput
5
+
6
+
7
+ #create critic chain
8
+
9
+ def create_critic_chain():
10
+ """
11
+ Creates the Critic Agent chain.
12
+
13
+ Responsibilities:
14
+ - Review generated code
15
+ - Analyze execution results
16
+ - Detect vulnerabilities
17
+ - Evaluate architecture quality
18
+ - Validate implementation correctness
19
+ - Suggest improvements
20
+ """
21
+
22
+ model = llm()
23
+
24
+ critic_prompt = ChatPromptTemplate.from_messages([
25
+
26
+ (
27
+ "system",
28
+
29
+ """
30
+ You are the Critic Agent inside an autonomous AI system called OMNIAGENT.
31
+
32
+ You are a senior AI software architect and security reviewer.
33
+
34
+ Your responsibilities:
35
+ - Review generated code quality
36
+ - Evaluate runtime execution results
37
+ - Detect architectural problems
38
+ - Identify security vulnerabilities
39
+ - Detect hallucinated libraries/APIs
40
+ - Validate implementation correctness
41
+ - Suggest improvements
42
+ - Decide workflow continuation
43
+
44
+ ==================================================
45
+ FIRST: DETECT PROJECT TYPE
46
+ ==================================================
47
+
48
+ BEFORE REVIEWING, detect if this is a web application:
49
+
50
+ WEB APPLICATION KEYWORDS:
51
+ - Flask, FastAPI, Django, Tornado, etc.
52
+ - User request mentions: website, web app, web interface, portfolio, blog, dashboard
53
+ - Generated files include: templates/, static/, html files, CSS, JavaScript
54
+ - Main app file uses: Flask(), FastAPI(), Django
55
+
56
+ If this is a WEB APPLICATION:
57
+ - Follow the "WEB SERVER EXECUTION RULES" section below
58
+ - Evaluate based on code structure, NOT execution_success
59
+ - Remember: web apps cannot finish executing (servers block indefinitely)
60
+
61
+ ==================================================
62
+ YOUR ROLE
63
+ ==================================================
64
+
65
+ You are NOT a coding agent.
66
+
67
+ You DO NOT generate code directly.
68
+
69
+ You ONLY:
70
+ - analyze
71
+ - evaluate
72
+ - validate
73
+ - critique
74
+ - review
75
+
76
+ ==================================================
77
+ REVIEW OBJECTIVES
78
+ ==================================================
79
+
80
+ You must evaluate:
81
+
82
+ 1. CODE QUALITY
83
+ - readability
84
+ - maintainability
85
+ - modularity
86
+ - consistency
87
+
88
+ 2. EXECUTION QUALITY
89
+ - runtime success
90
+ - runtime stability
91
+ - error handling
92
+
93
+ 3. ARCHITECTURE
94
+ - clean structure
95
+ - scalability
96
+ - separation of concerns
97
+
98
+ 4. SECURITY
99
+ - hardcoded secrets
100
+ - dangerous subprocess usage
101
+ - shell injection risks
102
+ - unsafe eval/exec usage
103
+ - insecure configurations
104
+
105
+ 5. RELIABILITY
106
+ - missing dependencies
107
+ - broken imports
108
+ - incomplete implementations
109
+
110
+ 6. TESTING
111
+ - validation logic
112
+ - testability
113
+ - coverage awareness
114
+
115
+ ==================================================
116
+ CURRENT WORKFLOW STATE
117
+ ==================================================
118
+
119
+ USER REQUEST:
120
+ {user_request}
121
+
122
+ CURRENT TASK:
123
+ {current_step}
124
+
125
+ GENERATED FILES:
126
+ {generated_files}
127
+
128
+ EXECUTION SUCCESS:
129
+ {execution_success}
130
+
131
+ EXECUTION LOGS:
132
+ {execution_logs}
133
+
134
+ EXECUTION OUTPUT:
135
+ {execution_output}
136
+
137
+ ERROR MESSAGE:
138
+ {error_message}
139
+
140
+ RETRY COUNT:
141
+ {retry_count}
142
+
143
+ ==================================================
144
+ REVIEW RULES
145
+ ==================================================
146
+
147
+ - If execution failed:
148
+ strongly recommend returning to coder.
149
+
150
+ - If security vulnerabilities exist:
151
+ mark review_status as "unsafe".
152
+
153
+ - If implementation is acceptable:
154
+ mark review_status as "approved".
155
+
156
+ - If implementation works but needs refinement:
157
+ mark review_status as "needs_improvement".
158
+
159
+ - If implementation is fundamentally broken:
160
+ mark review_status as "failed".
161
+
162
+ ==================================================
163
+ WEB SERVER EXECUTION RULES
164
+ ==================================================
165
+
166
+ CRITICAL: Web applications (Flask, FastAPI, Django, etc.) CANNOT be validated
167
+ by running them to completion. They run indefinitely by design.
168
+
169
+ When evaluating web applications:
170
+
171
+ 1. IGNORE execution_success = False if it's a web server app
172
+ - Validation script success means the app is structurally sound
173
+
174
+ 2. EVALUATE WEB APPS BASED ON:
175
+ - Directory structure (templates/, static/ folders present)
176
+ - HTML templates properly structured and syntactically valid
177
+ - CSS files with proper styling rules
178
+ - JavaScript files present and valid
179
+ - Flask/FastAPI application entry point (app.py or main.py) well-formed
180
+ - requirements.txt listing correct dependencies
181
+ - All imports and syntax are valid
182
+
183
+ 3. SCORING WEB APPLICATIONS:
184
+ - Score 8-9: Complete structure, all files present, no security issues, follows conventions
185
+ - Score 7-8: Most files present, minor structural issues, good architecture
186
+ - Score below 7: Missing critical files, incomplete implementation, or security risks
187
+
188
+ 4. APPROVE WEB APPLICATIONS IF:
189
+ - All expected files are generated (templates, static assets, main app file)
190
+ - No hardcoded secrets or security vulnerabilities
191
+ - Proper folder structure for Flask/FastAPI (templates/, static/ dirs)
192
+ - Requirements.txt exists with valid syntax
193
+ - Application file has valid Flask/FastAPI app definition
194
+
195
+ ==================================================
196
+ NEXT AGENT RULES
197
+ ==================================================
198
+
199
+ - approved → end
200
+ - needs_improvement → coder
201
+ - failed → coder
202
+ - unsafe → human
203
+
204
+ ==================================================
205
+ OUTPUT REQUIREMENTS
206
+ ==================================================
207
+
208
+ Return ONLY structured output.
209
+
210
+ ==================================================
211
+ QUALITY SCORING
212
+ ==================================================
213
+
214
+ Score from 0 → 10
215
+
216
+ STANDARD PROJECTS (CLI, Libraries, Scripts):
217
+ 0-3: Broken or unsafe implementation
218
+ 4-6: Partially working but significant issues
219
+ 7-8: Good implementation with minor improvements needed
220
+ 9-10: Production-quality implementation
221
+
222
+ WEB APPLICATIONS (Flask, FastAPI, Django, etc.):
223
+ 0-3: Broken structure, missing critical files
224
+ 4-5: Incomplete - major components missing
225
+ 6-7: Functional structure but needs refinement
226
+ 8-9: Well-structured, all components present, good architecture
227
+ 10: Production-ready with perfect structure and styling
228
+
229
+ CRITICAL: For web apps that validated successfully (exit code 0),
230
+ minimum score should be 7-8 if structure is correct.
231
+
232
+ ==================================================
233
+ IMPORTANT
234
+ ==================================================
235
+
236
+ Be extremely critical and realistic.
237
+
238
+ Do NOT approve weak implementations.
239
+
240
+ Your job is to ensure:
241
+ - reliability
242
+ - correctness
243
+ - maintainability
244
+ - security
245
+ """
246
+ ),
247
+
248
+ (
249
+ "human",
250
+
251
+ """
252
+ Review the generated implementation and determine workflow quality.
253
+ """
254
+ )
255
+
256
+ ])
257
+
258
+
259
+ #structure output
260
+
261
+ structured_llm = model.with_structured_output(
262
+ CriticOutput
263
+ )
264
+
265
+
266
+ #return chain
267
+
268
+ return critic_prompt | structured_llm
File without changes
@@ -0,0 +1,114 @@
1
+ import subprocess
2
+ import time
3
+ import shlex
4
+ import os
5
+ from pathlib import Path
6
+ from typing import List, Union
7
+ from acra.schemas.execution_schema import ExecutionResult
8
+
9
+
10
+ Command = Union[str, List[str]]
11
+
12
+
13
+ def _normalize_command(command: Command) -> List[str]:
14
+ if isinstance(command, str):
15
+ return shlex.split(command)
16
+ return command
17
+
18
+
19
+ def run_in_docker(
20
+ project_path: str,
21
+ command: Command = "python app.py",
22
+ timeout: int = 60,
23
+ ) -> ExecutionResult:
24
+ """
25
+ executes generated prject in side docker container."""
26
+
27
+ start_time = time.time()
28
+ resolved_project_path = str(Path(project_path).resolve())
29
+ command_args = _normalize_command(command)
30
+ docker_command = [
31
+ "docker",
32
+ "run",
33
+ "--rm",
34
+ "--network",
35
+ "none",
36
+ "--user",
37
+ f"{os.getuid()}:{os.getgid()}",
38
+ "--memory",
39
+ "256m",
40
+ "--cpus",
41
+ "1",
42
+ "--pids-limit",
43
+ "128",
44
+ "--cap-drop",
45
+ "ALL",
46
+ "--security-opt",
47
+ "no-new-privileges",
48
+ "-v",
49
+ f"{resolved_project_path}:/app:rw",
50
+ "-w",
51
+ "/app",
52
+ "python:3.11",
53
+ *command_args
54
+ ]
55
+
56
+ try:
57
+ result = subprocess.run(
58
+ docker_command,
59
+ capture_output=True,
60
+ text=True,
61
+ timeout=timeout
62
+ )
63
+
64
+ execution_time = time.time() - start_time
65
+ success = result.returncode == 0
66
+
67
+ return ExecutionResult(
68
+ execution_status=(
69
+ "success" if success else "failed"
70
+ ),
71
+
72
+ execution_success=success,
73
+
74
+ stdout=result.stdout,
75
+
76
+ stderr=result.stderr,
77
+
78
+ error_message=(
79
+ result.stderr if not success else None
80
+ ),
81
+
82
+ executed_command=" ".join(command_args),
83
+
84
+ execution_time=execution_time,
85
+
86
+ next_agent=(
87
+ "critic" if success else "coder"
88
+ )
89
+ )
90
+
91
+ except subprocess.TimeoutExpired:
92
+ return ExecutionResult(
93
+ execution_status="timeout",
94
+ execution_success=False,
95
+ stdout="",
96
+ stderr="Execution timed out.",
97
+ error_message="Execution exceeded the timeout limit.",
98
+ executed_command=" ".join(command_args),
99
+ execution_time=timeout,
100
+ next_agent="coder"
101
+ )
102
+
103
+ except Exception as e:
104
+
105
+ return ExecutionResult(
106
+ execution_status="crashed",
107
+ execution_success=False,
108
+ stdout="",
109
+ stderr=str(e),
110
+ error_message=str(e),
111
+ executed_command=" ".join(command_args),
112
+ execution_time=time.time() - start_time,
113
+ next_agent="human"
114
+ )
@@ -0,0 +1,142 @@
1
+ from typing import Dict
2
+ from langchain_core.messages import AIMessage
3
+ from acra.graph.state import AgentState
4
+
5
+ from acra.agents.executor.sandbox_runner import (
6
+ execute_generated_project
7
+ )
8
+
9
+ def executor_agent(state: AgentState) -> Dict:
10
+ """
11
+ Execution Agent
12
+
13
+ Responsibilities:
14
+ - Save generated files.
15
+ - Execute generated code
16
+ - Capture runtime logs/errors
17
+ - Detect failures
18
+ - Route workflow intelligently
19
+ """
20
+
21
+ # Extract state
22
+ generated_files = state.get(
23
+ "generated_files",
24
+ {}
25
+ )
26
+
27
+ project_name = state.get(
28
+ "project_name",
29
+ "current_project"
30
+ )
31
+
32
+ entry_point = state.get("entry_point", "app.py") or "app.py"
33
+
34
+ retry_count = state.get(
35
+ "retry_count",
36
+ 0
37
+ )
38
+
39
+ #Validate generated files
40
+
41
+ if not generated_files:
42
+ return {
43
+ "messages": [
44
+ AIMessage(
45
+ content=(
46
+ "Execution Agent: "
47
+ "No generated files found."
48
+ )
49
+ )
50
+ ],
51
+
52
+ "execution_success": False,
53
+
54
+ "error_message": (
55
+ "No files available for execution."
56
+ ),
57
+
58
+ "next_agent": "coder",
59
+ "current_agent": "executor",
60
+ "project_name": project_name,
61
+
62
+ "retry_count": retry_count + 1
63
+
64
+ }
65
+
66
+ # Execute generated project
67
+ execution_result = execute_generated_project(
68
+ generated_files=generated_files,
69
+ project_name=project_name,
70
+ entry_point=entry_point,
71
+ )
72
+
73
+ # Detect if this is a web server app
74
+ all_content = " ".join(generated_files.values())
75
+ is_web_server = any(kw in all_content for kw in [
76
+ "app.run(", "uvicorn", "Flask(", "FastAPI(", "django", "tornado"
77
+ ])
78
+
79
+ # For web servers that validate successfully, consider it a success
80
+ # (validation passes = syntax OK + dependencies OK)
81
+ execution_success = execution_result.execution_success
82
+ if is_web_server and execution_result.execution_status == "success":
83
+ execution_success = True
84
+
85
+ if execution_success:
86
+ if is_web_server:
87
+ executor_message = (
88
+ "Execution Agent: "
89
+ "Web application structure validated successfully.\n\n"
90
+ f"Execution time: {execution_result.execution_time:.2f} seconds."
91
+ )
92
+ else:
93
+ executor_message = (
94
+ "Execution Agent: "
95
+ "Project executed successfully.\n\n"
96
+ f"Execution time: {execution_result.execution_time:.2f} seconds."
97
+ )
98
+ next_agent = "critic"
99
+ else:
100
+ executor_message = (
101
+ "Execution Agent: "
102
+ "Project execution failed.\n\n"
103
+ f"Error: {execution_result.error_message or 'Unknown error.'}"
104
+ )
105
+ next_agent = "coder" if retry_count < 3 else "critic"
106
+
107
+ # Detect if failure was a web server timeout (not a real code error)
108
+ is_timeout = execution_result.execution_status == "timeout"
109
+ is_expected_timeout = is_web_server and is_timeout
110
+
111
+ # Only increment retry_count for real failures, not expected web server timeouts
112
+ retry_increment = 0 if is_expected_timeout else (
113
+ 0 if execution_success else 1
114
+ )
115
+
116
+ # Return updated state
117
+ return {
118
+ "messages": [
119
+ AIMessage(
120
+ content=executor_message
121
+ )
122
+ ],
123
+
124
+ #execution results
125
+ "execution_success": execution_success,
126
+ "execution_status": execution_result.execution_status,
127
+ "execution_output": execution_result.stdout,
128
+ "execution_logs": (
129
+ f"STDOUT:\n{execution_result.stdout}\n\n"
130
+ f"STDERR:\n{execution_result.stderr}"
131
+ ),
132
+ "error_message": execution_result.error_message,
133
+ "execution_time": execution_result.execution_time,
134
+
135
+ #workflow
136
+ "next_agent": next_agent,
137
+ "current_agent": "executor",
138
+ "project_name": project_name,
139
+
140
+ #retry tracking
141
+ "retry_count": retry_count + retry_increment
142
+ }
@@ -0,0 +1,144 @@
1
+ import logging
2
+ from typing import Dict
3
+ from pathlib import Path
4
+
5
+ from acra.agents.executor.docker_runner import run_in_docker
6
+ from acra.schemas.execution_schema import ExecutionResult
7
+ from acra.config import GENERATED_PROJECT_DIR
8
+
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ def _safe_child_path(base_path: Path, relative_path: str) -> Path:
14
+ """
15
+ Resolve a user/model supplied child path and ensure it stays under base_path.
16
+ """
17
+ candidate = Path(relative_path)
18
+ if candidate.is_absolute() or ".." in candidate.parts:
19
+ raise ValueError(f"Unsafe generated file path: {relative_path}")
20
+
21
+ resolved_base = base_path.resolve()
22
+ resolved_candidate = (resolved_base / candidate).resolve()
23
+ if not resolved_candidate.is_relative_to(resolved_base):
24
+ raise ValueError(f"Generated file escapes project directory: {relative_path}")
25
+
26
+ return resolved_candidate
27
+
28
+
29
+ def save_generated_files(
30
+ generated_files: Dict[str, str],
31
+ project_name: str = "current_project"
32
+ ) -> str:
33
+ """
34
+ Save generated files to sandbox project directory.
35
+ """
36
+ project_path = _safe_child_path(
37
+ Path(GENERATED_PROJECT_DIR),
38
+ project_name or "current_project"
39
+ )
40
+
41
+ logger.info(f"Creating project directory: {project_path}")
42
+
43
+ project_path.mkdir(parents=True, exist_ok=True)
44
+
45
+ for filename, content in generated_files.items():
46
+
47
+ file_path = _safe_child_path(project_path, filename)
48
+
49
+ file_path.parent.mkdir(parents=True, exist_ok=True)
50
+
51
+ with open(file_path, "w", encoding="utf-8") as f:
52
+ f.write(content)
53
+
54
+ logger.debug(f"Saved file: {file_path} ({len(content)} bytes)")
55
+
56
+ logger.info(f"Project '{project_name}' saved with {len(generated_files)} files")
57
+
58
+ return str(project_path)
59
+
60
+ def execute_generated_project(
61
+ generated_files: Dict[str, str],
62
+ project_name: str = "current_project",
63
+ command: str = None,
64
+ entry_point: str = "app.py"
65
+ ) -> ExecutionResult:
66
+
67
+ """
68
+ Save generated project and execute inside a sandbox.
69
+ Auto-detects web server frameworks and validates them without running the server.
70
+ """
71
+ project_path = save_generated_files(
72
+ generated_files=generated_files,
73
+ project_name=project_name
74
+ )
75
+
76
+ # Auto-detect web server frameworks
77
+ all_content = " ".join(generated_files.values())
78
+ is_web_server = any(kw in all_content for kw in [
79
+ "app.run(", "uvicorn", "Flask(", "FastAPI(", "django", "tornado"
80
+ ])
81
+
82
+ if command is None:
83
+ if is_web_server:
84
+ # For web servers, create and run a validation script
85
+ validation_script = """import ast
86
+ import sys
87
+ from pathlib import Path
88
+
89
+ try:
90
+ from pip._vendor.packaging.requirements import Requirement
91
+ except Exception:
92
+ Requirement = None
93
+
94
+ # Validate Python syntax for all .py files recursively.
95
+ errors = []
96
+ for path in Path('.').rglob('*.py'):
97
+ if any(part.startswith('.') for part in path.parts):
98
+ continue
99
+ try:
100
+ source = path.read_text(encoding='utf-8')
101
+ ast.parse(source, filename=str(path))
102
+ except SyntaxError as e:
103
+ errors.append(f"{path}: {e}")
104
+ except UnicodeDecodeError as e:
105
+ errors.append(f"{path}: {e}")
106
+
107
+ requirements = Path('requirements.txt')
108
+ if requirements.exists():
109
+ for line_number, line in enumerate(requirements.read_text(encoding='utf-8').splitlines(), 1):
110
+ stripped = line.strip()
111
+ if not stripped or stripped.startswith('#'):
112
+ continue
113
+ if Requirement is None:
114
+ continue
115
+ try:
116
+ Requirement(stripped)
117
+ except Exception as e:
118
+ errors.append(f"requirements.txt:{line_number}: invalid requirement {stripped!r}: {e}")
119
+
120
+ if errors:
121
+ print("Validation errors found:")
122
+ for err in errors:
123
+ print(f" {err}")
124
+ sys.exit(1)
125
+
126
+ print("Web application validation passed: all files have valid syntax")
127
+ sys.exit(0)
128
+ """
129
+ # Save validation script to project
130
+ validation_path = _safe_child_path(Path(project_path), "_validate.py")
131
+ with open(validation_path, "w", encoding="utf-8") as f:
132
+ f.write(validation_script)
133
+
134
+ command = "python _validate.py"
135
+ else:
136
+ resolved_entry_point = (entry_point or "app.py").strip() or "app.py"
137
+ command = f"python {resolved_entry_point}"
138
+
139
+ result = run_in_docker(
140
+ project_path=project_path,
141
+ command=command
142
+ )
143
+
144
+ return result
File without changes