-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Fix: Update endpoint selection logic for Claude models in LanguageModelServer #2801
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR updates the endpoint selection logic in LanguageModelServer to handle Claude model variants (haiku, sonnet, and opus) by mapping them to version 4.5 endpoints instead of using GPT-4o-mini as a fallback for haiku models.
Changes:
- Updated model mapping logic to support Claude haiku-4.5, sonnet-4.5, and opus-4.5
- Modified fallback matching patterns for Claude models
- Removed the haiku-to-GPT-4o-mini mapping in favor of proper Claude model routing
Comments suppressed due to low confidence (3)
src/extension/agents/node/langModelServer.ts:321
- The selectEndpoint method lacks test coverage. Given that there are comprehensive tests for the adapters in this module (see openaiAdapter.spec.ts), this critical model selection logic should also have unit tests to verify the mapping and fallback behavior for different Claude model variants.
private selectEndpoint(endpoints: readonly IChatEndpoint[], requestedModel?: string): IChatEndpoint | undefined {
if (requestedModel) {
// Handle model mapping
let mappedModel = requestedModel;
if (requestedModel.startsWith('claude-haiku')) {
mappedModel = 'claude-haiku-4.5';
}
if (requestedModel.startsWith('claude-sonnet-4')) {
mappedModel = 'claude-sonnet-4.5';
}
if (requestedModel.startsWith('claude-opus-4')) {
mappedModel = 'claude-opus-4.5';
}
// Try to find exact match first
let selectedEndpoint = endpoints.find(e => e.family === mappedModel || e.model === mappedModel);
// If not found, try to find by partial match for Anthropic models
if (!selectedEndpoint && requestedModel.startsWith('claude-haiku-4')) {
selectedEndpoint = endpoints.find(e => e.model.includes('claude-haiku-4-5')) ?? endpoints.find(e => e.model.includes('claude'));
} else if (!selectedEndpoint && requestedModel.startsWith('claude-sonnet-4')) {
selectedEndpoint = endpoints.find(e => e.model.includes('claude-sonnet-4-5')) ?? endpoints.find(e => e.model.includes('claude'));
} else if (!selectedEndpoint && requestedModel.startsWith('claude-opus-4')) {
selectedEndpoint = endpoints.find(e => e.model.includes('claude-opus-4-5')) ?? endpoints.find(e => e.model.includes('claude'));
}
return selectedEndpoint;
}
// Use first available model if no criteria specified
return endpoints[0];
}
src/extension/agents/node/langModelServer.ts:311
- The model string format is inconsistent. The fallback searches for 'claude-sonnet-4-5' (with hyphens) while the mapped model is 'claude-sonnet-4.5' (with a dot). This mismatch means the partial match will likely fail. These should use the same format to ensure proper matching.
mappedModel = 'claude-sonnet-4.5';
}
if (requestedModel.startsWith('claude-opus-4')) {
mappedModel = 'claude-opus-4.5';
}
// Try to find exact match first
let selectedEndpoint = endpoints.find(e => e.family === mappedModel || e.model === mappedModel);
// If not found, try to find by partial match for Anthropic models
if (!selectedEndpoint && requestedModel.startsWith('claude-haiku-4')) {
selectedEndpoint = endpoints.find(e => e.model.includes('claude-haiku-4-5')) ?? endpoints.find(e => e.model.includes('claude'));
} else if (!selectedEndpoint && requestedModel.startsWith('claude-sonnet-4')) {
selectedEndpoint = endpoints.find(e => e.model.includes('claude-sonnet-4-5')) ?? endpoints.find(e => e.model.includes('claude'));
src/extension/agents/node/langModelServer.ts:313
- The model string format is inconsistent. The fallback searches for 'claude-opus-4-5' (with hyphens) while the mapped model is 'claude-opus-4.5' (with a dot). This mismatch means the partial match will likely fail. These should use the same format to ensure proper matching.
mappedModel = 'claude-opus-4.5';
}
// Try to find exact match first
let selectedEndpoint = endpoints.find(e => e.family === mappedModel || e.model === mappedModel);
// If not found, try to find by partial match for Anthropic models
if (!selectedEndpoint && requestedModel.startsWith('claude-haiku-4')) {
selectedEndpoint = endpoints.find(e => e.model.includes('claude-haiku-4-5')) ?? endpoints.find(e => e.model.includes('claude'));
} else if (!selectedEndpoint && requestedModel.startsWith('claude-sonnet-4')) {
selectedEndpoint = endpoints.find(e => e.model.includes('claude-sonnet-4-5')) ?? endpoints.find(e => e.model.includes('claude'));
} else if (!selectedEndpoint && requestedModel.startsWith('claude-opus-4')) {
selectedEndpoint = endpoints.find(e => e.model.includes('claude-opus-4-5')) ?? endpoints.find(e => e.model.includes('claude'));
| // If not found, try to find by partial match for Anthropic models | ||
| if (!selectedEndpoint && requestedModel.startsWith('claude-3-5-haiku')) { | ||
| selectedEndpoint = endpoints.find(e => e.model.includes('gpt-4o-mini')) ?? endpoints.find(e => e.model.includes('mini')); | ||
| if (!selectedEndpoint && requestedModel.startsWith('claude-haiku-4')) { |
Copilot
AI
Jan 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a logic inconsistency between the model mapping and the fallback condition. Line 294 maps models starting with 'claude-haiku' to 'claude-haiku-4.5', but line 308 checks if the requestedModel starts with 'claude-haiku-4'. This means the fallback logic will never execute for models that match the mapping condition. The conditions should be aligned: either both check for 'claude-haiku' or both check for 'claude-haiku-4'.
| if (!selectedEndpoint && requestedModel.startsWith('claude-haiku-4')) { | |
| if (!selectedEndpoint && requestedModel.startsWith('claude-haiku')) { |
| mappedModel = 'claude-haiku-4.5'; | ||
| } | ||
| if (requestedModel.startsWith('claude-sonnet-4')) { | ||
| mappedModel = 'claude-sonnet-4'; | ||
| mappedModel = 'claude-sonnet-4.5'; | ||
| } | ||
| if (requestedModel.startsWith('claude-opus-4')) { | ||
| mappedModel = 'claude-opus-4.5'; | ||
| } | ||
|
|
||
| // Try to find exact match first | ||
| let selectedEndpoint = endpoints.find(e => e.family === mappedModel || e.model === mappedModel); | ||
|
|
||
| // If not found, try to find by partial match for Anthropic models | ||
| if (!selectedEndpoint && requestedModel.startsWith('claude-3-5-haiku')) { | ||
| selectedEndpoint = endpoints.find(e => e.model.includes('gpt-4o-mini')) ?? endpoints.find(e => e.model.includes('mini')); | ||
| if (!selectedEndpoint && requestedModel.startsWith('claude-haiku-4')) { | ||
| selectedEndpoint = endpoints.find(e => e.model.includes('claude-haiku-4-5')) ?? endpoints.find(e => e.model.includes('claude')); |
Copilot
AI
Jan 12, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The model string format is inconsistent. The fallback searches for 'claude-haiku-4-5' (with hyphens) while the mapped model is 'claude-haiku-4.5' (with a dot). This mismatch means the partial match will likely fail. These should use the same format to ensure proper matching.
This issue also appears in the following locations of the same file:
- line 298
- line 301
See below for a potential fix:
selectedEndpoint = endpoints.find(e => e.model.includes('claude-haiku-4.5')) ?? endpoints.find(e => e.model.includes('claude'));
} else if (!selectedEndpoint && requestedModel.startsWith('claude-sonnet-4')) {
selectedEndpoint = endpoints.find(e => e.model.includes('claude-sonnet-4.5')) ?? endpoints.find(e => e.model.includes('claude'));
} else if (!selectedEndpoint && requestedModel.startsWith('claude-opus-4')) {
selectedEndpoint = endpoints.find(e => e.model.includes('claude-opus-4.5')) ?? endpoints.find(e => e.model.includes('claude'));
5877dab to
ddd5e1e
Compare
No description provided.