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,28 @@
1
+ from dataclasses import dataclass
2
+ from enum import Enum
3
+ from typing import Optional, Dict, Any
4
+
5
+ from siada.support.slash_commands import SwitchEvent
6
+
7
+
8
+ class TurnType(Enum):
9
+ """Types of interaction turns"""
10
+
11
+ COMMAND = "command" # Slash commands (/help, /edit, etc.)
12
+ CONVERSATION = "conversation" # Regular AI conversation
13
+
14
+
15
+ @dataclass
16
+ class TurnInput:
17
+ """Input data for a turn"""
18
+
19
+ use_input: str # Raw user input
20
+
21
+
22
+ @dataclass
23
+ class TurnOutput:
24
+ """Output data from a turn"""
25
+
26
+ output: str | SwitchEvent # Response content
27
+ metadata: Dict[str, Any] # Response metadata
28
+ next_action: Optional[str] # Suggested next action
@@ -0,0 +1,413 @@
1
+ import json
2
+ import os
3
+ import sys
4
+ from dataclasses import fields
5
+ from pathlib import Path
6
+ import warnings
7
+
8
+ from prompt_toolkit.completion import Completer
9
+
10
+ from siada.config.config_loader import Config, load_conf
11
+ from siada.entrypoint.args_parser.args import get_parser
12
+ from siada.entrypoint.interaction.config import RunningConfig
13
+ from siada.entrypoint.interaction.controller import Controller
14
+ from siada.entrypoint.interaction.nointeractive_controller import NoInteractiveController
15
+ from siada.foundation.logging import toggle_console_output, logger
16
+ from siada.io.color_settings import RunningConfigColorSettings
17
+ from siada.models.model_run_config import ModelRunConfig
18
+ from siada.models.model_base_config import ModelBaseConfig
19
+ from siada.support.completer import AutoCompleter
20
+ from siada.support.slash_commands import SlashCommands, SwitchEvent
21
+ from siada.support.envprocessor import load_dotenv_files
22
+ from siada.support.repo import get_git_root
23
+ from siada.utils import SettingsUtils
24
+ from siada.io.io import InputOutput
25
+
26
+ try:
27
+ import git
28
+ except ImportError:
29
+ git = None
30
+
31
+ import shtab
32
+ from dotenv import load_dotenv
33
+ from prompt_toolkit.enums import EditingMode
34
+ from siada.services.model_info_service import ModelInfoService
35
+
36
+
37
+ def _suppress_third_party_warnings():
38
+ """Suppress harmless warnings from third-party libraries"""
39
+ # Suppress pydub ffmpeg/avconv warning - not relevant for Siada as we don't use audio features
40
+ warnings.filterwarnings(
41
+ "ignore",
42
+ message="Couldn't find ffmpeg or avconv.*",
43
+ category=RuntimeWarning
44
+ )
45
+
46
+ # Suppress all SyntaxWarning from pydub - use message pattern to catch invalid escape sequences
47
+ warnings.filterwarnings(
48
+ "ignore",
49
+ message="invalid escape sequence.*",
50
+ category=SyntaxWarning
51
+ )
52
+
53
+
54
+ def _configure_litellm_logging():
55
+ """Configure LiteLLM global logging settings to suppress debug logs"""
56
+ try:
57
+ import litellm
58
+
59
+ # Configure litellm global properties
60
+ litellm.set_verbose = False
61
+ litellm.turn_off_message_logging = True
62
+ litellm.suppress_debug_info = True
63
+ litellm.drop_params = True
64
+
65
+
66
+ # Try to disable internal debug logging
67
+ try:
68
+ litellm._logging._disable_debugging()
69
+ except Exception:
70
+ pass # Ignore if method doesn't exist
71
+
72
+ # Disable message logging and tracing
73
+ litellm.turn_off_message_logging = True
74
+ litellm.success_callback = []
75
+ litellm.failure_callback = []
76
+
77
+ logger.debug("LiteLLM logging configuration completed")
78
+
79
+ except ImportError:
80
+ logger.debug("LiteLLM not installed, skipping logging configuration")
81
+ except Exception as e:
82
+ logger.debug(f"Error configuring LiteLLM logging: {e}")
83
+
84
+
85
+ def _parse_args_and_setup_environment(argv):
86
+ """
87
+ Parse command line arguments and set up environment
88
+
89
+ Args:
90
+ argv: Command line argument list
91
+
92
+ Returns:
93
+ tuple: (args, unknown, loaded_dotenvs, git_root, workspace, parser) parsed arguments, unknown arguments, loaded environment variable files, git root directory, workspace path and parser
94
+ """
95
+ # workspace is specific for development and needs to be parsed early
96
+ import argparse
97
+
98
+ temp_parser = argparse.ArgumentParser(add_help=False)
99
+ temp_parser.add_argument("--workspace", default=None)
100
+ temp_args, _ = temp_parser.parse_known_args(argv)
101
+
102
+ # Now get git root from the specified workspace or current directory
103
+ if git is None:
104
+ git_root = None
105
+ else:
106
+ git_root = get_git_root(temp_args.workspace)
107
+
108
+ parser = get_parser(git_root=git_root, default_config_files=[])
109
+ try:
110
+ args, unknown = parser.parse_known_args(argv)
111
+ except AttributeError as e:
112
+ raise e
113
+
114
+ # Configure console output based on parsed arguments
115
+ if hasattr(args, 'disable_console_output') and args.disable_console_output:
116
+ toggle_console_output(False)
117
+ else:
118
+ toggle_console_output(True)
119
+
120
+ loaded_dotenvs = load_dotenv_files(git_root, args.env_file, args.encoding)
121
+
122
+ if args.verbose:
123
+ for fname in loaded_dotenvs:
124
+ logger.info(f"Loaded {fname}")
125
+
126
+ return args, unknown, loaded_dotenvs, git_root, temp_args.workspace, parser
127
+
128
+
129
+ def get_io(args, pretty=None):
130
+ """
131
+ Create InputOutput instance with complete IO configuration
132
+
133
+ Args:
134
+ args: Parsed command line arguments
135
+ pretty: Whether to enable pretty mode, defaults to args.pretty
136
+
137
+ Returns:
138
+ InputOutput: Configured IO instance
139
+
140
+ Raises:
141
+ ValueError: When theme configuration is invalid
142
+ """
143
+ from siada.io.color_settings import ColorSettings
144
+
145
+ # Configure color settings
146
+ color_settings = ColorSettings.from_theme(args.theme)
147
+ running_color_settings = RunningConfigColorSettings(color_settings=color_settings, pretty=args.pretty)
148
+ color_settings.apply_to_args(args)
149
+ if args.verbose:
150
+ print(f"Applied color theme: {args.theme}")
151
+
152
+ # Configure editing mode
153
+ editing_mode = EditingMode.VI if args.vim else EditingMode.EMACS
154
+
155
+ return InputOutput(
156
+ pretty=args.pretty,
157
+ running_color_settings=running_color_settings,
158
+ encoding=args.encoding,
159
+ line_endings=getattr(args, "line_endings", "platform"),
160
+ editingmode=editing_mode,
161
+ fancy_input=args.fancy_input,
162
+ multiline_mode=False,
163
+ notifications=True,
164
+ ), running_color_settings
165
+
166
+
167
+ def set_env(args, io):
168
+ """
169
+ Set environment variables, including general environment variables and API keys
170
+
171
+ Args:
172
+ args: Parsed command line arguments
173
+ io: InputOutput instance for printing error messages
174
+
175
+ Returns:
176
+ int: 0 for success, 1 for error
177
+ """
178
+ # Set general environment variables
179
+ if args.set_env:
180
+ for env_setting in args.set_env:
181
+ try:
182
+ name, value = env_setting.split("=", 1)
183
+ os.environ[name.strip()] = value.strip()
184
+ except ValueError:
185
+ io.print_error(f"Invalid --set-env format: {env_setting}")
186
+ io.print_info("Format should be: ENV_VAR_NAME=value")
187
+ return 1
188
+
189
+ return 0
190
+
191
+
192
+ def get_workspace(workspace_arg, git_root):
193
+ """
194
+ Get and set workspace directory
195
+
196
+ Args:
197
+ workspace_arg: User-specified workspace path
198
+ git_root: Git root directory path
199
+
200
+ Returns:
201
+ str: Workspace path
202
+
203
+ Raises:
204
+ SystemExit: When workspace directory does not exist or is not a directory
205
+ """
206
+ # Set workspace - prioritize user-specified workspace, then git root, then current directory
207
+ if workspace_arg:
208
+ workspace = os.path.abspath(workspace_arg)
209
+ # Ensure the workspace directory exists
210
+ if not os.path.exists(workspace):
211
+ logger.error(f"Workspace directory does not exist: {workspace}")
212
+ sys.exit(1)
213
+ if not os.path.isdir(workspace):
214
+ logger.error(f"Workspace path is not a directory: {workspace}")
215
+ sys.exit(1)
216
+ # Change to the specified workspace directory
217
+ os.chdir(workspace)
218
+ logger.debug(f"Changed to workspace directory: {workspace}")
219
+ else:
220
+ workspace = git_root if git_root else os.getcwd()
221
+ logger.debug(f"Using default workspace: {workspace}")
222
+
223
+ return workspace
224
+
225
+
226
+ def show_banner(io):
227
+ """
228
+ Display SIADA HUB banner with error handling
229
+
230
+ Args:
231
+ io: InputOutput instance
232
+
233
+ Raises:
234
+ Exception: When banner display fails
235
+ """
236
+ # Show SIADA HUB banner with gradient effect
237
+ from siada.io.banner import show_siada_banner
238
+
239
+ try:
240
+ io.rule()
241
+ show_siada_banner(pretty=io.pretty, console=io.console)
242
+ except UnicodeEncodeError as err:
243
+ io.print_error("Terminal does not support pretty output (UnicodeDecodeError)")
244
+ sys.exit(1)
245
+ except Exception as err:
246
+ io.print_error(f"Error showing banner: {err}")
247
+ sys.exit(1)
248
+
249
+
250
+ def get_config(args, io, conf: Config = None):
251
+ """
252
+ Configure and create model instance
253
+
254
+ Args:
255
+ args: Parsed command line arguments
256
+ io: InputOutput instance for displaying information
257
+
258
+ Returns:
259
+ ModelRunConfig: Configured model instance, returns None if exit is needed
260
+ """
261
+ # Configuration priority: args > config file > defaults
262
+ config = ModelRunConfig.get_default_config()
263
+
264
+ # Determine final values using priority order
265
+ final_model = args.model or (conf.llm_config.model if conf and conf.llm_config else None)
266
+ final_provider = args.provider or (conf.llm_config.provider if conf and conf.llm_config else None)
267
+
268
+ # Apply final configuration
269
+ if final_model is not None:
270
+ config.model_name = final_model
271
+ config.configure_model_settings(config.model_name)
272
+
273
+ if final_provider is not None:
274
+ config.provider = final_provider
275
+
276
+ # Check if provider is set
277
+ if config.provider is None:
278
+ io.print_error("No provider specified. Please set provider in agent_config.yaml or use --provider option")
279
+ sys.exit(1)
280
+
281
+ if config.provider == "openrouter":
282
+ ## check the openrouter api key is set
283
+ if os.getenv("OPENROUTER_API_KEY") is None:
284
+ io.print_error("OPENROUTER_API_KEY is not set for openrouter provider")
285
+ sys.exit(1)
286
+
287
+ # Set reasoning effort and thinking tokens if specified
288
+ if args.reasoning_effort is not None:
289
+ if (
290
+ not config.supports_extra_params
291
+ or "reasoning_effort" not in config.supports_extra_params
292
+ ):
293
+ io.print_error(f"Model {config.model_name} does not support reasoning effort")
294
+ sys.exit(1)
295
+ else:
296
+ config.set_reasoning_effort(args.reasoning_effort)
297
+
298
+ if args.thinking_tokens is not None:
299
+ if (
300
+ not config.supports_extra_params
301
+ or "thinking_tokens" not in config.supports_extra_params
302
+ ):
303
+ io.print_error(f"Model {config.model_name} does not support thinking tokens")
304
+ sys.exit(1)
305
+ else:
306
+ config.set_thinking_tokens(args.thinking_tokens)
307
+
308
+ # Display model settings in verbose mode
309
+ if args.verbose:
310
+ io.print_info("Model settings:")
311
+ for attr in sorted(fields(ModelRunConfig), key=lambda x: x.name):
312
+ value = getattr(config, attr.name)
313
+ if value is None:
314
+ val_str = "None"
315
+ else:
316
+ val_str = json.dumps(value, indent=4)
317
+ io.print_info(f"{attr.name}: {val_str}")
318
+
319
+ return config
320
+
321
+
322
+ def main():
323
+ # Suppress harmless warnings from third-party libraries
324
+ _suppress_third_party_warnings()
325
+
326
+ # Configure litellm globally to suppress debug logs
327
+ _configure_litellm_logging()
328
+
329
+ conf: Config = load_conf()
330
+
331
+ argv = sys.argv[1:]
332
+
333
+ args, _, _, git_root, workspace_arg, parser = _parse_args_and_setup_environment(argv)
334
+
335
+ interactive_mode = True
336
+ if args.prompt:
337
+ interactive_mode = False
338
+ args.pretty = False
339
+
340
+ try:
341
+ io, running_color_settings = get_io(args)
342
+ except ValueError as e:
343
+ print(f"Invalid theme configuration: {e}")
344
+ return 1
345
+
346
+ if args.list_models:
347
+ models = ModelInfoService.get_model_names()
348
+ io.print_info("\n".join(f"- {model}" for model in models))
349
+ return 0
350
+
351
+ # Configure model
352
+ model = get_config(args, io, conf)
353
+ # Display banner
354
+
355
+ # Set environment variables
356
+ if set_env(args, io) != 0:
357
+ return 1
358
+
359
+ # Get workspace
360
+ workspace = get_workspace(workspace_arg, git_root)
361
+
362
+ if args.verbose:
363
+ io.print_info(f"Using agent: {args.agent}")
364
+ io.print_info(f"Workspace: {workspace}")
365
+
366
+ if args.verbose:
367
+ show = SettingsUtils.format_settings(parser, args)
368
+ io.print_info(show)
369
+
370
+ # Show command line in verbose mode only
371
+ cmd_line = " ".join(sys.argv)
372
+ io.print_info(f"Command: {cmd_line}")
373
+
374
+ if model is None:
375
+ return 0
376
+
377
+ commands = SlashCommands(
378
+ io=io,
379
+ verbose=args.verbose,
380
+ editor=args.editor,
381
+ )
382
+
383
+ completer: Completer = AutoCompleter(
384
+ root=workspace,
385
+ commands=commands,
386
+ encoding=args.encoding,
387
+ )
388
+
389
+ running_config = RunningConfig(
390
+ llm_config=model,
391
+ io=io,
392
+ workspace=workspace,
393
+ agent_name=args.agent,
394
+ completer=completer,
395
+ running_color_settings=running_color_settings,
396
+ console_output=not args.disable_console_output if interactive_mode else True,
397
+ interactive=interactive_mode,
398
+ )
399
+ show_banner(io)
400
+
401
+ if not interactive_mode:
402
+ controller = NoInteractiveController(running_config)
403
+ controller.run(args.prompt)
404
+ return 0
405
+
406
+ controller = Controller(running_config, commands)
407
+ controller.show_announcements()
408
+ controller.run()
409
+
410
+
411
+ if __name__ == "__main__":
412
+ status = main()
413
+ sys.exit(status)
File without changes
@@ -0,0 +1,48 @@
1
+ from typing import Optional
2
+ from pydantic import BaseModel, ConfigDict
3
+
4
+ from siada.session.session_models import RunningSession
5
+ from typing import List
6
+ from agents import TResponseInputItem
7
+ from pydantic import BaseModel, Field
8
+
9
+
10
+ class CodeAgentContext(BaseModel):
11
+
12
+ model_config = ConfigDict(arbitrary_types_allowed=True)
13
+
14
+ session: Optional[RunningSession] = None
15
+
16
+ root_dir: str | None = None
17
+
18
+ provider: str | None = None
19
+
20
+ # 交互模式标识,True为交互模式,False为非交互模式
21
+ interactive_mode: bool = True
22
+
23
+ # 完整的消息历史列表
24
+ message_history: List[TResponseInputItem] = Field(default_factory=list)
25
+
26
+ def add_message(self, message: TResponseInputItem) -> None:
27
+ self.message_history.append(message)
28
+
29
+ def add_messages(self, messages: List[TResponseInputItem]) -> None:
30
+ self.message_history.extend(messages)
31
+
32
+ def remove_old_messages(self, remove_count: int) -> List[TResponseInputItem]:
33
+ """删除旧消息,返回剩余的消息列表,永远保留第一条消息"""
34
+ if remove_count <= 0:
35
+ return self.message_history.copy()
36
+
37
+ # 如果历史记录为空或只有一条消息,不删除任何消息
38
+ if len(self.message_history) <= 1:
39
+ return self.message_history.copy()
40
+
41
+ # 计算实际可删除的消息数量(保留第一条消息)
42
+ max_removable = len(self.message_history) - 1
43
+ actual_remove_count = min(remove_count, max_removable)
44
+
45
+ # 删除第1条消息之后的N条消息(索引1到1+actual_remove_count)
46
+ # 保留第一条消息和剩余的消息
47
+ self.message_history = [self.message_history[0]] + self.message_history[1 + actual_remove_count:]
48
+ return self.message_history.copy()
@@ -0,0 +1,59 @@
1
+ import agents
2
+ from pydantic_settings import BaseSettings
3
+ from typing import Optional, ClassVar
4
+
5
+
6
+ class Settings(BaseSettings):
7
+ """
8
+ 应用配置类
9
+
10
+ 使用pydantic_settings管理配置,支持从环境变量加载
11
+ """
12
+ # 应用基本信息
13
+ APP_NAME: str = "Siada API"
14
+ APP_VERSION: str = "0.1.0"
15
+ APP_DESCRIPTION: str = "提供Siada agent对外的RPC能力"
16
+
17
+ # API配置
18
+ API_PREFIX: str = "/api/v1"
19
+
20
+ # 服务器配置
21
+ HOST: str = "0.0.0.0"
22
+ PORT: int = 8000
23
+ RELOAD: bool = True
24
+
25
+ # 日志配置
26
+ LOG_LEVEL: str = "INFO"
27
+
28
+ # OpenAI API配置
29
+ OPENAI_API_KEY: Optional[str] = None
30
+ OPENAI_API_BASE: Optional[str] = None
31
+ OPENAI_API_VERSION: Optional[str] = None
32
+ OPENAI_API_TYPE: Optional[str] = None
33
+ OPENAI_ORGANIZATION: Optional[str] = None
34
+
35
+ # Agent配置
36
+ Claude_4_0_SONNET: str = "claude-sonnet-4"
37
+ Gemini_2_5_PRO: str = "gemini-2.5-pro"
38
+ Deepseek_V3_0324: str = "deepseek-v3-0324"
39
+ Deepseek_R1_0528: str = "deepseek-r1-0528"
40
+ O1_MINI: str = "o1-mini"
41
+ MAX_TURNS: int = 200
42
+ DEFAULT_MODEL: str = Claude_4_0_SONNET
43
+
44
+
45
+ # 将RunConfig设置为ClassVar,这样它不会被包含在模型验证中
46
+ _DEFAULT_RUN_CONFIG: ClassVar[agents.RunConfig] = None
47
+
48
+ class Config:
49
+ env_file = ".env"
50
+ case_sensitive = True
51
+
52
+ class Constants:
53
+ # Agent名称
54
+ JANK_PROBLEM_AGENT_NAME: str = "JankProblemAgent"
55
+
56
+
57
+ # 创建全局设置对象
58
+ settings = Settings()
59
+ settings.model_rebuild() # 确保模型完全构建
@@ -0,0 +1,2 @@
1
+ LLM_API_CONNECT_TIMEOUT = 30 # timeout for connecting to the API and sending the request (seconds)
2
+ LLM_API_READ_TIMEOUT = 600 # timeout for receiving the response (seconds)
@@ -0,0 +1,76 @@
1
+ """
2
+ 上下文管理模块
3
+
4
+ 提供全局上下文管理功能,类似于Java中的ThreadLocal
5
+ 支持存储多种类型的上下文变量
6
+ """
7
+ import contextvars
8
+ from typing import Any, Dict, Optional
9
+
10
+ # 创建上下文变量字典
11
+ context_dict_var = contextvars.ContextVar('context_dict', default={})
12
+
13
+ def set_context_var(key: str, value: Any) -> None:
14
+ """
15
+ 设置上下文变量
16
+
17
+ Args:
18
+ key: 变量名
19
+ value: 变量值
20
+ """
21
+ context_dict = context_dict_var.get()
22
+ new_dict = dict(context_dict) # 创建副本以避免修改原始字典
23
+ new_dict[key] = value
24
+ context_dict_var.set(new_dict)
25
+
26
+ def get_context_var(key: str, default: Any = None) -> Any:
27
+ """
28
+ 获取上下文变量
29
+
30
+ Args:
31
+ key: 变量名
32
+ default: 默认值,如果变量不存在则返回此值
33
+
34
+ Returns:
35
+ 上下文变量值,如果不存在则返回默认值
36
+ """
37
+ context_dict = context_dict_var.get()
38
+ return context_dict.get(key, default)
39
+
40
+ def remove_context_var(key: str) -> None:
41
+ """
42
+ 移除上下文变量
43
+
44
+ Args:
45
+ key: 变量名
46
+ """
47
+ context_dict = context_dict_var.get()
48
+ new_dict = dict(context_dict) # 创建副本以避免修改原始字典
49
+ if key in new_dict:
50
+ del new_dict[key]
51
+ context_dict_var.set(new_dict)
52
+
53
+ def clear_context() -> None:
54
+ """
55
+ 清空所有上下文变量
56
+ """
57
+ context_dict_var.set({})
58
+
59
+ # 为了保持向后兼容性,提供session_id的专用方法
60
+ def set_session_id(session_id: str) -> None:
61
+ """
62
+ 设置当前上下文的session_id
63
+
64
+ Args:
65
+ session_id: 会话ID
66
+ """
67
+ set_context_var('session_id', session_id)
68
+
69
+ def get_session_id() -> Optional[str]:
70
+ """
71
+ 获取当前上下文的session_id
72
+
73
+ Returns:
74
+ 当前上下文的session_id,如果不存在则返回None
75
+ """
76
+ return get_context_var('session_id')