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