@voltro/ai 0.85.0 → 0.86.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +666 -0
- package/THIRD-PARTY-NOTICES.md +5 -33
- package/dist/agent.js +64 -2
- package/dist/agentConstants.js +4 -0
- package/dist/agentExecutor.js +51 -0
- package/dist/api.js +199 -0
- package/dist/appTools.js +80 -0
- package/dist/budget.js +201 -0
- package/dist/callEvidence.js +51 -0
- package/dist/copilot.js +126 -0
- package/dist/cost.js +242 -0
- package/dist/embed.js +41 -0
- package/dist/embeddingProvider.js +51 -0
- package/dist/eval.js +130 -0
- package/dist/events.d.ts +4 -0
- package/dist/events.js +81 -2
- package/dist/executableModalities.js +13 -0
- package/dist/gateway.js +58 -0
- package/dist/gatewayCatalog.js +47 -0
- package/dist/gatewayMedia.js +88 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +42 -2750
- package/dist/inferenceDispatch.js +493 -0
- package/dist/inferenceQueue.js +96 -0
- package/dist/jsonOutputValidation.js +27 -0
- package/dist/mcpClient.js +332 -0
- package/dist/mcpTools.js +80 -0
- package/dist/media.js +182 -0
- package/dist/mediaBytes.js +19 -0
- package/dist/mediaCall.js +13 -0
- package/dist/mediaTaskProvider.js +16 -0
- package/dist/middleware.js +32 -0
- package/dist/observe.js +52 -0
- package/dist/prompts.js +103 -0
- package/dist/provider.js +303 -0
- package/dist/realtimeEvent.js +97 -0
- package/dist/realtimeSession.js +154 -0
- package/dist/rerank.js +61 -0
- package/dist/resumableStream.js +321 -0
- package/dist/retrieveReranked.js +16 -0
- package/dist/runAndPersist.js +148 -0
- package/dist/semanticCache.js +66 -0
- package/dist/speechProvider.js +16 -0
- package/dist/streamRegistry.js +14 -0
- package/dist/streamText.js +170 -0
- package/dist/streamTranscribe.js +74 -0
- package/dist/streamWakeups.js +44 -0
- package/dist/structuredEvidence.js +48 -0
- package/dist/test.js +1 -1
- package/dist/threads.js +92 -0
- package/dist/tools.js +68 -0
- package/dist/transcribe.js +64 -0
- package/dist/transcriptionEvent.js +46 -0
- package/dist/workflow.d.ts +2 -0
- package/dist/workflow.js +115 -110
- package/package.json +5 -5
- package/dist/agentDescriptor-DhoiXVqO.js +0 -63
- package/dist/agentEvent-Bl4egdM5.js +0 -211
- package/dist/inferenceQueue-DcEulZep.js +0 -1077
- package/dist/provider-BnemgE4D.js +0 -409
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
import { gatewayCostUsd as e, recordAiUsageBestEffort as t } from "./cost.js";
|
|
2
|
+
import { generateObjectJson as n, generateText as r } from "./api.js";
|
|
3
|
+
import { mediaCall as i } from "./mediaCall.js";
|
|
4
|
+
import { generateImage as a, generateSpeech as o, generateVideo as s } from "./media.js";
|
|
5
|
+
import { transcribe as c } from "./transcribe.js";
|
|
6
|
+
import { mediaTaskProvider as l } from "./mediaTaskProvider.js";
|
|
7
|
+
import { AI_INFERENCES_TABLE as u, decideInferenceFailure as d, toQueuedInference as f } from "./inferenceQueue.js";
|
|
8
|
+
import { and as p, eq as m, gt as h, gte as g, lt as _, lte as v, or as y, registerRetention as b, retentionTtlMsFromEnv as x } from "@voltro/database";
|
|
9
|
+
//#region src/inferenceDispatch.ts
|
|
10
|
+
var S = 4, C = 250, w = 5e3, T = 36e5, E = {
|
|
11
|
+
examined: !1,
|
|
12
|
+
claimed: 0,
|
|
13
|
+
succeeded: 0,
|
|
14
|
+
failed: 0,
|
|
15
|
+
retried: 0,
|
|
16
|
+
reclaimed: 0,
|
|
17
|
+
polled: 0,
|
|
18
|
+
failures: []
|
|
19
|
+
}, D = (e) => {
|
|
20
|
+
let t = e instanceof Error ? e.message : String(e);
|
|
21
|
+
return t.length > 1e3 ? `${t.slice(0, 1e3)}…` : t;
|
|
22
|
+
}, O = (e) => {
|
|
23
|
+
let t = e;
|
|
24
|
+
if (typeof t?.retryAfterMillis == "number") return t.retryAfterMillis;
|
|
25
|
+
if (typeof t?.retryAfter == "number") return t.retryAfter * 1e3;
|
|
26
|
+
}, k = (e) => {
|
|
27
|
+
if (typeof e != "object" || !e) return;
|
|
28
|
+
let t = e;
|
|
29
|
+
return t.__voltro_inference_delivery === 1 && (t.status === "succeeded" || t.status === "failed") && typeof t.attempts == "number" && "payload" in t && (t.errorMessage === null || typeof t.errorMessage == "string") ? e : void 0;
|
|
30
|
+
}, A = (e) => y(p(m("status", "pending"), v("enqueuedAt", new Date(e))), p(m("status", "running"), _("leaseUntil", new Date(e)))), j = 720, M = () => {
|
|
31
|
+
b({
|
|
32
|
+
source: "framework",
|
|
33
|
+
table: u,
|
|
34
|
+
timeColumn: "completedAt",
|
|
35
|
+
ttlMs: x(process.env.VOLTRO_AI_INFERENCES_TTL_HOURS, 720),
|
|
36
|
+
envVar: "VOLTRO_AI_INFERENCES_TTL_HOURS",
|
|
37
|
+
label: "offloaded inference queue"
|
|
38
|
+
});
|
|
39
|
+
}, N = async (e, t = {}) => {
|
|
40
|
+
M();
|
|
41
|
+
let n = e.now?.() ?? Date.now(), r = t.batch ?? 4, i = t.leaseMs ?? 3e5, a = t.maxAttempts ?? 4, o = e.replicaId ?? `pid-${process.pid}`, s;
|
|
42
|
+
try {
|
|
43
|
+
s = await e.store.query({
|
|
44
|
+
table: u,
|
|
45
|
+
predicate: A(n),
|
|
46
|
+
order: [{
|
|
47
|
+
column: "enqueuedAt",
|
|
48
|
+
direction: "asc"
|
|
49
|
+
}],
|
|
50
|
+
take: r,
|
|
51
|
+
skip: void 0,
|
|
52
|
+
projection: void 0
|
|
53
|
+
});
|
|
54
|
+
} catch (e) {
|
|
55
|
+
return {
|
|
56
|
+
...E,
|
|
57
|
+
examined: !0,
|
|
58
|
+
failures: [{
|
|
59
|
+
id: u,
|
|
60
|
+
detail: D(e)
|
|
61
|
+
}]
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
if (s.length === 0) return {
|
|
65
|
+
...E,
|
|
66
|
+
examined: !0,
|
|
67
|
+
...await P(e.store, n)
|
|
68
|
+
};
|
|
69
|
+
let c = [], h = 0, g = 0, y = 0, b = 0, x = 0, S = 0, C = s.map((r) => async () => {
|
|
70
|
+
let s = f(r), C = String(r.status) === "running", w = s.fence + 1, T = 0;
|
|
71
|
+
try {
|
|
72
|
+
T = await e.store.updateMany(u, {
|
|
73
|
+
status: "running",
|
|
74
|
+
leaseUntil: new Date(n + i),
|
|
75
|
+
claimedBy: o,
|
|
76
|
+
startedAt: new Date(n),
|
|
77
|
+
fence: w
|
|
78
|
+
}, { where: p(m("id", s.id), C ? p(m("status", "running"), _("leaseUntil", new Date(n))) : p(m("status", "pending"), v("enqueuedAt", new Date(n)))) });
|
|
79
|
+
} catch (e) {
|
|
80
|
+
c.push({
|
|
81
|
+
id: s.id,
|
|
82
|
+
detail: D(e)
|
|
83
|
+
});
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (T !== 1) return;
|
|
87
|
+
h += 1, C && (x += 1);
|
|
88
|
+
let E = async (t) => {
|
|
89
|
+
let n = await e.store.updateMany(u, t, { where: p(m("id", s.id), m("fence", w), m("status", "running")) });
|
|
90
|
+
return n !== 1 && c.push({
|
|
91
|
+
id: s.id,
|
|
92
|
+
detail: `a stale worker (fence ${w}) tried to write; the row has moved on`
|
|
93
|
+
}), n === 1;
|
|
94
|
+
}, A = Date.now(), j = async (r) => {
|
|
95
|
+
c.push({
|
|
96
|
+
id: s.id,
|
|
97
|
+
detail: D(r)
|
|
98
|
+
}), e.log?.warn("ai.infer: recovery deferred without a new submission", {
|
|
99
|
+
id: s.id,
|
|
100
|
+
model: s.model,
|
|
101
|
+
attempts: s.attempts,
|
|
102
|
+
detail: D(r)
|
|
103
|
+
}), await E({ leaseUntil: new Date(n + (t.baseDelayMs ?? 1e3)) }).catch(() => !1) && b++;
|
|
104
|
+
}, M = async (t, n = !1) => {
|
|
105
|
+
try {
|
|
106
|
+
if (!n && !await E({
|
|
107
|
+
result: t,
|
|
108
|
+
errorMessage: t.errorMessage
|
|
109
|
+
}) || (await e.resume({
|
|
110
|
+
executionId: s.executionId,
|
|
111
|
+
workflowName: s.workflowName,
|
|
112
|
+
signalName: s.signalName,
|
|
113
|
+
payload: t.payload
|
|
114
|
+
}), !await E({
|
|
115
|
+
status: t.status,
|
|
116
|
+
result: t.payload,
|
|
117
|
+
attempts: t.attempts,
|
|
118
|
+
errorMessage: t.errorMessage,
|
|
119
|
+
completedAt: /* @__PURE__ */ new Date(),
|
|
120
|
+
durationMs: Date.now() - A,
|
|
121
|
+
leaseUntil: null
|
|
122
|
+
}))) return;
|
|
123
|
+
t.status === "failed" ? (y++, e.log?.warn("ai.infer: offloaded call gave up", {
|
|
124
|
+
id: s.id,
|
|
125
|
+
model: s.model,
|
|
126
|
+
attempts: t.attempts,
|
|
127
|
+
detail: t.errorMessage,
|
|
128
|
+
outcome: t.payload.outcome
|
|
129
|
+
})) : (g++, e.log?.info("ai.infer: offloaded call completed", {
|
|
130
|
+
id: s.id,
|
|
131
|
+
model: s.model,
|
|
132
|
+
durationMs: Date.now() - A,
|
|
133
|
+
recoveredDelivery: n
|
|
134
|
+
}));
|
|
135
|
+
} catch (e) {
|
|
136
|
+
await j(e);
|
|
137
|
+
}
|
|
138
|
+
}, N = async (e, t, n) => {
|
|
139
|
+
c.push({
|
|
140
|
+
id: s.id,
|
|
141
|
+
detail: e
|
|
142
|
+
}), await M({
|
|
143
|
+
__voltro_inference_delivery: 1,
|
|
144
|
+
status: "failed",
|
|
145
|
+
attempts: t,
|
|
146
|
+
errorMessage: e,
|
|
147
|
+
payload: {
|
|
148
|
+
__voltro_ai_error: !0,
|
|
149
|
+
message: e,
|
|
150
|
+
attempts: t,
|
|
151
|
+
outcome: n
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}, P = async (e) => {
|
|
155
|
+
await M({
|
|
156
|
+
__voltro_inference_delivery: 1,
|
|
157
|
+
status: "succeeded",
|
|
158
|
+
payload: e,
|
|
159
|
+
attempts: s.attempts,
|
|
160
|
+
errorMessage: null
|
|
161
|
+
});
|
|
162
|
+
}, I = s.kind === "media" ? l(s.provider) : void 0, L = !1;
|
|
163
|
+
try {
|
|
164
|
+
let n = k(r.result);
|
|
165
|
+
if (n !== void 0) {
|
|
166
|
+
await M(n, !0);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (s.kind === "media" && e.persistMedia === void 0) {
|
|
170
|
+
await N(`inference ${s.id}: no media persistence is wired on this host`, s.attempts, "failed");
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (C && s.kind === "media" && s.taskHandle === null && I?.idempotentSubmit !== !0) {
|
|
174
|
+
await N(`media job ${s.id} was claimed by a worker that was lost before it reported; ` + (I === void 0 ? "the provider has no job lookup, so its outcome is unknown and the job is not submitted again" : "no task handle had reached the row, so its outcome is unknown and the job is not submitted again"), s.attempts, "unknown");
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (I !== void 0) {
|
|
178
|
+
let n = await z(e, {
|
|
179
|
+
...s,
|
|
180
|
+
fence: w
|
|
181
|
+
}, I, E, {
|
|
182
|
+
pollMs: t.taskPollMs ?? 5e3,
|
|
183
|
+
timeoutMs: t.taskTimeoutMs ?? 36e5
|
|
184
|
+
});
|
|
185
|
+
if (n.kind === "stale") return;
|
|
186
|
+
if (n.kind === "pending") {
|
|
187
|
+
await E({
|
|
188
|
+
status: "pending",
|
|
189
|
+
leaseUntil: null,
|
|
190
|
+
enqueuedAt: new Date(Date.now() + n.delayMs)
|
|
191
|
+
}).catch(() => !1) && (S += 1);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (n.kind === "failed") {
|
|
195
|
+
if (!n.retryable) {
|
|
196
|
+
await N(n.detail, s.attempts + 1, n.outcome ?? "failed");
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
throw L = !0, Error(n.detail);
|
|
200
|
+
}
|
|
201
|
+
await P(n.payload);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
await P((await F(e, {
|
|
205
|
+
...s,
|
|
206
|
+
fence: w
|
|
207
|
+
})).payload);
|
|
208
|
+
} catch (e) {
|
|
209
|
+
if (s.kind === "media" && !L) {
|
|
210
|
+
await j(e);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
let n = d({
|
|
214
|
+
attempts: s.attempts,
|
|
215
|
+
maxAttempts: a,
|
|
216
|
+
baseDelayMs: t.baseDelayMs ?? 1e3,
|
|
217
|
+
maxDelayMs: t.maxDelayMs ?? 3e4,
|
|
218
|
+
retryAfterMs: O(e)
|
|
219
|
+
}), r = D(e);
|
|
220
|
+
if (c.push({
|
|
221
|
+
id: s.id,
|
|
222
|
+
detail: r
|
|
223
|
+
}), n.kind === "retry") {
|
|
224
|
+
await E({
|
|
225
|
+
status: "pending",
|
|
226
|
+
attempts: n.attempts,
|
|
227
|
+
errorMessage: r,
|
|
228
|
+
leaseUntil: null,
|
|
229
|
+
enqueuedAt: new Date(Date.now() + n.delayMs),
|
|
230
|
+
taskHandle: null,
|
|
231
|
+
taskSubmittedAt: null
|
|
232
|
+
}).catch(() => !1) && (b += 1);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
c.pop(), await N(r, n.attempts, "failed");
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
await Promise.all(C.map((e) => e()));
|
|
239
|
+
let w = h === 0 ? await P(e.store, n) : {};
|
|
240
|
+
return {
|
|
241
|
+
examined: !0,
|
|
242
|
+
claimed: h,
|
|
243
|
+
succeeded: g,
|
|
244
|
+
failed: y,
|
|
245
|
+
retried: b,
|
|
246
|
+
reclaimed: x,
|
|
247
|
+
polled: S,
|
|
248
|
+
failures: c,
|
|
249
|
+
...w
|
|
250
|
+
};
|
|
251
|
+
}, P = async (e, t) => {
|
|
252
|
+
let n = async (t, n) => {
|
|
253
|
+
let [r] = await e.query({
|
|
254
|
+
table: u,
|
|
255
|
+
predicate: t,
|
|
256
|
+
order: [{
|
|
257
|
+
column: n,
|
|
258
|
+
direction: "asc"
|
|
259
|
+
}],
|
|
260
|
+
take: 1,
|
|
261
|
+
skip: void 0,
|
|
262
|
+
projection: void 0
|
|
263
|
+
}), i = r?.[n], a = i instanceof Date ? i.getTime() : typeof i == "string" || typeof i == "number" ? new Date(i).getTime() : NaN;
|
|
264
|
+
return Number.isFinite(a) ? a : void 0;
|
|
265
|
+
};
|
|
266
|
+
try {
|
|
267
|
+
let [e, r] = await Promise.all([n(p(m("status", "pending"), h("enqueuedAt", new Date(t))), "enqueuedAt"), n(p(m("status", "running"), g("leaseUntil", new Date(t))), "leaseUntil")]), i = [e, r === void 0 ? void 0 : r + 1].filter((e) => e !== void 0);
|
|
268
|
+
return { nextDueAt: i.length === 0 ? null : Math.min(...i) };
|
|
269
|
+
} catch {
|
|
270
|
+
return {};
|
|
271
|
+
}
|
|
272
|
+
}, F = async (e, t) => {
|
|
273
|
+
let i = {
|
|
274
|
+
name: t.provider,
|
|
275
|
+
model: t.model
|
|
276
|
+
}, a = {
|
|
277
|
+
prompt: t.prompt,
|
|
278
|
+
provider: i,
|
|
279
|
+
...t.system === null ? {} : { system: t.system },
|
|
280
|
+
...t.maxTokens === null ? {} : { maxTokens: t.maxTokens },
|
|
281
|
+
...t.providerOptions !== null && t.providerOptions !== void 0 ? { providerOptions: t.providerOptions } : {}
|
|
282
|
+
};
|
|
283
|
+
if (t.kind === "transcription") {
|
|
284
|
+
let n = t.transcription;
|
|
285
|
+
if (n === null) throw Error(`inference ${t.id} is kind 'transcription' but carries no source`);
|
|
286
|
+
let r = await e.run(c({
|
|
287
|
+
...n,
|
|
288
|
+
provider: i,
|
|
289
|
+
...e.resolveTranscriptionRef === void 0 ? {} : { resolveRef: e.resolveTranscriptionRef }
|
|
290
|
+
}));
|
|
291
|
+
return await B(e, t, "transcribe", { usage: {} }), { payload: r };
|
|
292
|
+
}
|
|
293
|
+
if (t.kind === "media") return { payload: await I(e, t, i) };
|
|
294
|
+
if (t.kind === "object") {
|
|
295
|
+
if (t.jsonSchema === null) throw Error(`inference ${t.id} is kind 'object' but carries no jsonSchema`);
|
|
296
|
+
let r = await e.run(n({
|
|
297
|
+
...a,
|
|
298
|
+
jsonSchema: t.jsonSchema
|
|
299
|
+
}));
|
|
300
|
+
return await B(e, t, "generateObject", r), { payload: r.object };
|
|
301
|
+
}
|
|
302
|
+
let o = await e.run(r(a));
|
|
303
|
+
return await B(e, t, "generateText", o), { payload: {
|
|
304
|
+
text: o.text,
|
|
305
|
+
inputTokens: o.usage.inputTokens ?? null,
|
|
306
|
+
outputTokens: o.usage.outputTokens ?? null,
|
|
307
|
+
totalTokens: o.usage.totalTokens ?? null
|
|
308
|
+
} };
|
|
309
|
+
}, I = async (e, t, n) => {
|
|
310
|
+
let r = t.media;
|
|
311
|
+
if (r === null) throw Error(`inference ${t.id} is kind 'media' but carries no job`);
|
|
312
|
+
let i = L(e, t), c = r.params;
|
|
313
|
+
if (r.modality === "image") {
|
|
314
|
+
let o = await e.run(a({
|
|
315
|
+
prompt: t.prompt,
|
|
316
|
+
provider: n,
|
|
317
|
+
...c,
|
|
318
|
+
...Object.keys(r.inputs).length > 0 ? { inputs: r.inputs } : {}
|
|
319
|
+
}));
|
|
320
|
+
await B(e, t, "generateImage", {
|
|
321
|
+
usage: o.usage ?? {},
|
|
322
|
+
providerMetadata: o.providerMetadata
|
|
323
|
+
});
|
|
324
|
+
let s = [];
|
|
325
|
+
for (let [e, t] of o.images.entries()) s.push(await i(e, t));
|
|
326
|
+
return {
|
|
327
|
+
artifacts: s,
|
|
328
|
+
usage: o.usage ?? null,
|
|
329
|
+
providerMetadata: o.providerMetadata ?? null
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
if (r.modality === "video") {
|
|
333
|
+
let a = await e.run(s({
|
|
334
|
+
prompt: t.prompt,
|
|
335
|
+
provider: n,
|
|
336
|
+
params: c,
|
|
337
|
+
...Object.keys(r.inputs).length > 0 ? { inputs: r.inputs } : {}
|
|
338
|
+
}));
|
|
339
|
+
return await B(e, t, "generateVideo", {
|
|
340
|
+
usage: a.usage ?? {},
|
|
341
|
+
providerMetadata: a.providerMetadata
|
|
342
|
+
}), {
|
|
343
|
+
artifacts: [await i(0, a.video)],
|
|
344
|
+
usage: a.usage ?? null,
|
|
345
|
+
providerMetadata: a.providerMetadata ?? null
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
let l = await e.run(o({
|
|
349
|
+
text: t.prompt,
|
|
350
|
+
provider: n,
|
|
351
|
+
...c
|
|
352
|
+
}));
|
|
353
|
+
return await B(e, t, "generateSpeech", {
|
|
354
|
+
usage: l.usage ?? {},
|
|
355
|
+
providerMetadata: l.providerMetadata
|
|
356
|
+
}), {
|
|
357
|
+
artifacts: [await i(0, l.audio)],
|
|
358
|
+
usage: l.usage ?? null,
|
|
359
|
+
providerMetadata: l.providerMetadata ?? null
|
|
360
|
+
};
|
|
361
|
+
}, L = (e, t) => {
|
|
362
|
+
let n = t.media?.persist ?? {}, r = e.persistMedia;
|
|
363
|
+
if (r === void 0) throw Error(`inference ${t.id} is a media job, but no media persistence is wired: install the storage plugin (it provides the mediaPersist capability) or pass persistMedia to the dispatcher`);
|
|
364
|
+
let i = (e) => `${t.id}#${t.attempts}#${e}`;
|
|
365
|
+
return async (e, a) => {
|
|
366
|
+
if (a.url !== void 0) {
|
|
367
|
+
if (r.ingestUrl === void 0) return {
|
|
368
|
+
url: a.url,
|
|
369
|
+
refId: null,
|
|
370
|
+
mediaType: a.mediaType
|
|
371
|
+
};
|
|
372
|
+
let o = await r.ingestUrl({
|
|
373
|
+
...n,
|
|
374
|
+
url: a.url,
|
|
375
|
+
tenantId: t.tenantId,
|
|
376
|
+
operationId: i(e)
|
|
377
|
+
});
|
|
378
|
+
return {
|
|
379
|
+
url: o.url,
|
|
380
|
+
refId: o.refId,
|
|
381
|
+
mediaType: a.mediaType
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
if (a.data === void 0) throw Error(`inference ${t.id}: the provider returned an artifact with neither bytes nor a URL`);
|
|
385
|
+
let o = typeof a.data == "string" ? Uint8Array.from(Buffer.from(a.data, "base64")) : a.data, s = await r.put({
|
|
386
|
+
...n,
|
|
387
|
+
bytes: o,
|
|
388
|
+
mediaType: a.mediaType,
|
|
389
|
+
tenantId: t.tenantId,
|
|
390
|
+
operationId: i(e)
|
|
391
|
+
});
|
|
392
|
+
return {
|
|
393
|
+
url: s.url,
|
|
394
|
+
refId: s.refId,
|
|
395
|
+
mediaType: a.mediaType
|
|
396
|
+
};
|
|
397
|
+
};
|
|
398
|
+
}, R = (e) => {
|
|
399
|
+
if (typeof e != "object" || !e || Array.isArray(e)) return !1;
|
|
400
|
+
try {
|
|
401
|
+
return typeof JSON.stringify(e) == "string";
|
|
402
|
+
} catch {
|
|
403
|
+
return !1;
|
|
404
|
+
}
|
|
405
|
+
}, z = async (e, t, n, r, a) => {
|
|
406
|
+
let o = t.media;
|
|
407
|
+
if (o === null) throw Error(`inference ${t.id} is kind 'media' but carries no job`);
|
|
408
|
+
let s = L(e, t), c = {
|
|
409
|
+
modality: o.modality,
|
|
410
|
+
prompt: t.prompt,
|
|
411
|
+
...t.model === "unknown" ? {} : { model: t.model },
|
|
412
|
+
params: o.params,
|
|
413
|
+
inputs: o.inputs,
|
|
414
|
+
...t.providerOptions !== null && t.providerOptions !== void 0 ? { providerOptions: t.providerOptions } : {},
|
|
415
|
+
tenantId: t.tenantId,
|
|
416
|
+
idempotencyKey: `${t.id}#${t.attempts}`
|
|
417
|
+
}, l = t.taskHandle, u = t.taskSubmittedAt;
|
|
418
|
+
if (u !== null && Date.now() - u.getTime() > a.timeoutMs) return {
|
|
419
|
+
kind: "failed",
|
|
420
|
+
retryable: !1,
|
|
421
|
+
detail: `media task ${t.id} is still pending after ${Math.round(a.timeoutMs / 1e3)}s at provider '${t.provider}'; the run is not kept waiting past the task timeout`
|
|
422
|
+
};
|
|
423
|
+
if (l === null) {
|
|
424
|
+
if (u === null && (u = /* @__PURE__ */ new Date(), !await r({ taskSubmittedAt: u }))) return { kind: "stale" };
|
|
425
|
+
let a = await e.run(i((e) => n.submit({
|
|
426
|
+
...c,
|
|
427
|
+
abortSignal: e
|
|
428
|
+
})));
|
|
429
|
+
if (!R(a)) return {
|
|
430
|
+
kind: "failed",
|
|
431
|
+
retryable: !1,
|
|
432
|
+
outcome: "unknown",
|
|
433
|
+
detail: `media task provider '${t.provider}' returned a task handle that cannot be stored as JSON (a plain object is required); the job cannot be found again`
|
|
434
|
+
};
|
|
435
|
+
if (!await r({
|
|
436
|
+
taskHandle: a,
|
|
437
|
+
taskSubmittedAt: u
|
|
438
|
+
})) return { kind: "stale" };
|
|
439
|
+
l = a;
|
|
440
|
+
}
|
|
441
|
+
if (u !== null && Date.now() - u.getTime() > a.timeoutMs) return {
|
|
442
|
+
kind: "failed",
|
|
443
|
+
retryable: !1,
|
|
444
|
+
detail: `media task ${t.id} is still pending after ${Math.round(a.timeoutMs / 1e3)}s at provider '${t.provider}'; the run is not kept waiting past the task timeout`
|
|
445
|
+
};
|
|
446
|
+
let d = await e.run(i((e) => n.poll(l, {
|
|
447
|
+
...c,
|
|
448
|
+
abortSignal: e
|
|
449
|
+
})));
|
|
450
|
+
if (d.status === "pending") return {
|
|
451
|
+
kind: "pending",
|
|
452
|
+
delayMs: Math.max(a.pollMs, d.retryAfterMs ?? 0)
|
|
453
|
+
};
|
|
454
|
+
if (d.status === "failed") return {
|
|
455
|
+
kind: "failed",
|
|
456
|
+
retryable: d.retryable === !0,
|
|
457
|
+
detail: `media task provider '${t.provider}' failed the job: ${d.message}`
|
|
458
|
+
};
|
|
459
|
+
await B(e, t, `generate${o.modality[0].toUpperCase()}${o.modality.slice(1)}`, {
|
|
460
|
+
usage: d.usage ?? {},
|
|
461
|
+
providerMetadata: d.providerMetadata
|
|
462
|
+
});
|
|
463
|
+
let f = [];
|
|
464
|
+
for (let [e, t] of d.artifacts.entries()) f.push(await s(e, t));
|
|
465
|
+
return {
|
|
466
|
+
kind: "done",
|
|
467
|
+
payload: {
|
|
468
|
+
artifacts: f,
|
|
469
|
+
usage: d.usage ?? null,
|
|
470
|
+
providerMetadata: d.providerMetadata ?? null
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
}, B = async (n, r, i, a) => {
|
|
474
|
+
let o = e(a.providerMetadata);
|
|
475
|
+
await n.run(t(n.store, {
|
|
476
|
+
provider: r.provider,
|
|
477
|
+
model: r.model,
|
|
478
|
+
operation: i,
|
|
479
|
+
agent: r.stepName,
|
|
480
|
+
requestId: r.id,
|
|
481
|
+
attempt: r.attempts,
|
|
482
|
+
usage: a.usage,
|
|
483
|
+
...r.tenantId === null ? {} : { tenantId: r.tenantId },
|
|
484
|
+
...o === void 0 ? {} : { actualCostUsd: o },
|
|
485
|
+
...r.promptId !== null && r.promptDigest !== null ? { prompt: {
|
|
486
|
+
promptId: r.promptId,
|
|
487
|
+
digest: r.promptDigest,
|
|
488
|
+
...r.promptRevision === null ? {} : { revision: r.promptRevision }
|
|
489
|
+
} } : {}
|
|
490
|
+
}));
|
|
491
|
+
};
|
|
492
|
+
//#endregion
|
|
493
|
+
export { S as DEFAULT_INFERENCE_CONCURRENCY, C as DEFAULT_INFERENCE_POLL_MS, j as DEFAULT_INFERENCE_RETENTION_HOURS, w as DEFAULT_MEDIA_TASK_POLL_MS, T as DEFAULT_MEDIA_TASK_TIMEOUT_MS, N as dispatchInferences };
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { id as e, integer as t, json as n, table as r, text as i, timestamp as a } from "@voltro/database";
|
|
2
|
+
import { createHash as o } from "node:crypto";
|
|
3
|
+
//#region src/inferenceQueue.ts
|
|
4
|
+
var s = "_voltro_ai_inferences", c = r(s, {
|
|
5
|
+
id: e({ prefix: "aiinf" }),
|
|
6
|
+
tenantId: i().nullable(),
|
|
7
|
+
executionId: i(),
|
|
8
|
+
workflowName: i(),
|
|
9
|
+
runId: i().nullable(),
|
|
10
|
+
signalName: i(),
|
|
11
|
+
stepName: i(),
|
|
12
|
+
status: i().oneOf([
|
|
13
|
+
"pending",
|
|
14
|
+
"running",
|
|
15
|
+
"succeeded",
|
|
16
|
+
"failed"
|
|
17
|
+
]).default("pending"),
|
|
18
|
+
kind: i().oneOf([
|
|
19
|
+
"text",
|
|
20
|
+
"object",
|
|
21
|
+
"transcription",
|
|
22
|
+
"media"
|
|
23
|
+
]).default("text"),
|
|
24
|
+
provider: i(),
|
|
25
|
+
model: i(),
|
|
26
|
+
prompt: i(),
|
|
27
|
+
system: i().nullable(),
|
|
28
|
+
promptId: i().nullable(),
|
|
29
|
+
promptDigest: i().nullable(),
|
|
30
|
+
promptRevision: t().nullable(),
|
|
31
|
+
maxTokens: t().nullable(),
|
|
32
|
+
providerOptions: n().nullable(),
|
|
33
|
+
jsonSchema: n().nullable(),
|
|
34
|
+
transcription: n().nullable(),
|
|
35
|
+
media: n().nullable(),
|
|
36
|
+
taskHandle: n().nullable(),
|
|
37
|
+
taskSubmittedAt: a().nullable(),
|
|
38
|
+
result: n().nullable(),
|
|
39
|
+
errorMessage: i().nullable(),
|
|
40
|
+
attempts: t().default(0),
|
|
41
|
+
fence: t().default(0),
|
|
42
|
+
leaseUntil: a().nullable(),
|
|
43
|
+
claimedBy: i().nullable(),
|
|
44
|
+
queuedAt: a().nullable(),
|
|
45
|
+
enqueuedAt: a().default("now"),
|
|
46
|
+
startedAt: a().nullable(),
|
|
47
|
+
completedAt: a().nullable(),
|
|
48
|
+
durationMs: t().nullable()
|
|
49
|
+
}).index(["status", "enqueuedAt"]).index(["status", "leaseUntil"]).index(["executionId"]).index(["tenantId"]), l = 3e5, u = 4, d = (e, t) => {
|
|
50
|
+
let n = typeof e == "number" ? e : Number(e);
|
|
51
|
+
return Number.isFinite(n) ? Math.trunc(n) : t;
|
|
52
|
+
}, f = (e) => e == null ? null : String(e), p = (e) => {
|
|
53
|
+
if (e instanceof Date) return e;
|
|
54
|
+
if (typeof e == "string" || typeof e == "number") {
|
|
55
|
+
let t = new Date(e);
|
|
56
|
+
return Number.isNaN(t.getTime()) ? null : t;
|
|
57
|
+
}
|
|
58
|
+
return null;
|
|
59
|
+
}, m = (e) => ({
|
|
60
|
+
id: String(e.id),
|
|
61
|
+
tenantId: f(e.tenantId),
|
|
62
|
+
executionId: String(e.executionId),
|
|
63
|
+
workflowName: String(e.workflowName),
|
|
64
|
+
runId: f(e.runId),
|
|
65
|
+
signalName: String(e.signalName),
|
|
66
|
+
stepName: String(e.stepName),
|
|
67
|
+
kind: e.kind === "object" ? "object" : e.kind === "transcription" ? "transcription" : e.kind === "media" ? "media" : "text",
|
|
68
|
+
provider: String(e.provider),
|
|
69
|
+
model: String(e.model),
|
|
70
|
+
prompt: String(e.prompt ?? ""),
|
|
71
|
+
system: f(e.system),
|
|
72
|
+
promptId: f(e.promptId),
|
|
73
|
+
promptDigest: f(e.promptDigest),
|
|
74
|
+
promptRevision: e.promptRevision === null || e.promptRevision === void 0 ? null : d(e.promptRevision, 0),
|
|
75
|
+
maxTokens: e.maxTokens === null || e.maxTokens === void 0 ? null : d(e.maxTokens, 0),
|
|
76
|
+
providerOptions: e.providerOptions ?? null,
|
|
77
|
+
jsonSchema: e.jsonSchema ?? null,
|
|
78
|
+
transcription: e.transcription ?? null,
|
|
79
|
+
media: e.media ?? null,
|
|
80
|
+
taskHandle: typeof e.taskHandle == "object" ? e.taskHandle : null,
|
|
81
|
+
taskSubmittedAt: p(e.taskSubmittedAt),
|
|
82
|
+
attempts: d(e.attempts, 0),
|
|
83
|
+
fence: d(e.fence, 0)
|
|
84
|
+
}), h = (e) => {
|
|
85
|
+
let t = e.attempts + 1;
|
|
86
|
+
return t >= e.maxAttempts ? {
|
|
87
|
+
kind: "give-up",
|
|
88
|
+
attempts: t
|
|
89
|
+
} : {
|
|
90
|
+
kind: "retry",
|
|
91
|
+
attempts: t,
|
|
92
|
+
delayMs: e.retryAfterMs !== void 0 && e.retryAfterMs > 0 ? e.retryAfterMs : Math.min(e.baseDelayMs * 2 ** (t - 1), e.maxDelayMs)
|
|
93
|
+
};
|
|
94
|
+
}, g = (e) => `voltro.ai.infer:${e}`, _ = (e, t) => `aiinf_${o("sha256").update(`${e}\u0000${t}`).digest("hex").slice(0, 40)}`;
|
|
95
|
+
//#endregion
|
|
96
|
+
export { s as AI_INFERENCES_TABLE, u as DEFAULT_INFERENCE_ATTEMPTS, l as DEFAULT_INFERENCE_LEASE_MS, c as aiInferencesTable, h as decideInferenceFailure, g as inferenceSignalName, _ as offloadedInferenceId, m as toQueuedInference };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsonSchema as e } from "ai";
|
|
2
|
+
import { Ajv as t } from "ajv";
|
|
3
|
+
import { Ajv2019 as n } from "ajv/dist/2019.js";
|
|
4
|
+
import { Ajv2020 as r } from "ajv/dist/2020.js";
|
|
5
|
+
import i from "ajv-formats";
|
|
6
|
+
//#region src/jsonOutputValidation.ts
|
|
7
|
+
var a = (a) => {
|
|
8
|
+
let o = structuredClone(a), s = {
|
|
9
|
+
strict: !0,
|
|
10
|
+
strictTypes: !1,
|
|
11
|
+
strictTuples: !1,
|
|
12
|
+
strictRequired: !1,
|
|
13
|
+
allowUnionTypes: !0
|
|
14
|
+
}, c = o.$schema, l = c === "https://json-schema.org/draft/2020-12/schema" ? new r(s) : c === "https://json-schema.org/draft/2019-09/schema" ? new n(s) : new t(s);
|
|
15
|
+
i(l);
|
|
16
|
+
let u = l.compile(o);
|
|
17
|
+
if ("$async" in u && u.$async === !0) throw Error("asynchronous output schemas are not supported");
|
|
18
|
+
return e(o, { validate: (e) => u(e) ? {
|
|
19
|
+
success: !0,
|
|
20
|
+
value: e
|
|
21
|
+
} : {
|
|
22
|
+
success: !1,
|
|
23
|
+
error: /* @__PURE__ */ Error("model output does not satisfy the declared JSON Schema")
|
|
24
|
+
} });
|
|
25
|
+
};
|
|
26
|
+
//#endregion
|
|
27
|
+
export { a as validatedOutputSchema };
|