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