diff --git a/internal/registry/model_definitions.go b/internal/registry/model_definitions.go index f074316c..04d8e467 100644 --- a/internal/registry/model_definitions.go +++ b/internal/registry/model_definitions.go @@ -584,6 +584,7 @@ func GetGitHubCopilotModels() []*ModelInfo { Description: "Google Gemini 2.5 Pro via GitHub Copilot", ContextLength: 1048576, MaxCompletionTokens: 65536, + SupportedEndpoints: []string{"/chat/completions"}, }, { ID: "gemini-3-pro-preview", @@ -595,6 +596,7 @@ func GetGitHubCopilotModels() []*ModelInfo { Description: "Google Gemini 3 Pro Preview via GitHub Copilot", ContextLength: 1048576, MaxCompletionTokens: 65536, + SupportedEndpoints: []string{"/chat/completions"}, }, { ID: "gemini-3.1-pro-preview", @@ -604,8 +606,9 @@ func GetGitHubCopilotModels() []*ModelInfo { Type: "github-copilot", DisplayName: "Gemini 3.1 Pro (Preview)", Description: "Google Gemini 3.1 Pro Preview via GitHub Copilot", - ContextLength: 1048576, + ContextLength: 173000, MaxCompletionTokens: 65536, + SupportedEndpoints: []string{"/chat/completions"}, }, { ID: "gemini-3-flash-preview", @@ -615,8 +618,9 @@ func GetGitHubCopilotModels() []*ModelInfo { Type: "github-copilot", DisplayName: "Gemini 3 Flash (Preview)", Description: "Google Gemini 3 Flash Preview via GitHub Copilot", - ContextLength: 1048576, + ContextLength: 173000, MaxCompletionTokens: 65536, + SupportedEndpoints: []string{"/chat/completions"}, }, { ID: "grok-code-fast-1", diff --git a/internal/registry/model_definitions_test.go b/internal/registry/model_definitions_test.go new file mode 100644 index 00000000..9bf6d416 --- /dev/null +++ b/internal/registry/model_definitions_test.go @@ -0,0 +1,29 @@ +package registry + +import "testing" + +func TestGitHubCopilotGeminiModelsAreChatOnly(t *testing.T) { + models := GetGitHubCopilotModels() + required := map[string]bool{ + "gemini-2.5-pro": false, + "gemini-3-pro-preview": false, + "gemini-3.1-pro-preview": false, + "gemini-3-flash-preview": false, + } + + for _, model := range models { + if _, ok := required[model.ID]; !ok { + continue + } + required[model.ID] = true + if len(model.SupportedEndpoints) != 1 || model.SupportedEndpoints[0] != "/chat/completions" { + t.Fatalf("model %q supported endpoints = %v, want [/chat/completions]", model.ID, model.SupportedEndpoints) + } + } + + for modelID, found := range required { + if !found { + t.Fatalf("expected GitHub Copilot model %q in definitions", modelID) + } + } +} diff --git a/sdk/api/handlers/openai/endpoint_compat.go b/sdk/api/handlers/openai/endpoint_compat.go index d7fc5f2f..991e863c 100644 --- a/sdk/api/handlers/openai/endpoint_compat.go +++ b/sdk/api/handlers/openai/endpoint_compat.go @@ -1,6 +1,9 @@ package openai -import "github.com/router-for-me/CLIProxyAPI/v6/internal/registry" +import ( + "github.com/router-for-me/CLIProxyAPI/v6/internal/registry" + "github.com/router-for-me/CLIProxyAPI/v6/internal/thinking" +) const ( openAIChatEndpoint = "/chat/completions" @@ -12,6 +15,12 @@ func resolveEndpointOverride(modelName, requestedEndpoint string) (string, bool) return "", false } info := registry.GetGlobalRegistry().GetModelInfo(modelName, "") + if info == nil { + baseModel := thinking.ParseSuffix(modelName).ModelName + if baseModel != "" && baseModel != modelName { + info = registry.GetGlobalRegistry().GetModelInfo(baseModel, "") + } + } if info == nil || len(info.SupportedEndpoints) == 0 { return "", false } diff --git a/sdk/api/handlers/openai/endpoint_compat_test.go b/sdk/api/handlers/openai/endpoint_compat_test.go new file mode 100644 index 00000000..b622e6dd --- /dev/null +++ b/sdk/api/handlers/openai/endpoint_compat_test.go @@ -0,0 +1,29 @@ +package openai + +import ( + "testing" + + "github.com/router-for-me/CLIProxyAPI/v6/internal/registry" +) + +func TestResolveEndpointOverride_StripsThinkingSuffix(t *testing.T) { + const clientID = "test-endpoint-compat-suffix" + reg := registry.GetGlobalRegistry() + reg.RegisterClient(clientID, "github-copilot", []*registry.ModelInfo{ + { + ID: "test-gemini-chat-only", + SupportedEndpoints: []string{openAIChatEndpoint}, + }, + }) + t.Cleanup(func() { + reg.UnregisterClient(clientID) + }) + + override, ok := resolveEndpointOverride("test-gemini-chat-only(high)", openAIResponsesEndpoint) + if !ok { + t.Fatalf("expected endpoint override to be resolved") + } + if override != openAIChatEndpoint { + t.Fatalf("override endpoint = %q, want %q", override, openAIChatEndpoint) + } +}