Skip to content

Fix GitHub Copilot gpt-5.4 endpoint routing#453

Open
HeCHieh wants to merge 2 commits intorouter-for-me:mainfrom
HeCHieh:fix/github-copilot-gpt54-responses
Open

Fix GitHub Copilot gpt-5.4 endpoint routing#453
HeCHieh wants to merge 2 commits intorouter-for-me:mainfrom
HeCHieh:fix/github-copilot-gpt54-responses

Conversation

@HeCHieh
Copy link

@HeCHieh HeCHieh commented Mar 22, 2026

Summary

  • route GitHub Copilot requests to /responses when the model metadata says chat/completions is unsupported
  • add a static GitHub Copilot model definition for gpt-5.4 as a /responses-only model
  • add a regression test covering gpt-5.4 routing

Problem

GitHub Copilot gpt-5.4 currently fails with:

{"error":{"message":"model \"gpt-5.4\" is not accessible via the /chat/completions endpoint","code":"unsupported_api_for_model"}}

The root cause is that upstream path selection only checked whether the model name contains codex. That works for codex models, but not for gpt-5.4, which also needs the /responses endpoint.

Testing

  • go test ./internal/runtime/executor ./internal/registry

Copilot AI review requested due to automatic review settings March 22, 2026 11:11
@gemini-code-assist
Copy link

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses an issue where GitHub Copilot's gpt-5.4 model was failing due to incorrect endpoint routing. The changes ensure that requests for gpt-5.4 are correctly routed to the /responses endpoint, as it does not support the /chat/completions endpoint. A new model definition and a regression test have been added to prevent future occurrences.

Highlights

  • Routing Fix: Routes GitHub Copilot requests to the /responses endpoint when the model metadata indicates that chat/completions is unsupported.
  • Model Definition: Adds a static GitHub Copilot model definition for gpt-5.4 as a /responses-only model.
  • Regression Test: Includes a regression test to ensure correct routing for gpt-5.4.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

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 fixes GitHub Copilot request routing for models (specifically gpt-5.4) that do not support the /chat/completions endpoint, ensuring requests are sent to /responses when required.

Changes:

  • Update GitHub Copilot endpoint selection to prefer /responses when model metadata indicates /chat/completions is unsupported.
  • Add a static GitHub Copilot model definition for gpt-5.4 as a /responses-only model.
  • Add a regression test to cover gpt-5.4 routing behavior.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
internal/runtime/executor/github_copilot_executor.go Adds endpoint selection logic based on SupportedEndpoints and a helper to check endpoint membership.
internal/runtime/executor/github_copilot_executor_test.go Adds a regression test asserting gpt-5.4 routes via /responses.
internal/registry/model_definitions.go Adds a static gpt-5.4 GitHub Copilot model entry marked as /responses-only.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +580 to +584
if info := registry.GetGlobalRegistry().GetModelInfo(baseModel, ""); info != nil {
if len(info.SupportedEndpoints) > 0 && !containsEndpoint(info.SupportedEndpoints, githubCopilotChatPath) && containsEndpoint(info.SupportedEndpoints, githubCopilotResponsesPath) {
return true
}
}
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

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

The registry lookup uses GetModelInfo(baseModel, "") which returns the last-registered/global model info for that ID, not the GitHub Copilot provider-specific info. Since ModelRegistry keys are shared across providers, this can select endpoints from an unrelated provider (depending on registration order) and mis-route Copilot traffic. Consider querying provider-specific info (e.g. provider "github-copilot") or otherwise scoping the lookup to Copilot models only.

Suggested change
if info := registry.GetGlobalRegistry().GetModelInfo(baseModel, ""); info != nil {
if len(info.SupportedEndpoints) > 0 && !containsEndpoint(info.SupportedEndpoints, githubCopilotChatPath) && containsEndpoint(info.SupportedEndpoints, githubCopilotResponsesPath) {
return true
}
}

Copilot uses AI. Check for mistakes.
Comment on lines +585 to +593
for _, info := range registry.GetGitHubCopilotModels() {
if info == nil || !strings.EqualFold(info.ID, baseModel) {
continue
}
if len(info.SupportedEndpoints) > 0 && !containsEndpoint(info.SupportedEndpoints, githubCopilotChatPath) && containsEndpoint(info.SupportedEndpoints, githubCopilotResponsesPath) {
return true
}
break
}
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

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

useGitHubCopilotResponsesEndpoint calls registry.GetGitHubCopilotModels() (which constructs the full static model list) on every request and then linearly scans it. This adds avoidable allocations/CPU in a hot path; consider caching a map/set of responses-only Copilot models (or consulting the dynamic registry first and only falling back to static definitions on miss).

