@aid-framework/runtime 0.0.0-stage → 0.1.39

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 (188) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +52 -2
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/agents/deep_agents.d.ts +78 -0
  5. package/dist/agents/deep_agents.d.ts.map +1 -0
  6. package/dist/agents/deep_agents.js +477 -0
  7. package/dist/agents/deep_agents.js.map +1 -0
  8. package/dist/agents/engine.d.ts +85 -0
  9. package/dist/agents/engine.d.ts.map +1 -0
  10. package/dist/agents/engine.js +129 -0
  11. package/dist/agents/engine.js.map +1 -0
  12. package/dist/agents/gateway_chat_model.d.ts +151 -0
  13. package/dist/agents/gateway_chat_model.d.ts.map +1 -0
  14. package/dist/agents/gateway_chat_model.js +350 -0
  15. package/dist/agents/gateway_chat_model.js.map +1 -0
  16. package/dist/agents/guardrail_middleware.d.ts +57 -0
  17. package/dist/agents/guardrail_middleware.d.ts.map +1 -0
  18. package/dist/agents/guardrail_middleware.js +85 -0
  19. package/dist/agents/guardrail_middleware.js.map +1 -0
  20. package/dist/agents/guardrails.d.ts +124 -0
  21. package/dist/agents/guardrails.d.ts.map +1 -0
  22. package/dist/agents/guardrails.js +336 -0
  23. package/dist/agents/guardrails.js.map +1 -0
  24. package/dist/agents/index.d.ts +15 -0
  25. package/dist/agents/index.d.ts.map +1 -0
  26. package/dist/agents/index.js +15 -0
  27. package/dist/agents/index.js.map +1 -0
  28. package/dist/agents/ir.d.ts +144 -0
  29. package/dist/agents/ir.d.ts.map +1 -0
  30. package/dist/agents/ir.js +513 -0
  31. package/dist/agents/ir.js.map +1 -0
  32. package/dist/agents/loop.d.ts +145 -0
  33. package/dist/agents/loop.d.ts.map +1 -0
  34. package/dist/agents/loop.js +1025 -0
  35. package/dist/agents/loop.js.map +1 -0
  36. package/dist/agents/memory.d.ts +115 -0
  37. package/dist/agents/memory.d.ts.map +1 -0
  38. package/dist/agents/memory.js +258 -0
  39. package/dist/agents/memory.js.map +1 -0
  40. package/dist/agents/state.d.ts +137 -0
  41. package/dist/agents/state.d.ts.map +1 -0
  42. package/dist/agents/state.js +255 -0
  43. package/dist/agents/state.js.map +1 -0
  44. package/dist/concurrency.d.ts +36 -0
  45. package/dist/concurrency.d.ts.map +1 -0
  46. package/dist/concurrency.js +86 -0
  47. package/dist/concurrency.js.map +1 -0
  48. package/dist/control.d.ts +116 -0
  49. package/dist/control.d.ts.map +1 -0
  50. package/dist/control.js +281 -0
  51. package/dist/control.js.map +1 -0
  52. package/dist/cost.d.ts +95 -0
  53. package/dist/cost.d.ts.map +1 -0
  54. package/dist/cost.js +161 -0
  55. package/dist/cost.js.map +1 -0
  56. package/dist/errors.d.ts +213 -0
  57. package/dist/errors.d.ts.map +1 -0
  58. package/dist/errors.js +316 -0
  59. package/dist/errors.js.map +1 -0
  60. package/dist/evals/dataset.d.ts +45 -0
  61. package/dist/evals/dataset.d.ts.map +1 -0
  62. package/dist/evals/dataset.js +147 -0
  63. package/dist/evals/dataset.js.map +1 -0
  64. package/dist/evals/gate.d.ts +236 -0
  65. package/dist/evals/gate.d.ts.map +1 -0
  66. package/dist/evals/gate.js +572 -0
  67. package/dist/evals/gate.js.map +1 -0
  68. package/dist/evals/metrics.d.ts +141 -0
  69. package/dist/evals/metrics.d.ts.map +1 -0
  70. package/dist/evals/metrics.js +364 -0
  71. package/dist/evals/metrics.js.map +1 -0
  72. package/dist/evals/predicate.d.ts +79 -0
  73. package/dist/evals/predicate.d.ts.map +1 -0
  74. package/dist/evals/predicate.js +178 -0
  75. package/dist/evals/predicate.js.map +1 -0
  76. package/dist/evals/runner.d.ts +94 -0
  77. package/dist/evals/runner.d.ts.map +1 -0
  78. package/dist/evals/runner.js +202 -0
  79. package/dist/evals/runner.js.map +1 -0
  80. package/dist/gateway.d.ts +118 -0
  81. package/dist/gateway.d.ts.map +1 -0
  82. package/dist/gateway.js +250 -0
  83. package/dist/gateway.js.map +1 -0
  84. package/dist/index.d.ts +83 -0
  85. package/dist/index.d.ts.map +1 -0
  86. package/dist/index.js +59 -0
  87. package/dist/index.js.map +1 -0
  88. package/dist/internal/json.d.ts +44 -0
  89. package/dist/internal/json.d.ts.map +1 -0
  90. package/dist/internal/json.js +90 -0
  91. package/dist/internal/json.js.map +1 -0
  92. package/dist/internal/text.d.ts +17 -0
  93. package/dist/internal/text.d.ts.map +1 -0
  94. package/dist/internal/text.js +22 -0
  95. package/dist/internal/text.js.map +1 -0
  96. package/dist/pipeline.d.ts +80 -0
  97. package/dist/pipeline.d.ts.map +1 -0
  98. package/dist/pipeline.js +159 -0
  99. package/dist/pipeline.js.map +1 -0
  100. package/dist/policy.d.ts +52 -0
  101. package/dist/policy.d.ts.map +1 -0
  102. package/dist/policy.js +199 -0
  103. package/dist/policy.js.map +1 -0
  104. package/dist/prompts.d.ts +86 -0
  105. package/dist/prompts.d.ts.map +1 -0
  106. package/dist/prompts.js +337 -0
  107. package/dist/prompts.js.map +1 -0
  108. package/dist/providers/base.d.ts +19 -0
  109. package/dist/providers/base.d.ts.map +1 -0
  110. package/dist/providers/base.js +13 -0
  111. package/dist/providers/base.js.map +1 -0
  112. package/dist/providers/fake.d.ts +98 -0
  113. package/dist/providers/fake.d.ts.map +1 -0
  114. package/dist/providers/fake.js +181 -0
  115. package/dist/providers/fake.js.map +1 -0
  116. package/dist/providers/http.d.ts +62 -0
  117. package/dist/providers/http.d.ts.map +1 -0
  118. package/dist/providers/http.js +245 -0
  119. package/dist/providers/http.js.map +1 -0
  120. package/dist/retrieval/chunking.d.ts +88 -0
  121. package/dist/retrieval/chunking.d.ts.map +1 -0
  122. package/dist/retrieval/chunking.js +188 -0
  123. package/dist/retrieval/chunking.js.map +1 -0
  124. package/dist/retrieval/citations.d.ts +98 -0
  125. package/dist/retrieval/citations.d.ts.map +1 -0
  126. package/dist/retrieval/citations.js +134 -0
  127. package/dist/retrieval/citations.js.map +1 -0
  128. package/dist/retrieval/connectors.d.ts +74 -0
  129. package/dist/retrieval/connectors.d.ts.map +1 -0
  130. package/dist/retrieval/connectors.js +218 -0
  131. package/dist/retrieval/connectors.js.map +1 -0
  132. package/dist/retrieval/embedding.d.ts +116 -0
  133. package/dist/retrieval/embedding.d.ts.map +1 -0
  134. package/dist/retrieval/embedding.js +282 -0
  135. package/dist/retrieval/embedding.js.map +1 -0
  136. package/dist/retrieval/gate.d.ts +52 -0
  137. package/dist/retrieval/gate.d.ts.map +1 -0
  138. package/dist/retrieval/gate.js +91 -0
  139. package/dist/retrieval/gate.js.map +1 -0
  140. package/dist/retrieval/hybrid.d.ts +76 -0
  141. package/dist/retrieval/hybrid.d.ts.map +1 -0
  142. package/dist/retrieval/hybrid.js +150 -0
  143. package/dist/retrieval/hybrid.js.map +1 -0
  144. package/dist/retrieval/metrics.d.ts +126 -0
  145. package/dist/retrieval/metrics.d.ts.map +1 -0
  146. package/dist/retrieval/metrics.js +200 -0
  147. package/dist/retrieval/metrics.js.map +1 -0
  148. package/dist/retrieval/pgvector.d.ts +62 -0
  149. package/dist/retrieval/pgvector.d.ts.map +1 -0
  150. package/dist/retrieval/pgvector.js +262 -0
  151. package/dist/retrieval/pgvector.js.map +1 -0
  152. package/dist/retrieval/rerank.d.ts +61 -0
  153. package/dist/retrieval/rerank.d.ts.map +1 -0
  154. package/dist/retrieval/rerank.js +105 -0
  155. package/dist/retrieval/rerank.js.map +1 -0
  156. package/dist/retrieval/retriever.d.ts +125 -0
  157. package/dist/retrieval/retriever.d.ts.map +1 -0
  158. package/dist/retrieval/retriever.js +281 -0
  159. package/dist/retrieval/retriever.js.map +1 -0
  160. package/dist/retrieval/stores.d.ts +106 -0
  161. package/dist/retrieval/stores.d.ts.map +1 -0
  162. package/dist/retrieval/stores.js +142 -0
  163. package/dist/retrieval/stores.js.map +1 -0
  164. package/dist/retrieval/types.d.ts +314 -0
  165. package/dist/retrieval/types.d.ts.map +1 -0
  166. package/dist/retrieval/types.js +388 -0
  167. package/dist/retrieval/types.js.map +1 -0
  168. package/dist/steps.d.ts +156 -0
  169. package/dist/steps.d.ts.map +1 -0
  170. package/dist/steps.js +199 -0
  171. package/dist/steps.js.map +1 -0
  172. package/dist/structured.d.ts +91 -0
  173. package/dist/structured.d.ts.map +1 -0
  174. package/dist/structured.js +155 -0
  175. package/dist/structured.js.map +1 -0
  176. package/dist/tools.d.ts +100 -0
  177. package/dist/tools.d.ts.map +1 -0
  178. package/dist/tools.js +201 -0
  179. package/dist/tools.js.map +1 -0
  180. package/dist/tracing.d.ts +104 -0
  181. package/dist/tracing.d.ts.map +1 -0
  182. package/dist/tracing.js +155 -0
  183. package/dist/tracing.js.map +1 -0
  184. package/dist/types.d.ts +240 -0
  185. package/dist/types.d.ts.map +1 -0
  186. package/dist/types.js +357 -0
  187. package/dist/types.js.map +1 -0
  188. package/package.json +77 -3
