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