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.
Files changed (72) hide show
  1. klaude_code/app/__init__.py +1 -2
  2. klaude_code/app/runtime.py +13 -41
  3. klaude_code/cli/list_model.py +27 -10
  4. klaude_code/cli/main.py +42 -159
  5. klaude_code/config/assets/builtin_config.yaml +36 -14
  6. klaude_code/config/config.py +144 -7
  7. klaude_code/config/select_model.py +38 -13
  8. klaude_code/config/sub_agent_model_helper.py +217 -0
  9. klaude_code/const.py +2 -2
  10. klaude_code/core/agent_profile.py +71 -5
  11. klaude_code/core/executor.py +75 -0
  12. klaude_code/core/manager/llm_clients_builder.py +18 -12
  13. klaude_code/core/prompts/prompt-nano-banana.md +1 -0
  14. klaude_code/core/tool/shell/command_safety.py +4 -189
  15. klaude_code/core/tool/sub_agent_tool.py +2 -1
  16. klaude_code/core/turn.py +1 -1
  17. klaude_code/llm/anthropic/client.py +8 -5
  18. klaude_code/llm/anthropic/input.py +54 -29
  19. klaude_code/llm/google/client.py +2 -2
  20. klaude_code/llm/google/input.py +23 -2
  21. klaude_code/llm/openai_compatible/input.py +22 -13
  22. klaude_code/llm/openai_compatible/stream.py +1 -1
  23. klaude_code/llm/openrouter/input.py +37 -25
  24. klaude_code/llm/responses/client.py +1 -1
  25. klaude_code/llm/responses/input.py +96 -57
  26. klaude_code/protocol/commands.py +1 -2
  27. klaude_code/protocol/events/system.py +4 -0
  28. klaude_code/protocol/message.py +2 -2
  29. klaude_code/protocol/op.py +17 -0
  30. klaude_code/protocol/op_handler.py +5 -0
  31. klaude_code/protocol/sub_agent/AGENTS.md +28 -0
  32. klaude_code/protocol/sub_agent/__init__.py +10 -14
  33. klaude_code/protocol/sub_agent/image_gen.py +2 -1
  34. klaude_code/session/codec.py +2 -6
  35. klaude_code/session/session.py +9 -1
  36. klaude_code/skill/assets/create-plan/SKILL.md +3 -5
  37. klaude_code/tui/command/__init__.py +7 -10
  38. klaude_code/tui/command/clear_cmd.py +1 -1
  39. klaude_code/tui/command/command_abc.py +1 -2
  40. klaude_code/tui/command/copy_cmd.py +1 -2
  41. klaude_code/tui/command/fork_session_cmd.py +4 -4
  42. klaude_code/tui/command/model_cmd.py +6 -43
  43. klaude_code/tui/command/model_select.py +75 -15
  44. klaude_code/tui/command/refresh_cmd.py +1 -2
  45. klaude_code/tui/command/resume_cmd.py +3 -4
  46. klaude_code/tui/command/status_cmd.py +1 -1
  47. klaude_code/tui/command/sub_agent_model_cmd.py +190 -0
  48. klaude_code/tui/components/bash_syntax.py +1 -1
  49. klaude_code/tui/components/common.py +1 -1
  50. klaude_code/tui/components/developer.py +10 -15
  51. klaude_code/tui/components/metadata.py +2 -64
  52. klaude_code/tui/components/rich/cjk_wrap.py +3 -2
  53. klaude_code/tui/components/rich/status.py +49 -3
  54. klaude_code/tui/components/rich/theme.py +4 -2
  55. klaude_code/tui/components/sub_agent.py +25 -46
  56. klaude_code/tui/components/user_input.py +9 -21
  57. klaude_code/tui/components/welcome.py +99 -0
  58. klaude_code/tui/input/prompt_toolkit.py +14 -1
  59. klaude_code/tui/renderer.py +2 -3
  60. klaude_code/tui/runner.py +2 -2
  61. klaude_code/tui/terminal/selector.py +8 -18
  62. klaude_code/ui/__init__.py +0 -24
  63. klaude_code/ui/common.py +3 -2
  64. klaude_code/ui/core/display.py +2 -2
  65. {klaude_code-2.1.1.dist-info → klaude_code-2.3.0.dist-info}/METADATA +16 -81
  66. {klaude_code-2.1.1.dist-info → klaude_code-2.3.0.dist-info}/RECORD +68 -67
  67. klaude_code/tui/command/help_cmd.py +0 -51
  68. klaude_code/tui/command/prompt-commit.md +0 -82
  69. klaude_code/tui/command/release_notes_cmd.py +0 -85
  70. klaude_code/ui/exec_mode.py +0 -60
  71. {klaude_code-2.1.1.dist-info → klaude_code-2.3.0.dist-info}/WHEEL +0 -0
  72. {klaude_code-2.1.1.dist-info → klaude_code-2.3.0.dist-info}/entry_points.txt +0 -0
@@ -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, run_exec
7
+ from .runtime import AppInitConfig
8
8
 
9
9
  __all__ = [
10
10
  "AppInitConfig",
11
- "run_exec",
12
11
  ]
@@ -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 DefaultModelProfileProvider, VanillaModelProfileProvider
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
- model_profile_provider = VanillaModelProfileProvider() if init_config.vanilla else DefaultModelProfileProvider()
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)
@@ -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, main_model: str | None, sub_agent_models: dict[str, str] | None = None
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
- if sub_agent_models:
254
- for agent_role, model_name in sub_agent_models.items():
255
- if model_name not in model_to_agents:
256
- model_to_agents[model_name] = []
257
- model_to_agents[model_name].append(agent_role)
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
- if model.model_name == main_model:
294
+ selector = f"{model.model_name}@{provider.provider_name}"
295
+ if selector == default_selector:
279
296
  roles.append("default")
280
- if model.model_name in model_to_agents:
281
- roles.extend(role.lower() for role in model_to_agents[model.model_name])
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.main_model, config.sub_agent_models)
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
- if continue_ or resume or resume_by_id is not None:
248
- log(("Error: --continue/--resume options require a TTY", "red"))
249
- log(("Hint: use `klaude exec` for non-interactive usage", "yellow"))
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 model or select_model:
271
- chosen_model = select_model_interactive(preferred=model)
272
- if chosen_model is None:
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
- if any(m.model_name == session_meta.model_config_name for m in cfg.iter_model_entries()):
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.model_name
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
- chosen_model = select_model_interactive()
327
- if chosen_model is None:
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@ant
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@ant
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@or
190
+ - model_name: nano-banana-pro
191
191
  model_params:
192
192
  model: google/gemini-3-pro-image-preview
193
- context_limit: 1048576
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@google
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@google
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@bedrock
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@moonshot
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@claude-max
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@claude-max
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@claude-max
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