feat: model routing profiles with per-context model/provider selection#1168
feat: model routing profiles with per-context model/provider selection#1168virtaava wants to merge 7 commits intoNousResearch:mainfrom
Conversation
Every config-modifying routing operation now auto-creates a timestamped backup in ~/.hermes/config-backups/ before writing. Users can restore from any backup via --restore (routing sections only) or --restore-full. New CLI flags: hermes configure-model-routing --restore hermes configure-model-routing --restore-full hermes configure-model-routing --list-backups Also adds get_primary_model() to runtime_provider for wizard display. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
shutil.copy2 preserves the source file's mtime, so multiple backups of the same file get identical mtimes making ordering unreliable. shutil.copy lets the backup's mtime reflect when it was actually created. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Closing this for now — but I want to be clear that this is genuinely impressive work, @virtaava. 1700+ lines across routing profiles, an interactive config wizard, config backup/restore, delegation routing, and full test coverage. The design is thoughtful and the implementation is thorough. The reason I'm not merging this right now isn't quality — it's timing and direction. Model routing is something we're thinking about, but we want to land it in a way that fits with some other architectural changes we have in mind (auxiliary client unification, provider routing rework, etc.). Merging a large feature like this now would create friction with that work. If/when we revisit model routing profiles, this PR will be the reference implementation. The config schema design, the wizard UX, and the delegation routing hooks are all things worth drawing from. I'd genuinely welcome your continued involvement — whether that's a fresh PR when the time comes or input on the design direction. Thank you for the substantial effort here. This is the kind of contribution that makes open source great, even when the timing doesn't line up. |
Summary
Adds model routing profiles — optional per-context model and provider overrides that let users route different work types to different models. Includes an interactive configuration wizard, routing rules for
delegate_task, and a config backup/restore system so configuration changes are always reversible.Motivation
Hermes currently uses a single model for all operations. In practice, different contexts benefit from different models:
Today this requires manually editing config.yaml with no discovery, validation, or undo.
What's included
Model routing profiles (
config.yaml)Each profile supports
model,provider,base_url,api_key_env— works with any OpenAI-compatible endpoint including local servers (Ollama, vLLM, llama.cpp). Empty profiles inherit from the primary model.Interactive configuration wizard
/modelsendpointhermes model(which remains the primary model selector)Config backup/restore
Every config-modifying routing operation auto-creates a timestamped backup in
~/.hermes/config-backups/before writing. Restores can target justmodel_profiles+model_routing(leaving other config untouched) or the full config. Even restore creates apre_restorebackup, so undo is itself undoable. Auto-rotates at 10 backups.Routing rules for
delegate_taskFirst matching rule wins. Falls back to toolset heuristics when no rules match.
delegate_taskintegrationExplicit profile per call:
{"goal": "Refactor the parser module", "model_profile": "coding"}Or batch mode with mixed profiles:
{ "tasks": [ {"goal": "Refactor parser", "toolsets": ["terminal", "file"], "model_profile": "coding"}, {"goal": "Research alternatives", "toolsets": ["web"], "model_profile": "research"} ] }Precedence rules
Chat model
--modelargumentmodel_profiles.chat.modelmodel.defaultdelegate_taskroutingdelegation.model/delegation.provider(if set)model_profileon tool calldelegation.model_profiledefaultmodel_routing.rules)Backward compatibility
hermes modelbehavior is unchangeddelegate_taskwithoutmodel_profileuses existing behaviorsave_config()API is backward compatible (newbackup_reasonkwarg defaults to empty)delegation.model/delegation.providerstill takes precedenceTest plan
test_config_backup.py— 15 tests: create, prune, list, selective restore, full restore, pre-restore safety, CLI integrationtest_model_routing_command.py— reset with backup, list-backups empty statetest_delegate.py— profile resolution, routing rules, batch modetest_setup.py— wizard flow, profile normalization, provider discoverytest_runtime_provider_resolution.py— profile inheritance, primary model fallbackPath,shutil.copy2, no Unix-specific operationsFiles changed
hermes_cli/config_backup.pyhermes_cli/config.pymodel_profiles,model_routing,delegation.model_profilein defaults;backup_reasononsave_confighermes_cli/runtime_provider.pyresolve_model_profile(),get_primary_model(),resolve_model_for_profile()hermes_cli/setup.pyhermes_cli/main.pycmd_configure_model_routingwith--restore/--restore-full/--list-backupscli.pyHermesCLI.__init__gateway/run.pymodel_profiles.chatcron/scheduler.pytools/delegate_tool.pydocs/model-profiles.mdtests/🤖 Generated with Claude Code