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:
@@ -263,7 +263,7 @@ func TestModelsWithClientVersionReturnsCodexCatalog(t *testing.T) {
|
|||||||
DisplayName: "Custom Codex Model",
|
DisplayName: "Custom Codex Model",
|
||||||
Description: "Custom model from registry",
|
Description: "Custom model from registry",
|
||||||
ContextLength: 123456,
|
ContextLength: 123456,
|
||||||
Thinking: ®istry.ThinkingSupport{Levels: []string{"low", "medium"}},
|
Thinking: ®istry.ThinkingSupport{Levels: []string{"none", "minimal", "low", "medium", "unsupported", "high", "xhigh"}},
|
||||||
},
|
},
|
||||||
{ID: "grok-imagine-image-quality", Object: "model", OwnedBy: "xai", Type: "openai"},
|
{ID: "grok-imagine-image-quality", Object: "model", OwnedBy: "xai", Type: "openai"},
|
||||||
{ID: "gpt-image-2", Object: "model", OwnedBy: "openai", 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 {
|
if got, _ := custom["context_window"].(float64); got != 123456 {
|
||||||
t.Fatalf("custom context_window = %v, want 123456", custom["context_window"])
|
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"] {
|
if custom["base_instructions"] != gpt55["base_instructions"] {
|
||||||
t.Fatal("expected custom model to use gpt-5.5 base_instructions fallback")
|
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) {
|
func TestDefaultRequestLoggerFactory_UsesResolvedLogDirectory(t *testing.T) {
|
||||||
t.Setenv("WRITABLE_PATH", "")
|
t.Setenv("WRITABLE_PATH", "")
|
||||||
t.Setenv("writable_path", "")
|
t.Setenv("writable_path", "")
|
||||||
|
|||||||
@@ -20,6 +20,14 @@ var (
|
|||||||
codexClientModelTemplatesErr error
|
codexClientModelTemplatesErr error
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var codexClientAllowedReasoningLevels = map[string]struct{}{
|
||||||
|
"none": {},
|
||||||
|
"low": {},
|
||||||
|
"medium": {},
|
||||||
|
"high": {},
|
||||||
|
"xhigh": {},
|
||||||
|
}
|
||||||
|
|
||||||
func (h *OpenAIAPIHandler) codexClientModelsResponse() map[string]any {
|
func (h *OpenAIAPIHandler) codexClientModelsResponse() map[string]any {
|
||||||
return CodexClientModelsResponse(h.Models())
|
return CodexClientModelsResponse(h.Models())
|
||||||
}
|
}
|
||||||
@@ -45,6 +53,7 @@ func buildCodexClientModels(models []map[string]any) []map[string]any {
|
|||||||
|
|
||||||
if template, ok := templates[id]; ok {
|
if template, ok := templates[id]; ok {
|
||||||
entry := cloneCodexClientModelMap(template)
|
entry := cloneCodexClientModelMap(template)
|
||||||
|
sanitizeCodexClientReasoningMetadata(entry)
|
||||||
applyCodexClientVisibilityOverride(entry, id)
|
applyCodexClientVisibilityOverride(entry, id)
|
||||||
result = append(result, entry)
|
result = append(result, entry)
|
||||||
continue
|
continue
|
||||||
@@ -52,6 +61,7 @@ func buildCodexClientModels(models []map[string]any) []map[string]any {
|
|||||||
|
|
||||||
entry := cloneCodexClientModelMap(defaultTemplate)
|
entry := cloneCodexClientModelMap(defaultTemplate)
|
||||||
applyCodexClientModelMetadata(entry, id, model)
|
applyCodexClientModelMetadata(entry, id, model)
|
||||||
|
sanitizeCodexClientReasoningMetadata(entry)
|
||||||
applyCodexClientVisibilityOverride(entry, id)
|
applyCodexClientVisibilityOverride(entry, id)
|
||||||
result = append(result, entry)
|
result = append(result, entry)
|
||||||
}
|
}
|
||||||
@@ -153,12 +163,16 @@ func applyCodexClientThinkingMetadata(entry map[string]any, thinking *registry.T
|
|||||||
|
|
||||||
levels := make([]any, 0, len(thinking.Levels))
|
levels := make([]any, 0, len(thinking.Levels))
|
||||||
defaultLevel := ""
|
defaultLevel := ""
|
||||||
|
firstLevel := ""
|
||||||
for _, rawLevel := range thinking.Levels {
|
for _, rawLevel := range thinking.Levels {
|
||||||
level := strings.ToLower(strings.TrimSpace(rawLevel))
|
level := normalizeCodexClientReasoningLevel(rawLevel)
|
||||||
if level == "" || level == "none" {
|
if level == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if defaultLevel == "" || level == "medium" {
|
if firstLevel == "" {
|
||||||
|
firstLevel = level
|
||||||
|
}
|
||||||
|
if (defaultLevel == "" && level != "none") || level == "medium" {
|
||||||
defaultLevel = level
|
defaultLevel = level
|
||||||
}
|
}
|
||||||
levels = append(levels, map[string]any{
|
levels = append(levels, map[string]any{
|
||||||
@@ -169,15 +183,64 @@ func applyCodexClientThinkingMetadata(entry map[string]any, thinking *registry.T
|
|||||||
if len(levels) == 0 {
|
if len(levels) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if defaultLevel == "" {
|
||||||
|
defaultLevel = firstLevel
|
||||||
|
}
|
||||||
|
|
||||||
entry["supported_reasoning_levels"] = levels
|
entry["supported_reasoning_levels"] = levels
|
||||||
entry["default_reasoning_level"] = defaultLevel
|
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 {
|
func codexClientReasoningDescription(level string) string {
|
||||||
switch level {
|
switch level {
|
||||||
case "minimal":
|
case "none":
|
||||||
return "Fastest responses with minimal reasoning"
|
return "No reasoning"
|
||||||
case "low":
|
case "low":
|
||||||
return "Fast responses with lighter reasoning"
|
return "Fast responses with lighter reasoning"
|
||||||
case "medium":
|
case "medium":
|
||||||
|
|||||||
Reference in New Issue
Block a user