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