@mastra/inngest 0.0.0-feat-add-query-option-to-playground-20251209160219 → 0.0.0-feat-8782-cf-bindings-20260102164434
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 +771 -12
- package/dist/execution-engine.d.ts +31 -8
- package/dist/execution-engine.d.ts.map +1 -1
- package/dist/index.cjs +560 -184
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +21 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +556 -181
- 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 +36 -16
- package/dist/run.d.ts.map +1 -1
- package/dist/types.d.ts +7 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/workflow.d.ts +3 -0
- package/dist/workflow.d.ts.map +1 -1
- package/package.json +7 -8
package/dist/index.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
+
import { Agent } from '@mastra/core/agent';
|
|
1
2
|
import { Tool } from '@mastra/core/tools';
|
|
2
|
-
import { DefaultExecutionEngine, createTimeTravelExecutionParams, Run, Workflow } from '@mastra/core/workflows';
|
|
3
|
-
import {
|
|
3
|
+
import { DefaultExecutionEngine, createTimeTravelExecutionParams, Run, hydrateSerializedStepErrors, Workflow } from '@mastra/core/workflows';
|
|
4
|
+
import { PUBSUB_SYMBOL, STREAM_FORMAT_SYMBOL } from '@mastra/core/workflows/_constants';
|
|
4
5
|
import { z } from 'zod';
|
|
5
6
|
import { randomUUID } from 'crypto';
|
|
6
7
|
import { RequestContext } from '@mastra/core/di';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
8
|
+
import { NonRetriableError } from 'inngest';
|
|
9
|
+
import { getErrorFromUnknown } from '@mastra/core/error';
|
|
9
10
|
import { subscribe } from '@inngest/realtime';
|
|
11
|
+
import { PubSub } from '@mastra/core/events';
|
|
12
|
+
import { ReadableStream } from 'stream/web';
|
|
10
13
|
import { ChunkFrom, WorkflowRunOutput } from '@mastra/core/stream';
|
|
11
14
|
import { serve as serve$1 } from 'inngest/hono';
|
|
12
15
|
|
|
@@ -23,17 +26,18 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
23
26
|
// Hook Overrides
|
|
24
27
|
// =============================================================================
|
|
25
28
|
/**
|
|
26
|
-
* Format errors
|
|
29
|
+
* Format errors while preserving Error instances and their custom properties.
|
|
30
|
+
* Uses getErrorFromUnknown to ensure all error properties are preserved.
|
|
27
31
|
*/
|
|
28
32
|
formatResultError(error, lastOutput) {
|
|
29
|
-
if (error instanceof Error) {
|
|
30
|
-
return error.stack ?? error.message;
|
|
31
|
-
}
|
|
32
33
|
const outputError = lastOutput?.error;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
const errorSource = error || outputError;
|
|
35
|
+
const errorInstance = getErrorFromUnknown(errorSource, {
|
|
36
|
+
serializeStack: true,
|
|
37
|
+
// Include stack in JSON for better debugging in Inngest
|
|
38
|
+
fallbackMessage: "Unknown workflow error"
|
|
39
|
+
});
|
|
40
|
+
return errorInstance.toJSON();
|
|
37
41
|
}
|
|
38
42
|
/**
|
|
39
43
|
* Detect InngestWorkflow instances for special nested workflow handling
|
|
@@ -55,32 +59,46 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
55
59
|
* After retries exhausted, error propagates here and we return a failed result.
|
|
56
60
|
*/
|
|
57
61
|
async executeStepWithRetry(stepId, runStep, params) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
} catch (e) {
|
|
62
|
-
const cause = e?.cause;
|
|
63
|
-
if (cause?.status === "failed") {
|
|
64
|
-
params.stepSpan?.error({
|
|
65
|
-
error: e,
|
|
66
|
-
attributes: { status: "failed" }
|
|
67
|
-
});
|
|
68
|
-
return { ok: false, error: cause };
|
|
62
|
+
for (let i = 0; i < params.retries + 1; i++) {
|
|
63
|
+
if (i > 0 && params.delay) {
|
|
64
|
+
await new Promise((resolve) => setTimeout(resolve, params.delay));
|
|
69
65
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
66
|
+
try {
|
|
67
|
+
const result = await this.wrapDurableOperation(stepId, runStep);
|
|
68
|
+
return { ok: true, result };
|
|
69
|
+
} catch (e) {
|
|
70
|
+
if (i === params.retries) {
|
|
71
|
+
const cause = e?.cause;
|
|
72
|
+
if (cause?.status === "failed") {
|
|
73
|
+
params.stepSpan?.error({
|
|
74
|
+
error: e,
|
|
75
|
+
attributes: { status: "failed" }
|
|
76
|
+
});
|
|
77
|
+
if (cause.error && !(cause.error instanceof Error)) {
|
|
78
|
+
cause.error = getErrorFromUnknown(cause.error, { serializeStack: false });
|
|
79
|
+
}
|
|
80
|
+
return { ok: false, error: cause };
|
|
81
|
+
}
|
|
82
|
+
const errorInstance = getErrorFromUnknown(e, {
|
|
83
|
+
serializeStack: false,
|
|
84
|
+
fallbackMessage: "Unknown step execution error"
|
|
85
|
+
});
|
|
86
|
+
params.stepSpan?.error({
|
|
87
|
+
error: errorInstance,
|
|
88
|
+
attributes: { status: "failed" }
|
|
89
|
+
});
|
|
90
|
+
return {
|
|
91
|
+
ok: false,
|
|
92
|
+
error: {
|
|
93
|
+
status: "failed",
|
|
94
|
+
error: errorInstance,
|
|
95
|
+
endedAt: Date.now()
|
|
96
|
+
}
|
|
97
|
+
};
|
|
81
98
|
}
|
|
82
|
-
}
|
|
99
|
+
}
|
|
83
100
|
}
|
|
101
|
+
return { ok: false, error: { status: "failed", error: new Error("Unknown error"), endedAt: Date.now() } };
|
|
84
102
|
}
|
|
85
103
|
/**
|
|
86
104
|
* Use Inngest's sleep primitive for durability
|
|
@@ -99,21 +117,11 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
99
117
|
* If retryConfig is provided, throws RetryAfterError INSIDE step.run() to trigger
|
|
100
118
|
* Inngest's step-level retry mechanism (not function-level retry).
|
|
101
119
|
*/
|
|
102
|
-
async wrapDurableOperation(operationId, operationFn
|
|
120
|
+
async wrapDurableOperation(operationId, operationFn) {
|
|
103
121
|
return this.inngestStep.run(operationId, async () => {
|
|
104
122
|
try {
|
|
105
123
|
return await operationFn();
|
|
106
124
|
} catch (e) {
|
|
107
|
-
if (retryConfig) {
|
|
108
|
-
const errorMessage = e instanceof Error ? e.message : String(e);
|
|
109
|
-
throw new RetryAfterError(errorMessage, retryConfig.delay, {
|
|
110
|
-
cause: {
|
|
111
|
-
status: "failed",
|
|
112
|
-
error: `Error: ${errorMessage}`,
|
|
113
|
-
endedAt: Date.now()
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
125
|
throw e;
|
|
118
126
|
}
|
|
119
127
|
});
|
|
@@ -124,6 +132,18 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
124
132
|
getEngineContext() {
|
|
125
133
|
return { step: this.inngestStep };
|
|
126
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* For Inngest, lifecycle callbacks are invoked in the workflow's finalize step
|
|
137
|
+
* (wrapped in step.run for durability), not in execute(). Override to skip.
|
|
138
|
+
*/
|
|
139
|
+
async invokeLifecycleCallbacks(_result) {
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Actually invoke the lifecycle callbacks. Called from workflow.ts finalize step.
|
|
143
|
+
*/
|
|
144
|
+
async invokeLifecycleCallbacksInternal(result) {
|
|
145
|
+
return super.invokeLifecycleCallbacks(result);
|
|
146
|
+
}
|
|
127
147
|
/**
|
|
128
148
|
* Execute nested InngestWorkflow using inngestStep.invoke() for durability.
|
|
129
149
|
* This MUST be called directly (not inside step.run()) due to Inngest constraints.
|
|
@@ -132,7 +152,18 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
132
152
|
if (!(params.step instanceof InngestWorkflow)) {
|
|
133
153
|
return null;
|
|
134
154
|
}
|
|
135
|
-
const {
|
|
155
|
+
const {
|
|
156
|
+
step,
|
|
157
|
+
stepResults,
|
|
158
|
+
executionContext,
|
|
159
|
+
resume,
|
|
160
|
+
timeTravel,
|
|
161
|
+
prevOutput,
|
|
162
|
+
inputData,
|
|
163
|
+
pubsub,
|
|
164
|
+
startedAt,
|
|
165
|
+
perStep
|
|
166
|
+
} = params;
|
|
136
167
|
const isResume = !!resume?.steps?.length;
|
|
137
168
|
let result;
|
|
138
169
|
let runId;
|
|
@@ -140,7 +171,8 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
140
171
|
try {
|
|
141
172
|
if (isResume) {
|
|
142
173
|
runId = stepResults[resume?.steps?.[0] ?? ""]?.suspendPayload?.__workflow_meta?.runId ?? randomUUID();
|
|
143
|
-
const
|
|
174
|
+
const workflowsStore = await this.mastra?.getStorage()?.getStore("workflows");
|
|
175
|
+
const snapshot = await workflowsStore?.loadWorkflowSnapshot({
|
|
144
176
|
workflowName: step.id,
|
|
145
177
|
runId
|
|
146
178
|
});
|
|
@@ -157,14 +189,16 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
157
189
|
resumePayload: resume.resumePayload,
|
|
158
190
|
resumePath: resume.steps?.[1] ? snapshot?.suspendedPaths?.[resume.steps?.[1]] : void 0
|
|
159
191
|
},
|
|
160
|
-
outputOptions: { includeState: true }
|
|
192
|
+
outputOptions: { includeState: true },
|
|
193
|
+
perStep
|
|
161
194
|
}
|
|
162
195
|
});
|
|
163
196
|
result = invokeResp.result;
|
|
164
197
|
runId = invokeResp.runId;
|
|
165
198
|
executionContext.state = invokeResp.result.state;
|
|
166
199
|
} else if (isTimeTravel) {
|
|
167
|
-
const
|
|
200
|
+
const workflowsStoreForTimeTravel = await this.mastra?.getStorage()?.getStore("workflows");
|
|
201
|
+
const snapshot = await workflowsStoreForTimeTravel?.loadWorkflowSnapshot({
|
|
168
202
|
workflowName: step.id,
|
|
169
203
|
runId: executionContext.runId
|
|
170
204
|
}) ?? { context: {} };
|
|
@@ -183,7 +217,8 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
183
217
|
timeTravel: timeTravelParams,
|
|
184
218
|
initialState: executionContext.state ?? {},
|
|
185
219
|
runId: executionContext.runId,
|
|
186
|
-
outputOptions: { includeState: true }
|
|
220
|
+
outputOptions: { includeState: true },
|
|
221
|
+
perStep
|
|
187
222
|
}
|
|
188
223
|
});
|
|
189
224
|
result = invokeResp.result;
|
|
@@ -195,7 +230,8 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
195
230
|
data: {
|
|
196
231
|
inputData,
|
|
197
232
|
initialState: executionContext.state ?? {},
|
|
198
|
-
outputOptions: { includeState: true }
|
|
233
|
+
outputOptions: { includeState: true },
|
|
234
|
+
perStep
|
|
199
235
|
}
|
|
200
236
|
});
|
|
201
237
|
result = invokeResp.result;
|
|
@@ -221,16 +257,20 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
221
257
|
`workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
|
|
222
258
|
async () => {
|
|
223
259
|
if (result.status === "failed") {
|
|
224
|
-
await
|
|
225
|
-
type: "
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
260
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
261
|
+
type: "watch",
|
|
262
|
+
runId: executionContext.runId,
|
|
263
|
+
data: {
|
|
264
|
+
type: "workflow-step-result",
|
|
265
|
+
payload: {
|
|
266
|
+
id: step.id,
|
|
267
|
+
status: "failed",
|
|
268
|
+
error: result?.error,
|
|
269
|
+
payload: prevOutput
|
|
270
|
+
}
|
|
231
271
|
}
|
|
232
272
|
});
|
|
233
|
-
return { executionContext, result: { status: "failed", error: result?.error } };
|
|
273
|
+
return { executionContext, result: { status: "failed", error: result?.error, endedAt: Date.now() } };
|
|
234
274
|
} else if (result.status === "suspended") {
|
|
235
275
|
const suspendedSteps = Object.entries(result.steps).filter(([_stepName, stepResult]) => {
|
|
236
276
|
const stepRes = stepResult;
|
|
@@ -239,17 +279,22 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
239
279
|
for (const [stepName, stepResult] of suspendedSteps) {
|
|
240
280
|
const suspendPath = [stepName, ...stepResult?.suspendPayload?.__workflow_meta?.path ?? []];
|
|
241
281
|
executionContext.suspendedPaths[step.id] = executionContext.executionPath;
|
|
242
|
-
await
|
|
243
|
-
type: "
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
282
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
283
|
+
type: "watch",
|
|
284
|
+
runId: executionContext.runId,
|
|
285
|
+
data: {
|
|
286
|
+
type: "workflow-step-suspended",
|
|
287
|
+
payload: {
|
|
288
|
+
id: step.id,
|
|
289
|
+
status: "suspended"
|
|
290
|
+
}
|
|
247
291
|
}
|
|
248
292
|
});
|
|
249
293
|
return {
|
|
250
294
|
executionContext,
|
|
251
295
|
result: {
|
|
252
296
|
status: "suspended",
|
|
297
|
+
suspendedAt: Date.now(),
|
|
253
298
|
payload: stepResult.payload,
|
|
254
299
|
suspendPayload: {
|
|
255
300
|
...stepResult?.suspendPayload,
|
|
@@ -262,39 +307,205 @@ var InngestExecutionEngine = class extends DefaultExecutionEngine {
|
|
|
262
307
|
executionContext,
|
|
263
308
|
result: {
|
|
264
309
|
status: "suspended",
|
|
310
|
+
suspendedAt: Date.now(),
|
|
265
311
|
payload: {}
|
|
266
312
|
}
|
|
267
313
|
};
|
|
314
|
+
} else if (result.status === "tripwire") {
|
|
315
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
316
|
+
type: "watch",
|
|
317
|
+
runId: executionContext.runId,
|
|
318
|
+
data: {
|
|
319
|
+
type: "workflow-step-result",
|
|
320
|
+
payload: {
|
|
321
|
+
id: step.id,
|
|
322
|
+
status: "tripwire",
|
|
323
|
+
error: result?.tripwire?.reason,
|
|
324
|
+
payload: prevOutput
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
return {
|
|
329
|
+
executionContext,
|
|
330
|
+
result: {
|
|
331
|
+
status: "tripwire",
|
|
332
|
+
tripwire: result?.tripwire,
|
|
333
|
+
endedAt: Date.now()
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
} else if (perStep || result.status === "paused") {
|
|
337
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
338
|
+
type: "watch",
|
|
339
|
+
runId: executionContext.runId,
|
|
340
|
+
data: {
|
|
341
|
+
type: "workflow-step-result",
|
|
342
|
+
payload: {
|
|
343
|
+
id: step.id,
|
|
344
|
+
status: "paused"
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
349
|
+
type: "watch",
|
|
350
|
+
runId: executionContext.runId,
|
|
351
|
+
data: {
|
|
352
|
+
type: "workflow-step-finish",
|
|
353
|
+
payload: {
|
|
354
|
+
id: step.id,
|
|
355
|
+
metadata: {}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
return { executionContext, result: { status: "paused" } };
|
|
268
360
|
}
|
|
269
|
-
await
|
|
270
|
-
type: "
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
361
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
362
|
+
type: "watch",
|
|
363
|
+
runId: executionContext.runId,
|
|
364
|
+
data: {
|
|
365
|
+
type: "workflow-step-result",
|
|
366
|
+
payload: {
|
|
367
|
+
id: step.id,
|
|
368
|
+
status: "success",
|
|
369
|
+
output: result?.result
|
|
370
|
+
}
|
|
275
371
|
}
|
|
276
372
|
});
|
|
277
|
-
await
|
|
278
|
-
type: "
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
373
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
374
|
+
type: "watch",
|
|
375
|
+
runId: executionContext.runId,
|
|
376
|
+
data: {
|
|
377
|
+
type: "workflow-step-finish",
|
|
378
|
+
payload: {
|
|
379
|
+
id: step.id,
|
|
380
|
+
metadata: {}
|
|
381
|
+
}
|
|
282
382
|
}
|
|
283
383
|
});
|
|
284
|
-
return { executionContext, result: { status: "success", output: result?.result } };
|
|
384
|
+
return { executionContext, result: { status: "success", output: result?.result, endedAt: Date.now() } };
|
|
285
385
|
}
|
|
286
386
|
);
|
|
287
387
|
Object.assign(executionContext, res.executionContext);
|
|
288
388
|
return {
|
|
289
389
|
...res.result,
|
|
290
390
|
startedAt,
|
|
291
|
-
endedAt: Date.now(),
|
|
292
391
|
payload: inputData,
|
|
293
392
|
resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
|
|
294
393
|
resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
|
|
295
394
|
};
|
|
296
395
|
}
|
|
297
396
|
};
|
|
397
|
+
var InngestPubSub = class extends PubSub {
|
|
398
|
+
inngest;
|
|
399
|
+
workflowId;
|
|
400
|
+
publishFn;
|
|
401
|
+
subscriptions = /* @__PURE__ */ new Map();
|
|
402
|
+
constructor(inngest, workflowId, publishFn) {
|
|
403
|
+
super();
|
|
404
|
+
this.inngest = inngest;
|
|
405
|
+
this.workflowId = workflowId;
|
|
406
|
+
this.publishFn = publishFn;
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Publish an event to Inngest's realtime system.
|
|
410
|
+
*
|
|
411
|
+
* Topic format: "workflow.events.v2.{runId}"
|
|
412
|
+
* Maps to Inngest channel: "workflow:{workflowId}:{runId}"
|
|
413
|
+
*/
|
|
414
|
+
async publish(topic, event) {
|
|
415
|
+
if (!this.publishFn) {
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
const match = topic.match(/^workflow\.events\.v2\.(.+)$/);
|
|
419
|
+
if (!match) {
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
const runId = match[1];
|
|
423
|
+
try {
|
|
424
|
+
await this.publishFn({
|
|
425
|
+
channel: `workflow:${this.workflowId}:${runId}`,
|
|
426
|
+
topic: "watch",
|
|
427
|
+
data: event.data
|
|
428
|
+
});
|
|
429
|
+
} catch (err) {
|
|
430
|
+
console.error("InngestPubSub publish error:", err?.message ?? err);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Subscribe to events from Inngest's realtime system.
|
|
435
|
+
*
|
|
436
|
+
* Topic format: "workflow.events.v2.{runId}"
|
|
437
|
+
* Maps to Inngest channel: "workflow:{workflowId}:{runId}"
|
|
438
|
+
*/
|
|
439
|
+
async subscribe(topic, cb) {
|
|
440
|
+
const match = topic.match(/^workflow\.events\.v2\.(.+)$/);
|
|
441
|
+
if (!match || !match[1]) {
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
const runId = match[1];
|
|
445
|
+
if (this.subscriptions.has(topic)) {
|
|
446
|
+
this.subscriptions.get(topic).callbacks.add(cb);
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
const callbacks = /* @__PURE__ */ new Set([cb]);
|
|
450
|
+
const channel = `workflow:${this.workflowId}:${runId}`;
|
|
451
|
+
const streamPromise = subscribe(
|
|
452
|
+
{
|
|
453
|
+
channel,
|
|
454
|
+
topics: ["watch"],
|
|
455
|
+
app: this.inngest
|
|
456
|
+
},
|
|
457
|
+
(message) => {
|
|
458
|
+
const event = {
|
|
459
|
+
id: crypto.randomUUID(),
|
|
460
|
+
type: "watch",
|
|
461
|
+
runId,
|
|
462
|
+
data: message.data,
|
|
463
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
464
|
+
};
|
|
465
|
+
for (const callback of callbacks) {
|
|
466
|
+
callback(event);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
);
|
|
470
|
+
this.subscriptions.set(topic, {
|
|
471
|
+
unsubscribe: () => {
|
|
472
|
+
streamPromise.then((stream) => stream.cancel()).catch((err) => {
|
|
473
|
+
console.error("InngestPubSub unsubscribe error:", err);
|
|
474
|
+
});
|
|
475
|
+
},
|
|
476
|
+
callbacks
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Unsubscribe a callback from a topic.
|
|
481
|
+
* If no callbacks remain, the underlying Inngest subscription is cancelled.
|
|
482
|
+
*/
|
|
483
|
+
async unsubscribe(topic, cb) {
|
|
484
|
+
const sub = this.subscriptions.get(topic);
|
|
485
|
+
if (!sub) {
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
sub.callbacks.delete(cb);
|
|
489
|
+
if (sub.callbacks.size === 0) {
|
|
490
|
+
sub.unsubscribe();
|
|
491
|
+
this.subscriptions.delete(topic);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Flush any pending operations. No-op for Inngest.
|
|
496
|
+
*/
|
|
497
|
+
async flush() {
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* Clean up all subscriptions during graceful shutdown.
|
|
501
|
+
*/
|
|
502
|
+
async close() {
|
|
503
|
+
for (const [, sub] of this.subscriptions) {
|
|
504
|
+
sub.unsubscribe();
|
|
505
|
+
}
|
|
506
|
+
this.subscriptions.clear();
|
|
507
|
+
}
|
|
508
|
+
};
|
|
298
509
|
var InngestRun = class extends Run {
|
|
299
510
|
inngest;
|
|
300
511
|
serializedStepGraph;
|
|
@@ -306,38 +517,90 @@ var InngestRun = class extends Run {
|
|
|
306
517
|
this.#mastra = params.mastra;
|
|
307
518
|
}
|
|
308
519
|
async getRuns(eventId) {
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
520
|
+
const maxRetries = 3;
|
|
521
|
+
let lastError = null;
|
|
522
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
523
|
+
try {
|
|
524
|
+
const response = await fetch(
|
|
525
|
+
`${this.inngest.apiBaseUrl ?? "https://api.inngest.com"}/v1/events/${eventId}/runs`,
|
|
526
|
+
{
|
|
527
|
+
headers: {
|
|
528
|
+
Authorization: `Bearer ${process.env.INNGEST_SIGNING_KEY}`
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
);
|
|
532
|
+
if (response.status === 429) {
|
|
533
|
+
const retryAfter = parseInt(response.headers.get("retry-after") || "2", 10);
|
|
534
|
+
await new Promise((resolve) => setTimeout(resolve, retryAfter * 1e3));
|
|
535
|
+
continue;
|
|
536
|
+
}
|
|
537
|
+
if (!response.ok) {
|
|
538
|
+
throw new Error(`Inngest API error: ${response.status} ${response.statusText}`);
|
|
539
|
+
}
|
|
540
|
+
const text = await response.text();
|
|
541
|
+
if (!text) {
|
|
542
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3 * (attempt + 1)));
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
const json = JSON.parse(text);
|
|
546
|
+
return json.data;
|
|
547
|
+
} catch (error) {
|
|
548
|
+
lastError = error;
|
|
549
|
+
if (attempt < maxRetries - 1) {
|
|
550
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3 * Math.pow(2, attempt)));
|
|
551
|
+
}
|
|
312
552
|
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
return json.data;
|
|
553
|
+
}
|
|
554
|
+
throw new NonRetriableError(`Failed to get runs after ${maxRetries} attempts: ${lastError?.message}`);
|
|
316
555
|
}
|
|
317
|
-
async getRunOutput(eventId) {
|
|
318
|
-
|
|
556
|
+
async getRunOutput(eventId, maxWaitMs = 3e5) {
|
|
557
|
+
const startTime = Date.now();
|
|
319
558
|
const storage = this.#mastra?.getStorage();
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
runs
|
|
559
|
+
const workflowsStore = await storage?.getStore("workflows");
|
|
560
|
+
while (Date.now() - startTime < maxWaitMs) {
|
|
561
|
+
let runs;
|
|
562
|
+
try {
|
|
563
|
+
runs = await this.getRuns(eventId);
|
|
564
|
+
} catch (error) {
|
|
565
|
+
if (error instanceof NonRetriableError) {
|
|
566
|
+
throw error;
|
|
567
|
+
}
|
|
568
|
+
throw new NonRetriableError(
|
|
569
|
+
`Failed to poll workflow status: ${error instanceof Error ? error.message : String(error)}`
|
|
570
|
+
);
|
|
571
|
+
}
|
|
572
|
+
if (runs?.[0]?.status === "Completed" && runs?.[0]?.event_id === eventId) {
|
|
573
|
+
return runs[0];
|
|
574
|
+
}
|
|
323
575
|
if (runs?.[0]?.status === "Failed") {
|
|
324
|
-
const snapshot = await
|
|
576
|
+
const snapshot = await workflowsStore?.loadWorkflowSnapshot({
|
|
325
577
|
workflowName: this.workflowId,
|
|
326
578
|
runId: this.runId
|
|
327
579
|
});
|
|
580
|
+
if (snapshot?.context) {
|
|
581
|
+
snapshot.context = hydrateSerializedStepErrors(snapshot.context);
|
|
582
|
+
}
|
|
328
583
|
return {
|
|
329
|
-
output: {
|
|
584
|
+
output: {
|
|
585
|
+
result: {
|
|
586
|
+
steps: snapshot?.context,
|
|
587
|
+
status: "failed",
|
|
588
|
+
// Get the original error from NonRetriableError's cause (which contains the workflow result)
|
|
589
|
+
error: getErrorFromUnknown(runs?.[0]?.output?.cause?.error, { serializeStack: false })
|
|
590
|
+
}
|
|
591
|
+
}
|
|
330
592
|
};
|
|
331
593
|
}
|
|
332
594
|
if (runs?.[0]?.status === "Cancelled") {
|
|
333
|
-
const snapshot = await
|
|
595
|
+
const snapshot = await workflowsStore?.loadWorkflowSnapshot({
|
|
334
596
|
workflowName: this.workflowId,
|
|
335
597
|
runId: this.runId
|
|
336
598
|
});
|
|
337
599
|
return { output: { result: { steps: snapshot?.context, status: "canceled" } } };
|
|
338
600
|
}
|
|
601
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3 + Math.random() * 1e3));
|
|
339
602
|
}
|
|
340
|
-
|
|
603
|
+
throw new NonRetriableError(`Workflow did not complete within ${maxWaitMs}ms`);
|
|
341
604
|
}
|
|
342
605
|
async cancel() {
|
|
343
606
|
const storage = this.#mastra?.getStorage();
|
|
@@ -347,12 +610,13 @@ var InngestRun = class extends Run {
|
|
|
347
610
|
runId: this.runId
|
|
348
611
|
}
|
|
349
612
|
});
|
|
350
|
-
const
|
|
613
|
+
const workflowsStore = await storage?.getStore("workflows");
|
|
614
|
+
const snapshot = await workflowsStore?.loadWorkflowSnapshot({
|
|
351
615
|
workflowName: this.workflowId,
|
|
352
616
|
runId: this.runId
|
|
353
617
|
});
|
|
354
618
|
if (snapshot) {
|
|
355
|
-
await
|
|
619
|
+
await workflowsStore?.persistWorkflowSnapshot({
|
|
356
620
|
workflowName: this.workflowId,
|
|
357
621
|
runId: this.runId,
|
|
358
622
|
resourceId: this.resourceId,
|
|
@@ -367,15 +631,64 @@ var InngestRun = class extends Run {
|
|
|
367
631
|
async start(params) {
|
|
368
632
|
return this._start(params);
|
|
369
633
|
}
|
|
634
|
+
/**
|
|
635
|
+
* Starts the workflow execution without waiting for completion (fire-and-forget).
|
|
636
|
+
* Returns immediately with the runId after sending the event to Inngest.
|
|
637
|
+
* The workflow executes independently in Inngest.
|
|
638
|
+
* Use this when you don't need to wait for the result or want to avoid polling failures.
|
|
639
|
+
*/
|
|
640
|
+
async startAsync(params) {
|
|
641
|
+
const workflowsStore = await this.#mastra.getStorage()?.getStore("workflows");
|
|
642
|
+
await workflowsStore?.persistWorkflowSnapshot({
|
|
643
|
+
workflowName: this.workflowId,
|
|
644
|
+
runId: this.runId,
|
|
645
|
+
resourceId: this.resourceId,
|
|
646
|
+
snapshot: {
|
|
647
|
+
runId: this.runId,
|
|
648
|
+
serializedStepGraph: this.serializedStepGraph,
|
|
649
|
+
status: "running",
|
|
650
|
+
value: {},
|
|
651
|
+
context: {},
|
|
652
|
+
activePaths: [],
|
|
653
|
+
suspendedPaths: {},
|
|
654
|
+
activeStepsPath: {},
|
|
655
|
+
resumeLabels: {},
|
|
656
|
+
waitingPaths: {},
|
|
657
|
+
timestamp: Date.now()
|
|
658
|
+
}
|
|
659
|
+
});
|
|
660
|
+
const inputDataToUse = await this._validateInput(params.inputData);
|
|
661
|
+
const initialStateToUse = await this._validateInitialState(params.initialState ?? {});
|
|
662
|
+
const eventOutput = await this.inngest.send({
|
|
663
|
+
name: `workflow.${this.workflowId}`,
|
|
664
|
+
data: {
|
|
665
|
+
inputData: inputDataToUse,
|
|
666
|
+
initialState: initialStateToUse,
|
|
667
|
+
runId: this.runId,
|
|
668
|
+
resourceId: this.resourceId,
|
|
669
|
+
outputOptions: params.outputOptions,
|
|
670
|
+
tracingOptions: params.tracingOptions,
|
|
671
|
+
requestContext: params.requestContext ? Object.fromEntries(params.requestContext.entries()) : {},
|
|
672
|
+
perStep: params.perStep
|
|
673
|
+
}
|
|
674
|
+
});
|
|
675
|
+
const eventId = eventOutput.ids[0];
|
|
676
|
+
if (!eventId) {
|
|
677
|
+
throw new Error("Event ID is not set");
|
|
678
|
+
}
|
|
679
|
+
return { runId: this.runId };
|
|
680
|
+
}
|
|
370
681
|
async _start({
|
|
371
682
|
inputData,
|
|
372
683
|
initialState,
|
|
373
684
|
outputOptions,
|
|
374
685
|
tracingOptions,
|
|
375
686
|
format,
|
|
376
|
-
requestContext
|
|
687
|
+
requestContext,
|
|
688
|
+
perStep
|
|
377
689
|
}) {
|
|
378
|
-
await this.#mastra.getStorage()?.
|
|
690
|
+
const workflowsStore = await this.#mastra.getStorage()?.getStore("workflows");
|
|
691
|
+
await workflowsStore?.persistWorkflowSnapshot({
|
|
379
692
|
workflowName: this.workflowId,
|
|
380
693
|
runId: this.runId,
|
|
381
694
|
resourceId: this.resourceId,
|
|
@@ -405,7 +718,8 @@ var InngestRun = class extends Run {
|
|
|
405
718
|
outputOptions,
|
|
406
719
|
tracingOptions,
|
|
407
720
|
format,
|
|
408
|
-
requestContext: requestContext ? Object.fromEntries(requestContext.entries()) : {}
|
|
721
|
+
requestContext: requestContext ? Object.fromEntries(requestContext.entries()) : {},
|
|
722
|
+
perStep
|
|
409
723
|
}
|
|
410
724
|
});
|
|
411
725
|
const eventId = eventOutput.ids[0];
|
|
@@ -414,9 +728,7 @@ var InngestRun = class extends Run {
|
|
|
414
728
|
}
|
|
415
729
|
const runOutput = await this.getRunOutput(eventId);
|
|
416
730
|
const result = runOutput?.output?.result;
|
|
417
|
-
|
|
418
|
-
result.error = new Error(result.error);
|
|
419
|
-
}
|
|
731
|
+
this.hydrateFailedResult(result);
|
|
420
732
|
if (result.status !== "suspended") {
|
|
421
733
|
this.cleanup?.();
|
|
422
734
|
}
|
|
@@ -443,7 +755,8 @@ var InngestRun = class extends Run {
|
|
|
443
755
|
(step) => typeof step === "string" ? step : step?.id
|
|
444
756
|
);
|
|
445
757
|
}
|
|
446
|
-
const
|
|
758
|
+
const workflowsStore = await storage?.getStore("workflows");
|
|
759
|
+
const snapshot = await workflowsStore?.loadWorkflowSnapshot({
|
|
447
760
|
workflowName: this.workflowId,
|
|
448
761
|
runId: this.runId
|
|
449
762
|
});
|
|
@@ -466,7 +779,8 @@ var InngestRun = class extends Run {
|
|
|
466
779
|
resumePayload: resumeDataToUse,
|
|
467
780
|
resumePath: steps?.[0] ? snapshot?.suspendedPaths?.[steps?.[0]] : void 0
|
|
468
781
|
},
|
|
469
|
-
requestContext: mergedRequestContext
|
|
782
|
+
requestContext: mergedRequestContext,
|
|
783
|
+
perStep: params.perStep
|
|
470
784
|
}
|
|
471
785
|
});
|
|
472
786
|
const eventId = eventOutput.ids[0];
|
|
@@ -475,9 +789,7 @@ var InngestRun = class extends Run {
|
|
|
475
789
|
}
|
|
476
790
|
const runOutput = await this.getRunOutput(eventId);
|
|
477
791
|
const result = runOutput?.output?.result;
|
|
478
|
-
|
|
479
|
-
result.error = new Error(result.error);
|
|
480
|
-
}
|
|
792
|
+
this.hydrateFailedResult(result);
|
|
481
793
|
return result;
|
|
482
794
|
}
|
|
483
795
|
async timeTravel(params) {
|
|
@@ -507,12 +819,13 @@ var InngestRun = class extends Run {
|
|
|
507
819
|
throw new Error("No steps provided to timeTravel");
|
|
508
820
|
}
|
|
509
821
|
const storage = this.#mastra?.getStorage();
|
|
510
|
-
const
|
|
822
|
+
const workflowsStore = await storage?.getStore("workflows");
|
|
823
|
+
const snapshot = await workflowsStore?.loadWorkflowSnapshot({
|
|
511
824
|
workflowName: this.workflowId,
|
|
512
825
|
runId: this.runId
|
|
513
826
|
});
|
|
514
827
|
if (!snapshot) {
|
|
515
|
-
await
|
|
828
|
+
await workflowsStore?.persistWorkflowSnapshot({
|
|
516
829
|
workflowName: this.workflowId,
|
|
517
830
|
runId: this.runId,
|
|
518
831
|
resourceId: this.resourceId,
|
|
@@ -546,7 +859,8 @@ var InngestRun = class extends Run {
|
|
|
546
859
|
nestedStepsContext: params.nestedStepsContext,
|
|
547
860
|
snapshot: snapshot ?? { context: {} },
|
|
548
861
|
graph: this.executionGraph,
|
|
549
|
-
initialState: params.initialState
|
|
862
|
+
initialState: params.initialState,
|
|
863
|
+
perStep: params.perStep
|
|
550
864
|
});
|
|
551
865
|
const eventOutput = await this.inngest.send({
|
|
552
866
|
name: `workflow.${this.workflowId}`,
|
|
@@ -558,7 +872,8 @@ var InngestRun = class extends Run {
|
|
|
558
872
|
timeTravel: timeTravelData,
|
|
559
873
|
tracingOptions: params.tracingOptions,
|
|
560
874
|
outputOptions: params.outputOptions,
|
|
561
|
-
requestContext: params.requestContext ? Object.fromEntries(params.requestContext.entries()) : {}
|
|
875
|
+
requestContext: params.requestContext ? Object.fromEntries(params.requestContext.entries()) : {},
|
|
876
|
+
perStep: params.perStep
|
|
562
877
|
}
|
|
563
878
|
});
|
|
564
879
|
const eventId = eventOutput.ids[0];
|
|
@@ -567,9 +882,7 @@ var InngestRun = class extends Run {
|
|
|
567
882
|
}
|
|
568
883
|
const runOutput = await this.getRunOutput(eventId);
|
|
569
884
|
const result = runOutput?.output?.result;
|
|
570
|
-
|
|
571
|
-
result.error = new Error(result.error);
|
|
572
|
-
}
|
|
885
|
+
this.hydrateFailedResult(result);
|
|
573
886
|
return result;
|
|
574
887
|
}
|
|
575
888
|
watch(cb) {
|
|
@@ -651,7 +964,8 @@ var InngestRun = class extends Run {
|
|
|
651
964
|
tracingOptions,
|
|
652
965
|
closeOnSuspend = true,
|
|
653
966
|
initialState,
|
|
654
|
-
outputOptions
|
|
967
|
+
outputOptions,
|
|
968
|
+
perStep
|
|
655
969
|
} = {}) {
|
|
656
970
|
if (this.closeStreamAction && this.streamOutput) {
|
|
657
971
|
return this.streamOutput;
|
|
@@ -687,7 +1001,8 @@ var InngestRun = class extends Run {
|
|
|
687
1001
|
initialState,
|
|
688
1002
|
tracingOptions,
|
|
689
1003
|
outputOptions,
|
|
690
|
-
format: "vnext"
|
|
1004
|
+
format: "vnext",
|
|
1005
|
+
perStep
|
|
691
1006
|
});
|
|
692
1007
|
let executionResults;
|
|
693
1008
|
try {
|
|
@@ -718,9 +1033,6 @@ var InngestRun = class extends Run {
|
|
|
718
1033
|
});
|
|
719
1034
|
return this.streamOutput;
|
|
720
1035
|
}
|
|
721
|
-
streamVNext(args = {}) {
|
|
722
|
-
return this.stream(args);
|
|
723
|
-
}
|
|
724
1036
|
timeTravelStream({
|
|
725
1037
|
inputData,
|
|
726
1038
|
resumeData,
|
|
@@ -730,7 +1042,8 @@ var InngestRun = class extends Run {
|
|
|
730
1042
|
nestedStepsContext,
|
|
731
1043
|
requestContext,
|
|
732
1044
|
tracingOptions,
|
|
733
|
-
outputOptions
|
|
1045
|
+
outputOptions,
|
|
1046
|
+
perStep
|
|
734
1047
|
}) {
|
|
735
1048
|
this.closeStreamAction = async () => {
|
|
736
1049
|
};
|
|
@@ -765,7 +1078,8 @@ var InngestRun = class extends Run {
|
|
|
765
1078
|
initialState,
|
|
766
1079
|
requestContext,
|
|
767
1080
|
tracingOptions,
|
|
768
|
-
outputOptions
|
|
1081
|
+
outputOptions,
|
|
1082
|
+
perStep
|
|
769
1083
|
});
|
|
770
1084
|
self.executionResults = executionResultsPromise;
|
|
771
1085
|
let executionResults;
|
|
@@ -790,6 +1104,18 @@ var InngestRun = class extends Run {
|
|
|
790
1104
|
});
|
|
791
1105
|
return this.streamOutput;
|
|
792
1106
|
}
|
|
1107
|
+
/**
|
|
1108
|
+
* Hydrates errors in a failed workflow result back to proper Error instances.
|
|
1109
|
+
* This ensures error.cause chains and custom properties are preserved.
|
|
1110
|
+
*/
|
|
1111
|
+
hydrateFailedResult(result) {
|
|
1112
|
+
if (result.status === "failed") {
|
|
1113
|
+
result.error = getErrorFromUnknown(result.error, { serializeStack: false });
|
|
1114
|
+
if (result.steps) {
|
|
1115
|
+
hydrateSerializedStepErrors(result.steps);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
793
1119
|
};
|
|
794
1120
|
|
|
795
1121
|
// src/workflow.ts
|
|
@@ -797,9 +1123,11 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
|
|
|
797
1123
|
#mastra;
|
|
798
1124
|
inngest;
|
|
799
1125
|
function;
|
|
1126
|
+
cronFunction;
|
|
800
1127
|
flowControlConfig;
|
|
1128
|
+
cronConfig;
|
|
801
1129
|
constructor(params, inngest) {
|
|
802
|
-
const { concurrency, rateLimit, throttle, debounce, priority, ...workflowParams } = params;
|
|
1130
|
+
const { concurrency, rateLimit, throttle, debounce, priority, cron, inputData, initialState, ...workflowParams } = params;
|
|
803
1131
|
super(workflowParams);
|
|
804
1132
|
this.engineType = "inngest";
|
|
805
1133
|
const flowControlEntries = Object.entries({ concurrency, rateLimit, throttle, debounce, priority }).filter(
|
|
@@ -808,6 +1136,9 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
|
|
|
808
1136
|
this.flowControlConfig = flowControlEntries.length > 0 ? Object.fromEntries(flowControlEntries) : void 0;
|
|
809
1137
|
this.#mastra = params.mastra;
|
|
810
1138
|
this.inngest = inngest;
|
|
1139
|
+
if (cron) {
|
|
1140
|
+
this.cronConfig = { cron, inputData, initialState };
|
|
1141
|
+
}
|
|
811
1142
|
}
|
|
812
1143
|
async listWorkflowRuns(args) {
|
|
813
1144
|
const storage = this.#mastra?.getStorage();
|
|
@@ -815,7 +1146,11 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
|
|
|
815
1146
|
this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
|
|
816
1147
|
return { runs: [], total: 0 };
|
|
817
1148
|
}
|
|
818
|
-
|
|
1149
|
+
const workflowsStore = await storage.getStore("workflows");
|
|
1150
|
+
if (!workflowsStore) {
|
|
1151
|
+
return { runs: [], total: 0 };
|
|
1152
|
+
}
|
|
1153
|
+
return workflowsStore.listWorkflowRuns({ workflowName: this.id, ...args ?? {} });
|
|
819
1154
|
}
|
|
820
1155
|
async getWorkflowRunById(runId) {
|
|
821
1156
|
const storage = this.#mastra?.getStorage();
|
|
@@ -823,10 +1158,15 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
|
|
|
823
1158
|
this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
|
|
824
1159
|
return this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null;
|
|
825
1160
|
}
|
|
826
|
-
const
|
|
1161
|
+
const workflowsStore = await storage.getStore("workflows");
|
|
1162
|
+
if (!workflowsStore) {
|
|
1163
|
+
return this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null;
|
|
1164
|
+
}
|
|
1165
|
+
const run = await workflowsStore.getWorkflowRunById({ runId, workflowName: this.id });
|
|
827
1166
|
return run ?? (this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null);
|
|
828
1167
|
}
|
|
829
1168
|
__registerMastra(mastra) {
|
|
1169
|
+
super.__registerMastra(mastra);
|
|
830
1170
|
this.#mastra = mastra;
|
|
831
1171
|
this.executionEngine.__registerMastra(mastra);
|
|
832
1172
|
const updateNested = (step) => {
|
|
@@ -868,9 +1208,12 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
|
|
|
868
1208
|
workflowStatus: run.workflowRunStatus,
|
|
869
1209
|
stepResults: {}
|
|
870
1210
|
});
|
|
871
|
-
const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse,
|
|
1211
|
+
const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse, {
|
|
1212
|
+
withNestedWorkflows: false
|
|
1213
|
+
});
|
|
872
1214
|
if (!workflowSnapshotInStorage && shouldPersistSnapshot) {
|
|
873
|
-
await this.mastra?.getStorage()?.
|
|
1215
|
+
const workflowsStore = await this.mastra?.getStorage()?.getStore("workflows");
|
|
1216
|
+
await workflowsStore?.persistWorkflowSnapshot({
|
|
874
1217
|
workflowName: this.id,
|
|
875
1218
|
runId: runIdToUse,
|
|
876
1219
|
resourceId: options?.resourceId,
|
|
@@ -893,6 +1236,30 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
|
|
|
893
1236
|
}
|
|
894
1237
|
return run;
|
|
895
1238
|
}
|
|
1239
|
+
//createCronFunction is only called if cronConfig.cron is defined.
|
|
1240
|
+
createCronFunction() {
|
|
1241
|
+
if (this.cronFunction) {
|
|
1242
|
+
return this.cronFunction;
|
|
1243
|
+
}
|
|
1244
|
+
this.cronFunction = this.inngest.createFunction(
|
|
1245
|
+
{
|
|
1246
|
+
id: `workflow.${this.id}.cron`,
|
|
1247
|
+
retries: 0,
|
|
1248
|
+
cancelOn: [{ event: `cancel.workflow.${this.id}` }],
|
|
1249
|
+
...this.flowControlConfig
|
|
1250
|
+
},
|
|
1251
|
+
{ cron: this.cronConfig?.cron ?? "" },
|
|
1252
|
+
async () => {
|
|
1253
|
+
const run = await this.createRun();
|
|
1254
|
+
const result = await run.start({
|
|
1255
|
+
inputData: this.cronConfig?.inputData,
|
|
1256
|
+
initialState: this.cronConfig?.initialState
|
|
1257
|
+
});
|
|
1258
|
+
return { result, runId: run.runId };
|
|
1259
|
+
}
|
|
1260
|
+
);
|
|
1261
|
+
return this.cronFunction;
|
|
1262
|
+
}
|
|
896
1263
|
getFunction() {
|
|
897
1264
|
if (this.function) {
|
|
898
1265
|
return this.function;
|
|
@@ -900,41 +1267,20 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
|
|
|
900
1267
|
this.function = this.inngest.createFunction(
|
|
901
1268
|
{
|
|
902
1269
|
id: `workflow.${this.id}`,
|
|
903
|
-
retries:
|
|
1270
|
+
retries: 0,
|
|
904
1271
|
cancelOn: [{ event: `cancel.workflow.${this.id}` }],
|
|
905
1272
|
// Spread flow control configuration
|
|
906
1273
|
...this.flowControlConfig
|
|
907
1274
|
},
|
|
908
1275
|
{ event: `workflow.${this.id}` },
|
|
909
1276
|
async ({ event, step, attempt, publish }) => {
|
|
910
|
-
let { inputData, initialState, runId, resourceId, resume, outputOptions, format, timeTravel } = event.data;
|
|
1277
|
+
let { inputData, initialState, runId, resourceId, resume, outputOptions, format, timeTravel, perStep } = event.data;
|
|
911
1278
|
if (!runId) {
|
|
912
1279
|
runId = await step.run(`workflow.${this.id}.runIdGen`, async () => {
|
|
913
1280
|
return randomUUID();
|
|
914
1281
|
});
|
|
915
1282
|
}
|
|
916
|
-
const
|
|
917
|
-
emit: async (event2, data) => {
|
|
918
|
-
if (!publish) {
|
|
919
|
-
return;
|
|
920
|
-
}
|
|
921
|
-
try {
|
|
922
|
-
await publish({
|
|
923
|
-
channel: `workflow:${this.id}:${runId}`,
|
|
924
|
-
topic: event2,
|
|
925
|
-
data
|
|
926
|
-
});
|
|
927
|
-
} catch (err) {
|
|
928
|
-
this.logger.error("Error emitting event: " + (err?.stack ?? err?.message ?? err));
|
|
929
|
-
}
|
|
930
|
-
},
|
|
931
|
-
on: (_event, _callback) => {
|
|
932
|
-
},
|
|
933
|
-
off: (_event, _callback) => {
|
|
934
|
-
},
|
|
935
|
-
once: (_event, _callback) => {
|
|
936
|
-
}
|
|
937
|
-
};
|
|
1283
|
+
const pubsub = new InngestPubSub(this.inngest, this.id, publish);
|
|
938
1284
|
const engine = new InngestExecutionEngine(this.#mastra, step, attempt, this.options);
|
|
939
1285
|
const result = await engine.execute({
|
|
940
1286
|
workflowId: this.id,
|
|
@@ -944,21 +1290,32 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
|
|
|
944
1290
|
serializedStepGraph: this.serializedStepGraph,
|
|
945
1291
|
input: inputData,
|
|
946
1292
|
initialState,
|
|
947
|
-
|
|
1293
|
+
pubsub,
|
|
948
1294
|
retryConfig: this.retryConfig,
|
|
949
1295
|
requestContext: new RequestContext(Object.entries(event.data.requestContext ?? {})),
|
|
950
1296
|
resume,
|
|
951
1297
|
timeTravel,
|
|
1298
|
+
perStep,
|
|
952
1299
|
format,
|
|
953
1300
|
abortController: new AbortController(),
|
|
954
1301
|
// currentSpan: undefined, // TODO: Pass actual parent Span from workflow execution context
|
|
955
1302
|
outputOptions,
|
|
956
1303
|
outputWriter: async (chunk) => {
|
|
957
|
-
|
|
958
|
-
|
|
1304
|
+
try {
|
|
1305
|
+
await pubsub.publish(`workflow.events.v2.${runId}`, {
|
|
1306
|
+
type: "watch",
|
|
1307
|
+
runId,
|
|
1308
|
+
data: chunk
|
|
1309
|
+
});
|
|
1310
|
+
} catch (err) {
|
|
1311
|
+
this.logger.debug?.("Failed to publish watch event:", err);
|
|
1312
|
+
}
|
|
959
1313
|
}
|
|
960
1314
|
});
|
|
961
1315
|
await step.run(`workflow.${this.id}.finalize`, async () => {
|
|
1316
|
+
if (result.status !== "paused") {
|
|
1317
|
+
await engine.invokeLifecycleCallbacksInternal(result);
|
|
1318
|
+
}
|
|
962
1319
|
if (result.status === "failed") {
|
|
963
1320
|
throw new NonRetriableError(`Workflow failed`, {
|
|
964
1321
|
cause: result
|
|
@@ -985,7 +1342,11 @@ var InngestWorkflow = class _InngestWorkflow extends Workflow {
|
|
|
985
1342
|
});
|
|
986
1343
|
}
|
|
987
1344
|
getFunctions() {
|
|
988
|
-
return [
|
|
1345
|
+
return [
|
|
1346
|
+
this.getFunction(),
|
|
1347
|
+
...this.cronConfig?.cron ? [this.createCronFunction()] : [],
|
|
1348
|
+
...this.getNestedFunctions(this.executionGraph.steps)
|
|
1349
|
+
];
|
|
989
1350
|
}
|
|
990
1351
|
};
|
|
991
1352
|
function serve({
|
|
@@ -1017,20 +1378,14 @@ function serve({
|
|
|
1017
1378
|
var _compatibilityCheck = true;
|
|
1018
1379
|
|
|
1019
1380
|
// src/index.ts
|
|
1020
|
-
function
|
|
1021
|
-
|
|
1022
|
-
}
|
|
1023
|
-
function isTool(params) {
|
|
1024
|
-
return params instanceof Tool;
|
|
1025
|
-
}
|
|
1026
|
-
function isInngestWorkflow(params) {
|
|
1027
|
-
return params instanceof InngestWorkflow;
|
|
1028
|
-
}
|
|
1029
|
-
function createStep(params, agentOptions) {
|
|
1030
|
-
if (isInngestWorkflow(params)) {
|
|
1381
|
+
function createStep(params, agentOrToolOptions) {
|
|
1382
|
+
if (params instanceof InngestWorkflow) {
|
|
1031
1383
|
return params;
|
|
1032
1384
|
}
|
|
1033
|
-
if (
|
|
1385
|
+
if (params instanceof Agent) {
|
|
1386
|
+
const options = agentOrToolOptions;
|
|
1387
|
+
const outputSchema = options?.structuredOutput?.schema ?? z.object({ text: z.string() });
|
|
1388
|
+
const { retries, scorers, ...agentOptions } = options ?? {};
|
|
1034
1389
|
return {
|
|
1035
1390
|
id: params.name,
|
|
1036
1391
|
description: params.getDescription(),
|
|
@@ -1039,12 +1394,13 @@ function createStep(params, agentOptions) {
|
|
|
1039
1394
|
// resourceId: z.string().optional(),
|
|
1040
1395
|
// threadId: z.string().optional(),
|
|
1041
1396
|
}),
|
|
1042
|
-
outputSchema
|
|
1043
|
-
|
|
1044
|
-
|
|
1397
|
+
outputSchema,
|
|
1398
|
+
retries,
|
|
1399
|
+
scorers,
|
|
1045
1400
|
execute: async ({
|
|
1046
1401
|
inputData,
|
|
1047
|
-
|
|
1402
|
+
runId,
|
|
1403
|
+
[PUBSUB_SYMBOL]: pubsub,
|
|
1048
1404
|
[STREAM_FORMAT_SYMBOL]: streamFormat,
|
|
1049
1405
|
requestContext,
|
|
1050
1406
|
tracingContext,
|
|
@@ -1057,6 +1413,7 @@ function createStep(params, agentOptions) {
|
|
|
1057
1413
|
streamPromise.resolve = resolve;
|
|
1058
1414
|
streamPromise.reject = reject;
|
|
1059
1415
|
});
|
|
1416
|
+
let structuredResult = null;
|
|
1060
1417
|
const toolData = {
|
|
1061
1418
|
name: params.name,
|
|
1062
1419
|
args: inputData
|
|
@@ -1070,6 +1427,10 @@ function createStep(params, agentOptions) {
|
|
|
1070
1427
|
requestContext,
|
|
1071
1428
|
tracingContext,
|
|
1072
1429
|
onFinish: (result) => {
|
|
1430
|
+
const resultWithObject = result;
|
|
1431
|
+
if (agentOptions?.structuredOutput?.schema && resultWithObject.object) {
|
|
1432
|
+
structuredResult = resultWithObject.object;
|
|
1433
|
+
}
|
|
1073
1434
|
streamPromise.resolve(result.text);
|
|
1074
1435
|
void agentOptions?.onFinish?.(result);
|
|
1075
1436
|
},
|
|
@@ -1082,6 +1443,10 @@ function createStep(params, agentOptions) {
|
|
|
1082
1443
|
requestContext,
|
|
1083
1444
|
tracingContext,
|
|
1084
1445
|
onFinish: (result) => {
|
|
1446
|
+
const resultWithObject = result;
|
|
1447
|
+
if (agentOptions?.structuredOutput?.schema && resultWithObject.object) {
|
|
1448
|
+
structuredResult = resultWithObject.object;
|
|
1449
|
+
}
|
|
1085
1450
|
streamPromise.resolve(result.text);
|
|
1086
1451
|
void agentOptions?.onFinish?.(result);
|
|
1087
1452
|
},
|
|
@@ -1090,22 +1455,24 @@ function createStep(params, agentOptions) {
|
|
|
1090
1455
|
stream = modelOutput.fullStream;
|
|
1091
1456
|
}
|
|
1092
1457
|
if (streamFormat === "legacy") {
|
|
1093
|
-
await
|
|
1094
|
-
type: "
|
|
1095
|
-
|
|
1458
|
+
await pubsub.publish(`workflow.events.v2.${runId}`, {
|
|
1459
|
+
type: "watch",
|
|
1460
|
+
runId,
|
|
1461
|
+
data: { type: "tool-call-streaming-start", ...toolData ?? {} }
|
|
1096
1462
|
});
|
|
1097
1463
|
for await (const chunk of stream) {
|
|
1098
1464
|
if (chunk.type === "text-delta") {
|
|
1099
|
-
await
|
|
1100
|
-
type: "
|
|
1101
|
-
|
|
1102
|
-
argsTextDelta: chunk.textDelta
|
|
1465
|
+
await pubsub.publish(`workflow.events.v2.${runId}`, {
|
|
1466
|
+
type: "watch",
|
|
1467
|
+
runId,
|
|
1468
|
+
data: { type: "tool-call-delta", ...toolData ?? {}, argsTextDelta: chunk.textDelta }
|
|
1103
1469
|
});
|
|
1104
1470
|
}
|
|
1105
1471
|
}
|
|
1106
|
-
await
|
|
1107
|
-
type: "
|
|
1108
|
-
|
|
1472
|
+
await pubsub.publish(`workflow.events.v2.${runId}`, {
|
|
1473
|
+
type: "watch",
|
|
1474
|
+
runId,
|
|
1475
|
+
data: { type: "tool-call-streaming-finish", ...toolData ?? {} }
|
|
1109
1476
|
});
|
|
1110
1477
|
} else {
|
|
1111
1478
|
for await (const chunk of stream) {
|
|
@@ -1115,6 +1482,9 @@ function createStep(params, agentOptions) {
|
|
|
1115
1482
|
if (abortSignal.aborted) {
|
|
1116
1483
|
return abort();
|
|
1117
1484
|
}
|
|
1485
|
+
if (structuredResult !== null) {
|
|
1486
|
+
return structuredResult;
|
|
1487
|
+
}
|
|
1118
1488
|
return {
|
|
1119
1489
|
text: await streamPromise.promise
|
|
1120
1490
|
};
|
|
@@ -1122,7 +1492,8 @@ function createStep(params, agentOptions) {
|
|
|
1122
1492
|
component: params.component
|
|
1123
1493
|
};
|
|
1124
1494
|
}
|
|
1125
|
-
if (
|
|
1495
|
+
if (params instanceof Tool) {
|
|
1496
|
+
const toolOpts = agentOrToolOptions;
|
|
1126
1497
|
if (!params.inputSchema || !params.outputSchema) {
|
|
1127
1498
|
throw new Error("Tool must have input and output schemas defined");
|
|
1128
1499
|
}
|
|
@@ -1134,6 +1505,8 @@ function createStep(params, agentOptions) {
|
|
|
1134
1505
|
outputSchema: params.outputSchema,
|
|
1135
1506
|
suspendSchema: params.suspendSchema,
|
|
1136
1507
|
resumeSchema: params.resumeSchema,
|
|
1508
|
+
retries: toolOpts?.retries,
|
|
1509
|
+
scorers: toolOpts?.scorers,
|
|
1137
1510
|
execute: async ({
|
|
1138
1511
|
inputData,
|
|
1139
1512
|
mastra,
|
|
@@ -1171,6 +1544,8 @@ function createStep(params, agentOptions) {
|
|
|
1171
1544
|
outputSchema: params.outputSchema,
|
|
1172
1545
|
resumeSchema: params.resumeSchema,
|
|
1173
1546
|
suspendSchema: params.suspendSchema,
|
|
1547
|
+
retries: params.retries,
|
|
1548
|
+
scorers: params.scorers,
|
|
1174
1549
|
execute: params.execute
|
|
1175
1550
|
};
|
|
1176
1551
|
}
|
|
@@ -1214,6 +1589,6 @@ function init(inngest) {
|
|
|
1214
1589
|
};
|
|
1215
1590
|
}
|
|
1216
1591
|
|
|
1217
|
-
export { InngestExecutionEngine, InngestRun, InngestWorkflow, _compatibilityCheck, createStep, init, serve };
|
|
1592
|
+
export { InngestExecutionEngine, InngestPubSub, InngestRun, InngestWorkflow, _compatibilityCheck, createStep, init, serve };
|
|
1218
1593
|
//# sourceMappingURL=index.js.map
|
|
1219
1594
|
//# sourceMappingURL=index.js.map
|