From 6c93f01c97cbd282dc094b74128a8db2cf34cb32 Mon Sep 17 00:00:00 2001 From: zhaozewu Date: Tue, 21 Jul 2026 11:16:04 +0800 Subject: [PATCH] fix(groups): tighten reasoning policy Remove unsupported none handling, align the auth cache version with the current baseline, consolidate the unshipped migrations, and update the public API contract. --- backend/internal/server/api_contract_test.go | 2 ++ backend/internal/service/api_key_auth_cache_impl.go | 2 +- backend/internal/service/api_key_auth_cache_version_test.go | 6 +++--- backend/internal/service/openai_reasoning_effort_policy.go | 4 ---- .../internal/service/openai_reasoning_effort_policy_test.go | 4 +++- backend/migrations/185_group_max_reasoning_effort.sql | 3 --- backend/migrations/185_group_reasoning_effort_policy.sql | 4 ++++ backend/migrations/186_group_reasoning_effort_mappings.sql | 3 --- 8 files changed, 13 insertions(+), 15 deletions(-) delete mode 100644 backend/migrations/185_group_max_reasoning_effort.sql create mode 100644 backend/migrations/185_group_reasoning_effort_policy.sql delete mode 100644 backend/migrations/186_group_reasoning_effort_mappings.sql diff --git a/backend/internal/server/api_contract_test.go b/backend/internal/server/api_contract_test.go index bffce67cd6..99c7c42328 100644 --- a/backend/internal/server/api_contract_test.go +++ b/backend/internal/server/api_contract_test.go @@ -381,6 +381,8 @@ func TestAPIContracts(t *testing.T) { "fallback_group_id_on_invalid_request": null, "require_oauth_only": false, "require_privacy_set": false, + "max_reasoning_effort": "", + "reasoning_effort_mappings": null, "rpm_limit": 0, "created_at": "2025-01-02T03:04:05Z", "updated_at": "2025-01-02T03:04:05Z" diff --git a/backend/internal/service/api_key_auth_cache_impl.go b/backend/internal/service/api_key_auth_cache_impl.go index cab03fbc53..94c4276bf1 100644 --- a/backend/internal/service/api_key_auth_cache_impl.go +++ b/backend/internal/service/api_key_auth_cache_impl.go @@ -14,7 +14,7 @@ import ( "github.com/dgraph-io/ristretto" ) -const apiKeyAuthSnapshotVersion = 18 // v18: include group reasoning effort ceiling and mappings +const apiKeyAuthSnapshotVersion = 16 // v16: include group reasoning effort ceiling and mappings type apiKeyAuthCacheConfig struct { l1Size int diff --git a/backend/internal/service/api_key_auth_cache_version_test.go b/backend/internal/service/api_key_auth_cache_version_test.go index 98a37f37d1..20d778971d 100644 --- a/backend/internal/service/api_key_auth_cache_version_test.go +++ b/backend/internal/service/api_key_auth_cache_version_test.go @@ -42,18 +42,18 @@ func TestAPIKeyService_RejectsV10AuthSnapshotWithoutModelsListConfig(t *testing. } } -func TestAPIKeyService_RejectsV17AuthSnapshotWithoutReasoningEffortMappings(t *testing.T) { +func TestAPIKeyService_RejectsV15AuthSnapshotWithoutReasoningEffortPolicy(t *testing.T) { svc := &APIKeyService{} apiKey, ok, err := svc.applyAuthCacheEntry("k-legacy-reasoning-mappings", &APIKeyAuthCacheEntry{ - Snapshot: &APIKeyAuthSnapshot{Version: 17}, + Snapshot: &APIKeyAuthSnapshot{Version: 15}, }) if err != nil { t.Fatalf("expected stale snapshot to be ignored without error, got %v", err) } if ok { - t.Fatal("expected v17 auth snapshot to be rejected after reasoning effort mappings were added") + t.Fatal("expected v15 auth snapshot to be rejected after reasoning effort policy was added") } if apiKey != nil { t.Fatalf("expected no API key from stale snapshot, got %#v", apiKey) diff --git a/backend/internal/service/openai_reasoning_effort_policy.go b/backend/internal/service/openai_reasoning_effort_policy.go index 38441d5743..92048430d5 100644 --- a/backend/internal/service/openai_reasoning_effort_policy.go +++ b/backend/internal/service/openai_reasoning_effort_policy.go @@ -23,8 +23,6 @@ func NormalizeMaxReasoningEffort(raw string) string { switch value { case "": return "" - case "none": - return "none" case "minimal": return "minimal" case "low": @@ -75,8 +73,6 @@ func normalizeMaxReasoningEffortForPlatform(platform, raw string) (string, error func reasoningEffortRank(raw string) (int, bool) { switch NormalizeMaxReasoningEffort(raw) { - case "none": - return 0, true case "minimal": return 1, true case "low": diff --git a/backend/internal/service/openai_reasoning_effort_policy_test.go b/backend/internal/service/openai_reasoning_effort_policy_test.go index 41df07a3c3..a7ab7b2ad5 100644 --- a/backend/internal/service/openai_reasoning_effort_policy_test.go +++ b/backend/internal/service/openai_reasoning_effort_policy_test.go @@ -16,6 +16,7 @@ func TestNormalizeMaxReasoningEffort(t *testing.T) { {name: "empty", in: "", want: ""}, {name: "separator", in: "x-high", want: "xhigh"}, {name: "max is distinct", in: "max", want: "max"}, + {name: "none is unsupported", in: "none", want: ""}, {name: "invalid", in: "banana", want: ""}, } for _, tt := range tests { @@ -58,7 +59,7 @@ func TestNormalizeReasoningEffortMappings(t *testing.T) { } _, err := NormalizeReasoningEffortMappings(PlatformOpenAI, []ReasoningEffortMapping{{From: "none", To: "low"}}) - require.ErrorContains(t, err, "not supported for platform") + require.ErrorContains(t, err, "empty or unknown") _, err = NormalizeReasoningEffortMappings(PlatformOpenAI, []ReasoningEffortMapping{{From: "ultra", To: "high"}}) require.ErrorContains(t, err, "empty or unknown") @@ -96,6 +97,7 @@ func TestApplyOpenAIReasoningEffortPolicy(t *testing.T) { {name: "normalizes request alias", body: `{"reasoning_effort":"x-high"}`, max: "xhigh", path: "reasoning_effort", want: "xhigh", changed: true}, {name: "caps max below its distinct rank", body: `{"reasoning_effort":"max"}`, max: "xhigh", path: "reasoning_effort", want: "xhigh", changed: true}, {name: "keeps xhigh below max", body: `{"reasoning_effort":"xhigh"}`, max: "max", path: "reasoning_effort", want: "xhigh", changed: false}, + {name: "ignores stale none ceiling", body: `{"reasoning_effort":"high"}`, max: "none", path: "reasoning_effort", want: "high", changed: false}, {name: "caps both shapes", body: `{"reasoning":{"effort":"high"},"reasoning_effort":"xhigh"}`, max: "low", path: "reasoning.effort", want: "low", changed: true}, {name: "maps before cap", body: `{"reasoning":{"effort":"MAX"}}`, max: "medium", mappings: []ReasoningEffortMapping{{From: "max", To: "xhigh"}}, path: "reasoning.effort", want: "medium", changed: true}, {name: "does not chain mappings", body: `{"reasoning_effort":"max"}`, mappings: []ReasoningEffortMapping{{From: "max", To: "xhigh"}, {From: "xhigh", To: "low"}}, path: "reasoning_effort", want: "xhigh", changed: true}, diff --git a/backend/migrations/185_group_max_reasoning_effort.sql b/backend/migrations/185_group_max_reasoning_effort.sql deleted file mode 100644 index 5c7d35a41f..0000000000 --- a/backend/migrations/185_group_max_reasoning_effort.sql +++ /dev/null @@ -1,3 +0,0 @@ --- Add a per-group ceiling for OpenAI/Codex reasoning effort. -ALTER TABLE groups - ADD COLUMN IF NOT EXISTS max_reasoning_effort VARCHAR(20) NOT NULL DEFAULT ''; diff --git a/backend/migrations/185_group_reasoning_effort_policy.sql b/backend/migrations/185_group_reasoning_effort_policy.sql new file mode 100644 index 0000000000..1dd491e3cb --- /dev/null +++ b/backend/migrations/185_group_reasoning_effort_policy.sql @@ -0,0 +1,4 @@ +-- Add per-group controls for explicit OpenAI/Codex reasoning effort values. +ALTER TABLE groups + ADD COLUMN IF NOT EXISTS max_reasoning_effort VARCHAR(20) NOT NULL DEFAULT '', + ADD COLUMN IF NOT EXISTS reasoning_effort_mappings JSONB NOT NULL DEFAULT '[]'::jsonb; diff --git a/backend/migrations/186_group_reasoning_effort_mappings.sql b/backend/migrations/186_group_reasoning_effort_mappings.sql deleted file mode 100644 index d843785895..0000000000 --- a/backend/migrations/186_group_reasoning_effort_mappings.sql +++ /dev/null @@ -1,3 +0,0 @@ --- Add exact rewrites for explicit OpenAI/Codex reasoning effort values. -ALTER TABLE groups - ADD COLUMN IF NOT EXISTS reasoning_effort_mappings JSONB NOT NULL DEFAULT '[]'::jsonb;