siada-cli 0.0.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 (215) hide show
  1. agent_config.yaml +34 -0
  2. siada/__init__.py +13 -0
  3. siada/agent_hub/__init__.py +10 -0
  4. siada/agent_hub/coder/bug_fix_agent.py +398 -0
  5. siada/agent_hub/coder/code_gen_agent.py +235 -0
  6. siada/agent_hub/coder/fe_gen_agent.py +37 -0
  7. siada/agent_hub/coder/issue_review_agent.py +68 -0
  8. siada/agent_hub/coder/prompt/__init__.py +0 -0
  9. siada/agent_hub/coder/prompt/base/__init__.py +0 -0
  10. siada/agent_hub/coder/prompt/base/capabilities.py +18 -0
  11. siada/agent_hub/coder/prompt/base/non_interactive.py +14 -0
  12. siada/agent_hub/coder/prompt/base/prompt_builder.py +33 -0
  13. siada/agent_hub/coder/prompt/base/rules.py +40 -0
  14. siada/agent_hub/coder/prompt/base/tool_use.py +14 -0
  15. siada/agent_hub/coder/prompt/bug_prompt/bug_fix_prompt.py +51 -0
  16. siada/agent_hub/coder/prompt/bug_prompt/rules.py +53 -0
  17. siada/agent_hub/coder/prompt/code_gen_prompt.py +45 -0
  18. siada/agent_hub/coder/prompt/fe_gen_prompt.py +45 -0
  19. siada/agent_hub/coder/prompt/issue_review_prompt.py +121 -0
  20. siada/agent_hub/coder/tracing/__init__.py +22 -0
  21. siada/agent_hub/coder/tracing/logger_tracing_processor.py +458 -0
  22. siada/agent_hub/hooks/__init__.py +0 -0
  23. siada/agent_hub/hooks/io_hooks.py +30 -0
  24. siada/agent_hub/siada_agent.py +214 -0
  25. siada/config/__init__.py +0 -0
  26. siada/config/config_loader.py +67 -0
  27. siada/entrypoint/__init__.py +3 -0
  28. siada/entrypoint/args_parser/__init__.py +0 -0
  29. siada/entrypoint/args_parser/args.py +195 -0
  30. siada/entrypoint/interaction/__init__.py +0 -0
  31. siada/entrypoint/interaction/base_turn.py +107 -0
  32. siada/entrypoint/interaction/config.py +30 -0
  33. siada/entrypoint/interaction/controller.py +122 -0
  34. siada/entrypoint/interaction/nointeractive_controller.py +27 -0
  35. siada/entrypoint/interaction/turn/interface.py +116 -0
  36. siada/entrypoint/interaction/turn/models.py +34 -0
  37. siada/entrypoint/interaction/turn/run_turn.py +641 -0
  38. siada/entrypoint/interaction/turn_factory.py +35 -0
  39. siada/entrypoint/interaction/turn_models.py +28 -0
  40. siada/entrypoint/siadahub.py +413 -0
  41. siada/foundation/__init__.py +0 -0
  42. siada/foundation/code_agent_context.py +48 -0
  43. siada/foundation/config.py +59 -0
  44. siada/foundation/constants.py +2 -0
  45. siada/foundation/context.py +76 -0
  46. siada/foundation/logging.py +341 -0
  47. siada/foundation/tools/__init__.py +0 -0
  48. siada/foundation/tools/get_git_diff.py +105 -0
  49. siada/io/__init__.py +0 -0
  50. siada/io/banner.py +148 -0
  51. siada/io/color_settings.py +207 -0
  52. siada/io/color_utils.py +16 -0
  53. siada/io/components/__init__.py +0 -0
  54. siada/io/components/mdstream.py +204 -0
  55. siada/io/console_printer.py +68 -0
  56. siada/io/io.py +661 -0
  57. siada/io/key_bindings.py +129 -0
  58. siada/io/notification_command.py +40 -0
  59. siada/io/test_banner.py +64 -0
  60. siada/models/__init__.py +0 -0
  61. siada/models/agent.py +47 -0
  62. siada/models/coder/__init__.py +0 -0
  63. siada/models/converter.py +27 -0
  64. siada/models/model_base_config.py +121 -0
  65. siada/models/model_run_config.py +153 -0
  66. siada/provider/__init__.py +0 -0
  67. siada/provider/client_factory.py +112 -0
  68. siada/provider/llm_client.py +11 -0
  69. siada/provider/openrouter/__init__.py +0 -0
  70. siada/provider/openrouter/coverter.py +17 -0
  71. siada/provider/openrouter/openroute_provider.py +33 -0
  72. siada/provider/provider_factory.py +53 -0
  73. siada/queries/tree-sitter-language-pack/README.md +7 -0
  74. siada/queries/tree-sitter-language-pack/arduino-tags.scm +5 -0
  75. siada/queries/tree-sitter-language-pack/c-tags.scm +9 -0
  76. siada/queries/tree-sitter-language-pack/chatito-tags.scm +16 -0
  77. siada/queries/tree-sitter-language-pack/commonlisp-tags.scm +122 -0
  78. siada/queries/tree-sitter-language-pack/cpp-tags.scm +15 -0
  79. siada/queries/tree-sitter-language-pack/csharp-tags.scm +26 -0
  80. siada/queries/tree-sitter-language-pack/d-tags.scm +26 -0
  81. siada/queries/tree-sitter-language-pack/dart-tags.scm +92 -0
  82. siada/queries/tree-sitter-language-pack/elisp-tags.scm +5 -0
  83. siada/queries/tree-sitter-language-pack/elixir-tags.scm +54 -0
  84. siada/queries/tree-sitter-language-pack/elm-tags.scm +19 -0
  85. siada/queries/tree-sitter-language-pack/gleam-tags.scm +41 -0
  86. siada/queries/tree-sitter-language-pack/go-tags.scm +42 -0
  87. siada/queries/tree-sitter-language-pack/java-tags.scm +20 -0
  88. siada/queries/tree-sitter-language-pack/javascript-tags.scm +88 -0
  89. siada/queries/tree-sitter-language-pack/lua-tags.scm +34 -0
  90. siada/queries/tree-sitter-language-pack/ocaml-tags.scm +115 -0
  91. siada/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +98 -0
  92. siada/queries/tree-sitter-language-pack/pony-tags.scm +39 -0
  93. siada/queries/tree-sitter-language-pack/properties-tags.scm +5 -0
  94. siada/queries/tree-sitter-language-pack/python-tags.scm +14 -0
  95. siada/queries/tree-sitter-language-pack/r-tags.scm +21 -0
  96. siada/queries/tree-sitter-language-pack/racket-tags.scm +12 -0
  97. siada/queries/tree-sitter-language-pack/ruby-tags.scm +64 -0
  98. siada/queries/tree-sitter-language-pack/rust-tags.scm +60 -0
  99. siada/queries/tree-sitter-language-pack/solidity-tags.scm +43 -0
  100. siada/queries/tree-sitter-language-pack/swift-tags.scm +51 -0
  101. siada/queries/tree-sitter-language-pack/udev-tags.scm +20 -0
  102. siada/queries/tree-sitter-languages/README.md +23 -0
  103. siada/queries/tree-sitter-languages/c-tags.scm +9 -0
  104. siada/queries/tree-sitter-languages/c_sharp-tags.scm +46 -0
  105. siada/queries/tree-sitter-languages/cpp-tags.scm +15 -0
  106. siada/queries/tree-sitter-languages/dart-tags.scm +91 -0
  107. siada/queries/tree-sitter-languages/elisp-tags.scm +8 -0
  108. siada/queries/tree-sitter-languages/elixir-tags.scm +54 -0
  109. siada/queries/tree-sitter-languages/elm-tags.scm +19 -0
  110. siada/queries/tree-sitter-languages/go-tags.scm +30 -0
  111. siada/queries/tree-sitter-languages/hcl-tags.scm +77 -0
  112. siada/queries/tree-sitter-languages/java-tags.scm +20 -0
  113. siada/queries/tree-sitter-languages/javascript-tags.scm +88 -0
  114. siada/queries/tree-sitter-languages/kotlin-tags.scm +27 -0
  115. siada/queries/tree-sitter-languages/ocaml-tags.scm +115 -0
  116. siada/queries/tree-sitter-languages/ocaml_interface-tags.scm +98 -0
  117. siada/queries/tree-sitter-languages/php-tags.scm +26 -0
  118. siada/queries/tree-sitter-languages/python-tags.scm +12 -0
  119. siada/queries/tree-sitter-languages/ql-tags.scm +26 -0
  120. siada/queries/tree-sitter-languages/ruby-tags.scm +64 -0
  121. siada/queries/tree-sitter-languages/rust-tags.scm +60 -0
  122. siada/queries/tree-sitter-languages/scala-tags.scm +65 -0
  123. siada/queries/tree-sitter-languages/typescript-tags.scm +41 -0
  124. siada/services/__init__.py +30 -0
  125. siada/services/at_file_recommendation_design_document.md +1213 -0
  126. siada/services/code_context_manager.py +136 -0
  127. siada/services/enhanced_fix_result_check.py +546 -0
  128. siada/services/execution_trace_collector.py +207 -0
  129. siada/services/file_recommendation/__init__.py +136 -0
  130. siada/services/file_recommendation/core/__init__.py +17 -0
  131. siada/services/file_recommendation/core/completion_engine.py +351 -0
  132. siada/services/file_recommendation/core/config.py +62 -0
  133. siada/services/file_recommendation/core/file_discovery.py +353 -0
  134. siada/services/file_recommendation/core/suggestion.py +110 -0
  135. siada/services/file_recommendation/utils/__init__.py +22 -0
  136. siada/services/file_recommendation/utils/path_utils.py +138 -0
  137. siada/services/file_recommendation/utils/text_utils.py +253 -0
  138. siada/services/fix_result_check.py +377 -0
  139. siada/services/handle_at_command/README.md +247 -0
  140. siada/services/handle_at_command/__init__.py +115 -0
  141. siada/services/handle_at_command/exceptions.py +46 -0
  142. siada/services/handle_at_command/models.py +92 -0
  143. siada/services/handle_at_command/parser.py +189 -0
  144. siada/services/handle_at_command/processor.py +321 -0
  145. siada/services/handle_at_command/resolver.py +318 -0
  146. siada/services/handle_at_command/utils.py +345 -0
  147. siada/services/model_info_service.py +103 -0
  148. siada/services/siada_runner.py +170 -0
  149. siada/session/__init__.py +29 -0
  150. siada/session/session_manager.py +66 -0
  151. siada/session/session_models.py +43 -0
  152. siada/support/__init__.py +0 -0
  153. siada/support/completer.py +204 -0
  154. siada/support/editor.py +146 -0
  155. siada/support/envprocessor.py +54 -0
  156. siada/support/repo.py +51 -0
  157. siada/support/slash_commands.py +410 -0
  158. siada/support/spinner.py +320 -0
  159. siada/tools/__init__.py +0 -0
  160. siada/tools/ast/README.md +345 -0
  161. siada/tools/ast/ast_tool.py +341 -0
  162. siada/tools/ast/models.py +11 -0
  163. siada/tools/coder/__init__.py +0 -0
  164. siada/tools/coder/ask_followup_question.py +70 -0
  165. siada/tools/coder/cmd_runner.py +132 -0
  166. siada/tools/coder/file_operator.py +284 -0
  167. siada/tools/coder/file_search/README.md +223 -0
  168. siada/tools/coder/file_search/__init__.py +10 -0
  169. siada/tools/coder/file_search/bin/rg-linux-arm64 +0 -0
  170. siada/tools/coder/file_search/bin/rg-linux-x64 +0 -0
  171. siada/tools/coder/file_search/bin/rg-macos-arm64 +0 -0
  172. siada/tools/coder/file_search/bin/rg-macos-x64 +0 -0
  173. siada/tools/coder/file_search/bin/rg.exe +0 -0
  174. siada/tools/coder/file_search/search.py +437 -0
  175. siada/tools/coder/file_search/setup_package.py +150 -0
  176. siada/tools/coder/file_search/test_search.py +142 -0
  177. siada/tools/coder/files.py +147 -0
  178. siada/tools/coder/fix_attempt_completion.py +58 -0
  179. siada/tools/coder/issue_review_completion.py +29 -0
  180. siada/tools/coder/observation/__init__.py +0 -0
  181. siada/tools/coder/observation/error.py +22 -0
  182. siada/tools/coder/observation/file_observation.py +216 -0
  183. siada/tools/coder/observation/observation.py +71 -0
  184. siada/tools/coder/repo_map/__init__.py +157 -0
  185. siada/tools/coder/repo_map/dump.py +29 -0
  186. siada/tools/coder/repo_map/io.py +226 -0
  187. siada/tools/coder/repo_map/repo_map.py +881 -0
  188. siada/tools/coder/repo_map/special.py +203 -0
  189. siada/tools/coder/repo_map/token_counter.py +154 -0
  190. siada/tools/coder/repo_map/waiting.py +221 -0
  191. siada/tools/coder/reproduce_completion.py +39 -0
  192. siada/tools/coder/run_cmd.py +60 -0
  193. siada/tools/coder/test_completion.py +25 -0
  194. siada/tools/coder/tool_docs.py +44 -0
  195. siada/tools/compression_tool.py +200 -0
  196. siada/tools/read_many_files/README.md +220 -0
  197. siada/tools/read_many_files/__init__.py +25 -0
  198. siada/tools/read_many_files/file_processor.py +486 -0
  199. siada/tools/read_many_files/filters.py +326 -0
  200. siada/tools/read_many_files/formatters.py +204 -0
  201. siada/tools/read_many_files/models.py +189 -0
  202. siada/tools/read_many_files_tool.py +233 -0
  203. siada/tools/tool_call_format/__init__.py +55 -0
  204. siada/tools/tool_call_format/file_to_language.py +231 -0
  205. siada/tools/tool_call_format/formatter_factory.py +80 -0
  206. siada/tools/tool_call_format/formatters.py +282 -0
  207. siada/tools/tool_call_format/tool_call_formatter.py +54 -0
  208. siada/tools/web/__init__.py +0 -0
  209. siada/tools/web/web_crawl.py +208 -0
  210. siada/utils.py +194 -0
  211. siada_cli-0.0.1.dist-info/LICENSE +201 -0
  212. siada_cli-0.0.1.dist-info/METADATA +457 -0
  213. siada_cli-0.0.1.dist-info/RECORD +215 -0
  214. siada_cli-0.0.1.dist-info/WHEEL +4 -0
  215. siada_cli-0.0.1.dist-info/entry_points.txt +3 -0
