feat(cli): resolve direct aliases at dispatch time - #29198
StartupBros wants to merge 1 commit into
Conversation
When `hermes chat` is invoked with a model id that lives in ``hermes_cli.model_switch.DIRECT_ALIASES`` (custom providers, local runtimes, ad-hoc base_url overrides), the CLI hands the raw alias to ``AIAgent`` and the agent emits "unknown provider/model" — the alias never gets translated to its underlying (provider, base_url, model_id, api_mode) triple. Fixes the dispatch-time gap for #18954 (model aliases not resolved for custom providers); same surface as the regression test file for #16767 (chat --provider / -m <alias>). Resolution is factored into two pure helpers — ``_resolve_direct_alias_for_chat`` and ``_apply_resolved_alias`` — so it's testable without an AIAgent. Explicit ``--model`` / ``--runtime`` overrides still win: ``self.*`` is only mutated when neither was passed (i.e. the user's interactive default), so callers that introspect ``self.model`` after a chat exit see the post-resolution value rather than the raw alias. ``ImportError`` (not bare ``Exception``) is caught around the model_switch import so trimmed embedded installs that don't ship the module fall through to the generic provider lookup instead of crashing.
2dd1cb2 to
deb60f1
Compare
|
Status re: @alt-glitch's note above: #21674 closed (its tests landed in |
|
Closing in favor of #19213 (@LeonSGP43). After tracing the call flow on current main:
#19213 is also architecturally cleaner — it adds a proper Going to validate #19213 against current main and post results there. Sorry for the duplicate noise. |
Problem
When
hermes chatis invoked with a model id that lives inhermes_cli.model_switch.DIRECT_ALIASES(custom providers, local runtimes, ad-hocbase_urloverrides), the CLI hands the raw alias toAIAgentand the agent emits "unknown provider/model" — the alias never gets translated to its underlying(provider, base_url, model_id, api_mode)triple.Same surface as:
tests/hermes_cli/test_regression_16767.pyexists) — CLI doesn't honor user-defined providers via chat --provider or -m . New tests for this PR land in that same file.Other call sites already perform direct-alias resolution at their dispatch points (
hermes_cli/oneshot.py:269,hermes_cli/commands.py:1542-1544). The interactivehermes chatpath was missing.Fix
Two pure helpers in
cli.py:_resolve_direct_alias_for_chat(effective_model, current_provider)— wrapsmodel_switch.resolve_aliasand returns a small dict, orNonewhen the input isn't a known alias._apply_resolved_alias(resolved, runtime)— folds the resolved triple into a copy of the runtime dict; recomputesapi_modeviadetermine_api_mode; preserves any caller-suppliedapi_key; only setsno-key-requiredfor local targets that have no key.HermesCLI._init_agentcalls them before constructingAIAgent.Behavioural notes a reviewer will want to see
self.model / self.provider / self.base_url / self.api_modeare mutated only when bothmodel_overrideandruntime_overrideareNone— i.e. the interactive default path. Callers passing--modelor--runtimeget the resolved triple in the agent butself.*is left alone.ImportError, not bareException. Trimmed embedded installs may omithermes_cli.model_switch. The resolver catchesImportErrorspecifically so a realAttributeErrororKeyErrorfrom insideresolve_aliasstill propagates._ensure_direct_aliases()call.resolve_aliasalready does this internally (hermes_cli/model_switch.py:469)._apply_resolved_aliascopies before writing.Test plan
5 new tests in
tests/hermes_cli/test_regression_16767.py, classTestChatDispatchDirectAliasResolution:test_alias_resolution_swaps_in_runtime— happy path, asserts model/provider/base_url/api_mode swap and the caller's dict isn't mutatedtest_unknown_alias_returns_none— non-alias inputs fall throughtest_existing_api_key_is_preserved—no-key-requiredplaceholder doesn't clobber real keystest_alias_without_base_url_does_not_set_one— catalog-only aliases don't blank an existing base_urltest_missing_model_switch_module_returns_none—ImportErrorpath degrades gracefullyAll 8 tests in the file pass (3 existing + 5 new).