fix(codex): gate tool search by model providers

This commit is contained in:
huahai0202
2026-07-13 19:22:58 +08:00
parent 6279bb8a4c
commit ceaeb75d53
2 changed files with 106 additions and 3 deletions
+37 -3
View File
@@ -13,6 +13,8 @@ type codexClientModelsPayload struct {
Models []map[string]any `json:"models"`
}
type codexClientModelProvidersFunc func(string) []string
var (
codexClientModelTemplatesMu sync.Mutex
codexClientModelTemplatesLoaded bool
@@ -33,16 +35,20 @@ var codexClientAllowedReasoningLevels = map[string]struct{}{
}
func (h *OpenAIAPIHandler) codexClientModelsResponse() map[string]any {
return CodexClientModelsResponse(h.Models())
return codexClientModelsResponse(h.Models(), registry.GetGlobalRegistry().GetModelProviders)
}
func CodexClientModelsResponse(models []map[string]any) map[string]any {
return codexClientModelsResponse(models, nil)
}
func codexClientModelsResponse(models []map[string]any, providersForModel codexClientModelProvidersFunc) map[string]any {
return map[string]any{
"models": buildCodexClientModels(models),
"models": buildCodexClientModels(models, providersForModel),
}
}
func buildCodexClientModels(models []map[string]any) []map[string]any {
func buildCodexClientModels(models []map[string]any, providersForModel codexClientModelProvidersFunc) []map[string]any {
templates, defaultTemplate, err := loadCodexClientModelTemplates()
if err != nil || defaultTemplate == nil {
return nil
@@ -58,6 +64,7 @@ func buildCodexClientModels(models []map[string]any) []map[string]any {
if template, ok := templates[id]; ok {
entry := cloneCodexClientModelMap(template)
applyCodexClientDisplayName(entry, model)
applyCodexClientSearchToolSupport(entry, id, true, providersForModel)
sanitizeCodexClientReasoningMetadata(entry)
applyCodexClientVisibilityOverride(entry, id)
result = append(result, entry)
@@ -66,6 +73,7 @@ func buildCodexClientModels(models []map[string]any) []map[string]any {
entry := cloneCodexClientModelMap(defaultTemplate)
applyCodexClientModelMetadata(entry, id, model)
applyCodexClientSearchToolSupport(entry, id, false, providersForModel)
sanitizeCodexClientReasoningMetadata(entry)
applyCodexClientVisibilityOverride(entry, id)
result = append(result, entry)
@@ -178,6 +186,32 @@ func applyCodexClientDisplayName(entry map[string]any, model map[string]any) {
}
}
func applyCodexClientSearchToolSupport(entry map[string]any, id string, templateModel bool, providersForModel codexClientModelProvidersFunc) {
supportsSearch, _ := entry["supports_search_tool"].(bool)
if !supportsSearch {
return
}
if providersForModel == nil {
if !templateModel {
entry["supports_search_tool"] = false
}
return
}
providers := providersForModel(id)
if len(providers) == 0 {
entry["supports_search_tool"] = false
return
}
for _, provider := range providers {
if !strings.EqualFold(strings.TrimSpace(provider), "codex") {
entry["supports_search_tool"] = false
return
}
}
}
func applyCodexClientModelMetadata(entry map[string]any, id string, model map[string]any) {
info := registry.LookupModelInfo(id)
@@ -143,6 +143,75 @@ func TestCodexClientModelsResponse_AppliesDisplayNameToTemplateModel(t *testing.
}
}
func TestCodexClientModelsResponse_DisablesSearchToolForSynthesizedModels(t *testing.T) {
resp := CodexClientModelsResponse([]map[string]any{
{"id": "custom-openai-compatible-model"},
{"id": "gpt-5.5"},
})
models, ok := resp["models"].([]map[string]any)
if !ok {
t.Fatalf("models type = %T, want []map[string]any", resp["models"])
}
bySlug := make(map[string]map[string]any, len(models))
for _, model := range models {
bySlug[stringModelValue(model, "slug")] = model
}
custom := bySlug["custom-openai-compatible-model"]
if custom == nil {
t.Fatal("expected synthesized custom model entry")
}
if got, ok := custom["supports_search_tool"].(bool); !ok || got {
t.Fatalf("custom supports_search_tool = %#v, want false", custom["supports_search_tool"])
}
official := bySlug["gpt-5.5"]
if official == nil {
t.Fatal("expected official template model entry")
}
if got, ok := official["supports_search_tool"].(bool); !ok || !got {
t.Fatalf("official supports_search_tool = %#v, want true", official["supports_search_tool"])
}
}
func TestCodexClientModelsResponse_UsesProviderSearchToolSupport(t *testing.T) {
providers := map[string][]string{
"new-codex-model": {"codex"},
"gpt-5.5": {"openai-compatible-deepseek"},
"gpt-5.4": {"codex", "xai"},
"gpt-5.6-sol": {"codex"},
}
resp := codexClientModelsResponse([]map[string]any{
{"id": "new-codex-model"},
{"id": "gpt-5.5"},
{"id": "gpt-5.4"},
{"id": "gpt-5.6-sol"},
}, func(id string) []string {
return providers[id]
})
models, ok := resp["models"].([]map[string]any)
if !ok {
t.Fatalf("models type = %T, want []map[string]any", resp["models"])
}
bySlug := make(map[string]map[string]any, len(models))
for _, model := range models {
bySlug[stringModelValue(model, "slug")] = model
}
for _, slug := range []string{"new-codex-model", "gpt-5.6-sol"} {
if got, ok := bySlug[slug]["supports_search_tool"].(bool); !ok || !got {
t.Errorf("%s supports_search_tool = %#v, want true", slug, bySlug[slug]["supports_search_tool"])
}
}
for _, slug := range []string{"gpt-5.5", "gpt-5.4"} {
if got, ok := bySlug[slug]["supports_search_tool"].(bool); !ok || got {
t.Errorf("%s supports_search_tool = %#v, want false", slug, bySlug[slug]["supports_search_tool"])
}
}
}
func TestCodexClientModelsResponse_PreservesUltraReasoningEffort(t *testing.T) {
resp := CodexClientModelsResponse([]map[string]any{{"id": "gpt-5.6-sol"}})
models, ok := resp["models"].([]map[string]any)