Skip to content

feat(chat): add 'model' parameter to runSubagent tool for call-time model override#306841

Closed
RockyWearsAHat wants to merge 2 commits intomicrosoft:mainfrom
RockyWearsAHat:feature/runsubagent-model-param
Closed

feat(chat): add 'model' parameter to runSubagent tool for call-time model override#306841
RockyWearsAHat wants to merge 2 commits intomicrosoft:mainfrom
RockyWearsAHat:feature/runsubagent-model-param

Conversation

@RockyWearsAHat
Copy link
Copy Markdown

Summary

Add an optional model parameter to the runSubagent built-in tool, allowing callers to specify which language model a subagent invocation should use at call time, independent of the agent's model: frontmatter configuration.

Motivation

Currently, subagent model assignment is static — determined by the agent definition's model: frontmatter and resolved at agent load time. This works for fixed agent pipelines, but leaves orchestrator agents unable to route individual tasks to the most appropriate model based on task complexity.

Use cases:

  • Cost optimization: An orchestrator routes simple lookups to a cheaper model (claude-haiku-4-5) and reserves the parent model for complex reasoning.
  • Capability matching: A task requiring strong code generation gets a code-specialized model, while a summarization task gets a general-purpose one.
  • Dynamic routing: MCP tools that surface available models (e.g., list_language_models) enable the LLM to make informed model selection decisions.

Design

Resolution Priority

The model resolution in resolveSubagentModel() now follows this priority order:

  1. Call-time model parameter (highest precedence) — from the tool invocation
  2. Agent's model: frontmatter — qualified names from .agent.md
  3. Parent model (default) — inherited from the orchestrator

Model Resolution

The new resolveModelHint() helper resolves a model hint string by:

  1. Trying qualified name lookup via lookupLanguageModelByQualifiedName() (e.g., "Claude Sonnet 4.6 (Copilot)")
  2. Falling back to direct model ID lookup via lookupLanguageModel() (e.g., "claude-sonnet-4-6")
  3. Returning undefined if neither resolves, with a warn-level log

Cost Safety

The existing multiplier-based cost cap continues to apply — the resolved model's multiplierNumeric must not exceed the parent agent's multiplier. This cap was moved outside the if (subagent) block to apply uniformly to all resolution paths.

Changes

runSubagentTool.ts (+65 lines)

  • Add model?: string to IRunSubagentToolInputParams
  • Add model to the tool's input schema in getToolData()
  • Extract resolveModelHint() helper for dual-lookup model resolution
  • Extend resolveSubagentModel() with optional callTimeModelHint parameter
  • Move multiplier cap to apply uniformly to all resolution paths
  • Update invoke() no-subagent branch to handle model override from cache
  • Update prepareToolInvocation() to pass args.model through

runSubagentTool.test.ts (+273 lines)

  • Schema test: model property present in tool data
  • 6 new tests in call-time model parameter suite:
    • Override via qualified name without a subagent
    • Override via direct model ID without a subagent
    • Call-time model takes precedence over agent model: frontmatter
    • Call-time model respects multiplier cap (falls back to parent)
    • Unknown model name falls back gracefully to parent model
    • Override with cheaper model on a named agent

Testing

All 24 tests in the RunSubagentTool test suite pass, including the 7 new tests.

Related

…odel override

Add an optional `model` parameter to the `runSubagent` built-in tool that
allows callers to specify which language model should be used for a subagent
invocation, overriding the agent's default model from frontmatter.

## Changes

### runSubagentTool.ts
- Add `model?: string` to `IRunSubagentToolInputParams`
- Add `model` property to the tool's input schema in `getToolData()`
- Extract `resolveModelHint()` helper that resolves a model hint string
  by trying qualified name lookup first, then direct model ID lookup
- Extend `resolveSubagentModel()` with optional `callTimeModelHint`
  parameter that takes highest precedence over agent frontmatter
- Move multiplier cap logic outside the `if (subagent)` block so it
  applies uniformly to both agent frontmatter and call-time overrides
- Update `invoke()` no-subagent branch to read `modeModelId` from cache
  and handle call-time model override without a named agent
- Update `prepareToolInvocation()` to pass `args.model` through

