@sema-agent/core
Version:
Stateless, task-oriented AI agent core
57 lines (56 loc) • 3.12 kB
TypeScript
import type { Model } from "../../internal/llm.js";
import { type ModelPricing } from "../pricing.js";
/** How a provider counts cached tokens in its reported `input`. */
export type CacheFamily = "input-includes-cached" | "input-excludes-cached";
/**
* Resolve the prompt-cache accounting family for a model. **Auto-inferred from `model.api`** by
* default (Anthropic/Bedrock report `input` EXCLUDING cached tokens; OpenAI/vLLM/Google INCLUDE them),
* but a deployment can **override** it via `model.params.promptCacheFamily` when `model.api` doesn't
* match the brain that actually serves the model — e.g. a routing brain that dispatches by `provider`
* while `api` is left at a default. Accepts the explicit values or the aliases `"anthropic"`/`"openai"`.
* (a route-by-provider setup with `api` hardcoded misjudged the family → 1090% hit rate.)
*/
export declare function cacheFamilyOf(model: Model): CacheFamily;
/** Total prompt tokens presented this turn, normalized so `cachedTokens` is always ⊆ the total. */
export declare function promptTokensOf(family: CacheFamily, u: {
input: number;
cacheRead: number;
cacheWrite: number;
}): number;
/**
* The prompt tokens that were NOT served from (or written to) the cache this turn — i.e. the quantity
* the Anthropic protocol calls `usage.input_tokens`, normalized across families. Derived from the
* normalized total so the identity `total = uncached + cacheRead + cacheWrite` holds by construction.
*
* The wire faces named `inputTokens`/`promptTokens` carry THIS, not the total — a consumer
* computing `cacheRead / (input + cacheRead)` on a same-named protocol field otherwise double-counts
* the cache subset and reports `h/(1+h)` (a real 98% hit rate surfaces as 49.5%). The BILLING path
* still consumes the total (`promptTokensOf`) — `computeCostMicroUsd` subtracts the priced subsets
* itself, so handing it the uncached figure would drop the cache legs from the bill entirely.
*
* Clamped at 0: when a provider reports subsets LARGER than its own total the cache family is
* misdeclared, which the run loop already reports on its own face; here the identity
* degrades to `total ≤ uncached + cacheRead + cacheWrite` rather than going negative.
*/
export declare function uncachedInputTokensOf(family: CacheFamily, u: {
input: number;
cacheRead: number;
cacheWrite: number;
}): number;
/**
* Normalize one turn's reported usage into its `{ totalInputTokens, uncachedInputTokens, costMicroUsd }`
* — the authoritative cost re-priced in core from injected pricing (so cached tokens aren't charged at
* full price). Shared by the run loop's per-turn accounting and post-task memory consolidation
* so a pricing change lives in ONE place. We emit only 5-min-TTL cache writes, so
* `cacheWriteTokensLong` is 0.
*/
export declare function usageCostMicroUsd(family: CacheFamily, u: {
input?: number;
output?: number;
cacheRead?: number;
cacheWrite?: number;
}, pricing: ModelPricing): {
totalInputTokens: number;
uncachedInputTokens: number;
costMicroUsd: number;
};