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