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