Skip to content

feat: per-skill model/provider override via SKILL.md frontmatter - #19270

Closed
luoyuctl wants to merge 1 commit into
NousResearch:mainfrom
luoyuctl:feat/skill-model-switch
Closed

feat: per-skill model/provider override via SKILL.md frontmatter#19270
luoyuctl wants to merge 1 commit into
NousResearch:mainfrom
luoyuctl:feat/skill-model-switch

Conversation

@luoyuctl

@luoyuctl luoyuctl commented May 3, 2026

Copy link
Copy Markdown

Summary

Fixes #5997 — Allow skills to declare a preferred model and provider in their SKILL.md frontmatter.

Currently, all skills run on the main agent's model, forcing users to pay top-tier model costs even for simple, well-structured skills (e.g., gif-search, arxiv) that could run on cheaper models.

This PR adds the model: and provider: optional fields to SKILL.md frontmatter. When a skill with these fields is loaded via skill_view(), the agent switches to the skill's preferred model for the current turn, then restores the original model at the start of the next turn.

Changes

  • tools/skills_tool.py: Include model_override and provider_override in the skill_view() JSON response
  • run_agent.py:
    • Initialize _pre_skill_model = None to track the pre-switch model state
    • In _execute_tool_calls_parallel: detect skill_view results with model overrides and call switch_model()
    • In _execute_tool_calls_sequential: same detection + switch logic
    • In run_conversation(): restore the original model at the start of each new user turn

How It Works

  1. Skill author adds to SKILL.md:
model: anthropic/claude-haiku-4
provider: anthropic
  1. When the agent calls skill_view("my-skill"), the response includes model_override

  2. The agent saves its current model, switches to the skill's model, and processes the skill's instructions

  3. On the next user message, the agent restores the original model

Test Plan

  • skill_view() returns model_override: None for skills without model config
  • skill_view() returns correct model_override/provider_override for skills with config
  • tests/tools/test_skills_tool.py — 80 tests passed, no regressions
  • Model switch is best-effort (non-fatal on failure)

@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint tool/skills Skills system (list, view, manage) labels May 3, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Related to #8485 and #4833 — prior attempts at per-skill model routing via SKILL.md frontmatter. This PR appears to be a narrower implementation (model switch only, no delegate_task override or config aliases).

@alt-glitch

Copy link
Copy Markdown
Collaborator

Related to #8485 and #4833.

Allow skills to declare their preferred model and provider in SKILL.md
frontmatter via optional 'model:' and 'provider:' fields. When the agent
loads a skill with these fields (via skill_view tool or /skill-name slash
command), it switches to the skill's preferred model for the current turn
and restores the original model at the start of the next turn.

Implementation:
- skills_tool.py: include model_override/provider_override in skill_view()
  JSON response, extracted from SKILL.md frontmatter YAML
- run_agent.py: add _maybe_apply_skill_model_override() method that
  detects skill_view results with model overrides, pushes current model
  onto a stack, and calls switch_model() — with debug logging on failure
- run_agent.py: add _pre_skill_model_stack (LIFO) to support cascading
  skill switches (skill A loads skill B, each with their own model)
- run_agent.py: restore original model(s) from stack at start of each
  user turn in run_conversation()
- agent/skill_commands.py: add get_skill_model_override() helper for
  CLI/gateway slash-command handlers to check model overrides before
  injecting skill content

Key design decisions:
- Uses a stack for model tracking so chains of skill_view calls each
  get their own model and restore correctly in reverse order
- Debug logging on switch and restore failures (not silent pass)
- Non-blocking: model switch failures don't interrupt the conversation
- Deduplicated: single method replaces copy-pasted logic in both
  parallel and sequential tool execution paths

Closes NousResearch#5997
@luoyuctl
luoyuctl force-pushed the feat/skill-model-switch branch from d48809a to 4aa0d0e Compare May 3, 2026 15:25
@luoyuctl

luoyuctl commented May 3, 2026

Copy link
Copy Markdown
Author

Thanks for the pointer to #8485 and #4833! I reviewed both.

