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
@@ -0,0 +1,231 @@
1
+ import os
2
+ from typing import Optional
3
+
4
+
5
+ def get_language_from_file_extension(filename: str) -> str:
6
+ """
7
+ Get the language identifier for markdown code fences based on file extension.
8
+
9
+ Args:
10
+ filename: The name of the file (can include path)
11
+
12
+ Returns:
13
+ Language identifier string for markdown code fences
14
+ """
15
+ if not filename:
16
+ return ""
17
+
18
+ # Extract the file extension (convert to lowercase for case-insensitive matching)
19
+ _, ext = os.path.splitext(filename.lower())
20
+
21
+ # Remove the leading dot
22
+ ext = ext.lstrip('.')
23
+
24
+ # Mapping of file extensions to language identifiers
25
+ extension_to_language = {
26
+ # Python
27
+ 'py': 'python',
28
+ 'pyw': 'python',
29
+ 'pyi': 'python',
30
+
31
+ # JavaScript/TypeScript
32
+ 'js': 'javascript',
33
+ 'mjs': 'javascript',
34
+ 'jsx': 'jsx',
35
+ 'ts': 'typescript',
36
+ 'tsx': 'tsx',
37
+
38
+ # Java/JVM languages
39
+ 'java': 'java',
40
+ 'kt': 'kotlin',
41
+ 'kts': 'kotlin',
42
+ 'scala': 'scala',
43
+ 'sc': 'scala',
44
+ 'groovy': 'groovy',
45
+ 'gradle': 'gradle',
46
+
47
+ # C/C++
48
+ 'c': 'c',
49
+ 'h': 'c',
50
+ 'cpp': 'cpp',
51
+ 'cxx': 'cpp',
52
+ 'cc': 'cpp',
53
+ 'c++': 'cpp',
54
+ 'hpp': 'cpp',
55
+ 'hxx': 'cpp',
56
+ 'hh': 'cpp',
57
+
58
+ # C#
59
+ 'cs': 'csharp',
60
+
61
+ # Go
62
+ 'go': 'go',
63
+
64
+ # Rust
65
+ 'rs': 'rust',
66
+
67
+ # PHP
68
+ 'php': 'php',
69
+ 'php3': 'php',
70
+ 'php4': 'php',
71
+ 'php5': 'php',
72
+ 'phtml': 'php',
73
+
74
+ # Ruby
75
+ 'rb': 'ruby',
76
+ 'rbw': 'ruby',
77
+ 'rake': 'ruby',
78
+ 'gemspec': 'ruby',
79
+
80
+ # Swift
81
+ 'swift': 'swift',
82
+
83
+ # Objective-C
84
+ 'm': 'objective-c',
85
+ 'mm': 'objective-c',
86
+
87
+ # Dart
88
+ 'dart': 'dart',
89
+
90
+ # R
91
+ 'r': 'r',
92
+
93
+ # Shell scripting
94
+ 'sh': 'bash',
95
+ 'bash': 'bash',
96
+ 'zsh': 'zsh',
97
+ 'fish': 'fish',
98
+ 'csh': 'csh',
99
+ 'tcsh': 'tcsh',
100
+
101
+ # PowerShell
102
+ 'ps1': 'powershell',
103
+ 'psm1': 'powershell',
104
+ 'psd1': 'powershell',
105
+
106
+ # SQL
107
+ 'sql': 'sql',
108
+ 'mysql': 'sql',
109
+ 'pgsql': 'sql',
110
+ 'plsql': 'sql',
111
+
112
+ # Web technologies
113
+ 'html': 'html',
114
+ 'htm': 'html',
115
+ 'xhtml': 'html',
116
+ 'css': 'css',
117
+ 'scss': 'scss',
118
+ 'sass': 'sass',
119
+ 'less': 'less',
120
+ 'styl': 'stylus',
121
+
122
+ # Markup and data formats
123
+ 'xml': 'xml',
124
+ 'xsl': 'xml',
125
+ 'xslt': 'xml',
126
+ 'svg': 'xml',
127
+ 'json': 'json',
128
+ 'yaml': 'yaml',
129
+ 'yml': 'yaml',
130
+ 'toml': 'toml',
131
+ 'ini': 'ini',
132
+ 'cfg': 'ini',
133
+ 'conf': 'ini',
134
+ 'properties': 'properties',
135
+
136
+ # Documentation
137
+ 'md': 'markdown',
138
+ 'markdown': 'markdown',
139
+ 'mdown': 'markdown',
140
+ 'mkd': 'markdown',
141
+ 'rst': 'rst',
142
+ 'tex': 'latex',
143
+ 'latex': 'latex',
144
+
145
+ # Docker
146
+ 'dockerfile': 'dockerfile',
147
+
148
+ # Other scripting languages
149
+ 'lua': 'lua',
150
+ 'pl': 'perl',
151
+ 'pm': 'perl',
152
+ 'py': 'python',
153
+ 'vim': 'vim',
154
+ 'vimrc': 'vim',
155
+
156
+ # Functional languages
157
+ 'hs': 'haskell',
158
+ 'lhs': 'haskell',
159
+ 'elm': 'elm',
160
+ 'ml': 'ocaml',
161
+ 'mli': 'ocaml',
162
+ 'fs': 'fsharp',
163
+ 'fsi': 'fsharp',
164
+ 'fsx': 'fsharp',
165
+ 'clj': 'clojure',
166
+ 'cljs': 'clojure',
167
+ 'cljc': 'clojure',
168
+ 'edn': 'clojure',
169
+
170
+ # Assembly
171
+ 'asm': 'assembly',
172
+ 's': 'assembly',
173
+
174
+ # Other formats
175
+ 'cmake': 'cmake',
176
+ 'make': 'makefile',
177
+ 'makefile': 'makefile',
178
+ 'dockerfile': 'dockerfile',
179
+ 'gitignore': 'gitignore',
180
+ 'gitconfig': 'gitconfig',
181
+ 'editorconfig': 'editorconfig',
182
+ }
183
+
184
+ # Return the mapped language or the extension itself if not found
185
+ return extension_to_language.get(ext, ext if ext else "text")
186
+
187
+
188
+ def get_language_from_filename(filename: str) -> str:
189
+ """
190
+ Alias for get_language_from_file_extension for backward compatibility.
191
+
192
+ Args:
193
+ filename: The name of the file (can include path)
194
+
195
+ Returns:
196
+ Language identifier string for markdown code fences
197
+ """
198
+ return get_language_from_file_extension(filename)
199
+
200
+
201
+ def is_supported_language(filename: str) -> bool:
202
+ """
203
+ Check if the file extension is supported (has a known language mapping).
204
+
205
+ Args:
206
+ filename: The name of the file (can include path)
207
+
208
+ Returns:
209
+ True if the language is explicitly supported, False otherwise
210
+ """
211
+ if not filename:
212
+ return False
213
+
214
+ _, ext = os.path.splitext(filename.lower())
215
+ ext = ext.lstrip('.')
216
+
217
+ supported_extensions = {
218
+ 'py', 'pyw', 'pyi', 'js', 'mjs', 'jsx', 'ts', 'tsx', 'java', 'kt', 'kts',
219
+ 'scala', 'sc', 'groovy', 'gradle', 'c', 'h', 'cpp', 'cxx', 'cc', 'c++',
220
+ 'hpp', 'hxx', 'hh', 'cs', 'go', 'rs', 'php', 'php3', 'php4', 'php5',
221
+ 'phtml', 'rb', 'rbw', 'rake', 'gemspec', 'swift', 'm', 'mm', 'dart', 'r',
222
+ 'sh', 'bash', 'zsh', 'fish', 'csh', 'tcsh', 'ps1', 'psm1', 'psd1', 'sql',
223
+ 'mysql', 'pgsql', 'plsql', 'html', 'htm', 'xhtml', 'css', 'scss', 'sass',
224
+ 'less', 'styl', 'xml', 'xsl', 'xslt', 'svg', 'json', 'yaml', 'yml', 'toml',
225
+ 'ini', 'cfg', 'conf', 'properties', 'md', 'markdown', 'mdown', 'mkd', 'rst',
226
+ 'tex', 'latex', 'dockerfile', 'lua', 'pl', 'pm', 'vim', 'vimrc', 'hs', 'lhs',
227
+ 'elm', 'ml', 'mli', 'fs', 'fsi', 'fsx', 'clj', 'cljs', 'cljc', 'edn', 'asm',
228
+ 's', 'cmake', 'make', 'makefile', 'gitignore', 'gitconfig', 'editorconfig'
229
+ }
230
+
231
+ return ext in supported_extensions
@@ -0,0 +1,80 @@
1
+ from typing import Dict, Optional, Type
2
+
3
+ from siada.tools.tool_call_format.formatters import DefaultFormatter
4
+ from .tool_call_formatter import ToolCallFormatter
5
+
6
+
7
+ class ToolCallFormatterFactory:
8
+ """
9
+ Tool call formatter 工厂类
10
+ 根据函数名创建相应的formatter实例
11
+ """
12
+
13
+ _formatters: Dict[str, Type[ToolCallFormatter]] = {}
14
+ _instances: Dict[str, ToolCallFormatter] = {}
15
+
16
+ @classmethod
17
+ def register_formatter(cls, formatter_class: Type[ToolCallFormatter]) -> None:
18
+ """
19
+ 注册一个formatter类
20
+
21
+ Args:
22
+ formatter_class: Formatter类
23
+ """
24
+ instance = formatter_class()
25
+ cls._formatters[instance.supported_function] = formatter_class
26
+
27
+ @classmethod
28
+ def get_formatter(cls, function_name: str) -> Optional[ToolCallFormatter]:
29
+ """
30
+ 根据函数名获取相应的formatter实例
31
+
32
+ Args:
33
+ function_name: 函数名称
34
+
35
+ Returns:
36
+ 对应的formatter实例,如果不存在则返回None
37
+ """
38
+ if function_name not in cls._formatters:
39
+ return DefaultFormatter()
40
+
41
+ if function_name not in cls._instances:
42
+ formatter_class = cls._formatters[function_name]
43
+ cls._instances[function_name] = formatter_class()
44
+
45
+ return cls._instances[function_name]
46
+
47
+ @classmethod
48
+ def create_formatter(cls, function_name: str) -> Optional[ToolCallFormatter]:
49
+ """
50
+ 创建formatter实例(每次都创建新实例)
51
+
52
+ Args:
53
+ function_name: 函数名称
54
+
55
+ Returns:
56
+ 新的formatter实例,如果不存在则返回None
57
+ """
58
+ if function_name not in cls._formatters:
59
+ return None
60
+
61
+ formatter_class = cls._formatters[function_name]
62
+ return formatter_class()
63
+
64
+ @classmethod
65
+ def list_supported_functions(cls) -> list[str]:
66
+ """
67
+ 列出所有支持的函数名
68
+
69
+ Returns:
70
+ 支持的函数名列表
71
+ """
72
+ return list(cls._formatters.keys())
73
+
74
+ @classmethod
75
+ def clear_registry(cls) -> None:
76
+ """
77
+ 清空注册的formatter(主要用于测试)
78
+ """
79
+ cls._formatters.clear()
80
+ cls._instances.clear()
@@ -0,0 +1,282 @@
1
+ import json
2
+ import os
3
+ from typing import Tuple
4
+ from siada.tools.coder.files import resolve_path
5
+ from siada.tools.tool_call_format.tool_call_formatter import ToolCallFormatter
6
+
7
+
8
+ from partial_json_parser import loads, MalformedJSON, ensure_json
9
+
10
+
11
+ class DefaultFormatter(ToolCallFormatter):
12
+
13
+
14
+ def format_input(self, call_id: str, function_name: str, arguments: str) -> Tuple[str, bool]:
15
+ return arguments, True
16
+
17
+ @property
18
+ def supported_function(self) -> str:
19
+ return "default"
20
+
21
+
22
+ class FileEditFormatter(ToolCallFormatter):
23
+ """
24
+ File operation formatter
25
+ """
26
+
27
+ def format_input(
28
+ self, call_id: str, function_name: str, arguments: str
29
+ ) -> Tuple[str, str, bool]:
30
+
31
+ # Valid command enumeration
32
+ VALID_COMMANDS = {"view", "create", "str_replace", "insert", "undo_edit"}
33
+ complete = False
34
+ content = ""
35
+ try:
36
+ # Use partial JSON parser to handle incomplete arguments
37
+ args = loads(arguments)
38
+ if arguments == ensure_json(arguments):
39
+ complete = True
40
+
41
+ # Safely extract values, handling potential None/missing keys
42
+ path = args.get("path", None) if args else ""
43
+ raw_command = args.get("command", None) if args else ""
44
+ file_text = args.get("file_text", None) if args else ""
45
+ old_str = args.get("old_str", None) if args else ""
46
+ new_str = args.get("new_str", None) if args else ""
47
+ view_range = args.get("view_range", None) if args else None
48
+ insert_line = args.get("insert_line", None) if args else None
49
+
50
+ # Validate command - only return valid commands, otherwise empty string
51
+ command = raw_command if raw_command in VALID_COMMANDS else ""
52
+
53
+ # If command is not valid, return empty string regardless of other parameters
54
+ if not command:
55
+ return "", False
56
+
57
+ # if path is a valid path, get the fence
58
+ fence = ""
59
+ if path:
60
+ from .file_to_language import get_language_from_file_extension
61
+ fence = get_language_from_file_extension(path)
62
+
63
+ if command == "view":
64
+ if complete:
65
+ if path:
66
+ # Safely check if path has file extension
67
+ try:
68
+ is_file = bool(os.path.splitext(str(path))[1]) # Has extension = likely a file
69
+ except Exception:
70
+ is_file = False # Default to directory if path is invalid
71
+ if is_file:
72
+ content = f"I will read the file `{path}"
73
+ if view_range and len(view_range) == 2:
74
+ content += f"` from line {view_range[0]} to line {view_range[1]}."
75
+ else:
76
+ content = f"I will view the directory `{path}`."
77
+ elif command == "create":
78
+
79
+ if path:
80
+ content = f"I will create the file `{path}"
81
+ if file_text and complete:
82
+ content += f"` with the following content:\n```{fence}\n{file_text}\n```"
83
+ elif command == "str_replace":
84
+ if path:
85
+ content = f"In the file `{path}"
86
+ if old_str is not None:
87
+ if new_str is not None:
88
+ content += f"`, I will replace the string:\n```{fence}\n{old_str}\n```"
89
+ if complete:
90
+ content += f"\nwith:\n```{fence}\n{new_str}\n```"
91
+ elif command == "insert":
92
+
93
+ if path:
94
+ content = f"In the file `{path}"
95
+ if insert_line is not None and new_str and complete:
96
+ content += f"`, I will insert the following text after line {insert_line}:\n```{fence}\n{new_str}\n```"
97
+ elif command == "undo_edit":
98
+ if path:
99
+ content = f"I will undo the last edit for the file `{path}"
100
+ if complete:
101
+ content += "`"
102
+ else:
103
+ # If command is not valid or empty, return empty content
104
+ content = ""
105
+
106
+ return content, complete
107
+ except Exception as e:
108
+ # Handle any parsing errors gracefully
109
+ return content + f"failed to parse arguments: {arguments}", False
110
+
111
+ def supports_streaming(self) -> bool:
112
+ """FileEditFormatter supports streaming rendering"""
113
+ return True
114
+
115
+ @property
116
+ def supported_function(self) -> str:
117
+ return "edit_file"
118
+
119
+
120
+ def get_style(self) -> str:
121
+ return "markdown"
122
+
123
+
124
+ class SearchFormatter(ToolCallFormatter):
125
+ """
126
+ Search formatter
127
+ """
128
+
129
+ def format_input(self, call_id: str, function_name: str, arguments: str) -> Tuple[str, bool]:
130
+ try:
131
+ args = json.loads(arguments)
132
+ cwd = args.get("cwd", os.getcwd())
133
+ directory_path = args.get("directory_path", "")
134
+ regex = args.get("regex", "")
135
+ file_pattern = args.get("file_pattern", "*")
136
+ content = f"I will search for: {regex} in {directory_path} with file pattern {file_pattern} in {cwd}"
137
+ return content, True
138
+ except json.JSONDecodeError:
139
+ return f"failed to parse arguments: {arguments}", False
140
+
141
+ @property
142
+ def supported_function(self) -> str:
143
+ return "search"
144
+
145
+
146
+ class CommandFormatter(ToolCallFormatter):
147
+ """
148
+ Command formatter
149
+ """
150
+
151
+ def format_input(self, call_id: str, function_name: str, arguments: str) -> Tuple[str, bool]:
152
+ try:
153
+ args = json.loads(arguments)
154
+ command = args.get("command", "")
155
+ return f"siada wants to run the following command: \n```bash \n{command}\n```", True
156
+ except json.JSONDecodeError:
157
+ return f"failed to parse arguments: {arguments}", False
158
+
159
+ @property
160
+ def supported_function(self) -> str:
161
+ return "run_cmd"
162
+
163
+ def get_style(self) -> str:
164
+ return "markdown"
165
+
166
+
167
+ class FixAttemptCompletionFormatter(ToolCallFormatter):
168
+ """
169
+ Formatter for the fix_attempt_completion function.
170
+ """
171
+
172
+ def format_input(
173
+ self, call_id: str, function_name: str, arguments: str
174
+ ) -> Tuple[str, bool]:
175
+
176
+ complete = False
177
+ content = ""
178
+ try:
179
+ # Use partial JSON parser to handle incomplete arguments
180
+ args = loads(arguments)
181
+ # Check if JSON is complete by comparing with ensured version
182
+ ensured_json = ensure_json(arguments)
183
+ if arguments == ensured_json:
184
+ complete = True
185
+
186
+ # Safely extract values, handling potential None/missing keys
187
+ result = args.get("result", "") if args else ""
188
+
189
+ if result:
190
+ content = f"The bug fix task has been successfully completed:\n{result}"
191
+ if complete:
192
+ content += ""
193
+
194
+ return content, complete
195
+ except Exception as e:
196
+ return content + f"failed to parse arguments: {arguments}", False
197
+
198
+ @property
199
+ def supports_streaming(self) -> bool:
200
+ return False
201
+
202
+ @property
203
+ def supported_function(self) -> str:
204
+ return "fix_attempt_completion"
205
+
206
+ def get_style(self) -> str:
207
+ return "markdown"
208
+
209
+
210
+ class ReproduceCompletionFormatter(ToolCallFormatter):
211
+ """
212
+ Formatter for the reproduce_completion function.
213
+ """
214
+
215
+ def format_input(self, call_id: str, function_name: str, arguments: str) -> Tuple[str, bool]:
216
+ try:
217
+ args = json.loads(arguments)
218
+ test_case = args.get("test_case", "")
219
+ bug_analysis = args.get("bug_analysis", "")
220
+ content = f"This issue can be reproduced using test case : {test_case}.\n Analysis of the issue: {bug_analysis}"
221
+ return content, True
222
+ except json.JSONDecodeError:
223
+ return f"failed to parse arguments: {arguments}", False
224
+
225
+ @property
226
+ def supported_function(self) -> str:
227
+ return "reproduce_completion"
228
+
229
+
230
+ class WebCrawlFormatter(ToolCallFormatter):
231
+ """
232
+ Formatter for the web_crawl function.
233
+ """
234
+
235
+ def format_input(self, call_id: str, function_name: str, arguments: str) -> Tuple[str, bool]:
236
+ try:
237
+ args = json.loads(arguments)
238
+ url = args.get("url", "")
239
+ crawl_format = args.get("format", "text")
240
+ return f"siada wants to crawl the url: {url} with format {crawl_format}", True
241
+ except json.JSONDecodeError:
242
+ return f"failed to parse arguments: {arguments}", False
243
+
244
+ @property
245
+ def supported_function(self) -> str:
246
+ return "web_crawl"
247
+
248
+
249
+ class AskFollowupQuestionFormatter(ToolCallFormatter):
250
+ """
251
+ Formatter for the ask_followup_question function.
252
+ """
253
+
254
+ def format_input(self, call_id: str, function_name: str, arguments: str) -> Tuple[str, bool]:
255
+ try:
256
+ args = json.loads(arguments)
257
+ question = args.get("question", "")
258
+ return f"{question}", True
259
+ except json.JSONDecodeError:
260
+ return f"failed to parse arguments: {arguments}", False
261
+
262
+ @property
263
+ def supported_function(self) -> str:
264
+ return "ask_followup_question"
265
+
266
+
267
+ class ListCodeDefinitionNamesFormatter(ToolCallFormatter):
268
+ """
269
+ Formatter for the list_code_definition_names function.
270
+ """
271
+
272
+ def format_input(self, call_id: str, function_name: str, arguments: str) -> Tuple[str, bool]:
273
+ try:
274
+ args = json.loads(arguments)
275
+ file_name = args.get("file_name", "Unknown file")
276
+ return f"siada wants to analyze definitions in `{file_name}`", True
277
+ except json.JSONDecodeError:
278
+ return "failed to parse arguments: {arguments}", False
279
+
280
+ @property
281
+ def supported_function(self) -> str:
282
+ return "list_code_definition_names"
@@ -0,0 +1,54 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Any, Dict, Tuple
3
+
4
+
5
+ class ToolCallFormatter(ABC):
6
+ """
7
+ Tool call formatter interface
8
+ Used to format input parameters for different functions
9
+ """
10
+
11
+ @abstractmethod
12
+ def format_input(self, call_id: str, function_name: str, arguments: str) -> Tuple[str, bool]:
13
+ """
14
+ Format function input parameters
15
+
16
+ Args:
17
+ call_id: tool_call id
18
+ function_name: function name
19
+ arguments: raw parameter string
20
+
21
+ Returns:
22
+ A tuple containing content and completeness flag (content, is_complete)
23
+ """
24
+ pass
25
+
26
+ def supports_streaming(self) -> bool:
27
+ """
28
+ Whether streaming rendering is supported
29
+
30
+ Returns:
31
+ True if this formatter supports streaming rendering, False otherwise
32
+ """
33
+ return False
34
+
35
+ @property
36
+ @abstractmethod
37
+ def supported_function(self) -> str:
38
+ """
39
+ Return the supported function name
40
+
41
+ Returns:
42
+ The supported function name
43
+ """
44
+ pass
45
+
46
+
47
+ def get_style(self) -> str:
48
+ """
49
+ Return the style
50
+
51
+ Returns:
52
+ The style
53
+ """
54
+ return "text"
File without changes