@mastra/inngest 0.0.0-vector-sources-20250516175436 → 0.0.0-vector-query-tool-provider-options-20250828222356
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +769 -2
- package/LICENSE.md +11 -42
- package/dist/index.cjs +758 -78
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +277 -5
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +755 -76
- package/dist/index.js.map +1 -0
- package/docker-compose.yaml +3 -3
- package/package.json +26 -20
- package/src/index.test.ts +5998 -3528
- package/src/index.ts +1089 -127
- package/tsconfig.build.json +9 -0
- package/tsconfig.json +1 -1
- package/tsup.config.ts +17 -0
- package/vitest.config.ts +6 -0
- package/dist/_tsup-dts-rollup.d.cts +0 -194
- package/dist/_tsup-dts-rollup.d.ts +0 -194
- package/dist/index.d.cts +0 -5
package/src/index.ts
CHANGED
|
@@ -1,34 +1,55 @@
|
|
|
1
1
|
import { randomUUID } from 'crypto';
|
|
2
|
+
import type { ReadableStream } from 'node:stream/web';
|
|
2
3
|
import { subscribe } from '@inngest/realtime';
|
|
3
|
-
import type {
|
|
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';
|
|
4
7
|
import { RuntimeContext } from '@mastra/core/di';
|
|
5
|
-
import {
|
|
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';
|
|
6
13
|
import type {
|
|
7
14
|
ExecuteFunction,
|
|
8
15
|
ExecutionContext,
|
|
9
16
|
ExecutionEngine,
|
|
10
17
|
ExecutionGraph,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
NewWorkflowConfig,
|
|
18
|
+
Step,
|
|
19
|
+
WorkflowConfig,
|
|
14
20
|
StepFlowEntry,
|
|
15
21
|
StepResult,
|
|
16
22
|
WorkflowResult,
|
|
17
|
-
|
|
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';
|
|
18
31
|
import type { Span } from '@opentelemetry/api';
|
|
19
32
|
import type { Inngest, BaseContext } from 'inngest';
|
|
20
33
|
import { serve as inngestServe } from 'inngest/hono';
|
|
21
|
-
import
|
|
34
|
+
import { z } from 'zod';
|
|
35
|
+
|
|
36
|
+
export type InngestEngineType = {
|
|
37
|
+
step: any;
|
|
38
|
+
};
|
|
22
39
|
|
|
23
40
|
export function serve({ mastra, inngest }: { mastra: Mastra; inngest: Inngest }): ReturnType<typeof inngestServe> {
|
|
24
|
-
const wfs = mastra.
|
|
25
|
-
const functions =
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
+
);
|
|
32
53
|
return inngestServe({
|
|
33
54
|
client: inngest,
|
|
34
55
|
functions,
|
|
@@ -36,11 +57,13 @@ export function serve({ mastra, inngest }: { mastra: Mastra; inngest: Inngest })
|
|
|
36
57
|
}
|
|
37
58
|
|
|
38
59
|
export class InngestRun<
|
|
39
|
-
|
|
60
|
+
TEngineType = InngestEngineType,
|
|
61
|
+
TSteps extends Step<string, any, any>[] = Step<string, any, any>[],
|
|
40
62
|
TInput extends z.ZodType<any> = z.ZodType<any>,
|
|
41
63
|
TOutput extends z.ZodType<any> = z.ZodType<any>,
|
|
42
|
-
> extends Run<TSteps, TInput, TOutput> {
|
|
64
|
+
> extends Run<TEngineType, TSteps, TInput, TOutput> {
|
|
43
65
|
private inngest: Inngest;
|
|
66
|
+
serializedStepGraph: SerializedStepFlowEntry[];
|
|
44
67
|
#mastra: Mastra;
|
|
45
68
|
|
|
46
69
|
constructor(
|
|
@@ -49,6 +72,7 @@ export class InngestRun<
|
|
|
49
72
|
runId: string;
|
|
50
73
|
executionEngine: ExecutionEngine;
|
|
51
74
|
executionGraph: ExecutionGraph;
|
|
75
|
+
serializedStepGraph: SerializedStepFlowEntry[];
|
|
52
76
|
mastra?: Mastra;
|
|
53
77
|
retryConfig?: {
|
|
54
78
|
attempts?: number;
|
|
@@ -60,11 +84,12 @@ export class InngestRun<
|
|
|
60
84
|
) {
|
|
61
85
|
super(params);
|
|
62
86
|
this.inngest = inngest;
|
|
87
|
+
this.serializedStepGraph = params.serializedStepGraph;
|
|
63
88
|
this.#mastra = params.mastra!;
|
|
64
89
|
}
|
|
65
90
|
|
|
66
91
|
async getRuns(eventId: string) {
|
|
67
|
-
const response = await fetch(`${this.inngest.apiBaseUrl}/v1/events/${eventId}/runs`, {
|
|
92
|
+
const response = await fetch(`${this.inngest.apiBaseUrl ?? 'https://api.inngest.com'}/v1/events/${eventId}/runs`, {
|
|
68
93
|
headers: {
|
|
69
94
|
Authorization: `Bearer ${process.env.INNGEST_SIGNING_KEY}`,
|
|
70
95
|
},
|
|
@@ -75,16 +100,55 @@ export class InngestRun<
|
|
|
75
100
|
|
|
76
101
|
async getRunOutput(eventId: string) {
|
|
77
102
|
let runs = await this.getRuns(eventId);
|
|
78
|
-
|
|
103
|
+
|
|
104
|
+
while (runs?.[0]?.status !== 'Completed' || runs?.[0]?.event_id !== eventId) {
|
|
79
105
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
80
106
|
runs = await this.getRuns(eventId);
|
|
81
|
-
if (runs?.[0]?.status === 'Failed'
|
|
107
|
+
if (runs?.[0]?.status === 'Failed') {
|
|
108
|
+
console.log('run', runs?.[0]);
|
|
82
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' } } };
|
|
83
116
|
}
|
|
84
117
|
}
|
|
85
118
|
return runs?.[0];
|
|
86
119
|
}
|
|
87
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
|
+
|
|
88
152
|
async start({
|
|
89
153
|
inputData,
|
|
90
154
|
}: {
|
|
@@ -96,11 +160,14 @@ export class InngestRun<
|
|
|
96
160
|
runId: this.runId,
|
|
97
161
|
snapshot: {
|
|
98
162
|
runId: this.runId,
|
|
163
|
+
serializedStepGraph: this.serializedStepGraph,
|
|
99
164
|
value: {},
|
|
100
165
|
context: {} as any,
|
|
101
166
|
activePaths: [],
|
|
102
167
|
suspendedPaths: {},
|
|
168
|
+
waitingPaths: {},
|
|
103
169
|
timestamp: Date.now(),
|
|
170
|
+
status: 'running',
|
|
104
171
|
},
|
|
105
172
|
});
|
|
106
173
|
|
|
@@ -122,7 +189,9 @@ export class InngestRun<
|
|
|
122
189
|
result.error = new Error(result.error);
|
|
123
190
|
}
|
|
124
191
|
|
|
125
|
-
|
|
192
|
+
if (result.status !== 'suspended') {
|
|
193
|
+
this.cleanup?.();
|
|
194
|
+
}
|
|
126
195
|
return result;
|
|
127
196
|
}
|
|
128
197
|
|
|
@@ -134,6 +203,27 @@ export class InngestRun<
|
|
|
134
203
|
| string
|
|
135
204
|
| string[];
|
|
136
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;
|
|
137
227
|
}): Promise<WorkflowResult<TOutput, TSteps>> {
|
|
138
228
|
const steps: string[] = (Array.isArray(params.step) ? params.step : [params.step]).map(step =>
|
|
139
229
|
typeof step === 'string' ? step : step?.id,
|
|
@@ -148,6 +238,7 @@ export class InngestRun<
|
|
|
148
238
|
data: {
|
|
149
239
|
inputData: params.resumeData,
|
|
150
240
|
runId: this.runId,
|
|
241
|
+
workflowId: this.workflowId,
|
|
151
242
|
stepResults: snapshot?.context as any,
|
|
152
243
|
resume: {
|
|
153
244
|
steps,
|
|
@@ -171,43 +262,88 @@ export class InngestRun<
|
|
|
171
262
|
return result;
|
|
172
263
|
}
|
|
173
264
|
|
|
174
|
-
watch(cb: (event:
|
|
265
|
+
watch(cb: (event: WatchEvent) => void, type: 'watch' | 'watch-v2' = 'watch'): () => void {
|
|
266
|
+
let active = true;
|
|
175
267
|
const streamPromise = subscribe(
|
|
176
268
|
{
|
|
177
269
|
channel: `workflow:${this.workflowId}:${this.runId}`,
|
|
178
|
-
topics: [
|
|
270
|
+
topics: [type],
|
|
179
271
|
app: this.inngest,
|
|
180
272
|
},
|
|
181
273
|
(message: any) => {
|
|
182
|
-
|
|
274
|
+
if (active) {
|
|
275
|
+
cb(message.data);
|
|
276
|
+
}
|
|
183
277
|
},
|
|
184
278
|
);
|
|
185
279
|
|
|
186
280
|
return () => {
|
|
281
|
+
active = false;
|
|
187
282
|
streamPromise
|
|
188
|
-
.then((stream:
|
|
189
|
-
stream.cancel();
|
|
283
|
+
.then(async (stream: Awaited<typeof streamPromise>) => {
|
|
284
|
+
return stream.cancel();
|
|
190
285
|
})
|
|
191
286
|
.catch(err => {
|
|
192
287
|
console.error(err);
|
|
193
288
|
});
|
|
194
289
|
};
|
|
195
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
|
+
}
|
|
196
331
|
}
|
|
197
332
|
|
|
198
333
|
export class InngestWorkflow<
|
|
199
|
-
|
|
334
|
+
TEngineType = InngestEngineType,
|
|
335
|
+
TSteps extends Step<string, any, any>[] = Step<string, any, any>[],
|
|
200
336
|
TWorkflowId extends string = string,
|
|
201
337
|
TInput extends z.ZodType<any> = z.ZodType<any>,
|
|
202
338
|
TOutput extends z.ZodType<any> = z.ZodType<any>,
|
|
203
339
|
TPrevSchema extends z.ZodType<any> = TInput,
|
|
204
|
-
> extends
|
|
340
|
+
> extends Workflow<TEngineType, TSteps, TWorkflowId, TInput, TOutput, TPrevSchema> {
|
|
205
341
|
#mastra: Mastra;
|
|
206
342
|
public inngest: Inngest;
|
|
207
343
|
|
|
208
344
|
private function: ReturnType<Inngest['createFunction']> | undefined;
|
|
209
345
|
|
|
210
|
-
constructor(params:
|
|
346
|
+
constructor(params: WorkflowConfig<TWorkflowId, TInput, TOutput, TSteps>, inngest: Inngest) {
|
|
211
347
|
super(params);
|
|
212
348
|
this.#mastra = params.mastra!;
|
|
213
349
|
this.inngest = inngest;
|
|
@@ -226,25 +362,52 @@ export class InngestWorkflow<
|
|
|
226
362
|
return { runs: [], total: 0 };
|
|
227
363
|
}
|
|
228
364
|
|
|
229
|
-
return storage.getWorkflowRuns({ workflowName: this.id, ...(args ?? {}) }) as unknown as
|
|
365
|
+
return storage.getWorkflowRuns({ workflowName: this.id, ...(args ?? {}) }) as unknown as WorkflowRuns;
|
|
230
366
|
}
|
|
231
367
|
|
|
232
|
-
async getWorkflowRunById(runId: string): Promise<
|
|
368
|
+
async getWorkflowRunById(runId: string): Promise<WorkflowRun | null> {
|
|
233
369
|
const storage = this.#mastra?.getStorage();
|
|
234
370
|
if (!storage) {
|
|
235
371
|
this.logger.debug('Cannot get workflow runs. Mastra engine is not initialized');
|
|
236
|
-
|
|
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;
|
|
237
376
|
}
|
|
238
|
-
const run = (await storage.getWorkflowRunById({ runId, workflowName: this.id })) as unknown as
|
|
377
|
+
const run = (await storage.getWorkflowRunById({ runId, workflowName: this.id })) as unknown as WorkflowRun;
|
|
239
378
|
|
|
240
379
|
return (
|
|
241
380
|
run ??
|
|
242
|
-
(this.runs.get(runId)
|
|
243
|
-
? ({ ...this.runs.get(runId), workflowName: this.id } as unknown as VNextWorkflowRun)
|
|
244
|
-
: null)
|
|
381
|
+
(this.runs.get(runId) ? ({ ...this.runs.get(runId), workflowName: this.id } as unknown as WorkflowRun) : null)
|
|
245
382
|
);
|
|
246
383
|
}
|
|
247
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
|
+
|
|
248
411
|
__registerMastra(mastra: Mastra) {
|
|
249
412
|
this.#mastra = mastra;
|
|
250
413
|
this.executionEngine.__registerMastra(mastra);
|
|
@@ -268,11 +431,35 @@ export class InngestWorkflow<
|
|
|
268
431
|
}
|
|
269
432
|
}
|
|
270
433
|
|
|
271
|
-
createRun(options?: { runId?: string }): Run<TSteps, TInput, TOutput> {
|
|
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>> {
|
|
272
459
|
const runIdToUse = options?.runId || randomUUID();
|
|
273
460
|
|
|
274
461
|
// Return a new Run instance with object parameters
|
|
275
|
-
const run: Run<TSteps, TInput, TOutput> =
|
|
462
|
+
const run: Run<TEngineType, TSteps, TInput, TOutput> =
|
|
276
463
|
this.runs.get(runIdToUse) ??
|
|
277
464
|
new InngestRun(
|
|
278
465
|
{
|
|
@@ -280,6 +467,7 @@ export class InngestWorkflow<
|
|
|
280
467
|
runId: runIdToUse,
|
|
281
468
|
executionEngine: this.executionEngine,
|
|
282
469
|
executionGraph: this.executionGraph,
|
|
470
|
+
serializedStepGraph: this.serializedStepGraph,
|
|
283
471
|
mastra: this.#mastra,
|
|
284
472
|
retryConfig: this.retryConfig,
|
|
285
473
|
cleanup: () => this.runs.delete(runIdToUse),
|
|
@@ -288,6 +476,30 @@ export class InngestWorkflow<
|
|
|
288
476
|
);
|
|
289
477
|
|
|
290
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
|
+
|
|
291
503
|
return run;
|
|
292
504
|
}
|
|
293
505
|
|
|
@@ -296,8 +508,12 @@ export class InngestWorkflow<
|
|
|
296
508
|
return this.function;
|
|
297
509
|
}
|
|
298
510
|
this.function = this.inngest.createFunction(
|
|
299
|
-
|
|
300
|
-
|
|
511
|
+
{
|
|
512
|
+
id: `workflow.${this.id}`,
|
|
513
|
+
// @ts-ignore
|
|
514
|
+
retries: this.retryConfig?.attempts ?? 0,
|
|
515
|
+
cancelOn: [{ event: `cancel.workflow.${this.id}` }],
|
|
516
|
+
},
|
|
301
517
|
{ event: `workflow.${this.id}` },
|
|
302
518
|
async ({ event, step, attempt, publish }) => {
|
|
303
519
|
let { inputData, runId, resume } = event.data;
|
|
@@ -317,13 +533,22 @@ export class InngestWorkflow<
|
|
|
317
533
|
try {
|
|
318
534
|
await publish({
|
|
319
535
|
channel: `workflow:${this.id}:${runId}`,
|
|
320
|
-
topic:
|
|
536
|
+
topic: event,
|
|
321
537
|
data,
|
|
322
538
|
});
|
|
323
539
|
} catch (err: any) {
|
|
324
540
|
this.logger.error('Error emitting event: ' + (err?.stack ?? err?.message ?? err));
|
|
325
541
|
}
|
|
326
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
|
+
},
|
|
327
552
|
};
|
|
328
553
|
|
|
329
554
|
const engine = new InngestExecutionEngine(this.#mastra, step, attempt);
|
|
@@ -331,11 +556,14 @@ export class InngestWorkflow<
|
|
|
331
556
|
workflowId: this.id,
|
|
332
557
|
runId,
|
|
333
558
|
graph: this.executionGraph,
|
|
559
|
+
serializedStepGraph: this.serializedStepGraph,
|
|
334
560
|
input: inputData,
|
|
335
561
|
emitter,
|
|
336
562
|
retryConfig: this.retryConfig,
|
|
337
563
|
runtimeContext: new RuntimeContext(), // TODO
|
|
338
564
|
resume,
|
|
565
|
+
abortController: new AbortController(),
|
|
566
|
+
currentSpan: undefined, // TODO: Pass actual parent AI span from workflow execution context
|
|
339
567
|
});
|
|
340
568
|
|
|
341
569
|
return { result, runId };
|
|
@@ -364,29 +592,199 @@ export class InngestWorkflow<
|
|
|
364
592
|
}
|
|
365
593
|
}
|
|
366
594
|
|
|
367
|
-
function
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
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>,
|
|
372
631
|
>(
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
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
|
+
}
|
|
386
778
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
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
|
+
};
|
|
390
788
|
}
|
|
391
789
|
|
|
392
790
|
export function init(inngest: Inngest) {
|
|
@@ -395,13 +793,59 @@ export function init(inngest: Inngest) {
|
|
|
395
793
|
TWorkflowId extends string = string,
|
|
396
794
|
TInput extends z.ZodType<any> = z.ZodType<any>,
|
|
397
795
|
TOutput extends z.ZodType<any> = z.ZodType<any>,
|
|
398
|
-
TSteps extends Step<string, any, any
|
|
399
|
-
|
|
400
|
-
|
|
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);
|
|
401
806
|
},
|
|
402
807
|
createStep,
|
|
403
|
-
cloneStep
|
|
404
|
-
|
|
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
|
+
},
|
|
405
849
|
};
|
|
406
850
|
}
|
|
407
851
|
|
|
@@ -415,11 +859,48 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
415
859
|
this.inngestAttempts = inngestAttempts;
|
|
416
860
|
}
|
|
417
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
|
+
|
|
418
899
|
protected async fmtReturnValue<TOutput>(
|
|
419
900
|
executionSpan: Span | undefined,
|
|
420
|
-
emitter:
|
|
421
|
-
stepResults: Record<string, StepResult<any>>,
|
|
422
|
-
lastOutput: StepResult<any>,
|
|
901
|
+
emitter: Emitter,
|
|
902
|
+
stepResults: Record<string, StepResult<any, any, any, any>>,
|
|
903
|
+
lastOutput: StepResult<any, any, any, any>,
|
|
423
904
|
error?: Error | string,
|
|
424
905
|
): Promise<TOutput> {
|
|
425
906
|
const base: any = {
|
|
@@ -489,41 +970,254 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
489
970
|
return base as TOutput;
|
|
490
971
|
}
|
|
491
972
|
|
|
492
|
-
async
|
|
973
|
+
// async executeSleep({ id, duration }: { id: string; duration: number }): Promise<void> {
|
|
974
|
+
// await this.inngestStep.sleep(id, duration);
|
|
975
|
+
// }
|
|
976
|
+
|
|
977
|
+
async executeSleep({
|
|
493
978
|
workflowId,
|
|
494
979
|
runId,
|
|
495
|
-
|
|
496
|
-
stepResults,
|
|
497
|
-
executionContext,
|
|
498
|
-
resume,
|
|
980
|
+
entry,
|
|
499
981
|
prevOutput,
|
|
982
|
+
stepResults,
|
|
500
983
|
emitter,
|
|
984
|
+
abortController,
|
|
501
985
|
runtimeContext,
|
|
986
|
+
writableStream,
|
|
987
|
+
tracingContext,
|
|
502
988
|
}: {
|
|
503
989
|
workflowId: string;
|
|
504
990
|
runId: string;
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
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>>;
|
|
508
1001
|
resume?: {
|
|
509
1002
|
steps: string[];
|
|
1003
|
+
stepResults: Record<string, StepResult<any, any, any, any>>;
|
|
510
1004
|
resumePayload: any;
|
|
1005
|
+
resumePath: number[];
|
|
511
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;
|
|
512
1112
|
prevOutput: any;
|
|
513
|
-
|
|
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;
|
|
514
1123
|
runtimeContext: RuntimeContext;
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
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,
|
|
526
1214
|
});
|
|
1215
|
+
|
|
1216
|
+
if (eventData === null) {
|
|
1217
|
+
throw 'Timeout waiting for event';
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
return eventData?.data;
|
|
527
1221
|
}
|
|
528
1222
|
|
|
529
1223
|
async executeStep({
|
|
@@ -533,29 +1227,41 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
533
1227
|
resume,
|
|
534
1228
|
prevOutput,
|
|
535
1229
|
emitter,
|
|
1230
|
+
abortController,
|
|
536
1231
|
runtimeContext,
|
|
1232
|
+
writableStream,
|
|
1233
|
+
disableScorers,
|
|
1234
|
+
tracingContext,
|
|
537
1235
|
}: {
|
|
538
1236
|
step: Step<string, any, any>;
|
|
539
|
-
stepResults: Record<string, StepResult<any>>;
|
|
540
|
-
executionContext:
|
|
541
|
-
workflowId: string;
|
|
542
|
-
runId: string;
|
|
543
|
-
executionPath: number[];
|
|
544
|
-
suspendedPaths: Record<string, number[]>;
|
|
545
|
-
retryConfig: { attempts: number; delay: number };
|
|
546
|
-
};
|
|
1237
|
+
stepResults: Record<string, StepResult<any, any, any, any>>;
|
|
1238
|
+
executionContext: ExecutionContext;
|
|
547
1239
|
resume?: {
|
|
548
1240
|
steps: string[];
|
|
549
1241
|
resumePayload: any;
|
|
550
1242
|
runId?: string;
|
|
551
1243
|
};
|
|
552
1244
|
prevOutput: any;
|
|
553
|
-
emitter:
|
|
1245
|
+
emitter: Emitter;
|
|
1246
|
+
abortController: AbortController;
|
|
554
1247
|
runtimeContext: RuntimeContext;
|
|
555
|
-
|
|
556
|
-
|
|
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(
|
|
557
1262
|
`workflow.${executionContext.workflowId}.run.${executionContext.runId}.step.${step.id}.running_ev`,
|
|
558
1263
|
async () => {
|
|
1264
|
+
const startedAt = Date.now();
|
|
559
1265
|
await emitter.emit('watch', {
|
|
560
1266
|
type: 'watch',
|
|
561
1267
|
payload: {
|
|
@@ -577,6 +1283,18 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
577
1283
|
},
|
|
578
1284
|
eventTimestamp: Date.now(),
|
|
579
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;
|
|
580
1298
|
},
|
|
581
1299
|
);
|
|
582
1300
|
|
|
@@ -643,10 +1361,20 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
643
1361
|
eventTimestamp: Date.now(),
|
|
644
1362
|
});
|
|
645
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
|
+
|
|
646
1374
|
return { executionContext, result: { status: 'failed', error: result?.error } };
|
|
647
1375
|
} else if (result.status === 'suspended') {
|
|
648
1376
|
const suspendedSteps = Object.entries(result.steps).filter(([_stepName, stepResult]) => {
|
|
649
|
-
const stepRes: StepResult<any> = stepResult as StepResult<any>;
|
|
1377
|
+
const stepRes: StepResult<any, any, any, any> = stepResult as StepResult<any, any, any, any>;
|
|
650
1378
|
return stepRes?.status === 'suspended';
|
|
651
1379
|
});
|
|
652
1380
|
|
|
@@ -673,6 +1401,14 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
673
1401
|
eventTimestamp: Date.now(),
|
|
674
1402
|
});
|
|
675
1403
|
|
|
1404
|
+
await emitter.emit('watch-v2', {
|
|
1405
|
+
type: 'step-suspended',
|
|
1406
|
+
payload: {
|
|
1407
|
+
id: step.id,
|
|
1408
|
+
status: 'suspended',
|
|
1409
|
+
},
|
|
1410
|
+
});
|
|
1411
|
+
|
|
676
1412
|
return {
|
|
677
1413
|
executionContext,
|
|
678
1414
|
result: {
|
|
@@ -729,23 +1465,58 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
729
1465
|
eventTimestamp: Date.now(),
|
|
730
1466
|
});
|
|
731
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
|
+
|
|
732
1485
|
return { executionContext, result: { status: 'success', output: result?.result } };
|
|
733
1486
|
},
|
|
734
1487
|
);
|
|
735
1488
|
|
|
736
1489
|
Object.assign(executionContext, res.executionContext);
|
|
737
|
-
return res.result as StepResult<any>;
|
|
1490
|
+
return res.result as StepResult<any, any, any, any>;
|
|
738
1491
|
}
|
|
739
1492
|
|
|
740
1493
|
const stepRes = await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}`, async () => {
|
|
741
|
-
let execResults:
|
|
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
|
+
};
|
|
742
1506
|
let suspended: { payload: any } | undefined;
|
|
1507
|
+
let bailed: { payload: any } | undefined;
|
|
1508
|
+
|
|
743
1509
|
try {
|
|
744
1510
|
const result = await step.execute({
|
|
1511
|
+
runId: executionContext.runId,
|
|
745
1512
|
mastra: this.mastra!,
|
|
746
1513
|
runtimeContext,
|
|
1514
|
+
writableStream,
|
|
747
1515
|
inputData: prevOutput,
|
|
748
1516
|
resumeData: resume?.steps[0] === step.id ? resume?.resumePayload : undefined,
|
|
1517
|
+
tracingContext: {
|
|
1518
|
+
currentSpan: stepAISpan,
|
|
1519
|
+
},
|
|
749
1520
|
getInitData: () => stepResults?.input as any,
|
|
750
1521
|
getStepResult: (step: any) => {
|
|
751
1522
|
const result = stepResults[step.id];
|
|
@@ -759,27 +1530,63 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
759
1530
|
executionContext.suspendedPaths[step.id] = executionContext.executionPath;
|
|
760
1531
|
suspended = { payload: suspendPayload };
|
|
761
1532
|
},
|
|
1533
|
+
bail: (result: any) => {
|
|
1534
|
+
bailed = { payload: result };
|
|
1535
|
+
},
|
|
762
1536
|
resume: {
|
|
763
1537
|
steps: resume?.steps?.slice(1) || [],
|
|
764
1538
|
resumePayload: resume?.resumePayload,
|
|
765
1539
|
// @ts-ignore
|
|
766
1540
|
runId: stepResults[step.id]?.payload?.__workflow_meta?.runId,
|
|
767
1541
|
},
|
|
768
|
-
emitter,
|
|
1542
|
+
[EMITTER_SYMBOL]: emitter,
|
|
1543
|
+
engine: {
|
|
1544
|
+
step: this.inngestStep,
|
|
1545
|
+
},
|
|
1546
|
+
abortSignal: abortController.signal,
|
|
769
1547
|
});
|
|
770
|
-
|
|
771
|
-
|
|
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
|
+
};
|
|
772
1559
|
} catch (e) {
|
|
773
|
-
execResults = {
|
|
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
|
+
};
|
|
774
1569
|
}
|
|
775
1570
|
|
|
776
1571
|
if (suspended) {
|
|
777
|
-
execResults = {
|
|
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 };
|
|
778
1583
|
}
|
|
779
1584
|
|
|
780
1585
|
if (execResults.status === 'failed') {
|
|
781
1586
|
if (executionContext.retryConfig.attempts > 0 && this.inngestAttempts < executionContext.retryConfig.attempts) {
|
|
782
|
-
|
|
1587
|
+
const error = new Error(execResults.error);
|
|
1588
|
+
stepAISpan?.error({ error });
|
|
1589
|
+
throw error;
|
|
783
1590
|
}
|
|
784
1591
|
}
|
|
785
1592
|
|
|
@@ -788,12 +1595,11 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
788
1595
|
payload: {
|
|
789
1596
|
currentStep: {
|
|
790
1597
|
id: step.id,
|
|
791
|
-
|
|
792
|
-
output: execResults.output,
|
|
1598
|
+
...execResults,
|
|
793
1599
|
},
|
|
794
1600
|
workflowState: {
|
|
795
1601
|
status: 'running',
|
|
796
|
-
steps: stepResults,
|
|
1602
|
+
steps: { ...stepResults, [step.id]: execResults },
|
|
797
1603
|
result: null,
|
|
798
1604
|
error: null,
|
|
799
1605
|
},
|
|
@@ -801,9 +1607,54 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
801
1607
|
eventTimestamp: Date.now(),
|
|
802
1608
|
});
|
|
803
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
|
+
|
|
804
1638
|
return { result: execResults, executionContext, stepResults };
|
|
805
1639
|
});
|
|
806
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
|
+
|
|
807
1658
|
// @ts-ignore
|
|
808
1659
|
Object.assign(executionContext.suspendedPaths, stepRes.executionContext.suspendedPaths);
|
|
809
1660
|
// @ts-ignore
|
|
@@ -818,11 +1669,20 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
818
1669
|
runId,
|
|
819
1670
|
stepResults,
|
|
820
1671
|
executionContext,
|
|
1672
|
+
serializedStepGraph,
|
|
1673
|
+
workflowStatus,
|
|
1674
|
+
result,
|
|
1675
|
+
error,
|
|
821
1676
|
}: {
|
|
822
1677
|
workflowId: string;
|
|
823
1678
|
runId: string;
|
|
824
|
-
stepResults: Record<string, StepResult<any>>;
|
|
1679
|
+
stepResults: Record<string, StepResult<any, any, any, any>>;
|
|
1680
|
+
serializedStepGraph: SerializedStepFlowEntry[];
|
|
825
1681
|
executionContext: ExecutionContext;
|
|
1682
|
+
workflowStatus: 'success' | 'failed' | 'suspended' | 'running';
|
|
1683
|
+
result?: Record<string, any>;
|
|
1684
|
+
error?: string | Error;
|
|
1685
|
+
runtimeContext: RuntimeContext;
|
|
826
1686
|
}) {
|
|
827
1687
|
await this.inngestStep.run(
|
|
828
1688
|
`workflow.${workflowId}.run.${runId}.path.${JSON.stringify(executionContext.executionPath)}.stepUpdate`,
|
|
@@ -836,6 +1696,11 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
836
1696
|
context: stepResults as any,
|
|
837
1697
|
activePaths: [],
|
|
838
1698
|
suspendedPaths: executionContext.suspendedPaths,
|
|
1699
|
+
waitingPaths: {},
|
|
1700
|
+
serializedStepGraph,
|
|
1701
|
+
status: workflowStatus,
|
|
1702
|
+
result,
|
|
1703
|
+
error,
|
|
839
1704
|
// @ts-ignore
|
|
840
1705
|
timestamp: Date.now(),
|
|
841
1706
|
},
|
|
@@ -851,37 +1716,75 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
851
1716
|
prevOutput,
|
|
852
1717
|
prevStep,
|
|
853
1718
|
stepResults,
|
|
1719
|
+
serializedStepGraph,
|
|
854
1720
|
resume,
|
|
855
1721
|
executionContext,
|
|
856
1722
|
emitter,
|
|
1723
|
+
abortController,
|
|
857
1724
|
runtimeContext,
|
|
1725
|
+
writableStream,
|
|
1726
|
+
disableScorers,
|
|
1727
|
+
tracingContext,
|
|
858
1728
|
}: {
|
|
859
1729
|
workflowId: string;
|
|
860
1730
|
runId: string;
|
|
861
|
-
entry: {
|
|
1731
|
+
entry: {
|
|
1732
|
+
type: 'conditional';
|
|
1733
|
+
steps: StepFlowEntry[];
|
|
1734
|
+
conditions: ExecuteFunction<any, any, any, any, InngestEngineType>[];
|
|
1735
|
+
};
|
|
862
1736
|
prevStep: StepFlowEntry;
|
|
1737
|
+
serializedStepGraph: SerializedStepFlowEntry[];
|
|
863
1738
|
prevOutput: any;
|
|
864
|
-
stepResults: Record<string, StepResult<any>>;
|
|
1739
|
+
stepResults: Record<string, StepResult<any, any, any, any>>;
|
|
865
1740
|
resume?: {
|
|
866
1741
|
steps: string[];
|
|
867
|
-
stepResults: Record<string, StepResult<any>>;
|
|
1742
|
+
stepResults: Record<string, StepResult<any, any, any, any>>;
|
|
868
1743
|
resumePayload: any;
|
|
869
1744
|
resumePath: number[];
|
|
870
1745
|
};
|
|
871
1746
|
executionContext: ExecutionContext;
|
|
872
|
-
emitter:
|
|
1747
|
+
emitter: Emitter;
|
|
1748
|
+
abortController: AbortController;
|
|
873
1749
|
runtimeContext: RuntimeContext;
|
|
874
|
-
|
|
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
|
+
|
|
875
1763
|
let execResults: any;
|
|
876
1764
|
const truthyIndexes = (
|
|
877
1765
|
await Promise.all(
|
|
878
1766
|
entry.conditions.map((cond, index) =>
|
|
879
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
|
+
|
|
880
1777
|
try {
|
|
881
1778
|
const result = await cond({
|
|
1779
|
+
runId,
|
|
1780
|
+
workflowId,
|
|
882
1781
|
mastra: this.mastra!,
|
|
883
1782
|
runtimeContext,
|
|
1783
|
+
runCount: -1,
|
|
884
1784
|
inputData: prevOutput,
|
|
1785
|
+
tracingContext: {
|
|
1786
|
+
currentSpan: evalSpan,
|
|
1787
|
+
},
|
|
885
1788
|
getInitData: () => stepResults?.input as any,
|
|
886
1789
|
getStepResult: (step: any) => {
|
|
887
1790
|
if (!step?.id) {
|
|
@@ -898,11 +1801,42 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
898
1801
|
|
|
899
1802
|
// TODO: this function shouldn't have suspend probably?
|
|
900
1803
|
suspend: async (_suspendPayload: any) => {},
|
|
901
|
-
|
|
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
|
+
),
|
|
902
1822
|
});
|
|
1823
|
+
|
|
1824
|
+
evalSpan?.end({
|
|
1825
|
+
output: result,
|
|
1826
|
+
attributes: {
|
|
1827
|
+
result: !!result,
|
|
1828
|
+
},
|
|
1829
|
+
});
|
|
1830
|
+
|
|
903
1831
|
return result ? index : null;
|
|
904
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
905
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
|
+
|
|
906
1840
|
return null;
|
|
907
1841
|
}
|
|
908
1842
|
}),
|
|
@@ -911,12 +1845,22 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
911
1845
|
).filter((index: any): index is number => index !== null);
|
|
912
1846
|
|
|
913
1847
|
const stepsToRun = entry.steps.filter((_, index) => truthyIndexes.includes(index));
|
|
914
|
-
|
|
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(
|
|
915
1858
|
stepsToRun.map((step, index) =>
|
|
916
1859
|
this.executeEntry({
|
|
917
1860
|
workflowId,
|
|
918
1861
|
runId,
|
|
919
1862
|
entry: step,
|
|
1863
|
+
serializedStepGraph,
|
|
920
1864
|
prevStep,
|
|
921
1865
|
stepResults,
|
|
922
1866
|
resume,
|
|
@@ -929,21 +1873,29 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
929
1873
|
executionSpan: executionContext.executionSpan,
|
|
930
1874
|
},
|
|
931
1875
|
emitter,
|
|
1876
|
+
abortController,
|
|
932
1877
|
runtimeContext,
|
|
1878
|
+
writableStream,
|
|
1879
|
+
disableScorers,
|
|
1880
|
+
tracingContext: {
|
|
1881
|
+
currentSpan: conditionalSpan,
|
|
1882
|
+
},
|
|
933
1883
|
}),
|
|
934
1884
|
),
|
|
935
1885
|
);
|
|
936
|
-
const hasFailed = results.find(result => result.status === 'failed')
|
|
937
|
-
|
|
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');
|
|
938
1890
|
if (hasFailed) {
|
|
939
|
-
execResults = { status: 'failed', error: hasFailed.error };
|
|
1891
|
+
execResults = { status: 'failed', error: hasFailed.result.error };
|
|
940
1892
|
} else if (hasSuspended) {
|
|
941
|
-
execResults = { status: 'suspended', payload: hasSuspended.
|
|
1893
|
+
execResults = { status: 'suspended', payload: hasSuspended.result.suspendPayload };
|
|
942
1894
|
} else {
|
|
943
1895
|
execResults = {
|
|
944
1896
|
status: 'success',
|
|
945
1897
|
output: results.reduce((acc: Record<string, any>, result, index) => {
|
|
946
|
-
if (result.status === 'success') {
|
|
1898
|
+
if (result.result.status === 'success') {
|
|
947
1899
|
// @ts-ignore
|
|
948
1900
|
acc[stepsToRun[index]!.step.id] = result.output;
|
|
949
1901
|
}
|
|
@@ -953,6 +1905,16 @@ export class InngestExecutionEngine extends DefaultExecutionEngine {
|
|
|
953
1905
|
};
|
|
954
1906
|
}
|
|
955
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
|
+
|
|
956
1918
|
return execResults;
|
|
957
1919
|
}
|
|
958
1920
|
}
|