import type { PublicModelPrice } from "../api/publicPricingClient"; import type { TextTokenCreditRate } from "./taskLifecycle"; const TOKENS_PER_MILLION = 1_000_000; const BACKEND_TOKEN_PRICE_UNIT = 1_000; const CREDITS_PER_CNY = 100; const MILLS_PER_CNY = 1_000; const CREDITS_PER_MILL = CREDITS_PER_CNY / MILLS_PER_CNY; function isUsablePrice(value: number | null | undefined): value is number { return typeof value === "number" && Number.isFinite(value) && value >= 0; } function normalizeModelKey(value: string): string { return value.trim().toLowerCase(); } function compactModelKey(value: string): string { return normalizeModelKey(value).replace(/[^a-z0-9]+/g, ""); } function addCandidate( candidates: PublicModelPrice[], seen: Set, price: PublicModelPrice, ): void { const key = normalizeModelKey(price.modelKey); if (seen.has(key)) return; seen.add(key); candidates.push(price); } export function millsPerThousandTokensToCreditsPerMillion( priceMills: number, ): number { if (!isUsablePrice(priceMills)) return 0; return ( priceMills * (TOKENS_PER_MILLION / BACKEND_TOKEN_PRICE_UNIT) * CREDITS_PER_MILL ); } export function modelPriceToTextTokenCreditRate( price: PublicModelPrice, ): TextTokenCreditRate | null { if ( !isUsablePrice(price.inputPriceMills) || !isUsablePrice(price.outputPriceMills) ) return null; return { inputCreditsPerMillion: millsPerThousandTokensToCreditsPerMillion( price.inputPriceMills, ), outputCreditsPerMillion: millsPerThousandTokensToCreditsPerMillion( price.outputPriceMills, ), source: "server", modelKey: price.modelKey, }; } export function resolveTextTokenCreditRate( prices: PublicModelPrice[], modelKey: string | null | undefined, ): TextTokenCreditRate | null { const normalizedTarget = normalizeModelKey(modelKey || ""); if (!normalizedTarget) return null; const compactTarget = compactModelKey(normalizedTarget); const candidates: PublicModelPrice[] = []; const seen = new Set(); for (const price of prices) { if (normalizeModelKey(price.modelKey) === normalizedTarget) { addCandidate(candidates, seen, price); } } for (const price of prices) { if (compactModelKey(price.modelKey) === compactTarget) { addCandidate(candidates, seen, price); } } for (const price of prices) { const compactPriceKey = compactModelKey(price.modelKey); if ( compactPriceKey.includes(compactTarget) || compactTarget.includes(compactPriceKey) ) { addCandidate(candidates, seen, price); } } for (const price of candidates) { const rate = modelPriceToTextTokenCreditRate(price); if (rate) return rate; } return null; }