feat(models): expand supported reasoning levels for Codex

- Added new reasoning levels: `none`, `minimal`, and `unsupported` to Codex model configurations.
- Introduced metadata sanitization and normalization for reasoning levels in API response.
- Extended unit tests to cover reasoning levels validation and metadata sanitation logic.
This commit is contained in:
Luis Pater
2026-05-20 03:21:46 +08:00
parent ea25949479
commit de0394917a
2 changed files with 91 additions and 6 deletions

View File

@@ -263,7 +263,7 @@ func TestModelsWithClientVersionReturnsCodexCatalog(t *testing.T) {
DisplayName: "Custom Codex Model",
Description: "Custom model from registry",
ContextLength: 123456,
Thinking: &registry.ThinkingSupport{Levels: []string{"low", "medium"}},
Thinking: &registry.ThinkingSupport{Levels: []string{"none", "minimal", "low", "medium", "unsupported", "high", "xhigh"}},
},
{ID: "grok-imagine-image-quality", Object: "model", OwnedBy: "xai", Type: "openai"},
{ID: "gpt-image-2", Object: "model", OwnedBy: "openai", Type: "openai"},
@@ -334,6 +334,7 @@ func TestModelsWithClientVersionReturnsCodexCatalog(t *testing.T) {
if got, _ := custom["context_window"].(float64); got != 123456 {
t.Fatalf("custom context_window = %v, want 123456", custom["context_window"])
}
assertCodexSupportedReasoningLevels(t, custom, []string{"none", "low", "medium", "high", "xhigh"})
if custom["base_instructions"] != gpt55["base_instructions"] {
t.Fatal("expected custom model to use gpt-5.5 base_instructions fallback")
}
@@ -376,6 +377,27 @@ func TestModelsWithClientVersionReturnsCodexCatalog(t *testing.T) {
}
}
func assertCodexSupportedReasoningLevels(t *testing.T, model map[string]any, want []string) {
t.Helper()
rawLevels, ok := model["supported_reasoning_levels"].([]any)
if !ok {
t.Fatalf("expected supported_reasoning_levels, got %#v", model["supported_reasoning_levels"])
}
if len(rawLevels) != len(want) {
t.Fatalf("supported_reasoning_levels length = %d, want %d: %#v", len(rawLevels), len(want), rawLevels)
}
for index, rawLevel := range rawLevels {
levelEntry, ok := rawLevel.(map[string]any)
if !ok {
t.Fatalf("supported_reasoning_levels[%d] = %#v, want object", index, rawLevel)
}
if got, _ := levelEntry["effort"].(string); got != want[index] {
t.Fatalf("supported_reasoning_levels[%d].effort = %q, want %q", index, got, want[index])
}
}
}
func TestDefaultRequestLoggerFactory_UsesResolvedLogDirectory(t *testing.T) {
t.Setenv("WRITABLE_PATH", "")
t.Setenv("writable_path", "")

View File

@@ -20,6 +20,14 @@ var (
codexClientModelTemplatesErr error
)
var codexClientAllowedReasoningLevels = map[string]struct{}{
"none": {},
"low": {},
"medium": {},
"high": {},
"xhigh": {},
}
func (h *OpenAIAPIHandler) codexClientModelsResponse() map[string]any {
return CodexClientModelsResponse(h.Models())
}
@@ -45,6 +53,7 @@ func buildCodexClientModels(models []map[string]any) []map[string]any {
if template, ok := templates[id]; ok {
entry := cloneCodexClientModelMap(template)
sanitizeCodexClientReasoningMetadata(entry)
applyCodexClientVisibilityOverride(entry, id)
result = append(result, entry)
continue
@@ -52,6 +61,7 @@ func buildCodexClientModels(models []map[string]any) []map[string]any {
entry := cloneCodexClientModelMap(defaultTemplate)
applyCodexClientModelMetadata(entry, id, model)
sanitizeCodexClientReasoningMetadata(entry)
applyCodexClientVisibilityOverride(entry, id)
result = append(result, entry)
}
@@ -153,12 +163,16 @@ func applyCodexClientThinkingMetadata(entry map[string]any, thinking *registry.T
levels := make([]any, 0, len(thinking.Levels))
defaultLevel := ""
firstLevel := ""
for _, rawLevel := range thinking.Levels {
level := strings.ToLower(strings.TrimSpace(rawLevel))
if level == "" || level == "none" {
level := normalizeCodexClientReasoningLevel(rawLevel)
if level == "" {
continue
}
if defaultLevel == "" || level == "medium" {
if firstLevel == "" {
firstLevel = level
}
if (defaultLevel == "" && level != "none") || level == "medium" {
defaultLevel = level
}
levels = append(levels, map[string]any{
@@ -169,15 +183,64 @@ func applyCodexClientThinkingMetadata(entry map[string]any, thinking *registry.T
if len(levels) == 0 {
return
}
if defaultLevel == "" {
defaultLevel = firstLevel
}
entry["supported_reasoning_levels"] = levels
entry["default_reasoning_level"] = defaultLevel
}
func sanitizeCodexClientReasoningMetadata(entry map[string]any) {
rawLevels, ok := entry["supported_reasoning_levels"].([]any)
if !ok {
return
}
levels := make([]any, 0, len(rawLevels))
allowedDefaults := make(map[string]struct{}, len(rawLevels))
for _, rawLevelEntry := range rawLevels {
levelEntry, ok := rawLevelEntry.(map[string]any)
if !ok {
continue
}
level := normalizeCodexClientReasoningLevel(stringModelValue(levelEntry, "effort"))
if level == "" {
continue
}
clonedEntry := cloneCodexClientModelMap(levelEntry)
clonedEntry["effort"] = level
levels = append(levels, clonedEntry)
allowedDefaults[level] = struct{}{}
}
if len(levels) == 0 {
delete(entry, "supported_reasoning_levels")
delete(entry, "default_reasoning_level")
return
}
defaultLevel := normalizeCodexClientReasoningLevel(stringModelValue(entry, "default_reasoning_level"))
if _, ok := allowedDefaults[defaultLevel]; !ok {
defaultLevel = stringModelValue(levels[0].(map[string]any), "effort")
}
entry["supported_reasoning_levels"] = levels
entry["default_reasoning_level"] = defaultLevel
}
func normalizeCodexClientReasoningLevel(rawLevel string) string {
level := strings.ToLower(strings.TrimSpace(rawLevel))
if _, ok := codexClientAllowedReasoningLevels[level]; !ok {
return ""
}
return level
}
func codexClientReasoningDescription(level string) string {
switch level {
case "minimal":
return "Fastest responses with minimal reasoning"
case "none":
return "No reasoning"
case "low":
return "Fast responses with lighter reasoning"
case "medium":