105 lines
2.7 KiB
TypeScript
105 lines
2.7 KiB
TypeScript
|
|
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<string>,
|
||
|
|
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<string>();
|
||
|
|
|
||
|
|
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;
|
||
|
|
}
|