feat: runtime model switching via /model command#160
feat: runtime model switching via /model command#160Talla wants to merge 3 commits intoRichardAtCT:mainfrom
Conversation
|
Good implementation overall — the UX flow is clean and the test coverage is solid. Six issues to address before merging: 🔴 Bug: This is the most critical issue. Fix by capturing explicitly: async def _make_model_effort_handler(action: str):
async def handler(query, param, context) -> None:
await _handle_model_selection(query, f"{action}:{param}", context)
return handlerOr just inline two named functions — cleaner and avoids the factory pattern for async. 🔴 Missing type annotations (mypy strict)
async def _handle_model_selection(
query: CallbackQuery,
data: str,
context: ContextTypes.DEFAULT_TYPE,
) -> None:Same applies to 🟡 The diff shows 🟡 Hardcoded model IDs will go stale
🟡 PR #165 migrates session state to 🟢 Test quality — mostly good 17 tests covering happy path, reset, Haiku (no effort), and effort selection is solid. Two suggestions:
Summary: The — Friday, AI assistant to @RichardAtCT (posted as @RichardAtCT — FridayOpenClawBot access pending) |
|
This is the preferred /model implementation — closing #139 in its favor. The inline keyboard UI and per-model effort levels are a nice touch. Action needed: The branch is currently in CONFLICTING state. Please rebase on main (we're merging several PRs that touch orchestrator.py). Once rebased with passing tests, this should be ready to merge. Note: #139 has been closed as superseded by this PR. |
Add /model command with inline keyboard UI for switching between Opus/Sonnet/Haiku models and effort levels (low/medium/high/max) at runtime. Model changes force a new session since the CLI doesn't support model switching on resumed sessions. - Effort levels are model-aware: Haiku has none, Sonnet excludes "max", Opus supports all including "max" - Override is per-user via context.user_data (in-memory, resets on bot restart) - Threaded through all run_command call sites (orchestrator, classic message handler) into the SDK layer - Registered in both agentic and classic handler modes - Added to bot command menu and /help text - 17 new tests covering keyboard display, model/effort selection, label formatting, and effort-per-model configuration Closes RichardAtCT#138
Instead of just "Default", show "Default (claude-sonnet-4-6)" or "Default (CLI default)" so users can verify what model is active after resetting.
…ases
- callback.py: replace shared _model_effort_handler closure with two
explicit lambdas (model:/effort:) — eliminates outer-scope capture;
move import to module level
- command.py: drop _MODELS dict with hardcoded version IDs; use
_MODEL_FAMILIES list of short CLI aliases ("opus"/"sonnet"/"haiku")
which the CLI resolves to current latest automatically
- command.py: add CallbackQuery + ContextTypes type annotations to
_handle_model_selection (fixes mypy strict mode)
- command.py: simplify _current_model_label (no reverse-map needed)
- command.py: add PR RichardAtCT#165 compatibility comment on force_new_session
- tests: update imports/assertions for _MODEL_FAMILIES; add 3 tests:
closure regression guard, effort: prefix isolation, force_new_session
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
babced6 to
1257203
Compare
|
Thanks for the detailed review! All points addressed in this force-push. Here's what was done: 🔴 Bug: "model": lambda q, p, ctx: _handle_model_selection(q, f"model:{p}", ctx),
"effort": lambda q, p, ctx: _handle_model_selection(q, f"effort:{p}", ctx),Also moved the import to module level. Added a regression test that would have caught this. 🔴 Missing type annotations 🟡 if model_override or effort_override:
logger.info("Runtime model/effort override active", model_override=..., effort_override=...)🟡 Hardcoded model IDs will go stale 🟡 # Note: if PR #165 merges first, change this to context.chat_data
context.user_data["force_new_session"] = TrueWhen #165 merges, that one-line change will be needed here too. Tests
Rebase |
Summary
Adds a
/modelcommand that lets users switch between Claude models (Opus, Sonnet, Haiku) and set effort levels at runtime via an inline keyboard UI — no bot restart or config changes needed./model→ pick model → pick effort level (if supported) → donecontext.user_data(in-memory). Resets on bot restart — server default (CLAUDE_MODELenv var) is used until the user overrides.Implements the feature requested in #138.
Changes
command.py/modelcommand,model_callback,_handle_model_selectionshared logic, model/effort constantscallback.pymodel:andeffort:callbacks in classic modeorchestrator.pyrun_commandmessage.pymodel_override+effort_overridethrough all 4 classicrun_commandcall sitesfacade.pymodel_override+effort_overrideparamssdk_integration.pymodel_overrideandefforttoClaudeAgentOptionstest_model_command.pytest_orchestrator.pyTest plan
/modelshows keyboard with Opus/Sonnet/Haiku + ResetCloses #138
🤖 Generated with Claude Code