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
siada/io/key_bindings.py
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import signal
|
|
3
|
+
|
|
4
|
+
from prompt_toolkit.enums import EditingMode
|
|
5
|
+
from prompt_toolkit.filters import Condition, is_searching, has_completions
|
|
6
|
+
from prompt_toolkit.key_binding import KeyBindings
|
|
7
|
+
from prompt_toolkit.key_binding.vi_state import InputMode
|
|
8
|
+
from prompt_toolkit.keys import Keys
|
|
9
|
+
|
|
10
|
+
from siada.support.editor import pipe_editor
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class KeyBindingsFactory:
|
|
14
|
+
"""A factory for creating key bindings for the IO instance."""
|
|
15
|
+
def __init__(self, io_instance):
|
|
16
|
+
self.io = io_instance
|
|
17
|
+
|
|
18
|
+
def create_key_bindings(self):
|
|
19
|
+
kb = KeyBindings()
|
|
20
|
+
|
|
21
|
+
def suspend_to_bg(event):
|
|
22
|
+
"""Suspend currently running application."""
|
|
23
|
+
event.app.suspend_to_background()
|
|
24
|
+
|
|
25
|
+
@kb.add(Keys.ControlZ, filter=Condition(lambda: hasattr(signal, "SIGTSTP")))
|
|
26
|
+
def _(event):
|
|
27
|
+
"Suspend to background with ctrl-z"
|
|
28
|
+
suspend_to_bg(event)
|
|
29
|
+
|
|
30
|
+
@kb.add("c-space")
|
|
31
|
+
def _(event):
|
|
32
|
+
"Ignore Ctrl when pressing space bar"
|
|
33
|
+
event.current_buffer.insert_text(" ")
|
|
34
|
+
|
|
35
|
+
@kb.add("c-up")
|
|
36
|
+
def _(event):
|
|
37
|
+
"Navigate backward through history"
|
|
38
|
+
event.current_buffer.history_backward()
|
|
39
|
+
|
|
40
|
+
@kb.add("c-down")
|
|
41
|
+
def _(event):
|
|
42
|
+
"Navigate forward through history"
|
|
43
|
+
event.current_buffer.history_forward()
|
|
44
|
+
|
|
45
|
+
@kb.add("c-x", "c-e")
|
|
46
|
+
def _(event):
|
|
47
|
+
"Edit current input in external editor (like Bash)"
|
|
48
|
+
buffer = event.current_buffer
|
|
49
|
+
current_text = buffer.text
|
|
50
|
+
|
|
51
|
+
# Open the editor with the current text
|
|
52
|
+
edited_text = pipe_editor(input_data=current_text, suffix="md")
|
|
53
|
+
|
|
54
|
+
# Replace the buffer with the edited text, strip any trailing newlines
|
|
55
|
+
buffer.text = edited_text.rstrip("\n")
|
|
56
|
+
|
|
57
|
+
# Move cursor to the end of the text
|
|
58
|
+
buffer.cursor_position = len(buffer.text)
|
|
59
|
+
|
|
60
|
+
@kb.add("enter", eager=True, filter=~is_searching & ~has_completions)
|
|
61
|
+
def _(event):
|
|
62
|
+
"Handle Enter key press when no completions are shown"
|
|
63
|
+
if self.io.multiline_mode and not (
|
|
64
|
+
self.io.editingmode == EditingMode.VI
|
|
65
|
+
and event.app.vi_state.input_mode == InputMode.NAVIGATION
|
|
66
|
+
):
|
|
67
|
+
# In multiline mode and if not in vi-mode or vi navigation/normal mode,
|
|
68
|
+
# Enter adds a newline
|
|
69
|
+
event.current_buffer.insert_text("\n")
|
|
70
|
+
else:
|
|
71
|
+
# In normal mode, Enter submits
|
|
72
|
+
event.current_buffer.validate_and_handle()
|
|
73
|
+
|
|
74
|
+
@kb.add("enter", eager=True, filter=~is_searching & has_completions)
|
|
75
|
+
def _(event):
|
|
76
|
+
"Handle Enter key press when completions are shown"
|
|
77
|
+
buffer = event.current_buffer
|
|
78
|
+
|
|
79
|
+
# Check if this is an @ command completion using cursor position (consistent with completer.py)
|
|
80
|
+
text_before_cursor = buffer.document.text_before_cursor
|
|
81
|
+
at_pos = text_before_cursor.rfind("@")
|
|
82
|
+
is_at_command = at_pos != -1
|
|
83
|
+
|
|
84
|
+
if buffer.complete_state and buffer.complete_state.completions:
|
|
85
|
+
# Ensure first completion is selected by default if none is currently selected
|
|
86
|
+
if buffer.complete_state.current_completion is None:
|
|
87
|
+
# Move to first completion to ensure it's highlighted
|
|
88
|
+
buffer.complete_next()
|
|
89
|
+
|
|
90
|
+
# Accept the currently selected completion
|
|
91
|
+
if buffer.complete_state.current_completion:
|
|
92
|
+
buffer.apply_completion(buffer.complete_state.current_completion)
|
|
93
|
+
|
|
94
|
+
# For @ commands, only accept completion without submitting
|
|
95
|
+
# For other commands (like /), accept completion and submit
|
|
96
|
+
if not is_at_command:
|
|
97
|
+
# For non-@ commands, also submit after accepting completion
|
|
98
|
+
if self.io.multiline_mode and not (
|
|
99
|
+
self.io.editingmode == EditingMode.VI
|
|
100
|
+
and event.app.vi_state.input_mode == InputMode.NAVIGATION
|
|
101
|
+
):
|
|
102
|
+
# In multiline mode, Enter adds a newline after accepting completion
|
|
103
|
+
buffer.insert_text("\n")
|
|
104
|
+
else:
|
|
105
|
+
# In normal mode, Enter submits after accepting completion
|
|
106
|
+
buffer.validate_and_handle()
|
|
107
|
+
else:
|
|
108
|
+
# No completions available, just handle enter normally
|
|
109
|
+
if self.io.multiline_mode and not (
|
|
110
|
+
self.io.editingmode == EditingMode.VI
|
|
111
|
+
and event.app.vi_state.input_mode == InputMode.NAVIGATION
|
|
112
|
+
):
|
|
113
|
+
# In multiline mode, Enter adds a newline
|
|
114
|
+
buffer.insert_text("\n")
|
|
115
|
+
else:
|
|
116
|
+
# In normal mode, Enter submits
|
|
117
|
+
buffer.validate_and_handle()
|
|
118
|
+
|
|
119
|
+
@kb.add("escape", "enter", eager=True, filter=~is_searching) # This is Alt+Enter
|
|
120
|
+
def _(event):
|
|
121
|
+
"Handle Alt+Enter key press"
|
|
122
|
+
if self.io.multiline_mode:
|
|
123
|
+
# In multiline mode, Alt+Enter submits
|
|
124
|
+
event.current_buffer.validate_and_handle()
|
|
125
|
+
else:
|
|
126
|
+
# In normal mode, Alt+Enter adds a newline
|
|
127
|
+
event.current_buffer.insert_text("\n")
|
|
128
|
+
|
|
129
|
+
return kb
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
|
|
2
|
+
import shutil
|
|
3
|
+
import platform
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class NotificationCommandUtil:
|
|
7
|
+
"""A utility class for handling notification commands."""
|
|
8
|
+
|
|
9
|
+
NOTIFICATION_MESSAGE = "Siada is waiting for your input"
|
|
10
|
+
|
|
11
|
+
@staticmethod
|
|
12
|
+
def get_default_notification_command():
|
|
13
|
+
"""Return a default notification command based on the operating system."""
|
|
14
|
+
system = platform.system()
|
|
15
|
+
|
|
16
|
+
if system == "Darwin": # macOS
|
|
17
|
+
# Check for terminal-notifier first
|
|
18
|
+
if shutil.which("terminal-notifier"):
|
|
19
|
+
return f"terminal-notifier -title 'Siada' -message '{NotificationCommandUtil.NOTIFICATION_MESSAGE}'"
|
|
20
|
+
# Fall back to osascript
|
|
21
|
+
return f'osascript -e \'display notification "{NotificationCommandUtil.NOTIFICATION_MESSAGE}" with title "Siada"\''
|
|
22
|
+
elif system == "Linux":
|
|
23
|
+
# Check for common Linux notification tools
|
|
24
|
+
for cmd in ["notify-send", "zenity"]:
|
|
25
|
+
if shutil.which(cmd):
|
|
26
|
+
if cmd == "notify-send":
|
|
27
|
+
return f"notify-send 'Siada' '{NotificationCommandUtil.NOTIFICATION_MESSAGE}'"
|
|
28
|
+
elif cmd == "zenity":
|
|
29
|
+
return f"zenity --notification --text='{NotificationCommandUtil.NOTIFICATION_MESSAGE}'"
|
|
30
|
+
return None # No known notification tool found
|
|
31
|
+
elif system == "Windows":
|
|
32
|
+
# PowerShell notification
|
|
33
|
+
return (
|
|
34
|
+
"powershell -command"
|
|
35
|
+
" \"[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms');"
|
|
36
|
+
f" [System.Windows.Forms.MessageBox]::Show('{NotificationCommandUtil.NOTIFICATION_MESSAGE}',"
|
|
37
|
+
" 'Siada')\""
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
return None # Unknown system
|
siada/io/test_banner.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Test script for banner display functionality.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
python test_banner.py [--pretty|--plain|--both]
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
# Add parent directory to path so we can import siada modules
|
|
14
|
+
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
|
|
15
|
+
|
|
16
|
+
from siada.io.banner import show_siada_banner, get_banner_text
|
|
17
|
+
from rich.console import Console
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_pretty_banner():
|
|
21
|
+
"""Test colorful banner with gradient."""
|
|
22
|
+
print("=== PRETTY BANNER (Left-to-Right Gradient) ===")
|
|
23
|
+
show_siada_banner(pretty=True)
|
|
24
|
+
print()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_plain_banner():
|
|
28
|
+
"""Test plain text banner."""
|
|
29
|
+
print("=== PLAIN BANNER ===")
|
|
30
|
+
show_siada_banner(pretty=False)
|
|
31
|
+
print()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_text_banner():
|
|
35
|
+
"""Test banner as string."""
|
|
36
|
+
print("=== BANNER AS TEXT ===")
|
|
37
|
+
print(get_banner_text())
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def main():
|
|
41
|
+
parser = argparse.ArgumentParser(description="Test SIADA HUB banner display")
|
|
42
|
+
parser.add_argument("--pretty", action="store_true", help="Show only pretty banner")
|
|
43
|
+
parser.add_argument("--plain", action="store_true", help="Show only plain banner")
|
|
44
|
+
parser.add_argument("--both", action="store_true", help="Show both versions")
|
|
45
|
+
|
|
46
|
+
args = parser.parse_args()
|
|
47
|
+
|
|
48
|
+
if args.pretty:
|
|
49
|
+
test_pretty_banner()
|
|
50
|
+
elif args.plain:
|
|
51
|
+
test_plain_banner()
|
|
52
|
+
elif args.both:
|
|
53
|
+
test_pretty_banner()
|
|
54
|
+
test_plain_banner()
|
|
55
|
+
test_text_banner()
|
|
56
|
+
else:
|
|
57
|
+
# Default: show all versions
|
|
58
|
+
test_pretty_banner()
|
|
59
|
+
test_plain_banner()
|
|
60
|
+
test_text_banner()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
if __name__ == "__main__":
|
|
64
|
+
main()
|
siada/models/__init__.py
ADDED
|
File without changes
|
siada/models/agent.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Agent数据模型
|
|
3
|
+
|
|
4
|
+
定义与Agent相关的请求和响应模型
|
|
5
|
+
"""
|
|
6
|
+
from typing import Any, Dict, List, Optional
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Tool(BaseModel):
|
|
12
|
+
"""工具模型"""
|
|
13
|
+
name: str = Field(..., description="工具名称")
|
|
14
|
+
description: str = Field(..., description="工具描述")
|
|
15
|
+
function: str = Field(..., description="工具函数代码")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class CreateAgentRequest(BaseModel):
|
|
19
|
+
"""创建Agent的请求模型"""
|
|
20
|
+
name: str = Field(..., description="Agent名称")
|
|
21
|
+
instructions: str = Field(..., description="Agent指令")
|
|
22
|
+
tools: Optional[List[Tool]] = Field(None, description="Agent可以使用的工具列表")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class AgentResponse(BaseModel):
|
|
26
|
+
"""Agent响应模型"""
|
|
27
|
+
id: str = Field(..., description="Agent ID")
|
|
28
|
+
name: str = Field(..., description="Agent名称")
|
|
29
|
+
instructions: str = Field(..., description="Agent指令")
|
|
30
|
+
model: str = Field(..., description="使用的模型名称")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class RunAgentRequest(BaseModel):
|
|
34
|
+
"""运行Agent的请求模型"""
|
|
35
|
+
agent_name: str = Field(..., description="Agent 名称")
|
|
36
|
+
input: str = Field(..., description="输入文本")
|
|
37
|
+
model: Optional[str] = Field("gpt-4o", description="使用的模型名称")
|
|
38
|
+
max_turns: Optional[int] = Field(10, description="最大运行轮数")
|
|
39
|
+
session_id: Optional[str] = Field(None, description="会话ID")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class RunAgentResponse(BaseModel):
|
|
43
|
+
"""运行Agent的响应模型"""
|
|
44
|
+
final_output: str = Field(..., description="最终输出")
|
|
45
|
+
turns: int = Field(..., description="运行轮数")
|
|
46
|
+
completed: bool = Field(..., description="是否完成")
|
|
47
|
+
trace_id: Optional[str] = Field(None, description="跟踪ID")
|
|
File without changes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from agents import ModelSettings
|
|
2
|
+
from litellm import Reasoning
|
|
3
|
+
from siada.models.model_base_config import is_gemini_model
|
|
4
|
+
from siada.models.model_run_config import ModelRunConfig
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ModelSettingsConverter:
|
|
8
|
+
|
|
9
|
+
@staticmethod
|
|
10
|
+
def convert_model_settings(model_running_config: ModelRunConfig) -> ModelSettings:
|
|
11
|
+
|
|
12
|
+
extra_body = {}
|
|
13
|
+
reasoning = {}
|
|
14
|
+
if model_running_config.get_reasoning_effort() is not None:
|
|
15
|
+
reasoning["effort"] = model_running_config.get_reasoning_effort()
|
|
16
|
+
if model_running_config.get_raw_thinking_tokens() is not None:
|
|
17
|
+
reasoning["max_tokens"] = model_running_config.get_raw_thinking_tokens()
|
|
18
|
+
|
|
19
|
+
if reasoning:
|
|
20
|
+
extra_body["reasoning"] = reasoning
|
|
21
|
+
|
|
22
|
+
model_settings = ModelSettings(
|
|
23
|
+
max_tokens=model_running_config.max_tokens,
|
|
24
|
+
extra_body=extra_body,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
return model_settings
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Optional, List
|
|
3
|
+
|
|
4
|
+
@dataclass
|
|
5
|
+
class ModelBaseConfig:
|
|
6
|
+
"""
|
|
7
|
+
Represents the configuration for a specific language model.
|
|
8
|
+
"""
|
|
9
|
+
model_name: str
|
|
10
|
+
context_window: int
|
|
11
|
+
max_tokens: Optional[int] = None
|
|
12
|
+
supports_images: bool = False
|
|
13
|
+
supports_prompt_cache: bool = False
|
|
14
|
+
|
|
15
|
+
supports_extra_params: Optional[List[str]] = None
|
|
16
|
+
|
|
17
|
+
# Simple list of all model configurations
|
|
18
|
+
MODEL_SETTING: List[ModelBaseConfig] = [
|
|
19
|
+
|
|
20
|
+
# ModelBaseConfig(
|
|
21
|
+
# model_name="o3-pro",
|
|
22
|
+
# max_tokens=8192,
|
|
23
|
+
# context_window=200_000,
|
|
24
|
+
# supports_images=True,
|
|
25
|
+
# supports_extra_params=["reasoning_effort"],
|
|
26
|
+
# ),
|
|
27
|
+
ModelBaseConfig(
|
|
28
|
+
model_name="claude-opus-4.1",
|
|
29
|
+
max_tokens=8192,
|
|
30
|
+
context_window=200_000,
|
|
31
|
+
supports_extra_params=[],
|
|
32
|
+
),
|
|
33
|
+
|
|
34
|
+
ModelBaseConfig(
|
|
35
|
+
model_name="gpt-5",
|
|
36
|
+
max_tokens=8192,
|
|
37
|
+
context_window=400_000,
|
|
38
|
+
supports_images=True,
|
|
39
|
+
supports_extra_params=["reasoning_effort"],
|
|
40
|
+
),
|
|
41
|
+
|
|
42
|
+
ModelBaseConfig(
|
|
43
|
+
model_name="gpt-5-mini",
|
|
44
|
+
max_tokens=8192,
|
|
45
|
+
context_window=400_000,
|
|
46
|
+
supports_images=True,
|
|
47
|
+
supports_extra_params=["reasoning_effort"],
|
|
48
|
+
),
|
|
49
|
+
|
|
50
|
+
ModelBaseConfig(
|
|
51
|
+
model_name="gpt-4.1",
|
|
52
|
+
max_tokens=8192,
|
|
53
|
+
context_window=1_047_576,
|
|
54
|
+
supports_images=True,
|
|
55
|
+
),
|
|
56
|
+
|
|
57
|
+
ModelBaseConfig(
|
|
58
|
+
model_name="claude-opus-4",
|
|
59
|
+
max_tokens=8192,
|
|
60
|
+
context_window=200_000,
|
|
61
|
+
supports_images=True,
|
|
62
|
+
supports_extra_params=[],
|
|
63
|
+
),
|
|
64
|
+
ModelBaseConfig(
|
|
65
|
+
model_name="claude-sonnet-4",
|
|
66
|
+
max_tokens=8192,
|
|
67
|
+
context_window=200_000,
|
|
68
|
+
supports_images=True,
|
|
69
|
+
supports_extra_params=[],
|
|
70
|
+
),
|
|
71
|
+
ModelBaseConfig(
|
|
72
|
+
model_name="claude-3.7-sonnet",
|
|
73
|
+
max_tokens=8192,
|
|
74
|
+
context_window=200_000,
|
|
75
|
+
supports_images=True,
|
|
76
|
+
supports_extra_params=[],
|
|
77
|
+
),
|
|
78
|
+
|
|
79
|
+
ModelBaseConfig(
|
|
80
|
+
model_name="gemini-2.5-pro",
|
|
81
|
+
max_tokens=8192,
|
|
82
|
+
context_window=1_048_576,
|
|
83
|
+
supports_extra_params=["thinking_tokens"],
|
|
84
|
+
),
|
|
85
|
+
|
|
86
|
+
ModelBaseConfig(
|
|
87
|
+
model_name="deepseek-v3-0324",
|
|
88
|
+
max_tokens=8192,
|
|
89
|
+
context_window=128_000,
|
|
90
|
+
),
|
|
91
|
+
|
|
92
|
+
ModelBaseConfig(
|
|
93
|
+
model_name="kimi-k2",
|
|
94
|
+
max_tokens=8192,
|
|
95
|
+
context_window=131_072,
|
|
96
|
+
),
|
|
97
|
+
]
|
|
98
|
+
|
|
99
|
+
def is_gemini_model(model_name: str) -> bool:
|
|
100
|
+
return model_name.startswith("gemini-")
|
|
101
|
+
|
|
102
|
+
def get_model_config(model_name: str) -> Optional[ModelBaseConfig]:
|
|
103
|
+
"""
|
|
104
|
+
Retrieves the configuration for a given model name.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
model_name: The name of the model to retrieve.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
A ModelSettings instance if the model is found, otherwise None.
|
|
111
|
+
"""
|
|
112
|
+
# Check if model_name is None or empty
|
|
113
|
+
if not model_name:
|
|
114
|
+
raise ValueError("Model name cannot be None or empty")
|
|
115
|
+
|
|
116
|
+
# Only exact match
|
|
117
|
+
for model_config in MODEL_SETTING:
|
|
118
|
+
if model_config.model_name == model_name:
|
|
119
|
+
return model_config
|
|
120
|
+
|
|
121
|
+
return None
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
from dataclasses import dataclass, fields
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from siada.models.model_base_config import ModelBaseConfig, get_model_config
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
import yaml
|
|
11
|
+
@dataclass()
|
|
12
|
+
class ModelRunConfig(ModelBaseConfig):
|
|
13
|
+
|
|
14
|
+
reasoning_effort : Optional[str] = None
|
|
15
|
+
thinking_tokens : Optional[int] = None
|
|
16
|
+
temperature : Optional[float] = None
|
|
17
|
+
extra_params : Optional[dict] = None
|
|
18
|
+
provider: Optional[str] = None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def __init__(self, model):
|
|
23
|
+
self.configure_model_settings(model)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _copy_fields(self, source):
|
|
27
|
+
"""Helper to copy fields from a ModelSettings instance to self"""
|
|
28
|
+
for field in fields(ModelBaseConfig):
|
|
29
|
+
val = getattr(source, field.name)
|
|
30
|
+
setattr(self, field.name, val)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def configure_model_settings(self, model):
|
|
34
|
+
# Look for exact model match
|
|
35
|
+
model_config = get_model_config(model)
|
|
36
|
+
if model_config:
|
|
37
|
+
self._copy_fields(model_config)
|
|
38
|
+
else:
|
|
39
|
+
raise ValueError(f"Model {model} not found in model settings")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def set_reasoning_effort(self, reasoning_effort):
|
|
43
|
+
self.reasoning_effort = reasoning_effort
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def get_raw_thinking_tokens(self):
|
|
47
|
+
"""Get formatted thinking token budget if available"""
|
|
48
|
+
return self.thinking_tokens
|
|
49
|
+
|
|
50
|
+
def get_thinking_tokens(self):
|
|
51
|
+
budget = self.get_raw_thinking_tokens()
|
|
52
|
+
|
|
53
|
+
if budget is not None:
|
|
54
|
+
# Format as xx.yK for thousands, xx.yM for millions
|
|
55
|
+
if budget >= 1024 * 1024:
|
|
56
|
+
value = budget / (1024 * 1024)
|
|
57
|
+
if value == int(value):
|
|
58
|
+
return f"{int(value)}M"
|
|
59
|
+
else:
|
|
60
|
+
return f"{value:.1f}M"
|
|
61
|
+
else:
|
|
62
|
+
value = budget / 1024
|
|
63
|
+
if value == int(value):
|
|
64
|
+
return f"{int(value)}k"
|
|
65
|
+
else:
|
|
66
|
+
return f"{value:.1f}k"
|
|
67
|
+
return None
|
|
68
|
+
|
|
69
|
+
def get_reasoning_effort(self):
|
|
70
|
+
"""Get reasoning effort value if available"""
|
|
71
|
+
return self.reasoning_effort
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def set_reasoning_effort(self, effort):
|
|
75
|
+
"""Set the reasoning effort parameter for models that support it"""
|
|
76
|
+
if effort is not None:
|
|
77
|
+
self.reasoning_effort = effort
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def set_thinking_tokens(self, value):
|
|
81
|
+
"""
|
|
82
|
+
Set the thinking token budget for models that support it.
|
|
83
|
+
Accepts formats: 8096, "8k", "10.5k", "0.5M", "10K", etc.
|
|
84
|
+
Pass "0" to disable thinking tokens.
|
|
85
|
+
"""
|
|
86
|
+
if value is not None:
|
|
87
|
+
num_tokens = self.parse_token_value(value)
|
|
88
|
+
self.temperature = None
|
|
89
|
+
if num_tokens > 0:
|
|
90
|
+
self.thinking_tokens = num_tokens
|
|
91
|
+
else:
|
|
92
|
+
self.thinking_tokens = None
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def parse_token_value(self, value):
|
|
96
|
+
"""
|
|
97
|
+
Parse a token value string into an integer.
|
|
98
|
+
Accepts formats: 8096, "8k", "10.5k", "0.5M", "10K", etc.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
value: String or int token value
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
Integer token value
|
|
105
|
+
"""
|
|
106
|
+
if isinstance(value, int):
|
|
107
|
+
return value
|
|
108
|
+
|
|
109
|
+
if not isinstance(value, str):
|
|
110
|
+
return int(value) # Try to convert to int
|
|
111
|
+
|
|
112
|
+
value = value.strip().upper()
|
|
113
|
+
|
|
114
|
+
if value.endswith("K"):
|
|
115
|
+
multiplier = 1024
|
|
116
|
+
value = value[:-1]
|
|
117
|
+
elif value.endswith("M"):
|
|
118
|
+
multiplier = 1024 * 1024
|
|
119
|
+
value = value[:-1]
|
|
120
|
+
else:
|
|
121
|
+
multiplier = 1
|
|
122
|
+
|
|
123
|
+
# Convert to float first to handle decimal values like "10.5k"
|
|
124
|
+
return int(float(value) * multiplier)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@staticmethod
|
|
128
|
+
def get_default_config():
|
|
129
|
+
project_root = Path(__file__).parent.parent.parent
|
|
130
|
+
config_path = project_root / "agent_config.yaml"
|
|
131
|
+
llm_config = {}
|
|
132
|
+
if os.path.exists(config_path):
|
|
133
|
+
try:
|
|
134
|
+
with open(config_path, 'r', encoding='utf-8') as f:
|
|
135
|
+
config = yaml.safe_load(f)
|
|
136
|
+
llm_config = config.get('llm_config', {})
|
|
137
|
+
except Exception as e:
|
|
138
|
+
logging.warning(f"Failed to read agent config file for repo map instance: {str(e)}")
|
|
139
|
+
model_name = llm_config.get('model_name')
|
|
140
|
+
provider = llm_config.get('provider')
|
|
141
|
+
|
|
142
|
+
model_config = ModelRunConfig(model_name)
|
|
143
|
+
|
|
144
|
+
model_config.provider = provider
|
|
145
|
+
return model_config
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
|
|
File without changes
|