Files
dev/third_party/pi/packages/ai/scripts/ai-gateway-pricing.ts
alexion 6081e3e325 chore(vendor): import pi 1.3.0
Imported as a linear vendor commit so forge rebases do not replay the upstream root tree.

git-subtree-dir: third_party/pi

git-subtree-split: 71023aec1546d6d677a65669c5665c0927ce0dd0
2026-10-15 17:58:48 -04:00

68 lines
2.3 KiB
TypeScript

import type { ModelCost, ModelCostRates, ModelCostTier } from "../src/types.ts";
/** A prompt-length bracket. `min` is inclusive and `max` exclusive, both in prompt tokens. */
export interface AiGatewayPriceTier {
cost?: string | number;
min?: number;
max?: number;
}
/** Vercel AI Gateway pricing in $/token. */
export interface AiGatewayPricing {
input?: string | number;
output?: string | number;
input_cache_read?: string | number;
input_cache_write?: string | number;
input_tiers?: AiGatewayPriceTier[];
output_tiers?: AiGatewayPriceTier[];
input_cache_read_tiers?: AiGatewayPriceTier[];
input_cache_write_tiers?: AiGatewayPriceTier[];
}
const RATE_FIELDS = [
{ rate: "input", tiers: "input_tiers" },
{ rate: "output", tiers: "output_tiers" },
{ rate: "cacheRead", tiers: "input_cache_read_tiers" },
{ rate: "cacheWrite", tiers: "input_cache_write_tiers" },
] as const;
function perMillion(value: string | number | undefined): number {
const parsed = typeof value === "number" ? value : parseFloat(value ?? "0");
return Number.isFinite(parsed) ? Number((parsed * 1_000_000).toFixed(6)) : 0;
}
/**
* Convert AI Gateway pricing to $/million tokens. Each rate lists its own prompt-length brackets;
* every bracket start above zero becomes a request-wide tier with the rates in effect there.
*/
export function getAiGatewayCost(pricing: AiGatewayPricing | undefined): ModelCost {
const base: ModelCostRates = {
input: perMillion(pricing?.input),
output: perMillion(pricing?.output),
cacheRead: perMillion(pricing?.input_cache_read),
cacheWrite: perMillion(pricing?.input_cache_write),
};
const starts = new Set<number>();
for (const field of RATE_FIELDS) {
for (const bracket of pricing?.[field.tiers] ?? []) {
if (bracket.min !== undefined && bracket.min > 0) starts.add(bracket.min);
}
}
const tiers = [...starts]
.sort((a, b) => a - b)
.map((start): ModelCostTier => {
const tier: ModelCostTier = { inputTokensAbove: start - 1, ...base };
for (const field of RATE_FIELDS) {
const bracket = pricing?.[field.tiers]?.find(
(candidate) => (candidate.min ?? 0) <= start && (candidate.max === undefined || start < candidate.max),
);
if (bracket?.cost !== undefined) tier[field.rate] = perMillion(bracket.cost);
}
return tier;
});
return tiers.length > 0 ? { ...base, tiers } : base;
}