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.
- agent_config.yaml +34 -0
- siada/__init__.py +13 -0
- siada/agent_hub/__init__.py +10 -0
- siada/agent_hub/coder/bug_fix_agent.py +398 -0
- siada/agent_hub/coder/code_gen_agent.py +235 -0
- siada/agent_hub/coder/fe_gen_agent.py +37 -0
- siada/agent_hub/coder/issue_review_agent.py +68 -0
- siada/agent_hub/coder/prompt/__init__.py +0 -0
- siada/agent_hub/coder/prompt/base/__init__.py +0 -0
- siada/agent_hub/coder/prompt/base/capabilities.py +18 -0
- siada/agent_hub/coder/prompt/base/non_interactive.py +14 -0
- siada/agent_hub/coder/prompt/base/prompt_builder.py +33 -0
- siada/agent_hub/coder/prompt/base/rules.py +40 -0
- siada/agent_hub/coder/prompt/base/tool_use.py +14 -0
- siada/agent_hub/coder/prompt/bug_prompt/bug_fix_prompt.py +51 -0
- siada/agent_hub/coder/prompt/bug_prompt/rules.py +53 -0
- siada/agent_hub/coder/prompt/code_gen_prompt.py +45 -0
- siada/agent_hub/coder/prompt/fe_gen_prompt.py +45 -0
- siada/agent_hub/coder/prompt/issue_review_prompt.py +121 -0
- siada/agent_hub/coder/tracing/__init__.py +22 -0
- siada/agent_hub/coder/tracing/logger_tracing_processor.py +458 -0
- siada/agent_hub/hooks/__init__.py +0 -0
- siada/agent_hub/hooks/io_hooks.py +30 -0
- siada/agent_hub/siada_agent.py +214 -0
- siada/config/__init__.py +0 -0
- siada/config/config_loader.py +67 -0
- siada/entrypoint/__init__.py +3 -0
- siada/entrypoint/args_parser/__init__.py +0 -0
- siada/entrypoint/args_parser/args.py +195 -0
- siada/entrypoint/interaction/__init__.py +0 -0
- siada/entrypoint/interaction/base_turn.py +107 -0
- siada/entrypoint/interaction/config.py +30 -0
- siada/entrypoint/interaction/controller.py +122 -0
- siada/entrypoint/interaction/nointeractive_controller.py +27 -0
- siada/entrypoint/interaction/turn/interface.py +116 -0
- siada/entrypoint/interaction/turn/models.py +34 -0
- siada/entrypoint/interaction/turn/run_turn.py +641 -0
- siada/entrypoint/interaction/turn_factory.py +35 -0
- siada/entrypoint/interaction/turn_models.py +28 -0
- siada/entrypoint/siadahub.py +413 -0
- siada/foundation/__init__.py +0 -0
- siada/foundation/code_agent_context.py +48 -0
- siada/foundation/config.py +59 -0
- siada/foundation/constants.py +2 -0
- siada/foundation/context.py +76 -0
- siada/foundation/logging.py +341 -0
- siada/foundation/tools/__init__.py +0 -0
- siada/foundation/tools/get_git_diff.py +105 -0
- siada/io/__init__.py +0 -0
- siada/io/banner.py +148 -0
- siada/io/color_settings.py +207 -0
- siada/io/color_utils.py +16 -0
- siada/io/components/__init__.py +0 -0
- siada/io/components/mdstream.py +204 -0
- siada/io/console_printer.py +68 -0
- siada/io/io.py +661 -0
- siada/io/key_bindings.py +129 -0
- siada/io/notification_command.py +40 -0
- siada/io/test_banner.py +64 -0
- siada/models/__init__.py +0 -0
- siada/models/agent.py +47 -0
- siada/models/coder/__init__.py +0 -0
- siada/models/converter.py +27 -0
- siada/models/model_base_config.py +121 -0
- siada/models/model_run_config.py +153 -0
- siada/provider/__init__.py +0 -0
- siada/provider/client_factory.py +112 -0
- siada/provider/llm_client.py +11 -0
- siada/provider/openrouter/__init__.py +0 -0
- siada/provider/openrouter/coverter.py +17 -0
- siada/provider/openrouter/openroute_provider.py +33 -0
- siada/provider/provider_factory.py +53 -0
- siada/queries/tree-sitter-language-pack/README.md +7 -0
- siada/queries/tree-sitter-language-pack/arduino-tags.scm +5 -0
- siada/queries/tree-sitter-language-pack/c-tags.scm +9 -0
- siada/queries/tree-sitter-language-pack/chatito-tags.scm +16 -0
- siada/queries/tree-sitter-language-pack/commonlisp-tags.scm +122 -0
- siada/queries/tree-sitter-language-pack/cpp-tags.scm +15 -0
- siada/queries/tree-sitter-language-pack/csharp-tags.scm +26 -0
- siada/queries/tree-sitter-language-pack/d-tags.scm +26 -0
- siada/queries/tree-sitter-language-pack/dart-tags.scm +92 -0
- siada/queries/tree-sitter-language-pack/elisp-tags.scm +5 -0
- siada/queries/tree-sitter-language-pack/elixir-tags.scm +54 -0
- siada/queries/tree-sitter-language-pack/elm-tags.scm +19 -0
- siada/queries/tree-sitter-language-pack/gleam-tags.scm +41 -0
- siada/queries/tree-sitter-language-pack/go-tags.scm +42 -0
- siada/queries/tree-sitter-language-pack/java-tags.scm +20 -0
- siada/queries/tree-sitter-language-pack/javascript-tags.scm +88 -0
- siada/queries/tree-sitter-language-pack/lua-tags.scm +34 -0
- siada/queries/tree-sitter-language-pack/ocaml-tags.scm +115 -0
- siada/queries/tree-sitter-language-pack/ocaml_interface-tags.scm +98 -0
- siada/queries/tree-sitter-language-pack/pony-tags.scm +39 -0
- siada/queries/tree-sitter-language-pack/properties-tags.scm +5 -0
- siada/queries/tree-sitter-language-pack/python-tags.scm +14 -0
- siada/queries/tree-sitter-language-pack/r-tags.scm +21 -0
- siada/queries/tree-sitter-language-pack/racket-tags.scm +12 -0
- siada/queries/tree-sitter-language-pack/ruby-tags.scm +64 -0
- siada/queries/tree-sitter-language-pack/rust-tags.scm +60 -0
- siada/queries/tree-sitter-language-pack/solidity-tags.scm +43 -0
- siada/queries/tree-sitter-language-pack/swift-tags.scm +51 -0
- siada/queries/tree-sitter-language-pack/udev-tags.scm +20 -0
- siada/queries/tree-sitter-languages/README.md +23 -0
- siada/queries/tree-sitter-languages/c-tags.scm +9 -0
- siada/queries/tree-sitter-languages/c_sharp-tags.scm +46 -0
- siada/queries/tree-sitter-languages/cpp-tags.scm +15 -0
- siada/queries/tree-sitter-languages/dart-tags.scm +91 -0
- siada/queries/tree-sitter-languages/elisp-tags.scm +8 -0
- siada/queries/tree-sitter-languages/elixir-tags.scm +54 -0
- siada/queries/tree-sitter-languages/elm-tags.scm +19 -0
- siada/queries/tree-sitter-languages/go-tags.scm +30 -0
- siada/queries/tree-sitter-languages/hcl-tags.scm +77 -0
- siada/queries/tree-sitter-languages/java-tags.scm +20 -0
- siada/queries/tree-sitter-languages/javascript-tags.scm +88 -0
- siada/queries/tree-sitter-languages/kotlin-tags.scm +27 -0
- siada/queries/tree-sitter-languages/ocaml-tags.scm +115 -0
- siada/queries/tree-sitter-languages/ocaml_interface-tags.scm +98 -0
- siada/queries/tree-sitter-languages/php-tags.scm +26 -0
- siada/queries/tree-sitter-languages/python-tags.scm +12 -0
- siada/queries/tree-sitter-languages/ql-tags.scm +26 -0
- siada/queries/tree-sitter-languages/ruby-tags.scm +64 -0
- siada/queries/tree-sitter-languages/rust-tags.scm +60 -0
- siada/queries/tree-sitter-languages/scala-tags.scm +65 -0
- siada/queries/tree-sitter-languages/typescript-tags.scm +41 -0
- siada/services/__init__.py +30 -0
- siada/services/at_file_recommendation_design_document.md +1213 -0
- siada/services/code_context_manager.py +136 -0
- siada/services/enhanced_fix_result_check.py +546 -0
- siada/services/execution_trace_collector.py +207 -0
- siada/services/file_recommendation/__init__.py +136 -0
- siada/services/file_recommendation/core/__init__.py +17 -0
- siada/services/file_recommendation/core/completion_engine.py +351 -0
- siada/services/file_recommendation/core/config.py +62 -0
- siada/services/file_recommendation/core/file_discovery.py +353 -0
- siada/services/file_recommendation/core/suggestion.py +110 -0
- siada/services/file_recommendation/utils/__init__.py +22 -0
- siada/services/file_recommendation/utils/path_utils.py +138 -0
- siada/services/file_recommendation/utils/text_utils.py +253 -0
- siada/services/fix_result_check.py +377 -0
- siada/services/handle_at_command/README.md +247 -0
- siada/services/handle_at_command/__init__.py +115 -0
- siada/services/handle_at_command/exceptions.py +46 -0
- siada/services/handle_at_command/models.py +92 -0
- siada/services/handle_at_command/parser.py +189 -0
- siada/services/handle_at_command/processor.py +321 -0
- siada/services/handle_at_command/resolver.py +318 -0
- siada/services/handle_at_command/utils.py +345 -0
- siada/services/model_info_service.py +103 -0
- siada/services/siada_runner.py +170 -0
- siada/session/__init__.py +29 -0
- siada/session/session_manager.py +66 -0
- siada/session/session_models.py +43 -0
- siada/support/__init__.py +0 -0
- siada/support/completer.py +204 -0
- siada/support/editor.py +146 -0
- siada/support/envprocessor.py +54 -0
- siada/support/repo.py +51 -0
- siada/support/slash_commands.py +410 -0
- siada/support/spinner.py +320 -0
- siada/tools/__init__.py +0 -0
- siada/tools/ast/README.md +345 -0
- siada/tools/ast/ast_tool.py +341 -0
- siada/tools/ast/models.py +11 -0
- siada/tools/coder/__init__.py +0 -0
- siada/tools/coder/ask_followup_question.py +70 -0
- siada/tools/coder/cmd_runner.py +132 -0
- siada/tools/coder/file_operator.py +284 -0
- siada/tools/coder/file_search/README.md +223 -0
- siada/tools/coder/file_search/__init__.py +10 -0
- siada/tools/coder/file_search/bin/rg-linux-arm64 +0 -0
- siada/tools/coder/file_search/bin/rg-linux-x64 +0 -0
- siada/tools/coder/file_search/bin/rg-macos-arm64 +0 -0
- siada/tools/coder/file_search/bin/rg-macos-x64 +0 -0
- siada/tools/coder/file_search/bin/rg.exe +0 -0
- siada/tools/coder/file_search/search.py +437 -0
- siada/tools/coder/file_search/setup_package.py +150 -0
- siada/tools/coder/file_search/test_search.py +142 -0
- siada/tools/coder/files.py +147 -0
- siada/tools/coder/fix_attempt_completion.py +58 -0
- siada/tools/coder/issue_review_completion.py +29 -0
- siada/tools/coder/observation/__init__.py +0 -0
- siada/tools/coder/observation/error.py +22 -0
- siada/tools/coder/observation/file_observation.py +216 -0
- siada/tools/coder/observation/observation.py +71 -0
- siada/tools/coder/repo_map/__init__.py +157 -0
- siada/tools/coder/repo_map/dump.py +29 -0
- siada/tools/coder/repo_map/io.py +226 -0
- siada/tools/coder/repo_map/repo_map.py +881 -0
- siada/tools/coder/repo_map/special.py +203 -0
- siada/tools/coder/repo_map/token_counter.py +154 -0
- siada/tools/coder/repo_map/waiting.py +221 -0
- siada/tools/coder/reproduce_completion.py +39 -0
- siada/tools/coder/run_cmd.py +60 -0
- siada/tools/coder/test_completion.py +25 -0
- siada/tools/coder/tool_docs.py +44 -0
- siada/tools/compression_tool.py +200 -0
- siada/tools/read_many_files/README.md +220 -0
- siada/tools/read_many_files/__init__.py +25 -0
- siada/tools/read_many_files/file_processor.py +486 -0
- siada/tools/read_many_files/filters.py +326 -0
- siada/tools/read_many_files/formatters.py +204 -0
- siada/tools/read_many_files/models.py +189 -0
- siada/tools/read_many_files_tool.py +233 -0
- siada/tools/tool_call_format/__init__.py +55 -0
- siada/tools/tool_call_format/file_to_language.py +231 -0
- siada/tools/tool_call_format/formatter_factory.py +80 -0
- siada/tools/tool_call_format/formatters.py +282 -0
- siada/tools/tool_call_format/tool_call_formatter.py +54 -0
- siada/tools/web/__init__.py +0 -0
- siada/tools/web/web_crawl.py +208 -0
- siada/utils.py +194 -0
- siada_cli-0.0.1.dist-info/LICENSE +201 -0
- siada_cli-0.0.1.dist-info/METADATA +457 -0
- siada_cli-0.0.1.dist-info/RECORD +215 -0
- siada_cli-0.0.1.dist-info/WHEEL +4 -0
- siada_cli-0.0.1.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Generic
|
|
3
|
+
import yaml
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
from agents import Agent, RunConfig, RunHooks, RunResult, RunResultStreaming, Runner, TContext, TResponseInputItem, set_trace_processors
|
|
7
|
+
from siada.agent_hub.coder.tracing.logger_tracing_processor import create_detailed_logger
|
|
8
|
+
from siada.foundation.code_agent_context import CodeAgentContext
|
|
9
|
+
from siada.models.converter import ModelSettingsConverter
|
|
10
|
+
from siada.provider.provider_factory import get_provider
|
|
11
|
+
from siada.session import RunningSessionManager
|
|
12
|
+
from siada.tools.coder.ask_followup_question import ask_followup_question
|
|
13
|
+
from siada.tools.coder.repo_map.repo_map import RepoMap
|
|
14
|
+
from siada.tools.coder.repo_map.token_counter import TokenCounterModel
|
|
15
|
+
from siada.tools.coder.repo_map.io import SilentIO
|
|
16
|
+
|
|
17
|
+
import logging
|
|
18
|
+
|
|
19
|
+
class SiadaAgent(Agent[Generic[TContext]], ABC):
|
|
20
|
+
|
|
21
|
+
@abstractmethod
|
|
22
|
+
async def get_context(self) -> TContext:
|
|
23
|
+
"""
|
|
24
|
+
Get the context object for this agent.
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
TContext: The context object containing relevant information for the agent's execution.
|
|
28
|
+
"""
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
@abstractmethod
|
|
32
|
+
async def run(self, user_input: str, context: TContext) -> RunResult:
|
|
33
|
+
"""
|
|
34
|
+
Execute the agent with the given user input and context.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
user_input (str): The input provided by the user.
|
|
38
|
+
context (TContext): The context object containing relevant information for execution.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
RunResult: The result of the agent's execution.
|
|
42
|
+
"""
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
@abstractmethod
|
|
46
|
+
async def run_streamed(self, user_input: str, context: TContext) -> RunResultStreaming:
|
|
47
|
+
"""
|
|
48
|
+
Execute Streamed the agent with the given user input and context
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
user_input (str): The input provided by the user.
|
|
52
|
+
context (TContext): The context object containing relevant information for execution.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
RunResultStreaming: The stream result of the agent's execution.
|
|
56
|
+
"""
|
|
57
|
+
pass
|
|
58
|
+
|
|
59
|
+
def get_interactive_mode(self) -> bool:
|
|
60
|
+
"""
|
|
61
|
+
获取当前的交互模式状态
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
bool: True为交互模式,False为非交互模式
|
|
65
|
+
"""
|
|
66
|
+
# 检查是否有--prompt参数,如果有则为非交互模式
|
|
67
|
+
import sys
|
|
68
|
+
return '--prompt' not in sys.argv and '-p' not in sys.argv
|
|
69
|
+
|
|
70
|
+
def get_repo_map_model_name(self) -> str:
|
|
71
|
+
"""
|
|
72
|
+
Get the model name used for repo map generation
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
str: Model name, defaults to claude-sonnet-4
|
|
76
|
+
"""
|
|
77
|
+
try:
|
|
78
|
+
# Read configuration file
|
|
79
|
+
config_path = os.path.join(os.getcwd(), "agent_config.yaml")
|
|
80
|
+
if os.path.exists(config_path):
|
|
81
|
+
with open(config_path, 'r', encoding='utf-8') as f:
|
|
82
|
+
config = yaml.safe_load(f)
|
|
83
|
+
llm_config = config.get('llm_config', {})
|
|
84
|
+
return llm_config.get('model_name', 'claude-sonnet-4')
|
|
85
|
+
except Exception as e:
|
|
86
|
+
logging.warning(f"Failed to read agent config file for repo map model name: {str(e)}")
|
|
87
|
+
|
|
88
|
+
# If reading configuration fails, use default value
|
|
89
|
+
return 'claude-sonnet-4'
|
|
90
|
+
|
|
91
|
+
def get_repo_map_instance(self, root_dir: str):
|
|
92
|
+
"""
|
|
93
|
+
Get RepoMap instance
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
root_dir (str): Repository root directory
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
RepoMap: Configured RepoMap instance
|
|
100
|
+
"""
|
|
101
|
+
try:
|
|
102
|
+
|
|
103
|
+
# Read configuration
|
|
104
|
+
config_path = os.path.join(os.getcwd(), "agent_config.yaml")
|
|
105
|
+
llm_config = {}
|
|
106
|
+
if os.path.exists(config_path):
|
|
107
|
+
try:
|
|
108
|
+
with open(config_path, 'r', encoding='utf-8') as f:
|
|
109
|
+
config = yaml.safe_load(f)
|
|
110
|
+
llm_config = config.get('llm_config', {})
|
|
111
|
+
except Exception as e:
|
|
112
|
+
logging.warning(f"Failed to read agent config file for repo map instance: {str(e)}")
|
|
113
|
+
|
|
114
|
+
# Get configuration parameters
|
|
115
|
+
model_name = llm_config.get('model_name', 'claude-sonnet-4')
|
|
116
|
+
repo_map_tokens = llm_config.get('repo_map_tokens', 8192)
|
|
117
|
+
repo_map_mul_no_files = llm_config.get('repo_map_mul_no_files', 16)
|
|
118
|
+
repo_verbose = llm_config.get('repo_verbose', True)
|
|
119
|
+
|
|
120
|
+
# Create components
|
|
121
|
+
token_counter = TokenCounterModel(model_name)
|
|
122
|
+
io = SilentIO() # Use silent IO to avoid output interference
|
|
123
|
+
|
|
124
|
+
return RepoMap(
|
|
125
|
+
root=root_dir,
|
|
126
|
+
main_model=token_counter,
|
|
127
|
+
io=io,
|
|
128
|
+
verbose=repo_verbose,
|
|
129
|
+
map_tokens=repo_map_tokens,
|
|
130
|
+
map_mul_no_files=repo_map_mul_no_files
|
|
131
|
+
)
|
|
132
|
+
except Exception as e:
|
|
133
|
+
logging.warning(f"Failed to create RepoMap instance for root directory '{root_dir}': {str(e)}")
|
|
134
|
+
# If creation fails, return None
|
|
135
|
+
return None
|
|
136
|
+
|
|
137
|
+
async def prepare_run_environment(
|
|
138
|
+
self,
|
|
139
|
+
context: TContext | None = None,
|
|
140
|
+
):
|
|
141
|
+
running_session = context.session
|
|
142
|
+
if running_session is None:
|
|
143
|
+
running_session = RunningSessionManager.get_default_session()
|
|
144
|
+
context.session = running_session
|
|
145
|
+
|
|
146
|
+
llm_config = running_session.siada_config.llm_config
|
|
147
|
+
model_settings = ModelSettingsConverter.convert_model_settings(llm_config)
|
|
148
|
+
model_provider_name = llm_config.provider
|
|
149
|
+
model_provider = get_provider(model_provider_name)
|
|
150
|
+
|
|
151
|
+
# Store provider name (string) in context for client factory
|
|
152
|
+
context.provider = model_provider_name
|
|
153
|
+
|
|
154
|
+
# if running_session.running_config.interactive:
|
|
155
|
+
# ## in the interactive mode, we need to add the ask_followup_question tool
|
|
156
|
+
# if ask_followup_question not in self.tools:
|
|
157
|
+
# self.tools.append(ask_followup_question)
|
|
158
|
+
|
|
159
|
+
run_config = RunConfig(
|
|
160
|
+
tracing_disabled=running_session.siada_config.tracing_disabled,
|
|
161
|
+
model=llm_config.model_name,
|
|
162
|
+
model_provider=model_provider,
|
|
163
|
+
model_settings=model_settings
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
session = running_session.state.openai_session
|
|
167
|
+
return run_config, session
|
|
168
|
+
|
|
169
|
+
async def run_impl(
|
|
170
|
+
self,
|
|
171
|
+
starting_agent: Agent[TContext],
|
|
172
|
+
input: str | list[TResponseInputItem],
|
|
173
|
+
context: TContext | None = None,
|
|
174
|
+
max_turns: int = 10,
|
|
175
|
+
hooks: RunHooks[TContext] | None = None,
|
|
176
|
+
previous_response_id: str | None = None,
|
|
177
|
+
) -> RunResult:
|
|
178
|
+
|
|
179
|
+
run_config, session = await self.prepare_run_environment(context)
|
|
180
|
+
|
|
181
|
+
return await Runner.run(
|
|
182
|
+
starting_agent=starting_agent,
|
|
183
|
+
input=input,
|
|
184
|
+
context=context,
|
|
185
|
+
max_turns=max_turns,
|
|
186
|
+
hooks=hooks,
|
|
187
|
+
run_config=run_config,
|
|
188
|
+
previous_response_id=previous_response_id,
|
|
189
|
+
session=session,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
async def run_streamed_impl(
|
|
193
|
+
self,
|
|
194
|
+
starting_agent: Agent[TContext],
|
|
195
|
+
input: str | list[TResponseInputItem],
|
|
196
|
+
context: TContext | None = None,
|
|
197
|
+
max_turns: int = 10,
|
|
198
|
+
hooks: RunHooks[TContext] | None = None,
|
|
199
|
+
run_config: RunConfig | None = None,
|
|
200
|
+
previous_response_id: str | None = None,
|
|
201
|
+
) -> RunResultStreaming:
|
|
202
|
+
|
|
203
|
+
run_config, session = await self.prepare_run_environment(context)
|
|
204
|
+
|
|
205
|
+
return Runner.run_streamed(
|
|
206
|
+
starting_agent=starting_agent,
|
|
207
|
+
input=input,
|
|
208
|
+
context=context,
|
|
209
|
+
max_turns=max_turns,
|
|
210
|
+
hooks=hooks,
|
|
211
|
+
run_config=run_config,
|
|
212
|
+
previous_response_id=previous_response_id,
|
|
213
|
+
session=session,
|
|
214
|
+
)
|
siada/config/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import yaml
|
|
9
|
+
from dataclasses import dataclass, asdict, field
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Optional, Dict, Any
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True)
|
|
15
|
+
class LLMConfig:
|
|
16
|
+
"""LLM configuration class"""
|
|
17
|
+
model: Optional[str] = None
|
|
18
|
+
provider: Optional[str] = None
|
|
19
|
+
|
|
20
|
+
@classmethod
|
|
21
|
+
def from_dict(cls, data: Dict[str, Any]) -> 'LLMConfig':
|
|
22
|
+
"""Create LLMConfig instance from dictionary"""
|
|
23
|
+
return cls(
|
|
24
|
+
model=data.get('model'),
|
|
25
|
+
provider=data.get('provider')
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
29
|
+
"""Convert to dictionary"""
|
|
30
|
+
return asdict(self)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _get_default_config_path() -> Path:
|
|
34
|
+
"""Get default configuration file path"""
|
|
35
|
+
home_dir = Path.home()
|
|
36
|
+
return home_dir / '.siada-cli' / 'conf.yaml'
|
|
37
|
+
|
|
38
|
+
@dataclass(frozen=True)
|
|
39
|
+
class Config:
|
|
40
|
+
"""Main configuration class (immutable)"""
|
|
41
|
+
llm_config: LLMConfig = field(default_factory=LLMConfig)
|
|
42
|
+
|
|
43
|
+
def load_conf(config_path: Optional[Path] = None) -> 'Config':
|
|
44
|
+
"""Load configuration from YAML file"""
|
|
45
|
+
if config_path is None:
|
|
46
|
+
config_path = _get_default_config_path()
|
|
47
|
+
|
|
48
|
+
llm_config = LLMConfig()
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
if config_path.exists():
|
|
52
|
+
with open(config_path, 'r', encoding='utf-8') as file:
|
|
53
|
+
data = yaml.safe_load(file) or {}
|
|
54
|
+
|
|
55
|
+
# Load LLM configuration
|
|
56
|
+
if 'llm_config' in data:
|
|
57
|
+
llm_config = LLMConfig.from_dict(data['llm_config'])
|
|
58
|
+
# If config file doesn't exist, return default values without creating directories
|
|
59
|
+
|
|
60
|
+
except yaml.YAMLError as e:
|
|
61
|
+
print(f"Warning: Configuration file format error: {e}")
|
|
62
|
+
except Exception as e:
|
|
63
|
+
print(f"Warning: Failed to load configuration file: {e}")
|
|
64
|
+
|
|
65
|
+
return Config(llm_config=llm_config)
|
|
66
|
+
|
|
67
|
+
|
|
File without changes
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import configargparse
|
|
9
|
+
import shtab
|
|
10
|
+
|
|
11
|
+
from siada import __version__
|
|
12
|
+
from siada.services.siada_runner import SiadaRunner
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def default_env_file(git_root):
|
|
16
|
+
return os.path.join(git_root, ".env") if git_root else ".env"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_parser(default_config_files, git_root):
|
|
20
|
+
parser = configargparse.ArgumentParser(
|
|
21
|
+
description="Siada-CLI is AI pair programming in your terminal",
|
|
22
|
+
add_config_file_help=True,
|
|
23
|
+
default_config_files=default_config_files,
|
|
24
|
+
config_file_parser_class=configargparse.YAMLConfigFileParser,
|
|
25
|
+
auto_env_var_prefix="SIADA_",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# Load agent configurations from config file
|
|
29
|
+
try:
|
|
30
|
+
agent_configs = SiadaRunner._load_agent_config()
|
|
31
|
+
# Get enabled agent types for choices
|
|
32
|
+
agent_choices = [name for name, config in agent_configs.items()
|
|
33
|
+
if config.get('enabled', False) and config.get('class')]
|
|
34
|
+
except Exception as e:
|
|
35
|
+
# Fallback to default if config loading fails
|
|
36
|
+
agent_configs = {}
|
|
37
|
+
agent_choices = ['bugfix', 'coder', 'fegen', 'bugreproduce']
|
|
38
|
+
print(f"Warning: Failed to load agent config, using defaults: {e}")
|
|
39
|
+
|
|
40
|
+
##########
|
|
41
|
+
group = parser.add_argument_group("agent config")
|
|
42
|
+
|
|
43
|
+
group.add_argument(
|
|
44
|
+
"--agent",
|
|
45
|
+
"-a",
|
|
46
|
+
metavar="AGENT",
|
|
47
|
+
choices=agent_choices,
|
|
48
|
+
default="coder",
|
|
49
|
+
help=f"Specify the agent type to use (choices: {', '.join(agent_choices)}, default: coder)",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Generate individual agent command arguments
|
|
53
|
+
for agent_name in agent_choices:
|
|
54
|
+
agent_config = agent_configs.get(agent_name, {})
|
|
55
|
+
description = agent_config.get('description', f'{agent_name.title()} agent')
|
|
56
|
+
|
|
57
|
+
group.add_argument(
|
|
58
|
+
f"--{agent_name}",
|
|
59
|
+
action="store_const",
|
|
60
|
+
dest="agent",
|
|
61
|
+
const=agent_name,
|
|
62
|
+
help=f"Use {description}",
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
##########
|
|
66
|
+
group = parser.add_argument_group("prompt config")
|
|
67
|
+
group.add_argument(
|
|
68
|
+
"--prompt",
|
|
69
|
+
"-p",
|
|
70
|
+
metavar="PROMPT",
|
|
71
|
+
default=None,
|
|
72
|
+
help="Specify the prompt, if provided, it will be activated for the no interaction mode",
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
##########
|
|
76
|
+
group = parser.add_argument_group("API Keys and settings")
|
|
77
|
+
group.add_argument(
|
|
78
|
+
"--env-file",
|
|
79
|
+
metavar="ENV_FILE",
|
|
80
|
+
default=default_env_file(git_root),
|
|
81
|
+
help="Specify the .env file to load (default: .env in git root)",
|
|
82
|
+
).complete = shtab.FILE
|
|
83
|
+
|
|
84
|
+
group.add_argument(
|
|
85
|
+
"--set-env",
|
|
86
|
+
action="append",
|
|
87
|
+
metavar="ENV_VAR_NAME=value",
|
|
88
|
+
help="Set an environment variable (to control API settings, can be used multiple times)",
|
|
89
|
+
default=[],
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
group = parser.add_argument_group("Model settings")
|
|
93
|
+
|
|
94
|
+
group.add_argument(
|
|
95
|
+
"--model",
|
|
96
|
+
metavar="MODEL",
|
|
97
|
+
default=None,
|
|
98
|
+
help="Specify the model to use for the main chat",
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
group.add_argument(
|
|
102
|
+
"--list-models",
|
|
103
|
+
"--models",
|
|
104
|
+
action="store_true",
|
|
105
|
+
help="List all available models",
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
group.add_argument(
|
|
109
|
+
"--reasoning-effort",
|
|
110
|
+
type=str,
|
|
111
|
+
help="Set the reasoning_effort API parameter (default: not set)",
|
|
112
|
+
)
|
|
113
|
+
group.add_argument(
|
|
114
|
+
"--thinking-tokens",
|
|
115
|
+
type=str,
|
|
116
|
+
help=(
|
|
117
|
+
"Set the thinking token budget for models that support it. Use 0 to disable. (default:"
|
|
118
|
+
" not set)"
|
|
119
|
+
),
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
group.add_argument(
|
|
123
|
+
"--provider",
|
|
124
|
+
choices=["openrouter", "li"],
|
|
125
|
+
default=None,
|
|
126
|
+
help="Specify the provider to use for the main chat (choices: openrouter, li, default: li)",
|
|
127
|
+
metavar="PROVIDER",
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
group = parser.add_argument_group("Output settings")
|
|
131
|
+
group.add_argument(
|
|
132
|
+
"--theme",
|
|
133
|
+
choices=["default", "dark", "light"],
|
|
134
|
+
default="dark",
|
|
135
|
+
help="Select color theme: default, dark, or light (default: None, auto-detect or use individual mode flags)",
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
group.add_argument(
|
|
139
|
+
"--pretty",
|
|
140
|
+
action=argparse.BooleanOptionalAction,
|
|
141
|
+
default=True,
|
|
142
|
+
help="Enable/disable pretty, colorized output (default: True)",
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
group.add_argument(
|
|
146
|
+
"--fancy-input",
|
|
147
|
+
action=argparse.BooleanOptionalAction,
|
|
148
|
+
default=True,
|
|
149
|
+
help="Enable/disable fancy input (default: True)",
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
group = parser.add_argument_group("Upgrading")
|
|
153
|
+
group.add_argument(
|
|
154
|
+
"--version",
|
|
155
|
+
action="version",
|
|
156
|
+
version=f"%(prog)s {__version__}",
|
|
157
|
+
help="Show the version number and exit",
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
######
|
|
161
|
+
group = parser.add_argument_group("Other settings")
|
|
162
|
+
|
|
163
|
+
group.add_argument(
|
|
164
|
+
"--vim",
|
|
165
|
+
action="store_true",
|
|
166
|
+
help="Use VI editing mode in the terminal (default: False)",
|
|
167
|
+
default=False,
|
|
168
|
+
)
|
|
169
|
+
group.add_argument(
|
|
170
|
+
"-v",
|
|
171
|
+
"--verbose",
|
|
172
|
+
action="store_true",
|
|
173
|
+
help="Enable verbose output",
|
|
174
|
+
default=False,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
group.add_argument(
|
|
178
|
+
"--encoding",
|
|
179
|
+
default="utf-8",
|
|
180
|
+
help="Specify the encoding for input and output (default: utf-8)",
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
group.add_argument(
|
|
184
|
+
"--editor",
|
|
185
|
+
help="Specify which editor to use for the /editor command",
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
group.add_argument(
|
|
189
|
+
"--disable-console-output",
|
|
190
|
+
action=argparse.BooleanOptionalAction,
|
|
191
|
+
default=True,
|
|
192
|
+
help="Disable console output for debugging (default: True)",
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
return parser
|
|
File without changes
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import Optional, Any
|
|
3
|
+
import traceback
|
|
4
|
+
|
|
5
|
+
from siada.entrypoint.interaction.config import RunningConfig
|
|
6
|
+
from siada.entrypoint.interaction.turn_models import TurnInput, TurnOutput, TurnType
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class RunTurn(ABC):
|
|
10
|
+
"""Abstract base class for interaction turns"""
|
|
11
|
+
|
|
12
|
+
def __init__(self, config: RunningConfig, session: Any, slash_commands: Any):
|
|
13
|
+
"""Initialize turn with configuration and session
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
config: InteractionConfig with execution parameters
|
|
17
|
+
session: Current session instance
|
|
18
|
+
"""
|
|
19
|
+
self.config = config
|
|
20
|
+
self.session = session
|
|
21
|
+
self.slash_commands = slash_commands
|
|
22
|
+
|
|
23
|
+
# Data tracking
|
|
24
|
+
self.input_data: Optional[TurnInput] = None
|
|
25
|
+
self.output_data: Optional[TurnOutput] = None
|
|
26
|
+
self.start_time: Optional[float] = None
|
|
27
|
+
self.end_time: Optional[float] = None
|
|
28
|
+
self.error: Optional[Exception] = None
|
|
29
|
+
|
|
30
|
+
@abstractmethod
|
|
31
|
+
def execute(self, turn_input: TurnInput) -> TurnOutput:
|
|
32
|
+
"""Execute the turn with given input
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
turn_input: Input data for this turn
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
TurnOutput: Result of turn execution
|
|
39
|
+
"""
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
def can_handle(self, user_input: str) -> bool:
|
|
43
|
+
"""Check if this turn type can handle the given input
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
user_input: Raw user input string
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
bool: True if this turn can handle the input
|
|
50
|
+
"""
|
|
51
|
+
return True
|
|
52
|
+
|
|
53
|
+
def prepare_input(self, raw_input: str) -> TurnInput:
|
|
54
|
+
"""Prepare turn input from raw user input
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
raw_input: Raw user input string
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
TurnInput: Prepared input for turn execution
|
|
61
|
+
"""
|
|
62
|
+
return TurnInput(use_input=raw_input)
|
|
63
|
+
|
|
64
|
+
def get_turn_type(self) -> TurnType:
|
|
65
|
+
"""Get the type of this turn
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
TurnType: Type of this turn
|
|
69
|
+
"""
|
|
70
|
+
return TurnType.CONVERSATION
|
|
71
|
+
|
|
72
|
+
def validate_input(self, turn_input: TurnInput) -> bool:
|
|
73
|
+
"""Validate turn input
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
turn_input: Input to validate
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
bool: True if input is valid
|
|
80
|
+
"""
|
|
81
|
+
return bool(turn_input.use_input and turn_input.use_input.strip())
|
|
82
|
+
|
|
83
|
+
def handle_error(self, error: Exception) -> TurnOutput:
|
|
84
|
+
"""Handle execution error
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
error: Exception that occurred
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
TurnOutput: Error response
|
|
91
|
+
"""
|
|
92
|
+
self.error = error
|
|
93
|
+
|
|
94
|
+
full_traceback = traceback.format_exc()
|
|
95
|
+
self.config.io.print_error(f"Error occurred: {str(error)}\n\nFull traceback:\n{full_traceback}")
|
|
96
|
+
|
|
97
|
+
return TurnOutput(
|
|
98
|
+
output=f"Error: {str(error)}",
|
|
99
|
+
metadata={"error_type": type(error).__name__},
|
|
100
|
+
next_action=None,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
def _get_timestamp(self) -> float:
|
|
104
|
+
"""Get current timestamp"""
|
|
105
|
+
import time
|
|
106
|
+
|
|
107
|
+
return time.time()
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration classes for interaction module
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Optional
|
|
7
|
+
from rlcompleter import Completer
|
|
8
|
+
|
|
9
|
+
from siada.io.color_settings import RunningConfigColorSettings
|
|
10
|
+
from siada.io.io import InputOutput
|
|
11
|
+
from siada.models.model_run_config import ModelRunConfig
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class RunningConfig:
|
|
16
|
+
"""Configuration data class for interaction controller"""
|
|
17
|
+
|
|
18
|
+
# Required fields (no default values)
|
|
19
|
+
llm_config: ModelRunConfig
|
|
20
|
+
io: InputOutput
|
|
21
|
+
workspace: str
|
|
22
|
+
agent_name: str
|
|
23
|
+
|
|
24
|
+
# Optional fields (with default values)
|
|
25
|
+
completer: Optional[Completer] = None
|
|
26
|
+
running_color_settings: Optional[RunningConfigColorSettings] = None
|
|
27
|
+
max_turns: int = 10
|
|
28
|
+
tracing_disabled: bool = False
|
|
29
|
+
console_output: bool = False
|
|
30
|
+
interactive: bool = True
|