@mastra/inngest 0.0.0-taofeeqInngest-20250603090617 → 0.0.0-tsconfig-compile-20250703214351
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 +274 -2
- package/dist/_tsup-dts-rollup.d.cts +150 -40
- package/dist/_tsup-dts-rollup.d.ts +150 -40
- package/dist/index.cjs +564 -53
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +564 -54
- package/docker-compose.yaml +3 -3
- package/package.json +21 -19
- package/src/index.test.ts +5538 -3441
- package/src/index.ts +843 -78
- package/vitest.config.ts +6 -0
package/src/index.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { randomUUID } from 'crypto';
|
|
2
|
+
import type { ReadableStream } from 'node:stream/web';
|
|
2
3
|
import { subscribe } from '@inngest/realtime';
|
|
3
|
-
import type { Mastra, WorkflowRun, WorkflowRuns } from '@mastra/core';
|
|
4
|
+
import type { Agent, Mastra, ToolExecutionContext, WorkflowRun, WorkflowRuns } from '@mastra/core';
|
|
4
5
|
import { RuntimeContext } from '@mastra/core/di';
|
|
5
|
-
import {
|
|
6
|
+
import { Tool } from '@mastra/core/tools';
|
|
7
|
+
import { Workflow, Run, DefaultExecutionEngine } from '@mastra/core/workflows';
|
|
6
8
|
import type {
|
|
7
9
|
ExecuteFunction,
|
|
8
10
|
ExecutionContext,
|
|
@@ -14,22 +16,34 @@ import type {
|
|
|
14
16
|
StepResult,
|
|
15
17
|
WorkflowResult,
|
|
16
18
|
SerializedStepFlowEntry,
|
|
19
|
+
StepFailure,
|
|
20
|
+
Emitter,
|
|
21
|
+
WatchEvent,
|
|
22
|
+
StreamEvent,
|
|
17
23
|
} from '@mastra/core/workflows';
|
|
18
24
|
import { EMITTER_SYMBOL } from '@mastra/core/workflows/_constants';
|
|
19
25
|
import type { Span } from '@opentelemetry/api';
|
|
20
26
|
import type { Inngest, BaseContext } from 'inngest';
|
|
21
27
|
import { serve as inngestServe } from 'inngest/hono';
|
|
22
|
-
import
|
|
28
|
+
import { z } from 'zod';
|
|
29
|
+
|
|
30
|
+
export type InngestEngineType = {
|
|
31
|
+
step: any;
|
|
32
|
+
};
|
|
23
33
|
|
|
24
34
|
export function serve({ mastra, inngest }: { mastra: Mastra; inngest: Inngest }): ReturnType<typeof inngestServe> {
|
|
25
35
|
const wfs = mastra.getWorkflows();
|
|
26
|
-
const functions =
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
const functions = Array.from(
|
|
37
|
+
new Set(
|
|
38
|
+
Object.values(wfs).flatMap(wf => {
|
|
39
|
+
if (wf instanceof InngestWorkflow) {
|
|
40
|
+
wf.__registerMastra(mastra);
|
|
41
|
+
return wf.getFunctions();
|
|
42
|
+
}
|
|
43
|
+
return [];
|
|
44
|
+
}),
|
|
45
|
+
),
|
|
46
|
+
);
|
|
33
47
|
return inngestServe({
|
|
34
48
|
client: inngest,
|
|
35
49
|
functions,
|
|
@@ -37,10 +51,11 @@ export function serve({ mastra, inngest }: { mastra: Mastra; inngest: Inngest })
|
|
|
37
51
|
}
|
|
38
52
|
|
|
39
53
|
export class InngestRun<
|
|
54
|
+
TEngineType = InngestEngineType,
|
|
40
55
|
TSteps extends Step<string, any, any>[] = Step<string, any, any>[],
|
|
41
56
|
TInput extends z.ZodType<any> = z.ZodType<any>,
|
|
42
57
|
TOutput extends z.ZodType<any> = z.ZodType<any>,
|
|
43
|
-
> extends Run<TSteps, TInput, TOutput> {
|
|
58
|
+
> extends Run<TEngineType, TSteps, TInput, TOutput> {
|
|
44
59
|
private inngest: Inngest;
|
|
45
60
|
serializedStepGraph: SerializedStepFlowEntry[];
|
|
46
61
|
#mastra: Mastra;
|
|
@@ -79,16 +94,55 @@ export class InngestRun<
|
|
|
79
94
|
|
|
80
95
|
async getRunOutput(eventId: string) {
|
|
81
96
|
let runs = await this.getRuns(eventId);
|
|
82
|
-
|
|
97
|
+
|
|
98
|
+
while (runs?.[0]?.status !== 'Completed' || runs?.[0]?.event_id !== eventId) {
|
|
83
99
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
84
100
|
runs = await this.getRuns(eventId);
|
|
85
|
-
if (runs?.[0]?.status === 'Failed'
|
|
101
|
+
if (runs?.[0]?.status === 'Failed') {
|
|
102
|
+
console.log('run', runs?.[0]);
|
|
86
103
|
throw new Error(`Function run ${runs?.[0]?.status}`);
|
|
104
|
+
} else if (runs?.[0]?.status === 'Cancelled') {
|
|
105
|
+
const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
|
|
106
|
+
workflowName: this.workflowId,
|
|
107
|
+
runId: this.runId,
|
|
108
|
+
});
|
|
109
|
+
return { output: { result: { steps: snapshot?.context, status: 'canceled' } } };
|
|
87
110
|
}
|
|
88
111
|
}
|
|
89
112
|
return runs?.[0];
|
|
90
113
|
}
|
|
91
114
|
|
|
115
|
+
async sendEvent(event: string, data: any) {
|
|
116
|
+
await this.inngest.send({
|
|
117
|
+
name: `user-event-${event}`,
|
|
118
|
+
data,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async cancel() {
|
|
123
|
+
await this.inngest.send({
|
|
124
|
+
name: `cancel.workflow.${this.workflowId}`,
|
|
125
|
+
data: {
|
|
126
|
+
runId: this.runId,
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
|
|
131
|
+
workflowName: this.workflowId,
|
|
132
|
+
runId: this.runId,
|
|
133
|
+
});
|
|
134
|
+
if (snapshot) {
|
|
135
|
+
await this.#mastra?.storage?.persistWorkflowSnapshot({
|
|
136
|
+
workflowName: this.workflowId,
|
|
137
|
+
runId: this.runId,
|
|
138
|
+
snapshot: {
|
|
139
|
+
...snapshot,
|
|
140
|
+
status: 'canceled' as any,
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
92
146
|
async start({
|
|
93
147
|
inputData,
|
|
94
148
|
}: {
|
|
@@ -106,6 +160,7 @@ export class InngestRun<
|
|
|
106
160
|
activePaths: [],
|
|
107
161
|
suspendedPaths: {},
|
|
108
162
|
timestamp: Date.now(),
|
|
163
|
+
status: 'running',
|
|
109
164
|
},
|
|
110
165
|
});
|
|
111
166
|
|
|
@@ -127,7 +182,9 @@ export class InngestRun<
|
|
|
127
182
|
result.error = new Error(result.error);
|
|
128
183
|
}
|
|
129
184
|
|
|
130
|
-
|
|
185
|
+
if (result.status !== 'suspended') {
|
|
186
|
+
this.cleanup?.();
|
|
187
|
+
}
|
|
131
188
|
return result;
|
|
132
189
|
}
|
|
133
190
|
|
|
@@ -139,6 +196,27 @@ export class InngestRun<
|
|
|
139
196
|
| string
|
|
140
197
|
| string[];
|
|
141
198
|
runtimeContext?: RuntimeContext;
|
|
199
|
+
}): Promise<WorkflowResult<TOutput, TSteps>> {
|
|
200
|
+
const p = this._resume(params).then(result => {
|
|
201
|
+
if (result.status !== 'suspended') {
|
|
202
|
+
this.closeStreamAction?.().catch(() => {});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return result;
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
this.executionResults = p;
|
|
209
|
+
return p;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async _resume<TResumeSchema extends z.ZodType<any>>(params: {
|
|
213
|
+
resumeData?: z.infer<TResumeSchema>;
|
|
214
|
+
step:
|
|
215
|
+
| Step<string, any, any, TResumeSchema, any>
|
|
216
|
+
| [...Step<string, any, any, any, any>[], Step<string, any, any, TResumeSchema, any>]
|
|
217
|
+
| string
|
|
218
|
+
| string[];
|
|
219
|
+
runtimeContext?: RuntimeContext;
|
|
142
220
|
}): Promise<WorkflowResult<TOutput, TSteps>> {
|
|
143
221
|
const steps: string[] = (Array.isArray(params.step) ? params.step : [params.step]).map(step =>
|
|
144
222
|
typeof step === 'string' ? step : step?.id,
|
|
@@ -176,37 +254,82 @@ export class InngestRun<
|
|
|
176
254
|
return result;
|
|
177
255
|
}
|
|
178
256
|
|
|
179
|
-
watch(cb: (event:
|
|
257
|
+
watch(cb: (event: WatchEvent) => void, type: 'watch' | 'watch-v2' = 'watch'): () => void {
|
|
258
|
+
let active = true;
|
|
180
259
|
const streamPromise = subscribe(
|
|
181
260
|
{
|
|
182
261
|
channel: `workflow:${this.workflowId}:${this.runId}`,
|
|
183
|
-
topics: [
|
|
262
|
+
topics: [type],
|
|
184
263
|
app: this.inngest,
|
|
185
264
|
},
|
|
186
265
|
(message: any) => {
|
|
187
|
-
|
|
266
|
+
if (active) {
|
|
267
|
+
cb(message.data);
|
|
268
|
+
}
|
|
188
269
|
},
|
|
189
270
|
);
|
|
190
271
|
|
|
191
272
|
return () => {
|
|
273
|
+
active = false;
|
|
192
274
|
streamPromise
|
|
193
|
-
.then((stream:
|
|
194
|
-
stream.cancel();
|
|
275
|
+
.then(async (stream: Awaited<typeof streamPromise>) => {
|
|
276
|
+
return stream.cancel();
|
|
195
277
|
})
|
|
196
278
|
.catch(err => {
|
|
197
279
|
console.error(err);
|
|
198
280
|
});
|
|
199
281
|
};
|
|
200
282
|
}
|
|
283
|
+
|
|
284
|
+
stream({ inputData, runtimeContext }: { inputData?: z.infer<TInput>; runtimeContext?: RuntimeContext } = {}): {
|
|
285
|
+
stream: ReadableStream<StreamEvent>;
|
|
286
|
+
getWorkflowState: () => Promise<WorkflowResult<TOutput, TSteps>>;
|
|
287
|
+
} {
|
|
288
|
+
const { readable, writable } = new TransformStream<StreamEvent, StreamEvent>();
|
|
289
|
+
|
|
290
|
+
const writer = writable.getWriter();
|
|
291
|
+
const unwatch = this.watch(async event => {
|
|
292
|
+
try {
|
|
293
|
+
// watch-v2 events are data stream events, so we need to cast them to the correct type
|
|
294
|
+
await writer.write(event as any);
|
|
295
|
+
} catch {}
|
|
296
|
+
}, 'watch-v2');
|
|
297
|
+
|
|
298
|
+
this.closeStreamAction = async () => {
|
|
299
|
+
unwatch();
|
|
300
|
+
|
|
301
|
+
try {
|
|
302
|
+
await writer.close();
|
|
303
|
+
} catch (err) {
|
|
304
|
+
console.error('Error closing stream:', err);
|
|
305
|
+
} finally {
|
|
306
|
+
writer.releaseLock();
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
this.executionResults = this.start({ inputData, runtimeContext }).then(result => {
|
|
311
|
+
if (result.status !== 'suspended') {
|
|
312
|
+
this.closeStreamAction?.().catch(() => {});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return result;
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
return {
|
|
319
|
+
stream: readable as ReadableStream<StreamEvent>,
|
|
320
|
+
getWorkflowState: () => this.executionResults!,
|
|
321
|
+
};
|
|
322
|
+
}
|
|
201
323
|
}
|
|
202
324
|
|
|
203
325
|
export class InngestWorkflow<
|
|
326
|
+
TEngineType = InngestEngineType,
|
|
204
327
|
TSteps extends Step<string, any, any>[] = Step<string, any, any>[],
|
|
205
328
|
TWorkflowId extends string = string,
|
|
206
329
|
TInput extends z.ZodType<any> = z.ZodType<any>,
|
|
207
330
|
TOutput extends z.ZodType<any> = z.ZodType<any>,
|
|
208
331
|
TPrevSchema extends z.ZodType<any> = TInput,
|
|
209
|
-
> extends Workflow<TSteps, TWorkflowId, TInput, TOutput, TPrevSchema> {
|
|
332
|
+
> extends Workflow<TEngineType, TSteps, TWorkflowId, TInput, TOutput, TPrevSchema> {
|
|
210
333
|
#mastra: Mastra;
|
|
211
334
|
public inngest: Inngest;
|
|
212
335
|
|
|
@@ -238,7 +361,10 @@ export class InngestWorkflow<
|
|
|
238
361
|
const storage = this.#mastra?.getStorage();
|
|
239
362
|
if (!storage) {
|
|
240
363
|
this.logger.debug('Cannot get workflow runs. Mastra engine is not initialized');
|
|
241
|
-
|
|
364
|
+
//returning in memory run if no storage is initialized
|
|
365
|
+
return this.runs.get(runId)
|
|
366
|
+
? ({ ...this.runs.get(runId), workflowName: this.id } as unknown as WorkflowRun)
|
|
367
|
+
: null;
|
|
242
368
|
}
|
|
243
369
|
const run = (await storage.getWorkflowRunById({ runId, workflowName: this.id })) as unknown as WorkflowRun;
|
|
244
370
|
|
|
@@ -248,6 +374,32 @@ export class InngestWorkflow<
|
|
|
248
374
|
);
|
|
249
375
|
}
|
|
250
376
|
|
|
377
|
+
async getWorkflowRunExecutionResult(runId: string): Promise<WatchEvent['payload']['workflowState'] | null> {
|
|
378
|
+
const storage = this.#mastra?.getStorage();
|
|
379
|
+
if (!storage) {
|
|
380
|
+
this.logger.debug('Cannot get workflow run execution result. Mastra storage is not initialized');
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const run = await storage.getWorkflowRunById({ runId, workflowName: this.id });
|
|
385
|
+
|
|
386
|
+
if (!run?.snapshot) {
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (typeof run.snapshot === 'string') {
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return {
|
|
395
|
+
status: run.snapshot.status,
|
|
396
|
+
result: run.snapshot.result,
|
|
397
|
+
error: run.snapshot.error,
|
|
398
|
+
payload: run.snapshot.context?.input,
|
|
399
|
+
steps: run.snapshot.context as any,
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
|
|
251
403
|
__registerMastra(mastra: Mastra) {
|
|
252
404
|
this.#mastra = mastra;
|
|
253
405
|
this.executionEngine.__registerMastra(mastra);
|
|
@@ -271,11 +423,11 @@ export class InngestWorkflow<
|
|
|
271
423
|
}
|
|
272
424
|
}
|
|
273
425
|
|
|
274
|
-
createRun(options?: { runId?: string }): Run<TSteps, TInput, TOutput> {
|
|
426
|
+
createRun(options?: { runId?: string }): Run<TEngineType, TSteps, TInput, TOutput> {
|
|
275
427
|
const runIdToUse = options?.runId || randomUUID();
|
|
276
428
|
|
|
277
429
|
// Return a new Run instance with object parameters
|
|
278
|
-
const run: Run<TSteps, TInput, TOutput> =
|
|
430
|
+
const run: Run<TEngineType, TSteps, TInput, TOutput> =
|
|
279
431
|
this.runs.get(runIdToUse) ??
|
|
280
432
|
new InngestRun(
|
|
281
433
|
{
|
|
@@ -295,13 +447,64 @@ export class InngestWorkflow<
|
|
|
295
447
|
return run;
|
|
296
448
|
}
|
|
297
449
|
|
|
450
|
+
async createRunAsync(options?: { runId?: string }): Promise<Run<TEngineType, TSteps, TInput, TOutput>> {
|
|
451
|
+
const runIdToUse = options?.runId || randomUUID();
|
|
452
|
+
|
|
453
|
+
// Return a new Run instance with object parameters
|
|
454
|
+
const run: Run<TEngineType, TSteps, TInput, TOutput> =
|
|
455
|
+
this.runs.get(runIdToUse) ??
|
|
456
|
+
new InngestRun(
|
|
457
|
+
{
|
|
458
|
+
workflowId: this.id,
|
|
459
|
+
runId: runIdToUse,
|
|
460
|
+
executionEngine: this.executionEngine,
|
|
461
|
+
executionGraph: this.executionGraph,
|
|
462
|
+
serializedStepGraph: this.serializedStepGraph,
|
|
463
|
+
mastra: this.#mastra,
|
|
464
|
+
retryConfig: this.retryConfig,
|
|
465
|
+
cleanup: () => this.runs.delete(runIdToUse),
|
|
466
|
+
},
|
|
467
|
+
this.inngest,
|
|
468
|
+
);
|
|
469
|
+
|
|
470
|
+
this.runs.set(runIdToUse, run);
|
|
471
|
+
|
|
472
|
+
const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse);
|
|
473
|
+
|
|
474
|
+
if (!workflowSnapshotInStorage) {
|
|
475
|
+
await this.mastra?.getStorage()?.persistWorkflowSnapshot({
|
|
476
|
+
workflowName: this.id,
|
|
477
|
+
runId: runIdToUse,
|
|
478
|
+
snapshot: {
|
|
479
|
+
runId: runIdToUse,
|
|
480
|
+
status: 'pending',
|
|
481
|
+
value: {},
|
|
482
|
+
context: {},
|
|
483
|
+
activePaths: [],
|
|
484
|
+
serializedStepGraph: this.serializedStepGraph,
|
|
485
|
+
suspendedPaths: {},
|
|
486
|
+
result: undefined,
|
|
487
|
+
error: undefined,
|
|
488
|
+
// @ts-ignore
|
|
489
|
+
timestamp: Date.now(),
|
|
490
|
+
},
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
return run;
|
|
495
|
+
}
|
|
496
|
+
|
|
298
497
|
getFunction() {
|
|
299
498
|
if (this.function) {
|
|
300
499
|
return this.function;
|
|
301
500
|
}
|
|
302
501
|
this.function = this.inngest.createFunction(
|
|
303
|
-
|
|
304
|
-
|
|
502
|
+
{
|
|
503
|
+
id: `workflow.${this.id}`,
|
|
504
|
+
// @ts-ignore
|
|
505
|
+
retries: this.retryConfig?.attempts ?? 0,
|
|
506
|
+
cancelOn: [{ event: `cancel.workflow.${this.id}` }],
|
|
507
|
+
},
|
|
305
508
|
{ event: `workflow.${this.id}` },
|
|
306
509
|
async ({ event, step, attempt, publish }) => {
|
|
307
510
|
let { inputData, runId, resume } = event.data;
|
|
@@ -321,13 +524,22 @@ export class InngestWorkflow<
|
|
|
321
524
|
try {
|
|
322
525
|
await publish({
|
|
323
526
|
channel: `workflow:${this.id}:${runId}`,
|
|
324
|
-
topic:
|
|
527
|
+
topic: event,
|
|
325
528
|
data,
|
|
326
529
|
});
|
|
327
530
|
} catch (err: any) {
|
|
328
531
|
this.logger.error('Error emitting event: ' + (err?.stack ?? err?.message ?? err));
|
|
329
532
|
}
|
|
330
533
|
},
|
|
534
|
+
on: (_event: string, _callback: (data: any) => void) => {
|
|
535
|
+
// no-op
|
|
536
|
+
},
|
|
537
|
+
off: (_event: string, _callback: (data: any) => void) => {
|
|
538
|
+
// no-op
|
|
539
|
+
},
|
|
540
|
+
once: (_event: string, _callback: (data: any) => void) => {
|
|
541
|
+
// no-op
|
|
542
|
+
},
|
|
331
543
|
};
|
|
332
544
|
|
|
333
545
|
const engine = new InngestExecutionEngine(this.#mastra, step, attempt);
|
|
@@ -341,6 +553,7 @@ export class InngestWorkflow<
|
|
|
341
553
|
retryConfig: this.retryConfig,
|
|
342
554
|
runtimeContext: new RuntimeContext(), // TODO
|
|
343
555
|
resume,
|
|
556
|
+
abortController: new AbortController(),
|
|
344
557
|
});
|
|
345
558
|
|
|
346
559
|
return { result, runId };
|
|
@@ -369,29 +582,197 @@ export class InngestWorkflow<
|
|
|
369
582
|
}
|
|
370
583
|
}
|
|
371
584
|
|
|
372
|
-
function
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
585
|
+
function isAgent(params: any): params is Agent<any, any, any> {
|
|
586
|
+
return params?.component === 'AGENT';
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
function isTool(params: any): params is Tool<any, any, any> {
|
|
590
|
+
return params instanceof Tool;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
export function createStep<
|
|
594
|
+
TStepId extends string,
|
|
595
|
+
TStepInput extends z.ZodType<any>,
|
|
596
|
+
TStepOutput extends z.ZodType<any>,
|
|
597
|
+
TResumeSchema extends z.ZodType<any>,
|
|
598
|
+
TSuspendSchema extends z.ZodType<any>,
|
|
599
|
+
>(params: {
|
|
600
|
+
id: TStepId;
|
|
601
|
+
description?: string;
|
|
602
|
+
inputSchema: TStepInput;
|
|
603
|
+
outputSchema: TStepOutput;
|
|
604
|
+
resumeSchema?: TResumeSchema;
|
|
605
|
+
suspendSchema?: TSuspendSchema;
|
|
606
|
+
execute: ExecuteFunction<
|
|
607
|
+
z.infer<TStepInput>,
|
|
608
|
+
z.infer<TStepOutput>,
|
|
609
|
+
z.infer<TResumeSchema>,
|
|
610
|
+
z.infer<TSuspendSchema>,
|
|
611
|
+
InngestEngineType
|
|
612
|
+
>;
|
|
613
|
+
}): Step<TStepId, TStepInput, TStepOutput, TResumeSchema, TSuspendSchema, InngestEngineType>;
|
|
614
|
+
|
|
615
|
+
export function createStep<
|
|
616
|
+
TStepId extends string,
|
|
617
|
+
TStepInput extends z.ZodObject<{ prompt: z.ZodString }>,
|
|
618
|
+
TStepOutput extends z.ZodObject<{ text: z.ZodString }>,
|
|
619
|
+
TResumeSchema extends z.ZodType<any>,
|
|
620
|
+
TSuspendSchema extends z.ZodType<any>,
|
|
377
621
|
>(
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
622
|
+
agent: Agent<TStepId, any, any>,
|
|
623
|
+
): Step<TStepId, TStepInput, TStepOutput, TResumeSchema, TSuspendSchema, InngestEngineType>;
|
|
624
|
+
|
|
625
|
+
export function createStep<
|
|
626
|
+
TSchemaIn extends z.ZodType<any>,
|
|
627
|
+
TSchemaOut extends z.ZodType<any>,
|
|
628
|
+
TContext extends ToolExecutionContext<TSchemaIn>,
|
|
629
|
+
>(
|
|
630
|
+
tool: Tool<TSchemaIn, TSchemaOut, TContext> & {
|
|
631
|
+
inputSchema: TSchemaIn;
|
|
632
|
+
outputSchema: TSchemaOut;
|
|
633
|
+
execute: (context: TContext) => Promise<any>;
|
|
634
|
+
},
|
|
635
|
+
): Step<string, TSchemaIn, TSchemaOut, z.ZodType<any>, z.ZodType<any>, InngestEngineType>;
|
|
636
|
+
export function createStep<
|
|
637
|
+
TStepId extends string,
|
|
638
|
+
TStepInput extends z.ZodType<any>,
|
|
639
|
+
TStepOutput extends z.ZodType<any>,
|
|
640
|
+
TResumeSchema extends z.ZodType<any>,
|
|
641
|
+
TSuspendSchema extends z.ZodType<any>,
|
|
642
|
+
>(
|
|
643
|
+
params:
|
|
644
|
+
| {
|
|
645
|
+
id: TStepId;
|
|
646
|
+
description?: string;
|
|
647
|
+
inputSchema: TStepInput;
|
|
648
|
+
outputSchema: TStepOutput;
|
|
649
|
+
resumeSchema?: TResumeSchema;
|
|
650
|
+
suspendSchema?: TSuspendSchema;
|
|
651
|
+
execute: ExecuteFunction<
|
|
652
|
+
z.infer<TStepInput>,
|
|
653
|
+
z.infer<TStepOutput>,
|
|
654
|
+
z.infer<TResumeSchema>,
|
|
655
|
+
z.infer<TSuspendSchema>,
|
|
656
|
+
InngestEngineType
|
|
657
|
+
>;
|
|
658
|
+
}
|
|
659
|
+
| Agent<any, any, any>
|
|
660
|
+
| (Tool<TStepInput, TStepOutput, any> & {
|
|
661
|
+
inputSchema: TStepInput;
|
|
662
|
+
outputSchema: TStepOutput;
|
|
663
|
+
execute: (context: ToolExecutionContext<TStepInput>) => Promise<any>;
|
|
664
|
+
}),
|
|
665
|
+
): Step<TStepId, TStepInput, TStepOutput, TResumeSchema, TSuspendSchema, InngestEngineType> {
|
|
666
|
+
if (isAgent(params)) {
|
|
667
|
+
return {
|
|
668
|
+
id: params.name,
|
|
669
|
+
// @ts-ignore
|
|
670
|
+
inputSchema: z.object({
|
|
671
|
+
prompt: z.string(),
|
|
672
|
+
// resourceId: z.string().optional(),
|
|
673
|
+
// threadId: z.string().optional(),
|
|
674
|
+
}),
|
|
675
|
+
// @ts-ignore
|
|
676
|
+
outputSchema: z.object({
|
|
677
|
+
text: z.string(),
|
|
678
|
+
}),
|
|
679
|
+
execute: async ({ inputData, [EMITTER_SYMBOL]: emitter, runtimeContext, abortSignal, abort }) => {
|
|
680
|
+
let streamPromise = {} as {
|
|
681
|
+
promise: Promise<string>;
|
|
682
|
+
resolve: (value: string) => void;
|
|
683
|
+
reject: (reason?: any) => void;
|
|
684
|
+
};
|
|
391
685
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
686
|
+
streamPromise.promise = new Promise((resolve, reject) => {
|
|
687
|
+
streamPromise.resolve = resolve;
|
|
688
|
+
streamPromise.reject = reject;
|
|
689
|
+
});
|
|
690
|
+
const toolData = {
|
|
691
|
+
name: params.name,
|
|
692
|
+
args: inputData,
|
|
693
|
+
};
|
|
694
|
+
await emitter.emit('watch-v2', {
|
|
695
|
+
type: 'tool-call-streaming-start',
|
|
696
|
+
...toolData,
|
|
697
|
+
});
|
|
698
|
+
const { fullStream } = await params.stream(inputData.prompt, {
|
|
699
|
+
// resourceId: inputData.resourceId,
|
|
700
|
+
// threadId: inputData.threadId,
|
|
701
|
+
runtimeContext,
|
|
702
|
+
onFinish: result => {
|
|
703
|
+
streamPromise.resolve(result.text);
|
|
704
|
+
},
|
|
705
|
+
abortSignal,
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
if (abortSignal.aborted) {
|
|
709
|
+
return abort();
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
for await (const chunk of fullStream) {
|
|
713
|
+
switch (chunk.type) {
|
|
714
|
+
case 'text-delta':
|
|
715
|
+
await emitter.emit('watch-v2', {
|
|
716
|
+
type: 'tool-call-delta',
|
|
717
|
+
...toolData,
|
|
718
|
+
argsTextDelta: chunk.textDelta,
|
|
719
|
+
});
|
|
720
|
+
break;
|
|
721
|
+
|
|
722
|
+
case 'step-start':
|
|
723
|
+
case 'step-finish':
|
|
724
|
+
case 'finish':
|
|
725
|
+
break;
|
|
726
|
+
|
|
727
|
+
case 'tool-call':
|
|
728
|
+
case 'tool-result':
|
|
729
|
+
case 'tool-call-streaming-start':
|
|
730
|
+
case 'tool-call-delta':
|
|
731
|
+
case 'source':
|
|
732
|
+
case 'file':
|
|
733
|
+
default:
|
|
734
|
+
await emitter.emit('watch-v2', chunk);
|
|
735
|
+
break;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
return {
|
|
740
|
+
text: await streamPromise.promise,
|
|
741
|
+
};
|
|
742
|
+
},
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
if (isTool(params)) {
|
|
747
|
+
if (!params.inputSchema || !params.outputSchema) {
|
|
748
|
+
throw new Error('Tool must have input and output schemas defined');
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
return {
|
|
752
|
+
// TODO: tool probably should have strong id type
|
|
753
|
+
// @ts-ignore
|
|
754
|
+
id: params.id,
|
|
755
|
+
inputSchema: params.inputSchema,
|
|
756
|
+
outputSchema: params.outputSchema,
|
|
757
|
+
execute: async ({ inputData, mastra, runtimeContext }) => {
|
|
758
|
+
return params.execute({
|
|
759
|
+
context: inputData,
|
|
760
|
+
mastra,
|
|
761
|
+
runtimeContext,
|
|
762
|
+
});
|
|
763
|
+
},
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
return {
|
|
768
|
+
id: params.id,
|
|
769
|
+
description: params.description,
|
|
770
|
+
inputSchema: params.inputSchema,
|
|
771
|
+
outputSchema: params.outputSchema,
|
|
772
|
+
resumeSchema: params.resumeSchema,
|
|
773
|
+
suspendSchema: params.suspendSchema,
|
|
774
|
+
execute: params.execute,
|
|
775
|
+
};
|
|
395
776
|
}
|
|
396
777
|
|
|
397
778
|
export function init(inngest: Inngest) {
|
|
@@ -400,13 +781,59 @@ export function init(inngest: Inngest) {
|
|
|
400
781
|
TWorkflowId extends string = string,
|
|
401
782
|
TInput extends z.ZodType<any> = z.ZodType<any>,
|
|
402
783
|
TOutput extends z.ZodType<any> = z.ZodType<any>,
|
|
403
|
-
TSteps extends Step<string, any, any
|
|
784
|
+
TSteps extends Step<string, any, any, any, any, InngestEngineType>[] = Step<
|
|
785
|
+
string,
|
|
786
|
+
any,
|
|
787
|
+
any,
|
|
788
|
+
any,
|
|
789
|
+
any,
|
|
790
|
+
InngestEngineType
|
|
791
|
+
>[],
|
|
404
792
|
>(params: WorkflowConfig<TWorkflowId, TInput, TOutput, TSteps>) {
|
|
405
|
-
return new InngestWorkflow(params, inngest);
|
|
793
|
+
return new InngestWorkflow<InngestEngineType, TSteps, TWorkflowId, TInput, TOutput, TInput>(params, inngest);
|
|
406
794
|
},
|
|
407
795
|
createStep,
|
|
408
|
-
cloneStep
|
|
409
|
-
|
|
796
|
+
cloneStep<TStepId extends string>(
|
|
797
|
+
step: Step<string, any, any, any, any, InngestEngineType>,
|
|
798
|
+
opts: { id: TStepId },
|
|
799
|
+
): Step<TStepId, any, any, any, any, InngestEngineType> {
|
|
800
|
+
return {
|
|
801
|
+
id: opts.id,
|
|
802
|
+
description: step.description,
|
|
803
|
+
inputSchema: step.inputSchema,
|
|
804
|
+
outputSchema: step.outputSchema,
|
|
805
|
+
execute: step.execute,
|
|
806
|
+
};
|
|
807
|
+
},
|
|
808
|
+
cloneWorkflow<
|
|
809
|
+
TWorkflowId extends string = string,
|
|
810
|
+
TInput extends z.ZodType<any> = z.ZodType<any>,
|
|
811
|
+
TOutput extends z.ZodType<any> = z.ZodType<any>,
|
|
812
|
+
TSteps extends Step<string, any, any, any, any, InngestEngineType>[] = Step<
|
|
813
|
+
string,
|
|
814
|
+
any,
|
|
815
|
+
any,
|
|
816
|
+
any,
|
|
817
|
+
any,
|
|
818
|
+
InngestEngineType
|
|
819
|
+
>[],
|
|
820
|
+
TPrevSchema extends z.ZodType<any> = TInput,
|
|
821
|
+
>(
|
|
822
|
+
workflow: Workflow<InngestEngineType, TSteps, string, TInput, TOutput, TPrevSchema>,
|
|
823
|
+
opts: { id: TWorkflowId },
|
|
824
|
+
): Workflow<InngestEngineType, TSteps, TWorkflowId, TInput, TOutput, TPrevSchema> {
|
|
825
|
+
const wf: Workflow<InngestEngineType, TSteps, TWorkflowId, TInput, TOutput, TPrevSchema> = new Workflow({
|
|
826
|
+
id: opts.id,
|
|
827
|
+
inputSchema: workflow.inputSchema,
|
|
828
|
+
outputSchema: workflow.outputSchema,
|
|
829
|
+
steps: workflow.stepDefs,
|
|
830
|
+
mastra: workflow.mastra,
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
wf.setStepFlow(workflow.stepGraph);
|
|
834
|
+
wf.commit();
|
|
835
|
+
return wf;
|
|
836
|
+
},
|
|
410
837
|
};
|
|
411
838
|
}
|
|
412
839
|
|
|
@@ -420,9 +847,45 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
420
847
|
this.inngestAttempts = inngestAttempts;
|
|
421
848
|
}
|
|
422
849
|
|
|
850
|
+
async execute<TInput, TOutput>(params: {
|
|
851
|
+
workflowId: string;
|
|
852
|
+
runId: string;
|
|
853
|
+
graph: ExecutionGraph;
|
|
854
|
+
serializedStepGraph: SerializedStepFlowEntry[];
|
|
855
|
+
input?: TInput;
|
|
856
|
+
resume?: {
|
|
857
|
+
// TODO: add execute path
|
|
858
|
+
steps: string[];
|
|
859
|
+
stepResults: Record<string, StepResult<any, any, any, any>>;
|
|
860
|
+
resumePayload: any;
|
|
861
|
+
resumePath: number[];
|
|
862
|
+
};
|
|
863
|
+
emitter: Emitter;
|
|
864
|
+
retryConfig?: {
|
|
865
|
+
attempts?: number;
|
|
866
|
+
delay?: number;
|
|
867
|
+
};
|
|
868
|
+
runtimeContext: RuntimeContext;
|
|
869
|
+
abortController: AbortController;
|
|
870
|
+
}): Promise<TOutput> {
|
|
871
|
+
await params.emitter.emit('watch-v2', {
|
|
872
|
+
type: 'start',
|
|
873
|
+
payload: { runId: params.runId },
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
const result = await super.execute<TInput, TOutput>(params);
|
|
877
|
+
|
|
878
|
+
await params.emitter.emit('watch-v2', {
|
|
879
|
+
type: 'finish',
|
|
880
|
+
payload: { runId: params.runId },
|
|
881
|
+
});
|
|
882
|
+
|
|
883
|
+
return result;
|
|
884
|
+
}
|
|
885
|
+
|
|
423
886
|
protected async fmtReturnValue<TOutput>(
|
|
424
887
|
executionSpan: Span | undefined,
|
|
425
|
-
emitter:
|
|
888
|
+
emitter: Emitter,
|
|
426
889
|
stepResults: Record<string, StepResult<any, any, any, any>>,
|
|
427
890
|
lastOutput: StepResult<any, any, any, any>,
|
|
428
891
|
error?: Error | string,
|
|
@@ -503,6 +966,7 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
503
966
|
resume,
|
|
504
967
|
prevOutput,
|
|
505
968
|
emitter,
|
|
969
|
+
abortController,
|
|
506
970
|
runtimeContext,
|
|
507
971
|
}: {
|
|
508
972
|
workflowId: string;
|
|
@@ -515,7 +979,8 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
515
979
|
resumePayload: any;
|
|
516
980
|
};
|
|
517
981
|
prevOutput: any;
|
|
518
|
-
emitter:
|
|
982
|
+
emitter: Emitter;
|
|
983
|
+
abortController: AbortController;
|
|
519
984
|
runtimeContext: RuntimeContext;
|
|
520
985
|
}): Promise<StepResult<any, any, any, any>> {
|
|
521
986
|
return super.executeStep({
|
|
@@ -527,10 +992,178 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
527
992
|
resume,
|
|
528
993
|
prevOutput,
|
|
529
994
|
emitter,
|
|
995
|
+
abortController,
|
|
530
996
|
runtimeContext,
|
|
531
997
|
});
|
|
532
998
|
}
|
|
533
999
|
|
|
1000
|
+
// async executeSleep({ id, duration }: { id: string; duration: number }): Promise<void> {
|
|
1001
|
+
// await this.inngestStep.sleep(id, duration);
|
|
1002
|
+
// }
|
|
1003
|
+
|
|
1004
|
+
async executeSleep({
|
|
1005
|
+
workflowId,
|
|
1006
|
+
runId,
|
|
1007
|
+
entry,
|
|
1008
|
+
prevOutput,
|
|
1009
|
+
stepResults,
|
|
1010
|
+
emitter,
|
|
1011
|
+
abortController,
|
|
1012
|
+
runtimeContext,
|
|
1013
|
+
}: {
|
|
1014
|
+
workflowId: string;
|
|
1015
|
+
runId: string;
|
|
1016
|
+
serializedStepGraph: SerializedStepFlowEntry[];
|
|
1017
|
+
entry: {
|
|
1018
|
+
type: 'sleep';
|
|
1019
|
+
id: string;
|
|
1020
|
+
duration?: number;
|
|
1021
|
+
fn?: ExecuteFunction<any, any, any, any, InngestEngineType>;
|
|
1022
|
+
};
|
|
1023
|
+
prevStep: StepFlowEntry;
|
|
1024
|
+
prevOutput: any;
|
|
1025
|
+
stepResults: Record<string, StepResult<any, any, any, any>>;
|
|
1026
|
+
resume?: {
|
|
1027
|
+
steps: string[];
|
|
1028
|
+
stepResults: Record<string, StepResult<any, any, any, any>>;
|
|
1029
|
+
resumePayload: any;
|
|
1030
|
+
resumePath: number[];
|
|
1031
|
+
};
|
|
1032
|
+
executionContext: ExecutionContext;
|
|
1033
|
+
emitter: Emitter;
|
|
1034
|
+
abortController: AbortController;
|
|
1035
|
+
runtimeContext: RuntimeContext;
|
|
1036
|
+
}): Promise<void> {
|
|
1037
|
+
let { duration, fn } = entry;
|
|
1038
|
+
|
|
1039
|
+
if (fn) {
|
|
1040
|
+
duration = await this.inngestStep.run(`workflow.${workflowId}.sleep.${entry.id}`, async () => {
|
|
1041
|
+
return await fn({
|
|
1042
|
+
runId,
|
|
1043
|
+
mastra: this.mastra!,
|
|
1044
|
+
runtimeContext,
|
|
1045
|
+
inputData: prevOutput,
|
|
1046
|
+
runCount: -1,
|
|
1047
|
+
getInitData: () => stepResults?.input as any,
|
|
1048
|
+
getStepResult: (step: any) => {
|
|
1049
|
+
if (!step?.id) {
|
|
1050
|
+
return null;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
const result = stepResults[step.id];
|
|
1054
|
+
if (result?.status === 'success') {
|
|
1055
|
+
return result.output;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
return null;
|
|
1059
|
+
},
|
|
1060
|
+
|
|
1061
|
+
// TODO: this function shouldn't have suspend probably?
|
|
1062
|
+
suspend: async (_suspendPayload: any): Promise<any> => {},
|
|
1063
|
+
bail: () => {},
|
|
1064
|
+
abort: () => {
|
|
1065
|
+
abortController?.abort();
|
|
1066
|
+
},
|
|
1067
|
+
[EMITTER_SYMBOL]: emitter,
|
|
1068
|
+
engine: { step: this.inngestStep },
|
|
1069
|
+
abortSignal: abortController?.signal,
|
|
1070
|
+
});
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
await this.inngestStep.sleep(entry.id, !duration || duration < 0 ? 0 : duration);
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
async executeSleepUntil({
|
|
1078
|
+
workflowId,
|
|
1079
|
+
runId,
|
|
1080
|
+
entry,
|
|
1081
|
+
prevOutput,
|
|
1082
|
+
stepResults,
|
|
1083
|
+
emitter,
|
|
1084
|
+
abortController,
|
|
1085
|
+
runtimeContext,
|
|
1086
|
+
}: {
|
|
1087
|
+
workflowId: string;
|
|
1088
|
+
runId: string;
|
|
1089
|
+
serializedStepGraph: SerializedStepFlowEntry[];
|
|
1090
|
+
entry: {
|
|
1091
|
+
type: 'sleepUntil';
|
|
1092
|
+
id: string;
|
|
1093
|
+
date?: Date;
|
|
1094
|
+
fn?: ExecuteFunction<any, any, any, any, InngestEngineType>;
|
|
1095
|
+
};
|
|
1096
|
+
prevStep: StepFlowEntry;
|
|
1097
|
+
prevOutput: any;
|
|
1098
|
+
stepResults: Record<string, StepResult<any, any, any, any>>;
|
|
1099
|
+
resume?: {
|
|
1100
|
+
steps: string[];
|
|
1101
|
+
stepResults: Record<string, StepResult<any, any, any, any>>;
|
|
1102
|
+
resumePayload: any;
|
|
1103
|
+
resumePath: number[];
|
|
1104
|
+
};
|
|
1105
|
+
executionContext: ExecutionContext;
|
|
1106
|
+
emitter: Emitter;
|
|
1107
|
+
abortController: AbortController;
|
|
1108
|
+
runtimeContext: RuntimeContext;
|
|
1109
|
+
}): Promise<void> {
|
|
1110
|
+
let { date, fn } = entry;
|
|
1111
|
+
|
|
1112
|
+
if (fn) {
|
|
1113
|
+
date = await this.inngestStep.run(`workflow.${workflowId}.sleepUntil.${entry.id}`, async () => {
|
|
1114
|
+
return await fn({
|
|
1115
|
+
runId,
|
|
1116
|
+
mastra: this.mastra!,
|
|
1117
|
+
runtimeContext,
|
|
1118
|
+
inputData: prevOutput,
|
|
1119
|
+
runCount: -1,
|
|
1120
|
+
getInitData: () => stepResults?.input as any,
|
|
1121
|
+
getStepResult: (step: any) => {
|
|
1122
|
+
if (!step?.id) {
|
|
1123
|
+
return null;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
const result = stepResults[step.id];
|
|
1127
|
+
if (result?.status === 'success') {
|
|
1128
|
+
return result.output;
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
return null;
|
|
1132
|
+
},
|
|
1133
|
+
|
|
1134
|
+
// TODO: this function shouldn't have suspend probably?
|
|
1135
|
+
suspend: async (_suspendPayload: any): Promise<any> => {},
|
|
1136
|
+
bail: () => {},
|
|
1137
|
+
abort: () => {
|
|
1138
|
+
abortController?.abort();
|
|
1139
|
+
},
|
|
1140
|
+
[EMITTER_SYMBOL]: emitter,
|
|
1141
|
+
engine: { step: this.inngestStep },
|
|
1142
|
+
abortSignal: abortController?.signal,
|
|
1143
|
+
});
|
|
1144
|
+
});
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
if (!(date instanceof Date)) {
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
await this.inngestStep.sleepUntil(entry.id, date);
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
async executeWaitForEvent({ event, timeout }: { event: string; timeout?: number }): Promise<any> {
|
|
1155
|
+
const eventData = await this.inngestStep.waitForEvent(`user-event-${event}`, {
|
|
1156
|
+
event: `user-event-${event}`,
|
|
1157
|
+
timeout: timeout ?? 5e3,
|
|
1158
|
+
});
|
|
1159
|
+
|
|
1160
|
+
if (eventData === null) {
|
|
1161
|
+
throw 'Timeout waiting for event';
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
return eventData?.data;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
534
1167
|
async executeStep({
|
|
535
1168
|
step,
|
|
536
1169
|
stepResults,
|
|
@@ -538,29 +1171,26 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
538
1171
|
resume,
|
|
539
1172
|
prevOutput,
|
|
540
1173
|
emitter,
|
|
1174
|
+
abortController,
|
|
541
1175
|
runtimeContext,
|
|
542
1176
|
}: {
|
|
543
1177
|
step: Step<string, any, any>;
|
|
544
1178
|
stepResults: Record<string, StepResult<any, any, any, any>>;
|
|
545
|
-
executionContext:
|
|
546
|
-
workflowId: string;
|
|
547
|
-
runId: string;
|
|
548
|
-
executionPath: number[];
|
|
549
|
-
suspendedPaths: Record<string, number[]>;
|
|
550
|
-
retryConfig: { attempts: number; delay: number };
|
|
551
|
-
};
|
|
1179
|
+
executionContext: ExecutionContext;
|
|
552
1180
|
resume?: {
|
|
553
1181
|
steps: string[];
|
|
554
1182
|
resumePayload: any;
|
|
555
1183
|
runId?: string;
|
|
556
1184
|
};
|
|
557
1185
|
prevOutput: any;
|
|
558
|
-
emitter:
|
|
1186
|
+
emitter: Emitter;
|
|
1187
|
+
abortController: AbortController;
|
|
559
1188
|
runtimeContext: RuntimeContext;
|
|
560
1189
|
}): Promise<StepResult<any, any, any, any>> {
|
|
561
|
-
await this.inngestStep.run(
|
|
1190
|
+
const startedAt = await this.inngestStep.run(
|
|
562
1191
|
`workflow.${executionContext.workflowId}.run.${executionContext.runId}.step.${step.id}.running_ev`,
|
|
563
1192
|
async () => {
|
|
1193
|
+
const startedAt = Date.now();
|
|
564
1194
|
await emitter.emit('watch', {
|
|
565
1195
|
type: 'watch',
|
|
566
1196
|
payload: {
|
|
@@ -582,6 +1212,18 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
582
1212
|
},
|
|
583
1213
|
eventTimestamp: Date.now(),
|
|
584
1214
|
});
|
|
1215
|
+
|
|
1216
|
+
await emitter.emit('watch-v2', {
|
|
1217
|
+
type: 'step-start',
|
|
1218
|
+
payload: {
|
|
1219
|
+
id: step.id,
|
|
1220
|
+
status: 'running',
|
|
1221
|
+
payload: prevOutput,
|
|
1222
|
+
startedAt,
|
|
1223
|
+
},
|
|
1224
|
+
});
|
|
1225
|
+
|
|
1226
|
+
return startedAt;
|
|
585
1227
|
},
|
|
586
1228
|
);
|
|
587
1229
|
|
|
@@ -648,6 +1290,16 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
648
1290
|
eventTimestamp: Date.now(),
|
|
649
1291
|
});
|
|
650
1292
|
|
|
1293
|
+
await emitter.emit('watch-v2', {
|
|
1294
|
+
type: 'step-result',
|
|
1295
|
+
payload: {
|
|
1296
|
+
id: step.id,
|
|
1297
|
+
status: 'failed',
|
|
1298
|
+
error: result?.error,
|
|
1299
|
+
payload: prevOutput,
|
|
1300
|
+
},
|
|
1301
|
+
});
|
|
1302
|
+
|
|
651
1303
|
return { executionContext, result: { status: 'failed', error: result?.error } };
|
|
652
1304
|
} else if (result.status === 'suspended') {
|
|
653
1305
|
const suspendedSteps = Object.entries(result.steps).filter(([_stepName, stepResult]) => {
|
|
@@ -678,6 +1330,14 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
678
1330
|
eventTimestamp: Date.now(),
|
|
679
1331
|
});
|
|
680
1332
|
|
|
1333
|
+
await emitter.emit('watch-v2', {
|
|
1334
|
+
type: 'step-suspended',
|
|
1335
|
+
payload: {
|
|
1336
|
+
id: step.id,
|
|
1337
|
+
status: 'suspended',
|
|
1338
|
+
},
|
|
1339
|
+
});
|
|
1340
|
+
|
|
681
1341
|
return {
|
|
682
1342
|
executionContext,
|
|
683
1343
|
result: {
|
|
@@ -734,6 +1394,23 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
734
1394
|
eventTimestamp: Date.now(),
|
|
735
1395
|
});
|
|
736
1396
|
|
|
1397
|
+
await emitter.emit('watch-v2', {
|
|
1398
|
+
type: 'step-result',
|
|
1399
|
+
payload: {
|
|
1400
|
+
id: step.id,
|
|
1401
|
+
status: 'success',
|
|
1402
|
+
output: result?.result,
|
|
1403
|
+
},
|
|
1404
|
+
});
|
|
1405
|
+
|
|
1406
|
+
await emitter.emit('watch-v2', {
|
|
1407
|
+
type: 'step-finish',
|
|
1408
|
+
payload: {
|
|
1409
|
+
id: step.id,
|
|
1410
|
+
metadata: {},
|
|
1411
|
+
},
|
|
1412
|
+
});
|
|
1413
|
+
|
|
737
1414
|
return { executionContext, result: { status: 'success', output: result?.result } };
|
|
738
1415
|
},
|
|
739
1416
|
);
|
|
@@ -745,6 +1422,8 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
745
1422
|
const stepRes = await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}`, async () => {
|
|
746
1423
|
let execResults: any;
|
|
747
1424
|
let suspended: { payload: any } | undefined;
|
|
1425
|
+
let bailed: { payload: any } | undefined;
|
|
1426
|
+
|
|
748
1427
|
try {
|
|
749
1428
|
const result = await step.execute({
|
|
750
1429
|
runId: executionContext.runId,
|
|
@@ -765,22 +1444,56 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
765
1444
|
executionContext.suspendedPaths[step.id] = executionContext.executionPath;
|
|
766
1445
|
suspended = { payload: suspendPayload };
|
|
767
1446
|
},
|
|
1447
|
+
bail: (result: any) => {
|
|
1448
|
+
bailed = { payload: result };
|
|
1449
|
+
},
|
|
768
1450
|
resume: {
|
|
769
1451
|
steps: resume?.steps?.slice(1) || [],
|
|
770
1452
|
resumePayload: resume?.resumePayload,
|
|
771
1453
|
// @ts-ignore
|
|
772
1454
|
runId: stepResults[step.id]?.payload?.__workflow_meta?.runId,
|
|
773
1455
|
},
|
|
774
|
-
emitter,
|
|
1456
|
+
[EMITTER_SYMBOL]: emitter,
|
|
1457
|
+
engine: {
|
|
1458
|
+
step: this.inngestStep,
|
|
1459
|
+
},
|
|
1460
|
+
abortSignal: abortController.signal,
|
|
775
1461
|
});
|
|
776
|
-
|
|
777
|
-
|
|
1462
|
+
const endedAt = Date.now();
|
|
1463
|
+
|
|
1464
|
+
execResults = {
|
|
1465
|
+
status: 'success',
|
|
1466
|
+
output: result,
|
|
1467
|
+
startedAt,
|
|
1468
|
+
endedAt,
|
|
1469
|
+
payload: prevOutput,
|
|
1470
|
+
resumedAt: resume?.steps[0] === step.id ? startedAt : undefined,
|
|
1471
|
+
resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : undefined,
|
|
1472
|
+
};
|
|
778
1473
|
} catch (e) {
|
|
779
|
-
execResults = {
|
|
1474
|
+
execResults = {
|
|
1475
|
+
status: 'failed',
|
|
1476
|
+
payload: prevOutput,
|
|
1477
|
+
error: e instanceof Error ? e.message : String(e),
|
|
1478
|
+
endedAt: Date.now(),
|
|
1479
|
+
startedAt,
|
|
1480
|
+
resumedAt: resume?.steps[0] === step.id ? startedAt : undefined,
|
|
1481
|
+
resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : undefined,
|
|
1482
|
+
};
|
|
780
1483
|
}
|
|
781
1484
|
|
|
782
1485
|
if (suspended) {
|
|
783
|
-
execResults = {
|
|
1486
|
+
execResults = {
|
|
1487
|
+
status: 'suspended',
|
|
1488
|
+
suspendedPayload: suspended.payload,
|
|
1489
|
+
payload: prevOutput,
|
|
1490
|
+
suspendedAt: Date.now(),
|
|
1491
|
+
startedAt,
|
|
1492
|
+
resumedAt: resume?.steps[0] === step.id ? startedAt : undefined,
|
|
1493
|
+
resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : undefined,
|
|
1494
|
+
};
|
|
1495
|
+
} else if (bailed) {
|
|
1496
|
+
execResults = { status: 'bailed', output: bailed.payload, payload: prevOutput, endedAt: Date.now(), startedAt };
|
|
784
1497
|
}
|
|
785
1498
|
|
|
786
1499
|
if (execResults.status === 'failed') {
|
|
@@ -794,12 +1507,11 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
794
1507
|
payload: {
|
|
795
1508
|
currentStep: {
|
|
796
1509
|
id: step.id,
|
|
797
|
-
|
|
798
|
-
output: execResults.output,
|
|
1510
|
+
...execResults,
|
|
799
1511
|
},
|
|
800
1512
|
workflowState: {
|
|
801
1513
|
status: 'running',
|
|
802
|
-
steps: stepResults,
|
|
1514
|
+
steps: { ...stepResults, [step.id]: execResults },
|
|
803
1515
|
result: null,
|
|
804
1516
|
error: null,
|
|
805
1517
|
},
|
|
@@ -807,6 +1519,32 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
807
1519
|
eventTimestamp: Date.now(),
|
|
808
1520
|
});
|
|
809
1521
|
|
|
1522
|
+
if (execResults.status === 'suspended') {
|
|
1523
|
+
await emitter.emit('watch-v2', {
|
|
1524
|
+
type: 'step-suspended',
|
|
1525
|
+
payload: {
|
|
1526
|
+
id: step.id,
|
|
1527
|
+
...execResults,
|
|
1528
|
+
},
|
|
1529
|
+
});
|
|
1530
|
+
} else {
|
|
1531
|
+
await emitter.emit('watch-v2', {
|
|
1532
|
+
type: 'step-result',
|
|
1533
|
+
payload: {
|
|
1534
|
+
id: step.id,
|
|
1535
|
+
...execResults,
|
|
1536
|
+
},
|
|
1537
|
+
});
|
|
1538
|
+
|
|
1539
|
+
await emitter.emit('watch-v2', {
|
|
1540
|
+
type: 'step-finish',
|
|
1541
|
+
payload: {
|
|
1542
|
+
id: step.id,
|
|
1543
|
+
metadata: {},
|
|
1544
|
+
},
|
|
1545
|
+
});
|
|
1546
|
+
}
|
|
1547
|
+
|
|
810
1548
|
return { result: execResults, executionContext, stepResults };
|
|
811
1549
|
});
|
|
812
1550
|
|
|
@@ -825,12 +1563,18 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
825
1563
|
stepResults,
|
|
826
1564
|
executionContext,
|
|
827
1565
|
serializedStepGraph,
|
|
1566
|
+
workflowStatus,
|
|
1567
|
+
result,
|
|
1568
|
+
error,
|
|
828
1569
|
}: {
|
|
829
1570
|
workflowId: string;
|
|
830
1571
|
runId: string;
|
|
831
1572
|
stepResults: Record<string, StepResult<any, any, any, any>>;
|
|
832
1573
|
serializedStepGraph: SerializedStepFlowEntry[];
|
|
833
1574
|
executionContext: ExecutionContext;
|
|
1575
|
+
workflowStatus: 'success' | 'failed' | 'suspended' | 'running';
|
|
1576
|
+
result?: Record<string, any>;
|
|
1577
|
+
error?: string | Error;
|
|
834
1578
|
}) {
|
|
835
1579
|
await this.inngestStep.run(
|
|
836
1580
|
`workflow.${workflowId}.run.${runId}.path.${JSON.stringify(executionContext.executionPath)}.stepUpdate`,
|
|
@@ -845,6 +1589,9 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
845
1589
|
activePaths: [],
|
|
846
1590
|
suspendedPaths: executionContext.suspendedPaths,
|
|
847
1591
|
serializedStepGraph,
|
|
1592
|
+
status: workflowStatus,
|
|
1593
|
+
result,
|
|
1594
|
+
error,
|
|
848
1595
|
// @ts-ignore
|
|
849
1596
|
timestamp: Date.now(),
|
|
850
1597
|
},
|
|
@@ -864,11 +1611,16 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
864
1611
|
resume,
|
|
865
1612
|
executionContext,
|
|
866
1613
|
emitter,
|
|
1614
|
+
abortController,
|
|
867
1615
|
runtimeContext,
|
|
868
1616
|
}: {
|
|
869
1617
|
workflowId: string;
|
|
870
1618
|
runId: string;
|
|
871
|
-
entry: {
|
|
1619
|
+
entry: {
|
|
1620
|
+
type: 'conditional';
|
|
1621
|
+
steps: StepFlowEntry[];
|
|
1622
|
+
conditions: ExecuteFunction<any, any, any, any, InngestEngineType>[];
|
|
1623
|
+
};
|
|
872
1624
|
prevStep: StepFlowEntry;
|
|
873
1625
|
serializedStepGraph: SerializedStepFlowEntry[];
|
|
874
1626
|
prevOutput: any;
|
|
@@ -880,7 +1632,8 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
880
1632
|
resumePath: number[];
|
|
881
1633
|
};
|
|
882
1634
|
executionContext: ExecutionContext;
|
|
883
|
-
emitter:
|
|
1635
|
+
emitter: Emitter;
|
|
1636
|
+
abortController: AbortController;
|
|
884
1637
|
runtimeContext: RuntimeContext;
|
|
885
1638
|
}): Promise<StepResult<any, any, any, any>> {
|
|
886
1639
|
let execResults: any;
|
|
@@ -893,6 +1646,7 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
893
1646
|
runId,
|
|
894
1647
|
mastra: this.mastra!,
|
|
895
1648
|
runtimeContext,
|
|
1649
|
+
runCount: -1,
|
|
896
1650
|
inputData: prevOutput,
|
|
897
1651
|
getInitData: () => stepResults?.input as any,
|
|
898
1652
|
getStepResult: (step: any) => {
|
|
@@ -910,7 +1664,15 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
910
1664
|
|
|
911
1665
|
// TODO: this function shouldn't have suspend probably?
|
|
912
1666
|
suspend: async (_suspendPayload: any) => {},
|
|
1667
|
+
bail: () => {},
|
|
1668
|
+
abort: () => {
|
|
1669
|
+
abortController.abort();
|
|
1670
|
+
},
|
|
913
1671
|
[EMITTER_SYMBOL]: emitter,
|
|
1672
|
+
engine: {
|
|
1673
|
+
step: this.inngestStep,
|
|
1674
|
+
},
|
|
1675
|
+
abortSignal: abortController.signal,
|
|
914
1676
|
});
|
|
915
1677
|
return result ? index : null;
|
|
916
1678
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
@@ -923,7 +1685,7 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
923
1685
|
).filter((index: any): index is number => index !== null);
|
|
924
1686
|
|
|
925
1687
|
const stepsToRun = entry.steps.filter((_, index) => truthyIndexes.includes(index));
|
|
926
|
-
const results: StepResult<any, any, any, any>[] = await Promise.all(
|
|
1688
|
+
const results: { result: StepResult<any, any, any, any> }[] = await Promise.all(
|
|
927
1689
|
stepsToRun.map((step, index) =>
|
|
928
1690
|
this.executeEntry({
|
|
929
1691
|
workflowId,
|
|
@@ -942,21 +1704,24 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
942
1704
|
executionSpan: executionContext.executionSpan,
|
|
943
1705
|
},
|
|
944
1706
|
emitter,
|
|
1707
|
+
abortController,
|
|
945
1708
|
runtimeContext,
|
|
946
1709
|
}),
|
|
947
1710
|
),
|
|
948
1711
|
);
|
|
949
|
-
const hasFailed = results.find(result => result.status === 'failed')
|
|
950
|
-
|
|
1712
|
+
const hasFailed = results.find(result => result.result.status === 'failed') as {
|
|
1713
|
+
result: StepFailure<any, any, any>;
|
|
1714
|
+
};
|
|
1715
|
+
const hasSuspended = results.find(result => result.result.status === 'suspended');
|
|
951
1716
|
if (hasFailed) {
|
|
952
|
-
execResults = { status: 'failed', error: hasFailed.error };
|
|
1717
|
+
execResults = { status: 'failed', error: hasFailed.result.error };
|
|
953
1718
|
} else if (hasSuspended) {
|
|
954
|
-
execResults = { status: 'suspended', payload: hasSuspended.
|
|
1719
|
+
execResults = { status: 'suspended', payload: hasSuspended.result.suspendPayload };
|
|
955
1720
|
} else {
|
|
956
1721
|
execResults = {
|
|
957
1722
|
status: 'success',
|
|
958
1723
|
output: results.reduce((acc: Record<string, any>, result, index) => {
|
|
959
|
-
if (result.status === 'success') {
|
|
1724
|
+
if (result.result.status === 'success') {
|
|
960
1725
|
// @ts-ignore
|
|
961
1726
|
acc[stepsToRun[index]!.step.id] = result.output;
|
|
962
1727
|
}
|