agent_config.yaml ADDED
@@ -0,0 +1,34 @@
1
+ # Agent Configuration File
2
+ # Defines the mapping from agent names to Agent classes
3
+
4
+ agents:
5
+ # Bug Fix Agent
6
+ bugfix:
7
+ class: "siada.agent_hub.coder.bug_fix_agent.BugFixAgent"
8
+ description: "Specialized agent for code bug fixing"
9
+ enabled: true
10
+
11
+ # Code Development Agent (not implemented yet)
12
+ coder:
13
+ class: "siada.agent_hub.coder.code_gen_agent.CodeGenAgent"
14
+ description: "General-purpose code development agent"
15
+ enabled: true
16
+
17
+ # Frontend Generation Agent
18
+ fegen:
19
+ class: "siada.agent_hub.coder.fe_gen_agent.FeGenAgent"
20
+ description: "Frontend code generation agent"
21
+ enabled: false
22
+
23
+ # LLM Configuration
24
+ llm_config:
25
+ provider: "openrouter" # Optional:“openrouter”
26
+ model_name: "claude-sonnet-4"
27
+ repo_map_tokens: 8192 # repo map max token
28
+ repo_map_mul_no_files: 16 # Multiple without chat files
29
+ repo_verbose: true # Enable detailed output or not
30
+
31
+ # Configuration Options:
32
+ # - class: Full import path of the Agent class, null means not implemented yet
33
+ # - description: Description of the agent
34
+ # - enabled: Whether the agent is enabled, false means temporarily disabled
siada/__init__.py ADDED
@@ -0,0 +1,13 @@
1
+ try:
2
+ from importlib.metadata import version
3
+ except ImportError:
4
+ # Python < 3.8 compatibility
5
+ from importlib_metadata import version
6
+
7
+ try:
8
+ __version__ = version("siada-cli")
9
+ except Exception:
10
+ # Fallback for development environment
11
+ __version__ = "dev"
12
+
13
+ __all__ = ["__version__"]
@@ -0,0 +1,10 @@
1
+ """
2
+ Agent模块
3
+
4
+ 提供预定义的Agent实例和初始化功能
5
+ """
6
+ from typing import Dict, Any
7
+
8
+
9
+ # 存储创建的Agent,key是agent名称,value是agent实例
10
+ agent_map: Dict[str, Any] = {}
@@ -0,0 +1,398 @@
1
+ import ast
2
+ from datetime import datetime
3
+ import os
4
+
5
+ from agents import RunContextWrapper, RunConfig, RunResult, RunResultStreaming, Runner
6
+
7
+ from siada.agent_hub.coder.code_gen_agent import CodeGenAgent
8
+ from siada.agent_hub.coder.issue_review_agent import IssueReviewAgent
9
+ from siada.agent_hub.coder.prompt.bug_prompt import bug_fix_prompt
10
+ from siada.foundation.code_agent_context import CodeAgentContext
11
+ from siada.foundation.config import settings
12
+ from siada.foundation.tools.get_git_diff import GitDiffUtil
13
+ from siada.services.fix_result_check import FixResultChecker
14
+ from siada.services.execution_trace_collector import ExecutionTrace, ModelCall, ToolCall
15
+ from siada.tools.ast.ast_tool import list_code_definition_names
16
+ from siada.tools.coder.file_operator import edit
17
+ from siada.tools.coder.file_search import regex_search_files
18
+ from siada.tools.coder.run_cmd import run_cmd
19
+ from siada.tools.coder.fix_attempt_completion import fix_attempt_completion
20
+ from siada.services.enhanced_fix_result_check import EnhancedFixResultChecker
21
+ from typing import Optional, List, Dict, Any
22
+ from openai.types.responses import ResponseFunctionToolCall, ResponseOutputMessage
23
+
24
+
25
+ class BugFixAgent(CodeGenAgent):
26
+ fix_result_checker: FixResultChecker
27
+ enhanced_fix_result_checker: EnhancedFixResultChecker
28
+ issue_review_agent: IssueReviewAgent # Forward declaration for type hinting
29
+
30
+ def __init__(self, *args, **kwargs):
31
+
32
+ self.fix_result_checker = FixResultChecker()
33
+ self.issue_review_agent = IssueReviewAgent()
34
+ self.enhanced_fix_result_checker = EnhancedFixResultChecker()
35
+
36
+ super().__init__(
37
+ name="BugFixAgent",
38
+ tools=[edit, regex_search_files, run_cmd, fix_attempt_completion, list_code_definition_names],
39
+ tool_use_behavior={
40
+ "stop_at_tool_names": ["fix_attempt_completion"],
41
+ },
42
+ *args,
43
+ **kwargs,
44
+ )
45
+
46
+ async def get_system_prompt(
47
+ self, run_context: RunContextWrapper[CodeAgentContext]
48
+ ) -> str | None:
49
+ root_dir = run_context.context.root_dir
50
+ interactive_mode = run_context.context.interactive_mode
51
+ system_prompt = bug_fix_prompt.get_system_prompt(root_dir, interactive_mode)
52
+ return system_prompt
53
+
54
+ async def get_context(self) -> CodeAgentContext:
55
+ current_working_dir = os.getcwd()
56
+ interactive_mode = self.get_interactive_mode()
57
+
58
+ context = CodeAgentContext(
59
+ root_dir=current_working_dir,
60
+ interactive_mode=interactive_mode
61
+ )
62
+
63
+ # 将 context 值赋给 model 对象
64
+ if hasattr(self, "model") and hasattr(self.model, "context"):
65
+ self.model.context = context
66
+
67
+ return context
68
+
69
+ async def run(self, user_input: str, context: CodeAgentContext) -> RunResult:
70
+ """
71
+ Execute bug fixing task.
72
+ Use reproduce_agent to reproduce the issue, then use current Agent to fix it.
73
+
74
+ Args:
75
+ user_input: User-described bug problem, including error messages, related file paths, etc.
76
+ context: Context object for providing contextual information
77
+ Returns:
78
+ Fix result, including final output, execution rounds, and other information
79
+ """
80
+ #config = RunConfig(tracing_disabled=False)
81
+ #set_trace_processors([create_detailed_logger()])
82
+ input_with_env = self.assemble_user_input(user_input, context)
83
+
84
+ max_turns = 3
85
+ current_turn = 0
86
+ task_message = {"content": input_with_env, "role": "user"}
87
+ input_list = [task_message]
88
+
89
+ run_config, _ = await self.prepare_run_environment(context)
90
+
91
+ while current_turn < max_turns:
92
+ # Run BugFixAgent for fixing
93
+ result = await Runner.run(
94
+ starting_agent=self,
95
+ input=input_list,
96
+ max_turns=settings.MAX_TURNS,
97
+ run_config=run_config,
98
+ context=context
99
+ )
100
+
101
+ # input_list = result.to_input_list()
102
+
103
+ # Check if the issue is fixed using run_checker
104
+ try:
105
+ check_result = await self.run_checker(user_input, context)
106
+ enhance_check_result = await self.run_enhanced_checker(
107
+ user_input, context, result
108
+ )
109
+
110
+ if check_result.get("is_fixed", False):
111
+ # Issue is fixed, break the loop
112
+ print(
113
+ f"Fix_check_result, Issue fixed: {check_result.get('check_summary', 'Fix verified')}"
114
+ )
115
+ break
116
+ else:
117
+ # Issue not fixed, add the check_summary to input_list for next iteration
118
+ check_summary = check_result.get(
119
+ "check_summary", "Fix verification failed"
120
+ )
121
+ print(
122
+ f"Fix_check_result, Issue not fixed, continue fixing (round {current_turn + 1}): {check_summary}"
123
+ )
124
+
125
+ # Add the unfixed check_summary to input_list for next round
126
+ feedback_message = {
127
+ "content": self._build_enhanced_feedback(
128
+ current_turn,
129
+ result,
130
+ check_result,
131
+ check_summary,
132
+ enhance_check_result,
133
+ ),
134
+ "role": "user",
135
+ }
136
+ # feedback_message = {
137
+ # "content": f"Here is the previous fix logic:\n{result.final_output}"
138
+ # f"Here is the current code diff:\n{check_result.get('code_diff', '')}"
139
+ # f"But previous fix attempt was not sufficient. Reason: {check_summary}.\n"
140
+ # f"**Please continue fixing.**",
141
+ # "role": "user",
142
+ # }
143
+ input_list = [task_message, feedback_message]
144
+ except Exception as e:
145
+ # If checker fails, log error and continue to next round
146
+ print(f"Fix result checker failed: {e}, stopping verification.")
147
+ break
148
+
149
+ current_turn += 1
150
+
151
+ return result
152
+
153
+ async def run_checker_by_agent(self, user_input: str, context: CodeAgentContext):
154
+
155
+ result = await self.issue_review_agent.run(user_input, context)
156
+
157
+ output = ast.literal_eval(result.final_output)
158
+ return output
159
+
160
+ async def run_checker(self, user_input: str, context: CodeAgentContext) -> dict:
161
+
162
+ diff_patch = GitDiffUtil.get_git_diff_exclude_test_files(context.root_dir)
163
+
164
+ check_result = await self.fix_result_checker.check(
165
+ issue_desc=user_input,
166
+ fix_code=diff_patch,
167
+ context=context
168
+ )
169
+
170
+ check_result["code_diff"] = diff_patch
171
+ return check_result
172
+
173
+
174
+ async def run_enhanced_checker(
175
+ self,
176
+ user_input: str,
177
+ context: CodeAgentContext,
178
+ run_result: Optional[RunResult] = None,
179
+ ) -> dict:
180
+ """
181
+
182
+ Args:
183
+ user_input: problem description provided by the user
184
+ context: to get the root directory and other context informatio
185
+ run_result: for extracting execution trace if available
186
+
187
+ Returns:
188
+ the enhanced check result, which includes:
189
+ - check_summary: Summary of the fix result check
190
+ - execution_stats: Statistics about model and tool calls during the fix
191
+ - strategy_suggestions: Suggestions for improving the fixing strategy
192
+ - trace_analysis: Analysis of the execution trace if available
193
+ """
194
+
195
+ diff_patch = GitDiffUtil.get_git_diff_exclude_test_files(context.root_dir)
196
+ execution_trace = None
197
+ if run_result:
198
+ try:
199
+ execution_trace = self._extract_execution_trace_from_run_result(run_result)
200
+ except Exception as e:
201
+ print(f"Failed to extract execution trace: {e}")
202
+ execution_trace = None
203
+
204
+ enhanced_result = await self.enhanced_fix_result_checker.check_with_trace(
205
+ issue_desc=user_input,
206
+ fix_code=diff_patch,
207
+ context=context,
208
+ execution_trace=execution_trace,
209
+ )
210
+
211
+ enhanced_result["code_diff"] = diff_patch
212
+ return enhanced_result
213
+
214
+ def run_streamed(
215
+ self, user_input: str, context: CodeAgentContext
216
+ ) -> RunResultStreaming:
217
+ """
218
+ Execute bug fixing task in streaming mode
219
+
220
+ Args:
221
+ user_input: User-described bug problem, including error messages, related file paths, etc.
222
+ context: Context object for providing contextual information
223
+ Returns:
224
+ Fix result, including final output, execution rounds, and other information
225
+ """
226
+ pass
227
+
228
+ def _extract_execution_trace_from_run_result(
229
+ self, run_result: RunResult
230
+ ) -> Optional[Dict[str, Any]]:
231
+ """
232
+ Extract execution trace from RunResult if available
233
+ """
234
+ model_calls = []
235
+ tool_calls = []
236
+
237
+ if hasattr(run_result, "new_items"):
238
+ new_items = run_result.new_items
239
+ else:
240
+ raise ValueError("RunResult does not have 'new_items' attribute")
241
+
242
+ for i in range(len(new_items)):
243
+ item = new_items[i]
244
+
245
+ if hasattr(item, "type"):
246
+ item_type = item.type
247
+ else:
248
+ raise ValueError("Item does not have 'type' attribute")
249
+
250
+ if item_type == "message_output_item":
251
+ raw_item = item.raw_item
252
+ model_calls.append(
253
+ self._extract_model_call_from_item(len(model_calls) + 1, raw_item)
254
+ )
255
+ elif item_type == "tool_call_output_item":
256
+ tool_call_output_raw_item = item.raw_item
257
+ if i > 0 and new_items[i - 1].type == "tool_call_item":
258
+ tool_calls_raw_item = new_items[i - 1].raw_item
259
+ else:
260
+ raise ValueError(
261
+ "ToolCallOutputItem does not have a preceding ToolCallItem"
262
+ )
263
+ tool_calls.append(
264
+ self._extract_tool_call_from_item(
265
+ len(tool_calls) + 1,
266
+ tool_call_output_raw_item,
267
+ tool_calls_raw_item,
268
+ )
269
+ )
270
+
271
+ return ExecutionTrace(
272
+ trace_id=f"trace_{datetime.now().strftime('%Y%m%d_%H%M%S')}",
273
+ workflow_name="bug_fix",
274
+ start_time=datetime.now(),
275
+ model_calls=model_calls,
276
+ tool_calls=tool_calls,
277
+ )
278
+
279
+ def _extract_model_call_from_item(
280
+ self, call_id: int, raw_item: ResponseOutputMessage
281
+ ) -> ModelCall:
282
+ model_call = ModelCall(
283
+ call_id=call_id,
284
+ model=None,
285
+ input_messages="",
286
+ output_messages=[{"role": "assistant", "content": str(raw_item.content)}],
287
+ usage=None,
288
+ timestamp=datetime.now(),
289
+ duration_ms=0
290
+ )
291
+ return model_call
292
+
293
+ def _extract_tool_call_from_item(
294
+ self,
295
+ call_id: int,
296
+ tool_call_output_raw_item: dict,
297
+ tool_call_raw_item: ResponseFunctionToolCall,
298
+ ) -> ModelCall:
299
+ tool_call = ToolCall(
300
+ call_id=call_id,
301
+ tool_name="",
302
+ timestamp=datetime.now(),
303
+ duration_ms=0,
304
+ success=True,
305
+ error_message=None,
306
+ input_args=tool_call_raw_item.arguments,
307
+ output_result=(
308
+ tool_call_output_raw_item.get("output", None)
309
+ ),
310
+ )
311
+
312
+ return tool_call
313
+
314
+ def _build_enhanced_feedback(
315
+ self,
316
+ current_turn: int,
317
+ result: dict,
318
+ check_result: dict,
319
+ check_summary: str,
320
+ enhanced_check_result: dict,
321
+ ) -> str:
322
+ """
323
+ Build enhanced feedback message for the next round of fixing
324
+
325
+ Args:
326
+ current_turn: Current turn number
327
+ result: Current run result
328
+ check_result: Result from the fix result checker
329
+ check_summary: Summary of the fix result check
330
+ enhanced_check_result: Result from the enhanced fix result checker
331
+
332
+ Returns:
333
+ Formatted feedback message for the next round
334
+ """
335
+
336
+ enhanced_feedback_content = f"""## Previous Fix Attempt (Round {current_turn + 1})
337
+ **Fix Logic:**
338
+ {result.final_output}
339
+
340
+ **Current Code Diff:**
341
+ {check_result.get('code_diff', 'No changes detected')}
342
+
343
+ ## Primary Check Result
344
+ **Fix Status:** ❌ Not Fixed
345
+ **Primary Analysis:** {check_summary}
346
+
347
+ ## Enhanced Analysis"""
348
+
349
+ overall_score = enhanced_check_result.get("overall_score")
350
+ if overall_score is not None:
351
+ enhanced_feedback_content += f"\n**Quality Score:** {overall_score:.1f}/10"
352
+
353
+ enhanced_summary = enhanced_check_result.get("check_summary")
354
+ if enhanced_summary:
355
+ enhanced_feedback_content += f"\n**Enhanced Summary:** {enhanced_summary}"
356
+
357
+ execution_stats = enhanced_check_result.get("execution_stats")
358
+ if execution_stats:
359
+ enhanced_feedback_content += f"""
360
+
361
+ **Execution Statistics:**
362
+ - Model calls: {execution_stats.get('model_calls', 0)}
363
+ - Tool calls: {execution_stats.get('tool_calls', 0)}"""
364
+
365
+ strategy_suggestions = self._extract_strategy_suggestions(enhanced_check_result)
366
+ if strategy_suggestions:
367
+ enhanced_feedback_content += "\n\n**Strategy Improvement Suggestions:"
368
+ for i, suggestion in enumerate(strategy_suggestions[:3], 1):
369
+ enhanced_feedback_content += f"\n{i}. {suggestion}"
370
+
371
+ trace_analysis = enhanced_check_result.get("trace_analysis")
372
+ if trace_analysis:
373
+ enhanced_feedback_content += (
374
+ f"\n\n**Execution Trace Analysis:**\n{trace_analysis}"
375
+ )
376
+
377
+ enhanced_feedback_content += f"""
378
+
379
+ ## Next Steps
380
+ The previous fix attempt was not sufficient. Please analyze the above feedback and continue fixing the issue.
381
+ **Primary Focus:** {check_summary}"""
382
+
383
+ fix_analysis = enhanced_check_result.get("fix_analysis")
384
+ if fix_analysis:
385
+ enhanced_feedback_content += f"\n**Additional Focus:** {fix_analysis}"
386
+
387
+ return enhanced_feedback_content
388
+
389
+ def _extract_strategy_suggestions(self, enhanced_check_result: dict) -> List[str]:
390
+ prof_rec = enhanced_check_result.get("professional_recommendations", {})
391
+ strategic_improvements = prof_rec.get("strategic_improvements", [])
392
+
393
+ if not strategic_improvements:
394
+ strategic_improvements = enhanced_check_result.get(
395
+ "strategy_suggestions", []
396
+ )
397
+
398
+ return strategic_improvements
@@ -0,0 +1,235 @@
1
+ """
2
+ Code Generation Agent Module
3
+
4
+ Provides specialized Agent implementation for code generation tasks.
5
+ """
6
+ import os
7
+
8
+ from agents import RunContextWrapper, RunResult, RunResultStreaming
9
+ from siada.foundation.code_agent_context import CodeAgentContext
10
+ from siada.agent_hub.siada_agent import SiadaAgent
11
+ from siada.tools.ast.ast_tool import list_code_definition_names
12
+ from siada.tools.coder.file_operator import edit
13
+ from siada.tools.coder.file_search import regex_search_files
14
+ from siada.tools.coder.run_cmd import run_cmd
15
+ from siada.foundation.config import settings
16
+ from siada.agent_hub.coder.prompt import code_gen_prompt
17
+ from siada.services.handle_at_command import handle_at_command
18
+ import logging
19
+
20
+
21
+ logging.getLogger().setLevel(logging.INFO)
22
+
23
+ class CodeGenAgent(SiadaAgent[CodeAgentContext]):
24
+ """
25
+ Code Generation Agent
26
+
27
+ Specialized Agent implementation for code generation tasks.
28
+ """
29
+
30
+ def __init__(self, *args, **kwargs):
31
+
32
+ if 'name' not in kwargs:
33
+ kwargs['name'] = "CodeGenAgent"
34
+
35
+ if 'tools' not in kwargs:
36
+ kwargs['tools'] = [edit, regex_search_files, run_cmd, list_code_definition_names]
37
+
38
+ super().__init__(
39
+ *args,
40
+ **kwargs
41
+ )
42
+
43
+ async def get_system_prompt(self, run_context: RunContextWrapper[CodeAgentContext]) -> str | None:
44
+ root_dir = run_context.context.root_dir
45
+ interactive_mode = run_context.context.interactive_mode
46
+ system_prompt = code_gen_prompt.get_system_prompt(root_dir, interactive_mode)
47
+ return system_prompt
48
+
49
+ async def get_context(self) -> CodeAgentContext:
50
+ current_working_dir = os.getcwd()
51
+ interactive_mode = self.get_interactive_mode()
52
+
53
+ context = CodeAgentContext(
54
+ root_dir=current_working_dir,
55
+ interactive_mode=interactive_mode
56
+ )
57
+ return context
58
+
59
+ async def process_at_commands(self, user_input: str, context: CodeAgentContext) -> str:
60
+ """
61
+ Process @ commands in user input and return processed input
62
+
63
+ Args:
64
+ user_input: Original user input that may contain @ commands
65
+ context: Code agent context
66
+
67
+ Returns:
68
+ Processed user input with @ command content injected
69
+ """
70
+ try:
71
+ # Check if input contains @ commands
72
+ if '@' not in user_input:
73
+ return user_input
74
+
75
+ # Create configuration object for at command processing
76
+ class AtCommandConfig:
77
+ def __init__(self, root_dir: str):
78
+ self.root_dir = root_dir
79
+
80
+ config = AtCommandConfig(context.root_dir)
81
+
82
+ # Create callback functions
83
+ def add_item(item, message_id):
84
+ # Log the item for debugging
85
+ logging.debug(f"AtCommand item added: {item}")
86
+
87
+ def on_debug_message(message):
88
+ # Log debug messages
89
+ logging.debug(f"AtCommand debug: {message}")
90
+
91
+ # Process at commands
92
+ result = await handle_at_command(
93
+ query=user_input,
94
+ config=config,
95
+ add_item=add_item,
96
+ on_debug_message=on_debug_message,
97
+ message_id=1
98
+ )
99
+
100
+ if result.should_proceed and result.processed_query:
101
+ # Combine all text parts from processed query
102
+ processed_text = ""
103
+ for part in result.processed_query:
104
+ if isinstance(part, dict) and 'text' in part:
105
+ processed_text += part['text']
106
+
107
+ return processed_text.strip() if processed_text else user_input
108
+ else:
109
+ # If processing failed, return original input
110
+ return user_input
111
+
112
+ except Exception as e:
113
+ # If any error occurs, log it and return original input
114
+ logging.warning(f"Failed to process @ commands: {e}")
115
+ return user_input
116
+
117
+ async def run(self, user_input: str, context: CodeAgentContext) -> RunResult:
118
+ """
119
+ Execute code generation task.
120
+
121
+ Args:
122
+ user_input: User's code generation request with requirements and specifications
123
+ context: Context object providing project information
124
+ Returns:
125
+ Generation result containing final output and execution details
126
+ """
127
+
128
+ # Process @ commands first
129
+ processed_input = await self.process_at_commands(user_input, context)
130
+
131
+ input_with_env = self.assemble_user_input(processed_input, context)
132
+ result = await self.run_impl(
133
+ starting_agent=self,
134
+ input=input_with_env,
135
+ max_turns=settings.MAX_TURNS,
136
+ context=context,
137
+ )
138
+
139
+ return result
140
+
141
+ async def run_streamed(
142
+ self, user_input: str, context: CodeAgentContext
143
+ ) -> RunResultStreaming:
144
+ """
145
+ Execute code generation task with streaming output.
146
+
147
+ Args:
148
+ user_input: User's code generation request with requirements and specifications
149
+ context: Context object providing project information
150
+ Returns:
151
+ A streaming result of the generation, containing final output and execution details.
152
+ """
153
+
154
+ # Process @ commands first
155
+ processed_input = await self.process_at_commands(user_input, context)
156
+
157
+ input_with_env = self.assemble_user_input(processed_input, context)
158
+ result = await self.run_streamed_impl(
159
+ starting_agent=self,
160
+ input=input_with_env,
161
+ context=context,
162
+ max_turns=settings.MAX_TURNS,
163
+ )
164
+
165
+ return result
166
+
167
+ def assemble_user_input(self, user_input: str, context: CodeAgentContext) -> str:
168
+ task = f'<task>\n{user_input}\n</task>'
169
+ return task
170
+ # repo_map_content = self.generate_repo_map(context)
171
+
172
+ # if repo_map_content:
173
+ # project_structure = f"Repository Map:\n{repo_map_content}"
174
+ # else:
175
+ # project_structure = "Repository Map: Unable to generate repository map"
176
+
177
+ # environment_details = f'<environment_details>\n{project_structure}\n</environment_details>'
178
+ # return task + '\n' + environment_details
179
+
180
+ def generate_repo_map(self, context: CodeAgentContext) -> str:
181
+ """
182
+ Generate repository map for project structure analysis.
183
+
184
+ Args:
185
+ context: Code agent context containing project information
186
+
187
+ Returns:
188
+ Repository map content as string
189
+ """
190
+ try:
191
+ if not context.root_dir:
192
+ return ""
193
+
194
+ repo_map = self.get_repo_map_instance(context.root_dir)
195
+ if not repo_map:
196
+ return ""
197
+
198
+ python_files = []
199
+ for root, dirs, files in os.walk(context.root_dir):
200
+ dirs[:] = [d for d in dirs if not d.startswith('.') and d not in [
201
+ '__pycache__', 'node_modules', '.git', '.venv', 'venv', 'env'
202
+ ]]
203
+
204
+ for file in files:
205
+ if file.endswith('.py') and not file.startswith('.'):
206
+ filepath = os.path.join(root, file)
207
+ python_files.append(filepath)
208
+
209
+ substantial_files = []
210
+ for filepath in python_files:
211
+ try:
212
+ with open(filepath, 'r', encoding='utf-8') as f:
213
+ content = f.read().strip()
214
+ if len(content) > 100:
215
+ lines = [line.strip() for line in content.split('\n') if line.strip()]
216
+ non_comment_lines = [line for line in lines if not line.startswith('#')]
217
+ if len(non_comment_lines) > 5:
218
+ substantial_files.append(filepath)
219
+ except Exception:
220
+ continue
221
+
222
+ if len(substantial_files) > 50:
223
+ substantial_files = substantial_files[:50]
224
+
225
+ result = repo_map.get_repo_map(
226
+ chat_files=[],
227
+ other_files=substantial_files,
228
+ mentioned_fnames=set(),
229
+ mentioned_idents=set(['class', 'def', 'function'])
230
+ )
231
+
232
+ return result or ""
233
+
234
+ except Exception as e:
235
+ return f"Generate repo map failed: {str(e)}"