### runSubagentTool.test.ts
- Add schema test: `model` property appears in tool data
- Add 6 call-time model parameter tests:
  - Override via qualified name without a subagent
  - Override via direct model ID without a subagent
  - Call-time model takes precedence over agent frontmatter
  - Call-time model respects multiplier cap
  - Unknown model name falls back gracefully
  - Override with cheaper model on a named agent

## Design

Resolution priority:
1. Call-time `model` parameter (highest precedence)
2. Agent's `model:` frontmatter qualified names
3. Parent model (default)

After resolution, the existing multiplier-based cost cap still applies:
a subagent cannot use a more expensive model than the parent agent
regardless of how the model was specified.

Fixes microsoft#306836
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an optional call-time model override to the built-in runSubagent tool so orchestrator agents can route individual subagent invocations to specific language models, while still enforcing the existing multiplier-based cost cap.

Changes:

  • Extend runSubagent tool input schema and params with optional model?: string.
  • Implement model-hint resolution (qualified name → model id) and update model resolution priority (call-time override → agent frontmatter → parent).
  • Add a dedicated test suite covering precedence, qualified-name/id resolution, cost-cap behavior, and fallback cases.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/vs/workbench/contrib/chat/common/tools/builtinTools/runSubagentTool.ts Adds model param support, resolves model hints, and applies multiplier cap across all resolution paths.
src/vs/workbench/contrib/chat/test/common/tools/builtinTools/runSubagentTool.test.ts Adds schema coverage and multiple call-time model override behavior tests.

Comment on lines +446 to +454
if (modeModelId && modeModelId !== mainModelId) {
const mainModelMetadata = mainModelId ? this.languageModelsService.lookupLanguageModel(mainModelId) : undefined;
const resolvedModelMetadata = this.languageModelsService.lookupLanguageModel(modeModelId);
const mainMultiplier = mainModelMetadata?.multiplierNumeric;
const resolvedMultiplier = resolvedModelMetadata?.multiplierNumeric;
if (mainMultiplier !== undefined && resolvedMultiplier !== undefined && resolvedMultiplier > mainMultiplier) {
const source = callTimeModelHint ? 'Call-time' : subagent ? `Subagent '${subagent.name}'` : 'Unknown';
this.logService.warn(`[RunSubagentTool] ${source} requested model '${resolvedModelMetadata?.name}' (multiplier: ${resolvedMultiplier}) which exceeds the main agent model '${mainModelMetadata?.name}' (multiplier: ${mainMultiplier}). Falling back to the main agent model.`);
modeModelId = mainModelId;
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In resolveSubagentModel, the multiplier-cap warning uses source = callTimeModelHint ? 'Call-time' : .... If a call-time model hint is provided but doesn’t resolve (so the override is ignored), the subsequent multiplier-cap warning will still attribute the model request to “Call-time”, even though the selected model may have come from the subagent frontmatter. Consider tracking whether the call-time override actually applied (e.g., a boolean or enum resolutionSource) and use that for the warning message.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comment on lines +521 to +525
suite('call-time model parameter', () => {
function createMetadata(name: string, multiplierNumeric?: number): ILanguageModelChatMetadata {
return {
extension: new ExtensionIdentifier('test.extension'),
name,
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This suite redefines helper functions (createMetadata/createTool/createAgent) that already exist in the earlier "model fallback behavior" suite in the same file. Consider extracting these helpers once (e.g., to file scope or a shared helper) to avoid duplication and keep future changes (like new required metadata fields) from needing updates in multiple places.

Copilot uses AI. Check for mistakes.
Previous commit exposed the runSubagent `model` schema unconditionally, which
conflicted with the feature toggle that controls custom-agent usage. This
change moves the property description under the `customAgentInSubagent` gate
so the schema only offers `model` when agents are available, and updates the
schema test to enable that flag to keep coverage aligned.
@connor4312 connor4312 assigned roblourens and unassigned connor4312 Mar 31, 2026
@roblourens
Copy link
Copy Markdown
Member

roblourens commented Mar 31, 2026

Thanks for the PR, but this is already in progress in #298161, and we should drive that forward cc @digitarald

@roblourens roblourens closed this Mar 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Chat: add 'model' parameter to runSubagent tool for call-time model override

4 participants