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