mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-07-09 17:19:15 +00:00
Fix model pricing variant resolution
This commit is contained in:
parent
74b1f1e14d
commit
f2fa575a52
2 changed files with 81 additions and 0 deletions
|
|
@ -289,6 +289,8 @@ const BUILTIN_ALIASES: Record<string, string> = {
|
|||
'kimi-auto': 'kimi-k2-thinking',
|
||||
'kimi-code': 'kimi-k2-thinking',
|
||||
'kimi-for-coding': 'kimi-k2-thinking',
|
||||
'mimo-v2-flash': 'xiaomi/mimo-v2-flash',
|
||||
'kat-coder-pro-v1': 'kwaipilot/kat-coder-pro',
|
||||
// Cursor emits dot-version tier-last names plus tier/reasoning suffixes
|
||||
// that LiteLLM does not index (`-high`, `-low`, `-medium`, `-thinking`,
|
||||
// `-high-thinking`, `-fast-mode`). Missing aliases here surface as $0 in
|
||||
|
|
@ -575,6 +577,8 @@ export function getProxyPathsConfigHash(): string {
|
|||
function resolveAlias(model: string): string {
|
||||
if (Object.hasOwn(userAliases, model)) return userAliases[model]!
|
||||
if (Object.hasOwn(BUILTIN_ALIASES, model)) return BUILTIN_ALIASES[model]!
|
||||
const lowercase = model.toLowerCase()
|
||||
if (lowercase !== model && Object.hasOwn(BUILTIN_ALIASES, lowercase)) return BUILTIN_ALIASES[lowercase]!
|
||||
return model
|
||||
}
|
||||
function getCanonicalName(model: string): string {
|
||||
|
|
@ -584,6 +588,16 @@ function getCanonicalName(model: string): string {
|
|||
.replace(/^[^/]+\//, '') // strip provider prefix: anthropic/foo -> foo
|
||||
}
|
||||
|
||||
function stripKnownPricingVariantSuffix(model: string): string | null {
|
||||
const withoutColonSuffix = model.replace(/:(thinking|cloud)$/i, '')
|
||||
if (withoutColonSuffix !== model) return withoutColonSuffix
|
||||
|
||||
const withoutTeeSuffix = model.replace(/-TEE$/i, '')
|
||||
if (withoutTeeSuffix !== model) return withoutTeeSuffix
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export function getModelCosts(model: string): ModelCosts | null {
|
||||
// Try with provider prefix preserved (azure/gpt-5.4, openrouter/anthropic/claude-opus-4.6)
|
||||
const withPrefix = model.replace(/@.*$/, '').replace(/-\d{8}$/, '')
|
||||
|
|
@ -631,6 +645,18 @@ export function getModelCosts(model: string): ModelCosts | null {
|
|||
const byPrefix = lowerIndex.get(withPrefix.toLowerCase())
|
||||
if (byPrefix) return byPrefix
|
||||
|
||||
const withPrefixVariant = stripKnownPricingVariantSuffix(withPrefix)
|
||||
if (withPrefixVariant && withPrefixVariant !== withPrefix) {
|
||||
const variantCosts = getModelCosts(withPrefixVariant)
|
||||
if (variantCosts) return variantCosts
|
||||
}
|
||||
|
||||
const canonicalVariant = stripKnownPricingVariantSuffix(canonical)
|
||||
if (canonicalVariant && canonicalVariant !== canonical && canonicalVariant !== withPrefixVariant) {
|
||||
const variantCosts = getModelCosts(canonicalVariant)
|
||||
if (variantCosts) return variantCosts
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -620,3 +620,58 @@ describe('DeepSeek v4 models resolve to pricing', () => {
|
|||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('provider pricing suffix variants', () => {
|
||||
const cases: Array<[string, string]> = [
|
||||
['GLM-4.7-TEE', 'glm-4.7'],
|
||||
['glm-4.7:thinking', 'glm-4.7'],
|
||||
['Kimi-K2.5-TEE', 'kimi-k2.5'],
|
||||
['deepseek-v4-pro:cloud', 'deepseek-v4-pro'],
|
||||
['glm-5:thinking', 'glm-5'],
|
||||
['kimi-k2.6:thinking', 'kimi-k2.6'],
|
||||
['deepseek-v4-flash:thinking', 'deepseek-v4-flash'],
|
||||
['minimax-m3:cloud', 'minimax-m3'],
|
||||
]
|
||||
|
||||
for (const [input, expectedBase] of cases) {
|
||||
it(`${input} resolves through ${expectedBase}`, () => {
|
||||
const costs = getModelCosts(input)
|
||||
const expected = getModelCosts(expectedBase)
|
||||
expect(costs).not.toBeNull()
|
||||
expect(expected).not.toBeNull()
|
||||
expect(costs!.inputCostPerToken).toBe(expected!.inputCostPerToken)
|
||||
expect(costs!.outputCostPerToken).toBe(expected!.outputCostPerToken)
|
||||
})
|
||||
}
|
||||
|
||||
it('does not strip arbitrary local runtime tags', () => {
|
||||
expect(getModelCosts('qwen3.6:35b-a3b-bf16')).toBeNull()
|
||||
})
|
||||
|
||||
it('does not strip free-tier markers into paid pricing', () => {
|
||||
expect(getModelCosts('mimo-v2-flash:free')).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('observed provider model aliases', () => {
|
||||
const cases: Array<[string, string]> = [
|
||||
['MiMo-V2-Flash', 'xiaomi/mimo-v2-flash'],
|
||||
['KAT-Coder-Pro-V1', 'kwaipilot/kat-coder-pro'],
|
||||
]
|
||||
|
||||
for (const [input, expectedModel] of cases) {
|
||||
it(`${input} resolves through ${expectedModel}`, () => {
|
||||
const costs = getModelCosts(input)
|
||||
const expected = getModelCosts(expectedModel)
|
||||
expect(costs).not.toBeNull()
|
||||
expect(expected).not.toBeNull()
|
||||
expect(costs).toEqual(expected)
|
||||
expect(calculateCost(input, 1_000_000, 1_000_000, 0, 0, 0)).toBeGreaterThan(0)
|
||||
})
|
||||
}
|
||||
|
||||
it('does not map dated Qwen3 Max to a reseller price without provider context', () => {
|
||||
expect(getModelCosts('qwen3-max-2026-01-23')).toBeNull()
|
||||
expect(calculateCost('qwen3-max-2026-01-23', 1_000_000, 1_000_000, 0, 0, 0)).toBe(0)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue