Fix router model selection: filter available_models, remove same-provider override, iterate candidates#310210
Open
Fix router model selection: filter available_models, remove same-provider override, iterate candidates#310210
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Updates Copilot’s automode router model selection so that when the router returns a ranked candidate_models list, the service selects the first candidate that actually exists in the user’s configured endpoint list (instead of only trying candidate_models[0]).
Changes:
- Iterate through
candidate_modelsto find the first model with a matching known endpoint. - Keep same-provider preference, but apply it against the candidate list before falling back to the first available candidate endpoint.
Show a summary per file
| File | Description |
|---|---|
| extensions/copilot/src/platform/endpoint/node/automodeService.ts | Adjusts router-result handling to choose the first viable endpoint from the router’s ordered candidates. |
Copilot's findings
- Files reviewed: 1/1 changed files
- Comments generated: 2
Contributor
Screenshot ChangesBase: Changed (3)blocks-ci screenshots changedReplace the contents of Updated blocks-ci-screenshots.md<!-- auto-generated by CI — do not edit manually -->
#### editor/codeEditor/CodeEditor/Dark

#### editor/codeEditor/CodeEditor/Light
 |
f5ce3f6 to
3183de3
Compare
c25e8dd to
8535d04
Compare
jinu-jang
approved these changes
Apr 15, 2026
2108023 to
1c38157
Compare
Two changes to how the router's model recommendation is applied: 1. Remove _findSameProviderModel from _tryRouterSelection: When the router re-runs after /compact or background summarization, the same-provider preference was overriding the router's cost-saving decision. E.g., router picks claude-haiku-4.5 (cheap) but same- provider forces claude-sonnet-4.6 (expensive) because the prior turn used Anthropic. Same-provider is retained in _selectDefaultModel (the non-router fallback path) where it makes sense. 2. Use _findFirstAvailableModel to iterate the full candidate_models list instead of only trying [0], as a defensive fallback. Telemetry from candidateModel field (#308597) shows 63% switch rate where the router's pick is overridden — most switches go toward more expensive models, erasing cost savings.
1c38157 to
051b047
Compare
added 2 commits
April 15, 2026 21:28
…ndidate-models-fallback # Conflicts: # extensions/copilot/src/platform/endpoint/node/automodeService.ts # extensions/copilot/src/platform/endpoint/node/test/automodeService.spec.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Three issues cause the router's model recommendation to be silently overridden on the client, sending simple queries to expensive models:
1. available_models / knownEndpoints out of sync
knownEndpoints(fromGET /models) andavailable_models(fromPOST /auto_models) are separate CAPI calls with independent caches (10-min vs 5-min refresh). When a model likeclaude-haiku-4.5appears inavailable_modelsbefore the Models API returns it, the router recommends it but the client can't resolve it to an endpoint — causing a silent fallback to the first available (often the most expensive) model.Observed in logs: Router returns
no_reasoningat 96% confidence withcandidate_models: [claude-haiku-4.5, gpt-4.1, ...]for "what day is today", but the actual request goes toclaude-sonnet-4.6.2. Same-provider override negates router decisions
When the router re-runs after
/compactor background summarization,_findSameProviderModeloverrides the router's recommendation with a same-provider model, often switching to a more expensive one.3. Only candidate_models[0] attempted
The fallback only tried the first candidate model. If that one model had no matching endpoint, all remaining candidates were discarded.
Fix
Filter available_models before routing
Intersect
available_modelswithknownEndpointsbefore sending to the router. The router now only sees models the client can actually serve, eliminating the sync gap at the source.Remove same-provider override from router path
When the router runs, trust its ranked candidate list directly. Same-provider preference is retained in
_selectDefaultModel(the non-router fallback path).Iterate all candidates via _findFirstAvailableModel
Walk the full
candidate_modelslist instead of only trying[0], as defense-in-depth.Forward
has_imageto router instead of early-returningImage requests no longer bypass the router. Instead,
has_image: trueis sent so the server can factor it in. Vision fallback (_applyVisionFallback) still handles downstream model selection if the routed model doesn't support vision.Emit
automode.routerModelSelectiontelemetry (from #310238)New event reports
candidateModelvsactualModelwithoverrideReasonandfilteredModelCountso analysts can detect overrides and CAPI sync gaps without fragile telemetry joins.Add
RouterDecisionErrorwith server error code parsingNon-OK router responses now parse the JSON error body and surface the error code (e.g.
no_vision_models) as thefallbackReasoninstead of a genericrouterError.Warn-level logs for debuggability
All sync mismatches and fallbacks now emit warn-level logs with full context instead of being silently swallowed.
Behavioral changes
has_image: true) instead of being skipped entirely. Vision fallback still applies after routing.conversationIdpassed to router usessessionResource ?? sessionId ?? 'unknown'(matching cache key) instead of rawchatRequest?.sessionId.Supersedes #310238.