This PR takes a deliberately narrower approach for a few reasons:

  1. Agent-level switch, not subagent-only. iRonin's approach routes model through delegate_task(), which only works when skills run in subagents. This PR uses switch_model() directly on the agent, so any skill — whether invoked via tool call or /skill-name slash command — gets its preferred model. No delegate_task dependency.

  2. Stack-based restoration. Multiple skill_view() calls in one turn each push/pop from _pre_skill_model_stack, so cascading skill chains (skill A loads skill B) each get their own model and restore correctly.

  3. Minimal surface. +107 lines in 3 files, zero API changes to delegate_task. This keeps the scope tight and review surface small — the delegate_task(model=) / config alias features from feat: per-skill model routing + supervisor/execution model config #8485 can land independently as follow-up work without blocking this.

  4. Slash command support. Added get_skill_model_override() in agent/skill_commands.py so CLI/gateway handlers can check model overrides before injecting skill content — not just tool-call path.

Happy to adjust if there's a preferred integration path relative to #8485.

@ether-btc

Copy link
Copy Markdown
Contributor

This frontmatter schema is a clean extension point. A few observations from reviewing the patch:

  1. Schema extensibility: The model and provider keys map well to the existing custom_providers config. Have you verified this works with nested custom_providers entries (e.g., custom:openai:gpt-4o)? The alias resolution path in run_agent.py should be traced.

  2. Fallback behavior: If a skill specifies a model that the current HERMES_MODEL doesn't support (e.g., vision-only model for a text task), what's the expected fallback — error, ignore, or cascade to default? This should be documented in the SKILL.md schema.

  3. Test coverage: The PR touches skill_commands.py and run_agent.py but I don't see dedicated test files. Recommend adding tests for: (a) skill with valid frontmatter override, (b) skill with invalid model name, (c) skill with no frontmatter (defaults apply), (d) interaction with HERMES_MODEL env var precedence.

Overall the pattern is sound and worth merging. The frontmatter approach is more ergonomic than CLI flags for skill-specific routing.

@luoyuctl

luoyuctl commented May 4, 2026

Copy link
Copy Markdown
Author

Thanks for the thorough review, @ether-btc! Addressing each point:

1. Schema extensibility & custom_providers

Yes, I traced the resolution path — frontmatter model:/provider: keys flow through:

SKILL.md frontmatter
  → skill_view() returns model_override/provider_override in JSON
  → _maybe_apply_skill_model_override() in run_agent.py
  → switch_model(skill_model, skill_provider)

switch_model() is the same code path that handles /model slash commands and CLI --model flags. So custom:openai:gpt-4o entries resolve through the existing custom_providers config section exactly as they would from any other model switch. No special-casing needed.

2. Fallback behavior

The switch is best-effort — wrapped in try/except in _maybe_apply_skill_model_override():

  • On success: model switches, and the previous (model, provider) pair is pushed onto _pre_skill_model_stack for restoration at the next user turn.
  • On failure: the stack entry is popped (so restoration doesn't try to restore to a model we never switched to), a debug log is emitted, and the agent keeps its current model — no crash, no error surfaced to the user.

I'll add this fallback behavior to the SKILL.md schema documentation. Good catch — should be explicit.

3. Test coverage

Fair point. Two of the three files (skill_commands.py and skills_tool.py) are testable in isolation, and I'll add unit tests for:

  • (a) valid frontmatter with model: + provider: → correct extraction
  • (b) invalid/unknown model name → graceful None return
  • (c) no frontmatter → (None, None)
  • (d) HERMES_MODEL env var interaction is an integration concern (run_agent.py) — I'll add a smoke test for the stack push/pop pattern

Will push a follow-up commit with these. The overall pattern being best-effort + stack-based makes it resilient even without tests — a failed model switch degrades silently rather than breaking the conversation.

@teknium1

teknium1 commented May 7, 2026

Copy link
Copy Markdown
Contributor

Thanks for the PR. Closing — switching the model mid-session invalidates the prompt cache, and this would do it twice per skill_view() call (switch on load, restore on next turn). On expensive models that's a massive cost regression per AGENTS.md's prompt-caching integrity invariants.

The underlying concern — not wanting to burn expensive-model tokens on structured skill workflows — is real but handled differently:

  • The skill_view tool call itself is cheap (one tool call, no extra inference)
  • The skill content is read into the model's context once and the rest of the skill execution uses that same model
  • If a specific skill is expensive-model-heavy (like a long reasoning task), it's a skill-design question, not a framework one

If you want to explore this, the right shape is probably skill-level delegate_task with an acp_command or toolsets override, which spawns a subagent on a different model with its own context — no cache invalidation in the parent.

@teknium1 teknium1 closed this May 7, 2026
@luoyuctl

luoyuctl commented May 7, 2026

Copy link
Copy Markdown
Author

ok

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P3 Low — cosmetic, nice to have tool/skills Skills system (list, view, manage) type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: LLM model switch by skill

4 participants