@mastra/inngest 0.0.0-message-list-update-20250715150321 → 0.0.0-monorepo-binary-20251013210052
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +708 -3
- package/dist/index.cjs +611 -329
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +309 -7
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +614 -332
- package/dist/index.js.map +1 -0
- package/package.json +31 -17
- package/dist/_tsup-dts-rollup.d.cts +0 -310
- package/dist/_tsup-dts-rollup.d.ts +0 -310
- package/dist/index.d.cts +0 -7
- package/docker-compose.yaml +0 -10
- package/eslint.config.js +0 -6
- package/src/index.test.ts +0 -7697
- package/src/index.ts +0 -1737
- package/tsconfig.json +0 -5
- package/vitest.config.ts +0 -14
package/dist/index.cjs
CHANGED
|
@@ -2,17 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
var crypto = require('crypto');
|
|
4
4
|
var realtime = require('@inngest/realtime');
|
|
5
|
+
var aiTracing = require('@mastra/core/ai-tracing');
|
|
5
6
|
var di = require('@mastra/core/di');
|
|
6
7
|
var tools = require('@mastra/core/tools');
|
|
7
8
|
var workflows = require('@mastra/core/workflows');
|
|
8
9
|
var _constants = require('@mastra/core/workflows/_constants');
|
|
10
|
+
var inngest = require('inngest');
|
|
9
11
|
var hono = require('inngest/hono');
|
|
10
12
|
var zod = require('zod');
|
|
11
13
|
|
|
12
14
|
// src/index.ts
|
|
13
|
-
function serve({
|
|
15
|
+
function serve({
|
|
16
|
+
mastra,
|
|
17
|
+
inngest,
|
|
18
|
+
functions: userFunctions = [],
|
|
19
|
+
registerOptions
|
|
20
|
+
}) {
|
|
14
21
|
const wfs = mastra.getWorkflows();
|
|
15
|
-
const
|
|
22
|
+
const workflowFunctions = Array.from(
|
|
16
23
|
new Set(
|
|
17
24
|
Object.values(wfs).flatMap((wf) => {
|
|
18
25
|
if (wf instanceof InngestWorkflow) {
|
|
@@ -24,8 +31,9 @@ function serve({ mastra, inngest }) {
|
|
|
24
31
|
)
|
|
25
32
|
);
|
|
26
33
|
return hono.serve({
|
|
34
|
+
...registerOptions,
|
|
27
35
|
client: inngest,
|
|
28
|
-
functions
|
|
36
|
+
functions: [...workflowFunctions, ...userFunctions]
|
|
29
37
|
});
|
|
30
38
|
}
|
|
31
39
|
var InngestRun = class extends workflows.Run {
|
|
@@ -53,9 +61,15 @@ var InngestRun = class extends workflows.Run {
|
|
|
53
61
|
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
54
62
|
runs = await this.getRuns(eventId);
|
|
55
63
|
if (runs?.[0]?.status === "Failed") {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
64
|
+
const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
|
|
65
|
+
workflowName: this.workflowId,
|
|
66
|
+
runId: this.runId
|
|
67
|
+
});
|
|
68
|
+
return {
|
|
69
|
+
output: { result: { steps: snapshot?.context, status: "failed", error: runs?.[0]?.output?.message } }
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
if (runs?.[0]?.status === "Cancelled") {
|
|
59
73
|
const snapshot = await this.#mastra?.storage?.loadWorkflowSnapshot({
|
|
60
74
|
workflowName: this.workflowId,
|
|
61
75
|
runId: this.runId
|
|
@@ -86,6 +100,7 @@ var InngestRun = class extends workflows.Run {
|
|
|
86
100
|
await this.#mastra?.storage?.persistWorkflowSnapshot({
|
|
87
101
|
workflowName: this.workflowId,
|
|
88
102
|
runId: this.runId,
|
|
103
|
+
resourceId: this.resourceId,
|
|
89
104
|
snapshot: {
|
|
90
105
|
...snapshot,
|
|
91
106
|
status: "canceled"
|
|
@@ -94,11 +109,13 @@ var InngestRun = class extends workflows.Run {
|
|
|
94
109
|
}
|
|
95
110
|
}
|
|
96
111
|
async start({
|
|
97
|
-
inputData
|
|
112
|
+
inputData,
|
|
113
|
+
initialState
|
|
98
114
|
}) {
|
|
99
115
|
await this.#mastra.getStorage()?.persistWorkflowSnapshot({
|
|
100
116
|
workflowName: this.workflowId,
|
|
101
117
|
runId: this.runId,
|
|
118
|
+
resourceId: this.resourceId,
|
|
102
119
|
snapshot: {
|
|
103
120
|
runId: this.runId,
|
|
104
121
|
serializedStepGraph: this.serializedStepGraph,
|
|
@@ -106,15 +123,20 @@ var InngestRun = class extends workflows.Run {
|
|
|
106
123
|
context: {},
|
|
107
124
|
activePaths: [],
|
|
108
125
|
suspendedPaths: {},
|
|
126
|
+
waitingPaths: {},
|
|
109
127
|
timestamp: Date.now(),
|
|
110
128
|
status: "running"
|
|
111
129
|
}
|
|
112
130
|
});
|
|
131
|
+
const inputDataToUse = await this._validateInput(inputData);
|
|
132
|
+
const initialStateToUse = await this._validateInitialState(initialState ?? {});
|
|
113
133
|
const eventOutput = await this.inngest.send({
|
|
114
134
|
name: `workflow.${this.workflowId}`,
|
|
115
135
|
data: {
|
|
116
|
-
inputData,
|
|
117
|
-
|
|
136
|
+
inputData: inputDataToUse,
|
|
137
|
+
initialState: initialStateToUse,
|
|
138
|
+
runId: this.runId,
|
|
139
|
+
resourceId: this.resourceId
|
|
118
140
|
}
|
|
119
141
|
});
|
|
120
142
|
const eventId = eventOutput.ids[0];
|
|
@@ -150,16 +172,20 @@ var InngestRun = class extends workflows.Run {
|
|
|
150
172
|
workflowName: this.workflowId,
|
|
151
173
|
runId: this.runId
|
|
152
174
|
});
|
|
175
|
+
const suspendedStep = this.workflowSteps[steps?.[0] ?? ""];
|
|
176
|
+
const resumeDataToUse = await this._validateResumeData(params.resumeData, suspendedStep);
|
|
153
177
|
const eventOutput = await this.inngest.send({
|
|
154
178
|
name: `workflow.${this.workflowId}`,
|
|
155
179
|
data: {
|
|
156
|
-
inputData:
|
|
180
|
+
inputData: resumeDataToUse,
|
|
181
|
+
initialState: snapshot?.value ?? {},
|
|
157
182
|
runId: this.runId,
|
|
183
|
+
workflowId: this.workflowId,
|
|
158
184
|
stepResults: snapshot?.context,
|
|
159
185
|
resume: {
|
|
160
186
|
steps,
|
|
161
187
|
stepResults: snapshot?.context,
|
|
162
|
-
resumePayload:
|
|
188
|
+
resumePayload: resumeDataToUse,
|
|
163
189
|
// @ts-ignore
|
|
164
190
|
resumePath: snapshot?.suspendedPaths?.[steps?.[0]]
|
|
165
191
|
}
|
|
@@ -204,7 +230,11 @@ var InngestRun = class extends workflows.Run {
|
|
|
204
230
|
const writer = writable.getWriter();
|
|
205
231
|
const unwatch = this.watch(async (event) => {
|
|
206
232
|
try {
|
|
207
|
-
|
|
233
|
+
const e = {
|
|
234
|
+
...event,
|
|
235
|
+
type: event.type.replace("workflow-", "")
|
|
236
|
+
};
|
|
237
|
+
await writer.write(e);
|
|
208
238
|
} catch {
|
|
209
239
|
}
|
|
210
240
|
}, "watch-v2");
|
|
@@ -235,8 +265,14 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
|
|
|
235
265
|
#mastra;
|
|
236
266
|
inngest;
|
|
237
267
|
function;
|
|
268
|
+
flowControlConfig;
|
|
238
269
|
constructor(params, inngest) {
|
|
239
|
-
|
|
270
|
+
const { concurrency, rateLimit, throttle, debounce, priority, ...workflowParams } = params;
|
|
271
|
+
super(workflowParams);
|
|
272
|
+
const flowControlEntries = Object.entries({ concurrency, rateLimit, throttle, debounce, priority }).filter(
|
|
273
|
+
([_, value]) => value !== void 0
|
|
274
|
+
);
|
|
275
|
+
this.flowControlConfig = flowControlEntries.length > 0 ? Object.fromEntries(flowControlEntries) : void 0;
|
|
240
276
|
this.#mastra = params.mastra;
|
|
241
277
|
this.inngest = inngest;
|
|
242
278
|
}
|
|
@@ -257,27 +293,6 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
|
|
|
257
293
|
const run = await storage.getWorkflowRunById({ runId, workflowName: this.id });
|
|
258
294
|
return run ?? (this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null);
|
|
259
295
|
}
|
|
260
|
-
async getWorkflowRunExecutionResult(runId) {
|
|
261
|
-
const storage = this.#mastra?.getStorage();
|
|
262
|
-
if (!storage) {
|
|
263
|
-
this.logger.debug("Cannot get workflow run execution result. Mastra storage is not initialized");
|
|
264
|
-
return null;
|
|
265
|
-
}
|
|
266
|
-
const run = await storage.getWorkflowRunById({ runId, workflowName: this.id });
|
|
267
|
-
if (!run?.snapshot) {
|
|
268
|
-
return null;
|
|
269
|
-
}
|
|
270
|
-
if (typeof run.snapshot === "string") {
|
|
271
|
-
return null;
|
|
272
|
-
}
|
|
273
|
-
return {
|
|
274
|
-
status: run.snapshot.status,
|
|
275
|
-
result: run.snapshot.result,
|
|
276
|
-
error: run.snapshot.error,
|
|
277
|
-
payload: run.snapshot.context?.input,
|
|
278
|
-
steps: run.snapshot.context
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
296
|
__registerMastra(mastra) {
|
|
282
297
|
this.#mastra = mastra;
|
|
283
298
|
this.executionEngine.__registerMastra(mastra);
|
|
@@ -296,23 +311,14 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
|
|
|
296
311
|
}
|
|
297
312
|
}
|
|
298
313
|
}
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
executionGraph: this.executionGraph,
|
|
307
|
-
serializedStepGraph: this.serializedStepGraph,
|
|
308
|
-
mastra: this.#mastra,
|
|
309
|
-
retryConfig: this.retryConfig,
|
|
310
|
-
cleanup: () => this.runs.delete(runIdToUse)
|
|
311
|
-
},
|
|
312
|
-
this.inngest
|
|
314
|
+
/**
|
|
315
|
+
* @deprecated Use createRunAsync() instead.
|
|
316
|
+
* @throws {Error} Always throws an error directing users to use createRunAsync()
|
|
317
|
+
*/
|
|
318
|
+
createRun(_options) {
|
|
319
|
+
throw new Error(
|
|
320
|
+
"createRun() has been deprecated. Please use createRunAsync() instead.\n\nMigration guide:\n Before: const run = workflow.createRun();\n After: const run = await workflow.createRunAsync();\n\nNote: createRunAsync() is an async method, so make sure your calling function is async."
|
|
313
321
|
);
|
|
314
|
-
this.runs.set(runIdToUse, run);
|
|
315
|
-
return run;
|
|
316
322
|
}
|
|
317
323
|
async createRunAsync(options) {
|
|
318
324
|
const runIdToUse = options?.runId || crypto.randomUUID();
|
|
@@ -320,27 +326,35 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
|
|
|
320
326
|
{
|
|
321
327
|
workflowId: this.id,
|
|
322
328
|
runId: runIdToUse,
|
|
329
|
+
resourceId: options?.resourceId,
|
|
323
330
|
executionEngine: this.executionEngine,
|
|
324
331
|
executionGraph: this.executionGraph,
|
|
325
332
|
serializedStepGraph: this.serializedStepGraph,
|
|
326
333
|
mastra: this.#mastra,
|
|
327
334
|
retryConfig: this.retryConfig,
|
|
328
|
-
cleanup: () => this.runs.delete(runIdToUse)
|
|
335
|
+
cleanup: () => this.runs.delete(runIdToUse),
|
|
336
|
+
workflowSteps: this.steps
|
|
329
337
|
},
|
|
330
338
|
this.inngest
|
|
331
339
|
);
|
|
332
340
|
this.runs.set(runIdToUse, run);
|
|
333
|
-
const
|
|
334
|
-
|
|
341
|
+
const shouldPersistSnapshot = this.options.shouldPersistSnapshot({
|
|
342
|
+
workflowStatus: run.workflowRunStatus,
|
|
343
|
+
stepResults: {}
|
|
344
|
+
});
|
|
345
|
+
const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse, false);
|
|
346
|
+
if (!workflowSnapshotInStorage && shouldPersistSnapshot) {
|
|
335
347
|
await this.mastra?.getStorage()?.persistWorkflowSnapshot({
|
|
336
348
|
workflowName: this.id,
|
|
337
349
|
runId: runIdToUse,
|
|
350
|
+
resourceId: options?.resourceId,
|
|
338
351
|
snapshot: {
|
|
339
352
|
runId: runIdToUse,
|
|
340
353
|
status: "pending",
|
|
341
354
|
value: {},
|
|
342
355
|
context: {},
|
|
343
356
|
activePaths: [],
|
|
357
|
+
waitingPaths: {},
|
|
344
358
|
serializedStepGraph: this.serializedStepGraph,
|
|
345
359
|
suspendedPaths: {},
|
|
346
360
|
result: void 0,
|
|
@@ -361,11 +375,13 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
|
|
|
361
375
|
id: `workflow.${this.id}`,
|
|
362
376
|
// @ts-ignore
|
|
363
377
|
retries: this.retryConfig?.attempts ?? 0,
|
|
364
|
-
cancelOn: [{ event: `cancel.workflow.${this.id}` }]
|
|
378
|
+
cancelOn: [{ event: `cancel.workflow.${this.id}` }],
|
|
379
|
+
// Spread flow control configuration
|
|
380
|
+
...this.flowControlConfig
|
|
365
381
|
},
|
|
366
382
|
{ event: `workflow.${this.id}` },
|
|
367
383
|
async ({ event, step, attempt, publish }) => {
|
|
368
|
-
let { inputData, runId, resume } = event.data;
|
|
384
|
+
let { inputData, initialState, runId, resourceId, resume, outputOptions } = event.data;
|
|
369
385
|
if (!runId) {
|
|
370
386
|
runId = await step.run(`workflow.${this.id}.runIdGen`, async () => {
|
|
371
387
|
return crypto.randomUUID();
|
|
@@ -393,19 +409,32 @@ var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
|
|
|
393
409
|
once: (_event, _callback) => {
|
|
394
410
|
}
|
|
395
411
|
};
|
|
396
|
-
const engine = new InngestExecutionEngine(this.#mastra, step, attempt);
|
|
412
|
+
const engine = new InngestExecutionEngine(this.#mastra, step, attempt, this.options);
|
|
397
413
|
const result = await engine.execute({
|
|
398
414
|
workflowId: this.id,
|
|
399
415
|
runId,
|
|
416
|
+
resourceId,
|
|
400
417
|
graph: this.executionGraph,
|
|
401
418
|
serializedStepGraph: this.serializedStepGraph,
|
|
402
419
|
input: inputData,
|
|
420
|
+
initialState,
|
|
403
421
|
emitter,
|
|
404
422
|
retryConfig: this.retryConfig,
|
|
405
423
|
runtimeContext: new di.RuntimeContext(),
|
|
406
424
|
// TODO
|
|
407
425
|
resume,
|
|
408
|
-
abortController: new AbortController()
|
|
426
|
+
abortController: new AbortController(),
|
|
427
|
+
currentSpan: void 0,
|
|
428
|
+
// TODO: Pass actual parent AI span from workflow execution context
|
|
429
|
+
outputOptions
|
|
430
|
+
});
|
|
431
|
+
await step.run(`workflow.${this.id}.finalize`, async () => {
|
|
432
|
+
if (result.status === "failed") {
|
|
433
|
+
throw new inngest.NonRetriableError(`Workflow failed`, {
|
|
434
|
+
cause: result
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
return result;
|
|
409
438
|
});
|
|
410
439
|
return { result, runId };
|
|
411
440
|
}
|
|
@@ -439,17 +468,16 @@ function createStep(params) {
|
|
|
439
468
|
if (isAgent(params)) {
|
|
440
469
|
return {
|
|
441
470
|
id: params.name,
|
|
471
|
+
description: params.getDescription(),
|
|
442
472
|
// @ts-ignore
|
|
443
473
|
inputSchema: zod.z.object({
|
|
444
474
|
prompt: zod.z.string()
|
|
445
|
-
// resourceId: z.string().optional(),
|
|
446
|
-
// threadId: z.string().optional(),
|
|
447
475
|
}),
|
|
448
476
|
// @ts-ignore
|
|
449
477
|
outputSchema: zod.z.object({
|
|
450
478
|
text: zod.z.string()
|
|
451
479
|
}),
|
|
452
|
-
execute: async ({ inputData, [_constants.EMITTER_SYMBOL]: emitter, runtimeContext, abortSignal, abort }) => {
|
|
480
|
+
execute: async ({ inputData, [_constants.EMITTER_SYMBOL]: emitter, runtimeContext, abortSignal, abort, tracingContext }) => {
|
|
453
481
|
let streamPromise = {};
|
|
454
482
|
streamPromise.promise = new Promise((resolve, reject) => {
|
|
455
483
|
streamPromise.resolve = resolve;
|
|
@@ -459,50 +487,66 @@ function createStep(params) {
|
|
|
459
487
|
name: params.name,
|
|
460
488
|
args: inputData
|
|
461
489
|
};
|
|
462
|
-
await
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
490
|
+
if ((await params.getLLM()).getModel().specificationVersion === `v2`) {
|
|
491
|
+
const { fullStream } = await params.stream(inputData.prompt, {
|
|
492
|
+
runtimeContext,
|
|
493
|
+
tracingContext,
|
|
494
|
+
onFinish: (result) => {
|
|
495
|
+
streamPromise.resolve(result.text);
|
|
496
|
+
},
|
|
497
|
+
abortSignal
|
|
498
|
+
});
|
|
499
|
+
if (abortSignal.aborted) {
|
|
500
|
+
return abort();
|
|
501
|
+
}
|
|
502
|
+
await emitter.emit("watch-v2", {
|
|
503
|
+
type: "tool-call-streaming-start",
|
|
504
|
+
...toolData ?? {}
|
|
505
|
+
});
|
|
506
|
+
for await (const chunk of fullStream) {
|
|
507
|
+
if (chunk.type === "text-delta") {
|
|
508
|
+
await emitter.emit("watch-v2", {
|
|
509
|
+
type: "tool-call-delta",
|
|
510
|
+
...toolData ?? {},
|
|
511
|
+
argsTextDelta: chunk.payload.text
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
} else {
|
|
516
|
+
const { fullStream } = await params.streamLegacy(inputData.prompt, {
|
|
517
|
+
runtimeContext,
|
|
518
|
+
tracingContext,
|
|
519
|
+
onFinish: (result) => {
|
|
520
|
+
streamPromise.resolve(result.text);
|
|
521
|
+
},
|
|
522
|
+
abortSignal
|
|
523
|
+
});
|
|
524
|
+
if (abortSignal.aborted) {
|
|
525
|
+
return abort();
|
|
526
|
+
}
|
|
527
|
+
await emitter.emit("watch-v2", {
|
|
528
|
+
type: "tool-call-streaming-start",
|
|
529
|
+
...toolData ?? {}
|
|
530
|
+
});
|
|
531
|
+
for await (const chunk of fullStream) {
|
|
532
|
+
if (chunk.type === "text-delta") {
|
|
481
533
|
await emitter.emit("watch-v2", {
|
|
482
534
|
type: "tool-call-delta",
|
|
483
|
-
...toolData,
|
|
535
|
+
...toolData ?? {},
|
|
484
536
|
argsTextDelta: chunk.textDelta
|
|
485
537
|
});
|
|
486
|
-
|
|
487
|
-
case "step-start":
|
|
488
|
-
case "step-finish":
|
|
489
|
-
case "finish":
|
|
490
|
-
break;
|
|
491
|
-
case "tool-call":
|
|
492
|
-
case "tool-result":
|
|
493
|
-
case "tool-call-streaming-start":
|
|
494
|
-
case "tool-call-delta":
|
|
495
|
-
case "source":
|
|
496
|
-
case "file":
|
|
497
|
-
default:
|
|
498
|
-
await emitter.emit("watch-v2", chunk);
|
|
499
|
-
break;
|
|
538
|
+
}
|
|
500
539
|
}
|
|
501
540
|
}
|
|
541
|
+
await emitter.emit("watch-v2", {
|
|
542
|
+
type: "tool-call-streaming-finish",
|
|
543
|
+
...toolData ?? {}
|
|
544
|
+
});
|
|
502
545
|
return {
|
|
503
546
|
text: await streamPromise.promise
|
|
504
547
|
};
|
|
505
|
-
}
|
|
548
|
+
},
|
|
549
|
+
component: params.component
|
|
506
550
|
};
|
|
507
551
|
}
|
|
508
552
|
if (isTool(params)) {
|
|
@@ -513,15 +557,20 @@ function createStep(params) {
|
|
|
513
557
|
// TODO: tool probably should have strong id type
|
|
514
558
|
// @ts-ignore
|
|
515
559
|
id: params.id,
|
|
560
|
+
description: params.description,
|
|
516
561
|
inputSchema: params.inputSchema,
|
|
517
562
|
outputSchema: params.outputSchema,
|
|
518
|
-
execute: async ({ inputData, mastra, runtimeContext }) => {
|
|
563
|
+
execute: async ({ inputData, mastra, runtimeContext, tracingContext, suspend, resumeData }) => {
|
|
519
564
|
return params.execute({
|
|
520
565
|
context: inputData,
|
|
521
|
-
mastra,
|
|
522
|
-
runtimeContext
|
|
566
|
+
mastra: aiTracing.wrapMastra(mastra, tracingContext),
|
|
567
|
+
runtimeContext,
|
|
568
|
+
tracingContext,
|
|
569
|
+
suspend,
|
|
570
|
+
resumeData
|
|
523
571
|
});
|
|
524
|
-
}
|
|
572
|
+
},
|
|
573
|
+
component: "TOOL"
|
|
525
574
|
};
|
|
526
575
|
}
|
|
527
576
|
return {
|
|
@@ -537,7 +586,10 @@ function createStep(params) {
|
|
|
537
586
|
function init(inngest) {
|
|
538
587
|
return {
|
|
539
588
|
createWorkflow(params) {
|
|
540
|
-
return new InngestWorkflow(
|
|
589
|
+
return new InngestWorkflow(
|
|
590
|
+
params,
|
|
591
|
+
inngest
|
|
592
|
+
);
|
|
541
593
|
},
|
|
542
594
|
createStep,
|
|
543
595
|
cloneStep(step, opts) {
|
|
@@ -546,7 +598,11 @@ function init(inngest) {
|
|
|
546
598
|
description: step.description,
|
|
547
599
|
inputSchema: step.inputSchema,
|
|
548
600
|
outputSchema: step.outputSchema,
|
|
549
|
-
|
|
601
|
+
resumeSchema: step.resumeSchema,
|
|
602
|
+
suspendSchema: step.suspendSchema,
|
|
603
|
+
stateSchema: step.stateSchema,
|
|
604
|
+
execute: step.execute,
|
|
605
|
+
component: step.component
|
|
550
606
|
};
|
|
551
607
|
},
|
|
552
608
|
cloneWorkflow(workflow, opts) {
|
|
@@ -566,19 +622,19 @@ function init(inngest) {
|
|
|
566
622
|
var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
567
623
|
inngestStep;
|
|
568
624
|
inngestAttempts;
|
|
569
|
-
constructor(mastra, inngestStep, inngestAttempts = 0) {
|
|
570
|
-
super({ mastra });
|
|
625
|
+
constructor(mastra, inngestStep, inngestAttempts = 0, options) {
|
|
626
|
+
super({ mastra, options });
|
|
571
627
|
this.inngestStep = inngestStep;
|
|
572
628
|
this.inngestAttempts = inngestAttempts;
|
|
573
629
|
}
|
|
574
630
|
async execute(params) {
|
|
575
631
|
await params.emitter.emit("watch-v2", {
|
|
576
|
-
type: "start",
|
|
632
|
+
type: "workflow-start",
|
|
577
633
|
payload: { runId: params.runId }
|
|
578
634
|
});
|
|
579
635
|
const result = await super.execute(params);
|
|
580
636
|
await params.emitter.emit("watch-v2", {
|
|
581
|
-
type: "finish",
|
|
637
|
+
type: "workflow-finish",
|
|
582
638
|
payload: { runId: params.runId }
|
|
583
639
|
});
|
|
584
640
|
return result;
|
|
@@ -630,7 +686,7 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
630
686
|
});
|
|
631
687
|
const suspendedStepIds = Object.entries(stepResults).flatMap(([stepId, stepResult]) => {
|
|
632
688
|
if (stepResult?.status === "suspended") {
|
|
633
|
-
const nestedPath = stepResult?.
|
|
689
|
+
const nestedPath = stepResult?.suspendPayload?.__workflow_meta?.path;
|
|
634
690
|
return nestedPath ? [[stepId, ...nestedPath]] : [[stepId]];
|
|
635
691
|
}
|
|
636
692
|
return [];
|
|
@@ -640,31 +696,6 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
640
696
|
executionSpan?.end();
|
|
641
697
|
return base;
|
|
642
698
|
}
|
|
643
|
-
async superExecuteStep({
|
|
644
|
-
workflowId,
|
|
645
|
-
runId,
|
|
646
|
-
step,
|
|
647
|
-
stepResults,
|
|
648
|
-
executionContext,
|
|
649
|
-
resume,
|
|
650
|
-
prevOutput,
|
|
651
|
-
emitter,
|
|
652
|
-
abortController,
|
|
653
|
-
runtimeContext
|
|
654
|
-
}) {
|
|
655
|
-
return super.executeStep({
|
|
656
|
-
workflowId,
|
|
657
|
-
runId,
|
|
658
|
-
step,
|
|
659
|
-
stepResults,
|
|
660
|
-
executionContext,
|
|
661
|
-
resume,
|
|
662
|
-
prevOutput,
|
|
663
|
-
emitter,
|
|
664
|
-
abortController,
|
|
665
|
-
runtimeContext
|
|
666
|
-
});
|
|
667
|
-
}
|
|
668
699
|
// async executeSleep({ id, duration }: { id: string; duration: number }): Promise<void> {
|
|
669
700
|
// await this.inngestStep.sleep(id, duration);
|
|
670
701
|
// }
|
|
@@ -676,28 +707,40 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
676
707
|
stepResults,
|
|
677
708
|
emitter,
|
|
678
709
|
abortController,
|
|
679
|
-
runtimeContext
|
|
710
|
+
runtimeContext,
|
|
711
|
+
executionContext,
|
|
712
|
+
writableStream,
|
|
713
|
+
tracingContext
|
|
680
714
|
}) {
|
|
681
715
|
let { duration, fn } = entry;
|
|
716
|
+
const sleepSpan = tracingContext?.currentSpan?.createChildSpan({
|
|
717
|
+
type: aiTracing.AISpanType.WORKFLOW_SLEEP,
|
|
718
|
+
name: `sleep: ${duration ? `${duration}ms` : "dynamic"}`,
|
|
719
|
+
attributes: {
|
|
720
|
+
durationMs: duration,
|
|
721
|
+
sleepType: fn ? "dynamic" : "fixed"
|
|
722
|
+
},
|
|
723
|
+
tracingPolicy: this.options?.tracingPolicy
|
|
724
|
+
});
|
|
682
725
|
if (fn) {
|
|
726
|
+
const stepCallId = crypto.randomUUID();
|
|
683
727
|
duration = await this.inngestStep.run(`workflow.${workflowId}.sleep.${entry.id}`, async () => {
|
|
684
728
|
return await fn({
|
|
685
729
|
runId,
|
|
730
|
+
workflowId,
|
|
686
731
|
mastra: this.mastra,
|
|
687
732
|
runtimeContext,
|
|
688
733
|
inputData: prevOutput,
|
|
734
|
+
state: executionContext.state,
|
|
735
|
+
setState: (state) => {
|
|
736
|
+
executionContext.state = state;
|
|
737
|
+
},
|
|
689
738
|
runCount: -1,
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
if (!step?.id) {
|
|
693
|
-
return null;
|
|
694
|
-
}
|
|
695
|
-
const result = stepResults[step.id];
|
|
696
|
-
if (result?.status === "success") {
|
|
697
|
-
return result.output;
|
|
698
|
-
}
|
|
699
|
-
return null;
|
|
739
|
+
tracingContext: {
|
|
740
|
+
currentSpan: sleepSpan
|
|
700
741
|
},
|
|
742
|
+
getInitData: () => stepResults?.input,
|
|
743
|
+
getStepResult: workflows.getStepResult.bind(this, stepResults),
|
|
701
744
|
// TODO: this function shouldn't have suspend probably?
|
|
702
745
|
suspend: async (_suspendPayload) => {
|
|
703
746
|
},
|
|
@@ -707,12 +750,34 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
707
750
|
abortController?.abort();
|
|
708
751
|
},
|
|
709
752
|
[_constants.EMITTER_SYMBOL]: emitter,
|
|
753
|
+
// TODO: add streamVNext support
|
|
754
|
+
[_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
|
|
710
755
|
engine: { step: this.inngestStep },
|
|
711
|
-
abortSignal: abortController?.signal
|
|
756
|
+
abortSignal: abortController?.signal,
|
|
757
|
+
writer: new tools.ToolStream(
|
|
758
|
+
{
|
|
759
|
+
prefix: "workflow-step",
|
|
760
|
+
callId: stepCallId,
|
|
761
|
+
name: "sleep",
|
|
762
|
+
runId
|
|
763
|
+
},
|
|
764
|
+
writableStream
|
|
765
|
+
)
|
|
712
766
|
});
|
|
713
767
|
});
|
|
768
|
+
sleepSpan?.update({
|
|
769
|
+
attributes: {
|
|
770
|
+
durationMs: duration
|
|
771
|
+
}
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
try {
|
|
775
|
+
await this.inngestStep.sleep(entry.id, !duration || duration < 0 ? 0 : duration);
|
|
776
|
+
sleepSpan?.end();
|
|
777
|
+
} catch (e) {
|
|
778
|
+
sleepSpan?.error({ error: e });
|
|
779
|
+
throw e;
|
|
714
780
|
}
|
|
715
|
-
await this.inngestStep.sleep(entry.id, !duration || duration < 0 ? 0 : duration);
|
|
716
781
|
}
|
|
717
782
|
async executeSleepUntil({
|
|
718
783
|
workflowId,
|
|
@@ -722,28 +787,41 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
722
787
|
stepResults,
|
|
723
788
|
emitter,
|
|
724
789
|
abortController,
|
|
725
|
-
runtimeContext
|
|
790
|
+
runtimeContext,
|
|
791
|
+
executionContext,
|
|
792
|
+
writableStream,
|
|
793
|
+
tracingContext
|
|
726
794
|
}) {
|
|
727
795
|
let { date, fn } = entry;
|
|
796
|
+
const sleepUntilSpan = tracingContext?.currentSpan?.createChildSpan({
|
|
797
|
+
type: aiTracing.AISpanType.WORKFLOW_SLEEP,
|
|
798
|
+
name: `sleepUntil: ${date ? date.toISOString() : "dynamic"}`,
|
|
799
|
+
attributes: {
|
|
800
|
+
untilDate: date,
|
|
801
|
+
durationMs: date ? Math.max(0, date.getTime() - Date.now()) : void 0,
|
|
802
|
+
sleepType: fn ? "dynamic" : "fixed"
|
|
803
|
+
},
|
|
804
|
+
tracingPolicy: this.options?.tracingPolicy
|
|
805
|
+
});
|
|
728
806
|
if (fn) {
|
|
729
807
|
date = await this.inngestStep.run(`workflow.${workflowId}.sleepUntil.${entry.id}`, async () => {
|
|
808
|
+
const stepCallId = crypto.randomUUID();
|
|
730
809
|
return await fn({
|
|
731
810
|
runId,
|
|
811
|
+
workflowId,
|
|
732
812
|
mastra: this.mastra,
|
|
733
813
|
runtimeContext,
|
|
734
814
|
inputData: prevOutput,
|
|
815
|
+
state: executionContext.state,
|
|
816
|
+
setState: (state) => {
|
|
817
|
+
executionContext.state = state;
|
|
818
|
+
},
|
|
735
819
|
runCount: -1,
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
if (!step?.id) {
|
|
739
|
-
return null;
|
|
740
|
-
}
|
|
741
|
-
const result = stepResults[step.id];
|
|
742
|
-
if (result?.status === "success") {
|
|
743
|
-
return result.output;
|
|
744
|
-
}
|
|
745
|
-
return null;
|
|
820
|
+
tracingContext: {
|
|
821
|
+
currentSpan: sleepUntilSpan
|
|
746
822
|
},
|
|
823
|
+
getInitData: () => stepResults?.input,
|
|
824
|
+
getStepResult: workflows.getStepResult.bind(this, stepResults),
|
|
747
825
|
// TODO: this function shouldn't have suspend probably?
|
|
748
826
|
suspend: async (_suspendPayload) => {
|
|
749
827
|
},
|
|
@@ -753,15 +831,42 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
753
831
|
abortController?.abort();
|
|
754
832
|
},
|
|
755
833
|
[_constants.EMITTER_SYMBOL]: emitter,
|
|
834
|
+
[_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
|
|
835
|
+
// TODO: add streamVNext support
|
|
756
836
|
engine: { step: this.inngestStep },
|
|
757
|
-
abortSignal: abortController?.signal
|
|
837
|
+
abortSignal: abortController?.signal,
|
|
838
|
+
writer: new tools.ToolStream(
|
|
839
|
+
{
|
|
840
|
+
prefix: "workflow-step",
|
|
841
|
+
callId: stepCallId,
|
|
842
|
+
name: "sleep",
|
|
843
|
+
runId
|
|
844
|
+
},
|
|
845
|
+
writableStream
|
|
846
|
+
)
|
|
758
847
|
});
|
|
759
848
|
});
|
|
849
|
+
if (date && !(date instanceof Date)) {
|
|
850
|
+
date = new Date(date);
|
|
851
|
+
}
|
|
852
|
+
const time = !date ? 0 : date.getTime() - Date.now();
|
|
853
|
+
sleepUntilSpan?.update({
|
|
854
|
+
attributes: {
|
|
855
|
+
durationMs: Math.max(0, time)
|
|
856
|
+
}
|
|
857
|
+
});
|
|
760
858
|
}
|
|
761
859
|
if (!(date instanceof Date)) {
|
|
860
|
+
sleepUntilSpan?.end();
|
|
762
861
|
return;
|
|
763
862
|
}
|
|
764
|
-
|
|
863
|
+
try {
|
|
864
|
+
await this.inngestStep.sleepUntil(entry.id, date);
|
|
865
|
+
sleepUntilSpan?.end();
|
|
866
|
+
} catch (e) {
|
|
867
|
+
sleepUntilSpan?.error({ error: e });
|
|
868
|
+
throw e;
|
|
869
|
+
}
|
|
765
870
|
}
|
|
766
871
|
async executeWaitForEvent({ event, timeout }) {
|
|
767
872
|
const eventData = await this.inngestStep.waitForEvent(`user-event-${event}`, {
|
|
@@ -781,8 +886,25 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
781
886
|
prevOutput,
|
|
782
887
|
emitter,
|
|
783
888
|
abortController,
|
|
784
|
-
runtimeContext
|
|
889
|
+
runtimeContext,
|
|
890
|
+
tracingContext,
|
|
891
|
+
writableStream,
|
|
892
|
+
disableScorers
|
|
785
893
|
}) {
|
|
894
|
+
const stepAISpan = tracingContext?.currentSpan?.createChildSpan({
|
|
895
|
+
name: `workflow step: '${step.id}'`,
|
|
896
|
+
type: aiTracing.AISpanType.WORKFLOW_STEP,
|
|
897
|
+
input: prevOutput,
|
|
898
|
+
attributes: {
|
|
899
|
+
stepId: step.id
|
|
900
|
+
},
|
|
901
|
+
tracingPolicy: this.options?.tracingPolicy
|
|
902
|
+
});
|
|
903
|
+
const { inputData, validationError } = await workflows.validateStepInput({
|
|
904
|
+
prevOutput,
|
|
905
|
+
step,
|
|
906
|
+
validateInputs: this.options?.validateInputs ?? false
|
|
907
|
+
});
|
|
786
908
|
const startedAt = await this.inngestStep.run(
|
|
787
909
|
`workflow.${executionContext.workflowId}.run.${executionContext.runId}.step.${step.id}.running_ev`,
|
|
788
910
|
async () => {
|
|
@@ -809,11 +931,11 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
809
931
|
eventTimestamp: Date.now()
|
|
810
932
|
});
|
|
811
933
|
await emitter.emit("watch-v2", {
|
|
812
|
-
type: "step-start",
|
|
934
|
+
type: "workflow-step-start",
|
|
813
935
|
payload: {
|
|
814
936
|
id: step.id,
|
|
815
937
|
status: "running",
|
|
816
|
-
payload:
|
|
938
|
+
payload: inputData,
|
|
817
939
|
startedAt: startedAt2
|
|
818
940
|
}
|
|
819
941
|
});
|
|
@@ -824,38 +946,60 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
824
946
|
const isResume = !!resume?.steps?.length;
|
|
825
947
|
let result;
|
|
826
948
|
let runId;
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
949
|
+
try {
|
|
950
|
+
if (isResume) {
|
|
951
|
+
runId = stepResults[resume?.steps?.[0]]?.suspendPayload?.__workflow_meta?.runId ?? crypto.randomUUID();
|
|
952
|
+
const snapshot = await this.mastra?.getStorage()?.loadWorkflowSnapshot({
|
|
953
|
+
workflowName: step.id,
|
|
954
|
+
runId
|
|
955
|
+
});
|
|
956
|
+
const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
|
|
957
|
+
function: step.getFunction(),
|
|
958
|
+
data: {
|
|
959
|
+
inputData,
|
|
960
|
+
initialState: executionContext.state ?? snapshot?.value ?? {},
|
|
839
961
|
runId,
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
962
|
+
resume: {
|
|
963
|
+
runId,
|
|
964
|
+
steps: resume.steps.slice(1),
|
|
965
|
+
stepResults: snapshot?.context,
|
|
966
|
+
resumePayload: resume.resumePayload,
|
|
967
|
+
// @ts-ignore
|
|
968
|
+
resumePath: snapshot?.suspendedPaths?.[resume.steps?.[1]]
|
|
969
|
+
},
|
|
970
|
+
outputOptions: { includeState: true }
|
|
845
971
|
}
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
972
|
+
});
|
|
973
|
+
result = invokeResp.result;
|
|
974
|
+
runId = invokeResp.runId;
|
|
975
|
+
executionContext.state = invokeResp.result.state;
|
|
976
|
+
} else {
|
|
977
|
+
const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
|
|
978
|
+
function: step.getFunction(),
|
|
979
|
+
data: {
|
|
980
|
+
inputData,
|
|
981
|
+
initialState: executionContext.state ?? {},
|
|
982
|
+
outputOptions: { includeState: true }
|
|
983
|
+
}
|
|
984
|
+
});
|
|
985
|
+
result = invokeResp.result;
|
|
986
|
+
runId = invokeResp.runId;
|
|
987
|
+
executionContext.state = invokeResp.result.state;
|
|
988
|
+
}
|
|
989
|
+
} catch (e) {
|
|
990
|
+
const errorCause = e?.cause;
|
|
991
|
+
if (errorCause && typeof errorCause === "object") {
|
|
992
|
+
result = errorCause;
|
|
993
|
+
runId = errorCause.runId || crypto.randomUUID();
|
|
994
|
+
} else {
|
|
995
|
+
runId = crypto.randomUUID();
|
|
996
|
+
result = {
|
|
997
|
+
status: "failed",
|
|
998
|
+
error: e instanceof Error ? e : new Error(String(e)),
|
|
999
|
+
steps: {},
|
|
1000
|
+
input: inputData
|
|
1001
|
+
};
|
|
1002
|
+
}
|
|
859
1003
|
}
|
|
860
1004
|
const res = await this.inngestStep.run(
|
|
861
1005
|
`workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
|
|
@@ -879,7 +1023,7 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
879
1023
|
eventTimestamp: Date.now()
|
|
880
1024
|
});
|
|
881
1025
|
await emitter.emit("watch-v2", {
|
|
882
|
-
type: "step-result",
|
|
1026
|
+
type: "workflow-step-result",
|
|
883
1027
|
payload: {
|
|
884
1028
|
id: step.id,
|
|
885
1029
|
status: "failed",
|
|
@@ -894,7 +1038,7 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
894
1038
|
return stepRes2?.status === "suspended";
|
|
895
1039
|
});
|
|
896
1040
|
for (const [stepName, stepResult] of suspendedSteps) {
|
|
897
|
-
const suspendPath = [stepName, ...stepResult?.
|
|
1041
|
+
const suspendPath = [stepName, ...stepResult?.suspendPayload?.__workflow_meta?.path ?? []];
|
|
898
1042
|
executionContext.suspendedPaths[step.id] = executionContext.executionPath;
|
|
899
1043
|
await emitter.emit("watch", {
|
|
900
1044
|
type: "watch",
|
|
@@ -902,7 +1046,11 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
902
1046
|
currentStep: {
|
|
903
1047
|
id: step.id,
|
|
904
1048
|
status: "suspended",
|
|
905
|
-
payload:
|
|
1049
|
+
payload: stepResult.payload,
|
|
1050
|
+
suspendPayload: {
|
|
1051
|
+
...stepResult?.suspendPayload,
|
|
1052
|
+
__workflow_meta: { runId, path: suspendPath }
|
|
1053
|
+
}
|
|
906
1054
|
},
|
|
907
1055
|
workflowState: {
|
|
908
1056
|
status: "running",
|
|
@@ -914,7 +1062,7 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
914
1062
|
eventTimestamp: Date.now()
|
|
915
1063
|
});
|
|
916
1064
|
await emitter.emit("watch-v2", {
|
|
917
|
-
type: "step-suspended",
|
|
1065
|
+
type: "workflow-step-suspended",
|
|
918
1066
|
payload: {
|
|
919
1067
|
id: step.id,
|
|
920
1068
|
status: "suspended"
|
|
@@ -924,7 +1072,11 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
924
1072
|
executionContext,
|
|
925
1073
|
result: {
|
|
926
1074
|
status: "suspended",
|
|
927
|
-
payload:
|
|
1075
|
+
payload: stepResult.payload,
|
|
1076
|
+
suspendPayload: {
|
|
1077
|
+
...stepResult?.suspendPayload,
|
|
1078
|
+
__workflow_meta: { runId, path: suspendPath }
|
|
1079
|
+
}
|
|
928
1080
|
}
|
|
929
1081
|
};
|
|
930
1082
|
}
|
|
@@ -971,7 +1123,7 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
971
1123
|
eventTimestamp: Date.now()
|
|
972
1124
|
});
|
|
973
1125
|
await emitter.emit("watch-v2", {
|
|
974
|
-
type: "step-result",
|
|
1126
|
+
type: "workflow-step-result",
|
|
975
1127
|
payload: {
|
|
976
1128
|
id: step.id,
|
|
977
1129
|
status: "success",
|
|
@@ -979,7 +1131,7 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
979
1131
|
}
|
|
980
1132
|
});
|
|
981
1133
|
await emitter.emit("watch-v2", {
|
|
982
|
-
type: "step-finish",
|
|
1134
|
+
type: "workflow-step-finish",
|
|
983
1135
|
payload: {
|
|
984
1136
|
id: step.id,
|
|
985
1137
|
metadata: {}
|
|
@@ -989,135 +1141,193 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
989
1141
|
}
|
|
990
1142
|
);
|
|
991
1143
|
Object.assign(executionContext, res.executionContext);
|
|
992
|
-
return
|
|
1144
|
+
return {
|
|
1145
|
+
...res.result,
|
|
1146
|
+
startedAt,
|
|
1147
|
+
endedAt: Date.now(),
|
|
1148
|
+
payload: inputData,
|
|
1149
|
+
resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
|
|
1150
|
+
resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
|
|
1151
|
+
};
|
|
993
1152
|
}
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1153
|
+
let stepRes;
|
|
1154
|
+
try {
|
|
1155
|
+
stepRes = await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}`, async () => {
|
|
1156
|
+
let execResults;
|
|
1157
|
+
let suspended;
|
|
1158
|
+
let bailed;
|
|
1159
|
+
try {
|
|
1160
|
+
if (validationError) {
|
|
1161
|
+
throw validationError;
|
|
1162
|
+
}
|
|
1163
|
+
const result = await step.execute({
|
|
1164
|
+
runId: executionContext.runId,
|
|
1165
|
+
mastra: this.mastra,
|
|
1166
|
+
runtimeContext,
|
|
1167
|
+
writableStream,
|
|
1168
|
+
state: executionContext?.state ?? {},
|
|
1169
|
+
setState: (state) => {
|
|
1170
|
+
executionContext.state = state;
|
|
1171
|
+
},
|
|
1172
|
+
inputData,
|
|
1173
|
+
resumeData: resume?.steps[0] === step.id ? resume?.resumePayload : void 0,
|
|
1174
|
+
tracingContext: {
|
|
1175
|
+
currentSpan: stepAISpan
|
|
1176
|
+
},
|
|
1177
|
+
getInitData: () => stepResults?.input,
|
|
1178
|
+
getStepResult: workflows.getStepResult.bind(this, stepResults),
|
|
1179
|
+
suspend: async (suspendPayload) => {
|
|
1180
|
+
executionContext.suspendedPaths[step.id] = executionContext.executionPath;
|
|
1181
|
+
suspended = { payload: suspendPayload };
|
|
1182
|
+
},
|
|
1183
|
+
bail: (result2) => {
|
|
1184
|
+
bailed = { payload: result2 };
|
|
1185
|
+
},
|
|
1186
|
+
resume: {
|
|
1187
|
+
steps: resume?.steps?.slice(1) || [],
|
|
1188
|
+
resumePayload: resume?.resumePayload,
|
|
1189
|
+
// @ts-ignore
|
|
1190
|
+
runId: stepResults[step.id]?.suspendPayload?.__workflow_meta?.runId
|
|
1191
|
+
},
|
|
1192
|
+
[_constants.EMITTER_SYMBOL]: emitter,
|
|
1193
|
+
engine: {
|
|
1194
|
+
step: this.inngestStep
|
|
1195
|
+
},
|
|
1196
|
+
abortSignal: abortController.signal
|
|
1197
|
+
});
|
|
1198
|
+
const endedAt = Date.now();
|
|
1199
|
+
execResults = {
|
|
1200
|
+
status: "success",
|
|
1201
|
+
output: result,
|
|
1202
|
+
startedAt,
|
|
1203
|
+
endedAt,
|
|
1204
|
+
payload: inputData,
|
|
1205
|
+
resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
|
|
1206
|
+
resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
|
|
1207
|
+
};
|
|
1208
|
+
} catch (e) {
|
|
1209
|
+
const stepFailure = {
|
|
1210
|
+
status: "failed",
|
|
1211
|
+
payload: inputData,
|
|
1212
|
+
error: e instanceof Error ? e.message : String(e),
|
|
1213
|
+
endedAt: Date.now(),
|
|
1214
|
+
startedAt,
|
|
1215
|
+
resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
|
|
1216
|
+
resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
|
|
1217
|
+
};
|
|
1218
|
+
execResults = stepFailure;
|
|
1219
|
+
const fallbackErrorMessage = `Step ${step.id} failed`;
|
|
1220
|
+
stepAISpan?.error({ error: new Error(execResults.error ?? fallbackErrorMessage) });
|
|
1221
|
+
throw new inngest.RetryAfterError(execResults.error ?? fallbackErrorMessage, executionContext.retryConfig.delay, {
|
|
1222
|
+
cause: execResults
|
|
1223
|
+
});
|
|
1224
|
+
}
|
|
1225
|
+
if (suspended) {
|
|
1226
|
+
execResults = {
|
|
1227
|
+
status: "suspended",
|
|
1228
|
+
suspendPayload: suspended.payload,
|
|
1229
|
+
payload: inputData,
|
|
1230
|
+
suspendedAt: Date.now(),
|
|
1231
|
+
startedAt,
|
|
1232
|
+
resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
|
|
1233
|
+
resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
|
|
1234
|
+
};
|
|
1235
|
+
} else if (bailed) {
|
|
1236
|
+
execResults = {
|
|
1237
|
+
status: "bailed",
|
|
1238
|
+
output: bailed.payload,
|
|
1239
|
+
payload: inputData,
|
|
1240
|
+
endedAt: Date.now(),
|
|
1241
|
+
startedAt
|
|
1242
|
+
};
|
|
1243
|
+
}
|
|
1244
|
+
await emitter.emit("watch", {
|
|
1245
|
+
type: "watch",
|
|
1246
|
+
payload: {
|
|
1247
|
+
currentStep: {
|
|
1248
|
+
id: step.id,
|
|
1249
|
+
...execResults
|
|
1250
|
+
},
|
|
1251
|
+
workflowState: {
|
|
1252
|
+
status: "running",
|
|
1253
|
+
steps: { ...stepResults, [step.id]: execResults },
|
|
1254
|
+
result: null,
|
|
1255
|
+
error: null
|
|
1010
1256
|
}
|
|
1011
|
-
return null;
|
|
1012
|
-
},
|
|
1013
|
-
suspend: async (suspendPayload) => {
|
|
1014
|
-
executionContext.suspendedPaths[step.id] = executionContext.executionPath;
|
|
1015
|
-
suspended = { payload: suspendPayload };
|
|
1016
|
-
},
|
|
1017
|
-
bail: (result2) => {
|
|
1018
|
-
bailed = { payload: result2 };
|
|
1019
|
-
},
|
|
1020
|
-
resume: {
|
|
1021
|
-
steps: resume?.steps?.slice(1) || [],
|
|
1022
|
-
resumePayload: resume?.resumePayload,
|
|
1023
|
-
// @ts-ignore
|
|
1024
|
-
runId: stepResults[step.id]?.payload?.__workflow_meta?.runId
|
|
1025
|
-
},
|
|
1026
|
-
[_constants.EMITTER_SYMBOL]: emitter,
|
|
1027
|
-
engine: {
|
|
1028
|
-
step: this.inngestStep
|
|
1029
1257
|
},
|
|
1030
|
-
|
|
1258
|
+
eventTimestamp: Date.now()
|
|
1031
1259
|
});
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
status: "suspended",
|
|
1056
|
-
suspendedPayload: suspended.payload,
|
|
1057
|
-
payload: prevOutput,
|
|
1058
|
-
suspendedAt: Date.now(),
|
|
1059
|
-
startedAt,
|
|
1060
|
-
resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
|
|
1061
|
-
resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
|
|
1062
|
-
};
|
|
1063
|
-
} else if (bailed) {
|
|
1064
|
-
execResults = { status: "bailed", output: bailed.payload, payload: prevOutput, endedAt: Date.now(), startedAt };
|
|
1065
|
-
}
|
|
1066
|
-
if (execResults.status === "failed") {
|
|
1067
|
-
if (executionContext.retryConfig.attempts > 0 && this.inngestAttempts < executionContext.retryConfig.attempts) {
|
|
1068
|
-
throw execResults.error;
|
|
1260
|
+
if (execResults.status === "suspended") {
|
|
1261
|
+
await emitter.emit("watch-v2", {
|
|
1262
|
+
type: "workflow-step-suspended",
|
|
1263
|
+
payload: {
|
|
1264
|
+
id: step.id,
|
|
1265
|
+
...execResults
|
|
1266
|
+
}
|
|
1267
|
+
});
|
|
1268
|
+
} else {
|
|
1269
|
+
await emitter.emit("watch-v2", {
|
|
1270
|
+
type: "workflow-step-result",
|
|
1271
|
+
payload: {
|
|
1272
|
+
id: step.id,
|
|
1273
|
+
...execResults
|
|
1274
|
+
}
|
|
1275
|
+
});
|
|
1276
|
+
await emitter.emit("watch-v2", {
|
|
1277
|
+
type: "workflow-step-finish",
|
|
1278
|
+
payload: {
|
|
1279
|
+
id: step.id,
|
|
1280
|
+
metadata: {}
|
|
1281
|
+
}
|
|
1282
|
+
});
|
|
1069
1283
|
}
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
type: "watch",
|
|
1073
|
-
payload: {
|
|
1074
|
-
currentStep: {
|
|
1075
|
-
id: step.id,
|
|
1076
|
-
...execResults
|
|
1077
|
-
},
|
|
1078
|
-
workflowState: {
|
|
1079
|
-
status: "running",
|
|
1080
|
-
steps: { ...stepResults, [step.id]: execResults },
|
|
1081
|
-
result: null,
|
|
1082
|
-
error: null
|
|
1083
|
-
}
|
|
1084
|
-
},
|
|
1085
|
-
eventTimestamp: Date.now()
|
|
1284
|
+
stepAISpan?.end({ output: execResults });
|
|
1285
|
+
return { result: execResults, executionContext, stepResults };
|
|
1086
1286
|
});
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1287
|
+
} catch (e) {
|
|
1288
|
+
const stepFailure = e instanceof Error ? e?.cause : {
|
|
1289
|
+
status: "failed",
|
|
1290
|
+
error: e instanceof Error ? e.message : String(e),
|
|
1291
|
+
payload: inputData,
|
|
1292
|
+
startedAt,
|
|
1293
|
+
endedAt: Date.now()
|
|
1294
|
+
};
|
|
1295
|
+
stepRes = {
|
|
1296
|
+
result: stepFailure,
|
|
1297
|
+
executionContext,
|
|
1298
|
+
stepResults: {
|
|
1299
|
+
...stepResults,
|
|
1300
|
+
[step.id]: stepFailure
|
|
1301
|
+
}
|
|
1302
|
+
};
|
|
1303
|
+
}
|
|
1304
|
+
if (disableScorers !== false && stepRes.result.status === "success") {
|
|
1305
|
+
await this.inngestStep.run(`workflow.${executionContext.workflowId}.step.${step.id}.score`, async () => {
|
|
1306
|
+
if (step.scorers) {
|
|
1307
|
+
await this.runScorers({
|
|
1308
|
+
scorers: step.scorers,
|
|
1309
|
+
runId: executionContext.runId,
|
|
1310
|
+
input: inputData,
|
|
1311
|
+
output: stepRes.result,
|
|
1312
|
+
workflowId: executionContext.workflowId,
|
|
1313
|
+
stepId: step.id,
|
|
1314
|
+
runtimeContext,
|
|
1315
|
+
disableScorers,
|
|
1316
|
+
tracingContext: { currentSpan: stepAISpan }
|
|
1317
|
+
});
|
|
1318
|
+
}
|
|
1319
|
+
});
|
|
1320
|
+
}
|
|
1113
1321
|
Object.assign(executionContext.suspendedPaths, stepRes.executionContext.suspendedPaths);
|
|
1114
1322
|
Object.assign(stepResults, stepRes.stepResults);
|
|
1323
|
+
executionContext.state = stepRes.executionContext.state;
|
|
1115
1324
|
return stepRes.result;
|
|
1116
1325
|
}
|
|
1117
1326
|
async persistStepUpdate({
|
|
1118
1327
|
workflowId,
|
|
1119
1328
|
runId,
|
|
1120
1329
|
stepResults,
|
|
1330
|
+
resourceId,
|
|
1121
1331
|
executionContext,
|
|
1122
1332
|
serializedStepGraph,
|
|
1123
1333
|
workflowStatus,
|
|
@@ -1127,15 +1337,21 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
1127
1337
|
await this.inngestStep.run(
|
|
1128
1338
|
`workflow.${workflowId}.run.${runId}.path.${JSON.stringify(executionContext.executionPath)}.stepUpdate`,
|
|
1129
1339
|
async () => {
|
|
1340
|
+
const shouldPersistSnapshot = this.options.shouldPersistSnapshot({ stepResults, workflowStatus });
|
|
1341
|
+
if (!shouldPersistSnapshot) {
|
|
1342
|
+
return;
|
|
1343
|
+
}
|
|
1130
1344
|
await this.mastra?.getStorage()?.persistWorkflowSnapshot({
|
|
1131
1345
|
workflowName: workflowId,
|
|
1132
1346
|
runId,
|
|
1347
|
+
resourceId,
|
|
1133
1348
|
snapshot: {
|
|
1134
1349
|
runId,
|
|
1135
|
-
value:
|
|
1350
|
+
value: executionContext.state,
|
|
1136
1351
|
context: stepResults,
|
|
1137
1352
|
activePaths: [],
|
|
1138
1353
|
suspendedPaths: executionContext.suspendedPaths,
|
|
1354
|
+
waitingPaths: {},
|
|
1139
1355
|
serializedStepGraph,
|
|
1140
1356
|
status: workflowStatus,
|
|
1141
1357
|
result,
|
|
@@ -1159,30 +1375,50 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
1159
1375
|
executionContext,
|
|
1160
1376
|
emitter,
|
|
1161
1377
|
abortController,
|
|
1162
|
-
runtimeContext
|
|
1378
|
+
runtimeContext,
|
|
1379
|
+
writableStream,
|
|
1380
|
+
disableScorers,
|
|
1381
|
+
tracingContext
|
|
1163
1382
|
}) {
|
|
1383
|
+
const conditionalSpan = tracingContext?.currentSpan?.createChildSpan({
|
|
1384
|
+
type: aiTracing.AISpanType.WORKFLOW_CONDITIONAL,
|
|
1385
|
+
name: `conditional: '${entry.conditions.length} conditions'`,
|
|
1386
|
+
input: prevOutput,
|
|
1387
|
+
attributes: {
|
|
1388
|
+
conditionCount: entry.conditions.length
|
|
1389
|
+
},
|
|
1390
|
+
tracingPolicy: this.options?.tracingPolicy
|
|
1391
|
+
});
|
|
1164
1392
|
let execResults;
|
|
1165
1393
|
const truthyIndexes = (await Promise.all(
|
|
1166
1394
|
entry.conditions.map(
|
|
1167
1395
|
(cond, index) => this.inngestStep.run(`workflow.${workflowId}.conditional.${index}`, async () => {
|
|
1396
|
+
const evalSpan = conditionalSpan?.createChildSpan({
|
|
1397
|
+
type: aiTracing.AISpanType.WORKFLOW_CONDITIONAL_EVAL,
|
|
1398
|
+
name: `condition: '${index}'`,
|
|
1399
|
+
input: prevOutput,
|
|
1400
|
+
attributes: {
|
|
1401
|
+
conditionIndex: index
|
|
1402
|
+
},
|
|
1403
|
+
tracingPolicy: this.options?.tracingPolicy
|
|
1404
|
+
});
|
|
1168
1405
|
try {
|
|
1169
1406
|
const result = await cond({
|
|
1170
1407
|
runId,
|
|
1408
|
+
workflowId,
|
|
1171
1409
|
mastra: this.mastra,
|
|
1172
1410
|
runtimeContext,
|
|
1173
1411
|
runCount: -1,
|
|
1174
1412
|
inputData: prevOutput,
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
if (result2?.status === "success") {
|
|
1182
|
-
return result2.output;
|
|
1183
|
-
}
|
|
1184
|
-
return null;
|
|
1413
|
+
state: executionContext.state,
|
|
1414
|
+
setState: (state) => {
|
|
1415
|
+
executionContext.state = state;
|
|
1416
|
+
},
|
|
1417
|
+
tracingContext: {
|
|
1418
|
+
currentSpan: evalSpan
|
|
1185
1419
|
},
|
|
1420
|
+
getInitData: () => stepResults?.input,
|
|
1421
|
+
getStepResult: workflows.getStepResult.bind(this, stepResults),
|
|
1186
1422
|
// TODO: this function shouldn't have suspend probably?
|
|
1187
1423
|
suspend: async (_suspendPayload) => {
|
|
1188
1424
|
},
|
|
@@ -1192,40 +1428,75 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
1192
1428
|
abortController.abort();
|
|
1193
1429
|
},
|
|
1194
1430
|
[_constants.EMITTER_SYMBOL]: emitter,
|
|
1431
|
+
[_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
|
|
1432
|
+
// TODO: add streamVNext support
|
|
1195
1433
|
engine: {
|
|
1196
1434
|
step: this.inngestStep
|
|
1197
1435
|
},
|
|
1198
|
-
abortSignal: abortController.signal
|
|
1436
|
+
abortSignal: abortController.signal,
|
|
1437
|
+
writer: new tools.ToolStream(
|
|
1438
|
+
{
|
|
1439
|
+
prefix: "workflow-step",
|
|
1440
|
+
callId: crypto.randomUUID(),
|
|
1441
|
+
name: "conditional",
|
|
1442
|
+
runId
|
|
1443
|
+
},
|
|
1444
|
+
writableStream
|
|
1445
|
+
)
|
|
1446
|
+
});
|
|
1447
|
+
evalSpan?.end({
|
|
1448
|
+
output: result,
|
|
1449
|
+
attributes: {
|
|
1450
|
+
result: !!result
|
|
1451
|
+
}
|
|
1199
1452
|
});
|
|
1200
1453
|
return result ? index : null;
|
|
1201
1454
|
} catch (e) {
|
|
1455
|
+
evalSpan?.error({
|
|
1456
|
+
error: e instanceof Error ? e : new Error(String(e)),
|
|
1457
|
+
attributes: {
|
|
1458
|
+
result: false
|
|
1459
|
+
}
|
|
1460
|
+
});
|
|
1202
1461
|
return null;
|
|
1203
1462
|
}
|
|
1204
1463
|
})
|
|
1205
1464
|
)
|
|
1206
1465
|
)).filter((index) => index !== null);
|
|
1207
1466
|
const stepsToRun = entry.steps.filter((_, index) => truthyIndexes.includes(index));
|
|
1467
|
+
conditionalSpan?.update({
|
|
1468
|
+
attributes: {
|
|
1469
|
+
truthyIndexes,
|
|
1470
|
+
selectedSteps: stepsToRun.map((s) => s.type === "step" ? s.step.id : `control-${s.type}`)
|
|
1471
|
+
}
|
|
1472
|
+
});
|
|
1208
1473
|
const results = await Promise.all(
|
|
1209
1474
|
stepsToRun.map(
|
|
1210
1475
|
(step, index) => this.executeEntry({
|
|
1211
1476
|
workflowId,
|
|
1212
1477
|
runId,
|
|
1213
1478
|
entry: step,
|
|
1479
|
+
serializedStepGraph,
|
|
1214
1480
|
prevStep,
|
|
1215
1481
|
stepResults,
|
|
1216
1482
|
resume,
|
|
1217
|
-
serializedStepGraph,
|
|
1218
1483
|
executionContext: {
|
|
1219
1484
|
workflowId,
|
|
1220
1485
|
runId,
|
|
1221
1486
|
executionPath: [...executionContext.executionPath, index],
|
|
1222
1487
|
suspendedPaths: executionContext.suspendedPaths,
|
|
1223
1488
|
retryConfig: executionContext.retryConfig,
|
|
1224
|
-
executionSpan: executionContext.executionSpan
|
|
1489
|
+
executionSpan: executionContext.executionSpan,
|
|
1490
|
+
state: executionContext.state
|
|
1225
1491
|
},
|
|
1226
1492
|
emitter,
|
|
1227
1493
|
abortController,
|
|
1228
|
-
runtimeContext
|
|
1494
|
+
runtimeContext,
|
|
1495
|
+
writableStream,
|
|
1496
|
+
disableScorers,
|
|
1497
|
+
tracingContext: {
|
|
1498
|
+
currentSpan: conditionalSpan
|
|
1499
|
+
}
|
|
1229
1500
|
})
|
|
1230
1501
|
)
|
|
1231
1502
|
);
|
|
@@ -1234,7 +1505,7 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
1234
1505
|
if (hasFailed) {
|
|
1235
1506
|
execResults = { status: "failed", error: hasFailed.result.error };
|
|
1236
1507
|
} else if (hasSuspended) {
|
|
1237
|
-
execResults = { status: "suspended",
|
|
1508
|
+
execResults = { status: "suspended", suspendPayload: hasSuspended.result.suspendPayload };
|
|
1238
1509
|
} else {
|
|
1239
1510
|
execResults = {
|
|
1240
1511
|
status: "success",
|
|
@@ -1246,6 +1517,15 @@ var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
|
1246
1517
|
}, {})
|
|
1247
1518
|
};
|
|
1248
1519
|
}
|
|
1520
|
+
if (execResults.status === "failed") {
|
|
1521
|
+
conditionalSpan?.error({
|
|
1522
|
+
error: new Error(execResults.error)
|
|
1523
|
+
});
|
|
1524
|
+
} else {
|
|
1525
|
+
conditionalSpan?.end({
|
|
1526
|
+
output: execResults.output || execResults
|
|
1527
|
+
});
|
|
1528
|
+
}
|
|
1249
1529
|
return execResults;
|
|
1250
1530
|
}
|
|
1251
1531
|
};
|
|
@@ -1256,3 +1536,5 @@ exports.InngestWorkflow = InngestWorkflow;
|
|
|
1256
1536
|
exports.createStep = createStep;
|
|
1257
1537
|
exports.init = init;
|
|
1258
1538
|
exports.serve = serve;
|
|
1539
|
+
//# sourceMappingURL=index.cjs.map
|
|
1540
|
+
//# sourceMappingURL=index.cjs.map
|