mirror of
https://github.com/router-for-me/CLIProxyAPIPlus.git
synced 2026-05-06 21:53:28 +08:00
Merge pull request #453 from HeCHieh/fix/github-copilot-gpt54-responses
Fix GitHub Copilot gpt-5.4 endpoint routing
This commit is contained in:
@@ -449,6 +449,19 @@ func GetGitHubCopilotModels() []*ModelInfo {
|
|||||||
SupportedEndpoints: []string{"/responses"},
|
SupportedEndpoints: []string{"/responses"},
|
||||||
Thinking: &ThinkingSupport{Levels: []string{"none", "low", "medium", "high", "xhigh"}},
|
Thinking: &ThinkingSupport{Levels: []string{"none", "low", "medium", "high", "xhigh"}},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
ID: "gpt-5.4",
|
||||||
|
Object: "model",
|
||||||
|
Created: now,
|
||||||
|
OwnedBy: "github-copilot",
|
||||||
|
Type: "github-copilot",
|
||||||
|
DisplayName: "GPT-5.4",
|
||||||
|
Description: "OpenAI GPT-5.4 via GitHub Copilot",
|
||||||
|
ContextLength: 200000,
|
||||||
|
MaxCompletionTokens: 32768,
|
||||||
|
SupportedEndpoints: []string{"/responses"},
|
||||||
|
Thinking: &ThinkingSupport{Levels: []string{"none", "low", "medium", "high", "xhigh"}},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
ID: "claude-haiku-4.5",
|
ID: "claude-haiku-4.5",
|
||||||
Object: "model",
|
Object: "model",
|
||||||
|
|||||||
@@ -577,9 +577,33 @@ func useGitHubCopilotResponsesEndpoint(sourceFormat sdktranslator.Format, model
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
baseModel := strings.ToLower(thinking.ParseSuffix(model).ModelName)
|
baseModel := strings.ToLower(thinking.ParseSuffix(model).ModelName)
|
||||||
|
if info := registry.GetGlobalRegistry().GetModelInfo(baseModel, githubCopilotAuthType); info != nil {
|
||||||
|
return len(info.SupportedEndpoints) > 0 && !containsEndpoint(info.SupportedEndpoints, githubCopilotChatPath) && containsEndpoint(info.SupportedEndpoints, githubCopilotResponsesPath)
|
||||||
|
}
|
||||||
|
if info := lookupGitHubCopilotStaticModelInfo(baseModel); info != nil {
|
||||||
|
return len(info.SupportedEndpoints) > 0 && !containsEndpoint(info.SupportedEndpoints, githubCopilotChatPath) && containsEndpoint(info.SupportedEndpoints, githubCopilotResponsesPath)
|
||||||
|
}
|
||||||
return strings.Contains(baseModel, "codex")
|
return strings.Contains(baseModel, "codex")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func lookupGitHubCopilotStaticModelInfo(model string) *registry.ModelInfo {
|
||||||
|
for _, info := range registry.GetStaticModelDefinitionsByChannel(githubCopilotAuthType) {
|
||||||
|
if info != nil && strings.EqualFold(info.ID, model) {
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func containsEndpoint(endpoints []string, endpoint string) bool {
|
||||||
|
for _, item := range endpoints {
|
||||||
|
if item == endpoint {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// flattenAssistantContent converts assistant message content from array format
|
// flattenAssistantContent converts assistant message content from array format
|
||||||
// to a joined string. GitHub Copilot requires assistant content as a string;
|
// to a joined string. GitHub Copilot requires assistant content as a string;
|
||||||
// sending it as an array causes Claude models to re-answer all previous prompts.
|
// sending it as an array causes Claude models to re-answer all previous prompts.
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
|
||||||
sdktranslator "github.com/router-for-me/CLIProxyAPI/v6/sdk/translator"
|
sdktranslator "github.com/router-for-me/CLIProxyAPI/v6/sdk/translator"
|
||||||
"github.com/tidwall/gjson"
|
"github.com/tidwall/gjson"
|
||||||
)
|
)
|
||||||
@@ -70,6 +71,29 @@ func TestUseGitHubCopilotResponsesEndpoint_CodexModel(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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_DynamicRegistryWinsOverStatic(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
reg := registry.GetGlobalRegistry()
|
||||||
|
clientID := "github-copilot-test-client"
|
||||||
|
reg.RegisterClient(clientID, "github-copilot", []*registry.ModelInfo{{
|
||||||
|
ID: "gpt-5.4",
|
||||||
|
SupportedEndpoints: []string{"/chat/completions", "/responses"},
|
||||||
|
}})
|
||||||
|
defer reg.UnregisterClient(clientID)
|
||||||
|
|
||||||
|
if useGitHubCopilotResponsesEndpoint(sdktranslator.FromString("openai"), "gpt-5.4") {
|
||||||
|
t.Fatal("expected dynamic registry definition to take precedence over static fallback")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestUseGitHubCopilotResponsesEndpoint_DefaultChat(t *testing.T) {
|
func TestUseGitHubCopilotResponsesEndpoint_DefaultChat(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
if useGitHubCopilotResponsesEndpoint(sdktranslator.FromString("openai"), "claude-3-5-sonnet") {
|
if useGitHubCopilotResponsesEndpoint(sdktranslator.FromString("openai"), "claude-3-5-sonnet") {
|
||||||
|
|||||||
Reference in New Issue
Block a user