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