Copilot uses AI. Check for mistakes.
Comment on lines +73 to +76
func TestUseGitHubCopilotResponsesEndpoint_RegistryResponsesOnlyModel(t *testing.T) {
t.Parallel()
if !useGitHubCopilotResponsesEndpoint(sdktranslator.FromString("openai"), "gpt-5.4") {
t.Fatal("expected responses-only registry model to use /responses")
Copy link

Copilot AI Mar 22, 2026

Choose a reason for hiding this comment

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

This test name suggests it exercises the dynamic ModelRegistry path, but it only relies on the static GitHub Copilot model list (no registry setup). Either rename it to reflect the static-definition behavior, or extend it to register a temporary model in the global registry with SupportedEndpoints={"/responses"} and assert the registry-based routing logic is honored.

Suggested change
func TestUseGitHubCopilotResponsesEndpoint_RegistryResponsesOnlyModel(t *testing.T) {
t.Parallel()
if !useGitHubCopilotResponsesEndpoint(sdktranslator.FromString("openai"), "gpt-5.4") {
t.Fatal("expected responses-only registry model to use /responses")
func TestUseGitHubCopilotResponsesEndpoint_StaticResponsesOnlyModel(t *testing.T) {
t.Parallel()
if !useGitHubCopilotResponsesEndpoint(sdktranslator.FromString("openai"), "gpt-5.4") {
t.Fatal("expected responses-only model to use /responses")

Copilot uses AI. Check for mistakes.
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request correctly addresses the routing issue for the gpt-5.4 model by adding a static definition and updating the endpoint selection logic. The changes are logical and include a necessary regression test. I have two suggestions for improvement: one to enhance consistency in the new model definition, and another to correct a potential logic flaw in the endpoint selection that could cause issues with model definitions in the dynamic registry.

Comment on lines +580 to +593
if info := registry.GetGlobalRegistry().GetModelInfo(baseModel, ""); info != nil {
if len(info.SupportedEndpoints) > 0 && !containsEndpoint(info.SupportedEndpoints, githubCopilotChatPath) && containsEndpoint(info.SupportedEndpoints, githubCopilotResponsesPath) {
return true
}
}
for _, info := range registry.GetGitHubCopilotModels() {
if info == nil || !strings.EqualFold(info.ID, baseModel) {
continue
}
if len(info.SupportedEndpoints) > 0 && !containsEndpoint(info.SupportedEndpoints, githubCopilotChatPath) && containsEndpoint(info.SupportedEndpoints, githubCopilotResponsesPath) {
return true
}
break
}

Choose a reason for hiding this comment

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

high

The current logic for determining which endpoint to use has a potential flaw in how it prioritizes model definitions. It first checks the dynamic registry, and if the model is found but isn't a /responses-only model, it falls through to check the static list of GitHub Copilot models. This can lead to incorrect behavior if a model has different definitions in the dynamic and static registries, as the dynamic definition should be considered authoritative.

For instance, if a model is dynamically registered with SupportedEndpoints: ["/chat/completions", "/responses"], the first check fails. If a stale static definition for the same model exists with SupportedEndpoints: ["/responses"], the function will incorrectly return true.

The logic should be revised to ensure that if a model is found in the dynamic registry, that definition is used exclusively for the decision, and the static list is only consulted as a fallback.

Suggested change
if info := registry.GetGlobalRegistry().GetModelInfo(baseModel, ""); info != nil {
if len(info.SupportedEndpoints) > 0 && !containsEndpoint(info.SupportedEndpoints, githubCopilotChatPath) && containsEndpoint(info.SupportedEndpoints, githubCopilotResponsesPath) {
return true
}
}
for _, info := range registry.GetGitHubCopilotModels() {
if info == nil || !strings.EqualFold(info.ID, baseModel) {
continue
}
if len(info.SupportedEndpoints) > 0 && !containsEndpoint(info.SupportedEndpoints, githubCopilotChatPath) && containsEndpoint(info.SupportedEndpoints, githubCopilotResponsesPath) {
return true
}
break
}
if info := registry.GetGlobalRegistry().GetModelInfo(baseModel, ""); info != nil {
// If model is in dynamic registry, its definition is authoritative.
if len(info.SupportedEndpoints) > 0 {
return !containsEndpoint(info.SupportedEndpoints, githubCopilotChatPath) && containsEndpoint(info.SupportedEndpoints, githubCopilotResponsesPath)
}
// No endpoint info, so fall through to the codex check.
} else {
// If not in dynamic registry, check static definitions.
for _, info := range registry.GetGitHubCopilotModels() {
if info != nil && strings.EqualFold(info.ID, baseModel) {
if len(info.SupportedEndpoints) > 0 {
return !containsEndpoint(info.SupportedEndpoints, githubCopilotChatPath) && containsEndpoint(info.SupportedEndpoints, githubCopilotResponsesPath)
}
// Found model, but no endpoint info, so fall through to codex check.
break
}
}
}

ContextLength: 200000,
MaxCompletionTokens: 32768,
SupportedEndpoints: []string{"/responses"},
Thinking: &ThinkingSupport{Levels: []string{"low", "medium", "high", "xhigh"}},

Choose a reason for hiding this comment

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

medium

For consistency with other /responses-only models like gpt-5.3-codex, consider adding "none" to the Thinking.Levels for gpt-5.4. This would allow users to explicitly disable the thinking feature for this model, which is a common option for similar models.

Suggested change
Thinking: &ThinkingSupport{Levels: []string{"low", "medium", "high", "xhigh"}},
Thinking: &ThinkingSupport{Levels: []string{"none", "low", "medium", "high", "xhigh"}},

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.

2 participants