Skip to content

Conversation

@bhavyaus
Copy link
Contributor

No description provided.

@TylerLeonhardt TylerLeonhardt marked this pull request as ready for review January 12, 2026 23:14
Copilot AI review requested due to automatic review settings January 12, 2026 23:14
Copy link
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

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')) {
Copy link

Copilot AI Jan 12, 2026

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'.

Suggested change
if (!selectedEndpoint && requestedModel.startsWith('claude-haiku-4')) {
if (!selectedEndpoint && requestedModel.startsWith('claude-haiku')) {

Copilot uses AI. Check for mistakes.
Comment on lines +295 to +309
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'));
Copy link

Copilot AI Jan 12, 2026

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'));

Copilot uses AI. Check for mistakes.
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to no response for status checks Jan 12, 2026
@TylerLeonhardt TylerLeonhardt added this pull request to the merge queue Jan 12, 2026
@bhavyaus bhavyaus removed this pull request from the merge queue due to a manual request Jan 13, 2026
@bhavyaus bhavyaus force-pushed the dev/bhavyau/fix-claude-code branch from 5877dab to ddd5e1e Compare January 13, 2026 00:03
@bhavyaus bhavyaus enabled auto-merge January 13, 2026 00:03
@bhavyaus bhavyaus added this pull request to the merge queue Jan 13, 2026
Merged via the queue into main with commit c473644 Jan 13, 2026
22 of 23 checks passed
@bhavyaus bhavyaus deleted the dev/bhavyau/fix-claude-code branch January 13, 2026 00:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants