ripperdoc 0.2.8__py3-none-any.whl → 0.2.9__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.
- ripperdoc/__init__.py +1 -1
- ripperdoc/cli/cli.py +28 -115
- ripperdoc/cli/commands/__init__.py +0 -1
- ripperdoc/cli/commands/agents_cmd.py +6 -3
- ripperdoc/cli/commands/clear_cmd.py +1 -4
- ripperdoc/cli/commands/config_cmd.py +1 -1
- ripperdoc/cli/commands/context_cmd.py +3 -2
- ripperdoc/cli/commands/doctor_cmd.py +18 -4
- ripperdoc/cli/commands/hooks_cmd.py +27 -53
- ripperdoc/cli/commands/models_cmd.py +26 -9
- ripperdoc/cli/commands/permissions_cmd.py +27 -9
- ripperdoc/cli/commands/resume_cmd.py +5 -3
- ripperdoc/cli/commands/status_cmd.py +4 -4
- ripperdoc/cli/commands/tasks_cmd.py +8 -4
- ripperdoc/cli/ui/file_mention_completer.py +2 -1
- ripperdoc/cli/ui/interrupt_handler.py +2 -3
- ripperdoc/cli/ui/message_display.py +4 -2
- ripperdoc/cli/ui/provider_options.py +247 -0
- ripperdoc/cli/ui/rich_ui.py +110 -59
- ripperdoc/cli/ui/spinner.py +25 -1
- ripperdoc/cli/ui/tool_renderers.py +8 -2
- ripperdoc/cli/ui/wizard.py +215 -0
- ripperdoc/core/agents.py +9 -3
- ripperdoc/core/config.py +49 -12
- ripperdoc/core/custom_commands.py +7 -6
- ripperdoc/core/default_tools.py +11 -2
- ripperdoc/core/hooks/config.py +1 -3
- ripperdoc/core/hooks/events.py +23 -28
- ripperdoc/core/hooks/executor.py +4 -6
- ripperdoc/core/hooks/integration.py +12 -21
- ripperdoc/core/hooks/manager.py +40 -15
- ripperdoc/core/permissions.py +40 -8
- ripperdoc/core/providers/anthropic.py +109 -36
- ripperdoc/core/providers/gemini.py +70 -5
- ripperdoc/core/providers/openai.py +60 -5
- ripperdoc/core/query.py +82 -38
- ripperdoc/core/query_utils.py +2 -0
- ripperdoc/core/skills.py +9 -3
- ripperdoc/core/system_prompt.py +4 -2
- ripperdoc/core/tool.py +9 -5
- ripperdoc/sdk/client.py +2 -2
- ripperdoc/tools/ask_user_question_tool.py +5 -3
- ripperdoc/tools/background_shell.py +2 -1
- ripperdoc/tools/bash_output_tool.py +1 -1
- ripperdoc/tools/bash_tool.py +26 -16
- ripperdoc/tools/dynamic_mcp_tool.py +29 -8
- ripperdoc/tools/enter_plan_mode_tool.py +1 -1
- ripperdoc/tools/exit_plan_mode_tool.py +1 -1
- ripperdoc/tools/file_edit_tool.py +8 -4
- ripperdoc/tools/file_read_tool.py +8 -4
- ripperdoc/tools/file_write_tool.py +9 -5
- ripperdoc/tools/glob_tool.py +3 -2
- ripperdoc/tools/grep_tool.py +3 -2
- ripperdoc/tools/kill_bash_tool.py +1 -1
- ripperdoc/tools/ls_tool.py +1 -1
- ripperdoc/tools/mcp_tools.py +13 -10
- ripperdoc/tools/multi_edit_tool.py +8 -7
- ripperdoc/tools/notebook_edit_tool.py +7 -4
- ripperdoc/tools/skill_tool.py +1 -1
- ripperdoc/tools/task_tool.py +5 -4
- ripperdoc/tools/todo_tool.py +2 -2
- ripperdoc/tools/tool_search_tool.py +3 -2
- ripperdoc/utils/conversation_compaction.py +8 -4
- ripperdoc/utils/file_watch.py +8 -2
- ripperdoc/utils/json_utils.py +2 -1
- ripperdoc/utils/mcp.py +11 -3
- ripperdoc/utils/memory.py +4 -2
- ripperdoc/utils/message_compaction.py +21 -7
- ripperdoc/utils/message_formatting.py +11 -7
- ripperdoc/utils/messages.py +105 -66
- ripperdoc/utils/path_ignore.py +35 -8
- ripperdoc/utils/permissions/path_validation_utils.py +2 -1
- ripperdoc/utils/permissions/shell_command_validation.py +427 -91
- ripperdoc/utils/safe_get_cwd.py +2 -1
- ripperdoc/utils/session_history.py +13 -6
- ripperdoc/utils/todo.py +2 -1
- ripperdoc/utils/token_estimation.py +6 -1
- {ripperdoc-0.2.8.dist-info → ripperdoc-0.2.9.dist-info}/METADATA +1 -1
- ripperdoc-0.2.9.dist-info/RECORD +123 -0
- ripperdoc-0.2.8.dist-info/RECORD +0 -121
- {ripperdoc-0.2.8.dist-info → ripperdoc-0.2.9.dist-info}/WHEEL +0 -0
- {ripperdoc-0.2.8.dist-info → ripperdoc-0.2.9.dist-info}/entry_points.txt +0 -0
- {ripperdoc-0.2.8.dist-info → ripperdoc-0.2.9.dist-info}/licenses/LICENSE +0 -0
- {ripperdoc-0.2.8.dist-info → ripperdoc-0.2.9.dist-info}/top_level.txt +0 -0
|
@@ -52,9 +52,7 @@ def _get_scope_info(scope: ScopeType, project_path: Path) -> tuple[str, str]:
|
|
|
52
52
|
return "Local (private)", str(project_path / ".ripperdoc" / "config.local.json")
|
|
53
53
|
|
|
54
54
|
|
|
55
|
-
def _get_rules_for_scope(
|
|
56
|
-
scope: ScopeType, project_path: Path
|
|
57
|
-
) -> tuple[List[str], List[str]]:
|
|
55
|
+
def _get_rules_for_scope(scope: ScopeType, project_path: Path) -> tuple[List[str], List[str]]:
|
|
58
56
|
"""Return (allow_rules, deny_rules) for a given scope."""
|
|
59
57
|
if scope == "user":
|
|
60
58
|
user_config: GlobalConfig = get_global_config()
|
|
@@ -76,21 +74,31 @@ def _add_rule(
|
|
|
76
74
|
"""Add a rule to the specified scope. Returns True if added, False if already exists."""
|
|
77
75
|
if scope == "user":
|
|
78
76
|
user_config: GlobalConfig = get_global_config()
|
|
79
|
-
rules =
|
|
77
|
+
rules = (
|
|
78
|
+
user_config.user_allow_rules if rule_type == "allow" else user_config.user_deny_rules
|
|
79
|
+
)
|
|
80
80
|
if rule in rules:
|
|
81
81
|
return False
|
|
82
82
|
rules.append(rule)
|
|
83
83
|
save_global_config(user_config)
|
|
84
84
|
elif scope == "project":
|
|
85
85
|
project_config: ProjectConfig = get_project_config(project_path)
|
|
86
|
-
rules =
|
|
86
|
+
rules = (
|
|
87
|
+
project_config.bash_allow_rules
|
|
88
|
+
if rule_type == "allow"
|
|
89
|
+
else project_config.bash_deny_rules
|
|
90
|
+
)
|
|
87
91
|
if rule in rules:
|
|
88
92
|
return False
|
|
89
93
|
rules.append(rule)
|
|
90
94
|
save_project_config(project_config, project_path)
|
|
91
95
|
else: # local
|
|
92
96
|
local_config: ProjectLocalConfig = get_project_local_config(project_path)
|
|
93
|
-
rules =
|
|
97
|
+
rules = (
|
|
98
|
+
local_config.local_allow_rules
|
|
99
|
+
if rule_type == "allow"
|
|
100
|
+
else local_config.local_deny_rules
|
|
101
|
+
)
|
|
94
102
|
if rule in rules:
|
|
95
103
|
return False
|
|
96
104
|
rules.append(rule)
|
|
@@ -107,21 +115,31 @@ def _remove_rule(
|
|
|
107
115
|
"""Remove a rule from the specified scope. Returns True if removed, False if not found."""
|
|
108
116
|
if scope == "user":
|
|
109
117
|
user_config: GlobalConfig = get_global_config()
|
|
110
|
-
rules =
|
|
118
|
+
rules = (
|
|
119
|
+
user_config.user_allow_rules if rule_type == "allow" else user_config.user_deny_rules
|
|
120
|
+
)
|
|
111
121
|
if rule not in rules:
|
|
112
122
|
return False
|
|
113
123
|
rules.remove(rule)
|
|
114
124
|
save_global_config(user_config)
|
|
115
125
|
elif scope == "project":
|
|
116
126
|
project_config: ProjectConfig = get_project_config(project_path)
|
|
117
|
-
rules =
|
|
127
|
+
rules = (
|
|
128
|
+
project_config.bash_allow_rules
|
|
129
|
+
if rule_type == "allow"
|
|
130
|
+
else project_config.bash_deny_rules
|
|
131
|
+
)
|
|
118
132
|
if rule not in rules:
|
|
119
133
|
return False
|
|
120
134
|
rules.remove(rule)
|
|
121
135
|
save_project_config(project_config, project_path)
|
|
122
136
|
else: # local
|
|
123
137
|
local_config: ProjectLocalConfig = get_project_local_config(project_path)
|
|
124
|
-
rules =
|
|
138
|
+
rules = (
|
|
139
|
+
local_config.local_allow_rules
|
|
140
|
+
if rule_type == "allow"
|
|
141
|
+
else local_config.local_deny_rules
|
|
142
|
+
)
|
|
125
143
|
if rule not in rules:
|
|
126
144
|
return False
|
|
127
145
|
rules.remove(rule)
|
|
@@ -72,14 +72,14 @@ def _choose_session(ui: Any, arg: str) -> Optional[SessionSummary]:
|
|
|
72
72
|
return None
|
|
73
73
|
|
|
74
74
|
# Handle pagination commands
|
|
75
|
-
if choice_text ==
|
|
75
|
+
if choice_text == "n":
|
|
76
76
|
if current_page < total_pages - 1:
|
|
77
77
|
current_page += 1
|
|
78
78
|
continue
|
|
79
79
|
else:
|
|
80
80
|
ui.console.print("[yellow]Already at the last page.[/yellow]")
|
|
81
81
|
continue
|
|
82
|
-
elif choice_text ==
|
|
82
|
+
elif choice_text == "p":
|
|
83
83
|
if current_page > 0:
|
|
84
84
|
current_page -= 1
|
|
85
85
|
continue
|
|
@@ -89,7 +89,9 @@ def _choose_session(ui: Any, arg: str) -> Optional[SessionSummary]:
|
|
|
89
89
|
|
|
90
90
|
# Handle session selection
|
|
91
91
|
if not choice_text.isdigit():
|
|
92
|
-
ui.console.print(
|
|
92
|
+
ui.console.print(
|
|
93
|
+
"[red]Please enter a session index number, 'n' for next page, or 'p' for previous page.[/red]"
|
|
94
|
+
)
|
|
93
95
|
continue
|
|
94
96
|
|
|
95
97
|
idx = int(choice_text)
|
|
@@ -85,7 +85,7 @@ def _setting_sources_summary(
|
|
|
85
85
|
profile: Optional[ModelProfile],
|
|
86
86
|
memory_files: List[MemoryFile],
|
|
87
87
|
auth_env_var: Optional[str],
|
|
88
|
-
|
|
88
|
+
yolo_mode: bool,
|
|
89
89
|
verbose: bool,
|
|
90
90
|
project_path: Path,
|
|
91
91
|
) -> str:
|
|
@@ -105,9 +105,9 @@ def _setting_sources_summary(
|
|
|
105
105
|
if auth_env_var:
|
|
106
106
|
sources.append("Environment variables")
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
config_yolo_mode = getattr(config, "yolo_mode", False)
|
|
109
109
|
config_verbose = getattr(config, "verbose", False)
|
|
110
|
-
if
|
|
110
|
+
if yolo_mode != config_yolo_mode or verbose != config_verbose:
|
|
111
111
|
sources.append("Command line arguments")
|
|
112
112
|
|
|
113
113
|
if profile and profile.api_key and not auth_env_var:
|
|
@@ -133,7 +133,7 @@ def _handle(ui: Any, _: str) -> bool:
|
|
|
133
133
|
profile,
|
|
134
134
|
memory_files,
|
|
135
135
|
auth_env_var,
|
|
136
|
-
ui.
|
|
136
|
+
ui.yolo_mode,
|
|
137
137
|
ui.verbose,
|
|
138
138
|
ui.project_path,
|
|
139
139
|
)
|
|
@@ -109,7 +109,8 @@ def _list_tasks(ui: Any) -> bool:
|
|
|
109
109
|
table.add_row(escape(task_id), "[red]error[/]", escape(str(exc)), "-")
|
|
110
110
|
logger.warning(
|
|
111
111
|
"[tasks_cmd] Failed to read background task status: %s: %s",
|
|
112
|
-
type(exc).__name__,
|
|
112
|
+
type(exc).__name__,
|
|
113
|
+
exc,
|
|
113
114
|
extra={"task_id": task_id, "session_id": getattr(ui, "session_id", None)},
|
|
114
115
|
)
|
|
115
116
|
continue
|
|
@@ -148,7 +149,8 @@ def _kill_task(ui: Any, task_id: str) -> bool:
|
|
|
148
149
|
console.print(f"[red]Failed to read task '{escape(task_id)}': {escape(str(exc))}[/red]")
|
|
149
150
|
logger.warning(
|
|
150
151
|
"[tasks_cmd] Failed to read task before kill: %s: %s",
|
|
151
|
-
type(exc).__name__,
|
|
152
|
+
type(exc).__name__,
|
|
153
|
+
exc,
|
|
152
154
|
extra={"task_id": task_id, "session_id": getattr(ui, "session_id", None)},
|
|
153
155
|
)
|
|
154
156
|
return True
|
|
@@ -172,7 +174,8 @@ def _kill_task(ui: Any, task_id: str) -> bool:
|
|
|
172
174
|
console.print(f"[red]Error stopping task {escape(task_id)}: {escape(str(exc))}[/red]")
|
|
173
175
|
logger.warning(
|
|
174
176
|
"[tasks_cmd] Error stopping background task: %s: %s",
|
|
175
|
-
type(exc).__name__,
|
|
177
|
+
type(exc).__name__,
|
|
178
|
+
exc,
|
|
176
179
|
extra={"task_id": task_id, "session_id": getattr(ui, "session_id", None)},
|
|
177
180
|
)
|
|
178
181
|
return True
|
|
@@ -197,7 +200,8 @@ def _show_task(ui: Any, task_id: str) -> bool:
|
|
|
197
200
|
console.print(f"[red]Failed to read task '{escape(task_id)}': {escape(str(exc))}[/red]")
|
|
198
201
|
logger.warning(
|
|
199
202
|
"[tasks_cmd] Failed to read task for detail view: %s: %s",
|
|
200
|
-
type(exc).__name__,
|
|
203
|
+
type(exc).__name__,
|
|
204
|
+
exc,
|
|
201
205
|
extra={"task_id": task_id, "session_id": getattr(ui, "session_id", None)},
|
|
202
206
|
)
|
|
203
207
|
return True
|
|
@@ -85,7 +85,7 @@ class FileMentionCompleter(Completer):
|
|
|
85
85
|
return
|
|
86
86
|
|
|
87
87
|
# Extract the query after the @ symbol
|
|
88
|
-
query = text[at_pos + 1:].strip()
|
|
88
|
+
query = text[at_pos + 1 :].strip()
|
|
89
89
|
|
|
90
90
|
try:
|
|
91
91
|
matches = []
|
|
@@ -143,6 +143,7 @@ class FileMentionCompleter(Completer):
|
|
|
143
143
|
continue
|
|
144
144
|
|
|
145
145
|
import fnmatch
|
|
146
|
+
|
|
146
147
|
if fnmatch.fnmatch(item.name.lower(), pattern.lower()):
|
|
147
148
|
try:
|
|
148
149
|
rel_path = item.relative_to(self.project_path)
|
|
@@ -14,7 +14,7 @@ from ripperdoc.utils.log import get_logger
|
|
|
14
14
|
logger = get_logger()
|
|
15
15
|
|
|
16
16
|
# Keys that trigger interrupt
|
|
17
|
-
INTERRUPT_KEYS: Set[str] = {
|
|
17
|
+
INTERRUPT_KEYS: Set[str] = {"\x1b", "\x03"} # ESC, Ctrl+C
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
class InterruptHandler:
|
|
@@ -149,8 +149,7 @@ class InterruptHandler:
|
|
|
149
149
|
|
|
150
150
|
try:
|
|
151
151
|
done, _ = await asyncio.wait(
|
|
152
|
-
{query_task, interrupt_task},
|
|
153
|
-
return_when=asyncio.FIRST_COMPLETED
|
|
152
|
+
{query_task, interrupt_task}, return_when=asyncio.FIRST_COMPLETED
|
|
154
153
|
)
|
|
155
154
|
|
|
156
155
|
# Check if interrupted
|
|
@@ -22,15 +22,17 @@ ConversationMessage = Union[UserMessage, AssistantMessage, ProgressMessage]
|
|
|
22
22
|
class MessageDisplay:
|
|
23
23
|
"""Handles message rendering and display operations."""
|
|
24
24
|
|
|
25
|
-
def __init__(self, console: Console, verbose: bool = False):
|
|
25
|
+
def __init__(self, console: Console, verbose: bool = False, show_full_thinking: bool = False):
|
|
26
26
|
"""Initialize the message display handler.
|
|
27
27
|
|
|
28
28
|
Args:
|
|
29
29
|
console: Rich console for output
|
|
30
30
|
verbose: Whether to show verbose output
|
|
31
|
+
show_full_thinking: Whether to show full reasoning content instead of truncated preview
|
|
31
32
|
"""
|
|
32
33
|
self.console = console
|
|
33
34
|
self.verbose = verbose
|
|
35
|
+
self.show_full_thinking = show_full_thinking
|
|
34
36
|
|
|
35
37
|
def format_tool_args(self, tool_name: str, tool_args: Optional[dict]) -> List[str]:
|
|
36
38
|
"""Render tool arguments into concise display-friendly parts."""
|
|
@@ -212,7 +214,7 @@ class MessageDisplay:
|
|
|
212
214
|
|
|
213
215
|
def print_reasoning(self, reasoning: Any) -> None:
|
|
214
216
|
"""Display a collapsed preview of reasoning/thinking blocks."""
|
|
215
|
-
preview = format_reasoning_preview(reasoning)
|
|
217
|
+
preview = format_reasoning_preview(reasoning, self.show_full_thinking)
|
|
216
218
|
if preview:
|
|
217
219
|
self.console.print(f"[dim italic]Thinking: {escape(preview)}[/]")
|
|
218
220
|
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Provider metadata used by interactive UI flows.
|
|
3
|
+
|
|
4
|
+
Each provider option represents exactly one protocol endpoint. Vendors that
|
|
5
|
+
offer multiple protocols (for example, DeepSeek exposing both OpenAI-style and
|
|
6
|
+
Anthropic-style APIs on different URLs) should be modeled as distinct provider
|
|
7
|
+
options so that protocol-specific defaults stay unambiguous.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from typing import Dict, List, Optional, Sequence, Tuple
|
|
12
|
+
|
|
13
|
+
from ripperdoc.core.config import ProviderType
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class ProviderOption:
|
|
18
|
+
"""A single provider choice with protocol and UX defaults."""
|
|
19
|
+
|
|
20
|
+
key: str
|
|
21
|
+
protocol: ProviderType
|
|
22
|
+
default_model: str
|
|
23
|
+
model_suggestions: Tuple[str, ...] = ()
|
|
24
|
+
default_api_base: Optional[str] = None
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def label(self) -> str:
|
|
28
|
+
"""Display label shown to the user."""
|
|
29
|
+
return self.key
|
|
30
|
+
|
|
31
|
+
def with_api_base(self, api_base: Optional[str]) -> "ProviderOption":
|
|
32
|
+
"""Return a copy that overrides the default API base."""
|
|
33
|
+
return ProviderOption(
|
|
34
|
+
key=self.key,
|
|
35
|
+
protocol=self.protocol,
|
|
36
|
+
default_model=self.default_model,
|
|
37
|
+
model_suggestions=self.model_suggestions,
|
|
38
|
+
default_api_base=api_base if api_base else self.default_api_base,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ProviderRegistry:
|
|
43
|
+
"""Registry for known providers to keep wizard logic declarative."""
|
|
44
|
+
|
|
45
|
+
def __init__(self, providers: Sequence[ProviderOption], default_key: str) -> None:
|
|
46
|
+
if not providers:
|
|
47
|
+
raise ValueError("Provider registry cannot be empty")
|
|
48
|
+
self._providers: List[ProviderOption] = list(providers)
|
|
49
|
+
self._index: Dict[str, ProviderOption] = {p.key: p for p in providers}
|
|
50
|
+
if default_key not in self._index:
|
|
51
|
+
raise ValueError(f"Default provider '{default_key}' not found in registry")
|
|
52
|
+
self._default_key = default_key
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def providers(self) -> List[ProviderOption]:
|
|
56
|
+
"""Return providers in display order."""
|
|
57
|
+
return list(self._providers)
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def default_choice(self) -> ProviderOption:
|
|
61
|
+
"""Return the default provider selection."""
|
|
62
|
+
return self._index[self._default_key]
|
|
63
|
+
|
|
64
|
+
def get(self, key: str) -> Optional[ProviderOption]:
|
|
65
|
+
"""Look up a provider option by key."""
|
|
66
|
+
return self._index.get(key)
|
|
67
|
+
|
|
68
|
+
def keys(self) -> List[str]:
|
|
69
|
+
"""Return provider keys in display order."""
|
|
70
|
+
return [p.key for p in self._providers]
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def default_model_for_protocol(protocol: ProviderType) -> str:
|
|
74
|
+
"""Reasonable default model per protocol family."""
|
|
75
|
+
if protocol == ProviderType.ANTHROPIC:
|
|
76
|
+
return "claude-3-5-sonnet-20241022"
|
|
77
|
+
if protocol == ProviderType.GEMINI:
|
|
78
|
+
return "gemini-1.5-pro"
|
|
79
|
+
return "gpt-4o-mini"
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
KNOWN_PROVIDERS = ProviderRegistry(
|
|
83
|
+
providers=[
|
|
84
|
+
ProviderOption(
|
|
85
|
+
key="deepseek",
|
|
86
|
+
protocol=ProviderType.OPENAI_COMPATIBLE,
|
|
87
|
+
default_model="deepseek-chat",
|
|
88
|
+
model_suggestions=("deepseek-chat", "deepseek-reasoner"),
|
|
89
|
+
default_api_base="https://api.deepseek.com/v1",
|
|
90
|
+
),
|
|
91
|
+
ProviderOption(
|
|
92
|
+
key="openai",
|
|
93
|
+
protocol=ProviderType.OPENAI_COMPATIBLE,
|
|
94
|
+
default_model="gpt-4o-mini",
|
|
95
|
+
model_suggestions=(
|
|
96
|
+
"gpt-5.1",
|
|
97
|
+
"gpt-5.1-chat",
|
|
98
|
+
"gpt-5.1-codex",
|
|
99
|
+
"gpt-4o",
|
|
100
|
+
"gpt-4-turbo",
|
|
101
|
+
"o1-preview",
|
|
102
|
+
"o1-mini",
|
|
103
|
+
),
|
|
104
|
+
default_api_base="https://api.openai.com/v1",
|
|
105
|
+
),
|
|
106
|
+
ProviderOption(
|
|
107
|
+
key="openrouter",
|
|
108
|
+
protocol=ProviderType.OPENAI_COMPATIBLE,
|
|
109
|
+
default_model="openai/gpt-4o-mini",
|
|
110
|
+
model_suggestions=(
|
|
111
|
+
"openai/gpt-4o-mini",
|
|
112
|
+
"meta-llama/llama-3.1-8b-instruct",
|
|
113
|
+
"google/gemini-flash-1.5",
|
|
114
|
+
),
|
|
115
|
+
default_api_base="https://openrouter.ai/api/v1",
|
|
116
|
+
),
|
|
117
|
+
ProviderOption(
|
|
118
|
+
key="anthropic",
|
|
119
|
+
protocol=ProviderType.ANTHROPIC,
|
|
120
|
+
default_model="claude-3-5-sonnet-20241022",
|
|
121
|
+
model_suggestions=(
|
|
122
|
+
"claude-3-5-sonnet-20241022",
|
|
123
|
+
"claude-3-5-haiku-20241022",
|
|
124
|
+
"claude-3-opus-20240229",
|
|
125
|
+
"claude-3-sonnet-20240229",
|
|
126
|
+
"claude-3-haiku-20240307",
|
|
127
|
+
),
|
|
128
|
+
default_api_base=None,
|
|
129
|
+
),
|
|
130
|
+
ProviderOption(
|
|
131
|
+
key="openai_compatible",
|
|
132
|
+
protocol=ProviderType.OPENAI_COMPATIBLE,
|
|
133
|
+
default_model="gpt-4o-mini",
|
|
134
|
+
model_suggestions=(
|
|
135
|
+
"gpt-4o-mini",
|
|
136
|
+
"gpt-4o",
|
|
137
|
+
"gpt-3.5-turbo",
|
|
138
|
+
),
|
|
139
|
+
default_api_base=None,
|
|
140
|
+
),
|
|
141
|
+
ProviderOption(
|
|
142
|
+
key="mistralai",
|
|
143
|
+
protocol=ProviderType.OPENAI_COMPATIBLE,
|
|
144
|
+
default_model="mistral-small-creative",
|
|
145
|
+
model_suggestions=(
|
|
146
|
+
"mistral-small-creative",
|
|
147
|
+
"mistral-large-latest",
|
|
148
|
+
"mistral-small-latest",
|
|
149
|
+
"devstral-2512",
|
|
150
|
+
"ministral-14b-2512",
|
|
151
|
+
"ministral-8b-2512",
|
|
152
|
+
"codestral-latest",
|
|
153
|
+
"pixtral-large-latest",
|
|
154
|
+
),
|
|
155
|
+
default_api_base="https://api.mistral.ai/v1",
|
|
156
|
+
),
|
|
157
|
+
ProviderOption(
|
|
158
|
+
key="google",
|
|
159
|
+
protocol=ProviderType.GEMINI,
|
|
160
|
+
default_model="gemini-1.5-pro",
|
|
161
|
+
model_suggestions=(
|
|
162
|
+
"gemini-2.5-pro",
|
|
163
|
+
"gemini-2.5-flash-lite",
|
|
164
|
+
"gemini-2.5-flash",
|
|
165
|
+
"gemini-3-pro-preview",
|
|
166
|
+
"gemini-3-flash-preview",
|
|
167
|
+
),
|
|
168
|
+
default_api_base="https://generativelanguage.googleapis.com/v1beta",
|
|
169
|
+
),
|
|
170
|
+
ProviderOption(
|
|
171
|
+
key="moonshot",
|
|
172
|
+
protocol=ProviderType.OPENAI_COMPATIBLE,
|
|
173
|
+
default_model="kimi-k2-turbo-preview",
|
|
174
|
+
model_suggestions=(
|
|
175
|
+
"kimi-k2-0905-preview",
|
|
176
|
+
"kimi-k2-0711-preview",
|
|
177
|
+
"kimi-k2-turbo-preview",
|
|
178
|
+
"kimi-k2-thinking",
|
|
179
|
+
"kimi-k2-thinking-turbo",
|
|
180
|
+
),
|
|
181
|
+
default_api_base="https://api.moonshot.cn/v1",
|
|
182
|
+
),
|
|
183
|
+
ProviderOption(
|
|
184
|
+
key="qwen",
|
|
185
|
+
protocol=ProviderType.OPENAI_COMPATIBLE,
|
|
186
|
+
default_model="qwen-turbo",
|
|
187
|
+
model_suggestions=(
|
|
188
|
+
"qwen-turbo",
|
|
189
|
+
"qwen-plus",
|
|
190
|
+
"qwen-max",
|
|
191
|
+
"qwen2.5-32b",
|
|
192
|
+
"qwen2.5-coder-32b",
|
|
193
|
+
),
|
|
194
|
+
default_api_base="https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
195
|
+
),
|
|
196
|
+
ProviderOption(
|
|
197
|
+
key="zhipu",
|
|
198
|
+
protocol=ProviderType.OPENAI_COMPATIBLE,
|
|
199
|
+
default_model="glm-4-flash",
|
|
200
|
+
model_suggestions=(
|
|
201
|
+
"glm-4-plus",
|
|
202
|
+
"glm-4-air-250414",
|
|
203
|
+
"glm-4-airx",
|
|
204
|
+
"glm-4-long",
|
|
205
|
+
"glm-4-flashx",
|
|
206
|
+
"glm-4-flash-250414",
|
|
207
|
+
"glm-4.6",
|
|
208
|
+
"glm-4.5",
|
|
209
|
+
"glm-4.5-air",
|
|
210
|
+
"glm-4.5-airx",
|
|
211
|
+
"glm-4.5-x",
|
|
212
|
+
"glm-4.5-flash",
|
|
213
|
+
),
|
|
214
|
+
default_api_base="https://open.bigmodel.cn/api/paas/v4",
|
|
215
|
+
),
|
|
216
|
+
ProviderOption(
|
|
217
|
+
key="minimax",
|
|
218
|
+
protocol=ProviderType.OPENAI_COMPATIBLE,
|
|
219
|
+
default_model="MiniMax-M2",
|
|
220
|
+
model_suggestions=("MiniMax-M2",),
|
|
221
|
+
default_api_base="https://api.minimax.chat/v1",
|
|
222
|
+
),
|
|
223
|
+
ProviderOption(
|
|
224
|
+
key="siliconflow",
|
|
225
|
+
protocol=ProviderType.OPENAI_COMPATIBLE,
|
|
226
|
+
default_model="deepseek-ai/DeepSeek-V3.2",
|
|
227
|
+
model_suggestions=(
|
|
228
|
+
"deepseek-ai/DeepSeek-V3.2",
|
|
229
|
+
"Qwen/Qwen2.5-32B-Instruct",
|
|
230
|
+
"Qwen/Qwen3-Coder-480B-A35B-Instruct",
|
|
231
|
+
"zai-org/GLM-4.6",
|
|
232
|
+
"moonshotai/Kimi-K2-Thinking",
|
|
233
|
+
"MiniMaxAI/MiniMax-M2",
|
|
234
|
+
),
|
|
235
|
+
default_api_base="https://api.siliconflow.cn/v1",
|
|
236
|
+
),
|
|
237
|
+
],
|
|
238
|
+
default_key="deepseek",
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
__all__ = [
|
|
243
|
+
"KNOWN_PROVIDERS",
|
|
244
|
+
"ProviderOption",
|
|
245
|
+
"ProviderRegistry",
|
|
246
|
+
"default_model_for_protocol",
|
|
247
|
+
]
|