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