UNPKG

@adonis-agora/agent

Version:

Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.

75 lines • 4.05 kB
/** Seed (or refresh) a batch of model prices — one `upsertModelPrice` per row, in order. */ export async function seedModelPrices(store, prices) { for (const price of prices) { await store.upsertModelPrice(price); } } /** * Token-ledger estimate for one turn against a pricing row: the uncached input at the input rate, * cache-write/cache-read tokens at their own rates (falling back to the input rate when unpriced), * plus output at the output rate. Reasoning tokens are a subset of output and are billed at the * output rate, so they don't change the estimate. Cache token counts are subsets of `inputTokens`, * so the uncached remainder is the difference. Pure — the SQL and in-memory adapters share it, so * every surface reports identical numbers. */ export function estimateCost(usage, price) { const cacheWriteTokens = usage.cacheWriteTokens ?? 0; const cacheReadTokens = usage.cacheReadTokens ?? 0; const uncachedInputTokens = usage.inputTokens - cacheWriteTokens - cacheReadTokens; return ((uncachedInputTokens / 1_000_000) * price.inputPricePer1m + (cacheWriteTokens / 1_000_000) * (price.cacheWritePricePer1m ?? price.inputPricePer1m) + (cacheReadTokens / 1_000_000) * (price.cacheReadPricePer1m ?? price.inputPricePer1m) + (usage.outputTokens / 1_000_000) * price.outputPricePer1m); } /** * The forms of a model id we will accept a price under, most specific first. * * Exists because the ledger and the price table are filled by two different parties. The ledger gets * what the PROVIDER reports (`finalStep.response.modelId`), and OpenAI answers a request for * `gpt-4o-mini` with the dated snapshot `gpt-4o-mini-2024-07-18`. The price table gets what an * OPERATOR typed — and every example in these docs, and every published rate card, uses the alias. * Matched by raw equality, those two never meet: the fold misses and the dashboard prints `$0.00` * next to a real token count. * * Only DATE-shaped suffixes are stripped, and only from the end. That is the conservative line: a * date suffix is unambiguously a snapshot of the same model, whereas a trailing `-002` or `-v2` * could be a genuinely different model with a different price. Mispricing silently is worse than * not pricing — so anything we are not sure about stays unpriced and stays visible. * * The exact id is always tried FIRST, so an operator who deliberately prices one snapshot * differently from its alias keeps that. */ export function modelPriceCandidates(modelId) { const candidates = [modelId]; const add = (candidate) => { if (candidate.length > 0 && !candidates.includes(candidate)) candidates.push(candidate); }; // `openai/gpt-4o-mini`, `bedrock/us.anthropic.claude-…` — the route prefix is addressing, not // identity. Same shape the dashboard's `formatModelLabel` drops for display. const withoutRoute = modelId.includes('/') ? modelId.slice(modelId.lastIndexOf('/') + 1) : modelId; const withoutRegion = withoutRoute.replace(/^[a-z]{2}[a-z-]*\.[a-z0-9]+\./, ''); for (const base of [modelId, withoutRoute, withoutRegion]) { // OpenAI: `gpt-4o-mini-2024-07-18`. Anthropic: `claude-3-5-sonnet-20241022`. add(base.replace(/-\d{4}-\d{2}-\d{2}$/, '').replace(/-\d{8}$/, '')); } return candidates; } /** * The price to use for `modelId`, or `undefined` when the model is genuinely unpriced. * * `undefined` is the honest answer for "we don't know", and callers must keep it distinguishable * from zero: a run that cost nothing and a run nobody priced are different facts, and collapsing * them is what made a whole dashboard read `$0.00` while tokens were being burned. */ export function resolveModelPrice(pricing, modelId) { for (const candidate of modelPriceCandidates(modelId)) { const price = pricing.get(candidate); if (price !== undefined) return price; } return undefined; } //# sourceMappingURL=pricing-store.js.map