@rudderjs/ai 1.5.0 → 1.6.1

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.
Files changed (163) hide show
  1. package/README.md +482 -4
  2. package/boost/guidelines.md +60 -0
  3. package/boost/skills/ai-agents/SKILL.md +7 -0
  4. package/boost/skills/ai-tools/SKILL.md +7 -0
  5. package/dist/agent.d.ts +35 -1
  6. package/dist/agent.d.ts.map +1 -1
  7. package/dist/agent.js +118 -16
  8. package/dist/agent.js.map +1 -1
  9. package/dist/budget/pricing.d.ts +124 -0
  10. package/dist/budget/pricing.d.ts.map +1 -0
  11. package/dist/budget/pricing.js +175 -0
  12. package/dist/budget/pricing.js.map +1 -0
  13. package/dist/budget/storage.d.ts +104 -0
  14. package/dist/budget/storage.d.ts.map +1 -0
  15. package/dist/budget/storage.js +0 -0
  16. package/dist/budget/storage.js.map +1 -0
  17. package/dist/budget/with-budget.d.ts +119 -0
  18. package/dist/budget/with-budget.d.ts.map +1 -0
  19. package/dist/budget/with-budget.js +175 -0
  20. package/dist/budget/with-budget.js.map +1 -0
  21. package/dist/budget-orm/index.d.ts +96 -0
  22. package/dist/budget-orm/index.d.ts.map +1 -0
  23. package/dist/budget-orm/index.js +177 -0
  24. package/dist/budget-orm/index.js.map +1 -0
  25. package/dist/commands/ai-eval.d.ts +93 -0
  26. package/dist/commands/ai-eval.d.ts.map +1 -0
  27. package/dist/commands/ai-eval.js +378 -0
  28. package/dist/commands/ai-eval.js.map +1 -0
  29. package/dist/computer-use/actions.d.ts +214 -0
  30. package/dist/computer-use/actions.d.ts.map +1 -0
  31. package/dist/computer-use/actions.js +48 -0
  32. package/dist/computer-use/actions.js.map +1 -0
  33. package/dist/computer-use/errors.d.ts +57 -0
  34. package/dist/computer-use/errors.d.ts.map +1 -0
  35. package/dist/computer-use/errors.js +76 -0
  36. package/dist/computer-use/errors.js.map +1 -0
  37. package/dist/computer-use/index.d.ts +53 -0
  38. package/dist/computer-use/index.d.ts.map +1 -0
  39. package/dist/computer-use/index.js +51 -0
  40. package/dist/computer-use/index.js.map +1 -0
  41. package/dist/computer-use/playwright.d.ts +76 -0
  42. package/dist/computer-use/playwright.d.ts.map +1 -0
  43. package/dist/computer-use/playwright.js +270 -0
  44. package/dist/computer-use/playwright.js.map +1 -0
  45. package/dist/computer-use/tool.d.ts +154 -0
  46. package/dist/computer-use/tool.d.ts.map +1 -0
  47. package/dist/computer-use/tool.js +210 -0
  48. package/dist/computer-use/tool.js.map +1 -0
  49. package/dist/eval/fixtures.d.ts +65 -0
  50. package/dist/eval/fixtures.d.ts.map +1 -0
  51. package/dist/eval/fixtures.js +110 -0
  52. package/dist/eval/fixtures.js.map +1 -0
  53. package/dist/eval/html-reporter.d.ts +25 -0
  54. package/dist/eval/html-reporter.d.ts.map +1 -0
  55. package/dist/eval/html-reporter.js +209 -0
  56. package/dist/eval/html-reporter.js.map +1 -0
  57. package/dist/eval/index.d.ts +271 -0
  58. package/dist/eval/index.d.ts.map +1 -0
  59. package/dist/eval/index.js +510 -0
  60. package/dist/eval/index.js.map +1 -0
  61. package/dist/eval/json-reporter.d.ts +43 -0
  62. package/dist/eval/json-reporter.d.ts.map +1 -0
  63. package/dist/eval/json-reporter.js +40 -0
  64. package/dist/eval/json-reporter.js.map +1 -0
  65. package/dist/fake.d.ts +36 -1
  66. package/dist/fake.d.ts.map +1 -1
  67. package/dist/fake.js +49 -2
  68. package/dist/fake.js.map +1 -1
  69. package/dist/file-search.d.ts +168 -0
  70. package/dist/file-search.d.ts.map +1 -0
  71. package/dist/file-search.js +158 -0
  72. package/dist/file-search.js.map +1 -0
  73. package/dist/index.d.ts +22 -2
  74. package/dist/index.d.ts.map +1 -1
  75. package/dist/index.js +17 -1
  76. package/dist/index.js.map +1 -1
  77. package/dist/mcp/client-tools.d.ts +39 -0
  78. package/dist/mcp/client-tools.d.ts.map +1 -0
  79. package/dist/mcp/client-tools.js +147 -0
  80. package/dist/mcp/client-tools.js.map +1 -0
  81. package/dist/mcp/index.d.ts +16 -0
  82. package/dist/mcp/index.d.ts.map +1 -0
  83. package/dist/mcp/index.js +15 -0
  84. package/dist/mcp/index.js.map +1 -0
  85. package/dist/mcp/server-from-agent.d.ts +24 -0
  86. package/dist/mcp/server-from-agent.d.ts.map +1 -0
  87. package/dist/mcp/server-from-agent.js +113 -0
  88. package/dist/mcp/server-from-agent.js.map +1 -0
  89. package/dist/mcp/types.d.ts +64 -0
  90. package/dist/mcp/types.d.ts.map +1 -0
  91. package/dist/mcp/types.js +6 -0
  92. package/dist/mcp/types.js.map +1 -0
  93. package/dist/memory-embedding/index.d.ts +121 -0
  94. package/dist/memory-embedding/index.d.ts.map +1 -0
  95. package/dist/memory-embedding/index.js +229 -0
  96. package/dist/memory-embedding/index.js.map +1 -0
  97. package/dist/memory-extract.d.ts +60 -0
  98. package/dist/memory-extract.d.ts.map +1 -0
  99. package/dist/memory-extract.js +163 -0
  100. package/dist/memory-extract.js.map +1 -0
  101. package/dist/memory-inject.d.ts +39 -0
  102. package/dist/memory-inject.d.ts.map +1 -0
  103. package/dist/memory-inject.js +135 -0
  104. package/dist/memory-inject.js.map +1 -0
  105. package/dist/memory-orm/index.d.ts +118 -0
  106. package/dist/memory-orm/index.d.ts.map +1 -0
  107. package/dist/memory-orm/index.js +187 -0
  108. package/dist/memory-orm/index.js.map +1 -0
  109. package/dist/memory.d.ts +55 -0
  110. package/dist/memory.d.ts.map +1 -0
  111. package/dist/memory.js +132 -0
  112. package/dist/memory.js.map +1 -0
  113. package/dist/observers.d.ts +22 -0
  114. package/dist/observers.d.ts.map +1 -1
  115. package/dist/observers.js.map +1 -1
  116. package/dist/provider-tools.d.ts +15 -1
  117. package/dist/provider-tools.d.ts.map +1 -1
  118. package/dist/provider-tools.js +21 -1
  119. package/dist/provider-tools.js.map +1 -1
  120. package/dist/providers/anthropic.d.ts.map +1 -1
  121. package/dist/providers/anthropic.js +61 -6
  122. package/dist/providers/anthropic.js.map +1 -1
  123. package/dist/providers/elevenlabs.d.ts +98 -0
  124. package/dist/providers/elevenlabs.d.ts.map +1 -0
  125. package/dist/providers/elevenlabs.js +229 -0
  126. package/dist/providers/elevenlabs.js.map +1 -0
  127. package/dist/providers/google.d.ts +83 -1
  128. package/dist/providers/google.d.ts.map +1 -1
  129. package/dist/providers/google.js +491 -8
  130. package/dist/providers/google.js.map +1 -1
  131. package/dist/providers/openai.d.ts +3 -1
  132. package/dist/providers/openai.d.ts.map +1 -1
  133. package/dist/providers/openai.js +209 -5
  134. package/dist/providers/openai.js.map +1 -1
  135. package/dist/providers/voyage.d.ts +91 -0
  136. package/dist/providers/voyage.d.ts.map +1 -0
  137. package/dist/providers/voyage.js +166 -0
  138. package/dist/providers/voyage.js.map +1 -0
  139. package/dist/queue-job.d.ts +69 -4
  140. package/dist/queue-job.d.ts.map +1 -1
  141. package/dist/queue-job.js +114 -11
  142. package/dist/queue-job.js.map +1 -1
  143. package/dist/registry.d.ts +3 -1
  144. package/dist/registry.d.ts.map +1 -1
  145. package/dist/registry.js +10 -0
  146. package/dist/registry.js.map +1 -1
  147. package/dist/server/provider.d.ts.map +1 -1
  148. package/dist/server/provider.js +23 -1
  149. package/dist/server/provider.js.map +1 -1
  150. package/dist/similarity-search.d.ts +163 -0
  151. package/dist/similarity-search.d.ts.map +1 -0
  152. package/dist/similarity-search.js +147 -0
  153. package/dist/similarity-search.js.map +1 -0
  154. package/dist/tool.d.ts.map +1 -1
  155. package/dist/tool.js +13 -4
  156. package/dist/tool.js.map +1 -1
  157. package/dist/types.d.ts +246 -0
  158. package/dist/types.d.ts.map +1 -1
  159. package/dist/vector-stores/index.d.ts +96 -0
  160. package/dist/vector-stores/index.d.ts.map +1 -0
  161. package/dist/vector-stores/index.js +153 -0
  162. package/dist/vector-stores/index.js.map +1 -0
  163. package/package.json +41 -3
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Model pricing catalog — USD per 1k tokens, snapshot-dated per entry.
3
+ *
4
+ * Used by:
5
+ * - `@rudderjs/ai/eval` to populate the cost column on eval reports
6
+ * - `@rudderjs/ai`'s budget enforcement middleware (A6 phase 3) to debit
7
+ * per-user spend against caps
8
+ *
9
+ * # Pricing drift
10
+ *
11
+ * Provider list pricing changes monthly. Each entry carries `_snapshotDate`
12
+ * indicating when the rate was captured. Apps with negotiated rates should
13
+ * pass an override map:
14
+ *
15
+ * ```ts
16
+ * import { ModelPricing, withBudget } from '@rudderjs/ai'
17
+ *
18
+ * withBudget({
19
+ * pricing: {
20
+ * ...ModelPricing,
21
+ * 'anthropic/claude-opus-4-7': {
22
+ * inputPer1k: 0.012,
23
+ * outputPer1k: 0.060,
24
+ * _snapshotDate: '2026-01-15',
25
+ * },
26
+ * },
27
+ * // ...
28
+ * })
29
+ * ```
30
+ *
31
+ * # Cache rates
32
+ *
33
+ * Anthropic, OpenAI, and Google all offer reduced rates for cache reads
34
+ * (and Anthropic charges a small premium for cache writes). The optional
35
+ * `cacheReadPer1k` / `cacheWritePer1k` fields capture those. The budget
36
+ * middleware uses them when `TokenUsage` carries cache deltas; when absent
37
+ * (today's `TokenUsage` shape), input rate applies to all input tokens.
38
+ */
39
+ /** Pricing for a single `<provider>/<model>` id, USD per 1k tokens. */
40
+ export interface ModelPriceEntry {
41
+ /** Cost per 1k input (prompt) tokens, USD. */
42
+ inputPer1k: number;
43
+ /** Cost per 1k output (completion) tokens, USD. */
44
+ outputPer1k: number;
45
+ /**
46
+ * Optional cost per 1k cached-read input tokens, USD. Set when the
47
+ * provider exposes a discounted rate for cache hits (Anthropic ephemeral
48
+ * cache, OpenAI prefix cache, Google cachedContent). When omitted,
49
+ * `inputPer1k` applies to all input tokens.
50
+ */
51
+ cacheReadPer1k?: number;
52
+ /**
53
+ * Optional cost per 1k cache-write input tokens, USD. Anthropic charges
54
+ * a small premium on the first write that primes an ephemeral cache;
55
+ * other providers don't. When omitted, `inputPer1k` applies.
56
+ */
57
+ cacheWritePer1k?: number;
58
+ /**
59
+ * ISO date string indicating when this rate was captured from the
60
+ * provider's published pricing. Surfaced in
61
+ * {@link UnknownModelPricingError} messages and useful when auditing
62
+ * stale catalogs.
63
+ */
64
+ _snapshotDate: string;
65
+ }
66
+ /**
67
+ * `<provider>/<model>` → {@link ModelPriceEntry}. Override entries by
68
+ * spreading: `{ ...ModelPricing, 'anthropic/claude-opus-4-7': {...} }`.
69
+ *
70
+ * Snapshot date: 2026-05-11.
71
+ */
72
+ export declare const ModelPricing: Record<string, ModelPriceEntry>;
73
+ /**
74
+ * Compute USD cost for an agent call given prompt + completion token
75
+ * counts and a pricing catalog.
76
+ *
77
+ * Returns `0` when the model id isn't in `pricing` — eval cost columns
78
+ * shouldn't crash on a fresh model. The budget middleware (A6 phase 3)
79
+ * uses {@link assertKnownModelPricing} to fail loud at config time
80
+ * instead.
81
+ *
82
+ * @param model `<provider>/<model>` id
83
+ * @param promptTokens Input tokens charged at `inputPer1k`
84
+ * @param completionTokens Output tokens charged at `outputPer1k`
85
+ * @param pricing Catalog override (defaults to {@link ModelPricing})
86
+ */
87
+ export declare function estimateCost(model: string, promptTokens: number, completionTokens: number, pricing?: Record<string, ModelPriceEntry>): number;
88
+ /**
89
+ * Budget enforcement uses this — fail loud at agent-construction time
90
+ * if the configured model isn't priced. Call from `withBudget(...)` in
91
+ * phase 3.
92
+ *
93
+ * @throws {UnknownModelPricingError} when `pricing[model]` is missing
94
+ */
95
+ export declare function assertKnownModelPricing(model: string, pricing?: Record<string, ModelPriceEntry>): ModelPriceEntry;
96
+ /**
97
+ * Thrown by `assertKnownModelPricing` when a model id has no pricing
98
+ * entry. The budget middleware throws this at construction so apps
99
+ * fail at boot, not on first prompt.
100
+ */
101
+ export declare class UnknownModelPricingError extends Error {
102
+ readonly model: string;
103
+ readonly snapshotDate: string | null;
104
+ constructor(model: string, pricing: Record<string, ModelPriceEntry>);
105
+ }
106
+ /**
107
+ * Thrown by the budget middleware (A6 phase 3) when a request would
108
+ * exceed a user's daily or monthly cap. Apps that want a different
109
+ * error type can intercept via `withBudget({ onExceeded })` and throw
110
+ * their own.
111
+ */
112
+ export declare class BudgetExceededError extends Error {
113
+ readonly userId: string;
114
+ readonly period: 'daily' | 'monthly';
115
+ readonly spent: number;
116
+ readonly cap: number;
117
+ constructor(opts: {
118
+ userId: string;
119
+ period: 'daily' | 'monthly';
120
+ spent: number;
121
+ cap: number;
122
+ });
123
+ }
124
+ //# sourceMappingURL=pricing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../../src/budget/pricing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,uEAAuE;AACvE,MAAM,WAAW,eAAe;IAC9B,8CAA8C;IAC9C,UAAU,EAAE,MAAM,CAAA;IAClB,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAA;IACnB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB;;;;;OAKG;IACH,aAAa,EAAE,MAAM,CAAA;CACtB;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CA+DxD,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,EACpB,gBAAgB,EAAE,MAAM,EACxB,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAgB,GACtD,MAAM,CAIR;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAgB,GACtD,eAAe,CAIjB;AAED;;;;GAIG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;IACjD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;gBAExB,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC;CAapE;AAED;;;;;GAKG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS,CAAA;IACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;gBAER,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,GAAG,SAAS,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE;CAW9F"}
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Model pricing catalog — USD per 1k tokens, snapshot-dated per entry.
3
+ *
4
+ * Used by:
5
+ * - `@rudderjs/ai/eval` to populate the cost column on eval reports
6
+ * - `@rudderjs/ai`'s budget enforcement middleware (A6 phase 3) to debit
7
+ * per-user spend against caps
8
+ *
9
+ * # Pricing drift
10
+ *
11
+ * Provider list pricing changes monthly. Each entry carries `_snapshotDate`
12
+ * indicating when the rate was captured. Apps with negotiated rates should
13
+ * pass an override map:
14
+ *
15
+ * ```ts
16
+ * import { ModelPricing, withBudget } from '@rudderjs/ai'
17
+ *
18
+ * withBudget({
19
+ * pricing: {
20
+ * ...ModelPricing,
21
+ * 'anthropic/claude-opus-4-7': {
22
+ * inputPer1k: 0.012,
23
+ * outputPer1k: 0.060,
24
+ * _snapshotDate: '2026-01-15',
25
+ * },
26
+ * },
27
+ * // ...
28
+ * })
29
+ * ```
30
+ *
31
+ * # Cache rates
32
+ *
33
+ * Anthropic, OpenAI, and Google all offer reduced rates for cache reads
34
+ * (and Anthropic charges a small premium for cache writes). The optional
35
+ * `cacheReadPer1k` / `cacheWritePer1k` fields capture those. The budget
36
+ * middleware uses them when `TokenUsage` carries cache deltas; when absent
37
+ * (today's `TokenUsage` shape), input rate applies to all input tokens.
38
+ */
39
+ /**
40
+ * `<provider>/<model>` → {@link ModelPriceEntry}. Override entries by
41
+ * spreading: `{ ...ModelPricing, 'anthropic/claude-opus-4-7': {...} }`.
42
+ *
43
+ * Snapshot date: 2026-05-11.
44
+ */
45
+ export const ModelPricing = {
46
+ // ─── Anthropic ──────────────────────────────────────────
47
+ // Cache write = 1.25× input, cache read = 0.1× input (Anthropic ephemeral)
48
+ 'anthropic/claude-opus-4-7': { inputPer1k: 0.015, outputPer1k: 0.075, cacheWritePer1k: 0.01875, cacheReadPer1k: 0.0015, _snapshotDate: '2026-05-11' },
49
+ 'anthropic/claude-opus-4-6': { inputPer1k: 0.015, outputPer1k: 0.075, cacheWritePer1k: 0.01875, cacheReadPer1k: 0.0015, _snapshotDate: '2026-05-11' },
50
+ 'anthropic/claude-sonnet-4-6': { inputPer1k: 0.003, outputPer1k: 0.015, cacheWritePer1k: 0.00375, cacheReadPer1k: 0.0003, _snapshotDate: '2026-05-11' },
51
+ 'anthropic/claude-sonnet-4-5': { inputPer1k: 0.003, outputPer1k: 0.015, cacheWritePer1k: 0.00375, cacheReadPer1k: 0.0003, _snapshotDate: '2026-05-11' },
52
+ 'anthropic/claude-haiku-4-5': { inputPer1k: 0.0008, outputPer1k: 0.004, cacheWritePer1k: 0.001, cacheReadPer1k: 0.00008, _snapshotDate: '2026-05-11' },
53
+ 'anthropic/claude-3-7-sonnet': { inputPer1k: 0.003, outputPer1k: 0.015, cacheWritePer1k: 0.00375, cacheReadPer1k: 0.0003, _snapshotDate: '2026-05-11' },
54
+ 'anthropic/claude-3-5-sonnet': { inputPer1k: 0.003, outputPer1k: 0.015, cacheWritePer1k: 0.00375, cacheReadPer1k: 0.0003, _snapshotDate: '2026-05-11' },
55
+ 'anthropic/claude-3-5-haiku': { inputPer1k: 0.0008, outputPer1k: 0.004, cacheWritePer1k: 0.001, cacheReadPer1k: 0.00008, _snapshotDate: '2026-05-11' },
56
+ // ─── OpenAI ─────────────────────────────────────────────
57
+ // Cache read = 0.5× input (OpenAI prefix cache); no cache write surcharge
58
+ 'openai/gpt-4-1': { inputPer1k: 0.002, outputPer1k: 0.008, cacheReadPer1k: 0.0005, _snapshotDate: '2026-05-11' },
59
+ 'openai/gpt-4-1-mini': { inputPer1k: 0.0004, outputPer1k: 0.0016, cacheReadPer1k: 0.0001, _snapshotDate: '2026-05-11' },
60
+ 'openai/gpt-4-1-nano': { inputPer1k: 0.0001, outputPer1k: 0.0004, cacheReadPer1k: 0.000025, _snapshotDate: '2026-05-11' },
61
+ 'openai/gpt-4o': { inputPer1k: 0.0025, outputPer1k: 0.01, cacheReadPer1k: 0.00125, _snapshotDate: '2026-05-11' },
62
+ 'openai/gpt-4o-mini': { inputPer1k: 0.00015, outputPer1k: 0.0006, cacheReadPer1k: 0.000075, _snapshotDate: '2026-05-11' },
63
+ 'openai/o1': { inputPer1k: 0.015, outputPer1k: 0.06, cacheReadPer1k: 0.0075, _snapshotDate: '2026-05-11' },
64
+ 'openai/o1-mini': { inputPer1k: 0.0011, outputPer1k: 0.0044, cacheReadPer1k: 0.00055, _snapshotDate: '2026-05-11' },
65
+ 'openai/o3': { inputPer1k: 0.002, outputPer1k: 0.008, cacheReadPer1k: 0.0005, _snapshotDate: '2026-05-11' },
66
+ 'openai/o3-mini': { inputPer1k: 0.0011, outputPer1k: 0.0044, cacheReadPer1k: 0.00055, _snapshotDate: '2026-05-11' },
67
+ 'openai/o4-mini': { inputPer1k: 0.0011, outputPer1k: 0.0044, cacheReadPer1k: 0.000275, _snapshotDate: '2026-05-11' },
68
+ // ─── Google (Gemini) ────────────────────────────────────
69
+ // Cache read = 0.25× input (Google cachedContent); no cache write surcharge
70
+ 'google/gemini-2.5-pro': { inputPer1k: 0.00125, outputPer1k: 0.005, cacheReadPer1k: 0.0003125, _snapshotDate: '2026-05-11' },
71
+ 'google/gemini-2.5-flash': { inputPer1k: 0.000075, outputPer1k: 0.0003, cacheReadPer1k: 0.00001875, _snapshotDate: '2026-05-11' },
72
+ 'google/gemini-2.5-flash-lite': { inputPer1k: 0.00004, outputPer1k: 0.00015, cacheReadPer1k: 0.00001, _snapshotDate: '2026-05-11' },
73
+ 'google/gemini-2.0-flash': { inputPer1k: 0.0001, outputPer1k: 0.0004, cacheReadPer1k: 0.000025, _snapshotDate: '2026-05-11' },
74
+ 'google/gemini-2.0-flash-lite': { inputPer1k: 0.000075, outputPer1k: 0.0003, cacheReadPer1k: 0.00001875, _snapshotDate: '2026-05-11' },
75
+ // ─── Bedrock (Anthropic models on AWS Bedrock) ──────────
76
+ // Bedrock matches Anthropic list pricing for Claude family.
77
+ 'bedrock/anthropic.claude-opus-4-7': { inputPer1k: 0.015, outputPer1k: 0.075, cacheWritePer1k: 0.01875, cacheReadPer1k: 0.0015, _snapshotDate: '2026-05-11' },
78
+ 'bedrock/anthropic.claude-sonnet-4-6': { inputPer1k: 0.003, outputPer1k: 0.015, cacheWritePer1k: 0.00375, cacheReadPer1k: 0.0003, _snapshotDate: '2026-05-11' },
79
+ 'bedrock/anthropic.claude-haiku-4-5': { inputPer1k: 0.0008, outputPer1k: 0.004, cacheWritePer1k: 0.001, cacheReadPer1k: 0.00008, _snapshotDate: '2026-05-11' },
80
+ 'bedrock/anthropic.claude-3-5-sonnet': { inputPer1k: 0.003, outputPer1k: 0.015, cacheWritePer1k: 0.00375, cacheReadPer1k: 0.0003, _snapshotDate: '2026-05-11' },
81
+ 'bedrock/anthropic.claude-3-5-haiku': { inputPer1k: 0.0008, outputPer1k: 0.004, cacheWritePer1k: 0.001, cacheReadPer1k: 0.00008, _snapshotDate: '2026-05-11' },
82
+ // ─── xAI (Grok) ─────────────────────────────────────────
83
+ 'xai/grok-4': { inputPer1k: 0.003, outputPer1k: 0.015, _snapshotDate: '2026-05-11' },
84
+ 'xai/grok-3': { inputPer1k: 0.003, outputPer1k: 0.015, _snapshotDate: '2026-05-11' },
85
+ 'xai/grok-3-mini': { inputPer1k: 0.0003, outputPer1k: 0.0005, _snapshotDate: '2026-05-11' },
86
+ // ─── DeepSeek ───────────────────────────────────────────
87
+ 'deepseek/deepseek-chat': { inputPer1k: 0.00027, outputPer1k: 0.0011, cacheReadPer1k: 0.00007, _snapshotDate: '2026-05-11' },
88
+ 'deepseek/deepseek-reasoner': { inputPer1k: 0.00055, outputPer1k: 0.00219, cacheReadPer1k: 0.00014, _snapshotDate: '2026-05-11' },
89
+ // ─── Mistral ────────────────────────────────────────────
90
+ 'mistral/mistral-large': { inputPer1k: 0.002, outputPer1k: 0.006, _snapshotDate: '2026-05-11' },
91
+ 'mistral/mistral-medium': { inputPer1k: 0.0004, outputPer1k: 0.002, _snapshotDate: '2026-05-11' },
92
+ 'mistral/mistral-small': { inputPer1k: 0.0001, outputPer1k: 0.0003, _snapshotDate: '2026-05-11' },
93
+ // ─── Groq ───────────────────────────────────────────────
94
+ 'groq/llama-3.3-70b-versatile': { inputPer1k: 0.00059, outputPer1k: 0.00079, _snapshotDate: '2026-05-11' },
95
+ 'groq/llama-3.1-8b-instant': { inputPer1k: 0.00005, outputPer1k: 0.00008, _snapshotDate: '2026-05-11' },
96
+ // ─── Cohere ─────────────────────────────────────────────
97
+ 'cohere/command-a': { inputPer1k: 0.0025, outputPer1k: 0.01, _snapshotDate: '2026-05-11' },
98
+ 'cohere/command-r-plus': { inputPer1k: 0.0025, outputPer1k: 0.01, _snapshotDate: '2026-05-11' },
99
+ 'cohere/command-r': { inputPer1k: 0.00015, outputPer1k: 0.0006, _snapshotDate: '2026-05-11' },
100
+ };
101
+ /**
102
+ * Compute USD cost for an agent call given prompt + completion token
103
+ * counts and a pricing catalog.
104
+ *
105
+ * Returns `0` when the model id isn't in `pricing` — eval cost columns
106
+ * shouldn't crash on a fresh model. The budget middleware (A6 phase 3)
107
+ * uses {@link assertKnownModelPricing} to fail loud at config time
108
+ * instead.
109
+ *
110
+ * @param model `<provider>/<model>` id
111
+ * @param promptTokens Input tokens charged at `inputPer1k`
112
+ * @param completionTokens Output tokens charged at `outputPer1k`
113
+ * @param pricing Catalog override (defaults to {@link ModelPricing})
114
+ */
115
+ export function estimateCost(model, promptTokens, completionTokens, pricing = ModelPricing) {
116
+ const rate = pricing[model];
117
+ if (!rate)
118
+ return 0;
119
+ return (promptTokens * rate.inputPer1k + completionTokens * rate.outputPer1k) / 1000;
120
+ }
121
+ /**
122
+ * Budget enforcement uses this — fail loud at agent-construction time
123
+ * if the configured model isn't priced. Call from `withBudget(...)` in
124
+ * phase 3.
125
+ *
126
+ * @throws {UnknownModelPricingError} when `pricing[model]` is missing
127
+ */
128
+ export function assertKnownModelPricing(model, pricing = ModelPricing) {
129
+ const rate = pricing[model];
130
+ if (!rate)
131
+ throw new UnknownModelPricingError(model, pricing);
132
+ return rate;
133
+ }
134
+ /**
135
+ * Thrown by `assertKnownModelPricing` when a model id has no pricing
136
+ * entry. The budget middleware throws this at construction so apps
137
+ * fail at boot, not on first prompt.
138
+ */
139
+ export class UnknownModelPricingError extends Error {
140
+ model;
141
+ snapshotDate;
142
+ constructor(model, pricing) {
143
+ const sample = Object.keys(pricing)[0];
144
+ const snapshotDate = sample ? (pricing[sample]?._snapshotDate ?? null) : null;
145
+ const sampleSuffix = snapshotDate ? ` (catalog snapshot ${snapshotDate})` : '';
146
+ super(`[RudderJS AI] No pricing entry for model "${model}"${sampleSuffix}. ` +
147
+ `Either the model id is misspelled, or the catalog is stale — ` +
148
+ `add an override entry: \`pricing: { ...ModelPricing, "${model}": { inputPer1k, outputPer1k, _snapshotDate } }\`.`);
149
+ this.name = 'UnknownModelPricingError';
150
+ this.model = model;
151
+ this.snapshotDate = snapshotDate;
152
+ }
153
+ }
154
+ /**
155
+ * Thrown by the budget middleware (A6 phase 3) when a request would
156
+ * exceed a user's daily or monthly cap. Apps that want a different
157
+ * error type can intercept via `withBudget({ onExceeded })` and throw
158
+ * their own.
159
+ */
160
+ export class BudgetExceededError extends Error {
161
+ userId;
162
+ period;
163
+ spent;
164
+ cap;
165
+ constructor(opts) {
166
+ super(`[RudderJS AI] ${opts.period} budget of $${opts.cap.toFixed(2)} exceeded for user ${opts.userId} ` +
167
+ `(spent $${opts.spent.toFixed(4)}).`);
168
+ this.name = 'BudgetExceededError';
169
+ this.userId = opts.userId;
170
+ this.period = opts.period;
171
+ this.spent = opts.spent;
172
+ this.cap = opts.cap;
173
+ }
174
+ }
175
+ //# sourceMappingURL=pricing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pricing.js","sourceRoot":"","sources":["../../src/budget/pricing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Persistence contract for `withBudget(...)` middleware (#A6 Phase 3).
3
+ *
4
+ * Apps configure a `BudgetStorage` instance — the middleware calls
5
+ * `checkAndDebit()` once before each request (estimated input cost) and
6
+ * once per `usage` chunk (actual output cost). The storage is responsible
7
+ * for atomic increment + cap enforcement.
8
+ *
9
+ * # Atomicity
10
+ *
11
+ * `checkAndDebit` MUST be atomic: a concurrent caller cannot observe a
12
+ * stale `spent` value between the read and the write. Otherwise two
13
+ * requests both pass the check before either debits, and a user can
14
+ * exceed their cap by `costUsd × concurrency`.
15
+ *
16
+ * Implementations:
17
+ * - `memoryBudgetStorage()` — single-process Map; atomic because we never
18
+ * `await` between the read and the write.
19
+ * - `ormBudgetStorage()` (Phase 4) — uses `UPDATE … RETURNING` (Postgres)
20
+ * or row-level `SELECT … FOR UPDATE` (MySQL/SQLite) to keep the read +
21
+ * write in a single transaction.
22
+ * - Redis / Cache impls — would use `INCRBY` + a `EXPIRE`, treating the
23
+ * counter as authoritative.
24
+ *
25
+ * # Period semantics
26
+ *
27
+ * `daily` rolls at the user's local midnight when `timezone` is provided
28
+ * (IANA name, e.g. `'America/New_York'`); UTC otherwise. `monthly` rolls
29
+ * on the calendar-month boundary in the same TZ. Phase 2 does not
30
+ * implement rolling-30-days windows — adds storage complexity for marginal
31
+ * value.
32
+ */
33
+ export type BudgetPeriod = 'daily' | 'monthly';
34
+ export interface BudgetCheckOptions {
35
+ /** Identifier for the budget owner (typically a user id). */
36
+ userId: string;
37
+ /** Which cap to check against. */
38
+ period: BudgetPeriod;
39
+ /** Cap in USD. Caller resolves `budget()` once per request. */
40
+ cap: number;
41
+ /** Cost to debit in USD. Pass `0` to read the current spent value without mutating. */
42
+ costUsd: number;
43
+ /** Override the clock — useful for tests + period-rollover assertions. */
44
+ now?: Date;
45
+ /** IANA timezone for period boundaries. Defaults to UTC. */
46
+ timezone?: string;
47
+ }
48
+ export interface BudgetCheckResult {
49
+ /** True when the debit was applied; false when it would have exceeded `cap`. */
50
+ allowed: boolean;
51
+ /**
52
+ * Current spend for the period.
53
+ *
54
+ * - When `allowed: true`, this is the value AFTER the debit (i.e.
55
+ * includes `costUsd`).
56
+ * - When `allowed: false`, this is the value BEFORE the rejected debit
57
+ * (i.e. the prior `spent`). This matches the value `BudgetExceededError`
58
+ * should report — the user spent `spent`, the cap was `cap`, the
59
+ * request would have pushed past.
60
+ */
61
+ spent: number;
62
+ /** The cap passed in `BudgetCheckOptions.cap`, echoed for caller convenience. */
63
+ cap: number;
64
+ }
65
+ export interface BudgetStorage {
66
+ /**
67
+ * Atomically read the current `spent` value, add `costUsd` if the result
68
+ * stays within `cap`, and return the outcome.
69
+ *
70
+ * Pass `costUsd: 0` for a pure read (e.g. for a "you've spent $X today"
71
+ * status display) — the result reflects current spend without mutating.
72
+ */
73
+ checkAndDebit(opts: BudgetCheckOptions): Promise<BudgetCheckResult>;
74
+ /**
75
+ * Reset the budget counter for `(userId, period)` at the period
76
+ * containing `now` (or "now" by clock). Useful for tests and admin
77
+ * overrides; not required by the middleware.
78
+ */
79
+ reset?(userId: string, period: BudgetPeriod, now?: Date, timezone?: string): Promise<void>;
80
+ }
81
+ /**
82
+ * Format a `Date` as the bucket key for a billing period.
83
+ *
84
+ * - `daily` → `YYYY-MM-DD` in the given timezone (or UTC)
85
+ * - `monthly` → `YYYY-MM` in the given timezone (or UTC)
86
+ *
87
+ * Used by `memoryBudgetStorage` and `ormBudgetStorage` (Phase 4) to
88
+ * partition the counter by billing window. Exposed publicly so apps
89
+ * computing per-period totals outside the middleware (admin dashboards,
90
+ * tests) get the same bucketing.
91
+ */
92
+ export declare function periodKey(period: BudgetPeriod, now: Date, timezone?: string): string;
93
+ /**
94
+ * In-process `BudgetStorage` backed by a `Map`. Suitable for tests and
95
+ * single-process dev servers.
96
+ *
97
+ * **Cross-process caveat:** counters live in process-local memory, so
98
+ * queue workers (or any second process) see their own copy. For any app
99
+ * with workers or horizontal scaling, use `ormBudgetStorage()` (Phase 4)
100
+ * or a Redis-backed storage. Use `memoryBudgetStorage()` only when you're
101
+ * sure all budget-relevant code paths run in the same Node process.
102
+ */
103
+ export declare function memoryBudgetStorage(): BudgetStorage;
104
+ //# sourceMappingURL=storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/budget/storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,SAAS,CAAA;AAE9C,MAAM,WAAW,kBAAkB;IACjC,6DAA6D;IAC7D,MAAM,EAAK,MAAM,CAAA;IACjB,kCAAkC;IAClC,MAAM,EAAK,YAAY,CAAA;IACvB,+DAA+D;IAC/D,GAAG,EAAQ,MAAM,CAAA;IACjB,uFAAuF;IACvF,OAAO,EAAI,MAAM,CAAA;IACjB,0EAA0E;IAC1E,GAAG,CAAC,EAAO,IAAI,CAAA;IACf,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,gFAAgF;IAChF,OAAO,EAAE,OAAO,CAAA;IAChB;;;;;;;;;OASG;IACH,KAAK,EAAE,MAAM,CAAA;IACb,iFAAiF;IACjF,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,aAAa;IAC5B;;;;;;OAMG;IACH,aAAa,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAA;IAEnE;;;;OAIG;IACH,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,GAAG,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3F;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAUpF;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,IAAI,aAAa,CAgCnD"}
Binary file
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/budget/storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAuDH;;;;;;;;;;GAUG;AACH,MAAM,UAAU,SAAS,CAAC,MAAoB,EAAE,GAAS,EAAE,QAAiB;IAC1E,kEAAkE;IAClE,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE;QAC3C,QAAQ,EAAE,QAAQ,IAAI,KAAK;QAC3B,IAAI,EAAG,SAAS;QAChB,KAAK,EAAE,SAAS;QAChB,GAAG,EAAI,SAAS;KACjB,CAAC,CAAA;IACF,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA,CAAC,eAAe;IAC3C,OAAO,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA,CAAC,YAAY;AAChE,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,mBAAmB;IACjC,kEAAkE;IAClE,8DAA8D;IAC9D,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAA;IACtC,MAAM,GAAG,GAAG,CAAC,MAAc,EAAE,MAAoB,EAAE,GAAS,EAAE,EAAW,EAAU,EAAE,CACnF,GAAG,MAAM,IAAI,MAAM,IAAI,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,CAAC,EAAE,CAAA;IAErD,OAAO;QACL,sEAAsE;QACtE,sEAAsE;QACtE,yEAAyE;QACzE,sEAAsE;QACtE,gEAAgE;QAChE,aAAa;QACb,KAAK,CAAC,aAAa,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,GAAG,IAAI,IAAI,EAAE,EAAE,QAAQ,EAAE;YAC9E,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC;gBAAU,MAAM,IAAI,KAAK,CAAC,8EAA8E,GAAG,EAAE,CAAC,CAAA;YAClJ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,GAAG,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,kFAAkF,OAAO,EAAE,CAAC,CAAA;YAE1J,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAA;YAC5C,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;YAChC,MAAM,SAAS,GAAG,OAAO,GAAG,OAAO,CAAA;YACnC,IAAI,SAAS,GAAG,GAAG,EAAE,CAAC;gBACpB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,CAAA;YAChD,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,CAAA;YACtB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,CAAA;QACjD,CAAC;QAED,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,GAAG,IAAI,IAAI,EAAE,EAAE,QAAQ;YACpD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAA;QACjD,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,119 @@
1
+ import type { AiMiddleware, MiddlewareContext } from '../types.js';
2
+ import { type ModelPriceEntry } from './pricing.js';
3
+ import type { BudgetPeriod, BudgetStorage } from './storage.js';
4
+ /**
5
+ * Per-period caps in USD. Either or both can be set; periods with `null`
6
+ * / `undefined` are not enforced.
7
+ */
8
+ export interface BudgetCaps {
9
+ daily?: number | null;
10
+ monthly?: number | null;
11
+ }
12
+ export interface BudgetExceededArgs {
13
+ userId: string;
14
+ period: BudgetPeriod;
15
+ /** Spend recorded BEFORE the rejected debit. */
16
+ spent: number;
17
+ cap: number;
18
+ ctx: MiddlewareContext;
19
+ }
20
+ export interface WithBudgetOptions {
21
+ /**
22
+ * Resolves the user identifier for a given request. Return `null` (or
23
+ * `undefined`) to bypass budget enforcement entirely — useful for
24
+ * unauthenticated paths or admin tooling.
25
+ *
26
+ * Async return supported.
27
+ */
28
+ user(ctx: MiddlewareContext): string | null | undefined | Promise<string | null | undefined>;
29
+ /**
30
+ * USD caps for the resolved user. Called once per request (per agent
31
+ * step, more precisely). Caps may be set per-tier / per-plan by reading
32
+ * the user from your DB inside the callback.
33
+ */
34
+ budget(args: {
35
+ userId: string;
36
+ ctx: MiddlewareContext;
37
+ }): BudgetCaps | Promise<BudgetCaps>;
38
+ /**
39
+ * Where counters persist. Use {@link memoryBudgetStorage} for tests +
40
+ * single-process dev; `ormBudgetStorage` (#A6 Phase 4) for production.
41
+ */
42
+ storage: BudgetStorage;
43
+ /**
44
+ * Pricing catalog. Defaults to the shipped {@link ModelPricing}; spread
45
+ * to override entries for negotiated rates:
46
+ *
47
+ * ```ts
48
+ * pricing: { ...ModelPricing, 'anthropic/claude-opus-4-7': { ... } }
49
+ * ```
50
+ */
51
+ pricing?: Record<string, ModelPriceEntry>;
52
+ /**
53
+ * Called when a debit would exceed a cap. Default throws
54
+ * {@link BudgetExceededError}; supply your own to log/alert before
55
+ * throwing, or to throw a different error class. Must throw — return
56
+ * value is ignored. The throw aborts the agent run before the model
57
+ * call.
58
+ */
59
+ onExceeded?(args: BudgetExceededArgs): never | Promise<never>;
60
+ /**
61
+ * IANA timezone for daily / monthly period rollover. Defaults to UTC.
62
+ * Use the user's tz to roll caps at user-local midnight (matches
63
+ * billing dashboards).
64
+ */
65
+ timezone?: string;
66
+ /**
67
+ * Approximate-tokens estimator used for the pre-debit. Defaults to
68
+ * `Math.ceil(text.length / 4)` — fine for English-heavy prompts. Pass
69
+ * a tiktoken-backed estimator for accuracy.
70
+ */
71
+ estimateTokens?: (text: string) => number;
72
+ }
73
+ /**
74
+ * Per-user spend cap middleware (#A6 Phase 3).
75
+ *
76
+ * Pre-debits an input-cost estimate before each provider call (refusing
77
+ * with {@link BudgetExceededError} if the user would exceed any
78
+ * configured cap), then debits the actual cost difference once the
79
+ * `usage` chunk arrives.
80
+ *
81
+ * The pre-debit reserves budget so two concurrent requests can't both
82
+ * pass the check before either is billed — the `BudgetStorage` contract
83
+ * (#A6 Phase 2) requires `checkAndDebit` to be atomic.
84
+ *
85
+ * # Example
86
+ *
87
+ * ```ts
88
+ * import { withBudget, memoryBudgetStorage, ModelPricing } from '@rudderjs/ai'
89
+ *
90
+ * const budgeted = withBudget({
91
+ * user: (ctx) => ctx.context as string, // your app's user-id source
92
+ * budget: () => ({ daily: 0.50, monthly: 10 }), // USD
93
+ * storage: memoryBudgetStorage(),
94
+ * pricing: ModelPricing,
95
+ * })
96
+ *
97
+ * class MyAgent extends Agent {
98
+ * middleware() { return [budgeted] }
99
+ * }
100
+ * ```
101
+ *
102
+ * # Caveats
103
+ *
104
+ * - **Refunds on errors are not issued.** If the provider call fails
105
+ * after the pre-debit, the estimate stays debited. This avoids the
106
+ * complexity of distinguishing partial-credit cases (the model may
107
+ * have produced output before erroring). Apps that need refund-on-error
108
+ * should subscribe via `onError` and call `storage` directly.
109
+ * - **Cache token deltas not counted.** `TokenUsage` does not yet expose
110
+ * `cacheReadInputTokens` / `cacheWriteInputTokens`; cached requests are
111
+ * billed at the full `inputPer1k` rate today. Refining this is a phase
112
+ * 3.x follow-up that needs a `TokenUsage` widening.
113
+ * - **Tokenizer differences.** The default token estimator is
114
+ * `text.length / 4`. Provider-reported `usage.promptTokens` may differ
115
+ * by a few percent. Pass `estimateTokens: …` for a tiktoken-accurate
116
+ * pre-debit if your caps are tight.
117
+ */
118
+ export declare function withBudget(opts: WithBudgetOptions): AiMiddleware;
119
+ //# sourceMappingURL=with-budget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"with-budget.d.ts","sourceRoot":"","sources":["../../src/budget/with-budget.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,YAAY,EAEZ,iBAAiB,EAClB,MAAM,aAAa,CAAA;AACpB,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,cAAc,CAAA;AACrB,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACd,MAAM,cAAc,CAAA;AAErB;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAI,MAAM,GAAG,IAAI,CAAA;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,YAAY,CAAA;IACpB,gDAAgD;IAChD,KAAK,EAAG,MAAM,CAAA;IACd,GAAG,EAAK,MAAM,CAAA;IACd,GAAG,EAAK,iBAAiB,CAAA;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC;;;;;;OAMG;IACH,IAAI,CAAC,GAAG,EAAE,iBAAiB,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAA;IAE5F;;;;OAIG;IACH,MAAM,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,iBAAiB,CAAA;KAAE,GAAG,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;IAE1F;;;OAGG;IACH,OAAO,EAAE,aAAa,CAAA;IAEtB;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;IAEzC;;;;;;OAMG;IACH,UAAU,CAAC,CAAC,IAAI,EAAE,kBAAkB,GAAG,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,CAAA;IAE7D;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IAEjB;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;CAC1C;AAgBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,iBAAiB,GAAG,YAAY,CAmGhE"}