mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-12 06:00:20 +08:00
feat(models): track online pricing sources and unsupported fields
This commit is contained in:
@@ -63,6 +63,36 @@ describe('buildModelsDevTieredPricing', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('allows special token dimensions only when they use the base token price', () => {
|
||||
expect(buildModelsDevTieredPricing({
|
||||
input: 1,
|
||||
output: 2,
|
||||
input_audio: 1,
|
||||
output_audio: 2,
|
||||
reasoning: 2,
|
||||
})).toEqual({
|
||||
tiers: [{ up_to: null, input_price_per_1m: 1, output_price_per_1m: 2 }],
|
||||
})
|
||||
})
|
||||
|
||||
it.each([
|
||||
{ input: 1, output: 2, reasoning: 4 },
|
||||
{ input: 1, output: 2, input_audio: 3 },
|
||||
{ input: 1, output: 2, output_audio: 5 },
|
||||
{
|
||||
input: 1,
|
||||
output: 2,
|
||||
tiers: [{
|
||||
input: 3,
|
||||
output: 4,
|
||||
input_audio: 9,
|
||||
tier: { type: 'context', size: 100_000 },
|
||||
}],
|
||||
},
|
||||
])('rejects pricing dimensions the billing engine cannot settle independently', (cost) => {
|
||||
expect(buildModelsDevTieredPricing(cost)).toBeNull()
|
||||
})
|
||||
|
||||
it('omits an empty base band when context pricing starts at zero', () => {
|
||||
expect(buildModelsDevTieredPricing({
|
||||
input: 1,
|
||||
|
||||
@@ -51,6 +51,11 @@ describe('getModelsDevList', () => {
|
||||
output: ['text'],
|
||||
cost: { input: 1, output: 2 },
|
||||
},
|
||||
'audio-priced': {
|
||||
id: 'audio-priced',
|
||||
name: 'Audio Priced',
|
||||
cost: { input: 1, output: 2, input_audio: 4 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -59,6 +64,7 @@ describe('getModelsDevList', () => {
|
||||
const models = await getModelsDevList()
|
||||
const current = models.find(model => model.modelId === 'gpt-test')
|
||||
const legacy = models.find(model => model.modelId === 'legacy')
|
||||
const audioPriced = models.find(model => model.modelId === 'audio-priced')
|
||||
|
||||
expect(current).toMatchObject({
|
||||
supportsVision: true,
|
||||
@@ -73,5 +79,11 @@ describe('getModelsDevList', () => {
|
||||
inputModalities: ['text', 'image'],
|
||||
outputModalities: ['text'],
|
||||
})
|
||||
expect(audioPriced).toMatchObject({
|
||||
inputPrice: 1,
|
||||
outputPrice: 2,
|
||||
pricingUnsupportedFields: ['input_audio'],
|
||||
})
|
||||
expect(audioPriced?.tieredPricing).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -21,6 +21,8 @@ export interface ModelsDevCost extends ModelsDevTokenCost {
|
||||
tiers?: ModelsDevCostTier[]
|
||||
}
|
||||
|
||||
export type ModelsDevUnsupportedPricingField = 'reasoning' | 'input_audio' | 'output_audio'
|
||||
|
||||
const TOKEN_PRICE_FIELDS = [
|
||||
'input_price_per_1m',
|
||||
'output_price_per_1m',
|
||||
@@ -38,6 +40,40 @@ function isPrice(value: unknown): value is number {
|
||||
return typeof value === 'number' && Number.isFinite(value) && value >= 0
|
||||
}
|
||||
|
||||
const SPECIAL_PRICE_BASE_FIELDS: Array<{
|
||||
field: ModelsDevUnsupportedPricingField
|
||||
baseField: 'input' | 'output'
|
||||
}> = [
|
||||
{ field: 'reasoning', baseField: 'output' },
|
||||
{ field: 'input_audio', baseField: 'input' },
|
||||
{ field: 'output_audio', baseField: 'output' },
|
||||
]
|
||||
|
||||
export function getModelsDevUnsupportedPricingFields(
|
||||
cost: unknown,
|
||||
): ModelsDevUnsupportedPricingField[] {
|
||||
if (!isRecord(cost)) return []
|
||||
const unsupportedFields = new Set<ModelsDevUnsupportedPricingField>()
|
||||
const inspectPrices = (prices: Record<string, unknown>) => {
|
||||
for (const { field, baseField } of SPECIAL_PRICE_BASE_FIELDS) {
|
||||
const specialPrice = prices[field]
|
||||
if (specialPrice === undefined) continue
|
||||
const basePrice = prices[baseField]
|
||||
if (!isPrice(specialPrice) || !isPrice(basePrice) || specialPrice !== basePrice) {
|
||||
unsupportedFields.add(field)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inspectPrices(cost)
|
||||
if (Array.isArray(cost.tiers)) {
|
||||
for (const tier of cost.tiers) {
|
||||
if (isRecord(tier)) inspectPrices(tier)
|
||||
}
|
||||
}
|
||||
return [...unsupportedFields]
|
||||
}
|
||||
|
||||
function parseTokenPrices(value: unknown): Omit<PricingTier, 'up_to'> | null {
|
||||
if (!isRecord(value) || !isPrice(value.input) || !isPrice(value.output)) return null
|
||||
if (value.cache_write !== undefined && !isPrice(value.cache_write)) return null
|
||||
@@ -70,6 +106,7 @@ function parseContextTier(value: unknown): { size: number; prices: Omit<PricingT
|
||||
}
|
||||
|
||||
export function buildModelsDevTieredPricing(cost: unknown): TieredPricingConfig | null {
|
||||
if (getModelsDevUnsupportedPricingFields(cost).length > 0) return null
|
||||
const basePrices = parseTokenPrices(cost)
|
||||
if (!basePrices || !isRecord(cost)) return null
|
||||
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
|
||||
import api from './client'
|
||||
import {
|
||||
getModelsDevUnsupportedPricingFields,
|
||||
resolveModelsDevTieredPricing,
|
||||
type ModelsDevCost,
|
||||
type ModelsDevUnsupportedPricingField,
|
||||
} from './models-dev-pricing'
|
||||
import type { TieredPricingConfig } from './endpoints/types'
|
||||
|
||||
@@ -78,6 +80,7 @@ export interface ModelsDevModelItem {
|
||||
inputPrice?: number
|
||||
outputPrice?: number
|
||||
tieredPricing?: TieredPricingConfig
|
||||
pricingUnsupportedFields?: ModelsDevUnsupportedPricingField[]
|
||||
contextLimit?: number
|
||||
outputLimit?: number
|
||||
supportsVision?: boolean
|
||||
@@ -187,6 +190,11 @@ export async function getModelsDevList(officialOnly: boolean = true): Promise<Mo
|
||||
model.cost,
|
||||
model.experimental?.modes,
|
||||
)
|
||||
const pricingUnsupportedFields = [...new Set([
|
||||
...getModelsDevUnsupportedPricingFields(model.cost),
|
||||
...Object.values(model.experimental?.modes ?? {})
|
||||
.flatMap(mode => getModelsDevUnsupportedPricingFields(mode.cost)),
|
||||
])]
|
||||
const basePricingTier = tieredPricing?.tiers[0]
|
||||
items.push({
|
||||
providerId,
|
||||
@@ -197,6 +205,9 @@ export async function getModelsDevList(officialOnly: boolean = true): Promise<Mo
|
||||
inputPrice: basePricingTier?.input_price_per_1m ?? model.cost?.input,
|
||||
outputPrice: basePricingTier?.output_price_per_1m ?? model.cost?.output,
|
||||
tieredPricing: tieredPricing ?? undefined,
|
||||
pricingUnsupportedFields: pricingUnsupportedFields.length > 0
|
||||
? pricingUnsupportedFields
|
||||
: undefined,
|
||||
contextLimit: model.limit?.context,
|
||||
outputLimit: model.limit?.output,
|
||||
supportsVision: inputModalities?.includes('image'),
|
||||
|
||||
Reference in New Issue
Block a user