Add per-turn and per-image model overrides - #29923
Conversation
|
I checked current What still looks missing relative to current provider behavior is narrower:
Local verification I ran while checking the overlap:
So my recommendation is: keep |
|
I reproduced a remaining tool-surface gap on current main while testing image editing. Provider plugins can already honor per-call model selection, but Concrete repro from local testing:
I opened a focused follow-up with tests here:
That patch:
Verification on the follow-up branch:
|
teknium1
left a comment
There was a problem hiding this comment.
Thanks for carrying the one-turn restore state across CLI, gateway, and TUI surfaces. The current-main premise is real: cli.py:8045 still uses the legacy parser, while tools/image_generation_tool.py:1181 and :1513 keep image backend/model selection user-configured only.
Problems
- In the TUI path, the new
--oncebranch avoidssession["model_override"](tui_gateway/server.py:2885in this diff), but_run_prompt_submit()calls_sync_agent_model_with_config()before the turn (tui_gateway/server.py:8945). That helper only skips synchronization whensession.get("model_override")is present (:3022), so the intended override can be reverted before its first request. - In the gateway path, the temporary override is written before the existing session-store persistence call (
gateway/slash_commands.py:1863in this diff; current persistence is at:1879-1887). The new restore helper restores only the in-memory map, so a restart can rehydrate the temporary override.
Suggested changes
- Guard TUI pre-turn config synchronization with pending once state and add a test that observes the model actually used for the first turn.
- Avoid persisting a once override, or restore the persisted value alongside the in-memory value; test gateway restart after the once turn.
- Coordinate the overlapping image lane with open #59815.
Automated hermes-sweeper review.
| @@ -2832,7 +2885,7 @@ def _apply_model_switch( | |||
| # contamination bug). agent.switch_model() above already mutated the right | |||
There was a problem hiding this comment.
_run_prompt_submit() calls _sync_agent_model_with_config() before the turn, and its only opt-out is session["model_override"]. Because this branch suppresses that key for --once, the configured model can replace the one-turn model before run_conversation; preserve a transient guard until the first turn finishes.
| @@ -1850,6 +1863,14 @@ async def _finish_switch() -> str: | |||
| "base_url": result.base_url, | |||
There was a problem hiding this comment.
This temporary override is still passed to the unconditional session-store write below. _restore_pending_one_turn_model_override() restores only the in-memory map, so a restart may rehydrate this one-turn choice permanently. Skip the write for --once, or restore the persisted override too.
|
I rechecked the current head (
Given the review's note to coordinate the image lane with #59815, the cleanest split now looks like:
#59815 is mine — happy to rebase it around whatever lands here first if that helps keep the split low-friction. |
Adds --once to /model across CLI, TUI, and gateway: switch model for the next turn only, restoring the previous model in a finally block so success, exception, and interrupt all revert. Parsing extends parse_model_flags_detailed(); resolve_persist_behavior() treats --once as a persistence opt-out; --global + --once is rejected. Salvaged from PR #29923 (image-generation lane split to #59815 per review; conflict resolution against current main by the maintainers).
Fixes the two review defects that kept PR #29923 open, plus docs: - gateway: exclude --once from the session-store write-through. The once-override lived only in memory before, but the write-through persisted it, so a gateway restart before the finally-restore rehydrated a supposedly one-turn model permanently. - TUI: skip _sync_agent_model_with_config while a one-turn restore is pending. The once-model is deliberately not pinned as a session model_override, so the config sync saw a model mismatch and clobbered the once-override back to the config model before the turn ran. - tests: real _handle_model_command drive asserting --once never touches set_model_override while --session still does; restore-pop idempotency. - docs: /model --once in configuring-models.md with an honest prompt-cache cost note (one-shot switch breaks the cached prefix twice; wins for short sessions and cheap-to-expensive escalation).
|
Merged via PR #67113 — your commit was cherry-picked onto current main with your authorship preserved in git history. Thanks for a well-built feature: the Two adjustments landed on top in the salvage:
The per-call |
Adds --once to /model across CLI, TUI, and gateway: switch model for the next turn only, restoring the previous model in a finally block so success, exception, and interrupt all revert. Parsing extends parse_model_flags_detailed(); resolve_persist_behavior() treats --once as a persistence opt-out; --global + --once is rejected. Salvaged from PR NousResearch#29923 (image-generation lane split to NousResearch#59815 per review; conflict resolution against current main by the maintainers).
Fixes the two review defects that kept PR NousResearch#29923 open, plus docs: - gateway: exclude --once from the session-store write-through. The once-override lived only in memory before, but the write-through persisted it, so a gateway restart before the finally-restore rehydrated a supposedly one-turn model permanently. - TUI: skip _sync_agent_model_with_config while a one-turn restore is pending. The once-model is deliberately not pinned as a session model_override, so the config sync saw a model mismatch and clobbered the once-override back to the config model before the turn ran. - tests: real _handle_model_command drive asserting --once never touches set_model_override while --session still does; restore-pop idempotency. - docs: /model --once in configuring-models.md with an honest prompt-cache cost note (one-shot switch breaks the cached prefix twice; wins for short sessions and cheap-to-expensive escalation).
Summary
/model --oncesupport across CLI, TUI, and gateway, restoring the previous model/runtime after one assistant turn.providerandmodeloverrides toimage_generate./modelbehavior while adding cleanup for pending one-turn overrides.Fixes #29914
Tests
scripts/run_tests.sh tests/hermes_cli/test_model_switch_once_flags.py tests/hermes_cli/test_cli_model_once.py tests/hermes_cli/test_apply_model_switch_result_context.py tests/hermes_cli/test_model_switch_custom_providers.py tests/tools/test_image_generation_plugin_dispatch.py tests/tools/test_image_generation.py tests/gateway/test_model_command_custom_providers.py tests/gateway/test_model_switch_persistence.py tests/gateway/test_session_model_override_routing.py tests/test_tui_gateway_server.py -- -quv run --no-sync python -m pytest -q tests/hermes_cli/test_model_switch_once_flags.py tests/hermes_cli/test_cli_model_once.py tests/hermes_cli/test_apply_model_switch_result_context.py tests/hermes_cli/test_model_switch_custom_providers.py tests/tools/test_image_generation_plugin_dispatch.py tests/tools/test_image_generation.py tests/gateway/test_model_command_custom_providers.py tests/gateway/test_model_switch_persistence.py tests/gateway/test_session_model_override_routing.py tests/test_tui_gateway_server.py::test_config_set_model_once_keeps_env_and_records_restore tests/test_tui_gateway_server.py::test_config_set_model_once_requires_live_session tests/test_tui_gateway_server.py::test_config_set_model_session_switch_clears_pending_once_restore tests/test_tui_gateway_server.py::test_restore_agent_model_runtime_falls_back_to_switch_model tests/test_tui_gateway_server.py::test_config_set_model_syncs_inference_provider_env tests/test_tui_gateway_server.py::test_config_set_model_global_persists tests/test_tui_gateway_server.py::test_config_set_model_syncs_tui_provider_envuv run --no-sync python -m pytest -q tests/hermes_cli/test_user_providers_model_switch.py tests/hermes_cli/test_model_provider_persistence.py tests/hermes_cli/test_model_switch_variant_tags.py tests/hermes_cli/test_model_switch_context_display.py tests/hermes_cli/test_anthropic_model_flow_stale_oauth.py tests/hermes_cli/test_custom_provider_model_switch.py tests/hermes_cli/test_codex_cli_model_picker.py tests/run_agent/test_switch_model_context.py tests/run_agent/test_switch_model_fallback_prune.py tests/tools/test_image_generation_env.pypython3.12 -m compileall hermes_cli/model_switch.py cli.py tui_gateway/server.py gateway/run.py tools/image_generation_tool.py tests/hermes_cli/test_model_switch_once_flags.py tests/hermes_cli/test_cli_model_once.py tests/gateway/test_model_command_custom_providers.py tests/gateway/test_model_switch_persistence.py tests/gateway/test_session_model_override_routing.py tests/tools/test_image_generation_plugin_dispatch.py tests/tools/test_image_generation.py tests/test_tui_gateway_server.pygit diff --check