@mastra/inngest 0.0.0-vnext-inngest-20250508122351 → 0.0.0-vnext-20251104230439
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 +1457 -2
- package/LICENSE.md +11 -42
- package/dist/index.cjs +1138 -314
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +317 -5
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1136 -313
- package/dist/index.js.map +1 -0
- package/package.json +40 -21
- package/dist/_tsup-dts-rollup.d.cts +0 -184
- package/dist/_tsup-dts-rollup.d.ts +0 -184
- package/dist/index.d.cts +0 -5
- package/docker-compose.yaml +0 -10
- package/eslint.config.js +0 -6
- package/src/index.test.ts +0 -5437
- package/src/index.ts +0 -926
- package/tsconfig.json +0 -5
- package/vitest.config.ts +0 -8
package/dist/index.cjs
CHANGED
|
@@ -1,36 +1,55 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var crypto = require('crypto');
|
|
4
|
+
var web = require('stream/web');
|
|
4
5
|
var realtime = require('@inngest/realtime');
|
|
5
6
|
var di = require('@mastra/core/di');
|
|
6
|
-
var
|
|
7
|
+
var observability = require('@mastra/core/observability');
|
|
8
|
+
var stream = require('@mastra/core/stream');
|
|
9
|
+
var tools = require('@mastra/core/tools');
|
|
10
|
+
var workflows = require('@mastra/core/workflows');
|
|
11
|
+
var _constants = require('@mastra/core/workflows/_constants');
|
|
12
|
+
var inngest = require('inngest');
|
|
7
13
|
var hono = require('inngest/hono');
|
|
14
|
+
var zod = require('zod');
|
|
8
15
|
|
|
9
16
|
// src/index.ts
|
|
10
|
-
function serve({
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
function serve({
|
|
18
|
+
mastra,
|
|
19
|
+
inngest,
|
|
20
|
+
functions: userFunctions = [],
|
|
21
|
+
registerOptions
|
|
22
|
+
}) {
|
|
23
|
+
const wfs = mastra.listWorkflows();
|
|
24
|
+
const workflowFunctions = Array.from(
|
|
25
|
+
new Set(
|
|
26
|
+
Object.values(wfs).flatMap((wf) => {
|
|
27
|
+
if (wf instanceof InngestWorkflow) {
|
|
28
|
+
wf.__registerMastra(mastra);
|
|
29
|
+
return wf.getFunctions();
|
|
30
|
+
}
|
|
31
|
+
return [];
|
|
32
|
+
})
|
|
33
|
+
)
|
|
34
|
+
);
|
|
19
35
|
return hono.serve({
|
|
36
|
+
...registerOptions,
|
|
20
37
|
client: inngest,
|
|
21
|
-
functions
|
|
38
|
+
functions: [...workflowFunctions, ...userFunctions]
|
|
22
39
|
});
|
|
23
40
|
}
|
|
24
|
-
var InngestRun = class extends
|
|
41
|
+
var InngestRun = class extends workflows.Run {
|
|
25
42
|
inngest;
|
|
43
|
+
serializedStepGraph;
|
|
26
44
|
#mastra;
|
|
27
45
|
constructor(params, inngest) {
|
|
28
46
|
super(params);
|
|
29
47
|
this.inngest = inngest;
|
|
48
|
+
this.serializedStepGraph = params.serializedStepGraph;
|
|
30
49
|
this.#mastra = params.mastra;
|
|
31
50
|
}
|
|
32
51
|
async getRuns(eventId) {
|
|
33
|
-
const response = await fetch(`${this.inngest.apiBaseUrl}/v1/events/${eventId}/runs`, {
|
|
52
|
+
const response = await fetch(`${this.inngest.apiBaseUrl ?? "https://api.inngest.com"}/v1/events/${eventId}/runs`, {
|
|
34
53
|
headers: {
|
|
35
54
|
Authorization: `Bearer ${process.env.INNGEST_SIGNING_KEY}`
|
|
36
55
|
}
|
|
@@ -40,35 +59,98 @@ var InngestRun = class extends vNext.Run {
|
|
|
40
59
|
}
|
|
41
60
|
async getRunOutput(eventId) {
|
|
42
61
|
let runs = await this.getRuns(eventId);
|
|
43
|
-
|
|
62
|
+
const storage = this.#mastra?.getStorage();
|
|
63
|
+
while (runs?.[0]?.status !== "Completed" || runs?.[0]?.event_id !== eventId) {
|
|
44
64
|
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
45
65
|
runs = await this.getRuns(eventId);
|
|
46
|
-
if (runs?.[0]?.status === "Failed"
|
|
47
|
-
|
|
66
|
+
if (runs?.[0]?.status === "Failed") {
|
|
67
|
+
const snapshot = await storage?.loadWorkflowSnapshot({
|
|
68
|
+
workflowName: this.workflowId,
|
|
69
|
+
runId: this.runId
|
|
70
|
+
});
|
|
71
|
+
return {
|
|
72
|
+
output: { result: { steps: snapshot?.context, status: "failed", error: runs?.[0]?.output?.message } }
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (runs?.[0]?.status === "Cancelled") {
|
|
76
|
+
const snapshot = await storage?.loadWorkflowSnapshot({
|
|
77
|
+
workflowName: this.workflowId,
|
|
78
|
+
runId: this.runId
|
|
79
|
+
});
|
|
80
|
+
return { output: { result: { steps: snapshot?.context, status: "canceled" } } };
|
|
48
81
|
}
|
|
49
82
|
}
|
|
50
83
|
return runs?.[0];
|
|
51
84
|
}
|
|
52
|
-
async
|
|
53
|
-
|
|
85
|
+
async sendEvent(event, data) {
|
|
86
|
+
await this.inngest.send({
|
|
87
|
+
name: `user-event-${event}`,
|
|
88
|
+
data
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
async cancel() {
|
|
92
|
+
const storage = this.#mastra?.getStorage();
|
|
93
|
+
await this.inngest.send({
|
|
94
|
+
name: `cancel.workflow.${this.workflowId}`,
|
|
95
|
+
data: {
|
|
96
|
+
runId: this.runId
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
const snapshot = await storage?.loadWorkflowSnapshot({
|
|
100
|
+
workflowName: this.workflowId,
|
|
101
|
+
runId: this.runId
|
|
102
|
+
});
|
|
103
|
+
if (snapshot) {
|
|
104
|
+
await storage?.persistWorkflowSnapshot({
|
|
105
|
+
workflowName: this.workflowId,
|
|
106
|
+
runId: this.runId,
|
|
107
|
+
resourceId: this.resourceId,
|
|
108
|
+
snapshot: {
|
|
109
|
+
...snapshot,
|
|
110
|
+
status: "canceled"
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async start(params) {
|
|
116
|
+
return this._start(params);
|
|
117
|
+
}
|
|
118
|
+
async _start({
|
|
119
|
+
inputData,
|
|
120
|
+
initialState,
|
|
121
|
+
outputOptions,
|
|
122
|
+
tracingOptions,
|
|
123
|
+
format
|
|
54
124
|
}) {
|
|
55
125
|
await this.#mastra.getStorage()?.persistWorkflowSnapshot({
|
|
56
126
|
workflowName: this.workflowId,
|
|
57
127
|
runId: this.runId,
|
|
128
|
+
resourceId: this.resourceId,
|
|
58
129
|
snapshot: {
|
|
59
130
|
runId: this.runId,
|
|
131
|
+
serializedStepGraph: this.serializedStepGraph,
|
|
60
132
|
value: {},
|
|
61
133
|
context: {},
|
|
62
134
|
activePaths: [],
|
|
63
135
|
suspendedPaths: {},
|
|
64
|
-
|
|
136
|
+
resumeLabels: {},
|
|
137
|
+
waitingPaths: {},
|
|
138
|
+
timestamp: Date.now(),
|
|
139
|
+
status: "running"
|
|
65
140
|
}
|
|
66
141
|
});
|
|
142
|
+
const inputDataToUse = await this._validateInput(inputData);
|
|
143
|
+
const initialStateToUse = await this._validateInitialState(initialState ?? {});
|
|
67
144
|
const eventOutput = await this.inngest.send({
|
|
68
145
|
name: `workflow.${this.workflowId}`,
|
|
69
146
|
data: {
|
|
70
|
-
inputData,
|
|
71
|
-
|
|
147
|
+
inputData: inputDataToUse,
|
|
148
|
+
initialState: initialStateToUse,
|
|
149
|
+
runId: this.runId,
|
|
150
|
+
resourceId: this.resourceId,
|
|
151
|
+
outputOptions,
|
|
152
|
+
tracingOptions,
|
|
153
|
+
format
|
|
72
154
|
}
|
|
73
155
|
});
|
|
74
156
|
const eventId = eventOutput.ids[0];
|
|
@@ -80,27 +162,45 @@ var InngestRun = class extends vNext.Run {
|
|
|
80
162
|
if (result.status === "failed") {
|
|
81
163
|
result.error = new Error(result.error);
|
|
82
164
|
}
|
|
83
|
-
|
|
165
|
+
if (result.status !== "suspended") {
|
|
166
|
+
this.cleanup?.();
|
|
167
|
+
}
|
|
84
168
|
return result;
|
|
85
169
|
}
|
|
86
170
|
async resume(params) {
|
|
171
|
+
const p = this._resume(params).then((result) => {
|
|
172
|
+
if (result.status !== "suspended") {
|
|
173
|
+
this.closeStreamAction?.().catch(() => {
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
return result;
|
|
177
|
+
});
|
|
178
|
+
this.executionResults = p;
|
|
179
|
+
return p;
|
|
180
|
+
}
|
|
181
|
+
async _resume(params) {
|
|
182
|
+
const storage = this.#mastra?.getStorage();
|
|
87
183
|
const steps = (Array.isArray(params.step) ? params.step : [params.step]).map(
|
|
88
184
|
(step) => typeof step === "string" ? step : step?.id
|
|
89
185
|
);
|
|
90
|
-
const snapshot = await
|
|
186
|
+
const snapshot = await storage?.loadWorkflowSnapshot({
|
|
91
187
|
workflowName: this.workflowId,
|
|
92
188
|
runId: this.runId
|
|
93
189
|
});
|
|
190
|
+
const suspendedStep = this.workflowSteps[steps?.[0] ?? ""];
|
|
191
|
+
const resumeDataToUse = await this._validateResumeData(params.resumeData, suspendedStep);
|
|
94
192
|
const eventOutput = await this.inngest.send({
|
|
95
193
|
name: `workflow.${this.workflowId}`,
|
|
96
194
|
data: {
|
|
97
|
-
inputData:
|
|
195
|
+
inputData: resumeDataToUse,
|
|
196
|
+
initialState: snapshot?.value ?? {},
|
|
98
197
|
runId: this.runId,
|
|
198
|
+
workflowId: this.workflowId,
|
|
99
199
|
stepResults: snapshot?.context,
|
|
100
200
|
resume: {
|
|
101
201
|
steps,
|
|
102
202
|
stepResults: snapshot?.context,
|
|
103
|
-
resumePayload:
|
|
203
|
+
resumePayload: resumeDataToUse,
|
|
104
204
|
// @ts-ignore
|
|
105
205
|
resumePath: snapshot?.suspendedPaths?.[steps?.[0]]
|
|
106
206
|
}
|
|
@@ -118,6 +218,7 @@ var InngestRun = class extends vNext.Run {
|
|
|
118
218
|
return result;
|
|
119
219
|
}
|
|
120
220
|
watch(cb) {
|
|
221
|
+
let active = true;
|
|
121
222
|
const streamPromise = realtime.subscribe(
|
|
122
223
|
{
|
|
123
224
|
channel: `workflow:${this.workflowId}:${this.runId}`,
|
|
@@ -125,27 +226,179 @@ var InngestRun = class extends vNext.Run {
|
|
|
125
226
|
app: this.inngest
|
|
126
227
|
},
|
|
127
228
|
(message) => {
|
|
128
|
-
|
|
229
|
+
if (active) {
|
|
230
|
+
cb(message.data);
|
|
231
|
+
}
|
|
129
232
|
}
|
|
130
233
|
);
|
|
131
234
|
return () => {
|
|
132
|
-
|
|
133
|
-
|
|
235
|
+
active = false;
|
|
236
|
+
streamPromise.then(async (stream) => {
|
|
237
|
+
return stream.cancel();
|
|
134
238
|
}).catch((err) => {
|
|
135
239
|
console.error(err);
|
|
136
240
|
});
|
|
137
241
|
};
|
|
138
242
|
}
|
|
243
|
+
streamLegacy({ inputData, requestContext } = {}) {
|
|
244
|
+
const { readable, writable } = new TransformStream();
|
|
245
|
+
const writer = writable.getWriter();
|
|
246
|
+
const unwatch = this.watch(async (event) => {
|
|
247
|
+
try {
|
|
248
|
+
await writer.write({
|
|
249
|
+
// @ts-ignore
|
|
250
|
+
type: "start",
|
|
251
|
+
// @ts-ignore
|
|
252
|
+
payload: { runId: this.runId }
|
|
253
|
+
});
|
|
254
|
+
const e = {
|
|
255
|
+
...event,
|
|
256
|
+
type: event.type.replace("workflow-", "")
|
|
257
|
+
};
|
|
258
|
+
if (e.type === "step-output") {
|
|
259
|
+
e.type = e.payload.output.type;
|
|
260
|
+
e.payload = e.payload.output.payload;
|
|
261
|
+
}
|
|
262
|
+
await writer.write(e);
|
|
263
|
+
} catch {
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
this.closeStreamAction = async () => {
|
|
267
|
+
await writer.write({
|
|
268
|
+
type: "finish",
|
|
269
|
+
// @ts-ignore
|
|
270
|
+
payload: { runId: this.runId }
|
|
271
|
+
});
|
|
272
|
+
unwatch();
|
|
273
|
+
try {
|
|
274
|
+
await writer.close();
|
|
275
|
+
} catch (err) {
|
|
276
|
+
console.error("Error closing stream:", err);
|
|
277
|
+
} finally {
|
|
278
|
+
writer.releaseLock();
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
this.executionResults = this._start({ inputData, requestContext, format: "legacy" }).then((result) => {
|
|
282
|
+
if (result.status !== "suspended") {
|
|
283
|
+
this.closeStreamAction?.().catch(() => {
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
return result;
|
|
287
|
+
});
|
|
288
|
+
return {
|
|
289
|
+
stream: readable,
|
|
290
|
+
getWorkflowState: () => this.executionResults
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
stream({
|
|
294
|
+
inputData,
|
|
295
|
+
requestContext,
|
|
296
|
+
tracingOptions,
|
|
297
|
+
closeOnSuspend = true,
|
|
298
|
+
initialState,
|
|
299
|
+
outputOptions
|
|
300
|
+
} = {}) {
|
|
301
|
+
if (this.closeStreamAction && this.streamOutput) {
|
|
302
|
+
return this.streamOutput;
|
|
303
|
+
}
|
|
304
|
+
this.closeStreamAction = async () => {
|
|
305
|
+
};
|
|
306
|
+
const self = this;
|
|
307
|
+
const stream$1 = new web.ReadableStream({
|
|
308
|
+
async start(controller) {
|
|
309
|
+
const unwatch = self.watch(async ({ type, from = stream.ChunkFrom.WORKFLOW, payload }) => {
|
|
310
|
+
controller.enqueue({
|
|
311
|
+
type,
|
|
312
|
+
runId: self.runId,
|
|
313
|
+
from,
|
|
314
|
+
payload: {
|
|
315
|
+
stepName: payload?.id,
|
|
316
|
+
...payload
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
self.closeStreamAction = async () => {
|
|
321
|
+
unwatch();
|
|
322
|
+
try {
|
|
323
|
+
await controller.close();
|
|
324
|
+
} catch (err) {
|
|
325
|
+
console.error("Error closing stream:", err);
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
const executionResultsPromise = self._start({
|
|
329
|
+
inputData,
|
|
330
|
+
requestContext,
|
|
331
|
+
// tracingContext, // We are not able to pass a reference to a span here, what to do?
|
|
332
|
+
initialState,
|
|
333
|
+
tracingOptions,
|
|
334
|
+
outputOptions,
|
|
335
|
+
format: "vnext"
|
|
336
|
+
});
|
|
337
|
+
let executionResults;
|
|
338
|
+
try {
|
|
339
|
+
executionResults = await executionResultsPromise;
|
|
340
|
+
if (closeOnSuspend) {
|
|
341
|
+
self.closeStreamAction?.().catch(() => {
|
|
342
|
+
});
|
|
343
|
+
} else if (executionResults.status !== "suspended") {
|
|
344
|
+
self.closeStreamAction?.().catch(() => {
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
if (self.streamOutput) {
|
|
348
|
+
self.streamOutput.updateResults(
|
|
349
|
+
executionResults
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
} catch (err) {
|
|
353
|
+
self.streamOutput?.rejectResults(err);
|
|
354
|
+
self.closeStreamAction?.().catch(() => {
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
this.streamOutput = new stream.WorkflowRunOutput({
|
|
360
|
+
runId: this.runId,
|
|
361
|
+
workflowId: this.workflowId,
|
|
362
|
+
stream: stream$1
|
|
363
|
+
});
|
|
364
|
+
return this.streamOutput;
|
|
365
|
+
}
|
|
366
|
+
streamVNext(args = {}) {
|
|
367
|
+
return this.stream(args);
|
|
368
|
+
}
|
|
139
369
|
};
|
|
140
|
-
var InngestWorkflow = class _InngestWorkflow extends
|
|
370
|
+
var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
|
|
141
371
|
#mastra;
|
|
142
372
|
inngest;
|
|
143
373
|
function;
|
|
374
|
+
flowControlConfig;
|
|
144
375
|
constructor(params, inngest) {
|
|
145
|
-
|
|
376
|
+
const { concurrency, rateLimit, throttle, debounce, priority, ...workflowParams } = params;
|
|
377
|
+
super(workflowParams);
|
|
378
|
+
const flowControlEntries = Object.entries({ concurrency, rateLimit, throttle, debounce, priority }).filter(
|
|
379
|
+
([_, value]) => value !== void 0
|
|
380
|
+
);
|
|
381
|
+
this.flowControlConfig = flowControlEntries.length > 0 ? Object.fromEntries(flowControlEntries) : void 0;
|
|
146
382
|
this.#mastra = params.mastra;
|
|
147
383
|
this.inngest = inngest;
|
|
148
384
|
}
|
|
385
|
+
async listWorkflowRuns(args) {
|
|
386
|
+
const storage = this.#mastra?.getStorage();
|
|
387
|
+
if (!storage) {
|
|
388
|
+
this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
|
|
389
|
+
return { runs: [], total: 0 };
|
|
390
|
+
}
|
|
391
|
+
return storage.listWorkflowRuns({ workflowName: this.id, ...args ?? {} });
|
|
392
|
+
}
|
|
393
|
+
async getWorkflowRunById(runId) {
|
|
394
|
+
const storage = this.#mastra?.getStorage();
|
|
395
|
+
if (!storage) {
|
|
396
|
+
this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
|
|
397
|
+
return this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null;
|
|
398
|
+
}
|
|
399
|
+
const run = await storage.getWorkflowRunById({ runId, workflowName: this.id });
|
|
400
|
+
return run ?? (this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null);
|
|
401
|
+
}
|
|
149
402
|
__registerMastra(mastra) {
|
|
150
403
|
this.#mastra = mastra;
|
|
151
404
|
this.executionEngine.__registerMastra(mastra);
|
|
@@ -164,21 +417,51 @@ var InngestWorkflow = class _InngestWorkflow extends vNext.NewWorkflow {
|
|
|
164
417
|
}
|
|
165
418
|
}
|
|
166
419
|
}
|
|
167
|
-
createRun(options) {
|
|
420
|
+
async createRun(options) {
|
|
168
421
|
const runIdToUse = options?.runId || crypto.randomUUID();
|
|
169
422
|
const run = this.runs.get(runIdToUse) ?? new InngestRun(
|
|
170
423
|
{
|
|
171
424
|
workflowId: this.id,
|
|
172
425
|
runId: runIdToUse,
|
|
426
|
+
resourceId: options?.resourceId,
|
|
173
427
|
executionEngine: this.executionEngine,
|
|
174
428
|
executionGraph: this.executionGraph,
|
|
429
|
+
serializedStepGraph: this.serializedStepGraph,
|
|
175
430
|
mastra: this.#mastra,
|
|
176
431
|
retryConfig: this.retryConfig,
|
|
177
|
-
cleanup: () => this.runs.delete(runIdToUse)
|
|
432
|
+
cleanup: () => this.runs.delete(runIdToUse),
|
|
433
|
+
workflowSteps: this.steps
|
|
178
434
|
},
|
|
179
435
|
this.inngest
|
|
180
436
|
);
|
|
181
437
|
this.runs.set(runIdToUse, run);
|
|
438
|
+
const shouldPersistSnapshot = this.options.shouldPersistSnapshot({
|
|
439
|
+
workflowStatus: run.workflowRunStatus,
|
|
440
|
+
stepResults: {}
|
|
441
|
+
});
|
|
442
|
+
const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse, false);
|
|
443
|
+
if (!workflowSnapshotInStorage && shouldPersistSnapshot) {
|
|
444
|
+
await this.mastra?.getStorage()?.persistWorkflowSnapshot({
|
|
445
|
+
workflowName: this.id,
|
|
446
|
+
runId: runIdToUse,
|
|
447
|
+
resourceId: options?.resourceId,
|
|
448
|
+
snapshot: {
|
|
449
|
+
runId: runIdToUse,
|
|
450
|
+
status: "pending",
|
|
451
|
+
value: {},
|
|
452
|
+
context: {},
|
|
453
|
+
activePaths: [],
|
|
454
|
+
waitingPaths: {},
|
|
455
|
+
serializedStepGraph: this.serializedStepGraph,
|
|
456
|
+
suspendedPaths: {},
|
|
457
|
+
resumeLabels: {},
|
|
458
|
+
result: void 0,
|
|
459
|
+
error: void 0,
|
|
460
|
+
// @ts-ignore
|
|
461
|
+
timestamp: Date.now()
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
}
|
|
182
465
|
return run;
|
|
183
466
|
}
|
|
184
467
|
getFunction() {
|
|
@@ -186,11 +469,17 @@ var InngestWorkflow = class _InngestWorkflow extends vNext.NewWorkflow {
|
|
|
186
469
|
return this.function;
|
|
187
470
|
}
|
|
188
471
|
this.function = this.inngest.createFunction(
|
|
189
|
-
|
|
190
|
-
|
|
472
|
+
{
|
|
473
|
+
id: `workflow.${this.id}`,
|
|
474
|
+
// @ts-ignore
|
|
475
|
+
retries: this.retryConfig?.attempts ?? 0,
|
|
476
|
+
cancelOn: [{ event: `cancel.workflow.${this.id}` }],
|
|
477
|
+
// Spread flow control configuration
|
|
478
|
+
...this.flowControlConfig
|
|
479
|
+
},
|
|
191
480
|
{ event: `workflow.${this.id}` },
|
|
192
481
|
async ({ event, step, attempt, publish }) => {
|
|
193
|
-
let { inputData, runId, resume } = event.data;
|
|
482
|
+
let { inputData, initialState, runId, resourceId, resume, outputOptions, format } = event.data;
|
|
194
483
|
if (!runId) {
|
|
195
484
|
runId = await step.run(`workflow.${this.id}.runIdGen`, async () => {
|
|
196
485
|
return crypto.randomUUID();
|
|
@@ -204,25 +493,52 @@ var InngestWorkflow = class _InngestWorkflow extends vNext.NewWorkflow {
|
|
|
204
493
|
try {
|
|
205
494
|
await publish({
|
|
206
495
|
channel: `workflow:${this.id}:${runId}`,
|
|
207
|
-
topic:
|
|
496
|
+
topic: event2,
|
|
208
497
|
data
|
|
209
498
|
});
|
|
210
499
|
} catch (err) {
|
|
211
500
|
this.logger.error("Error emitting event: " + (err?.stack ?? err?.message ?? err));
|
|
212
501
|
}
|
|
502
|
+
},
|
|
503
|
+
on: (_event, _callback) => {
|
|
504
|
+
},
|
|
505
|
+
off: (_event, _callback) => {
|
|
506
|
+
},
|
|
507
|
+
once: (_event, _callback) => {
|
|
213
508
|
}
|
|
214
509
|
};
|
|
215
|
-
const engine = new InngestExecutionEngine(this.#mastra, step, attempt);
|
|
510
|
+
const engine = new InngestExecutionEngine(this.#mastra, step, attempt, this.options);
|
|
216
511
|
const result = await engine.execute({
|
|
217
512
|
workflowId: this.id,
|
|
218
513
|
runId,
|
|
514
|
+
resourceId,
|
|
219
515
|
graph: this.executionGraph,
|
|
516
|
+
serializedStepGraph: this.serializedStepGraph,
|
|
220
517
|
input: inputData,
|
|
518
|
+
initialState,
|
|
221
519
|
emitter,
|
|
222
520
|
retryConfig: this.retryConfig,
|
|
223
|
-
|
|
521
|
+
requestContext: new di.RequestContext(),
|
|
224
522
|
// TODO
|
|
225
|
-
resume
|
|
523
|
+
resume,
|
|
524
|
+
format,
|
|
525
|
+
abortController: new AbortController(),
|
|
526
|
+
// currentSpan: undefined, // TODO: Pass actual parent AI span from workflow execution context
|
|
527
|
+
outputOptions,
|
|
528
|
+
writableStream: new WritableStream({
|
|
529
|
+
write(chunk) {
|
|
530
|
+
void emitter.emit("watch", chunk).catch(() => {
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
})
|
|
534
|
+
});
|
|
535
|
+
await step.run(`workflow.${this.id}.finalize`, async () => {
|
|
536
|
+
if (result.status === "failed") {
|
|
537
|
+
throw new inngest.NonRetriableError(`Workflow failed`, {
|
|
538
|
+
cause: result
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
return result;
|
|
226
542
|
});
|
|
227
543
|
return { result, runId };
|
|
228
544
|
}
|
|
@@ -246,118 +562,403 @@ var InngestWorkflow = class _InngestWorkflow extends vNext.NewWorkflow {
|
|
|
246
562
|
return [this.getFunction(), ...this.getNestedFunctions(this.executionGraph.steps)];
|
|
247
563
|
}
|
|
248
564
|
};
|
|
249
|
-
function
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
565
|
+
function isAgent(params) {
|
|
566
|
+
return params?.component === "AGENT";
|
|
567
|
+
}
|
|
568
|
+
function isTool(params) {
|
|
569
|
+
return params instanceof tools.Tool;
|
|
570
|
+
}
|
|
571
|
+
function createStep(params, agentOptions) {
|
|
572
|
+
if (isAgent(params)) {
|
|
573
|
+
return {
|
|
574
|
+
id: params.name,
|
|
575
|
+
description: params.getDescription(),
|
|
576
|
+
// @ts-ignore
|
|
577
|
+
inputSchema: zod.z.object({
|
|
578
|
+
prompt: zod.z.string()
|
|
579
|
+
// resourceId: z.string().optional(),
|
|
580
|
+
// threadId: z.string().optional(),
|
|
581
|
+
}),
|
|
582
|
+
// @ts-ignore
|
|
583
|
+
outputSchema: zod.z.object({
|
|
584
|
+
text: zod.z.string()
|
|
585
|
+
}),
|
|
586
|
+
execute: async ({
|
|
587
|
+
inputData,
|
|
588
|
+
[_constants.EMITTER_SYMBOL]: emitter,
|
|
589
|
+
[_constants.STREAM_FORMAT_SYMBOL]: streamFormat,
|
|
590
|
+
requestContext,
|
|
591
|
+
tracingContext,
|
|
592
|
+
abortSignal,
|
|
593
|
+
abort,
|
|
594
|
+
writer
|
|
595
|
+
}) => {
|
|
596
|
+
let streamPromise = {};
|
|
597
|
+
streamPromise.promise = new Promise((resolve, reject) => {
|
|
598
|
+
streamPromise.resolve = resolve;
|
|
599
|
+
streamPromise.reject = reject;
|
|
600
|
+
});
|
|
601
|
+
const toolData = {
|
|
602
|
+
name: params.name,
|
|
603
|
+
args: inputData
|
|
604
|
+
};
|
|
605
|
+
let stream;
|
|
606
|
+
if ((await params.getModel()).specificationVersion === "v1") {
|
|
607
|
+
const { fullStream } = await params.streamLegacy(inputData.prompt, {
|
|
608
|
+
...agentOptions ?? {},
|
|
609
|
+
// resourceId: inputData.resourceId,
|
|
610
|
+
// threadId: inputData.threadId,
|
|
611
|
+
requestContext,
|
|
612
|
+
tracingContext,
|
|
613
|
+
onFinish: (result) => {
|
|
614
|
+
streamPromise.resolve(result.text);
|
|
615
|
+
void agentOptions?.onFinish?.(result);
|
|
616
|
+
},
|
|
617
|
+
abortSignal
|
|
618
|
+
});
|
|
619
|
+
stream = fullStream;
|
|
620
|
+
} else {
|
|
621
|
+
const modelOutput = await params.stream(inputData.prompt, {
|
|
622
|
+
...agentOptions ?? {},
|
|
623
|
+
requestContext,
|
|
624
|
+
tracingContext,
|
|
625
|
+
onFinish: (result) => {
|
|
626
|
+
streamPromise.resolve(result.text);
|
|
627
|
+
void agentOptions?.onFinish?.(result);
|
|
628
|
+
},
|
|
629
|
+
abortSignal
|
|
630
|
+
});
|
|
631
|
+
stream = modelOutput.fullStream;
|
|
632
|
+
}
|
|
633
|
+
if (streamFormat === "legacy") {
|
|
634
|
+
await emitter.emit("watch", {
|
|
635
|
+
type: "tool-call-streaming-start",
|
|
636
|
+
...toolData ?? {}
|
|
637
|
+
});
|
|
638
|
+
for await (const chunk of stream) {
|
|
639
|
+
if (chunk.type === "text-delta") {
|
|
640
|
+
await emitter.emit("watch", {
|
|
641
|
+
type: "tool-call-delta",
|
|
642
|
+
...toolData ?? {},
|
|
643
|
+
argsTextDelta: chunk.textDelta
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
await emitter.emit("watch", {
|
|
648
|
+
type: "tool-call-streaming-finish",
|
|
649
|
+
...toolData ?? {}
|
|
650
|
+
});
|
|
651
|
+
} else {
|
|
652
|
+
for await (const chunk of stream) {
|
|
653
|
+
await writer.write(chunk);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
if (abortSignal.aborted) {
|
|
657
|
+
return abort();
|
|
658
|
+
}
|
|
659
|
+
return {
|
|
660
|
+
text: await streamPromise.promise
|
|
661
|
+
};
|
|
662
|
+
},
|
|
663
|
+
component: params.component
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
if (isTool(params)) {
|
|
667
|
+
if (!params.inputSchema || !params.outputSchema) {
|
|
668
|
+
throw new Error("Tool must have input and output schemas defined");
|
|
669
|
+
}
|
|
670
|
+
return {
|
|
671
|
+
// TODO: tool probably should have strong id type
|
|
672
|
+
// @ts-ignore
|
|
673
|
+
id: params.id,
|
|
674
|
+
description: params.description,
|
|
675
|
+
inputSchema: params.inputSchema,
|
|
676
|
+
outputSchema: params.outputSchema,
|
|
677
|
+
execute: async ({ inputData, mastra, requestContext, tracingContext, suspend, resumeData }) => {
|
|
678
|
+
return params.execute({
|
|
679
|
+
context: inputData,
|
|
680
|
+
mastra: observability.wrapMastra(mastra, tracingContext),
|
|
681
|
+
requestContext,
|
|
682
|
+
tracingContext,
|
|
683
|
+
suspend,
|
|
684
|
+
resumeData
|
|
685
|
+
});
|
|
686
|
+
},
|
|
687
|
+
component: "TOOL"
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
return {
|
|
691
|
+
id: params.id,
|
|
692
|
+
description: params.description,
|
|
693
|
+
inputSchema: params.inputSchema,
|
|
694
|
+
outputSchema: params.outputSchema,
|
|
695
|
+
resumeSchema: params.resumeSchema,
|
|
696
|
+
suspendSchema: params.suspendSchema,
|
|
697
|
+
execute: params.execute
|
|
698
|
+
};
|
|
263
699
|
}
|
|
264
700
|
function init(inngest) {
|
|
265
701
|
return {
|
|
266
702
|
createWorkflow(params) {
|
|
267
|
-
return new InngestWorkflow(
|
|
703
|
+
return new InngestWorkflow(
|
|
704
|
+
params,
|
|
705
|
+
inngest
|
|
706
|
+
);
|
|
268
707
|
},
|
|
269
|
-
createStep
|
|
270
|
-
cloneStep
|
|
271
|
-
|
|
708
|
+
createStep,
|
|
709
|
+
cloneStep(step, opts) {
|
|
710
|
+
return {
|
|
711
|
+
id: opts.id,
|
|
712
|
+
description: step.description,
|
|
713
|
+
inputSchema: step.inputSchema,
|
|
714
|
+
outputSchema: step.outputSchema,
|
|
715
|
+
resumeSchema: step.resumeSchema,
|
|
716
|
+
suspendSchema: step.suspendSchema,
|
|
717
|
+
stateSchema: step.stateSchema,
|
|
718
|
+
execute: step.execute,
|
|
719
|
+
component: step.component
|
|
720
|
+
};
|
|
721
|
+
},
|
|
722
|
+
cloneWorkflow(workflow, opts) {
|
|
723
|
+
const wf = new workflows.Workflow({
|
|
724
|
+
id: opts.id,
|
|
725
|
+
inputSchema: workflow.inputSchema,
|
|
726
|
+
outputSchema: workflow.outputSchema,
|
|
727
|
+
steps: workflow.stepDefs,
|
|
728
|
+
mastra: workflow.mastra
|
|
729
|
+
});
|
|
730
|
+
wf.setStepFlow(workflow.stepGraph);
|
|
731
|
+
wf.commit();
|
|
732
|
+
return wf;
|
|
733
|
+
}
|
|
272
734
|
};
|
|
273
735
|
}
|
|
274
|
-
var InngestExecutionEngine = class extends
|
|
736
|
+
var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
275
737
|
inngestStep;
|
|
276
738
|
inngestAttempts;
|
|
277
|
-
constructor(mastra, inngestStep, inngestAttempts = 0) {
|
|
278
|
-
super({ mastra });
|
|
739
|
+
constructor(mastra, inngestStep, inngestAttempts = 0, options) {
|
|
740
|
+
super({ mastra, options });
|
|
279
741
|
this.inngestStep = inngestStep;
|
|
280
742
|
this.inngestAttempts = inngestAttempts;
|
|
281
743
|
}
|
|
282
|
-
async fmtReturnValue(
|
|
744
|
+
async fmtReturnValue(emitter, stepResults, lastOutput, error) {
|
|
283
745
|
const base = {
|
|
284
746
|
status: lastOutput.status,
|
|
285
747
|
steps: stepResults
|
|
286
748
|
};
|
|
287
749
|
if (lastOutput.status === "success") {
|
|
288
|
-
await emitter.emit("watch", {
|
|
289
|
-
type: "watch",
|
|
290
|
-
payload: {
|
|
291
|
-
workflowState: {
|
|
292
|
-
status: lastOutput.status,
|
|
293
|
-
steps: stepResults,
|
|
294
|
-
result: lastOutput.output
|
|
295
|
-
}
|
|
296
|
-
},
|
|
297
|
-
eventTimestamp: Date.now()
|
|
298
|
-
});
|
|
299
750
|
base.result = lastOutput.output;
|
|
300
751
|
} else if (lastOutput.status === "failed") {
|
|
301
752
|
base.error = error instanceof Error ? error?.stack ?? error.message : lastOutput?.error instanceof Error ? lastOutput.error.message : lastOutput.error ?? error ?? "Unknown error";
|
|
302
|
-
await emitter.emit("watch", {
|
|
303
|
-
type: "watch",
|
|
304
|
-
payload: {
|
|
305
|
-
workflowState: {
|
|
306
|
-
status: lastOutput.status,
|
|
307
|
-
steps: stepResults,
|
|
308
|
-
result: null,
|
|
309
|
-
error: base.error
|
|
310
|
-
}
|
|
311
|
-
},
|
|
312
|
-
eventTimestamp: Date.now()
|
|
313
|
-
});
|
|
314
753
|
} else if (lastOutput.status === "suspended") {
|
|
315
|
-
await emitter.emit("watch", {
|
|
316
|
-
type: "watch",
|
|
317
|
-
payload: {
|
|
318
|
-
workflowState: {
|
|
319
|
-
status: lastOutput.status,
|
|
320
|
-
steps: stepResults,
|
|
321
|
-
result: null,
|
|
322
|
-
error: null
|
|
323
|
-
}
|
|
324
|
-
},
|
|
325
|
-
eventTimestamp: Date.now()
|
|
326
|
-
});
|
|
327
754
|
const suspendedStepIds = Object.entries(stepResults).flatMap(([stepId, stepResult]) => {
|
|
328
755
|
if (stepResult?.status === "suspended") {
|
|
329
|
-
const nestedPath = stepResult?.
|
|
756
|
+
const nestedPath = stepResult?.suspendPayload?.__workflow_meta?.path;
|
|
330
757
|
return nestedPath ? [[stepId, ...nestedPath]] : [[stepId]];
|
|
331
758
|
}
|
|
332
759
|
return [];
|
|
333
760
|
});
|
|
334
761
|
base.suspended = suspendedStepIds;
|
|
335
762
|
}
|
|
336
|
-
executionSpan?.end();
|
|
337
763
|
return base;
|
|
338
764
|
}
|
|
339
|
-
async
|
|
765
|
+
// async executeSleep({ id, duration }: { id: string; duration: number }): Promise<void> {
|
|
766
|
+
// await this.inngestStep.sleep(id, duration);
|
|
767
|
+
// }
|
|
768
|
+
async executeSleep({
|
|
340
769
|
workflowId,
|
|
341
770
|
runId,
|
|
342
|
-
|
|
771
|
+
entry,
|
|
772
|
+
prevOutput,
|
|
343
773
|
stepResults,
|
|
774
|
+
emitter,
|
|
775
|
+
abortController,
|
|
776
|
+
requestContext,
|
|
344
777
|
executionContext,
|
|
345
|
-
|
|
778
|
+
writableStream,
|
|
779
|
+
tracingContext
|
|
780
|
+
}) {
|
|
781
|
+
let { duration, fn } = entry;
|
|
782
|
+
const sleepSpan = tracingContext?.currentSpan?.createChildSpan({
|
|
783
|
+
type: observability.AISpanType.WORKFLOW_SLEEP,
|
|
784
|
+
name: `sleep: ${duration ? `${duration}ms` : "dynamic"}`,
|
|
785
|
+
attributes: {
|
|
786
|
+
durationMs: duration,
|
|
787
|
+
sleepType: fn ? "dynamic" : "fixed"
|
|
788
|
+
},
|
|
789
|
+
tracingPolicy: this.options?.tracingPolicy
|
|
790
|
+
});
|
|
791
|
+
if (fn) {
|
|
792
|
+
const stepCallId = crypto.randomUUID();
|
|
793
|
+
duration = await this.inngestStep.run(`workflow.${workflowId}.sleep.${entry.id}`, async () => {
|
|
794
|
+
return await fn(
|
|
795
|
+
workflows.createDeprecationProxy(
|
|
796
|
+
{
|
|
797
|
+
runId,
|
|
798
|
+
workflowId,
|
|
799
|
+
mastra: this.mastra,
|
|
800
|
+
requestContext,
|
|
801
|
+
inputData: prevOutput,
|
|
802
|
+
state: executionContext.state,
|
|
803
|
+
setState: (state) => {
|
|
804
|
+
executionContext.state = state;
|
|
805
|
+
},
|
|
806
|
+
retryCount: -1,
|
|
807
|
+
tracingContext: {
|
|
808
|
+
currentSpan: sleepSpan
|
|
809
|
+
},
|
|
810
|
+
getInitData: () => stepResults?.input,
|
|
811
|
+
getStepResult: workflows.getStepResult.bind(this, stepResults),
|
|
812
|
+
// TODO: this function shouldn't have suspend probably?
|
|
813
|
+
suspend: async (_suspendPayload) => {
|
|
814
|
+
},
|
|
815
|
+
bail: () => {
|
|
816
|
+
},
|
|
817
|
+
abort: () => {
|
|
818
|
+
abortController?.abort();
|
|
819
|
+
},
|
|
820
|
+
[_constants.EMITTER_SYMBOL]: emitter,
|
|
821
|
+
[_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
|
|
822
|
+
engine: { step: this.inngestStep },
|
|
823
|
+
abortSignal: abortController?.signal,
|
|
824
|
+
writer: new tools.ToolStream(
|
|
825
|
+
{
|
|
826
|
+
prefix: "workflow-step",
|
|
827
|
+
callId: stepCallId,
|
|
828
|
+
name: "sleep",
|
|
829
|
+
runId
|
|
830
|
+
},
|
|
831
|
+
writableStream
|
|
832
|
+
)
|
|
833
|
+
},
|
|
834
|
+
{
|
|
835
|
+
paramName: "runCount",
|
|
836
|
+
deprecationMessage: workflows.runCountDeprecationMessage,
|
|
837
|
+
logger: this.logger
|
|
838
|
+
}
|
|
839
|
+
)
|
|
840
|
+
);
|
|
841
|
+
});
|
|
842
|
+
sleepSpan?.update({
|
|
843
|
+
attributes: {
|
|
844
|
+
durationMs: duration
|
|
845
|
+
}
|
|
846
|
+
});
|
|
847
|
+
}
|
|
848
|
+
try {
|
|
849
|
+
await this.inngestStep.sleep(entry.id, !duration || duration < 0 ? 0 : duration);
|
|
850
|
+
sleepSpan?.end();
|
|
851
|
+
} catch (e) {
|
|
852
|
+
sleepSpan?.error({ error: e });
|
|
853
|
+
throw e;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
async executeSleepUntil({
|
|
857
|
+
workflowId,
|
|
858
|
+
runId,
|
|
859
|
+
entry,
|
|
346
860
|
prevOutput,
|
|
861
|
+
stepResults,
|
|
347
862
|
emitter,
|
|
348
|
-
|
|
863
|
+
abortController,
|
|
864
|
+
requestContext,
|
|
865
|
+
executionContext,
|
|
866
|
+
writableStream,
|
|
867
|
+
tracingContext
|
|
349
868
|
}) {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
869
|
+
let { date, fn } = entry;
|
|
870
|
+
const sleepUntilSpan = tracingContext?.currentSpan?.createChildSpan({
|
|
871
|
+
type: observability.AISpanType.WORKFLOW_SLEEP,
|
|
872
|
+
name: `sleepUntil: ${date ? date.toISOString() : "dynamic"}`,
|
|
873
|
+
attributes: {
|
|
874
|
+
untilDate: date,
|
|
875
|
+
durationMs: date ? Math.max(0, date.getTime() - Date.now()) : void 0,
|
|
876
|
+
sleepType: fn ? "dynamic" : "fixed"
|
|
877
|
+
},
|
|
878
|
+
tracingPolicy: this.options?.tracingPolicy
|
|
360
879
|
});
|
|
880
|
+
if (fn) {
|
|
881
|
+
date = await this.inngestStep.run(`workflow.${workflowId}.sleepUntil.${entry.id}`, async () => {
|
|
882
|
+
const stepCallId = crypto.randomUUID();
|
|
883
|
+
return await fn(
|
|
884
|
+
workflows.createDeprecationProxy(
|
|
885
|
+
{
|
|
886
|
+
runId,
|
|
887
|
+
workflowId,
|
|
888
|
+
mastra: this.mastra,
|
|
889
|
+
requestContext,
|
|
890
|
+
inputData: prevOutput,
|
|
891
|
+
state: executionContext.state,
|
|
892
|
+
setState: (state) => {
|
|
893
|
+
executionContext.state = state;
|
|
894
|
+
},
|
|
895
|
+
retryCount: -1,
|
|
896
|
+
tracingContext: {
|
|
897
|
+
currentSpan: sleepUntilSpan
|
|
898
|
+
},
|
|
899
|
+
getInitData: () => stepResults?.input,
|
|
900
|
+
getStepResult: workflows.getStepResult.bind(this, stepResults),
|
|
901
|
+
// TODO: this function shouldn't have suspend probably?
|
|
902
|
+
suspend: async (_suspendPayload) => {
|
|
903
|
+
},
|
|
904
|
+
bail: () => {
|
|
905
|
+
},
|
|
906
|
+
abort: () => {
|
|
907
|
+
abortController?.abort();
|
|
908
|
+
},
|
|
909
|
+
[_constants.EMITTER_SYMBOL]: emitter,
|
|
910
|
+
[_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
|
|
911
|
+
engine: { step: this.inngestStep },
|
|
912
|
+
abortSignal: abortController?.signal,
|
|
913
|
+
writer: new tools.ToolStream(
|
|
914
|
+
{
|
|
915
|
+
prefix: "workflow-step",
|
|
916
|
+
callId: stepCallId,
|
|
917
|
+
name: "sleep",
|
|
918
|
+
runId
|
|
919
|
+
},
|
|
920
|
+
writableStream
|
|
921
|
+
)
|
|
922
|
+
},
|
|
923
|
+
{
|
|
924
|
+
paramName: "runCount",
|
|
925
|
+
deprecationMessage: workflows.runCountDeprecationMessage,
|
|
926
|
+
logger: this.logger
|
|
927
|
+
}
|
|
928
|
+
)
|
|
929
|
+
);
|
|
930
|
+
});
|
|
931
|
+
if (date && !(date instanceof Date)) {
|
|
932
|
+
date = new Date(date);
|
|
933
|
+
}
|
|
934
|
+
const time = !date ? 0 : date.getTime() - Date.now();
|
|
935
|
+
sleepUntilSpan?.update({
|
|
936
|
+
attributes: {
|
|
937
|
+
durationMs: Math.max(0, time)
|
|
938
|
+
}
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
if (!(date instanceof Date)) {
|
|
942
|
+
sleepUntilSpan?.end();
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
try {
|
|
946
|
+
await this.inngestStep.sleepUntil(entry.id, date);
|
|
947
|
+
sleepUntilSpan?.end();
|
|
948
|
+
} catch (e) {
|
|
949
|
+
sleepUntilSpan?.error({ error: e });
|
|
950
|
+
throw e;
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
async executeWaitForEvent({ event, timeout }) {
|
|
954
|
+
const eventData = await this.inngestStep.waitForEvent(`user-event-${event}`, {
|
|
955
|
+
event: `user-event-${event}`,
|
|
956
|
+
timeout: timeout ?? 5e3
|
|
957
|
+
});
|
|
958
|
+
if (eventData === null) {
|
|
959
|
+
throw "Timeout waiting for event";
|
|
960
|
+
}
|
|
961
|
+
return eventData?.data;
|
|
361
962
|
}
|
|
362
963
|
async executeStep({
|
|
363
964
|
step,
|
|
@@ -366,91 +967,113 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
|
|
|
366
967
|
resume,
|
|
367
968
|
prevOutput,
|
|
368
969
|
emitter,
|
|
369
|
-
|
|
970
|
+
abortController,
|
|
971
|
+
requestContext,
|
|
972
|
+
tracingContext,
|
|
973
|
+
writableStream,
|
|
974
|
+
disableScorers
|
|
370
975
|
}) {
|
|
371
|
-
|
|
976
|
+
const stepAISpan = tracingContext?.currentSpan?.createChildSpan({
|
|
977
|
+
name: `workflow step: '${step.id}'`,
|
|
978
|
+
type: observability.AISpanType.WORKFLOW_STEP,
|
|
979
|
+
input: prevOutput,
|
|
980
|
+
attributes: {
|
|
981
|
+
stepId: step.id
|
|
982
|
+
},
|
|
983
|
+
tracingPolicy: this.options?.tracingPolicy
|
|
984
|
+
});
|
|
985
|
+
const { inputData, validationError } = await workflows.validateStepInput({
|
|
986
|
+
prevOutput,
|
|
987
|
+
step,
|
|
988
|
+
validateInputs: this.options?.validateInputs ?? false
|
|
989
|
+
});
|
|
990
|
+
const startedAt = await this.inngestStep.run(
|
|
372
991
|
`workflow.${executionContext.workflowId}.run.${executionContext.runId}.step.${step.id}.running_ev`,
|
|
373
992
|
async () => {
|
|
993
|
+
const startedAt2 = Date.now();
|
|
374
994
|
await emitter.emit("watch", {
|
|
375
|
-
type: "
|
|
995
|
+
type: "workflow-step-start",
|
|
376
996
|
payload: {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
status: "running",
|
|
383
|
-
steps: {
|
|
384
|
-
...stepResults,
|
|
385
|
-
[step.id]: {
|
|
386
|
-
status: "running"
|
|
387
|
-
}
|
|
388
|
-
},
|
|
389
|
-
result: null,
|
|
390
|
-
error: null
|
|
391
|
-
}
|
|
392
|
-
},
|
|
393
|
-
eventTimestamp: Date.now()
|
|
997
|
+
id: step.id,
|
|
998
|
+
status: "running",
|
|
999
|
+
payload: inputData,
|
|
1000
|
+
startedAt: startedAt2
|
|
1001
|
+
}
|
|
394
1002
|
});
|
|
1003
|
+
return startedAt2;
|
|
395
1004
|
}
|
|
396
1005
|
);
|
|
397
1006
|
if (step instanceof InngestWorkflow) {
|
|
398
1007
|
const isResume = !!resume?.steps?.length;
|
|
399
1008
|
let result;
|
|
400
1009
|
let runId;
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
1010
|
+
try {
|
|
1011
|
+
if (isResume) {
|
|
1012
|
+
runId = stepResults[resume?.steps?.[0]]?.suspendPayload?.__workflow_meta?.runId ?? crypto.randomUUID();
|
|
1013
|
+
const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
|
|
1014
|
+
workflowName: step.id,
|
|
1015
|
+
runId
|
|
1016
|
+
});
|
|
1017
|
+
const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
|
|
1018
|
+
function: step.getFunction(),
|
|
1019
|
+
data: {
|
|
1020
|
+
inputData,
|
|
1021
|
+
initialState: executionContext.state ?? snapshot?.value ?? {},
|
|
413
1022
|
runId,
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
1023
|
+
resume: {
|
|
1024
|
+
runId,
|
|
1025
|
+
steps: resume.steps.slice(1),
|
|
1026
|
+
stepResults: snapshot?.context,
|
|
1027
|
+
resumePayload: resume.resumePayload,
|
|
1028
|
+
// @ts-ignore
|
|
1029
|
+
resumePath: snapshot?.suspendedPaths?.[resume.steps?.[1]]
|
|
1030
|
+
},
|
|
1031
|
+
outputOptions: { includeState: true }
|
|
419
1032
|
}
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
1033
|
+
});
|
|
1034
|
+
result = invokeResp.result;
|
|
1035
|
+
runId = invokeResp.runId;
|
|
1036
|
+
executionContext.state = invokeResp.result.state;
|
|
1037
|
+
} else {
|
|
1038
|
+
const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
|
|
1039
|
+
function: step.getFunction(),
|
|
1040
|
+
data: {
|
|
1041
|
+
inputData,
|
|
1042
|
+
initialState: executionContext.state ?? {},
|
|
1043
|
+
outputOptions: { includeState: true }
|
|
1044
|
+
}
|
|
1045
|
+
});
|
|
1046
|
+
result = invokeResp.result;
|
|
1047
|
+
runId = invokeResp.runId;
|
|
1048
|
+
executionContext.state = invokeResp.result.state;
|
|
1049
|
+
}
|
|
1050
|
+
} catch (e) {
|
|
1051
|
+
const errorCause = e?.cause;
|
|
1052
|
+
if (errorCause && typeof errorCause === "object") {
|
|
1053
|
+
result = errorCause;
|
|
1054
|
+
runId = errorCause.runId || crypto.randomUUID();
|
|
1055
|
+
} else {
|
|
1056
|
+
runId = crypto.randomUUID();
|
|
1057
|
+
result = {
|
|
1058
|
+
status: "failed",
|
|
1059
|
+
error: e instanceof Error ? e : new Error(String(e)),
|
|
1060
|
+
steps: {},
|
|
1061
|
+
input: inputData
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
433
1064
|
}
|
|
434
1065
|
const res = await this.inngestStep.run(
|
|
435
1066
|
`workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
|
|
436
1067
|
async () => {
|
|
437
1068
|
if (result.status === "failed") {
|
|
438
1069
|
await emitter.emit("watch", {
|
|
439
|
-
type: "
|
|
1070
|
+
type: "workflow-step-result",
|
|
440
1071
|
payload: {
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
workflowState: {
|
|
447
|
-
status: "running",
|
|
448
|
-
steps: stepResults,
|
|
449
|
-
result: null,
|
|
450
|
-
error: null
|
|
451
|
-
}
|
|
452
|
-
},
|
|
453
|
-
eventTimestamp: Date.now()
|
|
1072
|
+
id: step.id,
|
|
1073
|
+
status: "failed",
|
|
1074
|
+
error: result?.error,
|
|
1075
|
+
payload: prevOutput
|
|
1076
|
+
}
|
|
454
1077
|
});
|
|
455
1078
|
return { executionContext, result: { status: "failed", error: result?.error } };
|
|
456
1079
|
} else if (result.status === "suspended") {
|
|
@@ -459,50 +1082,27 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
|
|
|
459
1082
|
return stepRes2?.status === "suspended";
|
|
460
1083
|
});
|
|
461
1084
|
for (const [stepName, stepResult] of suspendedSteps) {
|
|
462
|
-
const suspendPath = [stepName, ...stepResult?.
|
|
1085
|
+
const suspendPath = [stepName, ...stepResult?.suspendPayload?.__workflow_meta?.path ?? []];
|
|
463
1086
|
executionContext.suspendedPaths[step.id] = executionContext.executionPath;
|
|
464
1087
|
await emitter.emit("watch", {
|
|
465
|
-
type: "
|
|
1088
|
+
type: "workflow-step-suspended",
|
|
466
1089
|
payload: {
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
payload: { ...stepResult?.payload, __workflow_meta: { runId, path: suspendPath } }
|
|
471
|
-
},
|
|
472
|
-
workflowState: {
|
|
473
|
-
status: "running",
|
|
474
|
-
steps: stepResults,
|
|
475
|
-
result: null,
|
|
476
|
-
error: null
|
|
477
|
-
}
|
|
478
|
-
},
|
|
479
|
-
eventTimestamp: Date.now()
|
|
1090
|
+
id: step.id,
|
|
1091
|
+
status: "suspended"
|
|
1092
|
+
}
|
|
480
1093
|
});
|
|
481
1094
|
return {
|
|
482
1095
|
executionContext,
|
|
483
1096
|
result: {
|
|
484
1097
|
status: "suspended",
|
|
485
|
-
payload:
|
|
1098
|
+
payload: stepResult.payload,
|
|
1099
|
+
suspendPayload: {
|
|
1100
|
+
...stepResult?.suspendPayload,
|
|
1101
|
+
__workflow_meta: { runId, path: suspendPath }
|
|
1102
|
+
}
|
|
486
1103
|
}
|
|
487
1104
|
};
|
|
488
1105
|
}
|
|
489
|
-
await emitter.emit("watch", {
|
|
490
|
-
type: "watch",
|
|
491
|
-
payload: {
|
|
492
|
-
currentStep: {
|
|
493
|
-
id: step.id,
|
|
494
|
-
status: "suspended",
|
|
495
|
-
payload: {}
|
|
496
|
-
},
|
|
497
|
-
workflowState: {
|
|
498
|
-
status: "running",
|
|
499
|
-
steps: stepResults,
|
|
500
|
-
result: null,
|
|
501
|
-
error: null
|
|
502
|
-
}
|
|
503
|
-
},
|
|
504
|
-
eventTimestamp: Date.now()
|
|
505
|
-
});
|
|
506
1106
|
return {
|
|
507
1107
|
executionContext,
|
|
508
1108
|
result: {
|
|
@@ -512,110 +1112,243 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
|
|
|
512
1112
|
};
|
|
513
1113
|
}
|
|
514
1114
|
await emitter.emit("watch", {
|
|
515
|
-
type: "
|
|
1115
|
+
type: "workflow-step-result",
|
|
516
1116
|
payload: {
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
},
|
|
529
|
-
eventTimestamp: Date.now()
|
|
1117
|
+
id: step.id,
|
|
1118
|
+
status: "success",
|
|
1119
|
+
output: result?.result
|
|
1120
|
+
}
|
|
1121
|
+
});
|
|
1122
|
+
await emitter.emit("watch", {
|
|
1123
|
+
type: "workflow-step-finish",
|
|
1124
|
+
payload: {
|
|
1125
|
+
id: step.id,
|
|
1126
|
+
metadata: {}
|
|
1127
|
+
}
|
|
530
1128
|
});
|
|
531
1129
|
return { executionContext, result: { status: "success", output: result?.result } };
|
|
532
1130
|
}
|
|
533
1131
|
);
|
|
534
1132
|
Object.assign(executionContext, res.executionContext);
|
|
535
|
-
return
|
|
1133
|
+
return {
|
|
1134
|
+
...res.result,
|
|
1135
|
+
startedAt,
|
|
1136
|
+
endedAt: Date.now(),
|
|
1137
|
+
payload: inputData,
|
|
1138
|
+
resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
|
|
1139
|
+
resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
|
|
1140
|
+
};
|
|
536
1141
|
}
|
|
537
|
-
const
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
1142
|
+
const stepCallId = crypto.randomUUID();
|
|
1143
|
+
let stepRes;
|
|
1144
|
+
try {
|
|
1145
|
+
stepRes = await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}`, async () => {
|
|
1146
|
+
let execResults;
|
|
1147
|
+
let suspended;
|
|
1148
|
+
let bailed;
|
|
1149
|
+
try {
|
|
1150
|
+
if (validationError) {
|
|
1151
|
+
throw validationError;
|
|
1152
|
+
}
|
|
1153
|
+
const result = await step.execute({
|
|
1154
|
+
runId: executionContext.runId,
|
|
1155
|
+
mastra: this.mastra,
|
|
1156
|
+
requestContext,
|
|
1157
|
+
writer: new tools.ToolStream(
|
|
1158
|
+
{
|
|
1159
|
+
prefix: "workflow-step",
|
|
1160
|
+
callId: stepCallId,
|
|
1161
|
+
name: step.id,
|
|
1162
|
+
runId: executionContext.runId
|
|
1163
|
+
},
|
|
1164
|
+
writableStream
|
|
1165
|
+
),
|
|
1166
|
+
state: executionContext?.state ?? {},
|
|
1167
|
+
setState: (state) => {
|
|
1168
|
+
executionContext.state = state;
|
|
1169
|
+
},
|
|
1170
|
+
inputData,
|
|
1171
|
+
resumeData: resume?.steps[0] === step.id ? resume?.resumePayload : void 0,
|
|
1172
|
+
tracingContext: {
|
|
1173
|
+
currentSpan: stepAISpan
|
|
1174
|
+
},
|
|
1175
|
+
getInitData: () => stepResults?.input,
|
|
1176
|
+
getStepResult: workflows.getStepResult.bind(this, stepResults),
|
|
1177
|
+
suspend: async (suspendPayload, suspendOptions) => {
|
|
1178
|
+
executionContext.suspendedPaths[step.id] = executionContext.executionPath;
|
|
1179
|
+
if (suspendOptions?.resumeLabel) {
|
|
1180
|
+
const resumeLabel = Array.isArray(suspendOptions.resumeLabel) ? suspendOptions.resumeLabel : [suspendOptions.resumeLabel];
|
|
1181
|
+
for (const label of resumeLabel) {
|
|
1182
|
+
executionContext.resumeLabels[label] = {
|
|
1183
|
+
stepId: step.id,
|
|
1184
|
+
foreachIndex: executionContext.foreachIndex
|
|
1185
|
+
};
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
suspended = { payload: suspendPayload };
|
|
1189
|
+
},
|
|
1190
|
+
bail: (result2) => {
|
|
1191
|
+
bailed = { payload: result2 };
|
|
1192
|
+
},
|
|
1193
|
+
resume: {
|
|
1194
|
+
steps: resume?.steps?.slice(1) || [],
|
|
1195
|
+
resumePayload: resume?.resumePayload,
|
|
1196
|
+
// @ts-ignore
|
|
1197
|
+
runId: stepResults[step.id]?.suspendPayload?.__workflow_meta?.runId
|
|
1198
|
+
},
|
|
1199
|
+
[_constants.EMITTER_SYMBOL]: emitter,
|
|
1200
|
+
[_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
|
|
1201
|
+
engine: {
|
|
1202
|
+
step: this.inngestStep
|
|
1203
|
+
},
|
|
1204
|
+
abortSignal: abortController.signal
|
|
1205
|
+
});
|
|
1206
|
+
const endedAt = Date.now();
|
|
1207
|
+
execResults = {
|
|
1208
|
+
status: "success",
|
|
1209
|
+
output: result,
|
|
1210
|
+
startedAt,
|
|
1211
|
+
endedAt,
|
|
1212
|
+
payload: inputData,
|
|
1213
|
+
resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
|
|
1214
|
+
resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
|
|
1215
|
+
};
|
|
1216
|
+
} catch (e) {
|
|
1217
|
+
const stepFailure = {
|
|
1218
|
+
status: "failed",
|
|
1219
|
+
payload: inputData,
|
|
1220
|
+
error: e instanceof Error ? e.message : String(e),
|
|
1221
|
+
endedAt: Date.now(),
|
|
1222
|
+
startedAt,
|
|
1223
|
+
resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
|
|
1224
|
+
resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
|
|
1225
|
+
};
|
|
1226
|
+
execResults = stepFailure;
|
|
1227
|
+
const fallbackErrorMessage = `Step ${step.id} failed`;
|
|
1228
|
+
stepAISpan?.error({ error: new Error(execResults.error ?? fallbackErrorMessage) });
|
|
1229
|
+
throw new inngest.RetryAfterError(execResults.error ?? fallbackErrorMessage, executionContext.retryConfig.delay, {
|
|
1230
|
+
cause: execResults
|
|
1231
|
+
});
|
|
1232
|
+
}
|
|
1233
|
+
if (suspended) {
|
|
1234
|
+
execResults = {
|
|
1235
|
+
status: "suspended",
|
|
1236
|
+
suspendPayload: suspended.payload,
|
|
1237
|
+
payload: inputData,
|
|
1238
|
+
suspendedAt: Date.now(),
|
|
1239
|
+
startedAt,
|
|
1240
|
+
resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
|
|
1241
|
+
resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
|
|
1242
|
+
};
|
|
1243
|
+
} else if (bailed) {
|
|
1244
|
+
execResults = {
|
|
1245
|
+
status: "bailed",
|
|
1246
|
+
output: bailed.payload,
|
|
1247
|
+
payload: inputData,
|
|
1248
|
+
endedAt: Date.now(),
|
|
1249
|
+
startedAt
|
|
1250
|
+
};
|
|
1251
|
+
}
|
|
1252
|
+
if (execResults.status === "suspended") {
|
|
1253
|
+
await emitter.emit("watch", {
|
|
1254
|
+
type: "workflow-step-suspended",
|
|
1255
|
+
payload: {
|
|
1256
|
+
id: step.id,
|
|
1257
|
+
...execResults
|
|
551
1258
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
execResults = { status: "failed", error: e instanceof Error ? e.message : String(e) };
|
|
569
|
-
}
|
|
570
|
-
if (suspended) {
|
|
571
|
-
execResults = { status: "suspended", payload: suspended.payload };
|
|
572
|
-
}
|
|
573
|
-
if (execResults.status === "failed") {
|
|
574
|
-
if (executionContext.retryConfig.attempts > 0 && this.inngestAttempts < executionContext.retryConfig.attempts) {
|
|
575
|
-
throw execResults.error;
|
|
1259
|
+
});
|
|
1260
|
+
} else {
|
|
1261
|
+
await emitter.emit("watch", {
|
|
1262
|
+
type: "workflow-step-result",
|
|
1263
|
+
payload: {
|
|
1264
|
+
id: step.id,
|
|
1265
|
+
...execResults
|
|
1266
|
+
}
|
|
1267
|
+
});
|
|
1268
|
+
await emitter.emit("watch", {
|
|
1269
|
+
type: "workflow-step-finish",
|
|
1270
|
+
payload: {
|
|
1271
|
+
id: step.id,
|
|
1272
|
+
metadata: {}
|
|
1273
|
+
}
|
|
1274
|
+
});
|
|
576
1275
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
type: "watch",
|
|
580
|
-
payload: {
|
|
581
|
-
currentStep: {
|
|
582
|
-
id: step.id,
|
|
583
|
-
status: execResults.status,
|
|
584
|
-
output: execResults.output
|
|
585
|
-
},
|
|
586
|
-
workflowState: {
|
|
587
|
-
status: "running",
|
|
588
|
-
steps: stepResults,
|
|
589
|
-
result: null,
|
|
590
|
-
error: null
|
|
591
|
-
}
|
|
592
|
-
},
|
|
593
|
-
eventTimestamp: Date.now()
|
|
1276
|
+
stepAISpan?.end({ output: execResults });
|
|
1277
|
+
return { result: execResults, executionContext, stepResults };
|
|
594
1278
|
});
|
|
595
|
-
|
|
596
|
-
|
|
1279
|
+
} catch (e) {
|
|
1280
|
+
const stepFailure = e instanceof Error ? e?.cause : {
|
|
1281
|
+
status: "failed",
|
|
1282
|
+
error: e instanceof Error ? e.message : String(e),
|
|
1283
|
+
payload: inputData,
|
|
1284
|
+
startedAt,
|
|
1285
|
+
endedAt: Date.now()
|
|
1286
|
+
};
|
|
1287
|
+
stepRes = {
|
|
1288
|
+
result: stepFailure,
|
|
1289
|
+
executionContext,
|
|
1290
|
+
stepResults: {
|
|
1291
|
+
...stepResults,
|
|
1292
|
+
[step.id]: stepFailure
|
|
1293
|
+
}
|
|
1294
|
+
};
|
|
1295
|
+
}
|
|
1296
|
+
if (disableScorers !== false && stepRes.result.status === "success") {
|
|
1297
|
+
await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}.score`, async () => {
|
|
1298
|
+
if (step.scorers) {
|
|
1299
|
+
await this.runScorers({
|
|
1300
|
+
scorers: step.scorers,
|
|
1301
|
+
runId: executionContext.runId,
|
|
1302
|
+
input: inputData,
|
|
1303
|
+
output: stepRes.result,
|
|
1304
|
+
workflowId: executionContext.workflowId,
|
|
1305
|
+
stepId: step.id,
|
|
1306
|
+
requestContext,
|
|
1307
|
+
disableScorers,
|
|
1308
|
+
tracingContext: { currentSpan: stepAISpan }
|
|
1309
|
+
});
|
|
1310
|
+
}
|
|
1311
|
+
});
|
|
1312
|
+
}
|
|
597
1313
|
Object.assign(executionContext.suspendedPaths, stepRes.executionContext.suspendedPaths);
|
|
598
1314
|
Object.assign(stepResults, stepRes.stepResults);
|
|
1315
|
+
executionContext.state = stepRes.executionContext.state;
|
|
599
1316
|
return stepRes.result;
|
|
600
1317
|
}
|
|
601
1318
|
async persistStepUpdate({
|
|
602
1319
|
workflowId,
|
|
603
1320
|
runId,
|
|
604
1321
|
stepResults,
|
|
605
|
-
|
|
1322
|
+
resourceId,
|
|
1323
|
+
executionContext,
|
|
1324
|
+
serializedStepGraph,
|
|
1325
|
+
workflowStatus,
|
|
1326
|
+
result,
|
|
1327
|
+
error
|
|
606
1328
|
}) {
|
|
607
1329
|
await this.inngestStep.run(
|
|
608
1330
|
`workflow.${workflowId}.run.${runId}.path.${JSON.stringify(executionContext.executionPath)}.stepUpdate`,
|
|
609
1331
|
async () => {
|
|
1332
|
+
const shouldPersistSnapshot = this.options.shouldPersistSnapshot({ stepResults, workflowStatus });
|
|
1333
|
+
if (!shouldPersistSnapshot) {
|
|
1334
|
+
return;
|
|
1335
|
+
}
|
|
610
1336
|
await this.mastra?.getStorage()?.persistWorkflowSnapshot({
|
|
611
1337
|
workflowName: workflowId,
|
|
612
1338
|
runId,
|
|
1339
|
+
resourceId,
|
|
613
1340
|
snapshot: {
|
|
614
1341
|
runId,
|
|
615
|
-
value:
|
|
1342
|
+
value: executionContext.state,
|
|
616
1343
|
context: stepResults,
|
|
617
1344
|
activePaths: [],
|
|
618
1345
|
suspendedPaths: executionContext.suspendedPaths,
|
|
1346
|
+
resumeLabels: executionContext.resumeLabels,
|
|
1347
|
+
waitingPaths: {},
|
|
1348
|
+
serializedStepGraph,
|
|
1349
|
+
status: workflowStatus,
|
|
1350
|
+
result,
|
|
1351
|
+
error,
|
|
619
1352
|
// @ts-ignore
|
|
620
1353
|
timestamp: Date.now()
|
|
621
1354
|
}
|
|
@@ -628,53 +1361,123 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
|
|
|
628
1361
|
runId,
|
|
629
1362
|
entry,
|
|
630
1363
|
prevOutput,
|
|
631
|
-
prevStep,
|
|
632
1364
|
stepResults,
|
|
633
1365
|
resume,
|
|
634
1366
|
executionContext,
|
|
635
1367
|
emitter,
|
|
636
|
-
|
|
1368
|
+
abortController,
|
|
1369
|
+
requestContext,
|
|
1370
|
+
writableStream,
|
|
1371
|
+
disableScorers,
|
|
1372
|
+
tracingContext
|
|
637
1373
|
}) {
|
|
1374
|
+
const conditionalSpan = tracingContext?.currentSpan?.createChildSpan({
|
|
1375
|
+
type: observability.AISpanType.WORKFLOW_CONDITIONAL,
|
|
1376
|
+
name: `conditional: '${entry.conditions.length} conditions'`,
|
|
1377
|
+
input: prevOutput,
|
|
1378
|
+
attributes: {
|
|
1379
|
+
conditionCount: entry.conditions.length
|
|
1380
|
+
},
|
|
1381
|
+
tracingPolicy: this.options?.tracingPolicy
|
|
1382
|
+
});
|
|
638
1383
|
let execResults;
|
|
639
1384
|
const truthyIndexes = (await Promise.all(
|
|
640
1385
|
entry.conditions.map(
|
|
641
1386
|
(cond, index) => this.inngestStep.run(`workflow.${workflowId}.conditional.${index}`, async () => {
|
|
1387
|
+
const evalSpan = conditionalSpan?.createChildSpan({
|
|
1388
|
+
type: observability.AISpanType.WORKFLOW_CONDITIONAL_EVAL,
|
|
1389
|
+
name: `condition: '${index}'`,
|
|
1390
|
+
input: prevOutput,
|
|
1391
|
+
attributes: {
|
|
1392
|
+
conditionIndex: index
|
|
1393
|
+
},
|
|
1394
|
+
tracingPolicy: this.options?.tracingPolicy
|
|
1395
|
+
});
|
|
642
1396
|
try {
|
|
643
|
-
const result = await cond(
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
1397
|
+
const result = await cond(
|
|
1398
|
+
workflows.createDeprecationProxy(
|
|
1399
|
+
{
|
|
1400
|
+
runId,
|
|
1401
|
+
workflowId,
|
|
1402
|
+
mastra: this.mastra,
|
|
1403
|
+
requestContext,
|
|
1404
|
+
retryCount: -1,
|
|
1405
|
+
inputData: prevOutput,
|
|
1406
|
+
state: executionContext.state,
|
|
1407
|
+
setState: (state) => {
|
|
1408
|
+
executionContext.state = state;
|
|
1409
|
+
},
|
|
1410
|
+
tracingContext: {
|
|
1411
|
+
currentSpan: evalSpan
|
|
1412
|
+
},
|
|
1413
|
+
getInitData: () => stepResults?.input,
|
|
1414
|
+
getStepResult: workflows.getStepResult.bind(this, stepResults),
|
|
1415
|
+
// TODO: this function shouldn't have suspend probably?
|
|
1416
|
+
suspend: async (_suspendPayload) => {
|
|
1417
|
+
},
|
|
1418
|
+
bail: () => {
|
|
1419
|
+
},
|
|
1420
|
+
abort: () => {
|
|
1421
|
+
abortController.abort();
|
|
1422
|
+
},
|
|
1423
|
+
[_constants.EMITTER_SYMBOL]: emitter,
|
|
1424
|
+
[_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
|
|
1425
|
+
engine: {
|
|
1426
|
+
step: this.inngestStep
|
|
1427
|
+
},
|
|
1428
|
+
abortSignal: abortController.signal,
|
|
1429
|
+
writer: new tools.ToolStream(
|
|
1430
|
+
{
|
|
1431
|
+
prefix: "workflow-step",
|
|
1432
|
+
callId: crypto.randomUUID(),
|
|
1433
|
+
name: "conditional",
|
|
1434
|
+
runId
|
|
1435
|
+
},
|
|
1436
|
+
writableStream
|
|
1437
|
+
)
|
|
1438
|
+
},
|
|
1439
|
+
{
|
|
1440
|
+
paramName: "runCount",
|
|
1441
|
+
deprecationMessage: workflows.runCountDeprecationMessage,
|
|
1442
|
+
logger: this.logger
|
|
655
1443
|
}
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
1444
|
+
)
|
|
1445
|
+
);
|
|
1446
|
+
evalSpan?.end({
|
|
1447
|
+
output: result,
|
|
1448
|
+
attributes: {
|
|
1449
|
+
result: !!result
|
|
1450
|
+
}
|
|
662
1451
|
});
|
|
663
1452
|
return result ? index : null;
|
|
664
1453
|
} catch (e) {
|
|
1454
|
+
evalSpan?.error({
|
|
1455
|
+
error: e instanceof Error ? e : new Error(String(e)),
|
|
1456
|
+
attributes: {
|
|
1457
|
+
result: false
|
|
1458
|
+
}
|
|
1459
|
+
});
|
|
665
1460
|
return null;
|
|
666
1461
|
}
|
|
667
1462
|
})
|
|
668
1463
|
)
|
|
669
1464
|
)).filter((index) => index !== null);
|
|
670
1465
|
const stepsToRun = entry.steps.filter((_, index) => truthyIndexes.includes(index));
|
|
1466
|
+
conditionalSpan?.update({
|
|
1467
|
+
attributes: {
|
|
1468
|
+
truthyIndexes,
|
|
1469
|
+
selectedSteps: stepsToRun.map((s) => s.type === "step" ? s.step.id : `control-${s.type}`)
|
|
1470
|
+
}
|
|
1471
|
+
});
|
|
671
1472
|
const results = await Promise.all(
|
|
672
|
-
stepsToRun.map(
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
1473
|
+
stepsToRun.map(async (step, index) => {
|
|
1474
|
+
const currStepResult = stepResults[step.step.id];
|
|
1475
|
+
if (currStepResult && currStepResult.status === "success") {
|
|
1476
|
+
return currStepResult;
|
|
1477
|
+
}
|
|
1478
|
+
const result = await this.executeStep({
|
|
1479
|
+
step: step.step,
|
|
1480
|
+
prevOutput,
|
|
678
1481
|
stepResults,
|
|
679
1482
|
resume,
|
|
680
1483
|
executionContext: {
|
|
@@ -682,20 +1485,29 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
|
|
|
682
1485
|
runId,
|
|
683
1486
|
executionPath: [...executionContext.executionPath, index],
|
|
684
1487
|
suspendedPaths: executionContext.suspendedPaths,
|
|
1488
|
+
resumeLabels: executionContext.resumeLabels,
|
|
685
1489
|
retryConfig: executionContext.retryConfig,
|
|
686
|
-
|
|
1490
|
+
state: executionContext.state
|
|
687
1491
|
},
|
|
688
1492
|
emitter,
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
1493
|
+
abortController,
|
|
1494
|
+
requestContext,
|
|
1495
|
+
writableStream,
|
|
1496
|
+
disableScorers,
|
|
1497
|
+
tracingContext: {
|
|
1498
|
+
currentSpan: conditionalSpan
|
|
1499
|
+
}
|
|
1500
|
+
});
|
|
1501
|
+
stepResults[step.step.id] = result;
|
|
1502
|
+
return result;
|
|
1503
|
+
})
|
|
692
1504
|
);
|
|
693
1505
|
const hasFailed = results.find((result) => result.status === "failed");
|
|
694
1506
|
const hasSuspended = results.find((result) => result.status === "suspended");
|
|
695
1507
|
if (hasFailed) {
|
|
696
1508
|
execResults = { status: "failed", error: hasFailed.error };
|
|
697
1509
|
} else if (hasSuspended) {
|
|
698
|
-
execResults = { status: "suspended",
|
|
1510
|
+
execResults = { status: "suspended", suspendPayload: hasSuspended.suspendPayload };
|
|
699
1511
|
} else {
|
|
700
1512
|
execResults = {
|
|
701
1513
|
status: "success",
|
|
@@ -707,6 +1519,15 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
|
|
|
707
1519
|
}, {})
|
|
708
1520
|
};
|
|
709
1521
|
}
|
|
1522
|
+
if (execResults.status === "failed") {
|
|
1523
|
+
conditionalSpan?.error({
|
|
1524
|
+
error: new Error(execResults.error)
|
|
1525
|
+
});
|
|
1526
|
+
} else {
|
|
1527
|
+
conditionalSpan?.end({
|
|
1528
|
+
output: execResults.output || execResults
|
|
1529
|
+
});
|
|
1530
|
+
}
|
|
710
1531
|
return execResults;
|
|
711
1532
|
}
|
|
712
1533
|
};
|
|
@@ -714,5 +1535,8 @@ var InngestExecutionEngine = class extends vNext.DefaultExecutionEngine {
|
|
|
714
1535
|
exports.InngestExecutionEngine = InngestExecutionEngine;
|
|
715
1536
|
exports.InngestRun = InngestRun;
|
|
716
1537
|
exports.InngestWorkflow = InngestWorkflow;
|
|
1538
|
+
exports.createStep = createStep;
|
|
717
1539
|
exports.init = init;
|
|
718
1540
|
exports.serve = serve;
|
|
1541
|
+
//# sourceMappingURL=index.cjs.map
|
|
1542
|
+
//# sourceMappingURL=index.cjs.map
|