From af15083496dfbde8ddaaafc5826e11583ac2f586 Mon Sep 17 00:00:00 2001 From: ChrAlpha <53332481+ChrAlpha@users.noreply.github.com> Date: Sun, 15 Feb 2026 03:16:08 +0000 Subject: [PATCH 1/3] feat(models): add Thinking support to GitHub Copilot models Enhance the model definitions by introducing Thinking support with various levels for each model. --- internal/registry/model_definitions.go | 9 +++++++++ .../executor/github_copilot_executor.go | 20 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/internal/registry/model_definitions.go b/internal/registry/model_definitions.go index 30ebe6c1..49a6de47 100644 --- a/internal/registry/model_definitions.go +++ b/internal/registry/model_definitions.go @@ -144,6 +144,7 @@ func GetGitHubCopilotModels() []*ModelInfo { ContextLength: 200000, MaxCompletionTokens: 32768, SupportedEndpoints: []string{"/chat/completions", "/responses"}, + Thinking: &ThinkingSupport{Levels: []string{"low", "medium", "high"}}, }, { ID: "gpt-5-mini", @@ -156,6 +157,7 @@ func GetGitHubCopilotModels() []*ModelInfo { ContextLength: 128000, MaxCompletionTokens: 16384, SupportedEndpoints: []string{"/chat/completions", "/responses"}, + Thinking: &ThinkingSupport{Levels: []string{"low", "medium", "high"}}, }, { ID: "gpt-5-codex", @@ -168,6 +170,7 @@ func GetGitHubCopilotModels() []*ModelInfo { ContextLength: 200000, MaxCompletionTokens: 32768, SupportedEndpoints: []string{"/responses"}, + Thinking: &ThinkingSupport{Levels: []string{"low", "medium", "high"}}, }, { ID: "gpt-5.1", @@ -180,6 +183,7 @@ func GetGitHubCopilotModels() []*ModelInfo { ContextLength: 200000, MaxCompletionTokens: 32768, SupportedEndpoints: []string{"/chat/completions", "/responses"}, + Thinking: &ThinkingSupport{Levels: []string{"none", "low", "medium", "high", "xhigh"}}, }, { ID: "gpt-5.1-codex", @@ -192,6 +196,7 @@ func GetGitHubCopilotModels() []*ModelInfo { ContextLength: 200000, MaxCompletionTokens: 32768, SupportedEndpoints: []string{"/responses"}, + Thinking: &ThinkingSupport{Levels: []string{"none", "low", "medium", "high", "xhigh"}}, }, { ID: "gpt-5.1-codex-mini", @@ -204,6 +209,7 @@ func GetGitHubCopilotModels() []*ModelInfo { ContextLength: 128000, MaxCompletionTokens: 16384, SupportedEndpoints: []string{"/responses"}, + Thinking: &ThinkingSupport{Levels: []string{"none", "low", "medium", "high", "xhigh"}}, }, { ID: "gpt-5.1-codex-max", @@ -216,6 +222,7 @@ func GetGitHubCopilotModels() []*ModelInfo { ContextLength: 200000, MaxCompletionTokens: 32768, SupportedEndpoints: []string{"/responses"}, + Thinking: &ThinkingSupport{Levels: []string{"none", "low", "medium", "high", "xhigh"}}, }, { ID: "gpt-5.2", @@ -228,6 +235,7 @@ func GetGitHubCopilotModels() []*ModelInfo { ContextLength: 200000, MaxCompletionTokens: 32768, SupportedEndpoints: []string{"/chat/completions", "/responses"}, + Thinking: &ThinkingSupport{Levels: []string{"none", "low", "medium", "high", "xhigh"}}, }, { ID: "gpt-5.2-codex", @@ -240,6 +248,7 @@ func GetGitHubCopilotModels() []*ModelInfo { ContextLength: 200000, MaxCompletionTokens: 32768, SupportedEndpoints: []string{"/responses"}, + Thinking: &ThinkingSupport{Levels: []string{"none", "low", "medium", "high", "xhigh"}}, }, { ID: "claude-haiku-4.5", diff --git a/internal/runtime/executor/github_copilot_executor.go b/internal/runtime/executor/github_copilot_executor.go index 3681faf8..09066186 100644 --- a/internal/runtime/executor/github_copilot_executor.go +++ b/internal/runtime/executor/github_copilot_executor.go @@ -123,6 +123,16 @@ func (e *GitHubCopilotExecutor) Execute(ctx context.Context, auth *cliproxyauth. body := sdktranslator.TranslateRequest(from, to, req.Model, bytes.Clone(req.Payload), false) body = e.normalizeModel(req.Model, body) body = flattenAssistantContent(body) + + thinkingProvider := "openai" + if useResponses { + thinkingProvider = "codex" + } + body, err = thinking.ApplyThinking(body, req.Model, from.String(), thinkingProvider, e.Identifier()) + if err != nil { + return resp, err + } + requestedModel := payloadRequestedModel(opts, req.Model) body = applyPayloadConfigWithRoot(e.cfg, req.Model, to.String(), "", body, originalTranslated, requestedModel) body, _ = sjson.SetBytes(body, "stream", false) @@ -229,6 +239,16 @@ func (e *GitHubCopilotExecutor) ExecuteStream(ctx context.Context, auth *cliprox body := sdktranslator.TranslateRequest(from, to, req.Model, bytes.Clone(req.Payload), true) body = e.normalizeModel(req.Model, body) body = flattenAssistantContent(body) + + thinkingProvider := "openai" + if useResponses { + thinkingProvider = "codex" + } + body, err = thinking.ApplyThinking(body, req.Model, from.String(), thinkingProvider, e.Identifier()) + if err != nil { + return nil, err + } + requestedModel := payloadRequestedModel(opts, req.Model) body = applyPayloadConfigWithRoot(e.cfg, req.Model, to.String(), "", body, originalTranslated, requestedModel) body, _ = sjson.SetBytes(body, "stream", true) From 9e652a35407d55cfbf5e718184b3521550e66285 Mon Sep 17 00:00:00 2001 From: ChrAlpha <53332481+ChrAlpha@users.noreply.github.com> Date: Sun, 15 Feb 2026 06:12:08 +0000 Subject: [PATCH 2/3] fix(github-copilot): remove 'xhigh' level from Thinking support --- internal/registry/model_definitions.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/registry/model_definitions.go b/internal/registry/model_definitions.go index 49a6de47..12464094 100644 --- a/internal/registry/model_definitions.go +++ b/internal/registry/model_definitions.go @@ -183,7 +183,7 @@ func GetGitHubCopilotModels() []*ModelInfo { ContextLength: 200000, MaxCompletionTokens: 32768, SupportedEndpoints: []string{"/chat/completions", "/responses"}, - Thinking: &ThinkingSupport{Levels: []string{"none", "low", "medium", "high", "xhigh"}}, + Thinking: &ThinkingSupport{Levels: []string{"none", "low", "medium", "high"}}, }, { ID: "gpt-5.1-codex", @@ -196,7 +196,7 @@ func GetGitHubCopilotModels() []*ModelInfo { ContextLength: 200000, MaxCompletionTokens: 32768, SupportedEndpoints: []string{"/responses"}, - Thinking: &ThinkingSupport{Levels: []string{"none", "low", "medium", "high", "xhigh"}}, + Thinking: &ThinkingSupport{Levels: []string{"none", "low", "medium", "high"}}, }, { ID: "gpt-5.1-codex-mini", @@ -209,7 +209,7 @@ func GetGitHubCopilotModels() []*ModelInfo { ContextLength: 128000, MaxCompletionTokens: 16384, SupportedEndpoints: []string{"/responses"}, - Thinking: &ThinkingSupport{Levels: []string{"none", "low", "medium", "high", "xhigh"}}, + Thinking: &ThinkingSupport{Levels: []string{"none", "low", "medium", "high"}}, }, { ID: "gpt-5.1-codex-max", From 795da13d5d7eaaeca538186210eb899c52e78d1c Mon Sep 17 00:00:00 2001 From: ChrAlpha <53332481+ChrAlpha@users.noreply.github.com> Date: Sun, 15 Feb 2026 06:40:52 +0000 Subject: [PATCH 3/3] feat(tests): add comprehensive GitHub Copilot tests for reasoning effort levels --- test/thinking_conversion_test.go | 286 +++++++++++++++++++++++++++++++ 1 file changed, 286 insertions(+) diff --git a/test/thinking_conversion_test.go b/test/thinking_conversion_test.go index 781a1667..e7beb1a3 100644 --- a/test/thinking_conversion_test.go +++ b/test/thinking_conversion_test.go @@ -1316,6 +1316,122 @@ func TestThinkingE2EMatrix_Suffix(t *testing.T) { includeThoughts: "true", expectErr: false, }, + + // GitHub Copilot tests: gpt-5, gpt-5.1, gpt-5.2 (Levels=low/medium/high, some with none/xhigh) + // Testing /chat/completions endpoint (openai format) - with suffix + + // Case 112: OpenAI to gpt-5, level high → high + { + name: "112", + from: "openai", + to: "github-copilot", + model: "gpt-5(high)", + inputJSON: `{"model":"gpt-5(high)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning_effort", + expectValue: "high", + expectErr: false, + }, + // Case 113: OpenAI to gpt-5, level none → clamped to low (ZeroAllowed=false) + { + name: "113", + from: "openai", + to: "github-copilot", + model: "gpt-5(none)", + inputJSON: `{"model":"gpt-5(none)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning_effort", + expectValue: "low", + expectErr: false, + }, + // Case 114: OpenAI to gpt-5.1, level none → none (ZeroAllowed=true) + { + name: "114", + from: "openai", + to: "github-copilot", + model: "gpt-5.1(none)", + inputJSON: `{"model":"gpt-5.1(none)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning_effort", + expectValue: "none", + expectErr: false, + }, + // Case 115: OpenAI to gpt-5.2, level xhigh → xhigh + { + name: "115", + from: "openai", + to: "github-copilot", + model: "gpt-5.2(xhigh)", + inputJSON: `{"model":"gpt-5.2(xhigh)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning_effort", + expectValue: "xhigh", + expectErr: false, + }, + // Case 116: OpenAI to gpt-5, level xhigh (out of range) → error + { + name: "116", + from: "openai", + to: "github-copilot", + model: "gpt-5(xhigh)", + inputJSON: `{"model":"gpt-5(xhigh)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "", + expectErr: true, + }, + // Case 117: Claude to gpt-5.1, budget 0 → none (ZeroAllowed=true) + { + name: "117", + from: "claude", + to: "github-copilot", + model: "gpt-5.1(0)", + inputJSON: `{"model":"gpt-5.1(0)","messages":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning_effort", + expectValue: "none", + expectErr: false, + }, + + // GitHub Copilot tests: /responses endpoint (codex format) - with suffix + + // Case 118: OpenAI-Response to gpt-5-codex, level high → high + { + name: "118", + from: "openai-response", + to: "github-copilot", + model: "gpt-5-codex(high)", + inputJSON: `{"model":"gpt-5-codex(high)","input":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning.effort", + expectValue: "high", + expectErr: false, + }, + // Case 119: OpenAI-Response to gpt-5.2-codex, level xhigh → xhigh + { + name: "119", + from: "openai-response", + to: "github-copilot", + model: "gpt-5.2-codex(xhigh)", + inputJSON: `{"model":"gpt-5.2-codex(xhigh)","input":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning.effort", + expectValue: "xhigh", + expectErr: false, + }, + // Case 120: OpenAI-Response to gpt-5.2-codex, level none → none + { + name: "120", + from: "openai-response", + to: "github-copilot", + model: "gpt-5.2-codex(none)", + inputJSON: `{"model":"gpt-5.2-codex(none)","input":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning.effort", + expectValue: "none", + expectErr: false, + }, + // Case 121: OpenAI-Response to gpt-5-codex, level none → clamped to low (ZeroAllowed=false) + { + name: "121", + from: "openai-response", + to: "github-copilot", + model: "gpt-5-codex(none)", + inputJSON: `{"model":"gpt-5-codex(none)","input":[{"role":"user","content":"hi"}]}`, + expectField: "reasoning.effort", + expectValue: "low", + expectErr: false, + }, } runThinkingTests(t, cases) @@ -2585,6 +2701,122 @@ func TestThinkingE2EMatrix_Body(t *testing.T) { includeThoughts: "true", expectErr: false, }, + + // GitHub Copilot tests: gpt-5, gpt-5.1, gpt-5.2 (Levels=low/medium/high, some with none/xhigh) + // Testing /chat/completions endpoint (openai format) - with body params + + // Case 112: OpenAI to gpt-5, reasoning_effort=high → high + { + name: "112", + from: "openai", + to: "github-copilot", + model: "gpt-5", + inputJSON: `{"model":"gpt-5","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"high"}`, + expectField: "reasoning_effort", + expectValue: "high", + expectErr: false, + }, + // Case 113: OpenAI to gpt-5, reasoning_effort=none → clamped to low (ZeroAllowed=false) + { + name: "113", + from: "openai", + to: "github-copilot", + model: "gpt-5", + inputJSON: `{"model":"gpt-5","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"none"}`, + expectField: "reasoning_effort", + expectValue: "low", + expectErr: false, + }, + // Case 114: OpenAI to gpt-5.1, reasoning_effort=none → none (ZeroAllowed=true) + { + name: "114", + from: "openai", + to: "github-copilot", + model: "gpt-5.1", + inputJSON: `{"model":"gpt-5.1","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"none"}`, + expectField: "reasoning_effort", + expectValue: "none", + expectErr: false, + }, + // Case 115: OpenAI to gpt-5.2, reasoning_effort=xhigh → xhigh + { + name: "115", + from: "openai", + to: "github-copilot", + model: "gpt-5.2", + inputJSON: `{"model":"gpt-5.2","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"xhigh"}`, + expectField: "reasoning_effort", + expectValue: "xhigh", + expectErr: false, + }, + // Case 116: OpenAI to gpt-5, reasoning_effort=xhigh (out of range) → error + { + name: "116", + from: "openai", + to: "github-copilot", + model: "gpt-5", + inputJSON: `{"model":"gpt-5","messages":[{"role":"user","content":"hi"}],"reasoning_effort":"xhigh"}`, + expectField: "", + expectErr: true, + }, + // Case 117: Claude to gpt-5.1, thinking.budget_tokens=0 → none (ZeroAllowed=true) + { + name: "117", + from: "claude", + to: "github-copilot", + model: "gpt-5.1", + inputJSON: `{"model":"gpt-5.1","messages":[{"role":"user","content":"hi"}],"thinking":{"type":"enabled","budget_tokens":0}}`, + expectField: "reasoning_effort", + expectValue: "none", + expectErr: false, + }, + + // GitHub Copilot tests: /responses endpoint (codex format) - with body params + + // Case 118: OpenAI-Response to gpt-5-codex, reasoning.effort=high → high + { + name: "118", + from: "openai-response", + to: "github-copilot", + model: "gpt-5-codex", + inputJSON: `{"model":"gpt-5-codex","input":[{"role":"user","content":"hi"}],"reasoning":{"effort":"high"}}`, + expectField: "reasoning.effort", + expectValue: "high", + expectErr: false, + }, + // Case 119: OpenAI-Response to gpt-5.2-codex, reasoning.effort=xhigh → xhigh + { + name: "119", + from: "openai-response", + to: "github-copilot", + model: "gpt-5.2-codex", + inputJSON: `{"model":"gpt-5.2-codex","input":[{"role":"user","content":"hi"}],"reasoning":{"effort":"xhigh"}}`, + expectField: "reasoning.effort", + expectValue: "xhigh", + expectErr: false, + }, + // Case 120: OpenAI-Response to gpt-5.2-codex, reasoning.effort=none → none + { + name: "120", + from: "openai-response", + to: "github-copilot", + model: "gpt-5.2-codex", + inputJSON: `{"model":"gpt-5.2-codex","input":[{"role":"user","content":"hi"}],"reasoning":{"effort":"none"}}`, + expectField: "reasoning.effort", + expectValue: "none", + expectErr: false, + }, + // Case 121: OpenAI-Response to gpt-5-codex, reasoning.effort=none → clamped to low (ZeroAllowed=false) + { + name: "121", + from: "openai-response", + to: "github-copilot", + model: "gpt-5-codex", + inputJSON: `{"model":"gpt-5-codex","input":[{"role":"user","content":"hi"}],"reasoning":{"effort":"none"}}`, + expectField: "reasoning.effort", + expectValue: "low", + expectErr: false, + }, } runThinkingTests(t, cases) @@ -2813,6 +3045,51 @@ func getTestModels() []*registry.ModelInfo { DisplayName: "MiniMax Test Model", Thinking: ®istry.ThinkingSupport{Levels: []string{"none", "auto", "minimal", "low", "medium", "high", "xhigh"}}, }, + { + ID: "gpt-5", + Object: "model", + Created: 1700000000, + OwnedBy: "github-copilot", + Type: "github-copilot", + DisplayName: "GPT-5", + Thinking: ®istry.ThinkingSupport{Levels: []string{"low", "medium", "high"}, ZeroAllowed: false, DynamicAllowed: false}, + }, + { + ID: "gpt-5.1", + Object: "model", + Created: 1700000000, + OwnedBy: "github-copilot", + Type: "github-copilot", + DisplayName: "GPT-5.1", + Thinking: ®istry.ThinkingSupport{Levels: []string{"none", "low", "medium", "high"}, ZeroAllowed: true, DynamicAllowed: false}, + }, + { + ID: "gpt-5.2", + Object: "model", + Created: 1700000000, + OwnedBy: "github-copilot", + Type: "github-copilot", + DisplayName: "GPT-5.2", + Thinking: ®istry.ThinkingSupport{Levels: []string{"none", "low", "medium", "high", "xhigh"}, ZeroAllowed: true, DynamicAllowed: false}, + }, + { + ID: "gpt-5-codex", + Object: "model", + Created: 1700000000, + OwnedBy: "github-copilot", + Type: "github-copilot", + DisplayName: "GPT-5 Codex", + Thinking: ®istry.ThinkingSupport{Levels: []string{"low", "medium", "high"}, ZeroAllowed: false, DynamicAllowed: false}, + }, + { + ID: "gpt-5.2-codex", + Object: "model", + Created: 1700000000, + OwnedBy: "github-copilot", + Type: "github-copilot", + DisplayName: "GPT-5.2 Codex", + Thinking: ®istry.ThinkingSupport{Levels: []string{"none", "low", "medium", "high", "xhigh"}, ZeroAllowed: true, DynamicAllowed: false}, + }, } } @@ -2831,6 +3108,15 @@ func runThinkingTests(t *testing.T, cases []thinkingTestCase) { translateTo = "openai" applyTo = "iflow" } + if tc.to == "github-copilot" { + if tc.from == "openai-response" { + translateTo = "codex" + applyTo = "codex" + } else { + translateTo = "openai" + applyTo = "openai" + } + } body := sdktranslator.TranslateRequest( sdktranslator.FromString(tc.from),