fix FindModel: prioritize canonical providers to prevent reseller limit overrides
FindModel iterates providers in random map order, so when deepseek-v4-pro exists in both 'deepseek' (output=384000) and 'ollama-cloud' (output=1048576), it sometimes returned the wrong metadata. The proxy then injected max_tokens=1048576 into DeepSeek's API, which rejected it with 400 (valid range is [1, 393216]). Fix: define CanonicalProviders list (deepseek, openai, google, xai, etc.) and search them in priority order before falling back to all providers. Each of the four lookup strategies (exact key, metadata ID, reverse fuzzy, forward fuzzy) checks canonical providers first.
This commit is contained in:
+154
-22
@@ -2,6 +2,24 @@ package models
|
||||
|
||||
import "strings"
|
||||
|
||||
// CanonicalProviders lists the original model creators in priority order.
|
||||
// When a model name exists in multiple providers (e.g. deepseek-v4-pro in
|
||||
// deepseek, ollama-cloud, openrouter, etc.), these providers take precedence
|
||||
// so the proxy uses authoritative metadata (pricing, limits) rather than a
|
||||
// reseller's values.
|
||||
var CanonicalProviders = []string{
|
||||
"openai",
|
||||
"google",
|
||||
"deepseek",
|
||||
"xai",
|
||||
"moonshotai",
|
||||
"moonshotai-cn",
|
||||
"anthropic",
|
||||
"mistral",
|
||||
"cohere",
|
||||
"minimax",
|
||||
}
|
||||
|
||||
type ModelRegistry struct {
|
||||
Providers map[string]ProviderInfo `json:"-"`
|
||||
}
|
||||
@@ -39,40 +57,154 @@ type ModelModalities struct {
|
||||
Output []string `json:"output"`
|
||||
}
|
||||
|
||||
func (r *ModelRegistry) FindModel(modelID string) *ModelMetadata {
|
||||
// First try exact match in models map
|
||||
for _, provider := range r.Providers {
|
||||
if model, ok := provider.Models[modelID]; ok {
|
||||
return &model
|
||||
}
|
||||
}
|
||||
|
||||
// Try searching by ID in metadata
|
||||
for _, provider := range r.Providers {
|
||||
for _, model := range provider.Models {
|
||||
if model.ID == modelID {
|
||||
return &model
|
||||
// findInCanonical searches the canonical providers in order for an exact model
|
||||
// key match. Returns the metadata and true if found.
|
||||
func (r *ModelRegistry) findInCanonical(modelID string) (*ModelMetadata, bool) {
|
||||
for _, key := range CanonicalProviders {
|
||||
if p, ok := r.Providers[key]; ok {
|
||||
if m, ok := p.Models[modelID]; ok {
|
||||
return &m, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Try reverse fuzzy matching (e.g. 'gpt-5.4-mini' matching 'gpt-5.4-mini-2026-04-01')
|
||||
for _, provider := range r.Providers {
|
||||
for id, model := range provider.Models {
|
||||
// findInAll searches all providers (map iteration, random order) for an exact
|
||||
// model key match. Used as fallback when canonical search fails.
|
||||
func (r *ModelRegistry) findInAll(modelID string) (*ModelMetadata, bool) {
|
||||
for _, p := range r.Providers {
|
||||
if m, ok := p.Models[modelID]; ok {
|
||||
return &m, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// findInCanonicalByID searches canonical providers for a model whose metadata
|
||||
// ID field matches modelID.
|
||||
func (r *ModelRegistry) findInCanonicalByID(modelID string) (*ModelMetadata, bool) {
|
||||
for _, key := range CanonicalProviders {
|
||||
if p, ok := r.Providers[key]; ok {
|
||||
for _, m := range p.Models {
|
||||
if m.ID == modelID {
|
||||
return &m, true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// findInAllByID searches all providers for a model whose metadata ID field
|
||||
// matches modelID.
|
||||
func (r *ModelRegistry) findInAllByID(modelID string) (*ModelMetadata, bool) {
|
||||
for _, p := range r.Providers {
|
||||
for _, m := range p.Models {
|
||||
if m.ID == modelID {
|
||||
return &m, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// findCanonicalReverseFuzzy searches canonical providers for any model whose
|
||||
// key starts with modelID.
|
||||
func (r *ModelRegistry) findCanonicalReverseFuzzy(modelID string) (*ModelMetadata, bool) {
|
||||
for _, key := range CanonicalProviders {
|
||||
if p, ok := r.Providers[key]; ok {
|
||||
for id, m := range p.Models {
|
||||
if strings.HasPrefix(id, modelID) {
|
||||
return &m, true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// findAllReverseFuzzy searches all providers for any model whose key starts
|
||||
// with modelID.
|
||||
func (r *ModelRegistry) findAllReverseFuzzy(modelID string) (*ModelMetadata, bool) {
|
||||
for _, p := range r.Providers {
|
||||
for id, m := range p.Models {
|
||||
if strings.HasPrefix(id, modelID) {
|
||||
return &model
|
||||
return &m, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Try fuzzy matching (e.g. 'gpt-4o-2024-05-13' matching 'gpt-4o')
|
||||
for _, provider := range r.Providers {
|
||||
for id, model := range provider.Models {
|
||||
if strings.HasPrefix(modelID, id) {
|
||||
return &model
|
||||
// findCanonicalForwardFuzzy searches canonical providers for any model whose
|
||||
// key is a prefix of modelID.
|
||||
func (r *ModelRegistry) findCanonicalForwardFuzzy(modelID string) (*ModelMetadata, bool) {
|
||||
for _, key := range CanonicalProviders {
|
||||
if p, ok := r.Providers[key]; ok {
|
||||
for id, m := range p.Models {
|
||||
if strings.HasPrefix(modelID, id) {
|
||||
return &m, true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// findAllForwardFuzzy searches all providers for any model whose key is a
|
||||
// prefix of modelID.
|
||||
func (r *ModelRegistry) findAllForwardFuzzy(modelID string) (*ModelMetadata, bool) {
|
||||
for _, p := range r.Providers {
|
||||
for id, m := range p.Models {
|
||||
if strings.HasPrefix(modelID, id) {
|
||||
return &m, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// FindModel looks up model metadata by ID. It searches canonical providers
|
||||
// first at each strategy level (exact key, metadata ID, reverse fuzzy,
|
||||
// forward fuzzy) and falls back to all providers only when canonical search
|
||||
// yields no result. This prevents reseller entries (ollama-cloud, openrouter,
|
||||
// etc.) from overriding the original provider's authoritative pricing and
|
||||
// limits.
|
||||
func (r *ModelRegistry) FindModel(modelID string) *ModelMetadata {
|
||||
// 1. Exact key match — canonical first, then all
|
||||
if m, ok := r.findInCanonical(modelID); ok {
|
||||
return m
|
||||
}
|
||||
if m, ok := r.findInAll(modelID); ok {
|
||||
return m
|
||||
}
|
||||
|
||||
// 2. Match by metadata ID field — canonical first, then all
|
||||
if m, ok := r.findInCanonicalByID(modelID); ok {
|
||||
return m
|
||||
}
|
||||
if m, ok := r.findInAllByID(modelID); ok {
|
||||
return m
|
||||
}
|
||||
|
||||
// 3. Reverse fuzzy: model key starts with modelID
|
||||
// e.g. 'gpt-5.4-mini' matching 'gpt-5.4-mini-2026-04-01'
|
||||
if m, ok := r.findCanonicalReverseFuzzy(modelID); ok {
|
||||
return m
|
||||
}
|
||||
if m, ok := r.findAllReverseFuzzy(modelID); ok {
|
||||
return m
|
||||
}
|
||||
|
||||
// 4. Forward fuzzy: modelID starts with model key
|
||||
// e.g. 'gpt-4o-2024-05-13' matching 'gpt-4o'
|
||||
if m, ok := r.findCanonicalForwardFuzzy(modelID); ok {
|
||||
return m
|
||||
}
|
||||
if m, ok := r.findAllForwardFuzzy(modelID); ok {
|
||||
return m
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user