@voltro/ai 0.28.0 → 0.30.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.
@@ -0,0 +1,447 @@
1
+ import { a as e, r as t } from "./provider-BOC5wBxx.js";
2
+ import { Output as n, generateObject as r, generateText as i, jsonSchema as a, stepCountIs as o, tool as s } from "ai";
3
+ import { Context as c, Effect as l, JSONSchema as u, Metric as d, MetricBoundaries as ee, Runtime as f, Schema as p, Stream as m } from "effect";
4
+ import { and as h, eq as g, generateId as te, gte as ne, id as _, integer as v, json as y, queryFor as b, registerRetention as x, retentionTtlMsFromEnv as S, table as C, text as w, timestamp as T } from "@voltro/database";
5
+ import { createHash as re } from "node:crypto";
6
+ //#region src/tools.ts
7
+ var ie = (e, t) => e(t), E = Symbol.for("@voltro/ai/Tool"), ae = (e) => ({
8
+ ...e,
9
+ [E]: !0
10
+ }), oe = (e) => typeof e == "object" && !!e && e[E] === !0, se = (e) => {
11
+ let t = e.default;
12
+ if (typeof t != "function") return 0;
13
+ let n = 0;
14
+ for (let r of Object.values(e)) oe(r) && r.execute === void 0 && r.handler === void 0 && (r.handler = t, n += 1);
15
+ return n;
16
+ }, D = (e, t) => {
17
+ let n = t ?? f.defaultRuntime;
18
+ return f.runPromise(n)(e);
19
+ }, O = (e, t, n) => n ? e.pipe(l.timeoutFail({
20
+ duration: `${n} millis`,
21
+ onTimeout: () => /* @__PURE__ */ Error(`tool '${t}' timed out`)
22
+ })) : e, ce = (e, t, n) => {
23
+ if (e.execute) return D(O(ie(e.execute, t), e.name, e.timeoutMs), n.runtime);
24
+ if (e.handler) {
25
+ let r = e.handler, i = r(t, n);
26
+ return l.isEffect(i) ? D(O(i, e.name, e.timeoutMs), n.runtime) : i;
27
+ }
28
+ return Promise.reject(/* @__PURE__ */ Error(`tool '${e.name}' has neither execute nor handler`));
29
+ }, le = (e) => {
30
+ let t = u.make(e);
31
+ return t && t.type === "object" ? t : {
32
+ type: "object",
33
+ properties: {},
34
+ additionalProperties: !1
35
+ };
36
+ }, k = (e, t = { store: void 0 }) => {
37
+ let n = {};
38
+ for (let [r, i] of Object.entries(e)) n[i.name || r] = s({
39
+ description: i.description,
40
+ inputSchema: a(le(i.input)),
41
+ execute: async (e) => {
42
+ let n = await l.runPromise(p.decodeUnknown(i.input)(e)), r = await ce(i, n, t);
43
+ return i.output ? l.runPromise(p.decodeUnknown(i.output)(r)) : r;
44
+ }
45
+ });
46
+ return n;
47
+ }, A = {
48
+ "claude-opus-4-8": {
49
+ inputPer1M: 15,
50
+ outputPer1M: 75
51
+ },
52
+ "claude-opus-4-6": {
53
+ inputPer1M: 15,
54
+ outputPer1M: 75
55
+ },
56
+ "claude-sonnet-4-6": {
57
+ inputPer1M: 3,
58
+ outputPer1M: 15
59
+ },
60
+ "claude-haiku-4-5": {
61
+ inputPer1M: 1,
62
+ outputPer1M: 5
63
+ },
64
+ "claude-haiku-4-5-20251001": {
65
+ inputPer1M: 1,
66
+ outputPer1M: 5
67
+ },
68
+ "gpt-5.5": {
69
+ inputPer1M: 5,
70
+ outputPer1M: 30
71
+ },
72
+ "gpt-4o": {
73
+ inputPer1M: 2.5,
74
+ outputPer1M: 10
75
+ },
76
+ "gpt-4o-mini": {
77
+ inputPer1M: .15,
78
+ outputPer1M: .6
79
+ },
80
+ "gemini-2.5-pro": {
81
+ inputPer1M: 1.25,
82
+ outputPer1M: 10
83
+ },
84
+ "gemini-2.5-flash": {
85
+ inputPer1M: .3,
86
+ outputPer1M: 2.5
87
+ },
88
+ "gemini-2.0-flash": {
89
+ inputPer1M: .1,
90
+ outputPer1M: .4
91
+ }
92
+ }, j = {}, ue = (e) => {
93
+ let t = j;
94
+ return j = { ...e }, t;
95
+ }, M = () => ({
96
+ ...A,
97
+ ...j
98
+ }), N = (e) => {
99
+ let t = M();
100
+ if (t[e] !== void 0) return t[e];
101
+ let n = e.indexOf("/");
102
+ if (n > 0) {
103
+ let r = e.slice(n + 1);
104
+ if (t[r] !== void 0) return t[r];
105
+ }
106
+ }, P = (e, t) => {
107
+ let n = e.inputTokens ?? 0, r = e.outputTokens ?? 0;
108
+ if (t.actualCostUsd !== void 0) {
109
+ let e = n + r, i = e > 0 ? n / e : 0;
110
+ return {
111
+ inputTokens: n,
112
+ outputTokens: r,
113
+ inputCostUsd: t.actualCostUsd * i,
114
+ outputCostUsd: t.actualCostUsd * (1 - i),
115
+ totalCostUsd: t.actualCostUsd,
116
+ costSource: "gateway"
117
+ };
118
+ }
119
+ let i = t.price ?? N(t.model) ?? {
120
+ inputPer1M: 0,
121
+ outputPer1M: 0
122
+ }, a = n / 1e6 * i.inputPer1M, o = r / 1e6 * i.outputPer1M;
123
+ return {
124
+ inputTokens: n,
125
+ outputTokens: r,
126
+ inputCostUsd: a,
127
+ outputCostUsd: o,
128
+ totalCostUsd: a + o,
129
+ costSource: "estimated"
130
+ };
131
+ }, F = "_voltro_ai_usage", I = C(F, {
132
+ id: _({ prefix: "aiu" }),
133
+ tenantId: w().nullable(),
134
+ provider: w(),
135
+ model: w(),
136
+ operation: w(),
137
+ agent: w().nullable(),
138
+ inputTokens: v().default(0),
139
+ outputTokens: v().default(0),
140
+ costMicroUsd: v().default(0),
141
+ costSource: w().default("estimated"),
142
+ calledAt: T().default("now")
143
+ }).index(["tenantId"]), de = (e) => {
144
+ let t = e?.gateway?.cost, n = typeof t == "number" ? t : typeof t == "string" ? Number(t) : NaN;
145
+ return Number.isFinite(n) ? n : void 0;
146
+ }, fe = (e, t) => (x({
147
+ table: F,
148
+ timeColumn: "calledAt",
149
+ ttlMs: S(process.env.VOLTRO_AI_USAGE_TTL_HOURS, 24 * 365)
150
+ }), l.promise(async () => {
151
+ let n = P(t.usage, {
152
+ model: t.model,
153
+ ...t.price === void 0 ? {} : { price: t.price },
154
+ ...t.actualCostUsd === void 0 ? {} : { actualCostUsd: t.actualCostUsd }
155
+ });
156
+ return await e.insert(F, {
157
+ tenantId: t.tenantId ?? null,
158
+ provider: t.provider,
159
+ model: t.model,
160
+ operation: t.operation,
161
+ agent: t.agent ?? null,
162
+ inputTokens: n.inputTokens,
163
+ outputTokens: n.outputTokens,
164
+ costMicroUsd: Math.round(n.totalCostUsd * 1e6),
165
+ costSource: n.costSource,
166
+ calledAt: t.calledAt ?? /* @__PURE__ */ new Date()
167
+ }), n;
168
+ })), pe = (e, t = {}) => l.promise(async () => {
169
+ let n = [...t.tenantId === void 0 ? [] : [g("tenantId", t.tenantId)], ...t.since === void 0 ? [] : [ne("calledAt", t.since)]], r = b(I), i = n.length === 0 ? r : r.where(n.length === 1 ? n[0] : h(...n));
170
+ return (await e.query(i.descriptor)).reduce((e, t) => e + (Number(t.costMicroUsd) || 0), 0) / 1e6;
171
+ }), L = class extends p.TaggedError()("AiBudgetExceeded", {
172
+ tenantId: p.NullOr(p.String),
173
+ limitUsd: p.Number,
174
+ spentUsd: p.Number,
175
+ attemptedUsd: p.Number
176
+ }) {}, R = "_voltro_ai_budget", z = C(R, {
177
+ id: _({ prefix: "aib" }),
178
+ bucket: w().maxLength(191),
179
+ tenantId: w().nullable(),
180
+ reservedMicroUsd: v().default(0),
181
+ updatedAt: T().default("now")
182
+ }).unique("byAiBudgetBucket", ["bucket"]), me = (e, t) => `${e ?? "global"}::${t === void 0 ? "all" : String(t.getTime())}`, B = 32, he = (e, t) => l.gen(function* () {
183
+ x({
184
+ table: R,
185
+ timeColumn: "updatedAt",
186
+ ttlMs: S(process.env.VOLTRO_AI_USAGE_TTL_HOURS, 24 * 365)
187
+ });
188
+ let n = t.addUsd ?? 0, r = Math.round(n * 1e6), i = Math.round(t.limitUsd * 1e6), a = me(t.tenantId, t.since), o = t.tenantId ?? null, s = (e) => new L({
189
+ tenantId: o,
190
+ limitUsd: t.limitUsd,
191
+ spentUsd: e / 1e6,
192
+ attemptedUsd: n
193
+ }), c = yield* l.promise(async () => {
194
+ for (let t = 0; t < B; t++) {
195
+ t > 0 && await new Promise((e) => setTimeout(e, t));
196
+ let n = (await e.query(b(z).where(g("bucket", a)).descriptor))[0], s = n && Number(n.reservedMicroUsd) || 0;
197
+ if (s + r > i) return {
198
+ ok: !1,
199
+ reservedMicro: s
200
+ };
201
+ if (r === 0) return { ok: !0 };
202
+ if (n) {
203
+ if (await e.updateMany("_voltro_ai_budget", {
204
+ reservedMicroUsd: s + r,
205
+ updatedAt: /* @__PURE__ */ new Date()
206
+ }, { where: h(g("id", String(n.id)), g("reservedMicroUsd", s)) }) === 1) return { ok: !0 };
207
+ } else {
208
+ let t = te({
209
+ kind: "typeid",
210
+ prefix: "aib"
211
+ }, R), n = await e.insertIgnore(R, {
212
+ id: t,
213
+ bucket: a,
214
+ tenantId: o,
215
+ reservedMicroUsd: r,
216
+ updatedAt: /* @__PURE__ */ new Date()
217
+ }, { conflictColumns: ["bucket"] });
218
+ if (String(n.id) === t) return { ok: !0 };
219
+ }
220
+ }
221
+ throw Error(`requireAiBudget: contention on bucket '${a}' exceeded ${B} attempts`);
222
+ });
223
+ if (!c.ok) return yield* l.fail(s(c.reservedMicro));
224
+ }), ge = ee.exponential({
225
+ start: .001,
226
+ factor: 2,
227
+ count: 16
228
+ }), V = d.counter("voltro_ai_calls_total", { description: "Total AI provider calls (generate + stream)." }), H = d.counter("voltro_ai_call_errors_total", { description: "AI provider calls that errored." }), U = d.histogram("voltro_ai_call_duration_seconds", ge, "AI provider call duration in seconds."), _e = d.counter("voltro_ai_input_tokens_total", {
229
+ description: "Total prompt (input) tokens across AI calls.",
230
+ incremental: !0
231
+ }), ve = d.counter("voltro_ai_output_tokens_total", {
232
+ description: "Total completion (output) tokens across AI calls.",
233
+ incremental: !0
234
+ }), ye = d.counter("voltro_ai_cost_microusd_total", {
235
+ description: "Total estimated AI spend in micro-USD (USD × 1e6).",
236
+ incremental: !0
237
+ }), W = (e, t) => ({
238
+ provider: e.name,
239
+ model: e.model ?? e.name,
240
+ operation: t
241
+ }), G = (e, t, n) => e.pipe(l.tagMetrics("provider", t.provider), l.tagMetrics("model", t.model), l.tagMetrics("operation", t.operation), l.tagMetrics("status", n)), K = (e, t) => {
242
+ let n = t.inputTokens ?? 0, r = t.outputTokens ?? 0, i = P({
243
+ inputTokens: n,
244
+ outputTokens: r
245
+ }, { model: e.model }), a = Math.round(i.totalCostUsd * 1e6);
246
+ return l.all([
247
+ G(d.incrementBy(_e, n), e, "ok"),
248
+ G(d.incrementBy(ve, r), e, "ok"),
249
+ G(d.incrementBy(ye, a), e, "ok")
250
+ ], { discard: !0 });
251
+ }, q = (e, t, n) => l.gen(function* () {
252
+ let r = yield* l.clockWith((e) => e.currentTimeMillis);
253
+ return yield* n.pipe(l.tap((n) => K(e, t(n))), l.tap(() => G(d.increment(V), e, "ok").pipe(l.zipRight(l.clockWith((e) => e.currentTimeMillis).pipe(l.flatMap((t) => G(d.update(U, (t - r) / 1e3), e, "ok")))))), l.tapError(() => G(d.increment(V), e, "error").pipe(l.zipRight(G(d.increment(H), e, "error")), l.zipRight(l.clockWith((e) => e.currentTimeMillis).pipe(l.flatMap((t) => G(d.update(U, (t - r) / 1e3), e, "error")))))), l.withSpan("voltro.ai.call", { attributes: {
254
+ "ai.provider": e.provider,
255
+ "ai.model": e.model,
256
+ "ai.operation": e.operation
257
+ } }));
258
+ }), be = (e, t) => {
259
+ let n = Date.now();
260
+ return t.pipe(m.tap((t) => t._tag === "done" ? G(d.increment(V), e, "ok").pipe(l.zipRight(G(d.update(U, (Date.now() - n) / 1e3), e, "ok")), l.zipRight(K(e, t.usage ?? {}))) : t._tag === "error" ? G(d.increment(V), e, "error").pipe(l.zipRight(G(d.increment(H), e, "error")), l.zipRight(G(d.update(U, (Date.now() - n) / 1e3), e, "error"))) : l.void), m.withSpan("voltro.ai.call", { attributes: {
261
+ "ai.provider": e.provider,
262
+ "ai.model": e.model,
263
+ "ai.operation": e.operation
264
+ } }));
265
+ }, J = class extends p.TaggedError()("AiError", {
266
+ reason: p.Literal("generation", "decode"),
267
+ message: p.String
268
+ }) {}, Y = (e, t, n) => {
269
+ let r = [e, ...t ?? []], i = (e) => n(r[e]).pipe(l.catchTag("AiError", (t) => t.reason === "generation" && e + 1 < r.length ? i(e + 1) : l.fail(t)));
270
+ return i(0);
271
+ }, X = (n) => Y(n.provider ?? t(), n.fallbacks, (t) => q(W(t, "generateText"), (e) => e.usage, l.tryPromise({
272
+ try: async () => {
273
+ let r = await i({
274
+ model: e(t),
275
+ prompt: n.prompt,
276
+ ...n.system === void 0 ? {} : { system: n.system },
277
+ ...n.maxTokens === void 0 ? {} : { maxOutputTokens: n.maxTokens },
278
+ ...n.providerOptions === void 0 ? {} : { providerOptions: n.providerOptions }
279
+ });
280
+ return {
281
+ text: r.text,
282
+ usage: {
283
+ inputTokens: r.usage.inputTokens,
284
+ outputTokens: r.usage.outputTokens,
285
+ totalTokens: r.usage.totalTokens
286
+ },
287
+ providerMetadata: r.providerMetadata
288
+ };
289
+ },
290
+ catch: (e) => new J({
291
+ reason: "generation",
292
+ message: String(e?.message ?? e)
293
+ })
294
+ }))), Z = (e, t) => l.gen(function* () {
295
+ let n = u.make(e);
296
+ if (n?.type !== "object") {
297
+ let e = n?.anyOf === void 0 ? `type:${JSON.stringify(n?.type ?? null)}` : "anyOf (e.g. Schema.Struct({}) or a union)";
298
+ return yield* l.fail(new J({
299
+ reason: "generation",
300
+ message: `${t}: the output schema must render as a top-level JSON object (type:"object"), but got ${e}. Use a Schema.Struct({ ... }) as the output schema — wrap arrays/scalars in a field (e.g. Schema.Struct({ items: Schema.Array(...) })).`
301
+ }));
302
+ }
303
+ return n;
304
+ }), xe = (e, t) => Z(e, t), Q = (n) => Y(n.provider ?? t(), n.fallbacks, (t) => q(W(t, "generateObject"), (e) => e.usage, l.gen(function* () {
305
+ let i = e(t), o = yield* l.tryPromise({
306
+ try: () => r({
307
+ model: i,
308
+ schema: a(n.jsonSchema),
309
+ prompt: n.prompt,
310
+ ...n.system === void 0 ? {} : { system: n.system },
311
+ ...n.maxTokens === void 0 ? {} : { maxOutputTokens: n.maxTokens },
312
+ ...n.providerOptions === void 0 ? {} : { providerOptions: n.providerOptions }
313
+ }),
314
+ catch: (e) => new J({
315
+ reason: "generation",
316
+ message: String(e?.message ?? e)
317
+ })
318
+ });
319
+ return {
320
+ object: o.object,
321
+ usage: {
322
+ inputTokens: o.usage.inputTokens,
323
+ outputTokens: o.usage.outputTokens,
324
+ totalTokens: o.usage.totalTokens
325
+ },
326
+ providerMetadata: o.providerMetadata
327
+ };
328
+ }))), Se = (e) => l.gen(function* () {
329
+ let t = yield* Q({
330
+ jsonSchema: yield* Z(e.schema, "generateObject"),
331
+ prompt: e.prompt,
332
+ ...e.system === void 0 ? {} : { system: e.system },
333
+ ...e.provider === void 0 ? {} : { provider: e.provider },
334
+ ...e.fallbacks === void 0 ? {} : { fallbacks: e.fallbacks },
335
+ ...e.maxTokens === void 0 ? {} : { maxTokens: e.maxTokens },
336
+ ...e.providerOptions === void 0 ? {} : { providerOptions: e.providerOptions }
337
+ }), n = yield* p.decodeUnknown(e.schema)(t.object).pipe(l.mapError((e) => new J({
338
+ reason: "decode",
339
+ message: `model output failed schema decode: ${String(e)}`
340
+ })));
341
+ return {
342
+ ...t,
343
+ object: n
344
+ };
345
+ }), Ce = (r) => l.gen(function* () {
346
+ let s = yield* l.runtime(), c = yield* Z(r.schema, "generateObjectWithTools"), u = {
347
+ store: void 0,
348
+ ...r.toolContext,
349
+ runtime: s
350
+ }, d = k(r.tools, u);
351
+ return yield* Y(r.provider ?? t(), r.fallbacks, (t) => q(W(t, "generateObjectWithTools"), (e) => e.usage, l.gen(function* () {
352
+ let s = e(t), u = yield* l.tryPromise({
353
+ try: () => i({
354
+ model: s,
355
+ prompt: r.prompt,
356
+ tools: d,
357
+ stopWhen: o(r.maxSteps ?? 8),
358
+ output: n.object({ schema: a(c) }),
359
+ ...r.system === void 0 ? {} : { system: r.system },
360
+ ...r.maxTokens === void 0 ? {} : { maxOutputTokens: r.maxTokens },
361
+ ...r.providerOptions === void 0 ? {} : { providerOptions: r.providerOptions }
362
+ }),
363
+ catch: (e) => new J({
364
+ reason: "generation",
365
+ message: String(e?.message ?? e)
366
+ })
367
+ });
368
+ return {
369
+ object: yield* p.decodeUnknown(r.schema)(u.output).pipe(l.mapError((e) => new J({
370
+ reason: "decode",
371
+ message: `model output failed schema decode: ${String(e)}`
372
+ }))),
373
+ usage: {
374
+ inputTokens: u.usage.inputTokens,
375
+ outputTokens: u.usage.outputTokens,
376
+ totalTokens: u.usage.totalTokens
377
+ },
378
+ providerMetadata: u.providerMetadata
379
+ };
380
+ })));
381
+ }), we = class extends c.Tag("@voltro/AiService")() {}, Te = {
382
+ generateText: (e) => X(e),
383
+ generateObject: (e) => Se(e)
384
+ }, Ee = "_voltro_ai_inferences", De = C(Ee, {
385
+ id: _({ prefix: "aiinf" }),
386
+ tenantId: w().nullable(),
387
+ executionId: w(),
388
+ workflowName: w(),
389
+ runId: w().nullable(),
390
+ signalName: w(),
391
+ stepName: w(),
392
+ status: w().oneOf([
393
+ "pending",
394
+ "running",
395
+ "succeeded",
396
+ "failed"
397
+ ]).default("pending"),
398
+ kind: w().oneOf(["text", "object"]).default("text"),
399
+ provider: w(),
400
+ model: w(),
401
+ prompt: w(),
402
+ system: w().nullable(),
403
+ maxTokens: v().nullable(),
404
+ providerOptions: y().nullable(),
405
+ jsonSchema: y().nullable(),
406
+ result: y().nullable(),
407
+ errorMessage: w().nullable(),
408
+ attempts: v().default(0),
409
+ leaseUntil: T().nullable(),
410
+ claimedBy: w().nullable(),
411
+ enqueuedAt: T().default("now"),
412
+ startedAt: T().nullable(),
413
+ completedAt: T().nullable(),
414
+ durationMs: v().nullable()
415
+ }).index(["status", "enqueuedAt"]).index(["status", "leaseUntil"]).index(["executionId"]).index(["tenantId"]), Oe = 5 * 6e4, ke = 4, Ae = (e, t) => {
416
+ let n = typeof e == "number" ? e : Number(e);
417
+ return Number.isFinite(n) ? Math.trunc(n) : t;
418
+ }, $ = (e) => e == null ? null : String(e), je = (e) => ({
419
+ id: String(e.id),
420
+ tenantId: $(e.tenantId),
421
+ executionId: String(e.executionId),
422
+ workflowName: String(e.workflowName),
423
+ runId: $(e.runId),
424
+ signalName: String(e.signalName),
425
+ stepName: String(e.stepName),
426
+ kind: e.kind === "object" ? "object" : "text",
427
+ provider: String(e.provider),
428
+ model: String(e.model),
429
+ prompt: String(e.prompt ?? ""),
430
+ system: $(e.system),
431
+ maxTokens: e.maxTokens === null || e.maxTokens === void 0 ? null : Ae(e.maxTokens, 0),
432
+ providerOptions: e.providerOptions ?? null,
433
+ jsonSchema: e.jsonSchema ?? null,
434
+ attempts: Ae(e.attempts, 0)
435
+ }), Me = (e) => {
436
+ let t = e.attempts + 1;
437
+ return t >= e.maxAttempts ? {
438
+ kind: "give-up",
439
+ attempts: t
440
+ } : {
441
+ kind: "retry",
442
+ attempts: t,
443
+ delayMs: e.retryAfterMs !== void 0 && e.retryAfterMs > 0 ? e.retryAfterMs : Math.min(e.baseDelayMs * 2 ** (t - 1), e.maxDelayMs)
444
+ };
445
+ }, Ne = (e) => `voltro.ai.infer:${e}`, Pe = (e, t) => `aiinf_${re("sha256").update(`${e}\u0000${t}`).digest("hex").slice(0, 40)}`;
446
+ //#endregion
447
+ export { N as A, A as C, P as D, I as E, ae as F, oe as I, se as L, he as M, ue as N, de as O, k as P, L as S, pe as T, W as _, Me as a, R as b, je as c, Te as d, Se as f, xe as g, X as h, De as i, fe as j, M as k, J as l, Ce as m, ke as n, Ne as o, Q as p, Oe as r, Pe as s, Ee as t, we as u, be as v, z as w, F as x, q as y };