floe-guard 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -0
- package/dist/index.cjs +982 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +177 -0
- package/dist/index.d.ts +177 -0
- package/dist/index.js +954 -0
- package/dist/index.js.map +1 -0
- package/package.json +54 -0
- package/src/cost_map.json +734 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/pricing.ts","../src/cost_map.json","../src/guard.ts","../src/middleware.ts"],"sourcesContent":["/**\n * floe-guard — a local budget guardrail for AI agents.\n *\n * Hard-stops your agent before its next LLM call when it would cross a USD spend\n * ceiling. This package is the Vercel AI SDK (TypeScript) adapter; the Python\n * package `floe-guard` (pip) carries the LiteLLM / CrewAI / LangChain adapters.\n */\n\nexport { BudgetGuard, type BudgetGuardOptions } from \"./guard.js\";\nexport {\n FloeGuardError,\n BudgetExceeded,\n UnpriceableModelError,\n} from \"./errors.js\";\nexport { budgetGuardMiddleware } from \"./middleware.js\";\nexport {\n type ManualPrice,\n type PricedModel,\n resolvePrice,\n priceTokens,\n} from \"./pricing.js\";\n\nexport * as pricing from \"./pricing.js\";\n","/**\n * Exceptions for floe-guard.\n *\n * Everything derives from {@link FloeGuardError} (the package-root base) so callers\n * can catch the whole family with a single `catch (e) { if (e instanceof FloeGuardError) ... }`.\n *\n * Mirrors `src/floe_guard/errors.py` in the Python package — message formats are\n * kept byte-for-byte identical so both adapters read the same.\n */\n\n/** Base class for every error raised by floe-guard. */\nexport class FloeGuardError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"FloeGuardError\";\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Thrown before an LLM call that would cross the configured spend ceiling.\n *\n * The guard throws this *instead of* letting the next call run, so a runaway loop\n * stops here rather than burning more money.\n */\nexport class BudgetExceeded extends FloeGuardError {\n readonly spentUsd: number;\n readonly limitUsd: number;\n\n constructor(spentUsd: number, limitUsd: number) {\n super(\n `BUDGET EXCEEDED — call blocked (spent $${spentUsd.toFixed(6)} of $${limitUsd.toFixed(6)} ceiling)`,\n );\n this.name = \"BudgetExceeded\";\n this.spentUsd = spentUsd;\n this.limitUsd = limitUsd;\n }\n}\n\n/**\n * Thrown when a model cannot be priced and the guard is fail-closed.\n *\n * We refuse rather than silently accrue $0 — \"we cannot cap what we cannot price\".\n * Pass a manual price (`priceOverrides` or `record(..., { price })`) to make the\n * model enforceable.\n */\nexport class UnpriceableModelError extends FloeGuardError {\n readonly model: string;\n\n constructor(model: string) {\n super(\n `Cannot price model '${model}': not in the bundled cost map and no ` +\n `manual price was given. The guard cannot enforce a budget on spend ` +\n `it cannot measure. Pass a price override to enable enforcement.`,\n );\n this.name = \"UnpriceableModelError\";\n this.model = model;\n }\n}\n","/**\n * Offline token pricing from a vendored LiteLLM cost map.\n *\n * Mirrors `src/floe_guard/pricing.py`: BOTH the input and output per-token prices\n * must be finite numbers, otherwise the model is treated as unpriceable. A\n * half-valid entry would silently undercharge — so we refuse it (fail-closed).\n *\n * No network. The cost map (`cost_map.json`) is a snapshot of LiteLLM's\n * `model_prices_and_context_window.json`; refresh it on a schedule or estimates\n * drift as vendors change prices.\n */\n\nimport costMapJson from \"./cost_map.json\";\n\n/** A user-supplied per-token price, in USD, for a model the map cannot price. */\nexport interface ManualPrice {\n inputCostPerToken: number;\n outputCostPerToken: number;\n}\n\n/** A resolved per-token price plus where it came from. */\nexport interface PricedModel {\n inputCostPerToken: number;\n outputCostPerToken: number;\n source: \"override\" | \"cost_map\";\n}\n\ninterface CostMapEntry {\n input_cost_per_token?: unknown;\n output_cost_per_token?: unknown;\n}\n\nconst COST_MAP = costMapJson as Record<string, CostMapEntry>;\n\n/** Strip an optional `provider/` prefix (LiteLLM convention, e.g. `openai/gpt-4o`). */\nfunction bareModel(model: string): string {\n const m = model.trim();\n const slash = m.lastIndexOf(\"/\");\n return slash === -1 ? m : m.slice(slash + 1);\n}\n\nfunction bothFinite(a: unknown, b: unknown): boolean {\n return (\n typeof a === \"number\" &&\n typeof b === \"number\" &&\n Number.isFinite(a) &&\n Number.isFinite(b)\n );\n}\n\n/**\n * Resolve a model to its per-token price, or `null` if it cannot be priced.\n *\n * Overrides win, then the bundled cost map (looked up by bare name, then the raw\n * field). Fail-closed: both prices must be finite, else `null`.\n */\nexport function resolvePrice(\n model: string,\n overrides?: Record<string, ManualPrice>,\n): PricedModel | null {\n const bare = bareModel(model);\n\n if (overrides) {\n const ov = overrides[bare] ?? overrides[model.trim()];\n if (ov !== undefined) {\n if (bothFinite(ov.inputCostPerToken, ov.outputCostPerToken)) {\n return {\n inputCostPerToken: ov.inputCostPerToken,\n outputCostPerToken: ov.outputCostPerToken,\n source: \"override\",\n };\n }\n return null;\n }\n }\n\n const entry = COST_MAP[bare] ?? COST_MAP[model.trim()];\n if (!entry) return null;\n\n const input = entry.input_cost_per_token;\n const output = entry.output_cost_per_token;\n if (!bothFinite(input, output)) return null;\n\n return {\n inputCostPerToken: input as number,\n outputCostPerToken: output as number,\n source: \"cost_map\",\n };\n}\n\n/** USD cost for token usage. Negative counts are clamped to zero. */\nexport function priceTokens(\n priced: PricedModel,\n promptTokens: number,\n completionTokens: number,\n): number {\n const p = Math.max(0, promptTokens);\n const c = Math.max(0, completionTokens);\n const cost = p * priced.inputCostPerToken + c * priced.outputCostPerToken;\n if (!Number.isFinite(cost)) {\n // Defense-in-depth: resolvePrice already guarantees finite rates.\n throw new Error(\"Non-finite LLM cost — pricing entry is invalid\");\n }\n return Math.max(0, cost);\n}\n","{\n \"chatgpt-4o-latest\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"claude-3-7-sonnet-20250219\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-3-haiku-20240307\": {\n \"input_cost_per_token\": 2.5e-7,\n \"output_cost_per_token\": 0.00000125,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-3-opus-20240229\": {\n \"input_cost_per_token\": 0.000015,\n \"output_cost_per_token\": 0.000075,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-4-opus-20250514\": {\n \"input_cost_per_token\": 0.000015,\n \"output_cost_per_token\": 0.000075,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-4-sonnet-20250514\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-haiku-4-5\": {\n \"input_cost_per_token\": 0.000001,\n \"output_cost_per_token\": 0.000005,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-haiku-4-5-20251001\": {\n \"input_cost_per_token\": 0.000001,\n \"output_cost_per_token\": 0.000005,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-1\": {\n \"input_cost_per_token\": 0.000015,\n \"output_cost_per_token\": 0.000075,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-1-20250805\": {\n \"input_cost_per_token\": 0.000015,\n \"output_cost_per_token\": 0.000075,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-20250514\": {\n \"input_cost_per_token\": 0.000015,\n \"output_cost_per_token\": 0.000075,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-5\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000025,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-5-20251101\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000025,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-6\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000025,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-6-20260205\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000025,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-7\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000025,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-7-20260416\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000025,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-opus-4-8\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000025,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-sonnet-4-20250514\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-sonnet-4-5\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-sonnet-4-5-20250929\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"claude-sonnet-4-6\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"anthropic\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-3.5-turbo\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-3.5-turbo-0125\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-3.5-turbo-0613\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-3.5-turbo-1106\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-4-0613\": {\n \"input_cost_per_token\": 0.00003,\n \"output_cost_per_token\": 0.00006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-4.1-2025-04-14\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000012,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-4.1-mini-2025-04-14\": {\n \"input_cost_per_token\": 8e-7,\n \"output_cost_per_token\": 0.0000032,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-4.1-nano-2025-04-14\": {\n \"input_cost_per_token\": 2e-7,\n \"output_cost_per_token\": 8e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-4o-2024-08-06\": {\n \"input_cost_per_token\": 0.00000375,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-4o-2024-11-20\": {\n \"input_cost_per_token\": 0.00000375,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:gpt-4o-mini-2024-07-18\": {\n \"input_cost_per_token\": 3e-7,\n \"output_cost_per_token\": 0.0000012,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"ft:o4-mini-2025-04-16\": {\n \"input_cost_per_token\": 0.000004,\n \"output_cost_per_token\": 0.000016,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-3.5-turbo\": {\n \"input_cost_per_token\": 5e-7,\n \"output_cost_per_token\": 0.0000015,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-3.5-turbo-0125\": {\n \"input_cost_per_token\": 5e-7,\n \"output_cost_per_token\": 0.0000015,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-3.5-turbo-1106\": {\n \"input_cost_per_token\": 0.000001,\n \"output_cost_per_token\": 0.000002,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-3.5-turbo-16k\": {\n \"input_cost_per_token\": 0.000003,\n \"output_cost_per_token\": 0.000004,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4\": {\n \"input_cost_per_token\": 0.00003,\n \"output_cost_per_token\": 0.00006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4-0125-preview\": {\n \"input_cost_per_token\": 0.00001,\n \"output_cost_per_token\": 0.00003,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4-0314\": {\n \"input_cost_per_token\": 0.00003,\n \"output_cost_per_token\": 0.00006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4-0613\": {\n \"input_cost_per_token\": 0.00003,\n \"output_cost_per_token\": 0.00006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4-1106-preview\": {\n \"input_cost_per_token\": 0.00001,\n \"output_cost_per_token\": 0.00003,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4-turbo\": {\n \"input_cost_per_token\": 0.00001,\n \"output_cost_per_token\": 0.00003,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4-turbo-2024-04-09\": {\n \"input_cost_per_token\": 0.00001,\n \"output_cost_per_token\": 0.00003,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4-turbo-preview\": {\n \"input_cost_per_token\": 0.00001,\n \"output_cost_per_token\": 0.00003,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4.1\": {\n \"input_cost_per_token\": 0.000002,\n \"output_cost_per_token\": 0.000008,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4.1-2025-04-14\": {\n \"input_cost_per_token\": 0.000002,\n \"output_cost_per_token\": 0.000008,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4.1-mini\": {\n \"input_cost_per_token\": 4e-7,\n \"output_cost_per_token\": 0.0000016,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4.1-mini-2025-04-14\": {\n \"input_cost_per_token\": 4e-7,\n \"output_cost_per_token\": 0.0000016,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4.1-nano\": {\n \"input_cost_per_token\": 1e-7,\n \"output_cost_per_token\": 4e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4.1-nano-2025-04-14\": {\n \"input_cost_per_token\": 1e-7,\n \"output_cost_per_token\": 4e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-2024-05-13\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-2024-08-06\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-2024-11-20\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-audio-preview\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-audio-preview-2024-12-17\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-audio-preview-2025-06-03\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-mini\": {\n \"input_cost_per_token\": 1.5e-7,\n \"output_cost_per_token\": 6e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-mini-2024-07-18\": {\n \"input_cost_per_token\": 1.5e-7,\n \"output_cost_per_token\": 6e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-mini-audio-preview\": {\n \"input_cost_per_token\": 1.5e-7,\n \"output_cost_per_token\": 6e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-mini-audio-preview-2024-12-17\": {\n \"input_cost_per_token\": 1.5e-7,\n \"output_cost_per_token\": 6e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-mini-realtime-preview\": {\n \"input_cost_per_token\": 6e-7,\n \"output_cost_per_token\": 0.0000024,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-mini-realtime-preview-2024-12-17\": {\n \"input_cost_per_token\": 6e-7,\n \"output_cost_per_token\": 0.0000024,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-mini-search-preview\": {\n \"input_cost_per_token\": 1.5e-7,\n \"output_cost_per_token\": 6e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-mini-search-preview-2025-03-11\": {\n \"input_cost_per_token\": 1.5e-7,\n \"output_cost_per_token\": 6e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-realtime-preview\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.00002,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-realtime-preview-2024-12-17\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.00002,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-realtime-preview-2025-06-03\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.00002,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-search-preview\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-4o-search-preview-2025-03-11\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5-2025-08-07\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5-chat\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5-chat-latest\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5-mini\": {\n \"input_cost_per_token\": 2.5e-7,\n \"output_cost_per_token\": 0.000002,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5-mini-2025-08-07\": {\n \"input_cost_per_token\": 2.5e-7,\n \"output_cost_per_token\": 0.000002,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5-nano\": {\n \"input_cost_per_token\": 5e-8,\n \"output_cost_per_token\": 4e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5-nano-2025-08-07\": {\n \"input_cost_per_token\": 5e-8,\n \"output_cost_per_token\": 4e-7,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5-search-api\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5-search-api-2025-10-14\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.1\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.1-2025-11-13\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.1-chat-latest\": {\n \"input_cost_per_token\": 0.00000125,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.2\": {\n \"input_cost_per_token\": 0.00000175,\n \"output_cost_per_token\": 0.000014,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.2-2025-12-11\": {\n \"input_cost_per_token\": 0.00000175,\n \"output_cost_per_token\": 0.000014,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.2-chat-latest\": {\n \"input_cost_per_token\": 0.00000175,\n \"output_cost_per_token\": 0.000014,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.3-chat-latest\": {\n \"input_cost_per_token\": 0.00000175,\n \"output_cost_per_token\": 0.000014,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.4\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.4-2026-03-05\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.000015,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.4-mini\": {\n \"input_cost_per_token\": 7.5e-7,\n \"output_cost_per_token\": 0.0000045,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.4-mini-2026-03-17\": {\n \"input_cost_per_token\": 7.5e-7,\n \"output_cost_per_token\": 0.0000045,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.4-nano\": {\n \"input_cost_per_token\": 2e-7,\n \"output_cost_per_token\": 0.00000125,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.4-nano-2026-03-17\": {\n \"input_cost_per_token\": 2e-7,\n \"output_cost_per_token\": 0.00000125,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.5\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.00003,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-5.5-2026-04-23\": {\n \"input_cost_per_token\": 0.000005,\n \"output_cost_per_token\": 0.00003,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-audio\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-audio-1.5\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-audio-2025-08-28\": {\n \"input_cost_per_token\": 0.0000025,\n \"output_cost_per_token\": 0.00001,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-audio-mini\": {\n \"input_cost_per_token\": 6e-7,\n \"output_cost_per_token\": 0.0000024,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-audio-mini-2025-10-06\": {\n \"input_cost_per_token\": 6e-7,\n \"output_cost_per_token\": 0.0000024,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-audio-mini-2025-12-15\": {\n \"input_cost_per_token\": 6e-7,\n \"output_cost_per_token\": 0.0000024,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-realtime\": {\n \"input_cost_per_token\": 0.000004,\n \"output_cost_per_token\": 0.000016,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-realtime-1.5\": {\n \"input_cost_per_token\": 0.000004,\n \"output_cost_per_token\": 0.000016,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-realtime-2\": {\n \"input_cost_per_token\": 0.000004,\n \"output_cost_per_token\": 0.000016,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-realtime-2025-08-28\": {\n \"input_cost_per_token\": 0.000004,\n \"output_cost_per_token\": 0.000016,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-realtime-mini\": {\n \"input_cost_per_token\": 6e-7,\n \"output_cost_per_token\": 0.0000024,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-realtime-mini-2025-10-06\": {\n \"input_cost_per_token\": 6e-7,\n \"output_cost_per_token\": 0.0000024,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"gpt-realtime-mini-2025-12-15\": {\n \"input_cost_per_token\": 6e-7,\n \"output_cost_per_token\": 0.0000024,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"o1\": {\n \"input_cost_per_token\": 0.000015,\n \"output_cost_per_token\": 0.00006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"o1-2024-12-17\": {\n \"input_cost_per_token\": 0.000015,\n \"output_cost_per_token\": 0.00006,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"o3\": {\n \"input_cost_per_token\": 0.000002,\n \"output_cost_per_token\": 0.000008,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"o3-2025-04-16\": {\n \"input_cost_per_token\": 0.000002,\n \"output_cost_per_token\": 0.000008,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"o3-mini\": {\n \"input_cost_per_token\": 0.0000011,\n \"output_cost_per_token\": 0.0000044,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"o3-mini-2025-01-31\": {\n \"input_cost_per_token\": 0.0000011,\n \"output_cost_per_token\": 0.0000044,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"o4-mini\": {\n \"input_cost_per_token\": 0.0000011,\n \"output_cost_per_token\": 0.0000044,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"o4-mini-2025-04-16\": {\n \"input_cost_per_token\": 0.0000011,\n \"output_cost_per_token\": 0.0000044,\n \"litellm_provider\": \"openai\",\n \"mode\": \"chat\"\n },\n \"text-embedding-3-large\": {\n \"input_cost_per_token\": 1.3e-7,\n \"output_cost_per_token\": 0,\n \"litellm_provider\": \"openai\",\n \"mode\": \"embedding\"\n },\n \"text-embedding-3-small\": {\n \"input_cost_per_token\": 2e-8,\n \"output_cost_per_token\": 0,\n \"litellm_provider\": \"openai\",\n \"mode\": \"embedding\"\n },\n \"text-embedding-ada-002\": {\n \"input_cost_per_token\": 1e-7,\n \"output_cost_per_token\": 0,\n \"litellm_provider\": \"openai\",\n \"mode\": \"embedding\"\n },\n \"text-embedding-ada-002-v2\": {\n \"input_cost_per_token\": 1e-7,\n \"output_cost_per_token\": 0,\n \"litellm_provider\": \"openai\",\n \"mode\": \"embedding\"\n }\n}\n","/**\n * The local, in-process budget guard.\n *\n * `BudgetGuard` is a kill-switch that lives in the LLM call path. The contract:\n *\n * 1. Call {@link BudgetGuard.check} BEFORE every LLM call. If the *next* call\n * would cross the ceiling, it throws {@link BudgetExceeded} and the call never\n * runs.\n * 2. Call {@link BudgetGuard.record} AFTER every response, with the token usage.\n * It prices the tokens offline and accrues the USD into a running total.\n *\n * This is a faithful port of `src/floe_guard/guard.py` — same prediction logic,\n * same epsilon handling, same fail-closed default.\n */\n\nimport { BudgetExceeded, UnpriceableModelError } from \"./errors.js\";\nimport {\n type ManualPrice,\n priceTokens,\n resolvePrice,\n} from \"./pricing.js\";\n\n/** Tolerance for float rounding in the running spend total (well below $0.000001). */\nconst EPS = 1e-12;\n\nexport interface BudgetGuardOptions {\n /** Per-model manual prices for models the bundled cost map cannot price. */\n priceOverrides?: Record<string, ManualPrice>;\n /**\n * When `true` (default), recording an unpriceable model without a manual price\n * warns loudly AND throws {@link UnpriceableModelError}. When `false`, it warns\n * and skips accrual (you have opted into un-enforced spend for that model).\n */\n failClosed?: boolean;\n /**\n * Optional callback invoked with `(spentUsd, limitUsd)` right before\n * {@link BudgetExceeded} is thrown. Defaults to printing the\n * `BUDGET EXCEEDED — call blocked` banner to stderr.\n */\n onBlock?: (spentUsd: number, limitUsd: number) => void;\n}\n\nexport class BudgetGuard {\n readonly limitUsd: number;\n spentUsd = 0;\n priceOverrides?: Record<string, ManualPrice>;\n failClosed: boolean;\n\n private readonly onBlock: (spentUsd: number, limitUsd: number) => void;\n /** Cost of the most recent priced call, used to predict the next one. */\n private lastCost = 0;\n\n /**\n * @param limitUsd the spend ceiling, in USD. `0` blocks the very first call.\n */\n constructor(limitUsd: number, options: BudgetGuardOptions = {}) {\n if (!Number.isFinite(limitUsd) || limitUsd < 0) {\n // NaN/Infinity would make every check() comparison fail-open, silently\n // disabling the guard — reject them up front.\n throw new RangeError(\n `limitUsd must be a finite, non-negative number, got ${limitUsd}`,\n );\n }\n this.limitUsd = limitUsd;\n this.priceOverrides = options.priceOverrides;\n this.failClosed = options.failClosed ?? true;\n this.onBlock = options.onBlock ?? defaultOnBlock;\n }\n\n /**\n * Throw {@link BudgetExceeded} if the next call would cross the ceiling.\n *\n * Call this immediately before each LLM request. The \"next call\" is estimated\n * from the last recorded call's cost (override with `estimatedNextCost`); the\n * first call is always allowed unless the ceiling is already met. A check on\n * the running total catches an overshoot if the estimate was too low.\n */\n check(estimatedNextCost?: number): void {\n const estimate =\n estimatedNextCost === undefined\n ? this.lastCost\n : Math.max(0, estimatedNextCost);\n const projected = this.spentUsd + estimate;\n // Compare with an epsilon so float rounding in the running total doesn't\n // block a call early or let one slip past the ceiling.\n if (\n this.spentUsd > this.limitUsd - EPS ||\n projected > this.limitUsd + EPS\n ) {\n this.onBlock(this.spentUsd, this.limitUsd);\n throw new BudgetExceeded(this.spentUsd, this.limitUsd);\n }\n }\n\n /**\n * Price one response's tokens offline and add the cost to the total.\n *\n * Returns the USD cost of this call. If the model is unpriceable and no `price`\n * is given, behaviour depends on `failClosed`: warn + throw (default), or\n * warn + skip accrual.\n */\n record(\n model: string,\n promptTokens: number,\n completionTokens: number,\n options: { price?: ManualPrice } = {},\n ): number {\n let overrides = this.priceOverrides;\n if (options.price !== undefined) {\n overrides = { ...(overrides ?? {}), [model]: options.price };\n }\n\n const priced = resolvePrice(model, overrides);\n if (priced === null) {\n console.warn(\n `Cannot price model '${model}': not in the bundled cost map and no ` +\n `manual price given. The budget guard cannot enforce a ceiling on ` +\n `spend it cannot measure — pass { price } or set it in priceOverrides.`,\n );\n if (this.failClosed) {\n throw new UnpriceableModelError(model);\n }\n return 0;\n }\n\n const cost = priceTokens(priced, promptTokens, completionTokens);\n this.spentUsd += cost;\n // Clamp a sub-epsilon float overshoot back to the limit so the running total\n // never reports as having crossed the ceiling by a rounding artifact.\n if (this.spentUsd - this.limitUsd > 0 && this.spentUsd - this.limitUsd < EPS) {\n this.spentUsd = this.limitUsd;\n }\n this.lastCost = cost;\n return cost;\n }\n\n /** USD left before the ceiling (never negative). */\n get remainingUsd(): number {\n return Math.max(0, this.limitUsd - this.spentUsd);\n }\n}\n\nfunction defaultOnBlock(spentUsd: number, limitUsd: number): void {\n console.error(\n \"BUDGET EXCEEDED — call blocked\\n\" +\n ` spent so far: $${spentUsd.toFixed(6)} | ceiling: $${limitUsd.toFixed(6)}\\n` +\n \" The next call would cross your budget; floe-guard stopped your agent \" +\n \"before it ran.\",\n );\n}\n","/**\n * Vercel AI SDK middleware that enforces a {@link BudgetGuard} in the call path.\n *\n * This is the TypeScript counterpart to the Python framework adapters. The AI SDK\n * is TypeScript-only, so it ships as its own npm package.\n *\n * Verified against `ai@4.3.19` (`LanguageModelV1Middleware`):\n * - `wrapGenerate({ doGenerate, model })` — we `check()` (throws to hard-stop)\n * BEFORE calling `doGenerate()`, then `record()` from `result.usage`.\n * - `wrapStream({ doStream, model })` — we `check()` BEFORE `doStream()`, then\n * read `usage` from the `finish` part as the stream drains.\n *\n * The model id used for pricing comes from `model.modelId`.\n */\n\nimport type { LanguageModelV1Middleware } from \"ai\";\n\nimport type { BudgetGuard } from \"./guard.js\";\n\n// The AI SDK does not re-export `LanguageModelV1StreamPart` from the \"ai\" entry\n// point, so we derive the stream element type from the middleware's own return\n// type. This keeps us fully typed without importing a transitive package.\ntype WrapStreamResult = Awaited<\n ReturnType<NonNullable<LanguageModelV1Middleware[\"wrapStream\"]>>\n>;\ntype StreamPart =\n WrapStreamResult[\"stream\"] extends ReadableStream<infer P> ? P : never;\n\n/**\n * Build a `LanguageModelV1Middleware` that hard-stops the model before a call\n * crosses the guard's USD ceiling, and records priced token usage after.\n *\n * @example\n * import { wrapLanguageModel } from \"ai\";\n * import { openai } from \"@ai-sdk/openai\";\n * import { BudgetGuard, budgetGuardMiddleware } from \"floe-guard\";\n *\n * const guard = new BudgetGuard(5.00);\n * const model = wrapLanguageModel({\n * model: openai(\"gpt-4o\"),\n * middleware: budgetGuardMiddleware(guard),\n * });\n */\nexport function budgetGuardMiddleware(\n guard: BudgetGuard,\n): LanguageModelV1Middleware {\n return {\n async wrapGenerate({ doGenerate, model }) {\n guard.check(); // throws BudgetExceeded before the call runs\n const result = await doGenerate();\n guard.record(\n model.modelId,\n result.usage.promptTokens,\n result.usage.completionTokens,\n );\n return result;\n },\n\n async wrapStream({ doStream, model }) {\n guard.check(); // throws BudgetExceeded before the stream starts\n const { stream, ...rest } = await doStream();\n\n const guarded = stream.pipeThrough(\n new TransformStream<StreamPart, StreamPart>({\n transform(chunk, controller) {\n if (chunk.type === \"finish\") {\n guard.record(\n model.modelId,\n chunk.usage.promptTokens,\n chunk.usage.completionTokens,\n );\n }\n controller.enqueue(chunk);\n },\n }),\n );\n\n return { stream: guarded, ...rest };\n },\n };\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { LanguageModelV1Middleware } from 'ai';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Offline token pricing from a vendored LiteLLM cost map.
|
|
5
|
+
*
|
|
6
|
+
* Mirrors `src/floe_guard/pricing.py`: BOTH the input and output per-token prices
|
|
7
|
+
* must be finite numbers, otherwise the model is treated as unpriceable. A
|
|
8
|
+
* half-valid entry would silently undercharge — so we refuse it (fail-closed).
|
|
9
|
+
*
|
|
10
|
+
* No network. The cost map (`cost_map.json`) is a snapshot of LiteLLM's
|
|
11
|
+
* `model_prices_and_context_window.json`; refresh it on a schedule or estimates
|
|
12
|
+
* drift as vendors change prices.
|
|
13
|
+
*/
|
|
14
|
+
/** A user-supplied per-token price, in USD, for a model the map cannot price. */
|
|
15
|
+
interface ManualPrice {
|
|
16
|
+
inputCostPerToken: number;
|
|
17
|
+
outputCostPerToken: number;
|
|
18
|
+
}
|
|
19
|
+
/** A resolved per-token price plus where it came from. */
|
|
20
|
+
interface PricedModel {
|
|
21
|
+
inputCostPerToken: number;
|
|
22
|
+
outputCostPerToken: number;
|
|
23
|
+
source: "override" | "cost_map";
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Resolve a model to its per-token price, or `null` if it cannot be priced.
|
|
27
|
+
*
|
|
28
|
+
* Overrides win, then the bundled cost map (looked up by bare name, then the raw
|
|
29
|
+
* field). Fail-closed: both prices must be finite, else `null`.
|
|
30
|
+
*/
|
|
31
|
+
declare function resolvePrice(model: string, overrides?: Record<string, ManualPrice>): PricedModel | null;
|
|
32
|
+
/** USD cost for token usage. Negative counts are clamped to zero. */
|
|
33
|
+
declare function priceTokens(priced: PricedModel, promptTokens: number, completionTokens: number): number;
|
|
34
|
+
|
|
35
|
+
type pricing_ManualPrice = ManualPrice;
|
|
36
|
+
type pricing_PricedModel = PricedModel;
|
|
37
|
+
declare const pricing_priceTokens: typeof priceTokens;
|
|
38
|
+
declare const pricing_resolvePrice: typeof resolvePrice;
|
|
39
|
+
declare namespace pricing {
|
|
40
|
+
export { type pricing_ManualPrice as ManualPrice, type pricing_PricedModel as PricedModel, pricing_priceTokens as priceTokens, pricing_resolvePrice as resolvePrice };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The local, in-process budget guard.
|
|
45
|
+
*
|
|
46
|
+
* `BudgetGuard` is a kill-switch that lives in the LLM call path. The contract:
|
|
47
|
+
*
|
|
48
|
+
* 1. Call {@link BudgetGuard.check} BEFORE every LLM call. If the *next* call
|
|
49
|
+
* would cross the ceiling, it throws {@link BudgetExceeded} and the call never
|
|
50
|
+
* runs.
|
|
51
|
+
* 2. Call {@link BudgetGuard.record} AFTER every response, with the token usage.
|
|
52
|
+
* It prices the tokens offline and accrues the USD into a running total.
|
|
53
|
+
*
|
|
54
|
+
* This is a faithful port of `src/floe_guard/guard.py` — same prediction logic,
|
|
55
|
+
* same epsilon handling, same fail-closed default.
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
interface BudgetGuardOptions {
|
|
59
|
+
/** Per-model manual prices for models the bundled cost map cannot price. */
|
|
60
|
+
priceOverrides?: Record<string, ManualPrice>;
|
|
61
|
+
/**
|
|
62
|
+
* When `true` (default), recording an unpriceable model without a manual price
|
|
63
|
+
* warns loudly AND throws {@link UnpriceableModelError}. When `false`, it warns
|
|
64
|
+
* and skips accrual (you have opted into un-enforced spend for that model).
|
|
65
|
+
*/
|
|
66
|
+
failClosed?: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Optional callback invoked with `(spentUsd, limitUsd)` right before
|
|
69
|
+
* {@link BudgetExceeded} is thrown. Defaults to printing the
|
|
70
|
+
* `BUDGET EXCEEDED — call blocked` banner to stderr.
|
|
71
|
+
*/
|
|
72
|
+
onBlock?: (spentUsd: number, limitUsd: number) => void;
|
|
73
|
+
}
|
|
74
|
+
declare class BudgetGuard {
|
|
75
|
+
readonly limitUsd: number;
|
|
76
|
+
spentUsd: number;
|
|
77
|
+
priceOverrides?: Record<string, ManualPrice>;
|
|
78
|
+
failClosed: boolean;
|
|
79
|
+
private readonly onBlock;
|
|
80
|
+
/** Cost of the most recent priced call, used to predict the next one. */
|
|
81
|
+
private lastCost;
|
|
82
|
+
/**
|
|
83
|
+
* @param limitUsd the spend ceiling, in USD. `0` blocks the very first call.
|
|
84
|
+
*/
|
|
85
|
+
constructor(limitUsd: number, options?: BudgetGuardOptions);
|
|
86
|
+
/**
|
|
87
|
+
* Throw {@link BudgetExceeded} if the next call would cross the ceiling.
|
|
88
|
+
*
|
|
89
|
+
* Call this immediately before each LLM request. The "next call" is estimated
|
|
90
|
+
* from the last recorded call's cost (override with `estimatedNextCost`); the
|
|
91
|
+
* first call is always allowed unless the ceiling is already met. A check on
|
|
92
|
+
* the running total catches an overshoot if the estimate was too low.
|
|
93
|
+
*/
|
|
94
|
+
check(estimatedNextCost?: number): void;
|
|
95
|
+
/**
|
|
96
|
+
* Price one response's tokens offline and add the cost to the total.
|
|
97
|
+
*
|
|
98
|
+
* Returns the USD cost of this call. If the model is unpriceable and no `price`
|
|
99
|
+
* is given, behaviour depends on `failClosed`: warn + throw (default), or
|
|
100
|
+
* warn + skip accrual.
|
|
101
|
+
*/
|
|
102
|
+
record(model: string, promptTokens: number, completionTokens: number, options?: {
|
|
103
|
+
price?: ManualPrice;
|
|
104
|
+
}): number;
|
|
105
|
+
/** USD left before the ceiling (never negative). */
|
|
106
|
+
get remainingUsd(): number;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Exceptions for floe-guard.
|
|
111
|
+
*
|
|
112
|
+
* Everything derives from {@link FloeGuardError} (the package-root base) so callers
|
|
113
|
+
* can catch the whole family with a single `catch (e) { if (e instanceof FloeGuardError) ... }`.
|
|
114
|
+
*
|
|
115
|
+
* Mirrors `src/floe_guard/errors.py` in the Python package — message formats are
|
|
116
|
+
* kept byte-for-byte identical so both adapters read the same.
|
|
117
|
+
*/
|
|
118
|
+
/** Base class for every error raised by floe-guard. */
|
|
119
|
+
declare class FloeGuardError extends Error {
|
|
120
|
+
constructor(message: string);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Thrown before an LLM call that would cross the configured spend ceiling.
|
|
124
|
+
*
|
|
125
|
+
* The guard throws this *instead of* letting the next call run, so a runaway loop
|
|
126
|
+
* stops here rather than burning more money.
|
|
127
|
+
*/
|
|
128
|
+
declare class BudgetExceeded extends FloeGuardError {
|
|
129
|
+
readonly spentUsd: number;
|
|
130
|
+
readonly limitUsd: number;
|
|
131
|
+
constructor(spentUsd: number, limitUsd: number);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Thrown when a model cannot be priced and the guard is fail-closed.
|
|
135
|
+
*
|
|
136
|
+
* We refuse rather than silently accrue $0 — "we cannot cap what we cannot price".
|
|
137
|
+
* Pass a manual price (`priceOverrides` or `record(..., { price })`) to make the
|
|
138
|
+
* model enforceable.
|
|
139
|
+
*/
|
|
140
|
+
declare class UnpriceableModelError extends FloeGuardError {
|
|
141
|
+
readonly model: string;
|
|
142
|
+
constructor(model: string);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Vercel AI SDK middleware that enforces a {@link BudgetGuard} in the call path.
|
|
147
|
+
*
|
|
148
|
+
* This is the TypeScript counterpart to the Python framework adapters. The AI SDK
|
|
149
|
+
* is TypeScript-only, so it ships as its own npm package.
|
|
150
|
+
*
|
|
151
|
+
* Verified against `ai@4.3.19` (`LanguageModelV1Middleware`):
|
|
152
|
+
* - `wrapGenerate({ doGenerate, model })` — we `check()` (throws to hard-stop)
|
|
153
|
+
* BEFORE calling `doGenerate()`, then `record()` from `result.usage`.
|
|
154
|
+
* - `wrapStream({ doStream, model })` — we `check()` BEFORE `doStream()`, then
|
|
155
|
+
* read `usage` from the `finish` part as the stream drains.
|
|
156
|
+
*
|
|
157
|
+
* The model id used for pricing comes from `model.modelId`.
|
|
158
|
+
*/
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Build a `LanguageModelV1Middleware` that hard-stops the model before a call
|
|
162
|
+
* crosses the guard's USD ceiling, and records priced token usage after.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* import { wrapLanguageModel } from "ai";
|
|
166
|
+
* import { openai } from "@ai-sdk/openai";
|
|
167
|
+
* import { BudgetGuard, budgetGuardMiddleware } from "floe-guard";
|
|
168
|
+
*
|
|
169
|
+
* const guard = new BudgetGuard(5.00);
|
|
170
|
+
* const model = wrapLanguageModel({
|
|
171
|
+
* model: openai("gpt-4o"),
|
|
172
|
+
* middleware: budgetGuardMiddleware(guard),
|
|
173
|
+
* });
|
|
174
|
+
*/
|
|
175
|
+
declare function budgetGuardMiddleware(guard: BudgetGuard): LanguageModelV1Middleware;
|
|
176
|
+
|
|
177
|
+
export { BudgetExceeded, BudgetGuard, type BudgetGuardOptions, FloeGuardError, type ManualPrice, type PricedModel, UnpriceableModelError, budgetGuardMiddleware, priceTokens, pricing, resolvePrice };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { LanguageModelV1Middleware } from 'ai';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Offline token pricing from a vendored LiteLLM cost map.
|
|
5
|
+
*
|
|
6
|
+
* Mirrors `src/floe_guard/pricing.py`: BOTH the input and output per-token prices
|
|
7
|
+
* must be finite numbers, otherwise the model is treated as unpriceable. A
|
|
8
|
+
* half-valid entry would silently undercharge — so we refuse it (fail-closed).
|
|
9
|
+
*
|
|
10
|
+
* No network. The cost map (`cost_map.json`) is a snapshot of LiteLLM's
|
|
11
|
+
* `model_prices_and_context_window.json`; refresh it on a schedule or estimates
|
|
12
|
+
* drift as vendors change prices.
|
|
13
|
+
*/
|
|
14
|
+
/** A user-supplied per-token price, in USD, for a model the map cannot price. */
|
|
15
|
+
interface ManualPrice {
|
|
16
|
+
inputCostPerToken: number;
|
|
17
|
+
outputCostPerToken: number;
|
|
18
|
+
}
|
|
19
|
+
/** A resolved per-token price plus where it came from. */
|
|
20
|
+
interface PricedModel {
|
|
21
|
+
inputCostPerToken: number;
|
|
22
|
+
outputCostPerToken: number;
|
|
23
|
+
source: "override" | "cost_map";
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Resolve a model to its per-token price, or `null` if it cannot be priced.
|
|
27
|
+
*
|
|
28
|
+
* Overrides win, then the bundled cost map (looked up by bare name, then the raw
|
|
29
|
+
* field). Fail-closed: both prices must be finite, else `null`.
|
|
30
|
+
*/
|
|
31
|
+
declare function resolvePrice(model: string, overrides?: Record<string, ManualPrice>): PricedModel | null;
|
|
32
|
+
/** USD cost for token usage. Negative counts are clamped to zero. */
|
|
33
|
+
declare function priceTokens(priced: PricedModel, promptTokens: number, completionTokens: number): number;
|
|
34
|
+
|
|
35
|
+
type pricing_ManualPrice = ManualPrice;
|
|
36
|
+
type pricing_PricedModel = PricedModel;
|
|
37
|
+
declare const pricing_priceTokens: typeof priceTokens;
|
|
38
|
+
declare const pricing_resolvePrice: typeof resolvePrice;
|
|
39
|
+
declare namespace pricing {
|
|
40
|
+
export { type pricing_ManualPrice as ManualPrice, type pricing_PricedModel as PricedModel, pricing_priceTokens as priceTokens, pricing_resolvePrice as resolvePrice };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The local, in-process budget guard.
|
|
45
|
+
*
|
|
46
|
+
* `BudgetGuard` is a kill-switch that lives in the LLM call path. The contract:
|
|
47
|
+
*
|
|
48
|
+
* 1. Call {@link BudgetGuard.check} BEFORE every LLM call. If the *next* call
|
|
49
|
+
* would cross the ceiling, it throws {@link BudgetExceeded} and the call never
|
|
50
|
+
* runs.
|
|
51
|
+
* 2. Call {@link BudgetGuard.record} AFTER every response, with the token usage.
|
|
52
|
+
* It prices the tokens offline and accrues the USD into a running total.
|
|
53
|
+
*
|
|
54
|
+
* This is a faithful port of `src/floe_guard/guard.py` — same prediction logic,
|
|
55
|
+
* same epsilon handling, same fail-closed default.
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
interface BudgetGuardOptions {
|
|
59
|
+
/** Per-model manual prices for models the bundled cost map cannot price. */
|
|
60
|
+
priceOverrides?: Record<string, ManualPrice>;
|
|
61
|
+
/**
|
|
62
|
+
* When `true` (default), recording an unpriceable model without a manual price
|
|
63
|
+
* warns loudly AND throws {@link UnpriceableModelError}. When `false`, it warns
|
|
64
|
+
* and skips accrual (you have opted into un-enforced spend for that model).
|
|
65
|
+
*/
|
|
66
|
+
failClosed?: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Optional callback invoked with `(spentUsd, limitUsd)` right before
|
|
69
|
+
* {@link BudgetExceeded} is thrown. Defaults to printing the
|
|
70
|
+
* `BUDGET EXCEEDED — call blocked` banner to stderr.
|
|
71
|
+
*/
|
|
72
|
+
onBlock?: (spentUsd: number, limitUsd: number) => void;
|
|
73
|
+
}
|
|
74
|
+
declare class BudgetGuard {
|
|
75
|
+
readonly limitUsd: number;
|
|
76
|
+
spentUsd: number;
|
|
77
|
+
priceOverrides?: Record<string, ManualPrice>;
|
|
78
|
+
failClosed: boolean;
|
|
79
|
+
private readonly onBlock;
|
|
80
|
+
/** Cost of the most recent priced call, used to predict the next one. */
|
|
81
|
+
private lastCost;
|
|
82
|
+
/**
|
|
83
|
+
* @param limitUsd the spend ceiling, in USD. `0` blocks the very first call.
|
|
84
|
+
*/
|
|
85
|
+
constructor(limitUsd: number, options?: BudgetGuardOptions);
|
|
86
|
+
/**
|
|
87
|
+
* Throw {@link BudgetExceeded} if the next call would cross the ceiling.
|
|
88
|
+
*
|
|
89
|
+
* Call this immediately before each LLM request. The "next call" is estimated
|
|
90
|
+
* from the last recorded call's cost (override with `estimatedNextCost`); the
|
|
91
|
+
* first call is always allowed unless the ceiling is already met. A check on
|
|
92
|
+
* the running total catches an overshoot if the estimate was too low.
|
|
93
|
+
*/
|
|
94
|
+
check(estimatedNextCost?: number): void;
|
|
95
|
+
/**
|
|
96
|
+
* Price one response's tokens offline and add the cost to the total.
|
|
97
|
+
*
|
|
98
|
+
* Returns the USD cost of this call. If the model is unpriceable and no `price`
|
|
99
|
+
* is given, behaviour depends on `failClosed`: warn + throw (default), or
|
|
100
|
+
* warn + skip accrual.
|
|
101
|
+
*/
|
|
102
|
+
record(model: string, promptTokens: number, completionTokens: number, options?: {
|
|
103
|
+
price?: ManualPrice;
|
|
104
|
+
}): number;
|
|
105
|
+
/** USD left before the ceiling (never negative). */
|
|
106
|
+
get remainingUsd(): number;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Exceptions for floe-guard.
|
|
111
|
+
*
|
|
112
|
+
* Everything derives from {@link FloeGuardError} (the package-root base) so callers
|
|
113
|
+
* can catch the whole family with a single `catch (e) { if (e instanceof FloeGuardError) ... }`.
|
|
114
|
+
*
|
|
115
|
+
* Mirrors `src/floe_guard/errors.py` in the Python package — message formats are
|
|
116
|
+
* kept byte-for-byte identical so both adapters read the same.
|
|
117
|
+
*/
|
|
118
|
+
/** Base class for every error raised by floe-guard. */
|
|
119
|
+
declare class FloeGuardError extends Error {
|
|
120
|
+
constructor(message: string);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Thrown before an LLM call that would cross the configured spend ceiling.
|
|
124
|
+
*
|
|
125
|
+
* The guard throws this *instead of* letting the next call run, so a runaway loop
|
|
126
|
+
* stops here rather than burning more money.
|
|
127
|
+
*/
|
|
128
|
+
declare class BudgetExceeded extends FloeGuardError {
|
|
129
|
+
readonly spentUsd: number;
|
|
130
|
+
readonly limitUsd: number;
|
|
131
|
+
constructor(spentUsd: number, limitUsd: number);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Thrown when a model cannot be priced and the guard is fail-closed.
|
|
135
|
+
*
|
|
136
|
+
* We refuse rather than silently accrue $0 — "we cannot cap what we cannot price".
|
|
137
|
+
* Pass a manual price (`priceOverrides` or `record(..., { price })`) to make the
|
|
138
|
+
* model enforceable.
|
|
139
|
+
*/
|
|
140
|
+
declare class UnpriceableModelError extends FloeGuardError {
|
|
141
|
+
readonly model: string;
|
|
142
|
+
constructor(model: string);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Vercel AI SDK middleware that enforces a {@link BudgetGuard} in the call path.
|
|
147
|
+
*
|
|
148
|
+
* This is the TypeScript counterpart to the Python framework adapters. The AI SDK
|
|
149
|
+
* is TypeScript-only, so it ships as its own npm package.
|
|
150
|
+
*
|
|
151
|
+
* Verified against `ai@4.3.19` (`LanguageModelV1Middleware`):
|
|
152
|
+
* - `wrapGenerate({ doGenerate, model })` — we `check()` (throws to hard-stop)
|
|
153
|
+
* BEFORE calling `doGenerate()`, then `record()` from `result.usage`.
|
|
154
|
+
* - `wrapStream({ doStream, model })` — we `check()` BEFORE `doStream()`, then
|
|
155
|
+
* read `usage` from the `finish` part as the stream drains.
|
|
156
|
+
*
|
|
157
|
+
* The model id used for pricing comes from `model.modelId`.
|
|
158
|
+
*/
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Build a `LanguageModelV1Middleware` that hard-stops the model before a call
|
|
162
|
+
* crosses the guard's USD ceiling, and records priced token usage after.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* import { wrapLanguageModel } from "ai";
|
|
166
|
+
* import { openai } from "@ai-sdk/openai";
|
|
167
|
+
* import { BudgetGuard, budgetGuardMiddleware } from "floe-guard";
|
|
168
|
+
*
|
|
169
|
+
* const guard = new BudgetGuard(5.00);
|
|
170
|
+
* const model = wrapLanguageModel({
|
|
171
|
+
* model: openai("gpt-4o"),
|
|
172
|
+
* middleware: budgetGuardMiddleware(guard),
|
|
173
|
+
* });
|
|
174
|
+
*/
|
|
175
|
+
declare function budgetGuardMiddleware(guard: BudgetGuard): LanguageModelV1Middleware;
|
|
176
|
+
|
|
177
|
+
export { BudgetExceeded, BudgetGuard, type BudgetGuardOptions, FloeGuardError, type ManualPrice, type PricedModel, UnpriceableModelError, budgetGuardMiddleware, priceTokens, pricing, resolvePrice };
|