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,85 @@
1
+ import logging
2
+ import os
3
+
4
+ from acra import config
5
+ from acra.config import SQLITE_DB_PATH
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+
10
+ class GraphCheckpointManager:
11
+ """
12
+ LangGraph checkpoint abstraction.
13
+
14
+ Responsibilities:
15
+ - provide checkpoint saver for graph persistence
16
+ - support graph resumability
17
+ """
18
+
19
+ def __init__(self):
20
+ self.backend = os.getenv("CHECKPOINT_BACKEND", config.CHECKPOINT_BACKEND).lower()
21
+ self._backend_context = None
22
+ self.checkpointer = self._initialize_backend()
23
+ logger.info("Checkpoint backend initialized: %s", self.backend)
24
+
25
+ def _initialize_backend(self):
26
+ """Initialize LangGraph checkpoint saver based on configured backend."""
27
+ if self.backend == "sqlite":
28
+ try:
29
+ from langgraph.checkpoint.sqlite import SqliteSaver
30
+
31
+ db_path = str(SQLITE_DB_PATH)
32
+ os.makedirs(os.path.dirname(db_path), exist_ok=True)
33
+
34
+ logger.info("Using SQLite checkpoint: %s", db_path)
35
+ self._backend_context = SqliteSaver.from_conn_string(db_path)
36
+ return self._backend_context.__enter__()
37
+ except ImportError:
38
+ logger.warning(
39
+ "SQLite backend not available. Install: pip install langgraph-checkpoint-sqlite. Falling back to memory saver."
40
+ )
41
+ return self._get_memory_saver()
42
+ except Exception as exc:
43
+ logger.error("Failed to initialize SQLite: %s. Using memory saver.", exc)
44
+ return self._get_memory_saver()
45
+
46
+ if self.backend == "postgres":
47
+ try:
48
+ from langgraph.checkpoint.postgres import PostgresSaver
49
+
50
+ connection_string = os.getenv(
51
+ "POSTGRES_CONNECTION_STRING",
52
+ "postgresql://user:password@localhost/omniagent",
53
+ )
54
+
55
+ logger.info("Using PostgreSQL checkpoint")
56
+ self._backend_context = PostgresSaver.from_conn_string(connection_string)
57
+ return self._backend_context.__enter__()
58
+ except ImportError:
59
+ logger.warning(
60
+ "PostgreSQL backend not available. Install: pip install langgraph-checkpoint-postgres. Falling back to memory saver."
61
+ )
62
+ return self._get_memory_saver()
63
+ except Exception as exc:
64
+ logger.error("Failed to initialize PostgreSQL: %s. Using memory saver.", exc)
65
+ return self._get_memory_saver()
66
+
67
+ return self._get_memory_saver()
68
+
69
+ def _get_memory_saver(self):
70
+ """Get in-memory checkpoint saver (default/fallback)."""
71
+ from langgraph.checkpoint.memory import MemorySaver
72
+
73
+ logger.info("Using MemorySaver (in-memory checkpoints)")
74
+ return MemorySaver()
75
+
76
+ def __del__(self):
77
+ if self._backend_context is not None:
78
+ try:
79
+ self._backend_context.__exit__(None, None, None)
80
+ except Exception:
81
+ pass
82
+
83
+
84
+ graph_checkpoint = GraphCheckpointManager()
85
+
@@ -0,0 +1,103 @@
1
+ from langgraph.graph import END
2
+
3
+ from acra.graph.router import (
4
+ route_workflow
5
+ )
6
+
7
+
8
+ # conditional edge map
9
+
10
+ CONDITIONAL_EDGE_MAP = {
11
+
12
+ "planner": "planner",
13
+
14
+ "researcher": "researcher",
15
+
16
+ "coder": "coder",
17
+
18
+ "executor": "executor",
19
+
20
+ "critic": "critic",
21
+
22
+ "memory": "memory",
23
+
24
+ "human": "human",
25
+
26
+ END: END
27
+ }
28
+
29
+
30
+ # register conditional edges
31
+
32
+ def register_conditional_edges(
33
+ workflow
34
+ ):
35
+ """
36
+ Register routing logic
37
+ for all workflow nodes.
38
+ """
39
+
40
+ workflow.add_conditional_edges(
41
+
42
+ "planner",
43
+
44
+ route_workflow,
45
+
46
+ CONDITIONAL_EDGE_MAP
47
+ )
48
+
49
+ workflow.add_conditional_edges(
50
+
51
+ "researcher",
52
+
53
+ route_workflow,
54
+
55
+ CONDITIONAL_EDGE_MAP
56
+ )
57
+
58
+ workflow.add_conditional_edges(
59
+
60
+ "coder",
61
+
62
+ route_workflow,
63
+
64
+ CONDITIONAL_EDGE_MAP
65
+ )
66
+
67
+ workflow.add_conditional_edges(
68
+
69
+ "executor",
70
+
71
+ route_workflow,
72
+
73
+ CONDITIONAL_EDGE_MAP
74
+ )
75
+
76
+ workflow.add_conditional_edges(
77
+
78
+ "critic",
79
+
80
+ route_workflow,
81
+
82
+ CONDITIONAL_EDGE_MAP
83
+ )
84
+
85
+ workflow.add_conditional_edges(
86
+
87
+ "memory",
88
+
89
+ route_workflow,
90
+
91
+ CONDITIONAL_EDGE_MAP
92
+ )
93
+
94
+ workflow.add_conditional_edges(
95
+
96
+ "human",
97
+
98
+ route_workflow,
99
+
100
+ CONDITIONAL_EDGE_MAP
101
+ )
102
+
103
+ return workflow
acra/graph/edges.py ADDED
@@ -0,0 +1,85 @@
1
+ from langgraph.graph import END
2
+
3
+
4
+ # static edge
5
+
6
+ GRAPH_EDGES = {
7
+
8
+ "planner": [
9
+ "researcher",
10
+ "coder",
11
+ "human"
12
+ ],
13
+
14
+ "researcher": [
15
+ "coder"
16
+ ],
17
+
18
+ "coder": [
19
+ "executor"
20
+ ],
21
+
22
+ "executor": [
23
+ "critic",
24
+ "coder",
25
+ "human"
26
+ ],
27
+
28
+ "critic": [
29
+ "memory",
30
+ "coder",
31
+ "human"
32
+ ],
33
+
34
+ "memory": [
35
+ END
36
+ ],
37
+
38
+ "human": [
39
+ "coder",
40
+ END
41
+ ]
42
+ }
43
+
44
+
45
+ # validate transition
46
+
47
+ def validate_transition(source: str, destination: str) -> bool:
48
+ """Returns True if the transition is explicitly defined OR destination is END."""
49
+ if destination == END:
50
+ return True
51
+ return destination in GRAPH_EDGES.get(source, [])
52
+
53
+
54
+ # get next edges
55
+
56
+ def get_next_nodes(
57
+ node_name: str
58
+ ):
59
+ """
60
+ Returns possible next nodes.
61
+ """
62
+
63
+ return GRAPH_EDGES.get(
64
+ node_name,
65
+ []
66
+ )
67
+
68
+
69
+ # validate edges
70
+
71
+ def is_valid_transition(
72
+ source: str,
73
+ destination: str
74
+ ):
75
+ """
76
+ Validates graph transition.
77
+ """
78
+
79
+ return (
80
+ destination
81
+ in GRAPH_EDGES.get(
82
+ source,
83
+ []
84
+ )
85
+ )
@@ -0,0 +1,175 @@
1
+ from pathlib import Path
2
+ from typing import Optional
3
+
4
+ from acra.graph.workflow import (
5
+ omniagent_graph
6
+ )
7
+
8
+
9
+ # graph visualizer
10
+
11
+ class GraphVisualizer:
12
+ """
13
+ Responsibilities:
14
+ - visualize LangGraph workflow
15
+ - export Mermaid diagrams
16
+ - save workflow images
17
+ - support debugging/documentation
18
+ """
19
+
20
+ def __init__(self):
21
+
22
+ self.graph = omniagent_graph
23
+
24
+
25
+ # get mermaid
26
+
27
+ def get_mermaid(self) -> str:
28
+ """
29
+ Returns Mermaid graph definition.
30
+ """
31
+
32
+ try:
33
+
34
+ return (
35
+ self.graph
36
+ .get_graph()
37
+ .draw_mermaid()
38
+ )
39
+
40
+ except Exception as e:
41
+
42
+ return (
43
+ f"Failed to generate Mermaid: {e}"
44
+ )
45
+
46
+
47
+ # save mermaid
48
+
49
+ def save_mermaid(
50
+ self,
51
+ filepath: str
52
+ ) -> str:
53
+ """
54
+ Save Mermaid graph to file.
55
+ """
56
+
57
+ mermaid = self.get_mermaid()
58
+
59
+ Path(filepath).parent.mkdir(
60
+ parents=True,
61
+ exist_ok=True
62
+ )
63
+
64
+ with open(
65
+ filepath,
66
+ "w",
67
+ encoding="utf-8"
68
+ ) as file:
69
+
70
+ file.write(mermaid)
71
+
72
+ return filepath
73
+
74
+
75
+ # save png
76
+
77
+ def save_png(
78
+ self,
79
+ filepath: str
80
+ ) -> str:
81
+ """
82
+ Save workflow graph as PNG.
83
+
84
+ Requires:
85
+ grandalf
86
+ pygraphviz
87
+ OR LangGraph image support
88
+ """
89
+
90
+ try:
91
+
92
+ png_data = (
93
+
94
+ self.graph
95
+ .get_graph()
96
+ .draw_mermaid_png()
97
+
98
+ )
99
+
100
+ Path(filepath).parent.mkdir(
101
+ parents=True,
102
+ exist_ok=True
103
+ )
104
+
105
+ with open(
106
+ filepath,
107
+ "wb"
108
+ ) as file:
109
+
110
+ file.write(
111
+ png_data
112
+ )
113
+
114
+ return filepath
115
+
116
+ except Exception as e:
117
+
118
+ raise RuntimeError(
119
+ f"PNG export failed: {e}"
120
+ )
121
+
122
+
123
+ # display mermaid
124
+
125
+ def print_graph(self):
126
+ """
127
+ Print Mermaid workflow.
128
+ """
129
+
130
+ print(
131
+ self.get_mermaid()
132
+ )
133
+
134
+
135
+ # export documentation
136
+
137
+ def export_documentation(
138
+ self,
139
+ filepath: str
140
+ ) -> str:
141
+ """
142
+ Export graph documentation.
143
+ """
144
+
145
+ mermaid = self.get_mermaid()
146
+
147
+ content = f"""
148
+ # OMNIAGENT Workflow
149
+
150
+ ```mermaid
151
+ {mermaid}
152
+ ```
153
+ """
154
+
155
+ Path(filepath).parent.mkdir(
156
+ parents=True,
157
+ exist_ok=True
158
+ )
159
+
160
+ with open(
161
+ filepath,
162
+ "w",
163
+ encoding="utf-8"
164
+ ) as file:
165
+
166
+ file.write(content)
167
+
168
+ return filepath
169
+
170
+
171
+ # global visualizer
172
+
173
+ graph_visualizer = (
174
+ GraphVisualizer()
175
+ )
acra/graph/nodes.py ADDED
@@ -0,0 +1,68 @@
1
+ from acra.agents.planner.planner_agent import (
2
+ planner_agent
3
+ )
4
+
5
+ from acra.agents.researcher.researcher_agent import (
6
+ researcher_agent
7
+ )
8
+
9
+ from acra.agents.coder.coder_agent import (
10
+ coder_agent
11
+ )
12
+
13
+ from acra.agents.executor.executor_agent import (
14
+ executor_agent
15
+ )
16
+
17
+ from acra.agents.critic.critic_agent import (
18
+ critic_agent
19
+ )
20
+
21
+ from acra.agents.memory.memory_agent import (
22
+ memory_agent
23
+ )
24
+
25
+ from acra.agents.human.human_node import (
26
+ human_node
27
+ )
28
+
29
+
30
+ # node registry
31
+
32
+ NODE_REGISTRY = {
33
+
34
+ "planner": planner_agent,
35
+
36
+ "researcher": researcher_agent,
37
+
38
+ "coder": coder_agent,
39
+
40
+ "executor": executor_agent,
41
+
42
+ "critic": critic_agent,
43
+
44
+ "memory": memory_agent,
45
+
46
+ "human": human_node,
47
+ }
48
+
49
+
50
+ # get nodes
51
+
52
+ def get_node(node_name: str):
53
+ """
54
+ Returns node function.
55
+ """
56
+
57
+ return NODE_REGISTRY.get(node_name)
58
+
59
+ # list nodes
60
+
61
+ def list_nodes():
62
+ """
63
+ Returns all available nodes.
64
+ """
65
+
66
+ return list(
67
+ NODE_REGISTRY.keys()
68
+ )
acra/graph/router.py ADDED
@@ -0,0 +1,69 @@
1
+ import logging
2
+ from langgraph.graph import END
3
+ from acra.graph.edges import validate_transition
4
+
5
+
6
+ # router
7
+
8
+ def route_workflow(state):
9
+ """
10
+ Central workflow router.
11
+
12
+ Reads state["next_agent"]
13
+ and returns the next graph node.
14
+ Enforces retry limits and validates transitions.
15
+ """
16
+
17
+ # Check retry limit
18
+ retry_count = state.get("retry_count", 0)
19
+ max_retries = state.get("max_retries", 5)
20
+ if retry_count >= max_retries:
21
+ logging.getLogger(__name__).warning(
22
+ "Max retries (%d) reached. Ending workflow.", max_retries
23
+ )
24
+ return END
25
+
26
+ next_agent = state.get(
27
+ "next_agent",
28
+ "end"
29
+ )
30
+
31
+ if not next_agent:
32
+
33
+ return END
34
+
35
+ if next_agent.lower() == "end":
36
+
37
+ return END
38
+
39
+ # Validate transition
40
+ current_agent = state.get("current_agent", "")
41
+ if not validate_transition(current_agent, next_agent):
42
+ logging.getLogger(__name__).warning(
43
+ "Unexpected transition from '%s' to '%s'", current_agent, next_agent
44
+ )
45
+ return END
46
+
47
+ return next_agent
48
+
49
+
50
+ # route mapping
51
+
52
+ ROUTE_MAPPING = {
53
+
54
+ "planner": "planner",
55
+
56
+ "researcher": "researcher",
57
+
58
+ "coder": "coder",
59
+
60
+ "executor": "executor",
61
+
62
+ "critic": "critic",
63
+
64
+ "memory": "memory",
65
+
66
+ "human": "human",
67
+
68
+ "end": END
69
+ }
acra/graph/state.py ADDED
@@ -0,0 +1,93 @@
1
+ from typing import List, Dict, Any
2
+
3
+ from langgraph.graph import MessagesState
4
+
5
+
6
+ class AgentState(MessagesState):
7
+ """Workflow state for OmniAgent.
8
+
9
+ TypedDict does not provide runtime defaults, so callers must read with
10
+ state.get(key, default) or populate fields explicitly when a node first
11
+ introduces them.
12
+ """
13
+
14
+ # USER INPUT
15
+
16
+ user_request: str
17
+ session_id: str
18
+
19
+ # PLANNING
20
+
21
+ plan: List[str]
22
+ current_step: str
23
+ completed_steps: List[str]
24
+
25
+ # RESEARCH
26
+
27
+ research_data: List[Any]
28
+ research_status: str
29
+ research_summary: str
30
+ research_sources: List[Any]
31
+ references: List[str]
32
+ implementation_recommendations: str
33
+ recommended_technologies: List[str]
34
+ security_considerations: List[str]
35
+ performance_considerations: List[str]
36
+ architecture_suggestions: List[str]
37
+
38
+ # CODE GENERATION
39
+
40
+ project_name: str
41
+ generated_files: Dict[str, str]
42
+ entry_point: str
43
+ coding_status: str
44
+ coding_explanation: str
45
+
46
+ # EXECUTION
47
+
48
+ execution_logs: str
49
+ execution_success: bool
50
+ execution_output: str
51
+ execution_status: str
52
+ execution_time: float
53
+
54
+ # ERROR HANDLING
55
+
56
+ error_message: str
57
+ retry_count: int
58
+ max_retries: int
59
+
60
+ # CRITIC / REVIEW
61
+
62
+ review_status: str
63
+ critic_feedback: str
64
+ critic_summary: str
65
+ quality_score: float
66
+ review_issues: List[Any]
67
+ security_issues: List[str]
68
+ improvement_suggestions: List[str]
69
+
70
+ # HUMAN-IN-THE-LOOP
71
+
72
+ approval_required: bool
73
+ approved: bool
74
+ human_feedback: str
75
+ interactive: bool
76
+
77
+ # MEMORY
78
+
79
+ memory_context: List[str]
80
+ memory_type: str
81
+ memory_stats: Dict[str, Any]
82
+ previous_attempts: List[Dict[str, Any]]
83
+ similar_failures: List[Any]
84
+
85
+ # WORKFLOW CONTROL
86
+
87
+ next_agent: str
88
+ workflow_status: str
89
+ current_agent: str
90
+
91
+ # OBSERVABILITY
92
+
93
+ token_usage: int