@mastra/inngest 0.0.0-vector-query-tool-provider-options-20250828222356 → 0.0.0-vector-extension-schema-20250922130418

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,1920 +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 } from '@mastra/core/agent';
5
- import { AISpanType, wrapMastra } from '@mastra/core/ai-tracing';
6
- import type { TracingContext, AnyAISpan } from '@mastra/core/ai-tracing';
7
- import { RuntimeContext } from '@mastra/core/di';
8
- import type { Mastra } from '@mastra/core/mastra';
9
- import type { WorkflowRun, WorkflowRuns } from '@mastra/core/storage';
10
- import type { ToolExecutionContext } from '@mastra/core/tools';
11
- import { Tool, ToolStream } from '@mastra/core/tools';
12
- import { Workflow, Run, DefaultExecutionEngine } from '@mastra/core/workflows';
13
- import type {
14
- ExecuteFunction,
15
- ExecutionContext,
16
- ExecutionEngine,
17
- ExecutionGraph,
18
- Step,
19
- WorkflowConfig,
20
- StepFlowEntry,
21
- StepResult,
22
- WorkflowResult,
23
- SerializedStepFlowEntry,
24
- StepFailure,
25
- Emitter,
26
- WatchEvent,
27
- StreamEvent,
28
- ChunkType,
29
- } from '@mastra/core/workflows';
30
- import { EMITTER_SYMBOL } from '@mastra/core/workflows/_constants';
31
- import type { Span } from '@opentelemetry/api';
32
- import type { Inngest, BaseContext } from 'inngest';
33
- import { serve as inngestServe } from 'inngest/hono';
34
- import { z } from 'zod';
35
-
36
- export type InngestEngineType = {
37
- step: any;
38
- };
39
-
40
- export function serve({ mastra, inngest }: { mastra: Mastra; inngest: Inngest }): ReturnType<typeof inngestServe> {
41
- const wfs = mastra.getWorkflows();
42
- const functions = Array.from(
43
- new Set(
44
- Object.values(wfs).flatMap(wf => {
45
- if (wf instanceof InngestWorkflow) {
46
- wf.__registerMastra(mastra);
47
- return wf.getFunctions();
48
- }
49
- return [];
50
- }),
51
- ),
52
- );
53
- return inngestServe({
54
- client: inngest,
55
- functions,
56
- });
57
- }
58
-
59
- export class InngestRun<
60
- TEngineType = InngestEngineType,
61
- TSteps extends Step<string, any, any>[] = Step<string, any, any>[],
62
- TInput extends z.ZodType<any> = z.ZodType<any>,
63
- TOutput extends z.ZodType<any> = z.ZodType<any>,
64
- > extends Run<TEngineType, TSteps, TInput, TOutput> {
65
- private inngest: Inngest;
66
- serializedStepGraph: SerializedStepFlowEntry[];
67
- #mastra: Mastra;
68
-
69
- constructor(
70
- params: {
71
- workflowId: string;
72
- runId: string;
73
- executionEngine: ExecutionEngine;
74
- executionGraph: ExecutionGraph;
75
- serializedStepGraph: SerializedStepFlowEntry[];
76
- mastra?: Mastra;
77
- retryConfig?: {
78
- attempts?: number;
79
- delay?: number;
80
- };
81
- cleanup?: () => void;
82
- },
83
- inngest: Inngest,
84
- ) {
85
- super(params);
86
- this.inngest = inngest;
87
- this.serializedStepGraph = params.serializedStepGraph;
88
- this.#mastra = params.mastra!;
89
- }
90
-
91
- async getRuns(eventId: string) {
92
- const response = await fetch(`${this.inngest.apiBaseUrl ?? 'https://api.inngest.com'}/v1/events/${eventId}/runs`, {
93
- headers: {
94
- Authorization: `Bearer ${process.env.INNGEST_SIGNING_KEY}`,
95
- },
96
- });
97
- const json = await response.json();
98
- return (json as any).data;
99
- }
100
-
101
- async getRunOutput(eventId: string) {
102
- let runs = await this.getRuns(eventId);
103
-
104
- while (runs?.[0]?.status !== 'Completed' || runs?.[0]?.event_id !== eventId) {
105
- await new Promise(resolve => setTimeout(resolve, 1000));
106
- runs = await this.getRuns(eventId);
107
- if (runs?.[0]?.status === 'Failed') {
108
- console.log('run', runs?.[0]);
109
- throw new Error(`Function run ${runs?.[0]?.status}`);
110
- } else if (runs?.[0]?.status === 'Cancelled') {
111
- const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
112
- workflowName: this.workflowId,
113
- runId: this.runId,
114
- });
115
- return { output: { result: { steps: snapshot?.context, status: 'canceled' } } };
116
- }
117
- }
118
- return runs?.[0];
119
- }
120
-
121
- async sendEvent(event: string, data: any) {
122
- await this.inngest.send({
123
- name: `user-event-${event}`,
124
- data,
125
- });
126
- }
127
-
128
- async cancel() {
129
- await this.inngest.send({
130
- name: `cancel.workflow.${this.workflowId}`,
131
- data: {
132
- runId: this.runId,
133
- },
134
- });
135
-
136
- const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
137
- workflowName: this.workflowId,
138
- runId: this.runId,
139
- });
140
- if (snapshot) {
141
- await this.#mastra?.storage?.persistWorkflowSnapshot({
142
- workflowName: this.workflowId,
143
- runId: this.runId,
144
- snapshot: {
145
- ...snapshot,
146
- status: 'canceled' as any,
147
- },
148
- });
149
- }
150
- }
151
-
152
- async start({
153
- inputData,
154
- }: {
155
- inputData?: z.infer<TInput>;
156
- runtimeContext?: RuntimeContext;
157
- }): Promise<WorkflowResult<TOutput, TSteps>> {
158
- await this.#mastra.getStorage()?.persistWorkflowSnapshot({
159
- workflowName: this.workflowId,
160
- runId: this.runId,
161
- snapshot: {
162
- runId: this.runId,
163
- serializedStepGraph: this.serializedStepGraph,
164
- value: {},
165
- context: {} as any,
166
- activePaths: [],
167
- suspendedPaths: {},
168
- waitingPaths: {},
169
- timestamp: Date.now(),
170
- status: 'running',
171
- },
172
- });
173
-
174
- const eventOutput = await this.inngest.send({
175
- name: `workflow.${this.workflowId}`,
176
- data: {
177
- inputData,
178
- runId: this.runId,
179
- },
180
- });
181
-
182
- const eventId = eventOutput.ids[0];
183
- if (!eventId) {
184
- throw new Error('Event ID is not set');
185
- }
186
- const runOutput = await this.getRunOutput(eventId);
187
- const result = runOutput?.output?.result;
188
- if (result.status === 'failed') {
189
- result.error = new Error(result.error);
190
- }
191
-
192
- if (result.status !== 'suspended') {
193
- this.cleanup?.();
194
- }
195
- return result;
196
- }
197
-
198
- async resume<TResumeSchema extends z.ZodType<any>>(params: {
199
- resumeData?: z.infer<TResumeSchema>;
200
- step:
201
- | Step<string, any, any, TResumeSchema, any>
202
- | [...Step<string, any, any, any, any>[], Step<string, any, any, TResumeSchema, any>]
203
- | string
204
- | string[];
205
- runtimeContext?: RuntimeContext;
206
- }): Promise<WorkflowResult<TOutput, TSteps>> {
207
- const p = this._resume(params).then(result => {
208
- if (result.status !== 'suspended') {
209
- this.closeStreamAction?.().catch(() => {});
210
- }
211
-
212
- return result;
213
- });
214
-
215
- this.executionResults = p;
216
- return p;
217
- }
218
-
219
- async _resume<TResumeSchema extends z.ZodType<any>>(params: {
220
- resumeData?: z.infer<TResumeSchema>;
221
- step:
222
- | Step<string, any, any, TResumeSchema, any>
223
- | [...Step<string, any, any, any, any>[], Step<string, any, any, TResumeSchema, any>]
224
- | string
225
- | string[];
226
- runtimeContext?: RuntimeContext;
227
- }): Promise<WorkflowResult<TOutput, TSteps>> {
228
- const steps: string[] = (Array.isArray(params.step) ? params.step : [params.step]).map(step =>
229
- typeof step === 'string' ? step : step?.id,
230
- );
231
- const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
232
- workflowName: this.workflowId,
233
- runId: this.runId,
234
- });
235
-
236
- const eventOutput = await this.inngest.send({
237
- name: `workflow.${this.workflowId}`,
238
- data: {
239
- inputData: params.resumeData,
240
- runId: this.runId,
241
- workflowId: this.workflowId,
242
- stepResults: snapshot?.context as any,
243
- resume: {
244
- steps,
245
- stepResults: snapshot?.context as any,
246
- resumePayload: params.resumeData,
247
- // @ts-ignore
248
- resumePath: snapshot?.suspendedPaths?.[steps?.[0]] as any,
249
- },
250
- },
251
- });
252
-
253
- const eventId = eventOutput.ids[0];
254
- if (!eventId) {
255
- throw new Error('Event ID is not set');
256
- }
257
- const runOutput = await this.getRunOutput(eventId);
258
- const result = runOutput?.output?.result;
259
- if (result.status === 'failed') {
260
- result.error = new Error(result.error);
261
- }
262
- return result;
263
- }
264
-
265
- watch(cb: (event: WatchEvent) => void, type: 'watch' | 'watch-v2' = 'watch'): () => void {
266
- let active = true;
267
- const streamPromise = subscribe(
268
- {
269
- channel: `workflow:${this.workflowId}:${this.runId}`,
270
- topics: [type],
271
- app: this.inngest,
272
- },
273
- (message: any) => {
274
- if (active) {
275
- cb(message.data);
276
- }
277
- },
278
- );
279
-
280
- return () => {
281
- active = false;
282
- streamPromise
283
- .then(async (stream: Awaited<typeof streamPromise>) => {
284
- return stream.cancel();
285
- })
286
- .catch(err => {
287
- console.error(err);
288
- });
289
- };
290
- }
291
-
292
- stream({ inputData, runtimeContext }: { inputData?: z.infer<TInput>; runtimeContext?: RuntimeContext } = {}): {
293
- stream: ReadableStream<StreamEvent>;
294
- getWorkflowState: () => Promise<WorkflowResult<TOutput, TSteps>>;
295
- } {
296
- const { readable, writable } = new TransformStream<StreamEvent, StreamEvent>();
297
-
298
- const writer = writable.getWriter();
299
- const unwatch = this.watch(async event => {
300
- try {
301
- // watch-v2 events are data stream events, so we need to cast them to the correct type
302
- await writer.write(event as any);
303
- } catch {}
304
- }, 'watch-v2');
305
-
306
- this.closeStreamAction = async () => {
307
- unwatch();
308
-
309
- try {
310
- await writer.close();
311
- } catch (err) {
312
- console.error('Error closing stream:', err);
313
- } finally {
314
- writer.releaseLock();
315
- }
316
- };
317
-
318
- this.executionResults = this.start({ inputData, runtimeContext }).then(result => {
319
- if (result.status !== 'suspended') {
320
- this.closeStreamAction?.().catch(() => {});
321
- }
322
-
323
- return result;
324
- });
325
-
326
- return {
327
- stream: readable as ReadableStream<StreamEvent>,
328
- getWorkflowState: () => this.executionResults!,
329
- };
330
- }
331
- }
332
-
333
- export class InngestWorkflow<
334
- TEngineType = InngestEngineType,
335
- TSteps extends Step<string, any, any>[] = Step<string, any, any>[],
336
- TWorkflowId extends string = string,
337
- TInput extends z.ZodType<any> = z.ZodType<any>,
338
- TOutput extends z.ZodType<any> = z.ZodType<any>,
339
- TPrevSchema extends z.ZodType<any> = TInput,
340
- > extends Workflow<TEngineType, TSteps, TWorkflowId, TInput, TOutput, TPrevSchema> {
341
- #mastra: Mastra;
342
- public inngest: Inngest;
343
-
344
- private function: ReturnType<Inngest['createFunction']> | undefined;
345
-
346
- constructor(params: WorkflowConfig<TWorkflowId, TInput, TOutput, TSteps>, inngest: Inngest) {
347
- super(params);
348
- this.#mastra = params.mastra!;
349
- this.inngest = inngest;
350
- }
351
-
352
- async getWorkflowRuns(args?: {
353
- fromDate?: Date;
354
- toDate?: Date;
355
- limit?: number;
356
- offset?: number;
357
- resourceId?: string;
358
- }) {
359
- const storage = this.#mastra?.getStorage();
360
- if (!storage) {
361
- this.logger.debug('Cannot get workflow runs. Mastra engine is not initialized');
362
- return { runs: [], total: 0 };
363
- }
364
-
365
- return storage.getWorkflowRuns({ workflowName: this.id, ...(args ?? {}) }) as unknown as WorkflowRuns;
366
- }
367
-
368
- async getWorkflowRunById(runId: string): Promise<WorkflowRun | null> {
369
- const storage = this.#mastra?.getStorage();
370
- if (!storage) {
371
- this.logger.debug('Cannot get workflow runs. Mastra engine is not initialized');
372
- //returning in memory run if no storage is initialized
373
- return this.runs.get(runId)
374
- ? ({ ...this.runs.get(runId), workflowName: this.id } as unknown as WorkflowRun)
375
- : null;
376
- }
377
- const run = (await storage.getWorkflowRunById({ runId, workflowName: this.id })) as unknown as WorkflowRun;
378
-
379
- return (
380
- run ??
381
- (this.runs.get(runId) ? ({ ...this.runs.get(runId), workflowName: this.id } as unknown as WorkflowRun) : null)
382
- );
383
- }
384
-
385
- async getWorkflowRunExecutionResult(runId: string): Promise<WatchEvent['payload']['workflowState'] | null> {
386
- const storage = this.#mastra?.getStorage();
387
- if (!storage) {
388
- this.logger.debug('Cannot get workflow run execution result. Mastra storage is not initialized');
389
- return null;
390
- }
391
-
392
- const run = await storage.getWorkflowRunById({ runId, workflowName: this.id });
393
-
394
- if (!run?.snapshot) {
395
- return null;
396
- }
397
-
398
- if (typeof run.snapshot === 'string') {
399
- return null;
400
- }
401
-
402
- return {
403
- status: run.snapshot.status,
404
- result: run.snapshot.result,
405
- error: run.snapshot.error,
406
- payload: run.snapshot.context?.input,
407
- steps: run.snapshot.context as any,
408
- };
409
- }
410
-
411
- __registerMastra(mastra: Mastra) {
412
- this.#mastra = mastra;
413
- this.executionEngine.__registerMastra(mastra);
414
- const updateNested = (step: StepFlowEntry) => {
415
- if (
416
- (step.type === 'step' || step.type === 'loop' || step.type === 'foreach') &&
417
- step.step instanceof InngestWorkflow
418
- ) {
419
- step.step.__registerMastra(mastra);
420
- } else if (step.type === 'parallel' || step.type === 'conditional') {
421
- for (const subStep of step.steps) {
422
- updateNested(subStep);
423
- }
424
- }
425
- };
426
-
427
- if (this.executionGraph.steps.length) {
428
- for (const step of this.executionGraph.steps) {
429
- updateNested(step);
430
- }
431
- }
432
- }
433
-
434
- createRun(options?: { runId?: string }): Run<TEngineType, TSteps, TInput, TOutput> {
435
- const runIdToUse = options?.runId || randomUUID();
436
-
437
- // Return a new Run instance with object parameters
438
- const run: Run<TEngineType, TSteps, TInput, TOutput> =
439
- this.runs.get(runIdToUse) ??
440
- new InngestRun(
441
- {
442
- workflowId: this.id,
443
- runId: runIdToUse,
444
- executionEngine: this.executionEngine,
445
- executionGraph: this.executionGraph,
446
- serializedStepGraph: this.serializedStepGraph,
447
- mastra: this.#mastra,
448
- retryConfig: this.retryConfig,
449
- cleanup: () => this.runs.delete(runIdToUse),
450
- },
451
- this.inngest,
452
- );
453
-
454
- this.runs.set(runIdToUse, run);
455
- return run;
456
- }
457
-
458
- async createRunAsync(options?: { runId?: string }): Promise<Run<TEngineType, TSteps, TInput, TOutput>> {
459
- const runIdToUse = options?.runId || randomUUID();
460
-
461
- // Return a new Run instance with object parameters
462
- const run: Run<TEngineType, TSteps, TInput, TOutput> =
463
- this.runs.get(runIdToUse) ??
464
- new InngestRun(
465
- {
466
- workflowId: this.id,
467
- runId: runIdToUse,
468
- executionEngine: this.executionEngine,
469
- executionGraph: this.executionGraph,
470
- serializedStepGraph: this.serializedStepGraph,
471
- mastra: this.#mastra,
472
- retryConfig: this.retryConfig,
473
- cleanup: () => this.runs.delete(runIdToUse),
474
- },
475
- this.inngest,
476
- );
477
-
478
- this.runs.set(runIdToUse, run);
479
-
480
- const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse);
481
-
482
- if (!workflowSnapshotInStorage) {
483
- await this.mastra?.getStorage()?.persistWorkflowSnapshot({
484
- workflowName: this.id,
485
- runId: runIdToUse,
486
- snapshot: {
487
- runId: runIdToUse,
488
- status: 'pending',
489
- value: {},
490
- context: {},
491
- activePaths: [],
492
- waitingPaths: {},
493
- serializedStepGraph: this.serializedStepGraph,
494
- suspendedPaths: {},
495
- result: undefined,
496
- error: undefined,
497
- // @ts-ignore
498
- timestamp: Date.now(),
499
- },
500
- });
501
- }
502
-
503
- return run;
504
- }
505
-
506
- getFunction() {
507
- if (this.function) {
508
- return this.function;
509
- }
510
- this.function = this.inngest.createFunction(
511
- {
512
- id: `workflow.${this.id}`,
513
- // @ts-ignore
514
- retries: this.retryConfig?.attempts ?? 0,
515
- cancelOn: [{ event: `cancel.workflow.${this.id}` }],
516
- },
517
- { event: `workflow.${this.id}` },
518
- async ({ event, step, attempt, publish }) => {
519
- let { inputData, runId, resume } = event.data;
520
-
521
- if (!runId) {
522
- runId = await step.run(`workflow.${this.id}.runIdGen`, async () => {
523
- return randomUUID();
524
- });
525
- }
526
-
527
- const emitter = {
528
- emit: async (event: string, data: any) => {
529
- if (!publish) {
530
- return;
531
- }
532
-
533
- try {
534
- await publish({
535
- channel: `workflow:${this.id}:${runId}`,
536
- topic: event,
537
- data,
538
- });
539
- } catch (err: any) {
540
- this.logger.error('Error emitting event: ' + (err?.stack ?? err?.message ?? err));
541
- }
542
- },
543
- on: (_event: string, _callback: (data: any) => void) => {
544
- // no-op
545
- },
546
- off: (_event: string, _callback: (data: any) => void) => {
547
- // no-op
548
- },
549
- once: (_event: string, _callback: (data: any) => void) => {
550
- // no-op
551
- },
552
- };
553
-
554
- const engine = new InngestExecutionEngine(this.#mastra, step, attempt);
555
- const result = await engine.execute<z.infer<TInput>, WorkflowResult<TOutput, TSteps>>({
556
- workflowId: this.id,
557
- runId,
558
- graph: this.executionGraph,
559
- serializedStepGraph: this.serializedStepGraph,
560
- input: inputData,
561
- emitter,
562
- retryConfig: this.retryConfig,
563
- runtimeContext: new RuntimeContext(), // TODO
564
- resume,
565
- abortController: new AbortController(),
566
- currentSpan: undefined, // TODO: Pass actual parent AI span from workflow execution context
567
- });
568
-
569
- return { result, runId };
570
- },
571
- );
572
- return this.function;
573
- }
574
-
575
- getNestedFunctions(steps: StepFlowEntry[]): ReturnType<Inngest['createFunction']>[] {
576
- return steps.flatMap(step => {
577
- if (step.type === 'step' || step.type === 'loop' || step.type === 'foreach') {
578
- if (step.step instanceof InngestWorkflow) {
579
- return [step.step.getFunction(), ...step.step.getNestedFunctions(step.step.executionGraph.steps)];
580
- }
581
- return [];
582
- } else if (step.type === 'parallel' || step.type === 'conditional') {
583
- return this.getNestedFunctions(step.steps);
584
- }
585
-
586
- return [];
587
- });
588
- }
589
-
590
- getFunctions() {
591
- return [this.getFunction(), ...this.getNestedFunctions(this.executionGraph.steps)];
592
- }
593
- }
594
-
595
- function isAgent(params: any): params is Agent<any, any, any> {
596
- return params?.component === 'AGENT';
597
- }
598
-
599
- function isTool(params: any): params is Tool<any, any, any> {
600
- return params instanceof Tool;
601
- }
602
-
603
- export function createStep<
604
- TStepId extends string,
605
- TStepInput extends z.ZodType<any>,
606
- TStepOutput extends z.ZodType<any>,
607
- TResumeSchema extends z.ZodType<any>,
608
- TSuspendSchema extends z.ZodType<any>,
609
- >(params: {
610
- id: TStepId;
611
- description?: string;
612
- inputSchema: TStepInput;
613
- outputSchema: TStepOutput;
614
- resumeSchema?: TResumeSchema;
615
- suspendSchema?: TSuspendSchema;
616
- execute: ExecuteFunction<
617
- z.infer<TStepInput>,
618
- z.infer<TStepOutput>,
619
- z.infer<TResumeSchema>,
620
- z.infer<TSuspendSchema>,
621
- InngestEngineType
622
- >;
623
- }): Step<TStepId, TStepInput, TStepOutput, TResumeSchema, TSuspendSchema, InngestEngineType>;
624
-
625
- export function createStep<
626
- TStepId extends string,
627
- TStepInput extends z.ZodObject<{ prompt: z.ZodString }>,
628
- TStepOutput extends z.ZodObject<{ text: z.ZodString }>,
629
- TResumeSchema extends z.ZodType<any>,
630
- TSuspendSchema extends z.ZodType<any>,
631
- >(
632
- agent: Agent<TStepId, any, any>,
633
- ): Step<TStepId, TStepInput, TStepOutput, TResumeSchema, TSuspendSchema, InngestEngineType>;
634
-
635
- export function createStep<
636
- TSchemaIn extends z.ZodType<any>,
637
- TSchemaOut extends z.ZodType<any>,
638
- TContext extends ToolExecutionContext<TSchemaIn>,
639
- >(
640
- tool: Tool<TSchemaIn, TSchemaOut, TContext> & {
641
- inputSchema: TSchemaIn;
642
- outputSchema: TSchemaOut;
643
- execute: (context: TContext) => Promise<any>;
644
- },
645
- ): Step<string, TSchemaIn, TSchemaOut, z.ZodType<any>, z.ZodType<any>, InngestEngineType>;
646
- export function createStep<
647
- TStepId extends string,
648
- TStepInput extends z.ZodType<any>,
649
- TStepOutput extends z.ZodType<any>,
650
- TResumeSchema extends z.ZodType<any>,
651
- TSuspendSchema extends z.ZodType<any>,
652
- >(
653
- params:
654
- | {
655
- id: TStepId;
656
- description?: string;
657
- inputSchema: TStepInput;
658
- outputSchema: TStepOutput;
659
- resumeSchema?: TResumeSchema;
660
- suspendSchema?: TSuspendSchema;
661
- execute: ExecuteFunction<
662
- z.infer<TStepInput>,
663
- z.infer<TStepOutput>,
664
- z.infer<TResumeSchema>,
665
- z.infer<TSuspendSchema>,
666
- InngestEngineType
667
- >;
668
- }
669
- | Agent<any, any, any>
670
- | (Tool<TStepInput, TStepOutput, any> & {
671
- inputSchema: TStepInput;
672
- outputSchema: TStepOutput;
673
- execute: (context: ToolExecutionContext<TStepInput>) => Promise<any>;
674
- }),
675
- ): Step<TStepId, TStepInput, TStepOutput, TResumeSchema, TSuspendSchema, InngestEngineType> {
676
- if (isAgent(params)) {
677
- return {
678
- id: params.name,
679
- // @ts-ignore
680
- inputSchema: z.object({
681
- prompt: z.string(),
682
- // resourceId: z.string().optional(),
683
- // threadId: z.string().optional(),
684
- }),
685
- // @ts-ignore
686
- outputSchema: z.object({
687
- text: z.string(),
688
- }),
689
- execute: async ({ inputData, [EMITTER_SYMBOL]: emitter, runtimeContext, abortSignal, abort, tracingContext }) => {
690
- let streamPromise = {} as {
691
- promise: Promise<string>;
692
- resolve: (value: string) => void;
693
- reject: (reason?: any) => void;
694
- };
695
-
696
- streamPromise.promise = new Promise((resolve, reject) => {
697
- streamPromise.resolve = resolve;
698
- streamPromise.reject = reject;
699
- });
700
- const toolData = {
701
- name: params.name,
702
- args: inputData,
703
- };
704
- await emitter.emit('watch-v2', {
705
- type: 'tool-call-streaming-start',
706
- ...toolData,
707
- });
708
- const { fullStream } = await params.stream(inputData.prompt, {
709
- // resourceId: inputData.resourceId,
710
- // threadId: inputData.threadId,
711
- runtimeContext,
712
- tracingContext,
713
- onFinish: result => {
714
- streamPromise.resolve(result.text);
715
- },
716
- abortSignal,
717
- });
718
-
719
- if (abortSignal.aborted) {
720
- return abort();
721
- }
722
-
723
- for await (const chunk of fullStream) {
724
- switch (chunk.type) {
725
- case 'text-delta':
726
- await emitter.emit('watch-v2', {
727
- type: 'tool-call-delta',
728
- ...toolData,
729
- argsTextDelta: chunk.textDelta,
730
- });
731
- break;
732
-
733
- case 'step-start':
734
- case 'step-finish':
735
- case 'finish':
736
- break;
737
-
738
- case 'tool-call':
739
- case 'tool-result':
740
- case 'tool-call-streaming-start':
741
- case 'tool-call-delta':
742
- case 'source':
743
- case 'file':
744
- default:
745
- await emitter.emit('watch-v2', chunk);
746
- break;
747
- }
748
- }
749
-
750
- return {
751
- text: await streamPromise.promise,
752
- };
753
- },
754
- };
755
- }
756
-
757
- if (isTool(params)) {
758
- if (!params.inputSchema || !params.outputSchema) {
759
- throw new Error('Tool must have input and output schemas defined');
760
- }
761
-
762
- return {
763
- // TODO: tool probably should have strong id type
764
- // @ts-ignore
765
- id: params.id,
766
- inputSchema: params.inputSchema,
767
- outputSchema: params.outputSchema,
768
- execute: async ({ inputData, mastra, runtimeContext, tracingContext }) => {
769
- return params.execute({
770
- context: inputData,
771
- mastra: wrapMastra(mastra, tracingContext),
772
- runtimeContext,
773
- tracingContext,
774
- });
775
- },
776
- };
777
- }
778
-
779
- return {
780
- id: params.id,
781
- description: params.description,
782
- inputSchema: params.inputSchema,
783
- outputSchema: params.outputSchema,
784
- resumeSchema: params.resumeSchema,
785
- suspendSchema: params.suspendSchema,
786
- execute: params.execute,
787
- };
788
- }
789
-
790
- export function init(inngest: Inngest) {
791
- return {
792
- createWorkflow<
793
- TWorkflowId extends string = string,
794
- TInput extends z.ZodType<any> = z.ZodType<any>,
795
- TOutput extends z.ZodType<any> = z.ZodType<any>,
796
- TSteps extends Step<string, any, any, any, any, InngestEngineType>[] = Step<
797
- string,
798
- any,
799
- any,
800
- any,
801
- any,
802
- InngestEngineType
803
- >[],
804
- >(params: WorkflowConfig<TWorkflowId, TInput, TOutput, TSteps>) {
805
- return new InngestWorkflow<InngestEngineType, TSteps, TWorkflowId, TInput, TOutput, TInput>(params, inngest);
806
- },
807
- createStep,
808
- cloneStep<TStepId extends string>(
809
- step: Step<string, any, any, any, any, InngestEngineType>,
810
- opts: { id: TStepId },
811
- ): Step<TStepId, any, any, any, any, InngestEngineType> {
812
- return {
813
- id: opts.id,
814
- description: step.description,
815
- inputSchema: step.inputSchema,
816
- outputSchema: step.outputSchema,
817
- execute: step.execute,
818
- };
819
- },
820
- cloneWorkflow<
821
- TWorkflowId extends string = string,
822
- TInput extends z.ZodType<any> = z.ZodType<any>,
823
- TOutput extends z.ZodType<any> = z.ZodType<any>,
824
- TSteps extends Step<string, any, any, any, any, InngestEngineType>[] = Step<
825
- string,
826
- any,
827
- any,
828
- any,
829
- any,
830
- InngestEngineType
831
- >[],
832
- TPrevSchema extends z.ZodType<any> = TInput,
833
- >(
834
- workflow: Workflow<InngestEngineType, TSteps, string, TInput, TOutput, TPrevSchema>,
835
- opts: { id: TWorkflowId },
836
- ): Workflow<InngestEngineType, TSteps, TWorkflowId, TInput, TOutput, TPrevSchema> {
837
- const wf: Workflow<InngestEngineType, TSteps, TWorkflowId, TInput, TOutput, TPrevSchema> = new Workflow({
838
- id: opts.id,
839
- inputSchema: workflow.inputSchema,
840
- outputSchema: workflow.outputSchema,
841
- steps: workflow.stepDefs,
842
- mastra: workflow.mastra,
843
- });
844
-
845
- wf.setStepFlow(workflow.stepGraph);
846
- wf.commit();
847
- return wf;
848
- },
849
- };
850
- }
851
-
852
- export class InngestExecutionEngine extends DefaultExecutionEngine {
853
- private inngestStep: BaseContext<Inngest>['step'];
854
- private inngestAttempts: number;
855
-
856
- constructor(mastra: Mastra, inngestStep: BaseContext<Inngest>['step'], inngestAttempts: number = 0) {
857
- super({ mastra });
858
- this.inngestStep = inngestStep;
859
- this.inngestAttempts = inngestAttempts;
860
- }
861
-
862
- async execute<TInput, TOutput>(params: {
863
- workflowId: string;
864
- runId: string;
865
- graph: ExecutionGraph;
866
- serializedStepGraph: SerializedStepFlowEntry[];
867
- input?: TInput;
868
- resume?: {
869
- // TODO: add execute path
870
- steps: string[];
871
- stepResults: Record<string, StepResult<any, any, any, any>>;
872
- resumePayload: any;
873
- resumePath: number[];
874
- };
875
- emitter: Emitter;
876
- retryConfig?: {
877
- attempts?: number;
878
- delay?: number;
879
- };
880
- runtimeContext: RuntimeContext;
881
- abortController: AbortController;
882
- currentSpan?: AnyAISpan;
883
- }): Promise<TOutput> {
884
- await params.emitter.emit('watch-v2', {
885
- type: 'start',
886
- payload: { runId: params.runId },
887
- });
888
-
889
- const result = await super.execute<TInput, TOutput>(params);
890
-
891
- await params.emitter.emit('watch-v2', {
892
- type: 'finish',
893
- payload: { runId: params.runId },
894
- });
895
-
896
- return result;
897
- }
898
-
899
- protected async fmtReturnValue<TOutput>(
900
- executionSpan: Span | undefined,
901
- emitter: Emitter,
902
- stepResults: Record<string, StepResult<any, any, any, any>>,
903
- lastOutput: StepResult<any, any, any, any>,
904
- error?: Error | string,
905
- ): Promise<TOutput> {
906
- const base: any = {
907
- status: lastOutput.status,
908
- steps: stepResults,
909
- };
910
- if (lastOutput.status === 'success') {
911
- await emitter.emit('watch', {
912
- type: 'watch',
913
- payload: {
914
- workflowState: {
915
- status: lastOutput.status,
916
- steps: stepResults,
917
- result: lastOutput.output,
918
- },
919
- },
920
- eventTimestamp: Date.now(),
921
- });
922
-
923
- base.result = lastOutput.output;
924
- } else if (lastOutput.status === 'failed') {
925
- base.error =
926
- error instanceof Error
927
- ? (error?.stack ?? error.message)
928
- : lastOutput?.error instanceof Error
929
- ? lastOutput.error.message
930
- : (lastOutput.error ?? error ?? 'Unknown error');
931
-
932
- await emitter.emit('watch', {
933
- type: 'watch',
934
- payload: {
935
- workflowState: {
936
- status: lastOutput.status,
937
- steps: stepResults,
938
- result: null,
939
- error: base.error,
940
- },
941
- },
942
- eventTimestamp: Date.now(),
943
- });
944
- } else if (lastOutput.status === 'suspended') {
945
- await emitter.emit('watch', {
946
- type: 'watch',
947
- payload: {
948
- workflowState: {
949
- status: lastOutput.status,
950
- steps: stepResults,
951
- result: null,
952
- error: null,
953
- },
954
- },
955
- eventTimestamp: Date.now(),
956
- });
957
-
958
- const suspendedStepIds = Object.entries(stepResults).flatMap(([stepId, stepResult]) => {
959
- if (stepResult?.status === 'suspended') {
960
- const nestedPath = stepResult?.payload?.__workflow_meta?.path;
961
- return nestedPath ? [[stepId, ...nestedPath]] : [[stepId]];
962
- }
963
-
964
- return [];
965
- });
966
- base.suspended = suspendedStepIds;
967
- }
968
-
969
- executionSpan?.end();
970
- return base as TOutput;
971
- }
972
-
973
- // async executeSleep({ id, duration }: { id: string; duration: number }): Promise<void> {
974
- // await this.inngestStep.sleep(id, duration);
975
- // }
976
-
977
- async executeSleep({
978
- workflowId,
979
- runId,
980
- entry,
981
- prevOutput,
982
- stepResults,
983
- emitter,
984
- abortController,
985
- runtimeContext,
986
- writableStream,
987
- tracingContext,
988
- }: {
989
- workflowId: string;
990
- runId: string;
991
- serializedStepGraph: SerializedStepFlowEntry[];
992
- entry: {
993
- type: 'sleep';
994
- id: string;
995
- duration?: number;
996
- fn?: ExecuteFunction<any, any, any, any, InngestEngineType>;
997
- };
998
- prevStep: StepFlowEntry;
999
- prevOutput: any;
1000
- stepResults: Record<string, StepResult<any, any, any, any>>;
1001
- resume?: {
1002
- steps: string[];
1003
- stepResults: Record<string, StepResult<any, any, any, any>>;
1004
- resumePayload: any;
1005
- resumePath: number[];
1006
- };
1007
- executionContext: ExecutionContext;
1008
- emitter: Emitter;
1009
- abortController: AbortController;
1010
- runtimeContext: RuntimeContext;
1011
- writableStream?: WritableStream<ChunkType>;
1012
- tracingContext?: TracingContext;
1013
- }): Promise<void> {
1014
- let { duration, fn } = entry;
1015
-
1016
- const sleepSpan = tracingContext?.currentSpan?.createChildSpan({
1017
- type: AISpanType.WORKFLOW_SLEEP,
1018
- name: `sleep: ${duration ? `${duration}ms` : 'dynamic'}`,
1019
- attributes: {
1020
- durationMs: duration,
1021
- sleepType: fn ? 'dynamic' : 'fixed',
1022
- },
1023
- });
1024
-
1025
- if (fn) {
1026
- const stepCallId = randomUUID();
1027
- duration = await this.inngestStep.run(`workflow.${workflowId}.sleep.${entry.id}`, async () => {
1028
- return await fn({
1029
- runId,
1030
- workflowId,
1031
- mastra: this.mastra!,
1032
- runtimeContext,
1033
- inputData: prevOutput,
1034
- runCount: -1,
1035
- tracingContext: {
1036
- currentSpan: sleepSpan,
1037
- },
1038
- getInitData: () => stepResults?.input as any,
1039
- getStepResult: (step: any) => {
1040
- if (!step?.id) {
1041
- return null;
1042
- }
1043
-
1044
- const result = stepResults[step.id];
1045
- if (result?.status === 'success') {
1046
- return result.output;
1047
- }
1048
-
1049
- return null;
1050
- },
1051
-
1052
- // TODO: this function shouldn't have suspend probably?
1053
- suspend: async (_suspendPayload: any): Promise<any> => {},
1054
- bail: () => {},
1055
- abort: () => {
1056
- abortController?.abort();
1057
- },
1058
- [EMITTER_SYMBOL]: emitter,
1059
- engine: { step: this.inngestStep },
1060
- abortSignal: abortController?.signal,
1061
- writer: new ToolStream(
1062
- {
1063
- prefix: 'step',
1064
- callId: stepCallId,
1065
- name: 'sleep',
1066
- runId,
1067
- },
1068
- writableStream,
1069
- ),
1070
- });
1071
- });
1072
-
1073
- // Update sleep span with dynamic duration
1074
- sleepSpan?.update({
1075
- attributes: {
1076
- durationMs: duration,
1077
- },
1078
- });
1079
- }
1080
-
1081
- try {
1082
- await this.inngestStep.sleep(entry.id, !duration || duration < 0 ? 0 : duration);
1083
- sleepSpan?.end();
1084
- } catch (e) {
1085
- sleepSpan?.error({ error: e as Error });
1086
- throw e;
1087
- }
1088
- }
1089
-
1090
- async executeSleepUntil({
1091
- workflowId,
1092
- runId,
1093
- entry,
1094
- prevOutput,
1095
- stepResults,
1096
- emitter,
1097
- abortController,
1098
- runtimeContext,
1099
- writableStream,
1100
- tracingContext,
1101
- }: {
1102
- workflowId: string;
1103
- runId: string;
1104
- serializedStepGraph: SerializedStepFlowEntry[];
1105
- entry: {
1106
- type: 'sleepUntil';
1107
- id: string;
1108
- date?: Date;
1109
- fn?: ExecuteFunction<any, any, any, any, InngestEngineType>;
1110
- };
1111
- prevStep: StepFlowEntry;
1112
- prevOutput: any;
1113
- stepResults: Record<string, StepResult<any, any, any, any>>;
1114
- resume?: {
1115
- steps: string[];
1116
- stepResults: Record<string, StepResult<any, any, any, any>>;
1117
- resumePayload: any;
1118
- resumePath: number[];
1119
- };
1120
- executionContext: ExecutionContext;
1121
- emitter: Emitter;
1122
- abortController: AbortController;
1123
- runtimeContext: RuntimeContext;
1124
- writableStream?: WritableStream<ChunkType>;
1125
- tracingContext?: TracingContext;
1126
- }): Promise<void> {
1127
- let { date, fn } = entry;
1128
-
1129
- const sleepUntilSpan = tracingContext?.currentSpan?.createChildSpan({
1130
- type: AISpanType.WORKFLOW_SLEEP,
1131
- name: `sleepUntil: ${date ? date.toISOString() : 'dynamic'}`,
1132
- attributes: {
1133
- untilDate: date,
1134
- durationMs: date ? Math.max(0, date.getTime() - Date.now()) : undefined,
1135
- sleepType: fn ? 'dynamic' : 'fixed',
1136
- },
1137
- });
1138
-
1139
- if (fn) {
1140
- date = await this.inngestStep.run(`workflow.${workflowId}.sleepUntil.${entry.id}`, async () => {
1141
- const stepCallId = randomUUID();
1142
- return await fn({
1143
- runId,
1144
- workflowId,
1145
- mastra: this.mastra!,
1146
- runtimeContext,
1147
- inputData: prevOutput,
1148
- runCount: -1,
1149
- tracingContext: {
1150
- currentSpan: sleepUntilSpan,
1151
- },
1152
- getInitData: () => stepResults?.input as any,
1153
- getStepResult: (step: any) => {
1154
- if (!step?.id) {
1155
- return null;
1156
- }
1157
-
1158
- const result = stepResults[step.id];
1159
- if (result?.status === 'success') {
1160
- return result.output;
1161
- }
1162
-
1163
- return null;
1164
- },
1165
-
1166
- // TODO: this function shouldn't have suspend probably?
1167
- suspend: async (_suspendPayload: any): Promise<any> => {},
1168
- bail: () => {},
1169
- abort: () => {
1170
- abortController?.abort();
1171
- },
1172
- [EMITTER_SYMBOL]: emitter,
1173
- engine: { step: this.inngestStep },
1174
- abortSignal: abortController?.signal,
1175
- writer: new ToolStream(
1176
- {
1177
- prefix: 'step',
1178
- callId: stepCallId,
1179
- name: 'sleep',
1180
- runId,
1181
- },
1182
- writableStream,
1183
- ),
1184
- });
1185
- });
1186
-
1187
- // Update sleep until span with dynamic duration
1188
- const time = !date ? 0 : date.getTime() - Date.now();
1189
- sleepUntilSpan?.update({
1190
- attributes: {
1191
- durationMs: Math.max(0, time),
1192
- },
1193
- });
1194
- }
1195
-
1196
- if (!(date instanceof Date)) {
1197
- sleepUntilSpan?.end();
1198
- return;
1199
- }
1200
-
1201
- try {
1202
- await this.inngestStep.sleepUntil(entry.id, date);
1203
- sleepUntilSpan?.end();
1204
- } catch (e) {
1205
- sleepUntilSpan?.error({ error: e as Error });
1206
- throw e;
1207
- }
1208
- }
1209
-
1210
- async executeWaitForEvent({ event, timeout }: { event: string; timeout?: number }): Promise<any> {
1211
- const eventData = await this.inngestStep.waitForEvent(`user-event-${event}`, {
1212
- event: `user-event-${event}`,
1213
- timeout: timeout ?? 5e3,
1214
- });
1215
-
1216
- if (eventData === null) {
1217
- throw 'Timeout waiting for event';
1218
- }
1219
-
1220
- return eventData?.data;
1221
- }
1222
-
1223
- async executeStep({
1224
- step,
1225
- stepResults,
1226
- executionContext,
1227
- resume,
1228
- prevOutput,
1229
- emitter,
1230
- abortController,
1231
- runtimeContext,
1232
- writableStream,
1233
- disableScorers,
1234
- tracingContext,
1235
- }: {
1236
- step: Step<string, any, any>;
1237
- stepResults: Record<string, StepResult<any, any, any, any>>;
1238
- executionContext: ExecutionContext;
1239
- resume?: {
1240
- steps: string[];
1241
- resumePayload: any;
1242
- runId?: string;
1243
- };
1244
- prevOutput: any;
1245
- emitter: Emitter;
1246
- abortController: AbortController;
1247
- runtimeContext: RuntimeContext;
1248
- writableStream?: WritableStream<ChunkType>;
1249
- disableScorers?: boolean;
1250
- tracingContext?: TracingContext;
1251
- }): Promise<StepResult<any, any, any, any>> {
1252
- const stepAISpan = tracingContext?.currentSpan?.createChildSpan({
1253
- name: `workflow step: '${step.id}'`,
1254
- type: AISpanType.WORKFLOW_STEP,
1255
- input: prevOutput,
1256
- attributes: {
1257
- stepId: step.id,
1258
- },
1259
- });
1260
-
1261
- const startedAt = await this.inngestStep.run(
1262
- `workflow.${executionContext.workflowId}.run.${executionContext.runId}.step.${step.id}.running_ev`,
1263
- async () => {
1264
- const startedAt = Date.now();
1265
- await emitter.emit('watch', {
1266
- type: 'watch',
1267
- payload: {
1268
- currentStep: {
1269
- id: step.id,
1270
- status: 'running',
1271
- },
1272
- workflowState: {
1273
- status: 'running',
1274
- steps: {
1275
- ...stepResults,
1276
- [step.id]: {
1277
- status: 'running',
1278
- },
1279
- },
1280
- result: null,
1281
- error: null,
1282
- },
1283
- },
1284
- eventTimestamp: Date.now(),
1285
- });
1286
-
1287
- await emitter.emit('watch-v2', {
1288
- type: 'step-start',
1289
- payload: {
1290
- id: step.id,
1291
- status: 'running',
1292
- payload: prevOutput,
1293
- startedAt,
1294
- },
1295
- });
1296
-
1297
- return startedAt;
1298
- },
1299
- );
1300
-
1301
- if (step instanceof InngestWorkflow) {
1302
- const isResume = !!resume?.steps?.length;
1303
- let result: WorkflowResult<any, any>;
1304
- let runId: string;
1305
- if (isResume) {
1306
- // @ts-ignore
1307
- runId = stepResults[resume?.steps?.[0]]?.payload?.__workflow_meta?.runId ?? randomUUID();
1308
-
1309
- const snapshot: any = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
1310
- workflowName: step.id,
1311
- runId: runId,
1312
- });
1313
-
1314
- const invokeResp = (await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
1315
- function: step.getFunction(),
1316
- data: {
1317
- inputData: prevOutput,
1318
- runId: runId,
1319
- resume: {
1320
- runId: runId,
1321
- steps: resume.steps.slice(1),
1322
- stepResults: snapshot?.context as any,
1323
- resumePayload: resume.resumePayload,
1324
- // @ts-ignore
1325
- resumePath: snapshot?.suspendedPaths?.[resume.steps?.[1]] as any,
1326
- },
1327
- },
1328
- })) as any;
1329
- result = invokeResp.result;
1330
- runId = invokeResp.runId;
1331
- } else {
1332
- const invokeResp = (await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
1333
- function: step.getFunction(),
1334
- data: {
1335
- inputData: prevOutput,
1336
- },
1337
- })) as any;
1338
- result = invokeResp.result;
1339
- runId = invokeResp.runId;
1340
- }
1341
-
1342
- const res = await this.inngestStep.run(
1343
- `workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
1344
- async () => {
1345
- if (result.status === 'failed') {
1346
- await emitter.emit('watch', {
1347
- type: 'watch',
1348
- payload: {
1349
- currentStep: {
1350
- id: step.id,
1351
- status: 'failed',
1352
- error: result?.error,
1353
- },
1354
- workflowState: {
1355
- status: 'running',
1356
- steps: stepResults,
1357
- result: null,
1358
- error: null,
1359
- },
1360
- },
1361
- eventTimestamp: Date.now(),
1362
- });
1363
-
1364
- await emitter.emit('watch-v2', {
1365
- type: 'step-result',
1366
- payload: {
1367
- id: step.id,
1368
- status: 'failed',
1369
- error: result?.error,
1370
- payload: prevOutput,
1371
- },
1372
- });
1373
-
1374
- return { executionContext, result: { status: 'failed', error: result?.error } };
1375
- } else if (result.status === 'suspended') {
1376
- const suspendedSteps = Object.entries(result.steps).filter(([_stepName, stepResult]) => {
1377
- const stepRes: StepResult<any, any, any, any> = stepResult as StepResult<any, any, any, any>;
1378
- return stepRes?.status === 'suspended';
1379
- });
1380
-
1381
- for (const [stepName, stepResult] of suspendedSteps) {
1382
- // @ts-ignore
1383
- const suspendPath: string[] = [stepName, ...(stepResult?.payload?.__workflow_meta?.path ?? [])];
1384
- executionContext.suspendedPaths[step.id] = executionContext.executionPath;
1385
-
1386
- await emitter.emit('watch', {
1387
- type: 'watch',
1388
- payload: {
1389
- currentStep: {
1390
- id: step.id,
1391
- status: 'suspended',
1392
- payload: { ...(stepResult as any)?.payload, __workflow_meta: { runId: runId, path: suspendPath } },
1393
- },
1394
- workflowState: {
1395
- status: 'running',
1396
- steps: stepResults,
1397
- result: null,
1398
- error: null,
1399
- },
1400
- },
1401
- eventTimestamp: Date.now(),
1402
- });
1403
-
1404
- await emitter.emit('watch-v2', {
1405
- type: 'step-suspended',
1406
- payload: {
1407
- id: step.id,
1408
- status: 'suspended',
1409
- },
1410
- });
1411
-
1412
- return {
1413
- executionContext,
1414
- result: {
1415
- status: 'suspended',
1416
- payload: { ...(stepResult as any)?.payload, __workflow_meta: { runId: runId, path: suspendPath } },
1417
- },
1418
- };
1419
- }
1420
-
1421
- await emitter.emit('watch', {
1422
- type: 'watch',
1423
- payload: {
1424
- currentStep: {
1425
- id: step.id,
1426
- status: 'suspended',
1427
- payload: {},
1428
- },
1429
- workflowState: {
1430
- status: 'running',
1431
- steps: stepResults,
1432
- result: null,
1433
- error: null,
1434
- },
1435
- },
1436
- eventTimestamp: Date.now(),
1437
- });
1438
-
1439
- return {
1440
- executionContext,
1441
- result: {
1442
- status: 'suspended',
1443
- payload: {},
1444
- },
1445
- };
1446
- }
1447
-
1448
- // is success
1449
-
1450
- await emitter.emit('watch', {
1451
- type: 'watch',
1452
- payload: {
1453
- currentStep: {
1454
- id: step.id,
1455
- status: 'success',
1456
- output: result?.result,
1457
- },
1458
- workflowState: {
1459
- status: 'running',
1460
- steps: stepResults,
1461
- result: null,
1462
- error: null,
1463
- },
1464
- },
1465
- eventTimestamp: Date.now(),
1466
- });
1467
-
1468
- await emitter.emit('watch-v2', {
1469
- type: 'step-result',
1470
- payload: {
1471
- id: step.id,
1472
- status: 'success',
1473
- output: result?.result,
1474
- },
1475
- });
1476
-
1477
- await emitter.emit('watch-v2', {
1478
- type: 'step-finish',
1479
- payload: {
1480
- id: step.id,
1481
- metadata: {},
1482
- },
1483
- });
1484
-
1485
- return { executionContext, result: { status: 'success', output: result?.result } };
1486
- },
1487
- );
1488
-
1489
- Object.assign(executionContext, res.executionContext);
1490
- return res.result as StepResult<any, any, any, any>;
1491
- }
1492
-
1493
- const stepRes = await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}`, async () => {
1494
- let execResults: {
1495
- status: 'success' | 'failed' | 'suspended' | 'bailed';
1496
- output?: any;
1497
- startedAt: number;
1498
- endedAt?: number;
1499
- payload: any;
1500
- error?: string;
1501
- resumedAt?: number;
1502
- resumePayload?: any;
1503
- suspendedPayload?: any;
1504
- suspendedAt?: number;
1505
- };
1506
- let suspended: { payload: any } | undefined;
1507
- let bailed: { payload: any } | undefined;
1508
-
1509
- try {
1510
- const result = await step.execute({
1511
- runId: executionContext.runId,
1512
- mastra: this.mastra!,
1513
- runtimeContext,
1514
- writableStream,
1515
- inputData: prevOutput,
1516
- resumeData: resume?.steps[0] === step.id ? resume?.resumePayload : undefined,
1517
- tracingContext: {
1518
- currentSpan: stepAISpan,
1519
- },
1520
- getInitData: () => stepResults?.input as any,
1521
- getStepResult: (step: any) => {
1522
- const result = stepResults[step.id];
1523
- if (result?.status === 'success') {
1524
- return result.output;
1525
- }
1526
-
1527
- return null;
1528
- },
1529
- suspend: async (suspendPayload: any) => {
1530
- executionContext.suspendedPaths[step.id] = executionContext.executionPath;
1531
- suspended = { payload: suspendPayload };
1532
- },
1533
- bail: (result: any) => {
1534
- bailed = { payload: result };
1535
- },
1536
- resume: {
1537
- steps: resume?.steps?.slice(1) || [],
1538
- resumePayload: resume?.resumePayload,
1539
- // @ts-ignore
1540
- runId: stepResults[step.id]?.payload?.__workflow_meta?.runId,
1541
- },
1542
- [EMITTER_SYMBOL]: emitter,
1543
- engine: {
1544
- step: this.inngestStep,
1545
- },
1546
- abortSignal: abortController.signal,
1547
- });
1548
- const endedAt = Date.now();
1549
-
1550
- execResults = {
1551
- status: 'success',
1552
- output: result,
1553
- startedAt,
1554
- endedAt,
1555
- payload: prevOutput,
1556
- resumedAt: resume?.steps[0] === step.id ? startedAt : undefined,
1557
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : undefined,
1558
- };
1559
- } catch (e) {
1560
- execResults = {
1561
- status: 'failed',
1562
- payload: prevOutput,
1563
- error: e instanceof Error ? e.message : String(e),
1564
- endedAt: Date.now(),
1565
- startedAt,
1566
- resumedAt: resume?.steps[0] === step.id ? startedAt : undefined,
1567
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : undefined,
1568
- };
1569
- }
1570
-
1571
- if (suspended) {
1572
- execResults = {
1573
- status: 'suspended',
1574
- suspendedPayload: suspended.payload,
1575
- payload: prevOutput,
1576
- suspendedAt: Date.now(),
1577
- startedAt,
1578
- resumedAt: resume?.steps[0] === step.id ? startedAt : undefined,
1579
- resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : undefined,
1580
- };
1581
- } else if (bailed) {
1582
- execResults = { status: 'bailed', output: bailed.payload, payload: prevOutput, endedAt: Date.now(), startedAt };
1583
- }
1584
-
1585
- if (execResults.status === 'failed') {
1586
- if (executionContext.retryConfig.attempts > 0 && this.inngestAttempts < executionContext.retryConfig.attempts) {
1587
- const error = new Error(execResults.error);
1588
- stepAISpan?.error({ error });
1589
- throw error;
1590
- }
1591
- }
1592
-
1593
- await emitter.emit('watch', {
1594
- type: 'watch',
1595
- payload: {
1596
- currentStep: {
1597
- id: step.id,
1598
- ...execResults,
1599
- },
1600
- workflowState: {
1601
- status: 'running',
1602
- steps: { ...stepResults, [step.id]: execResults },
1603
- result: null,
1604
- error: null,
1605
- },
1606
- },
1607
- eventTimestamp: Date.now(),
1608
- });
1609
-
1610
- if (execResults.status === 'suspended') {
1611
- await emitter.emit('watch-v2', {
1612
- type: 'step-suspended',
1613
- payload: {
1614
- id: step.id,
1615
- ...execResults,
1616
- },
1617
- });
1618
- } else {
1619
- await emitter.emit('watch-v2', {
1620
- type: 'step-result',
1621
- payload: {
1622
- id: step.id,
1623
- ...execResults,
1624
- },
1625
- });
1626
-
1627
- await emitter.emit('watch-v2', {
1628
- type: 'step-finish',
1629
- payload: {
1630
- id: step.id,
1631
- metadata: {},
1632
- },
1633
- });
1634
- }
1635
-
1636
- stepAISpan?.end({ output: execResults });
1637
-
1638
- return { result: execResults, executionContext, stepResults };
1639
- });
1640
-
1641
- if (disableScorers !== false) {
1642
- await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}.score`, async () => {
1643
- if (step.scorers) {
1644
- await this.runScorers({
1645
- scorers: step.scorers,
1646
- runId: executionContext.runId,
1647
- input: prevOutput,
1648
- output: stepRes.result,
1649
- workflowId: executionContext.workflowId,
1650
- stepId: step.id,
1651
- runtimeContext,
1652
- disableScorers,
1653
- });
1654
- }
1655
- });
1656
- }
1657
-
1658
- // @ts-ignore
1659
- Object.assign(executionContext.suspendedPaths, stepRes.executionContext.suspendedPaths);
1660
- // @ts-ignore
1661
- Object.assign(stepResults, stepRes.stepResults);
1662
-
1663
- // @ts-ignore
1664
- return stepRes.result;
1665
- }
1666
-
1667
- async persistStepUpdate({
1668
- workflowId,
1669
- runId,
1670
- stepResults,
1671
- executionContext,
1672
- serializedStepGraph,
1673
- workflowStatus,
1674
- result,
1675
- error,
1676
- }: {
1677
- workflowId: string;
1678
- runId: string;
1679
- stepResults: Record<string, StepResult<any, any, any, any>>;
1680
- serializedStepGraph: SerializedStepFlowEntry[];
1681
- executionContext: ExecutionContext;
1682
- workflowStatus: 'success' | 'failed' | 'suspended' | 'running';
1683
- result?: Record<string, any>;
1684
- error?: string | Error;
1685
- runtimeContext: RuntimeContext;
1686
- }) {
1687
- await this.inngestStep.run(
1688
- `workflow.${workflowId}.run.${runId}.path.${JSON.stringify(executionContext.executionPath)}.stepUpdate`,
1689
- async () => {
1690
- await this.mastra?.getStorage()?.persistWorkflowSnapshot({
1691
- workflowName: workflowId,
1692
- runId,
1693
- snapshot: {
1694
- runId,
1695
- value: {},
1696
- context: stepResults as any,
1697
- activePaths: [],
1698
- suspendedPaths: executionContext.suspendedPaths,
1699
- waitingPaths: {},
1700
- serializedStepGraph,
1701
- status: workflowStatus,
1702
- result,
1703
- error,
1704
- // @ts-ignore
1705
- timestamp: Date.now(),
1706
- },
1707
- });
1708
- },
1709
- );
1710
- }
1711
-
1712
- async executeConditional({
1713
- workflowId,
1714
- runId,
1715
- entry,
1716
- prevOutput,
1717
- prevStep,
1718
- stepResults,
1719
- serializedStepGraph,
1720
- resume,
1721
- executionContext,
1722
- emitter,
1723
- abortController,
1724
- runtimeContext,
1725
- writableStream,
1726
- disableScorers,
1727
- tracingContext,
1728
- }: {
1729
- workflowId: string;
1730
- runId: string;
1731
- entry: {
1732
- type: 'conditional';
1733
- steps: StepFlowEntry[];
1734
- conditions: ExecuteFunction<any, any, any, any, InngestEngineType>[];
1735
- };
1736
- prevStep: StepFlowEntry;
1737
- serializedStepGraph: SerializedStepFlowEntry[];
1738
- prevOutput: any;
1739
- stepResults: Record<string, StepResult<any, any, any, any>>;
1740
- resume?: {
1741
- steps: string[];
1742
- stepResults: Record<string, StepResult<any, any, any, any>>;
1743
- resumePayload: any;
1744
- resumePath: number[];
1745
- };
1746
- executionContext: ExecutionContext;
1747
- emitter: Emitter;
1748
- abortController: AbortController;
1749
- runtimeContext: RuntimeContext;
1750
- writableStream?: WritableStream<ChunkType>;
1751
- disableScorers?: boolean;
1752
- tracingContext?: TracingContext;
1753
- }): Promise<StepResult<any, any, any, any>> {
1754
- const conditionalSpan = tracingContext?.currentSpan?.createChildSpan({
1755
- type: AISpanType.WORKFLOW_CONDITIONAL,
1756
- name: `conditional: ${entry.conditions.length} conditions`,
1757
- input: prevOutput,
1758
- attributes: {
1759
- conditionCount: entry.conditions.length,
1760
- },
1761
- });
1762
-
1763
- let execResults: any;
1764
- const truthyIndexes = (
1765
- await Promise.all(
1766
- entry.conditions.map((cond, index) =>
1767
- this.inngestStep.run(`workflow.${workflowId}.conditional.${index}`, async () => {
1768
- const evalSpan = conditionalSpan?.createChildSpan({
1769
- type: AISpanType.WORKFLOW_CONDITIONAL_EVAL,
1770
- name: `condition ${index}`,
1771
- input: prevOutput,
1772
- attributes: {
1773
- conditionIndex: index,
1774
- },
1775
- });
1776
-
1777
- try {
1778
- const result = await cond({
1779
- runId,
1780
- workflowId,
1781
- mastra: this.mastra!,
1782
- runtimeContext,
1783
- runCount: -1,
1784
- inputData: prevOutput,
1785
- tracingContext: {
1786
- currentSpan: evalSpan,
1787
- },
1788
- getInitData: () => stepResults?.input as any,
1789
- getStepResult: (step: any) => {
1790
- if (!step?.id) {
1791
- return null;
1792
- }
1793
-
1794
- const result = stepResults[step.id];
1795
- if (result?.status === 'success') {
1796
- return result.output;
1797
- }
1798
-
1799
- return null;
1800
- },
1801
-
1802
- // TODO: this function shouldn't have suspend probably?
1803
- suspend: async (_suspendPayload: any) => {},
1804
- bail: () => {},
1805
- abort: () => {
1806
- abortController.abort();
1807
- },
1808
- [EMITTER_SYMBOL]: emitter,
1809
- engine: {
1810
- step: this.inngestStep,
1811
- },
1812
- abortSignal: abortController.signal,
1813
- writer: new ToolStream(
1814
- {
1815
- prefix: 'step',
1816
- callId: randomUUID(),
1817
- name: 'conditional',
1818
- runId,
1819
- },
1820
- writableStream,
1821
- ),
1822
- });
1823
-
1824
- evalSpan?.end({
1825
- output: result,
1826
- attributes: {
1827
- result: !!result,
1828
- },
1829
- });
1830
-
1831
- return result ? index : null;
1832
- } catch (e: unknown) {
1833
- evalSpan?.error({
1834
- error: e instanceof Error ? e : new Error(String(e)),
1835
- attributes: {
1836
- result: false,
1837
- },
1838
- });
1839
-
1840
- return null;
1841
- }
1842
- }),
1843
- ),
1844
- )
1845
- ).filter((index: any): index is number => index !== null);
1846
-
1847
- const stepsToRun = entry.steps.filter((_, index) => truthyIndexes.includes(index));
1848
-
1849
- // Update conditional span with evaluation results
1850
- conditionalSpan?.update({
1851
- attributes: {
1852
- truthyIndexes,
1853
- selectedSteps: stepsToRun.map(s => (s.type === 'step' ? s.step.id : `control-${s.type}`)),
1854
- },
1855
- });
1856
-
1857
- const results: { result: StepResult<any, any, any, any> }[] = await Promise.all(
1858
- stepsToRun.map((step, index) =>
1859
- this.executeEntry({
1860
- workflowId,
1861
- runId,
1862
- entry: step,
1863
- serializedStepGraph,
1864
- prevStep,
1865
- stepResults,
1866
- resume,
1867
- executionContext: {
1868
- workflowId,
1869
- runId,
1870
- executionPath: [...executionContext.executionPath, index],
1871
- suspendedPaths: executionContext.suspendedPaths,
1872
- retryConfig: executionContext.retryConfig,
1873
- executionSpan: executionContext.executionSpan,
1874
- },
1875
- emitter,
1876
- abortController,
1877
- runtimeContext,
1878
- writableStream,
1879
- disableScorers,
1880
- tracingContext: {
1881
- currentSpan: conditionalSpan,
1882
- },
1883
- }),
1884
- ),
1885
- );
1886
- const hasFailed = results.find(result => result.result.status === 'failed') as {
1887
- result: StepFailure<any, any, any>;
1888
- };
1889
- const hasSuspended = results.find(result => result.result.status === 'suspended');
1890
- if (hasFailed) {
1891
- execResults = { status: 'failed', error: hasFailed.result.error };
1892
- } else if (hasSuspended) {
1893
- execResults = { status: 'suspended', payload: hasSuspended.result.suspendPayload };
1894
- } else {
1895
- execResults = {
1896
- status: 'success',
1897
- output: results.reduce((acc: Record<string, any>, result, index) => {
1898
- if (result.result.status === 'success') {
1899
- // @ts-ignore
1900
- acc[stepsToRun[index]!.step.id] = result.output;
1901
- }
1902
-
1903
- return acc;
1904
- }, {}),
1905
- };
1906
- }
1907
-
1908
- if (execResults.status === 'failed') {
1909
- conditionalSpan?.error({
1910
- error: new Error(execResults.error),
1911
- });
1912
- } else {
1913
- conditionalSpan?.end({
1914
- output: execResults.output || execResults,
1915
- });
1916
- }
1917
-
1918
- return execResults;
1919
- }
1920
- }