@mastra/inngest 0.0.0-message-list-update-20250715150321 → 0.0.0-monorepo-binary-20251013210052

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/src/index.ts DELETED
@@ -1,1737 +0,0 @@
1
- import { randomUUID } from 'crypto';
2
- import type { ReadableStream } from 'node:stream/web';
3
- import { subscribe } from '@inngest/realtime';
4
- import type { Agent, Mastra, ToolExecutionContext, WorkflowRun, WorkflowRuns } from '@mastra/core';
5
- import { RuntimeContext } from '@mastra/core/di';
6
- import { Tool } from '@mastra/core/tools';
7
- import { Workflow, Run, DefaultExecutionEngine } from '@mastra/core/workflows';
8
- import type {
9
- ExecuteFunction,
10
- ExecutionContext,
11
- ExecutionEngine,
12
- ExecutionGraph,
13
- Step,
14
- WorkflowConfig,
15
- StepFlowEntry,
16
- StepResult,
17
- WorkflowResult,
18
- SerializedStepFlowEntry,
19
- StepFailure,
20
- Emitter,
21
- WatchEvent,
22
- StreamEvent,
23
- } from '@mastra/core/workflows';
24
- import { EMITTER_SYMBOL } from '@mastra/core/workflows/_constants';
25
- import type { Span } from '@opentelemetry/api';
26
- import type { Inngest, BaseContext } from 'inngest';
27
- import { serve as inngestServe } from 'inngest/hono';
28
- import { z } from 'zod';
29
-
30
- export type InngestEngineType = {
31
- step: any;
32
- };
33
-
34
- export function serve({ mastra, inngest }: { mastra: Mastra; inngest: Inngest }): ReturnType<typeof inngestServe> {
35
- const wfs = mastra.getWorkflows();
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
- );
47
- return inngestServe({
48
- client: inngest,
49
- functions,
50
- });
51
- }
52
-
53
- export class InngestRun<
54
- TEngineType = InngestEngineType,
55
- TSteps extends Step<string, any, any>[] = Step<string, any, any>[],
56
- TInput extends z.ZodType<any> = z.ZodType<any>,
57
- TOutput extends z.ZodType<any> = z.ZodType<any>,
58
- > extends Run<TEngineType, TSteps, TInput, TOutput> {
59
- private inngest: Inngest;
60
- serializedStepGraph: SerializedStepFlowEntry[];
61
- #mastra: Mastra;
62
-
63
- constructor(
64
- params: {
65
- workflowId: string;
66
- runId: string;
67
- executionEngine: ExecutionEngine;
68
- executionGraph: ExecutionGraph;
69
- serializedStepGraph: SerializedStepFlowEntry[];
70
- mastra?: Mastra;
71
- retryConfig?: {
72
- attempts?: number;
73
- delay?: number;
74
- };
75
- cleanup?: () => void;
76
- },
77
- inngest: Inngest,
78
- ) {
79
- super(params);
80
- this.inngest = inngest;
81
- this.serializedStepGraph = params.serializedStepGraph;
82
- this.#mastra = params.mastra!;
83
- }
84
-
85
- async getRuns(eventId: string) {
86
- const response = await fetch(`${this.inngest.apiBaseUrl ?? 'https://api.inngest.com'}/v1/events/${eventId}/runs`, {
87
- headers: {
88
- Authorization: `Bearer ${process.env.INNGEST_SIGNING_KEY}`,
89
- },
90
- });
91
- const json = await response.json();
92
- return (json as any).data;
93
- }
94
-
95
- async getRunOutput(eventId: string) {
96
- let runs = await this.getRuns(eventId);
97
-
98
- while (runs?.[0]?.status !== 'Completed' || runs?.[0]?.event_id !== eventId) {
99
- await new Promise(resolve => setTimeout(resolve, 1000));
100
- runs = await this.getRuns(eventId);
101
- if (runs?.[0]?.status === 'Failed') {
102
- console.log('run', runs?.[0]);
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' } } };
110
- }
111
- }
112
- return runs?.[0];
113
- }
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
-
146
- async start({
147
- inputData,
148
- }: {
149
- inputData?: z.infer<TInput>;
150
- runtimeContext?: RuntimeContext;
151
- }): Promise<WorkflowResult<TOutput, TSteps>> {
152
- await this.#mastra.getStorage()?.persistWorkflowSnapshot({
153
- workflowName: this.workflowId,
154
- runId: this.runId,
155
- snapshot: {
156
- runId: this.runId,
157
- serializedStepGraph: this.serializedStepGraph,
158
- value: {},
159
- context: {} as any,
160
- activePaths: [],
161
- suspendedPaths: {},
162
- timestamp: Date.now(),
163
- status: 'running',
164
- },
165
- });
166
-
167
- const eventOutput = await this.inngest.send({
168
- name: `workflow.${this.workflowId}`,
169
- data: {
170
- inputData,
171
- runId: this.runId,
172
- },
173
- });
174
-
175
- const eventId = eventOutput.ids[0];
176
- if (!eventId) {
177
- throw new Error('Event ID is not set');
178
- }
179
- const runOutput = await this.getRunOutput(eventId);
180
- const result = runOutput?.output?.result;
181
- if (result.status === 'failed') {
182
- result.error = new Error(result.error);
183
- }
184
-
185
- if (result.status !== 'suspended') {
186
- this.cleanup?.();
187
- }
188
- return result;
189
- }
190
-
191
- async resume<TResumeSchema extends z.ZodType<any>>(params: {
192
- resumeData?: z.infer<TResumeSchema>;
193
- step:
194
- | Step<string, any, any, TResumeSchema, any>
195
- | [...Step<string, any, any, any, any>[], Step<string, any, any, TResumeSchema, any>]
196
- | string
197
- | string[];
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;
220
- }): Promise<WorkflowResult<TOutput, TSteps>> {
221
- const steps: string[] = (Array.isArray(params.step) ? params.step : [params.step]).map(step =>
222
- typeof step === 'string' ? step : step?.id,
223
- );
224
- const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
225
- workflowName: this.workflowId,
226
- runId: this.runId,
227
- });
228
-
229
- const eventOutput = await this.inngest.send({
230
- name: `workflow.${this.workflowId}`,
231
- data: {
232
- inputData: params.resumeData,
233
- runId: this.runId,
234
- stepResults: snapshot?.context as any,
235
- resume: {
236
- steps,
237
- stepResults: snapshot?.context as any,
238
- resumePayload: params.resumeData,
239
- // @ts-ignore
240
- resumePath: snapshot?.suspendedPaths?.[steps?.[0]] as any,
241
- },
242
- },
243
- });
244
-
245
- const eventId = eventOutput.ids[0];
246
- if (!eventId) {
247
- throw new Error('Event ID is not set');
248
- }
249
- const runOutput = await this.getRunOutput(eventId);
250
- const result = runOutput?.output?.result;
251
- if (result.status === 'failed') {
252
- result.error = new Error(result.error);
253
- }
254
- return result;
255
- }
256
-
257
- watch(cb: (event: WatchEvent) => void, type: 'watch' | 'watch-v2' = 'watch'): () => void {
258
- let active = true;
259
- const streamPromise = subscribe(
260
- {
261
- channel: `workflow:${this.workflowId}:${this.runId}`,
262
- topics: [type],
263
- app: this.inngest,
264
- },
265
- (message: any) => {
266
- if (active) {
267
- cb(message.data);
268
- }
269
- },
270
- );
271
-
272
- return () => {
273
- active = false;
274
- streamPromise
275
- .then(async (stream: Awaited<typeof streamPromise>) => {
276
- return stream.cancel();
277
- })
278
- .catch(err => {
279
- console.error(err);
280
- });
281
- };
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
- }
323
- }
324
-
325
- export class InngestWorkflow<
326
- TEngineType = InngestEngineType,
327
- TSteps extends Step<string, any, any>[] = Step<string, any, any>[],
328
- TWorkflowId extends string = string,
329
- TInput extends z.ZodType<any> = z.ZodType<any>,
330
- TOutput extends z.ZodType<any> = z.ZodType<any>,
331
- TPrevSchema extends z.ZodType<any> = TInput,
332
- > extends Workflow<TEngineType, TSteps, TWorkflowId, TInput, TOutput, TPrevSchema> {
333
- #mastra: Mastra;
334
- public inngest: Inngest;
335
-
336
- private function: ReturnType<Inngest['createFunction']> | undefined;
337
-
338
- constructor(params: WorkflowConfig<TWorkflowId, TInput, TOutput, TSteps>, inngest: Inngest) {
339
- super(params);
340
- this.#mastra = params.mastra!;
341
- this.inngest = inngest;
342
- }
343
-
344
- async getWorkflowRuns(args?: {
345
- fromDate?: Date;
346
- toDate?: Date;
347
- limit?: number;
348
- offset?: number;
349
- resourceId?: string;
350
- }) {
351
- const storage = this.#mastra?.getStorage();
352
- if (!storage) {
353
- this.logger.debug('Cannot get workflow runs. Mastra engine is not initialized');
354
- return { runs: [], total: 0 };
355
- }
356
-
357
- return storage.getWorkflowRuns({ workflowName: this.id, ...(args ?? {}) }) as unknown as WorkflowRuns;
358
- }
359
-
360
- async getWorkflowRunById(runId: string): Promise<WorkflowRun | null> {
361
- const storage = this.#mastra?.getStorage();
362
- if (!storage) {
363
- this.logger.debug('Cannot get workflow runs. Mastra engine is not initialized');
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;
368
- }
369
- const run = (await storage.getWorkflowRunById({ runId, workflowName: this.id })) as unknown as WorkflowRun;
370
-
371
- return (
372
- run ??
373
- (this.runs.get(runId) ? ({ ...this.runs.get(runId), workflowName: this.id } as unknown as WorkflowRun) : null)
374
- );
375
- }
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
-
403
- __registerMastra(mastra: Mastra) {
404
- this.#mastra = mastra;
405
- this.executionEngine.__registerMastra(mastra);
406
- const updateNested = (step: StepFlowEntry) => {
407
- if (
408
- (step.type === 'step' || step.type === 'loop' || step.type === 'foreach') &&
409
- step.step instanceof InngestWorkflow
410
- ) {
411
- step.step.__registerMastra(mastra);
412
- } else if (step.type === 'parallel' || step.type === 'conditional') {
413
- for (const subStep of step.steps) {
414
- updateNested(subStep);
415
- }
416
- }
417
- };
418
-
419
- if (this.executionGraph.steps.length) {
420
- for (const step of this.executionGraph.steps) {
421
- updateNested(step);
422
- }
423
- }
424
- }
425
-
426
- createRun(options?: { runId?: string }): Run<TEngineType, TSteps, TInput, TOutput> {
427
- const runIdToUse = options?.runId || randomUUID();
428
-
429
- // Return a new Run instance with object parameters
430
- const run: Run<TEngineType, TSteps, TInput, TOutput> =
431
- this.runs.get(runIdToUse) ??
432
- new InngestRun(
433
- {
434
- workflowId: this.id,
435
- runId: runIdToUse,
436
- executionEngine: this.executionEngine,
437
- executionGraph: this.executionGraph,
438
- serializedStepGraph: this.serializedStepGraph,
439
- mastra: this.#mastra,
440
- retryConfig: this.retryConfig,
441
- cleanup: () => this.runs.delete(runIdToUse),
442
- },
443
- this.inngest,
444
- );
445
-
446
- this.runs.set(runIdToUse, run);
447
- return run;
448
- }
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
-
497
- getFunction() {
498
- if (this.function) {
499
- return this.function;
500
- }
501
- this.function = this.inngest.createFunction(
502
- {
503
- id: `workflow.${this.id}`,
504
- // @ts-ignore
505
- retries: this.retryConfig?.attempts ?? 0,
506
- cancelOn: [{ event: `cancel.workflow.${this.id}` }],
507
- },
508
- { event: `workflow.${this.id}` },
509
- async ({ event, step, attempt, publish }) => {
510
- let { inputData, runId, resume } = event.data;
511
-
512
- if (!runId) {
513
- runId = await step.run(`workflow.${this.id}.runIdGen`, async () => {
514
- return randomUUID();
515
- });
516
- }
517
-
518
- const emitter = {
519
- emit: async (event: string, data: any) => {
520
- if (!publish) {
521
- return;
522
- }
523
-
524
- try {
525
- await publish({
526
- channel: `workflow:${this.id}:${runId}`,
527
- topic: event,
528
- data,
529
- });
530
- } catch (err: any) {
531
- this.logger.error('Error emitting event: ' + (err?.stack ?? err?.message ?? err));
532
- }
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
- },
543
- };
544
-
545
- const engine = new InngestExecutionEngine(this.#mastra, step, attempt);
546
- const result = await engine.execute<z.infer<TInput>, WorkflowResult<TOutput, TSteps>>({
547
- workflowId: this.id,
548
- runId,
549
- graph: this.executionGraph,
550
- serializedStepGraph: this.serializedStepGraph,
551
- input: inputData,
552
- emitter,
553
- retryConfig: this.retryConfig,
554
- runtimeContext: new RuntimeContext(), // TODO
555
- resume,
556
- abortController: new AbortController(),
557
- });
558
-
559
- return { result, runId };
560
- },
561
- );
562
- return this.function;
563
- }
564
-
565
- getNestedFunctions(steps: StepFlowEntry[]): ReturnType<Inngest['createFunction']>[] {
566
- return steps.flatMap(step => {
567
- if (step.type === 'step' || step.type === 'loop' || step.type === 'foreach') {
568
- if (step.step instanceof InngestWorkflow) {
569
- return [step.step.getFunction(), ...step.step.getNestedFunctions(step.step.executionGraph.steps)];
570
- }
571
- return [];
572
- } else if (step.type === 'parallel' || step.type === 'conditional') {
573
- return this.getNestedFunctions(step.steps);
574
- }
575
-
576
- return [];
577
- });
578
- }
579
-
580
- getFunctions() {
581
- return [this.getFunction(), ...this.getNestedFunctions(this.executionGraph.steps)];
582
- }
583
- }
584
-
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>,
621
- >(
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
- };
685
-
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
- };
776
- }
777
-
778
- export function init(inngest: Inngest) {
779
- return {
780
- createWorkflow<
781
- TWorkflowId extends string = string,
782
- TInput extends z.ZodType<any> = z.ZodType<any>,
783
- TOutput extends z.ZodType<any> = z.ZodType<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
- >[],
792
- >(params: WorkflowConfig<TWorkflowId, TInput, TOutput, TSteps>) {
793
- return new InngestWorkflow<InngestEngineType, TSteps, TWorkflowId, TInput, TOutput, TInput>(params, inngest);
794
- },
795
- createStep,
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
- },
837
- };
838
- }
839
-
840
- export class InngestExecutionEngine extends DefaultExecutionEngine {
841
- private inngestStep: BaseContext<Inngest>['step'];
842
- private inngestAttempts: number;
843
-
844
- constructor(mastra: Mastra, inngestStep: BaseContext<Inngest>['step'], inngestAttempts: number = 0) {
845
- super({ mastra });
846
- this.inngestStep = inngestStep;
847
- this.inngestAttempts = inngestAttempts;
848
- }
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
-
886
- protected async fmtReturnValue<TOutput>(
887
- executionSpan: Span | undefined,
888
- emitter: Emitter,
889
- stepResults: Record<string, StepResult<any, any, any, any>>,
890
- lastOutput: StepResult<any, any, any, any>,
891
- error?: Error | string,
892
- ): Promise<TOutput> {
893
- const base: any = {
894
- status: lastOutput.status,
895
- steps: stepResults,
896
- };
897
- if (lastOutput.status === 'success') {
898
- await emitter.emit('watch', {
899
- type: 'watch',
900
- payload: {
901
- workflowState: {
902
- status: lastOutput.status,
903
- steps: stepResults,
904
- result: lastOutput.output,
905
- },
906
- },
907
- eventTimestamp: Date.now(),
908
- });
909
-
910
- base.result = lastOutput.output;
911
- } else if (lastOutput.status === 'failed') {
912
- base.error =
913
- error instanceof Error
914
- ? (error?.stack ?? error.message)
915
- : lastOutput?.error instanceof Error
916
- ? lastOutput.error.message
917
- : (lastOutput.error ?? error ?? 'Unknown error');
918
-
919
- await emitter.emit('watch', {
920
- type: 'watch',
921
- payload: {
922
- workflowState: {
923
- status: lastOutput.status,
924
- steps: stepResults,
925
- result: null,
926
- error: base.error,
927
- },
928
- },
929
- eventTimestamp: Date.now(),
930
- });
931
- } else if (lastOutput.status === 'suspended') {
932
- await emitter.emit('watch', {
933
- type: 'watch',
934
- payload: {
935
- workflowState: {
936
- status: lastOutput.status,
937
- steps: stepResults,
938
- result: null,
939
- error: null,
940
- },
941
- },
942
- eventTimestamp: Date.now(),
943
- });
944
-
945
- const suspendedStepIds = Object.entries(stepResults).flatMap(([stepId, stepResult]) => {
946
- if (stepResult?.status === 'suspended') {
947
- const nestedPath = stepResult?.payload?.__workflow_meta?.path;
948
- return nestedPath ? [[stepId, ...nestedPath]] : [[stepId]];
949
- }
950
-
951
- return [];
952
- });
953
- base.suspended = suspendedStepIds;
954
- }
955
-
956
- executionSpan?.end();
957
- return base as TOutput;
958
- }
959
-
960
- async superExecuteStep({
961
- workflowId,
962
- runId,
963
- step,
964
- stepResults,
965
- executionContext,
966
- resume,
967
- prevOutput,
968
- emitter,
969
- abortController,
970
- runtimeContext,
971
- }: {
972
- workflowId: string;
973
- runId: string;
974
- step: Step<string, any, any>;
975
- stepResults: Record<string, StepResult<any, any, any, any>>;
976
- executionContext: ExecutionContext;
977
- resume?: {
978
- steps: string[];
979
- resumePayload: any;
980
- };
981
- prevOutput: any;
982
- emitter: Emitter;
983
- abortController: AbortController;
984
- runtimeContext: RuntimeContext;
985
- }): Promise<StepResult<any, any, any, any>> {
986
- return super.executeStep({
987
- workflowId,
988
- runId,
989
- step,
990
- stepResults,
991
- executionContext,
992
- resume,
993
- prevOutput,
994
- emitter,
995
- abortController,
996
- runtimeContext,
997
- });
998
- }
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
-
1167
- async executeStep({
1168
- step,
1169
- stepResults,
1170
- executionContext,
1171
- resume,
1172
- prevOutput,
1173
- emitter,
1174
- abortController,
1175
- runtimeContext,
1176
- }: {
1177
- step: Step<string, any, any>;
1178
- stepResults: Record<string, StepResult<any, any, any, any>>;
1179
- executionContext: ExecutionContext;
1180
- resume?: {
1181
- steps: string[];
1182
- resumePayload: any;
1183
- runId?: string;
1184
- };
1185
- prevOutput: any;
1186
- emitter: Emitter;
1187
- abortController: AbortController;
1188
- runtimeContext: RuntimeContext;
1189
- }): Promise<StepResult<any, any, any, any>> {
1190
- const startedAt = await this.inngestStep.run(
1191
- `workflow.${executionContext.workflowId}.run.${executionContext.runId}.step.${step.id}.running_ev`,
1192
- async () => {
1193
- const startedAt = Date.now();
1194
- await emitter.emit('watch', {
1195
- type: 'watch',
1196
- payload: {
1197
- currentStep: {
1198
- id: step.id,
1199
- status: 'running',
1200
- },
1201
- workflowState: {
1202
- status: 'running',
1203
- steps: {
1204
- ...stepResults,
1205
- [step.id]: {
1206
- status: 'running',
1207
- },
1208
- },
1209
- result: null,
1210
- error: null,
1211
- },
1212
- },
1213
- eventTimestamp: Date.now(),
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;
1227
- },
1228
- );
1229
-
1230
- if (step instanceof InngestWorkflow) {
1231
- const isResume = !!resume?.steps?.length;
1232
- let result: WorkflowResult<any, any>;
1233
- let runId: string;
1234
- if (isResume) {
1235
- // @ts-ignore
1236
- runId = stepResults[resume?.steps?.[0]]?.payload?.__workflow_meta?.runId ?? randomUUID();
1237
-
1238
- const snapshot: any = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
1239
- workflowName: step.id,
1240
- runId: runId,
1241
- });
1242
-
1243
- const invokeResp = (await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
1244
- function: step.getFunction(),
1245
- data: {
1246
- inputData: prevOutput,
1247
- runId: runId,
1248
- resume: {
1249
- runId: runId,
1250
- steps: resume.steps.slice(1),
1251
- stepResults: snapshot?.context as any,
1252
- resumePayload: resume.resumePayload,
1253
- // @ts-ignore
1254
- resumePath: snapshot?.suspendedPaths?.[resume.steps?.[1]] as any,
1255
- },
1256
- },
1257
- })) as any;
1258
- result = invokeResp.result;
1259
- runId = invokeResp.runId;
1260
- } else {
1261
- const invokeResp = (await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
1262
- function: step.getFunction(),
1263
- data: {
1264
- inputData: prevOutput,
1265
- },
1266
- })) as any;
1267
- result = invokeResp.result;
1268
- runId = invokeResp.runId;
1269
- }
1270
-
1271
- const res = await this.inngestStep.run(
1272
- `workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
1273
- async () => {
1274
- if (result.status === 'failed') {
1275
- await emitter.emit('watch', {
1276
- type: 'watch',
1277
- payload: {
1278
- currentStep: {
1279
- id: step.id,
1280
- status: 'failed',
1281
- error: result?.error,
1282
- },
1283
- workflowState: {
1284
- status: 'running',
1285
- steps: stepResults,
1286
- result: null,
1287
- error: null,
1288
- },
1289
- },
1290
- eventTimestamp: Date.now(),
1291
- });
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
-
1303
- return { executionContext, result: { status: 'failed', error: result?.error } };
1304
- } else if (result.status === 'suspended') {
1305
- const suspendedSteps = Object.entries(result.steps).filter(([_stepName, stepResult]) => {
1306
- const stepRes: StepResult<any, any, any, any> = stepResult as StepResult<any, any, any, any>;
1307
- return stepRes?.status === 'suspended';
1308
- });
1309
-
1310
- for (const [stepName, stepResult] of suspendedSteps) {
1311
- // @ts-ignore
1312
- const suspendPath: string[] = [stepName, ...(stepResult?.payload?.__workflow_meta?.path ?? [])];
1313
- executionContext.suspendedPaths[step.id] = executionContext.executionPath;
1314
-
1315
- await emitter.emit('watch', {
1316
- type: 'watch',
1317
- payload: {
1318
- currentStep: {
1319
- id: step.id,
1320
- status: 'suspended',
1321
- payload: { ...(stepResult as any)?.payload, __workflow_meta: { runId: runId, path: suspendPath } },
1322
- },
1323
- workflowState: {
1324
- status: 'running',
1325
- steps: stepResults,
1326
- result: null,
1327
- error: null,
1328
- },
1329
- },
1330
- eventTimestamp: Date.now(),
1331
- });
1332
-
1333
- await emitter.emit('watch-v2', {
1334
- type: 'step-suspended',
1335
- payload: {
1336
- id: step.id,
1337
- status: 'suspended',
1338
- },
1339
- });
1340
-
1341
- return {
1342
- executionContext,
1343
- result: {
1344
- status: 'suspended',
1345
- payload: { ...(stepResult as any)?.payload, __workflow_meta: { runId: runId, path: suspendPath } },
1346
- },
1347
- };
1348
- }
1349
-
1350
- await emitter.emit('watch', {
1351
- type: 'watch',
1352
- payload: {
1353
- currentStep: {
1354
- id: step.id,
1355
- status: 'suspended',
1356
- payload: {},
1357
- },
1358
- workflowState: {
1359
- status: 'running',
1360
- steps: stepResults,
1361
- result: null,
1362
- error: null,
1363
- },
1364
- },
1365
- eventTimestamp: Date.now(),
1366
- });
1367
-
1368
- return {
1369
- executionContext,
1370
- result: {
1371
- status: 'suspended',
1372
- payload: {},
1373
- },
1374
- };
1375
- }
1376
-
1377
- // is success
1378
-
1379
- await emitter.emit('watch', {
1380
- type: 'watch',
1381
- payload: {
1382
- currentStep: {
1383
- id: step.id,
1384
- status: 'success',
1385
- output: result?.result,
1386
- },
1387
- workflowState: {
1388
- status: 'running',
1389
- steps: stepResults,
1390
- result: null,
1391
- error: null,
1392
- },
1393
- },
1394
- eventTimestamp: Date.now(),
1395
- });
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
-
1414
- return { executionContext, result: { status: 'success', output: result?.result } };
1415
- },
1416
- );
1417
-
1418
- Object.assign(executionContext, res.executionContext);
1419
- return res.result as StepResult<any, any, any, any>;
1420
- }
1421
-
1422
- const stepRes = await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}`, async () => {
1423
- let execResults: any;
1424
- let suspended: { payload: any } | undefined;
1425
- let bailed: { payload: any } | undefined;
1426
-
1427
- try {
1428
- const result = await step.execute({
1429
- runId: executionContext.runId,
1430
- mastra: this.mastra!,
1431
- runtimeContext,
1432
- inputData: prevOutput,
1433
- resumeData: resume?.steps[0] === step.id ? resume?.resumePayload : undefined,
1434
- getInitData: () => stepResults?.input as any,
1435
- getStepResult: (step: any) => {
1436
- const result = stepResults[step.id];
1437
- if (result?.status === 'success') {
1438
- return result.output;
1439
- }
1440
-
1441
- return null;
1442
- },
1443
- suspend: async (suspendPayload: any) => {
1444
- executionContext.suspendedPaths[step.id] = executionContext.executionPath;
1445
- suspended = { payload: suspendPayload };
1446
- },
1447
- bail: (result: any) => {
1448
- bailed = { payload: result };
1449
- },
1450
- resume: {
1451
- steps: resume?.steps?.slice(1) || [],
1452
- resumePayload: resume?.resumePayload,
1453
- // @ts-ignore
1454
- runId: stepResults[step.id]?.payload?.__workflow_meta?.runId,
1455
- },
1456
- [EMITTER_SYMBOL]: emitter,
1457
- engine: {
1458
- step: this.inngestStep,
1459
- },
1460
- abortSignal: abortController.signal,
1461
- });
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
- };
1473
- } catch (e) {
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
- };
1483
- }
1484
-
1485
- if (suspended) {
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 };
1497
- }
1498
-
1499
- if (execResults.status === 'failed') {
1500
- if (executionContext.retryConfig.attempts > 0 && this.inngestAttempts < executionContext.retryConfig.attempts) {
1501
- throw execResults.error;
1502
- }
1503
- }
1504
-
1505
- await emitter.emit('watch', {
1506
- type: 'watch',
1507
- payload: {
1508
- currentStep: {
1509
- id: step.id,
1510
- ...execResults,
1511
- },
1512
- workflowState: {
1513
- status: 'running',
1514
- steps: { ...stepResults, [step.id]: execResults },
1515
- result: null,
1516
- error: null,
1517
- },
1518
- },
1519
- eventTimestamp: Date.now(),
1520
- });
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
-
1548
- return { result: execResults, executionContext, stepResults };
1549
- });
1550
-
1551
- // @ts-ignore
1552
- Object.assign(executionContext.suspendedPaths, stepRes.executionContext.suspendedPaths);
1553
- // @ts-ignore
1554
- Object.assign(stepResults, stepRes.stepResults);
1555
-
1556
- // @ts-ignore
1557
- return stepRes.result;
1558
- }
1559
-
1560
- async persistStepUpdate({
1561
- workflowId,
1562
- runId,
1563
- stepResults,
1564
- executionContext,
1565
- serializedStepGraph,
1566
- workflowStatus,
1567
- result,
1568
- error,
1569
- }: {
1570
- workflowId: string;
1571
- runId: string;
1572
- stepResults: Record<string, StepResult<any, any, any, any>>;
1573
- serializedStepGraph: SerializedStepFlowEntry[];
1574
- executionContext: ExecutionContext;
1575
- workflowStatus: 'success' | 'failed' | 'suspended' | 'running';
1576
- result?: Record<string, any>;
1577
- error?: string | Error;
1578
- runtimeContext: RuntimeContext;
1579
- }) {
1580
- await this.inngestStep.run(
1581
- `workflow.${workflowId}.run.${runId}.path.${JSON.stringify(executionContext.executionPath)}.stepUpdate`,
1582
- async () => {
1583
- await this.mastra?.getStorage()?.persistWorkflowSnapshot({
1584
- workflowName: workflowId,
1585
- runId,
1586
- snapshot: {
1587
- runId,
1588
- value: {},
1589
- context: stepResults as any,
1590
- activePaths: [],
1591
- suspendedPaths: executionContext.suspendedPaths,
1592
- serializedStepGraph,
1593
- status: workflowStatus,
1594
- result,
1595
- error,
1596
- // @ts-ignore
1597
- timestamp: Date.now(),
1598
- },
1599
- });
1600
- },
1601
- );
1602
- }
1603
-
1604
- async executeConditional({
1605
- workflowId,
1606
- runId,
1607
- entry,
1608
- prevOutput,
1609
- prevStep,
1610
- stepResults,
1611
- serializedStepGraph,
1612
- resume,
1613
- executionContext,
1614
- emitter,
1615
- abortController,
1616
- runtimeContext,
1617
- }: {
1618
- workflowId: string;
1619
- runId: string;
1620
- entry: {
1621
- type: 'conditional';
1622
- steps: StepFlowEntry[];
1623
- conditions: ExecuteFunction<any, any, any, any, InngestEngineType>[];
1624
- };
1625
- prevStep: StepFlowEntry;
1626
- serializedStepGraph: SerializedStepFlowEntry[];
1627
- prevOutput: any;
1628
- stepResults: Record<string, StepResult<any, any, any, any>>;
1629
- resume?: {
1630
- steps: string[];
1631
- stepResults: Record<string, StepResult<any, any, any, any>>;
1632
- resumePayload: any;
1633
- resumePath: number[];
1634
- };
1635
- executionContext: ExecutionContext;
1636
- emitter: Emitter;
1637
- abortController: AbortController;
1638
- runtimeContext: RuntimeContext;
1639
- }): Promise<StepResult<any, any, any, any>> {
1640
- let execResults: any;
1641
- const truthyIndexes = (
1642
- await Promise.all(
1643
- entry.conditions.map((cond, index) =>
1644
- this.inngestStep.run(`workflow.${workflowId}.conditional.${index}`, async () => {
1645
- try {
1646
- const result = await cond({
1647
- runId,
1648
- mastra: this.mastra!,
1649
- runtimeContext,
1650
- runCount: -1,
1651
- inputData: prevOutput,
1652
- getInitData: () => stepResults?.input as any,
1653
- getStepResult: (step: any) => {
1654
- if (!step?.id) {
1655
- return null;
1656
- }
1657
-
1658
- const result = stepResults[step.id];
1659
- if (result?.status === 'success') {
1660
- return result.output;
1661
- }
1662
-
1663
- return null;
1664
- },
1665
-
1666
- // TODO: this function shouldn't have suspend probably?
1667
- suspend: async (_suspendPayload: any) => {},
1668
- bail: () => {},
1669
- abort: () => {
1670
- abortController.abort();
1671
- },
1672
- [EMITTER_SYMBOL]: emitter,
1673
- engine: {
1674
- step: this.inngestStep,
1675
- },
1676
- abortSignal: abortController.signal,
1677
- });
1678
- return result ? index : null;
1679
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1680
- } catch (e: unknown) {
1681
- return null;
1682
- }
1683
- }),
1684
- ),
1685
- )
1686
- ).filter((index: any): index is number => index !== null);
1687
-
1688
- const stepsToRun = entry.steps.filter((_, index) => truthyIndexes.includes(index));
1689
- const results: { result: StepResult<any, any, any, any> }[] = await Promise.all(
1690
- stepsToRun.map((step, index) =>
1691
- this.executeEntry({
1692
- workflowId,
1693
- runId,
1694
- entry: step,
1695
- prevStep,
1696
- stepResults,
1697
- resume,
1698
- serializedStepGraph,
1699
- executionContext: {
1700
- workflowId,
1701
- runId,
1702
- executionPath: [...executionContext.executionPath, index],
1703
- suspendedPaths: executionContext.suspendedPaths,
1704
- retryConfig: executionContext.retryConfig,
1705
- executionSpan: executionContext.executionSpan,
1706
- },
1707
- emitter,
1708
- abortController,
1709
- runtimeContext,
1710
- }),
1711
- ),
1712
- );
1713
- const hasFailed = results.find(result => result.result.status === 'failed') as {
1714
- result: StepFailure<any, any, any>;
1715
- };
1716
- const hasSuspended = results.find(result => result.result.status === 'suspended');
1717
- if (hasFailed) {
1718
- execResults = { status: 'failed', error: hasFailed.result.error };
1719
- } else if (hasSuspended) {
1720
- execResults = { status: 'suspended', payload: hasSuspended.result.suspendPayload };
1721
- } else {
1722
- execResults = {
1723
- status: 'success',
1724
- output: results.reduce((acc: Record<string, any>, result, index) => {
1725
- if (result.result.status === 'success') {
1726
- // @ts-ignore
1727
- acc[stepsToRun[index]!.step.id] = result.output;
1728
- }
1729
-
1730
- return acc;
1731
- }, {}),
1732
- };
1733
- }
1734
-
1735
- return execResults;
1736
- }
1737
- }