klaude-code 2.1.1__py3-none-any.whl → 2.3.0__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.
- klaude_code/app/__init__.py +1 -2
- klaude_code/app/runtime.py +13 -41
- klaude_code/cli/list_model.py +27 -10
- klaude_code/cli/main.py +42 -159
- klaude_code/config/assets/builtin_config.yaml +36 -14
- klaude_code/config/config.py +144 -7
- klaude_code/config/select_model.py +38 -13
- klaude_code/config/sub_agent_model_helper.py +217 -0
- klaude_code/const.py +2 -2
- klaude_code/core/agent_profile.py +71 -5
- klaude_code/core/executor.py +75 -0
- klaude_code/core/manager/llm_clients_builder.py +18 -12
- klaude_code/core/prompts/prompt-nano-banana.md +1 -0
- klaude_code/core/tool/shell/command_safety.py +4 -189
- klaude_code/core/tool/sub_agent_tool.py +2 -1
- klaude_code/core/turn.py +1 -1
- klaude_code/llm/anthropic/client.py +8 -5
- klaude_code/llm/anthropic/input.py +54 -29
- klaude_code/llm/google/client.py +2 -2
- klaude_code/llm/google/input.py +23 -2
- klaude_code/llm/openai_compatible/input.py +22 -13
- klaude_code/llm/openai_compatible/stream.py +1 -1
- klaude_code/llm/openrouter/input.py +37 -25
- klaude_code/llm/responses/client.py +1 -1
- klaude_code/llm/responses/input.py +96 -57
- klaude_code/protocol/commands.py +1 -2
- klaude_code/protocol/events/system.py +4 -0
- klaude_code/protocol/message.py +2 -2
- klaude_code/protocol/op.py +17 -0
- klaude_code/protocol/op_handler.py +5 -0
- klaude_code/protocol/sub_agent/AGENTS.md +28 -0
- klaude_code/protocol/sub_agent/__init__.py +10 -14
- klaude_code/protocol/sub_agent/image_gen.py +2 -1
- klaude_code/session/codec.py +2 -6
- klaude_code/session/session.py +9 -1
- klaude_code/skill/assets/create-plan/SKILL.md +3 -5
- klaude_code/tui/command/__init__.py +7 -10
- klaude_code/tui/command/clear_cmd.py +1 -1
- klaude_code/tui/command/command_abc.py +1 -2
- klaude_code/tui/command/copy_cmd.py +1 -2
- klaude_code/tui/command/fork_session_cmd.py +4 -4
- klaude_code/tui/command/model_cmd.py +6 -43
- klaude_code/tui/command/model_select.py +75 -15
- klaude_code/tui/command/refresh_cmd.py +1 -2
- klaude_code/tui/command/resume_cmd.py +3 -4
- klaude_code/tui/command/status_cmd.py +1 -1
- klaude_code/tui/command/sub_agent_model_cmd.py +190 -0
- klaude_code/tui/components/bash_syntax.py +1 -1
- klaude_code/tui/components/common.py +1 -1
- klaude_code/tui/components/developer.py +10 -15
- klaude_code/tui/components/metadata.py +2 -64
- klaude_code/tui/components/rich/cjk_wrap.py +3 -2
- klaude_code/tui/components/rich/status.py +49 -3
- klaude_code/tui/components/rich/theme.py +4 -2
- klaude_code/tui/components/sub_agent.py +25 -46
- klaude_code/tui/components/user_input.py +9 -21
- klaude_code/tui/components/welcome.py +99 -0
- klaude_code/tui/input/prompt_toolkit.py +14 -1
- klaude_code/tui/renderer.py +2 -3
- klaude_code/tui/runner.py +2 -2
- klaude_code/tui/terminal/selector.py +8 -18
- klaude_code/ui/__init__.py +0 -24
- klaude_code/ui/common.py +3 -2
- klaude_code/ui/core/display.py +2 -2
- {klaude_code-2.1.1.dist-info → klaude_code-2.3.0.dist-info}/METADATA +16 -81
- {klaude_code-2.1.1.dist-info → klaude_code-2.3.0.dist-info}/RECORD +68 -67
- klaude_code/tui/command/help_cmd.py +0 -51
- klaude_code/tui/command/prompt-commit.md +0 -82
- klaude_code/tui/command/release_notes_cmd.py +0 -85
- klaude_code/ui/exec_mode.py +0 -60
- {klaude_code-2.1.1.dist-info → klaude_code-2.3.0.dist-info}/WHEEL +0 -0
- {klaude_code-2.1.1.dist-info → klaude_code-2.3.0.dist-info}/entry_points.txt +0 -0
klaude_code/app/__init__.py
CHANGED
|
@@ -4,9 +4,8 @@ This package coordinates core execution (Executor) with frontend displays.
|
|
|
4
4
|
Terminal-specific rendering and input handling live in `klaude_code.tui`.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
from .runtime import AppInitConfig
|
|
7
|
+
from .runtime import AppInitConfig
|
|
8
8
|
|
|
9
9
|
__all__ = [
|
|
10
10
|
"AppInitConfig",
|
|
11
|
-
"run_exec",
|
|
12
11
|
]
|
klaude_code/app/runtime.py
CHANGED
|
@@ -9,12 +9,15 @@ import typer
|
|
|
9
9
|
from klaude_code import ui
|
|
10
10
|
from klaude_code.config import Config, load_config
|
|
11
11
|
from klaude_code.core.agent import Agent
|
|
12
|
-
from klaude_code.core.agent_profile import
|
|
12
|
+
from klaude_code.core.agent_profile import (
|
|
13
|
+
DefaultModelProfileProvider,
|
|
14
|
+
NanoBananaModelProfileProvider,
|
|
15
|
+
VanillaModelProfileProvider,
|
|
16
|
+
)
|
|
13
17
|
from klaude_code.core.executor import Executor
|
|
14
18
|
from klaude_code.core.manager import build_llm_clients
|
|
15
19
|
from klaude_code.log import DebugType, log, set_debug_logging
|
|
16
20
|
from klaude_code.protocol import events, op
|
|
17
|
-
from klaude_code.protocol.message import UserInputPayload
|
|
18
21
|
from klaude_code.session.session import Session, close_default_store
|
|
19
22
|
|
|
20
23
|
|
|
@@ -25,8 +28,8 @@ class AppInitConfig:
|
|
|
25
28
|
model: str | None
|
|
26
29
|
debug: bool
|
|
27
30
|
vanilla: bool
|
|
31
|
+
banana: bool
|
|
28
32
|
debug_filters: set[DebugType] | None = None
|
|
29
|
-
stream_json: bool = False
|
|
30
33
|
|
|
31
34
|
|
|
32
35
|
@dataclass
|
|
@@ -56,6 +59,7 @@ async def initialize_app_components(
|
|
|
56
59
|
llm_clients = build_llm_clients(
|
|
57
60
|
config,
|
|
58
61
|
model_override=init_config.model,
|
|
62
|
+
skip_sub_agents=init_config.vanilla or init_config.banana,
|
|
59
63
|
)
|
|
60
64
|
except ValueError as exc:
|
|
61
65
|
if init_config.model:
|
|
@@ -70,7 +74,12 @@ async def initialize_app_components(
|
|
|
70
74
|
log((f"Error: failed to load the default model configuration: {exc}", "red"))
|
|
71
75
|
raise typer.Exit(2) from None
|
|
72
76
|
|
|
73
|
-
|
|
77
|
+
if init_config.banana:
|
|
78
|
+
model_profile_provider = NanoBananaModelProfileProvider()
|
|
79
|
+
elif init_config.vanilla:
|
|
80
|
+
model_profile_provider = VanillaModelProfileProvider()
|
|
81
|
+
else:
|
|
82
|
+
model_profile_provider = DefaultModelProfileProvider(config=config)
|
|
74
83
|
|
|
75
84
|
event_queue: asyncio.Queue[events.Event] = asyncio.Queue()
|
|
76
85
|
|
|
@@ -176,40 +185,3 @@ async def handle_keyboard_interrupt(executor: Executor) -> None:
|
|
|
176
185
|
log(("Resume with:", "dim"), (f"klaude --resume-by-id {session_id}", "green"))
|
|
177
186
|
with contextlib.suppress(Exception):
|
|
178
187
|
await executor.submit(op.InterruptOperation(target_session_id=None))
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
async def run_exec(init_config: AppInitConfig, input_content: str) -> None:
|
|
182
|
-
"""Run a single task non-interactively (exec mode)."""
|
|
183
|
-
from klaude_code.ui.terminal.title import update_terminal_title
|
|
184
|
-
|
|
185
|
-
display = ui.create_exec_display(debug=init_config.debug, stream_json=init_config.stream_json)
|
|
186
|
-
components = await initialize_app_components(
|
|
187
|
-
init_config=init_config,
|
|
188
|
-
display=display,
|
|
189
|
-
on_model_change=update_terminal_title,
|
|
190
|
-
)
|
|
191
|
-
|
|
192
|
-
try:
|
|
193
|
-
session_id = await initialize_session(components.executor, components.event_queue)
|
|
194
|
-
backfill_session_model_config(
|
|
195
|
-
components.executor.context.current_agent,
|
|
196
|
-
init_config.model,
|
|
197
|
-
components.config.main_model,
|
|
198
|
-
is_new_session=True,
|
|
199
|
-
)
|
|
200
|
-
|
|
201
|
-
if session_id is None:
|
|
202
|
-
raise RuntimeError("No active session")
|
|
203
|
-
|
|
204
|
-
op_id = await components.executor.submit(
|
|
205
|
-
op.RunAgentOperation(
|
|
206
|
-
session_id=session_id,
|
|
207
|
-
input=UserInputPayload(text=input_content),
|
|
208
|
-
)
|
|
209
|
-
)
|
|
210
|
-
await components.executor.wait_for(op_id)
|
|
211
|
-
await components.event_queue.join()
|
|
212
|
-
except KeyboardInterrupt:
|
|
213
|
-
await handle_keyboard_interrupt(components.executor)
|
|
214
|
-
finally:
|
|
215
|
-
await cleanup_app_components(components)
|
klaude_code/cli/list_model.py
CHANGED
|
@@ -243,18 +243,34 @@ def _build_provider_info_panel(provider: ProviderConfig, available: bool) -> Quo
|
|
|
243
243
|
|
|
244
244
|
|
|
245
245
|
def _build_models_table(
|
|
246
|
-
provider: ProviderConfig,
|
|
246
|
+
provider: ProviderConfig,
|
|
247
|
+
config: Config,
|
|
247
248
|
) -> Table:
|
|
248
249
|
"""Build a table for models under a provider."""
|
|
249
250
|
provider_available = not provider.is_api_key_missing()
|
|
250
251
|
|
|
252
|
+
def _resolve_selector(value: str | None) -> str | None:
|
|
253
|
+
if not value:
|
|
254
|
+
return None
|
|
255
|
+
try:
|
|
256
|
+
resolved = config.resolve_model_location_prefer_available(value) or config.resolve_model_location(value)
|
|
257
|
+
except ValueError:
|
|
258
|
+
return None
|
|
259
|
+
if resolved is None:
|
|
260
|
+
return None
|
|
261
|
+
return f"{resolved[0]}@{resolved[1]}"
|
|
262
|
+
|
|
263
|
+
default_selector = _resolve_selector(config.main_model)
|
|
264
|
+
|
|
251
265
|
# Build reverse mapping: model_name -> list of agent roles using it
|
|
252
266
|
model_to_agents: dict[str, list[str]] = {}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
267
|
+
for agent_role, model_name in (config.sub_agent_models or {}).items():
|
|
268
|
+
selector = _resolve_selector(model_name)
|
|
269
|
+
if selector is None:
|
|
270
|
+
continue
|
|
271
|
+
if selector not in model_to_agents:
|
|
272
|
+
model_to_agents[selector] = []
|
|
273
|
+
model_to_agents[selector].append(agent_role)
|
|
258
274
|
|
|
259
275
|
models_table = Table.grid(
|
|
260
276
|
padding=(0, 2),
|
|
@@ -275,10 +291,11 @@ def _build_models_table(
|
|
|
275
291
|
else:
|
|
276
292
|
# Build role tags for this model
|
|
277
293
|
roles: list[str] = []
|
|
278
|
-
|
|
294
|
+
selector = f"{model.model_name}@{provider.provider_name}"
|
|
295
|
+
if selector == default_selector:
|
|
279
296
|
roles.append("default")
|
|
280
|
-
if
|
|
281
|
-
roles.extend(role.lower() for role in model_to_agents[
|
|
297
|
+
if selector in model_to_agents:
|
|
298
|
+
roles.extend(role.lower() for role in model_to_agents[selector])
|
|
282
299
|
|
|
283
300
|
if roles:
|
|
284
301
|
name = Text.assemble(
|
|
@@ -350,6 +367,6 @@ def display_models_and_providers(config: Config, *, show_all: bool = False):
|
|
|
350
367
|
console.print()
|
|
351
368
|
|
|
352
369
|
# Models table for this provider
|
|
353
|
-
models_table = _build_models_table(provider, config
|
|
370
|
+
models_table = _build_models_table(provider, config)
|
|
354
371
|
console.print(models_table)
|
|
355
372
|
console.print("\n")
|
klaude_code/cli/main.py
CHANGED
|
@@ -13,44 +13,6 @@ from klaude_code.session import Session
|
|
|
13
13
|
from klaude_code.tui.command.resume_cmd import select_session_sync
|
|
14
14
|
from klaude_code.ui.terminal.title import update_terminal_title
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
def read_input_content(cli_argument: str) -> str | None:
|
|
18
|
-
"""Read and merge input from stdin and CLI argument.
|
|
19
|
-
|
|
20
|
-
Args:
|
|
21
|
-
cli_argument: The input content passed as CLI argument.
|
|
22
|
-
|
|
23
|
-
Returns:
|
|
24
|
-
The merged input content, or None if no input was provided.
|
|
25
|
-
"""
|
|
26
|
-
from klaude_code.log import log
|
|
27
|
-
|
|
28
|
-
parts: list[str] = []
|
|
29
|
-
|
|
30
|
-
# Handle stdin input
|
|
31
|
-
if not sys.stdin.isatty():
|
|
32
|
-
try:
|
|
33
|
-
stdin = sys.stdin.read().rstrip("\n")
|
|
34
|
-
if stdin:
|
|
35
|
-
parts.append(stdin)
|
|
36
|
-
except (OSError, ValueError) as e:
|
|
37
|
-
# Expected I/O-related errors when reading from stdin (e.g. broken pipe, closed stream).
|
|
38
|
-
log((f"Error reading from stdin: {e}", "red"))
|
|
39
|
-
except Exception as e:
|
|
40
|
-
# Unexpected errors are still reported but kept from crashing the CLI.
|
|
41
|
-
log((f"Unexpected error reading from stdin: {e}", "red"))
|
|
42
|
-
|
|
43
|
-
if cli_argument:
|
|
44
|
-
parts.append(cli_argument)
|
|
45
|
-
|
|
46
|
-
content = "\n".join(parts)
|
|
47
|
-
if len(content) == 0:
|
|
48
|
-
log(("Error: No input content provided", "red"))
|
|
49
|
-
return None
|
|
50
|
-
|
|
51
|
-
return content
|
|
52
|
-
|
|
53
|
-
|
|
54
16
|
ENV_HELP = """\
|
|
55
17
|
Environment Variables:
|
|
56
18
|
|
|
@@ -76,101 +38,6 @@ register_cost_commands(app)
|
|
|
76
38
|
register_self_update_commands(app)
|
|
77
39
|
|
|
78
40
|
|
|
79
|
-
@app.command("exec")
|
|
80
|
-
def exec_command(
|
|
81
|
-
input_content: str = typer.Argument("", help="Input message to execute"),
|
|
82
|
-
model: str | None = typer.Option(
|
|
83
|
-
None,
|
|
84
|
-
"--model",
|
|
85
|
-
"-m",
|
|
86
|
-
help="Override model config name (uses main model by default)",
|
|
87
|
-
rich_help_panel="LLM",
|
|
88
|
-
),
|
|
89
|
-
select_model: bool = typer.Option(
|
|
90
|
-
False,
|
|
91
|
-
"--select-model",
|
|
92
|
-
"-s",
|
|
93
|
-
help="Interactively choose a model at startup",
|
|
94
|
-
rich_help_panel="LLM",
|
|
95
|
-
),
|
|
96
|
-
debug: bool = typer.Option(
|
|
97
|
-
False,
|
|
98
|
-
"--debug",
|
|
99
|
-
"-d",
|
|
100
|
-
help="Enable debug mode",
|
|
101
|
-
rich_help_panel="Debug",
|
|
102
|
-
),
|
|
103
|
-
debug_filter: str | None = typer.Option(
|
|
104
|
-
None,
|
|
105
|
-
"--debug-filter",
|
|
106
|
-
help=DEBUG_FILTER_HELP,
|
|
107
|
-
rich_help_panel="Debug",
|
|
108
|
-
),
|
|
109
|
-
vanilla: bool = typer.Option(
|
|
110
|
-
False,
|
|
111
|
-
"--vanilla",
|
|
112
|
-
help="Vanilla mode exposes the model's raw API behavior: it provides only minimal tools (Bash, Read & Edit) and omits system prompts and reminders.",
|
|
113
|
-
),
|
|
114
|
-
stream_json: bool = typer.Option(
|
|
115
|
-
False,
|
|
116
|
-
"--stream-json",
|
|
117
|
-
help="Stream all events as JSON lines to stdout.",
|
|
118
|
-
),
|
|
119
|
-
) -> None:
|
|
120
|
-
"""Execute non-interactively with provided input."""
|
|
121
|
-
update_terminal_title()
|
|
122
|
-
|
|
123
|
-
merged_input = read_input_content(input_content)
|
|
124
|
-
if merged_input is None:
|
|
125
|
-
raise typer.Exit(1)
|
|
126
|
-
|
|
127
|
-
from klaude_code.app.runtime import AppInitConfig, run_exec
|
|
128
|
-
from klaude_code.config import load_config
|
|
129
|
-
from klaude_code.tui.command.model_select import select_model_interactive
|
|
130
|
-
|
|
131
|
-
chosen_model = model
|
|
132
|
-
if model or select_model:
|
|
133
|
-
chosen_model = select_model_interactive(preferred=model)
|
|
134
|
-
if chosen_model is None:
|
|
135
|
-
raise typer.Exit(1)
|
|
136
|
-
else:
|
|
137
|
-
# Check if main_model is configured; if not, trigger interactive selection
|
|
138
|
-
config = load_config()
|
|
139
|
-
if config.main_model is None:
|
|
140
|
-
chosen_model = select_model_interactive()
|
|
141
|
-
if chosen_model is None:
|
|
142
|
-
raise typer.Exit(1)
|
|
143
|
-
# Save the selection as default
|
|
144
|
-
config.main_model = chosen_model
|
|
145
|
-
from klaude_code.config.config import config_path
|
|
146
|
-
from klaude_code.log import log
|
|
147
|
-
|
|
148
|
-
asyncio.run(config.save())
|
|
149
|
-
log(f"Saved main_model={chosen_model} to {config_path}", style="cyan")
|
|
150
|
-
|
|
151
|
-
debug_enabled, debug_filters, log_path = prepare_debug_logging(debug, debug_filter)
|
|
152
|
-
|
|
153
|
-
init_config = AppInitConfig(
|
|
154
|
-
model=chosen_model,
|
|
155
|
-
debug=debug_enabled,
|
|
156
|
-
vanilla=vanilla,
|
|
157
|
-
debug_filters=debug_filters,
|
|
158
|
-
stream_json=stream_json,
|
|
159
|
-
)
|
|
160
|
-
|
|
161
|
-
if log_path:
|
|
162
|
-
from klaude_code.log import log
|
|
163
|
-
|
|
164
|
-
log(f"Debug log: {log_path}", style="dim")
|
|
165
|
-
|
|
166
|
-
asyncio.run(
|
|
167
|
-
run_exec(
|
|
168
|
-
init_config=init_config,
|
|
169
|
-
input_content=merged_input,
|
|
170
|
-
)
|
|
171
|
-
)
|
|
172
|
-
|
|
173
|
-
|
|
174
41
|
@app.callback(invoke_without_command=True)
|
|
175
42
|
def main_callback(
|
|
176
43
|
ctx: typer.Context,
|
|
@@ -220,13 +87,23 @@ def main_callback(
|
|
|
220
87
|
vanilla: bool = typer.Option(
|
|
221
88
|
False,
|
|
222
89
|
"--vanilla",
|
|
223
|
-
help="Vanilla mode exposes the model's raw API behavior: it provides only minimal tools (Bash, Read & Edit) and omits system prompts and reminders.",
|
|
90
|
+
help="Vanilla mode exposes the model's raw API behavior: it provides only minimal tools (Bash, Read, Write & Edit) and omits system prompts and reminders.",
|
|
91
|
+
),
|
|
92
|
+
banana: bool = typer.Option(
|
|
93
|
+
False,
|
|
94
|
+
"--banana",
|
|
95
|
+
help="Image generation mode with Nano Banana",
|
|
96
|
+
rich_help_panel="LLM",
|
|
224
97
|
),
|
|
225
98
|
) -> None:
|
|
226
99
|
# Only run interactive mode when no subcommand is invoked
|
|
227
100
|
if ctx.invoked_subcommand is None:
|
|
228
101
|
from klaude_code.log import log
|
|
229
102
|
|
|
103
|
+
if vanilla and banana:
|
|
104
|
+
log(("Error: --banana cannot be combined with --vanilla", "red"))
|
|
105
|
+
raise typer.Exit(2)
|
|
106
|
+
|
|
230
107
|
resume_by_id_value = resume_by_id.strip() if resume_by_id is not None else None
|
|
231
108
|
if resume_by_id_value == "":
|
|
232
109
|
log(("Error: --resume-by-id cannot be empty", "red"))
|
|
@@ -241,35 +118,34 @@ def main_callback(
|
|
|
241
118
|
log(("Hint: run `klaude --resume` to select an existing session", "yellow"))
|
|
242
119
|
raise typer.Exit(2)
|
|
243
120
|
|
|
244
|
-
# In non-interactive environments, default to exec-mode behavior.
|
|
245
|
-
# This allows: echo "…" | klaude
|
|
246
121
|
if not sys.stdin.isatty() or not sys.stdout.isatty():
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
raise typer.Exit(2)
|
|
251
|
-
|
|
252
|
-
exec_command(
|
|
253
|
-
input_content="",
|
|
254
|
-
model=model,
|
|
255
|
-
select_model=select_model,
|
|
256
|
-
debug=debug,
|
|
257
|
-
debug_filter=debug_filter,
|
|
258
|
-
vanilla=vanilla,
|
|
259
|
-
stream_json=False,
|
|
260
|
-
)
|
|
261
|
-
return
|
|
122
|
+
log(("Error: interactive mode requires a TTY", "red"))
|
|
123
|
+
log(("Hint: run klaude from an interactive terminal", "yellow"))
|
|
124
|
+
raise typer.Exit(2)
|
|
262
125
|
|
|
263
126
|
from klaude_code.app.runtime import AppInitConfig
|
|
264
|
-
from klaude_code.tui.command.model_select import select_model_interactive
|
|
127
|
+
from klaude_code.tui.command.model_select import ModelSelectStatus, select_model_interactive
|
|
265
128
|
from klaude_code.tui.runner import run_interactive
|
|
266
129
|
|
|
267
130
|
update_terminal_title()
|
|
268
131
|
|
|
269
132
|
chosen_model = model
|
|
270
|
-
if
|
|
271
|
-
|
|
272
|
-
|
|
133
|
+
if banana:
|
|
134
|
+
keywords = ["gemini-3-pro-image", "gemini-2.5-flash-image"]
|
|
135
|
+
model_result = select_model_interactive(keywords=keywords)
|
|
136
|
+
if model_result.status == ModelSelectStatus.SELECTED and model_result.model is not None:
|
|
137
|
+
chosen_model = model_result.model
|
|
138
|
+
elif model_result.status == ModelSelectStatus.CANCELLED:
|
|
139
|
+
return
|
|
140
|
+
else:
|
|
141
|
+
log(("Error: no available nano-banana model", "red"))
|
|
142
|
+
log(("Hint: set OPENROUTER_API_KEY or GOOGLE_API_KEY to enable nano-banana models", "yellow"))
|
|
143
|
+
raise typer.Exit(2)
|
|
144
|
+
elif model or select_model:
|
|
145
|
+
model_result = select_model_interactive(preferred=model)
|
|
146
|
+
if model_result.status == ModelSelectStatus.SELECTED and model_result.model is not None:
|
|
147
|
+
chosen_model = model_result.model
|
|
148
|
+
else:
|
|
273
149
|
return
|
|
274
150
|
|
|
275
151
|
# Resolve session id before entering asyncio loop
|
|
@@ -296,7 +172,12 @@ def main_callback(
|
|
|
296
172
|
cfg = load_config()
|
|
297
173
|
|
|
298
174
|
if session_meta.model_config_name:
|
|
299
|
-
|
|
175
|
+
try:
|
|
176
|
+
model_is_known = cfg.has_model_config_name(session_meta.model_config_name)
|
|
177
|
+
except ValueError:
|
|
178
|
+
model_is_known = False
|
|
179
|
+
|
|
180
|
+
if model_is_known:
|
|
300
181
|
chosen_model = session_meta.model_config_name
|
|
301
182
|
else:
|
|
302
183
|
log(
|
|
@@ -310,7 +191,7 @@ def main_callback(
|
|
|
310
191
|
raw_model = session_meta.model_name.strip()
|
|
311
192
|
if raw_model:
|
|
312
193
|
matches = [
|
|
313
|
-
m.
|
|
194
|
+
m.selector
|
|
314
195
|
for m in cfg.iter_model_entries()
|
|
315
196
|
if (m.model_params.model or "").strip().lower() == raw_model.lower()
|
|
316
197
|
]
|
|
@@ -323,9 +204,10 @@ def main_callback(
|
|
|
323
204
|
|
|
324
205
|
cfg = load_config()
|
|
325
206
|
if cfg.main_model is None:
|
|
326
|
-
|
|
327
|
-
if
|
|
207
|
+
model_result = select_model_interactive()
|
|
208
|
+
if model_result.status != ModelSelectStatus.SELECTED or model_result.model is None:
|
|
328
209
|
raise typer.Exit(1)
|
|
210
|
+
chosen_model = model_result.model
|
|
329
211
|
# Save the selection as default
|
|
330
212
|
cfg.main_model = chosen_model
|
|
331
213
|
from klaude_code.config.config import config_path
|
|
@@ -340,6 +222,7 @@ def main_callback(
|
|
|
340
222
|
model=chosen_model,
|
|
341
223
|
debug=debug_enabled,
|
|
342
224
|
vanilla=vanilla,
|
|
225
|
+
banana=banana,
|
|
343
226
|
debug_filters=debug_filters,
|
|
344
227
|
)
|
|
345
228
|
|
|
@@ -7,7 +7,7 @@ provider_list:
|
|
|
7
7
|
protocol: anthropic
|
|
8
8
|
api_key: ${ANTHROPIC_API_KEY}
|
|
9
9
|
model_list:
|
|
10
|
-
- model_name: sonnet
|
|
10
|
+
- model_name: sonnet
|
|
11
11
|
model_params:
|
|
12
12
|
model: claude-sonnet-4-5-20250929
|
|
13
13
|
context_limit: 200000
|
|
@@ -18,7 +18,7 @@ provider_list:
|
|
|
18
18
|
output: 15.0
|
|
19
19
|
cache_read: 0.3
|
|
20
20
|
cache_write: 3.75
|
|
21
|
-
- model_name: opus
|
|
21
|
+
- model_name: opus
|
|
22
22
|
model_params:
|
|
23
23
|
model: claude-opus-4-5-20251101
|
|
24
24
|
context_limit: 200000
|
|
@@ -187,10 +187,10 @@ provider_list:
|
|
|
187
187
|
input: 0.5
|
|
188
188
|
output: 3.0
|
|
189
189
|
cache_read: 0.05
|
|
190
|
-
- model_name: nano-banana-pro
|
|
190
|
+
- model_name: nano-banana-pro
|
|
191
191
|
model_params:
|
|
192
192
|
model: google/gemini-3-pro-image-preview
|
|
193
|
-
context_limit:
|
|
193
|
+
context_limit: 66000
|
|
194
194
|
modalities:
|
|
195
195
|
- image
|
|
196
196
|
- text
|
|
@@ -199,6 +199,18 @@ provider_list:
|
|
|
199
199
|
output: 12
|
|
200
200
|
cache_read: 0.2
|
|
201
201
|
image: 120
|
|
202
|
+
- model_name: nano-banana
|
|
203
|
+
model_params:
|
|
204
|
+
model: google/gemini-2.5-flash-image
|
|
205
|
+
context_limit: 33000
|
|
206
|
+
modalities:
|
|
207
|
+
- image
|
|
208
|
+
- text
|
|
209
|
+
cost:
|
|
210
|
+
input: 0.3
|
|
211
|
+
output: 2.5
|
|
212
|
+
cache_read: 0.03
|
|
213
|
+
image: 30
|
|
202
214
|
- model_name: grok
|
|
203
215
|
model_params:
|
|
204
216
|
model: x-ai/grok-4.1-fast
|
|
@@ -234,7 +246,7 @@ provider_list:
|
|
|
234
246
|
protocol: google
|
|
235
247
|
api_key: ${GOOGLE_API_KEY}
|
|
236
248
|
model_list:
|
|
237
|
-
- model_name: gemini-pro
|
|
249
|
+
- model_name: gemini-pro
|
|
238
250
|
model_params:
|
|
239
251
|
model: gemini-3-pro-preview
|
|
240
252
|
context_limit: 1048576
|
|
@@ -242,7 +254,7 @@ provider_list:
|
|
|
242
254
|
input: 2.0
|
|
243
255
|
output: 12.0
|
|
244
256
|
cache_read: 0.2
|
|
245
|
-
- model_name: gemini-flash
|
|
257
|
+
- model_name: gemini-flash
|
|
246
258
|
model_params:
|
|
247
259
|
model: gemini-3-flash-preview
|
|
248
260
|
context_limit: 1048576
|
|
@@ -250,6 +262,18 @@ provider_list:
|
|
|
250
262
|
input: 0.5
|
|
251
263
|
output: 3.0
|
|
252
264
|
cache_read: 0.05
|
|
265
|
+
- model_name: nano-banana-pro
|
|
266
|
+
model_params:
|
|
267
|
+
model: gemini-3-pro-image-preview
|
|
268
|
+
context_limit: 66000
|
|
269
|
+
modalities:
|
|
270
|
+
- image
|
|
271
|
+
- text
|
|
272
|
+
cost:
|
|
273
|
+
input: 2
|
|
274
|
+
output: 12
|
|
275
|
+
cache_read: 0.2
|
|
276
|
+
image: 120
|
|
253
277
|
|
|
254
278
|
- provider_name: bedrock
|
|
255
279
|
protocol: bedrock
|
|
@@ -257,7 +281,7 @@ provider_list:
|
|
|
257
281
|
aws_secret_key: ${AWS_SECRET_ACCESS_KEY}
|
|
258
282
|
aws_region: ${AWS_REGION}
|
|
259
283
|
model_list:
|
|
260
|
-
- model_name: sonnet
|
|
284
|
+
- model_name: sonnet
|
|
261
285
|
model_params:
|
|
262
286
|
model: us.anthropic.claude-sonnet-4-5-20250929-v1:0
|
|
263
287
|
context_limit: 200000
|
|
@@ -266,6 +290,7 @@ provider_list:
|
|
|
266
290
|
output: 15.0
|
|
267
291
|
cache_read: 0.3
|
|
268
292
|
cache_write: 3.75
|
|
293
|
+
|
|
269
294
|
- provider_name: deepseek
|
|
270
295
|
protocol: anthropic
|
|
271
296
|
api_key: ${DEEPSEEK_API_KEY}
|
|
@@ -289,7 +314,7 @@ provider_list:
|
|
|
289
314
|
api_key: ${MOONSHOT_API_KEY}
|
|
290
315
|
base_url: https://api.moonshot.cn/anthropic
|
|
291
316
|
model_list:
|
|
292
|
-
- model_name: kimi
|
|
317
|
+
- model_name: kimi
|
|
293
318
|
model_params:
|
|
294
319
|
model: kimi-k2-thinking
|
|
295
320
|
context_limit: 262144
|
|
@@ -305,7 +330,7 @@ provider_list:
|
|
|
305
330
|
- provider_name: claude-max
|
|
306
331
|
protocol: claude_oauth
|
|
307
332
|
model_list:
|
|
308
|
-
- model_name: sonnet
|
|
333
|
+
- model_name: sonnet
|
|
309
334
|
model_params:
|
|
310
335
|
model: claude-sonnet-4-5-20250929
|
|
311
336
|
context_limit: 200000
|
|
@@ -314,7 +339,7 @@ provider_list:
|
|
|
314
339
|
output: 15.0
|
|
315
340
|
cache_read: 0.3
|
|
316
341
|
cache_write: 3.75
|
|
317
|
-
- model_name: opus
|
|
342
|
+
- model_name: opus
|
|
318
343
|
model_params:
|
|
319
344
|
model: claude-opus-4-5-20251101
|
|
320
345
|
context_limit: 200000
|
|
@@ -327,7 +352,7 @@ provider_list:
|
|
|
327
352
|
output: 25.0
|
|
328
353
|
cache_read: 0.5
|
|
329
354
|
cache_write: 6.25
|
|
330
|
-
- model_name: haiku
|
|
355
|
+
- model_name: haiku
|
|
331
356
|
model_params:
|
|
332
357
|
model: claude-haiku-4-5-20251001
|
|
333
358
|
context_limit: 200000
|
|
@@ -351,6 +376,3 @@ provider_list:
|
|
|
351
376
|
input: 1.75
|
|
352
377
|
output: 14.0
|
|
353
378
|
cache_read: 0.17
|
|
354
|
-
|
|
355
|
-
sub_agent_models:
|
|
356
|
-
ImageGen: nano-banana-pro@or
|