From 647e0db124b14f0a1d586a50f9ed674e1fc963a3 Mon Sep 17 00:00:00 2001 From: Anton Bogdanovich <27antonb@gmail.com> Date: Sun, 3 May 2026 20:04:08 -0700 Subject: [PATCH] support codex oauth thinking level --- pkg/providers/oauth/codex_provider.go | 22 +++++++++++- pkg/providers/oauth/codex_provider_test.go | 42 ++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/pkg/providers/oauth/codex_provider.go b/pkg/providers/oauth/codex_provider.go index 32ebcdb11..50205c70e 100644 --- a/pkg/providers/oauth/codex_provider.go +++ b/pkg/providers/oauth/codex_provider.go @@ -220,6 +220,10 @@ func (p *CodexProvider) SupportsNativeSearch() bool { return p.enableWebSearch } +func (p *CodexProvider) SupportsThinking() bool { + return true +} + func resolveCodexModel(model string) (string, string) { m := strings.ToLower(strings.TrimSpace(model)) if m == "" { @@ -274,7 +278,7 @@ func buildCodexParams( }, Store: openai.Opt(false), Reasoning: shared.ReasoningParam{ - Effort: shared.ReasoningEffortNone, + Effort: codexReasoningEffort(options["thinking_level"]), }, } @@ -299,6 +303,22 @@ func buildCodexParams( return params } +func codexReasoningEffort(raw any) shared.ReasoningEffort { + level, _ := raw.(string) + switch strings.ToLower(strings.TrimSpace(level)) { + case "low": + return shared.ReasoningEffortLow + case "medium", "adaptive": + return shared.ReasoningEffortMedium + case "high": + return shared.ReasoningEffortHigh + case "xhigh", "max": + return shared.ReasoningEffortXhigh + default: + return shared.ReasoningEffortNone + } +} + func CreateCodexTokenSource() func() (string, string, error) { return func() (string, string, error) { return auth.GetOpenAIToken() diff --git a/pkg/providers/oauth/codex_provider_test.go b/pkg/providers/oauth/codex_provider_test.go index 7b13f0365..2ed7aa997 100644 --- a/pkg/providers/oauth/codex_provider_test.go +++ b/pkg/providers/oauth/codex_provider_test.go @@ -10,6 +10,7 @@ import ( "github.com/openai/openai-go/v3" openaiopt "github.com/openai/openai-go/v3/option" "github.com/openai/openai-go/v3/responses" + "github.com/openai/openai-go/v3/shared" orc "github.com/sipeed/picoclaw/pkg/providers/openai_responses_common" ) @@ -34,6 +35,9 @@ func TestBuildCodexParams_BasicMessage(t *testing.T) { if params.MaxOutputTokens.Valid() { t.Fatalf("MaxOutputTokens should not be set for Codex backend") } + if params.Reasoning.Effort != shared.ReasoningEffortNone { + t.Fatalf("Reasoning.Effort = %q, want none", params.Reasoning.Effort) + } } func TestBuildCodexParams_SystemAsInstructions(t *testing.T) { @@ -50,6 +54,44 @@ func TestBuildCodexParams_SystemAsInstructions(t *testing.T) { } } +func TestBuildCodexParams_ThinkingLevel(t *testing.T) { + tests := []struct { + name string + level any + want shared.ReasoningEffort + }{ + {name: "default", level: nil, want: shared.ReasoningEffortNone}, + {name: "off", level: "off", want: shared.ReasoningEffortNone}, + {name: "low", level: "low", want: shared.ReasoningEffortLow}, + {name: "medium", level: "medium", want: shared.ReasoningEffortMedium}, + {name: "adaptive", level: "adaptive", want: shared.ReasoningEffortMedium}, + {name: "high", level: "high", want: shared.ReasoningEffortHigh}, + {name: "xhigh", level: "xhigh", want: shared.ReasoningEffortXhigh}, + {name: "max", level: "max", want: shared.ReasoningEffortXhigh}, + {name: "unknown", level: "banana", want: shared.ReasoningEffortNone}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + opts := map[string]any{} + if tt.level != nil { + opts["thinking_level"] = tt.level + } + params := buildCodexParams([]Message{{Role: "user", Content: "Hi"}}, nil, "gpt-5.4", opts, false) + if params.Reasoning.Effort != tt.want { + t.Fatalf("Reasoning.Effort = %q, want %q", params.Reasoning.Effort, tt.want) + } + }) + } +} + +func TestCodexProvider_SupportsThinking(t *testing.T) { + provider := NewCodexProvider("test-token", "acc-123") + if !provider.SupportsThinking() { + t.Fatal("CodexProvider should support thinking_level") + } +} + func TestBuildCodexParams_ToolCallConversation(t *testing.T) { messages := []Message{ {Role: "user", Content: "What's the weather?"},