@@ -0,0 +1,281 @@
1
+ /**
2
+ * Composite pipeline steps: `branch`, `parallel`, `map` and `reduce`.
3
+ *
4
+ * {@link Pipeline} is a sequential reducer, and it stays one. Each factory here returns
5
+ * an ordinary {@link Step}, so a composite composes with the flat steps by the same rule
6
+ * and replays unchanged on the streaming route, which inlines the pipeline instead of
7
+ * driving it step by step.
8
+ *
9
+ * Six context keys are reserved for the family, and no step may bind a name equal to one
10
+ * of them: `principal`, `limiter`, `item`, `index`, `depth` and `reduced`. The first
11
+ * two are seeded by the application, the rest are written and removed by the composites
12
+ * themselves, so an inner step can read `item` without a matching binding leaking to a
13
+ * later sibling.
14
+ *
15
+ * Two bounds are enforced here and both fail the run rather than degrade it:
16
+ *
17
+ * - nesting, at {@link MAX_STEP_DEPTH}. The spec layer rejects a deeper pipeline before
18
+ * generation, so reaching this bound at run time means a hand-built pipeline, and the
19
+ * reason is named in the error.
20
+ * - concurrency, through the application's one {@link ConcurrencyLimiter}. A fan-out
21
+ * with no limiter in the context raises rather than running unbounded, because the cap
22
+ * is what makes a fan-out safe and its absence is a wiring bug.
23
+ *
24
+ * This file mirrors `aid_runtime/control.py`.
25
+ */
26
+ import { ConcurrencyLimiter, holdsSlot } from './concurrency.js';
27
+ import { PipelineError } from './errors.js';
28
+ import { repr } from './internal/json.js';
29
+ import { STEPS_KEY } from './pipeline.js';
30
+ import { evaluatePolicy, Principal, policySource } from './policy.js';
31
+ /**
32
+ * Deepest composite nesting a pipeline may declare.
33
+ *
34
+ * The spec layer owns this number and the generator *emits* it into the generated
35
+ * package, so a generated app passes its own build's value; this default is what a
36
+ * hand-written pipeline gets. A test reads the spec constant and holds the two
37
+ * together, so a drift fails instead of diverging silently.
38
+ */
39
+ export const MAX_STEP_DEPTH = 4;
40
+ /** Who the caller is, for `branch`. Seeded by the route. */
41
+ export const PRINCIPAL_KEY = 'principal';
42
+ /** The application's single limiter, from `deployments.concurrency`. */
43
+ export const LIMITER_KEY = 'limiter';
44
+ /** The current item and its position, written by `map`/`reduce` for one iteration. */
45
+ export const ITEM_KEY = 'item';
46
+ export const INDEX_KEY = 'index';
47
+ /** How many composites deep the current step is. */
48
+ export const DEPTH_KEY = 'depth';
49
+ /**
50
+ * The binding `reduce` collects from each iteration. Reserved so a reduce body's own
51
+ * binding cannot collide with the name the composite reads.
52
+ */
53
+ export const REDUCED_KEY = 'reduced';
54
+ /** Names a composite owns. A spec binding equal to one of them is refused upstream. */
55
+ export const RESERVED_KEYS = new Set([
56
+ PRINCIPAL_KEY,
57
+ LIMITER_KEY,
58
+ ITEM_KEY,
59
+ INDEX_KEY,
60
+ DEPTH_KEY,
61
+ REDUCED_KEY,
62
+ ]);
63
+ /**
64
+ * Name a composite's callable, so a trace and `Pipeline.of` identify it.
65
+ *
66
+ * `Pipeline` names a step by the callable's `name` and refuses two steps with the same
67
+ * name, so a pipeline holding two `branch` composites must give them distinct names. The
68
+ * generator passes one; the default is the kind.
69
+ */
70
+ function named(step, name) {
71
+ if (name !== undefined) {
72
+ Object.defineProperty(step, 'name', { value: name, configurable: true });
73
+ }
74
+ return step;
75
+ }
76
+ function depthOf(context) {
77
+ const value = context.get(DEPTH_KEY, 0);
78
+ return typeof value === 'number' && Number.isFinite(value) ? Math.trunc(value) : 0;
79
+ }
80
+ /** The context a nested step list runs in, or a named refusal past the bound. */
81
+ function enter(context, maxDepth, where) {
82
+ const depth = depthOf(context);
83
+ if (depth >= maxDepth) {
84
+ throw new PipelineError(`composite step ${where} is nested deeper than ${maxDepth} levels; ` +
85
+ 'nesting is bounded so a self-similar spec cannot recurse without end');
86
+ }
87
+ return context.withValues({ [DEPTH_KEY]: depth + 1 });
88
+ }
89
+ function limiterFor(context, where) {
90
+ const limiter = context.get(LIMITER_KEY);
91
+ if (!(limiter instanceof ConcurrencyLimiter)) {
92
+ throw new PipelineError(`step ${where} fans out but the pipeline context has no concurrency limiter; ` +
93
+ 'declare `concurrency` under a deployment in the spec so the generator seeds one');
94
+ }
95
+ return limiter;
96
+ }
97
+ /** Drop the iteration keys, so nothing a composite added reaches a later sibling. */
98
+ function escapeIteration(context) {
99
+ return context.without(DEPTH_KEY, ITEM_KEY, INDEX_KEY, REDUCED_KEY);
100
+ }
101
+ /**
102
+ * Run one iteration and return its context with only the bindings it added.
103
+ *
104
+ * Only the added names come back, so a composite hands its siblings the bindings the
105
+ * spec declared and nothing else. The inner run records its own `steps`; it is excluded
106
+ * here because the outer `Pipeline.run` writes that key itself when the composite
107
+ * returns, and an inner list would otherwise be read as a binding.
108
+ */
109
+ async function runBody(body, context) {
110
+ const result = await body.run(context);
111
+ const added = {};
112
+ for (const [key, value] of Object.entries(result.values)) {
113
+ if (!Object.hasOwn(context.values, key) && !RESERVED_KEYS.has(key) && key !== STEPS_KEY) {
114
+ added[key] = value;
115
+ }
116
+ }
117
+ return [result, added];
118
+ }
119
+ /**
120
+ * Run every worker, in the order given, whatever order they finish in.
121
+ *
122
+ * `Promise.allSettled` returns results positionally, so the caller's ordering rule is
123
+ * input order even when the last worker finishes first.
124
+ *
125
+ * A worker inside a slot this context already holds runs its siblings one at a time
126
+ * instead of acquiring a second slot: the limiter is shared with the gateway and the
127
+ * tool executor, so a worker that acquired again while its own body waited for the
128
+ * gateway would deadlock at a cap of one. Serialising keeps the cap and cannot deadlock,
129
+ * which is the trade this family makes at nesting depth two.
130
+ */
131
+ async function fanOut(workers) {
132
+ if (holdsSlot()) {
133
+ const serial = [];
134
+ for (const worker of workers)
135
+ serial.push(await worker());
136
+ return serial;
137
+ }
138
+ const settled = await Promise.allSettled(workers.map((worker) => worker()));
139
+ // Every worker is awaited even after one fails, so nothing is left running and the
140
+ // error that surfaces is the first in input order rather than the first to finish.
141
+ for (const entry of settled) {
142
+ if (entry.status === 'rejected')
143
+ throw entry.reason;
144
+ }
145
+ return settled.map((entry) => entry.value);
146
+ }
147
+ /**
148
+ * Run `then` when the caller's principal satisfies `when`, else `otherwise`.
149
+ *
150
+ * `when` is a `PolicyExpr` AST, evaluated against the principal and nothing else: the
151
+ * grammar has no operand for a context value, so a branch cannot read what an earlier
152
+ * step produced. It is a plain mapping rather than a parsed object so the generated code
153
+ * passes the AST the IR already carries, and {@link evaluatePolicy} stays the only
154
+ * evaluator.
155
+ *
156
+ * No principal in the context is an error, not a false. Defaulting would decide an
157
+ * authorization question for a caller nobody identified.
158
+ */
159
+ export function branch(options) {
160
+ const { when, then, otherwise, maxDepth = MAX_STEP_DEPTH, name } = options;
161
+ // Named `_branch` by default, matching the reference runtime, so `steps` reads alike.
162
+ const run = async function _branch(context) {
163
+ const principal = context.get(PRINCIPAL_KEY);
164
+ if (!(principal instanceof Principal)) {
165
+ throw new PipelineError(`branch needs a principal in the pipeline context to evaluate ${repr(policySource(when))}; none was seeded`);
166
+ }
167
+ const nested = enter(context, maxDepth, `branch (${policySource(when)})`);
168
+ const chosen = evaluatePolicy(when, principal) ? then : otherwise;
169
+ if (chosen === undefined) {
170
+ // No alternative declared: the branch binds nothing at all.
171
+ return escapeIteration(context);
172
+ }
173
+ const [, added] = await runBody(chosen, nested);
174
+ return escapeIteration(context.withValues(added));
175
+ };
176
+ return named(run, name);
177
+ }
178
+ /**
179
+ * Run every arm concurrently under the shared cap, then merge what they bound.
180
+ *
181
+ * A failing arm fails the whole run: the results are inspected in input order and the
182
+ * first failure is raised, so which error surfaces does not depend on which arm happened
183
+ * to fail first. Two arms binding the same new name is an error rather than a silent
184
+ * last-writer-wins, because the winning value would depend on completion order and the
185
+ * ordering rule exists to remove that.
186
+ */
187
+ export function parallel(options) {
188
+ const { branches, maxDepth = MAX_STEP_DEPTH, name } = options;
189
+ const run = async function _parallel(context) {
190
+ if (branches.length === 0)
191
+ return escapeIteration(context);
192
+ const limiter = limiterFor(context, 'parallel');
193
+ const nested = enter(context, maxDepth, 'parallel');
194
+ const workers = branches.map((arm) => () => limiter.run(() => runBody(arm, nested)));
195
+ const outcomes = await fanOut(workers);
196
+ const merged = {};
197
+ for (const [, added] of outcomes) {
198
+ for (const [key, value] of Object.entries(added)) {
199
+ if (Object.hasOwn(merged, key)) {
200
+ throw new PipelineError(`parallel arms both bind ${repr(key)}; a name may be bound by one arm`);
201
+ }
202
+ merged[key] = value;
203
+ }
204
+ }
205
+ return escapeIteration(context.withValues(merged));
206
+ };
207
+ return named(run, name);
208
+ }
209
+ /**
210
+ * Run `step` once per item of `over`, binding the results in input order.
211
+ *
212
+ * `as` holds one mapping of the names the body bound for that item, so an item that
213
+ * bound nothing contributes `{}` rather than vanishing: the list keeps the length of the
214
+ * input. An empty or absent-over value binds an empty list.
215
+ */
216
+ export function mapStep(options) {
217
+ const { over, as: as_, step, maxDepth = MAX_STEP_DEPTH, name } = options;
218
+ const run = async function _map(context) {
219
+ const items = itemsOf(context, over, 'map');
220
+ const limiter = limiterFor(context, 'map');
221
+ const nested = enter(context, maxDepth, `map over ${repr(over)}`);
222
+ const workers = items.map((item, index) => () => {
223
+ const iteration = nested.withValues({ [ITEM_KEY]: item, [INDEX_KEY]: index });
224
+ return limiter.run(() => runBody(step, iteration));
225
+ });
226
+ const outcomes = await fanOut(workers);
227
+ return escapeIteration(context.withValues({ [as_]: outcomes.map(([, added]) => added) }));
228
+ };
229
+ return named(run, name);
230
+ }
231
+ /**
232
+ * Run `step` once per item of `over`, binding its {@link REDUCED_KEY} results.
233
+ *
234
+ * `step` is a one-step pipeline that renders the reduce prompt for one item and binds
235
+ * `reduced`; the composite collects that binding into `as` in input order. Combining the
236
+ * results is a following `generate` step, so this layer never invents a merge strategy.
237
+ */
238
+ export function reduceStep(options) {
239
+ const { over, as: as_, step, maxDepth = MAX_STEP_DEPTH, name } = options;
240
+ const run = async function _reduce(context) {
241
+ const items = itemsOf(context, over, 'reduce');
242
+ const limiter = limiterFor(context, 'reduce');
243
+ const nested = enter(context, maxDepth, `reduce over ${repr(over)}`);
244
+ const workers = items.map((item, index) => () => {
245
+ const iteration = nested.withValues({ [ITEM_KEY]: item, [INDEX_KEY]: index });
246
+ return limiter.run(async () => {
247
+ const result = await step.run(iteration);
248
+ if (!Object.hasOwn(result.values, REDUCED_KEY)) {
249
+ throw new PipelineError(`reduce over ${repr(over)} produced no ${repr(REDUCED_KEY)} binding for item ${index}`);
250
+ }
251
+ return [result, { [REDUCED_KEY]: result.require(REDUCED_KEY) }];
252
+ });
253
+ });
254
+ const outcomes = await fanOut(workers);
255
+ return escapeIteration(context.withValues({ [as_]: outcomes.map(([, added]) => added[REDUCED_KEY]) }));
256
+ };
257
+ return named(run, name);
258
+ }
259
+ /** The sequence a `map`/`reduce` iterates, refusing a missing or odd value. */
260
+ function itemsOf(context, over, where) {
261
+ const value = context.require(over);
262
+ if (!Array.isArray(value)) {
263
+ throw new PipelineError(`${where} over ${repr(over)} needs a list, got ${typeName(value)}`);
264
+ }
265
+ return value;
266
+ }
267
+ /** The reference runtime's spelling of a type name, so both runtimes' errors read alike. */
268
+ function typeName(value) {
269
+ if (value === null)
270
+ return 'NoneType';
271
+ if (Array.isArray(value))
272
+ return 'list';
273
+ if (typeof value === 'string')
274
+ return 'str';
275
+ if (typeof value === 'number')
276
+ return Number.isInteger(value) ? 'int' : 'float';
277
+ if (typeof value === 'boolean')
278
+ return 'bool';
279
+ return 'dict';
280
+ }
281
+ //# sourceMappingURL=control.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control.js","sourceRoot":"","sources":["../src/control.ts"],"names":[],"mappings":"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"}
package/dist/cost.d.ts ADDED
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Cost accounting and budget enforcement.
3
+ *
4
+ * Cost is the budget this runtime enforces. A latency p95 budget is deliberately
5
+ * absent: it is only meaningful next to a dashboard, and a gate nobody reads is not
6
+ * a gate.
7
+ */
8
+ import { type ModelSpec, TokenUsage } from './types.js';
9
+ /** A spending ceiling for one scope (a run, a request, a nightly job). */
10
+ export declare class Budget {
11
+ readonly maxUsd: number | undefined;
12
+ readonly maxTotalTokens: number | undefined;
13
+ readonly scope: string;
14
+ constructor(init: {
15
+ readonly maxUsd?: number;
16
+ readonly maxTotalTokens?: number;
17
+ readonly scope?: string;
18
+ });
19
+ }
20
+ /** One charged model call. */
21
+ export declare class LedgerEntry {
22
+ readonly alias: string;
23
+ readonly model: string;
24
+ readonly provider: string;
25
+ readonly usage: TokenUsage;
26
+ readonly costUsd: number;
27
+ constructor(init: {
28
+ readonly alias: string;
29
+ readonly model: string;
30
+ readonly provider: string;
31
+ readonly usage: TokenUsage;
32
+ readonly costUsd: number;
33
+ });
34
+ }
35
+ /** Aggregate spend, suitable for a log line or an OTel span attribute. */
36
+ export declare class SpendSummary {
37
+ readonly calls: number;
38
+ readonly inputTokens: number;
39
+ readonly outputTokens: number;
40
+ readonly costUsd: number;
41
+ readonly byModel: Readonly<Record<string, number>>;
42
+ constructor(init: {
43
+ readonly calls: number;
44
+ readonly inputTokens: number;
45
+ readonly outputTokens: number;
46
+ readonly costUsd: number;
47
+ readonly byModel?: Record<string, number>;
48
+ });
49
+ get totalTokens(): number;
50
+ }
51
+ /** Price a call against a pinned model's published cost. */
52
+ export declare function estimateCostUsd(spec: ModelSpec, usage: TokenUsage): number;
53
+ /**
54
+ * The runtime's token estimate for a text: four characters per token.
55
+ *
56
+ * The same approximation `estimatePromptCostUsd` uses, exposed because agent memory has to
57
+ * agree with it: a transcript trimmed against one estimate and priced against another would
58
+ * report a budget it did not honour.
59
+ */
60
+ export declare function estimateTokens(text: string): number;
61
+ /**
62
+ * Rough pre-flight price for a prompt, used to fail before spending.
63
+ *
64
+ * Four characters per token is the usual English approximation. It is deliberately
65
+ * an approximation, and the same one `estimate_tokens` uses in retrieval, so an
66
+ * offline estimate and an offline charge cannot disagree about what a text costs.
67
+ */
68
+ export declare function estimatePromptCostUsd(spec: ModelSpec, text: string): number;
69
+ /**
70
+ * Accumulates spend and refuses to let a budget be crossed.
71
+ *
72
+ * The ledger is intentionally not concurrency-safe: a gateway owns one ledger per
73
+ * logical run, and sharing one across concurrent runs would make "spent so far"
74
+ * meaningless.
75
+ */
76
+ export declare class CostLedger {
77
+ private readonly _entries;
78
+ get entries(): readonly LedgerEntry[];
79
+ get costUsd(): number;
80
+ get totalTokens(): number;
81
+ /** Raise if current spend already violates `budget`. */
82
+ check(budget: Budget): void;
83
+ /**
84
+ * Record a call, throwing {@link BudgetExceededError} if it overshoots.
85
+ *
86
+ * The entry is recorded before the check so the ledger reflects what was actually
87
+ * spent when the error propagates: a caller that catches the error still sees
88
+ * truthful accounting.
89
+ */
90
+ charge(budget: Budget | undefined, entry: LedgerEntry): void;
91
+ summary(): SpendSummary;
92
+ private checkUsd;
93
+ private checkTokens;
94
+ }
95
+ //# sourceMappingURL=cost.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cost.d.ts","sourceRoot":"","sources":["../src/cost.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,KAAK,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AASxD,0EAA0E;AAC1E,qBAAa,MAAM;IACjB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAEvB,YAAY,IAAI,EAAE;QAChB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;QACjC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;KACzB,EAgBA;CACF;AAED,8BAA8B;AAC9B,qBAAa,WAAW;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,YAAY,IAAI,EAAE;QAChB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;QAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;KAC1B,EAMA;CACF;AAED,0EAA0E;AAC1E,qBAAa,YAAY;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAEnD,YAAY,IAAI,EAAE;QAChB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;QAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KAC3C,EAMA;IAED,IAAI,WAAW,IAAI,MAAM,CAExB;CACF;AAED,4DAA4D;AAC5D,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,GAAG,MAAM,CAE1E;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3E;AAED;;;;;;GAMG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;IAE9C,IAAI,OAAO,IAAI,SAAS,WAAW,EAAE,CAEpC;IAED,IAAI,OAAO,IAAI,MAAM,CAKpB;IAED,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,wDAAwD;IACxD,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAG1B;IAED;;;;;;OAMG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CAI3D;IAED,OAAO,IAAI,YAAY,CAYtB;IAED,OAAO,CAAC,QAAQ;IAQhB,OAAO,CAAC,WAAW;CAOpB"}
package/dist/cost.js ADDED
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Cost accounting and budget enforcement.
3
+ *
4
+ * Cost is the budget this runtime enforces. A latency p95 budget is deliberately
5
+ * absent: it is only meaningful next to a dashboard, and a gate nobody reads is not
6
+ * a gate.
7
+ */
8
+ import { BudgetExceededError } from './errors.js';
9
+ import { roundTo } from './internal/json.js';
10
+ import { TokenUsage } from './types.js';
11
+ /**
12
+ * Prices are quoted per 1k tokens, so a call costs fractions of a cent. Six
13
+ * decimals keeps a single call representable without letting float error accumulate
14
+ * visibly over a long run.
15
+ */
16
+ const USD_PRECISION = 6;
17
+ /** A spending ceiling for one scope (a run, a request, a nightly job). */
18
+ export class Budget {
19
+ maxUsd;
20
+ maxTotalTokens;
21
+ scope;
22
+ constructor(init) {
23
+ if (init.maxUsd === undefined && init.maxTotalTokens === undefined) {
24
+ throw new RangeError('a budget must set maxUsd, maxTotalTokens, or both');
25
+ }
26
+ if (init.maxUsd !== undefined && (!Number.isFinite(init.maxUsd) || init.maxUsd < 0)) {
27
+ throw new RangeError('maxUsd must be a non-negative number');
28
+ }
29
+ if (init.maxTotalTokens !== undefined &&
30
+ (!Number.isInteger(init.maxTotalTokens) || init.maxTotalTokens <= 0)) {
31
+ throw new RangeError('maxTotalTokens must be a positive integer');
32
+ }
33
+ this.maxUsd = init.maxUsd;
34
+ this.maxTotalTokens = init.maxTotalTokens;
35
+ this.scope = init.scope ?? 'run';
36
+ }
37
+ }
38
+ /** One charged model call. */
39
+ export class LedgerEntry {
40
+ alias;
41
+ model;
42
+ provider;
43
+ usage;
44
+ costUsd;
45
+ constructor(init) {
46
+ this.alias = init.alias;
47
+ this.model = init.model;
48
+ this.provider = init.provider;
49
+ this.usage = init.usage;
50
+ this.costUsd = init.costUsd;
51
+ }
52
+ }
53
+ /** Aggregate spend, suitable for a log line or an OTel span attribute. */
54
+ export class SpendSummary {
55
+ calls;
56
+ inputTokens;
57
+ outputTokens;
58
+ costUsd;
59
+ byModel;
60
+ constructor(init) {
61
+ this.calls = init.calls;
62
+ this.inputTokens = init.inputTokens;
63
+ this.outputTokens = init.outputTokens;
64
+ this.costUsd = init.costUsd;
65
+ this.byModel = init.byModel ?? {};
66
+ }
67
+ get totalTokens() {
68
+ return this.inputTokens + this.outputTokens;
69
+ }
70
+ }
71
+ /** Price a call against a pinned model's published cost. */
72
+ export function estimateCostUsd(spec, usage) {
73
+ return spec.cost.costUsd(usage);
74
+ }
75
+ /**
76
+ * The runtime's token estimate for a text: four characters per token.
77
+ *
78
+ * The same approximation `estimatePromptCostUsd` uses, exposed because agent memory has to
79
+ * agree with it: a transcript trimmed against one estimate and priced against another would
80
+ * report a budget it did not honour.
81
+ */
82
+ export function estimateTokens(text) {
83
+ return (text.length + 3) >> 2;
84
+ }
85
+ /**
86
+ * Rough pre-flight price for a prompt, used to fail before spending.
87
+ *
88
+ * Four characters per token is the usual English approximation. It is deliberately
89
+ * an approximation, and the same one `estimate_tokens` uses in retrieval, so an
90
+ * offline estimate and an offline charge cannot disagree about what a text costs.
91
+ */
92
+ export function estimatePromptCostUsd(spec, text) {
93
+ return spec.cost.costUsd(new TokenUsage({ inputTokens: estimateTokens(text) }));
94
+ }
95
+ /**
96
+ * Accumulates spend and refuses to let a budget be crossed.
97
+ *
98
+ * The ledger is intentionally not concurrency-safe: a gateway owns one ledger per
99
+ * logical run, and sharing one across concurrent runs would make "spent so far"
100
+ * meaningless.
101
+ */
102
+ export class CostLedger {
103
+ _entries = [];
104
+ get entries() {
105
+ return this._entries;
106
+ }
107
+ get costUsd() {
108
+ return roundTo(this._entries.reduce((total, entry) => total + entry.costUsd, 0), USD_PRECISION);
109
+ }
110
+ get totalTokens() {
111
+ return this._entries.reduce((total, entry) => total + entry.usage.totalTokens, 0);
112
+ }
113
+ /** Raise if current spend already violates `budget`. */
114
+ check(budget) {
115
+ this.checkUsd(budget);
116
+ this.checkTokens(budget);
117
+ }
118
+ /**
119
+ * Record a call, throwing {@link BudgetExceededError} if it overshoots.
120
+ *
121
+ * The entry is recorded before the check so the ledger reflects what was actually
122
+ * spent when the error propagates: a caller that catches the error still sees
123
+ * truthful accounting.
124
+ */
125
+ charge(budget, entry) {
126
+ this._entries.push(entry);
127
+ if (budget === undefined)
128
+ return;
129
+ this.check(budget);
130
+ }
131
+ summary() {
132
+ const byModel = {};
133
+ for (const entry of this._entries) {
134
+ byModel[entry.model] = roundTo((byModel[entry.model] ?? 0) + entry.costUsd, USD_PRECISION);
135
+ }
136
+ return new SpendSummary({
137
+ calls: this._entries.length,
138
+ inputTokens: this._entries.reduce((total, entry) => total + entry.usage.inputTokens, 0),
139
+ outputTokens: this._entries.reduce((total, entry) => total + entry.usage.outputTokens, 0),
140
+ costUsd: this.costUsd,
141
+ byModel,
142
+ });
143
+ }
144
+ checkUsd(budget) {
145
+ if (budget.maxUsd === undefined)
146
+ return;
147
+ const spent = this.costUsd;
148
+ if (spent > budget.maxUsd) {
149
+ throw new BudgetExceededError(budget.scope, budget.maxUsd, spent);
150
+ }
151
+ }
152
+ checkTokens(budget) {
153
+ if (budget.maxTotalTokens === undefined)
154
+ return;
155
+ const spentTokens = this.totalTokens;
156
+ if (spentTokens > budget.maxTotalTokens) {
157
+ throw new BudgetExceededError(budget.scope, budget.maxTotalTokens, spentTokens, 'tokens');
158
+ }
159
+ }
160
+ }
161
+ //# sourceMappingURL=cost.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cost.js","sourceRoot":"","sources":["../src/cost.ts"],"names":[],"mappings":"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"}