888 lines · 25.7 KB · Go
RawDownload
// Package models builds model catalogs for official Codex clients.package models import (	"encoding/json"	"sort"	"strconv"	"strings"	"sync" 	"github.com/router-for-me/CLIProxyAPI/v7/internal/registry") type codexClientModelsPayload struct {	Models []map[string]any `json:"models"`} // ProvidersForModelFunc returns the providers registered for a model.type ProvidersForModelFunc func(string) []string // WebSearchCapabilityForModelFunc returns explicit conservative capability// metadata for an exact public model ID. nil means unknown.type WebSearchCapabilityForModelFunc func(string) *bool var (	codexClientModelTemplatesMu       sync.Mutex	codexClientModelTemplatesLoaded   bool	codexClientModelTemplatesRevision uint64	codexClientModelTemplates         map[string]map[string]any	codexClientDefaultTemplate        map[string]any	codexClientModelTemplatesErr      error) var codexClientAllowedReasoningLevels = map[string]struct{}{	"none":    {},	"minimal": {},	"low":     {},	"medium":  {},	"high":    {},	"xhigh":   {},	"max":     {},	"ultra":   {},} var codexClientLegacyAllowedReasoningLevels = map[string]struct{}{	"none":    {},	"minimal": {},	"low":     {},	"medium":  {},	"high":    {},	"xhigh":   {},} // BuildResponse builds a Codex client model response from available models.func BuildResponse(availableModels []map[string]any, providersForModel ProvidersForModelFunc, optimizeMultiAgentV2 bool) map[string]any {	return BuildResponseForClient(availableModels, providersForModel, optimizeMultiAgentV2, "")} // BuildResponseForClient builds a Codex client model response from available models// tailored for a specific client version.func BuildResponseForClient(availableModels []map[string]any, providersForModel ProvidersForModelFunc, optimizeMultiAgentV2 bool, clientVersion string) map[string]any {	return BuildResponseForClientWithCPACapabilities(availableModels, providersForModel, registry.GetGlobalRegistry().GetResponsesWebSearchCapability, optimizeMultiAgentV2, clientVersion)} // BuildResponseForClientWithCPACapabilities builds a client response while// allowing Home to supply capability metadata independent of the local registry.func BuildResponseForClientWithCPACapabilities(availableModels []map[string]any, providersForModel ProvidersForModelFunc, webSearchCapabilityForModel WebSearchCapabilityForModelFunc, optimizeMultiAgentV2 bool, clientVersion string) map[string]any {	return map[string]any{		"models": buildCodexClientModels(availableModels, providersForModel, webSearchCapabilityForModel, optimizeMultiAgentV2, clientVersion),	}} func buildCodexClientModels(models []map[string]any, providersForModel ProvidersForModelFunc, webSearchCapabilityForModel WebSearchCapabilityForModelFunc, optimizeMultiAgentV2 bool, clientVersion string) []map[string]any {	templates, defaultTemplate, err := loadCodexClientModelTemplates()	if err != nil || defaultTemplate == nil {		return nil	} 	result := make([]map[string]any, 0, len(models))	for _, model := range models {		id := strings.TrimSpace(stringModelValue(model, "id"))		if id == "" {			continue		} 		metadataID := codexClientMetadataModelID(id) 		if template, ok := templates[metadataID]; ok {			entry := cloneCodexClientModelMap(template)			entry["slug"] = id			info := registry.LookupModelInfo(id)			applyCodexClientModelCapabilities(entry, id, metadataID, info, providersForModel, clientVersion)			applyCodexClientDisplayName(entry, model)			applyCodexClientDescription(entry, model)			applyCodexClientBaseInstructions(entry, model)			applyCodexClientMaxContextLengthOverride(entry, model)			applyCodexClientMaxTokens(entry, model)			if thinkingSupport := codexClientThinkingSupport(model); thinkingSupport != nil {				applyCodexClientThinkingMetadata(entry, thinkingSupport, clientVersion)			}			applyCodexClientProviderCapabilities(entry, id, true, providersForModel)			applyCPAWebSearchCapability(entry, id, webSearchCapabilityForModel, clientVersion)			sanitizeCodexClientReasoningMetadata(entry, clientVersion)			applyCodexClientVisibilityOverride(entry, id)			if optimizeMultiAgentV2 {				entry["multi_agent_version"] = "v2"			}			result = append(result, entry)			continue		} 		entry := cloneCodexClientModelMap(defaultTemplate)		applyCodexClientModelMetadata(entry, id, model, optimizeMultiAgentV2, clientVersion)		applyCodexClientMaxTokens(entry, model)		applyCodexClientProviderCapabilities(entry, id, false, providersForModel)		applyCPAWebSearchCapability(entry, id, webSearchCapabilityForModel, clientVersion)		sanitizeCodexClientReasoningMetadata(entry, clientVersion)		applyCodexClientVisibilityOverride(entry, id)		result = append(result, entry)	} 	applyCodexClientNonTemplatePriorities(result, templates) 	sort.SliceStable(result, func(i, j int) bool {		return codexClientModelPriority(result[i]) < codexClientModelPriority(result[j])	}) 	return result} func maxCodexClientTemplatePriority(templates map[string]map[string]any) int {	maxPriority := 0	for _, template := range templates {		priority := codexClientModelPriority(template)		if priority > maxPriority {			maxPriority = priority		}	}	return maxPriority} func applyCodexClientNonTemplatePriorities(result []map[string]any, templates map[string]map[string]any) {	if len(result) == 0 {		return	} 	basePriority := maxCodexClientTemplatePriority(templates)	type nonTemplateEntry struct {		index       int		displayName string		slug        string	} 	pending := make([]nonTemplateEntry, 0)	for index, entry := range result {		slug := stringModelValue(entry, "slug")		if _, ok := templates[codexClientMetadataModelID(slug)]; ok {			continue		}		displayName := stringModelValue(entry, "display_name")		if displayName == "" {			displayName = slug		}		pending = append(pending, nonTemplateEntry{			index:       index,			displayName: displayName,			slug:        slug,		})	} 	sort.SliceStable(pending, func(i, j int) bool {		left := strings.ToLower(pending[i].displayName)		right := strings.ToLower(pending[j].displayName)		if left == right {			return pending[i].slug < pending[j].slug		}		return left < right	}) 	for rank, entry := range pending {		result[entry.index]["priority"] = basePriority + 100*(rank+1)	}} func loadCodexClientModelTemplates() (map[string]map[string]any, map[string]any, error) {	raw, revision := registry.GetCodexClientModelsSnapshot()	return loadCodexClientModelTemplatesSnapshot(raw, revision)} func loadCodexClientModelTemplatesSnapshot(raw []byte, revision uint64) (map[string]map[string]any, map[string]any, error) {	codexClientModelTemplatesMu.Lock()	defer codexClientModelTemplatesMu.Unlock()	if codexClientModelTemplatesLoaded && codexClientModelTemplatesRevision == revision {		return codexClientModelTemplates, codexClientDefaultTemplate, codexClientModelTemplatesErr	} 	var payload codexClientModelsPayload	err := json.Unmarshal(raw, &payload)	var templates map[string]map[string]any	var defaultTemplate map[string]any	if err == nil {		templates = make(map[string]map[string]any, len(payload.Models))		for _, model := range payload.Models {			slug := strings.TrimSpace(stringModelValue(model, "slug"))			if slug == "" {				continue			}			templates[slug] = cloneCodexClientModelMap(model)			if slug == "gpt-5.5" {				defaultTemplate = cloneCodexClientModelMap(model)			}		}	} 	codexClientModelTemplatesLoaded = true	codexClientModelTemplatesRevision = revision	codexClientModelTemplates = templates	codexClientDefaultTemplate = defaultTemplate	codexClientModelTemplatesErr = err	return codexClientModelTemplates, codexClientDefaultTemplate, codexClientModelTemplatesErr} func codexClientMetadataModelID(id string) string {	id = strings.TrimSpace(id)	if info := registry.LookupModelInfo(id); info != nil {		if metadataID := strings.TrimSpace(info.MetadataModelID); metadataID != "" {			return metadataID		}	}	if idx := strings.Index(id, "/"); idx != -1 {		base := strings.TrimSpace(id[idx+1:])		if info := registry.LookupModelInfo(base); info != nil && strings.TrimSpace(info.MetadataModelID) != "" {			return strings.TrimSpace(info.MetadataModelID)		}		return base	}	return id} func applyCodexClientDisplayName(entry map[string]any, model map[string]any) {	if displayName := stringModelValue(model, "display_name"); displayName != "" {		entry["display_name"] = displayName	}} func applyCodexClientDescription(entry map[string]any, model map[string]any) {	if description := stringModelValue(model, "description"); description != "" {		entry["description"] = description	}} func applyCodexClientBaseInstructions(entry map[string]any, model map[string]any) {	if baseInstructions := stringModelValue(model, "base_instructions"); baseInstructions != "" {		entry["base_instructions"] = baseInstructions	}} func applyCodexClientModelCapabilities(entry map[string]any, id, metadataID string, info *registry.ModelInfo, providersForModel ProvidersForModelFunc, clientVersion string) {	if info != nil && info.Type == registry.OpenAIImageModelType {		entry["visibility"] = "hide"		delete(entry, "input_modalities")		delete(entry, "supports_image_detail_original")		return	} 	var providers []string	if providersForModel != nil {		providers = providersForModel(id)		if len(providers) == 0 && strings.Contains(id, "/") {			idx := strings.Index(id, "/")			base := strings.TrimSpace(id[idx+1:])			providers = providersForModel(base)		}	} 	isAlias := metadataID != "" && !strings.EqualFold(id, metadataID) 	var constrainedModalities []string	hasModalitiesConstraint := false	for _, p := range providers {		pInfo := registry.LookupModelInfo(id, p)		if pInfo == nil && strings.Contains(id, "/") {			idx := strings.Index(id, "/")			pInfo = registry.LookupModelInfo(strings.TrimSpace(id[idx+1:]), p)		}		if pInfo == nil {			continue		}		isCodex := strings.EqualFold(strings.TrimSpace(p), "codex")		if (!isCodex && isAlias) || pInfo.ExplicitInputModalities {			mods := pInfo.SupportedInputModalities			if mods == nil {				mods = []string{}			}			if !hasModalitiesConstraint {				constrainedModalities = append([]string(nil), mods...)				hasModalitiesConstraint = true			} else {				constrainedModalities = intersectStringSlices(constrainedModalities, mods)			}		}	}	if !hasModalitiesConstraint && info != nil && info.ExplicitInputModalities {		mods := info.SupportedInputModalities		if mods == nil {			mods = []string{}		}		constrainedModalities = append([]string(nil), mods...)		hasModalitiesConstraint = true	}	if hasModalitiesConstraint {		codexModalities := filterCodexInputModalities(constrainedModalities)		entry["input_modalities"] = codexModalities		if hasImageModality(codexModalities) {			entry["supports_image_detail_original"] = true		} else {			delete(entry, "supports_image_detail_original")		}	} 	var constrainedThinking *registry.ThinkingSupport	hasThinkingConstraint := false	for _, p := range providers {		pInfo := registry.LookupModelInfo(id, p)		if pInfo == nil && strings.Contains(id, "/") {			idx := strings.Index(id, "/")			pInfo = registry.LookupModelInfo(strings.TrimSpace(id[idx+1:]), p)		}		if pInfo == nil {			continue		}		isCodex := strings.EqualFold(strings.TrimSpace(p), "codex")		if (!isCodex && isAlias) || pInfo.ExplicitThinking {			hasThinkingConstraint = true			pThinking := pInfo.Thinking			if pThinking == nil {				pThinking = &registry.ThinkingSupport{Levels: []string{}}			}			if constrainedThinking == nil {				constrainedThinking = pThinking			} else {				constrainedThinking = intersectThinkingSupport(constrainedThinking, pThinking)			}		}	}	if !hasThinkingConstraint && info != nil && info.ExplicitThinking {		pThinking := info.Thinking		if pThinking == nil {			pThinking = &registry.ThinkingSupport{Levels: []string{}}		}		constrainedThinking = pThinking		hasThinkingConstraint = true	}	if hasThinkingConstraint && constrainedThinking != nil {		applyCodexClientThinkingMetadata(entry, constrainedThinking, clientVersion)	}} func intersectThinkingSupport(a, b *registry.ThinkingSupport) *registry.ThinkingSupport {	if a == nil {		return b	}	if b == nil {		return a	}	levels := intersectStringSlices(a.Levels, b.Levels)	min := a.Min	if b.Min > min {		min = b.Min	}	max := a.Max	if b.Max > 0 && (max == 0 || b.Max < max) {		max = b.Max	}	return &registry.ThinkingSupport{		Min:            min,		Max:            max,		ZeroAllowed:    a.ZeroAllowed && b.ZeroAllowed,		DynamicAllowed: a.DynamicAllowed && b.DynamicAllowed,		Levels:         levels,	}} func intersectStringSlices(a, b []string) []string {	if len(a) == 0 || len(b) == 0 {		return []string{}	}	bMap := make(map[string]struct{}, len(b))	for _, item := range b {		bMap[strings.ToLower(strings.TrimSpace(item))] = struct{}{}	}	out := make([]string, 0, len(a))	seen := make(map[string]struct{}, len(a))	for _, item := range a {		key := strings.ToLower(strings.TrimSpace(item))		if _, inB := bMap[key]; inB {			if _, inSeen := seen[key]; !inSeen {				seen[key] = struct{}{}				out = append(out, item)			}		}	}	return out} func applyCodexClientMaxContextLengthOverride(entry map[string]any, model map[string]any) {	if maxContextLength := intModelValue(model, "max_context_length"); maxContextLength > 0 {		entry["context_window"] = maxContextLength		entry["max_context_window"] = maxContextLength	}} func applyCodexClientMaxTokens(entry map[string]any, model map[string]any) {	if maxCompletionTokens := intModelValue(model, "max_completion_tokens"); maxCompletionTokens > 0 {		entry["max_tokens"] = maxCompletionTokens	}} func applyCPAWebSearchCapability(entry map[string]any, id string, capabilityForModel WebSearchCapabilityForModelFunc, clientVersion string) {	// Templates must not supply runtime capability claims or leak CPA-only fields.	delete(entry, "cpa_capabilities")	if clientVersion != "cpa" || capabilityForModel == nil {		return	}	if supported := capabilityForModel(strings.TrimSpace(id)); supported != nil {		entry["cpa_capabilities"] = map[string]any{"web_search": *supported}	}} func applyCodexClientProviderCapabilities(entry map[string]any, id string, isTemplate bool, providersForModel ProvidersForModelFunc) {	if !isTemplate {		applyCodexClientSearchToolSupport(entry, id, false, providersForModel)		return	}	if providersForModel != nil && !isPureCodexProvider(id, providersForModel) {		entry["supports_search_tool"] = false		entry["prefer_websockets"] = false		delete(entry, "apply_patch_tool_type")		entry["service_tiers"] = []any{}		delete(entry, "upgrade")		delete(entry, "availability_nux")		return	}	applyCodexClientSearchToolSupport(entry, id, isTemplate, providersForModel)} func isPureCodexProvider(id string, providersForModel ProvidersForModelFunc) bool {	if providersForModel == nil {		return true	}	providers := providersForModel(id)	if len(providers) == 0 && strings.Contains(id, "/") {		idx := strings.Index(id, "/")		base := strings.TrimSpace(id[idx+1:])		providers = providersForModel(base)	}	if len(providers) == 0 {		return false	}	for _, provider := range providers {		if !strings.EqualFold(strings.TrimSpace(provider), "codex") {			return false		}	}	return true} func applyCodexClientSearchToolSupport(entry map[string]any, id string, templateModel bool, providersForModel ProvidersForModelFunc) {	supportsSearch, _ := entry["supports_search_tool"].(bool)	if !supportsSearch {		return	} 	if !templateModel {		entry["supports_search_tool"] = false		return	} 	if providersForModel == nil {		return	} 	providers := providersForModel(id)	if len(providers) == 0 && strings.Contains(id, "/") {		idx := strings.Index(id, "/")		base := strings.TrimSpace(id[idx+1:])		providers = providersForModel(base)	}	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		}	}} 

Lines 1–500 of 888.