@mastra/inngest 0.0.0-sidebar-window-undefined-fix-20251029233656 → 0.0.0-standard-schema-20260123120255
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 +1641 -3
- package/dist/__tests__/adapters/_utils.d.ts +18 -0
- package/dist/__tests__/adapters/_utils.d.ts.map +1 -0
- package/dist/execution-engine.d.ts +206 -0
- package/dist/execution-engine.d.ts.map +1 -0
- package/dist/index.cjs +2113 -1490
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +83 -305
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2112 -1492
- package/dist/index.js.map +1 -1
- package/dist/pubsub.d.ts +56 -0
- package/dist/pubsub.d.ts.map +1 -0
- package/dist/run.d.ts +175 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/serve.d.ts +76 -0
- package/dist/serve.d.ts.map +1 -0
- package/dist/types.d.ts +16 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/workflow.d.ts +52 -0
- package/dist/workflow.d.ts.map +1 -0
- package/package.json +34 -17
package/dist/index.cjs
CHANGED
|
@@ -1,1634 +1,2257 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
var
|
|
7
|
-
var
|
|
8
|
-
var stream = require('@mastra/core/stream');
|
|
3
|
+
var agent = require('@mastra/core/agent');
|
|
4
|
+
var error = require('@mastra/core/error');
|
|
5
|
+
var observability = require('@mastra/core/observability');
|
|
6
|
+
var processors = require('@mastra/core/processors');
|
|
7
|
+
var schema = require('@mastra/core/schema');
|
|
9
8
|
var tools = require('@mastra/core/tools');
|
|
10
9
|
var workflows = require('@mastra/core/workflows');
|
|
11
10
|
var _constants = require('@mastra/core/workflows/_constants');
|
|
11
|
+
var zod = require('zod');
|
|
12
|
+
var crypto$1 = require('crypto');
|
|
13
|
+
var di = require('@mastra/core/di');
|
|
12
14
|
var inngest = require('inngest');
|
|
15
|
+
var realtime = require('@inngest/realtime');
|
|
16
|
+
var events = require('@mastra/core/events');
|
|
17
|
+
var web = require('stream/web');
|
|
18
|
+
var stream = require('@mastra/core/stream');
|
|
13
19
|
var hono = require('inngest/hono');
|
|
14
|
-
var zod = require('zod');
|
|
15
20
|
|
|
16
21
|
// src/index.ts
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const workflowFunctions = Array.from(
|
|
25
|
-
new Set(
|
|
26
|
-
Object.values(wfs).flatMap((wf) => {
|
|
27
|
-
if (wf instanceof InngestWorkflow) {
|
|
28
|
-
wf.__registerMastra(mastra);
|
|
29
|
-
return wf.getFunctions();
|
|
30
|
-
}
|
|
31
|
-
return [];
|
|
32
|
-
})
|
|
33
|
-
)
|
|
34
|
-
);
|
|
35
|
-
return hono.serve({
|
|
36
|
-
...registerOptions,
|
|
37
|
-
client: inngest,
|
|
38
|
-
functions: [...workflowFunctions, ...userFunctions]
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
var InngestRun = class extends workflows.Run {
|
|
42
|
-
inngest;
|
|
43
|
-
serializedStepGraph;
|
|
44
|
-
#mastra;
|
|
45
|
-
constructor(params, inngest) {
|
|
46
|
-
super(params);
|
|
47
|
-
this.inngest = inngest;
|
|
48
|
-
this.serializedStepGraph = params.serializedStepGraph;
|
|
49
|
-
this.#mastra = params.mastra;
|
|
22
|
+
var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
23
|
+
inngestStep;
|
|
24
|
+
inngestAttempts;
|
|
25
|
+
constructor(mastra, inngestStep, inngestAttempts = 0, options) {
|
|
26
|
+
super({ mastra, options });
|
|
27
|
+
this.inngestStep = inngestStep;
|
|
28
|
+
this.inngestAttempts = inngestAttempts;
|
|
50
29
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
30
|
+
// =============================================================================
|
|
31
|
+
// Hook Overrides
|
|
32
|
+
// =============================================================================
|
|
33
|
+
/**
|
|
34
|
+
* Format errors while preserving Error instances and their custom properties.
|
|
35
|
+
* Uses getErrorFromUnknown to ensure all error properties are preserved.
|
|
36
|
+
*/
|
|
37
|
+
formatResultError(error$1, lastOutput) {
|
|
38
|
+
const outputError = lastOutput?.error;
|
|
39
|
+
const errorSource = error$1 || outputError;
|
|
40
|
+
const errorInstance = error.getErrorFromUnknown(errorSource, {
|
|
41
|
+
serializeStack: true,
|
|
42
|
+
// Include stack in JSON for better debugging in Inngest
|
|
43
|
+
fallbackMessage: "Unknown workflow error"
|
|
56
44
|
});
|
|
57
|
-
|
|
58
|
-
return json.data;
|
|
45
|
+
return errorInstance.toJSON();
|
|
59
46
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Detect InngestWorkflow instances for special nested workflow handling
|
|
49
|
+
*/
|
|
50
|
+
isNestedWorkflowStep(step) {
|
|
51
|
+
return step instanceof InngestWorkflow;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Inngest requires requestContext serialization for memoization.
|
|
55
|
+
* When steps are replayed, the original function doesn't re-execute,
|
|
56
|
+
* so requestContext modifications must be captured and restored.
|
|
57
|
+
*/
|
|
58
|
+
requiresDurableContextSerialization() {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Execute a step with retry logic for Inngest.
|
|
63
|
+
* Retries are handled via step-level retry (RetryAfterError thrown INSIDE step.run()).
|
|
64
|
+
* After retries exhausted, error propagates here and we return a failed result.
|
|
65
|
+
*/
|
|
66
|
+
async executeStepWithRetry(stepId, runStep, params) {
|
|
67
|
+
for (let i = 0; i < params.retries + 1; i++) {
|
|
68
|
+
if (i > 0 && params.delay) {
|
|
69
|
+
await new Promise((resolve) => setTimeout(resolve, params.delay));
|
|
73
70
|
}
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
71
|
+
try {
|
|
72
|
+
const result = await this.wrapDurableOperation(stepId, runStep);
|
|
73
|
+
return { ok: true, result };
|
|
74
|
+
} catch (e) {
|
|
75
|
+
if (i === params.retries) {
|
|
76
|
+
const cause = e?.cause;
|
|
77
|
+
if (cause?.status === "failed") {
|
|
78
|
+
params.stepSpan?.error({
|
|
79
|
+
error: e,
|
|
80
|
+
attributes: { status: "failed" }
|
|
81
|
+
});
|
|
82
|
+
if (cause.error && !(cause.error instanceof Error)) {
|
|
83
|
+
cause.error = error.getErrorFromUnknown(cause.error, { serializeStack: false });
|
|
84
|
+
}
|
|
85
|
+
return { ok: false, error: cause };
|
|
86
|
+
}
|
|
87
|
+
const errorInstance = error.getErrorFromUnknown(e, {
|
|
88
|
+
serializeStack: false,
|
|
89
|
+
fallbackMessage: "Unknown step execution error"
|
|
90
|
+
});
|
|
91
|
+
params.stepSpan?.error({
|
|
92
|
+
error: errorInstance,
|
|
93
|
+
attributes: { status: "failed" }
|
|
94
|
+
});
|
|
95
|
+
return {
|
|
96
|
+
ok: false,
|
|
97
|
+
error: {
|
|
98
|
+
status: "failed",
|
|
99
|
+
error: errorInstance,
|
|
100
|
+
endedAt: Date.now()
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
80
104
|
}
|
|
81
105
|
}
|
|
82
|
-
return
|
|
106
|
+
return { ok: false, error: { status: "failed", error: new Error("Unknown error"), endedAt: Date.now() } };
|
|
83
107
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
});
|
|
108
|
+
/**
|
|
109
|
+
* Use Inngest's sleep primitive for durability
|
|
110
|
+
*/
|
|
111
|
+
async executeSleepDuration(duration, sleepId, workflowId) {
|
|
112
|
+
await this.inngestStep.sleep(`workflow.${workflowId}.sleep.${sleepId}`, duration < 0 ? 0 : duration);
|
|
89
113
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
114
|
+
/**
|
|
115
|
+
* Use Inngest's sleepUntil primitive for durability
|
|
116
|
+
*/
|
|
117
|
+
async executeSleepUntilDate(date, sleepUntilId, workflowId) {
|
|
118
|
+
await this.inngestStep.sleepUntil(`workflow.${workflowId}.sleepUntil.${sleepUntilId}`, date);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Wrap durable operations in Inngest step.run() for durability.
|
|
122
|
+
*
|
|
123
|
+
* IMPORTANT: Errors are wrapped with a cause structure before throwing.
|
|
124
|
+
* This is necessary because Inngest's error serialization (serialize-error-cjs)
|
|
125
|
+
* only captures standard Error properties (message, name, stack, code, cause).
|
|
126
|
+
* Custom properties like statusCode, responseHeaders from AI SDK errors would
|
|
127
|
+
* be lost. By putting our serialized error (via getErrorFromUnknown with toJSON())
|
|
128
|
+
* in the cause property, we ensure custom properties survive serialization.
|
|
129
|
+
* The cause property is in serialize-error-cjs's allowlist, and when the cause
|
|
130
|
+
* object is finally JSON.stringify'd, our error's toJSON() is called.
|
|
131
|
+
*/
|
|
132
|
+
async wrapDurableOperation(operationId, operationFn) {
|
|
133
|
+
return this.inngestStep.run(operationId, async () => {
|
|
134
|
+
try {
|
|
135
|
+
return await operationFn();
|
|
136
|
+
} catch (e) {
|
|
137
|
+
const errorInstance = error.getErrorFromUnknown(e, {
|
|
138
|
+
serializeStack: false,
|
|
139
|
+
fallbackMessage: "Unknown step execution error"
|
|
140
|
+
});
|
|
141
|
+
throw new Error(errorInstance.message, {
|
|
142
|
+
cause: {
|
|
143
|
+
status: "failed",
|
|
144
|
+
error: errorInstance,
|
|
145
|
+
endedAt: Date.now()
|
|
146
|
+
}
|
|
147
|
+
});
|
|
95
148
|
}
|
|
96
149
|
});
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Provide Inngest step primitive in engine context
|
|
153
|
+
*/
|
|
154
|
+
getEngineContext() {
|
|
155
|
+
return { step: this.inngestStep };
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* For Inngest, lifecycle callbacks are invoked in the workflow's finalize step
|
|
159
|
+
* (wrapped in step.run for durability), not in execute(). Override to skip.
|
|
160
|
+
*/
|
|
161
|
+
async invokeLifecycleCallbacks(_result) {
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Actually invoke the lifecycle callbacks. Called from workflow.ts finalize step.
|
|
165
|
+
*/
|
|
166
|
+
async invokeLifecycleCallbacksInternal(result) {
|
|
167
|
+
return super.invokeLifecycleCallbacks(result);
|
|
168
|
+
}
|
|
169
|
+
// =============================================================================
|
|
170
|
+
// Durable Span Lifecycle Hooks
|
|
171
|
+
// =============================================================================
|
|
172
|
+
/**
|
|
173
|
+
* Create a step span durably - on first execution, creates and exports span.
|
|
174
|
+
* On replay, returns cached span data without re-creating.
|
|
175
|
+
*/
|
|
176
|
+
async createStepSpan(params) {
|
|
177
|
+
const { executionContext, operationId, options, parentSpan } = params;
|
|
178
|
+
const parentSpanId = parentSpan?.id ?? executionContext.tracingIds?.workflowSpanId;
|
|
179
|
+
const exportedSpan = await this.wrapDurableOperation(operationId, async () => {
|
|
180
|
+
const observability = this.mastra?.observability?.getSelectedInstance({});
|
|
181
|
+
if (!observability) return void 0;
|
|
182
|
+
const span = observability.startSpan({
|
|
183
|
+
...options,
|
|
184
|
+
entityType: options.entityType,
|
|
185
|
+
traceId: executionContext.tracingIds?.traceId,
|
|
186
|
+
parentSpanId
|
|
110
187
|
});
|
|
188
|
+
return span?.exportSpan();
|
|
189
|
+
});
|
|
190
|
+
if (exportedSpan) {
|
|
191
|
+
const observability = this.mastra?.observability?.getSelectedInstance({});
|
|
192
|
+
return observability?.rebuildSpan(exportedSpan);
|
|
111
193
|
}
|
|
194
|
+
return void 0;
|
|
112
195
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
snapshot: {
|
|
122
|
-
runId: this.runId,
|
|
123
|
-
serializedStepGraph: this.serializedStepGraph,
|
|
124
|
-
value: {},
|
|
125
|
-
context: {},
|
|
126
|
-
activePaths: [],
|
|
127
|
-
suspendedPaths: {},
|
|
128
|
-
resumeLabels: {},
|
|
129
|
-
waitingPaths: {},
|
|
130
|
-
timestamp: Date.now(),
|
|
131
|
-
status: "running"
|
|
132
|
-
}
|
|
196
|
+
/**
|
|
197
|
+
* End a step span durably.
|
|
198
|
+
*/
|
|
199
|
+
async endStepSpan(params) {
|
|
200
|
+
const { span, operationId, endOptions } = params;
|
|
201
|
+
if (!span) return;
|
|
202
|
+
await this.wrapDurableOperation(operationId, async () => {
|
|
203
|
+
span.end(endOptions);
|
|
133
204
|
});
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Record error on step span durably.
|
|
208
|
+
*/
|
|
209
|
+
async errorStepSpan(params) {
|
|
210
|
+
const { span, operationId, errorOptions } = params;
|
|
211
|
+
if (!span) return;
|
|
212
|
+
await this.wrapDurableOperation(operationId, async () => {
|
|
213
|
+
span.error(errorOptions);
|
|
144
214
|
});
|
|
145
|
-
const eventId = eventOutput.ids[0];
|
|
146
|
-
if (!eventId) {
|
|
147
|
-
throw new Error("Event ID is not set");
|
|
148
|
-
}
|
|
149
|
-
const runOutput = await this.getRunOutput(eventId);
|
|
150
|
-
const result = runOutput?.output?.result;
|
|
151
|
-
if (result.status === "failed") {
|
|
152
|
-
result.error = new Error(result.error);
|
|
153
|
-
}
|
|
154
|
-
if (result.status !== "suspended") {
|
|
155
|
-
this.cleanup?.();
|
|
156
|
-
}
|
|
157
|
-
return result;
|
|
158
215
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
216
|
+
/**
|
|
217
|
+
* Create a generic child span durably (for control-flow operations).
|
|
218
|
+
* On first execution, creates and exports span. On replay, returns cached span data.
|
|
219
|
+
*/
|
|
220
|
+
async createChildSpan(params) {
|
|
221
|
+
const { executionContext, operationId, options, parentSpan } = params;
|
|
222
|
+
const parentSpanId = parentSpan?.id ?? executionContext.tracingIds?.workflowSpanId;
|
|
223
|
+
const exportedSpan = await this.wrapDurableOperation(operationId, async () => {
|
|
224
|
+
const observability = this.mastra?.observability?.getSelectedInstance({});
|
|
225
|
+
if (!observability) return void 0;
|
|
226
|
+
const span = observability.startSpan({
|
|
227
|
+
...options,
|
|
228
|
+
traceId: executionContext.tracingIds?.traceId,
|
|
229
|
+
parentSpanId
|
|
230
|
+
});
|
|
231
|
+
return span?.exportSpan();
|
|
166
232
|
});
|
|
167
|
-
|
|
168
|
-
|
|
233
|
+
if (exportedSpan) {
|
|
234
|
+
const observability = this.mastra?.observability?.getSelectedInstance({});
|
|
235
|
+
return observability?.rebuildSpan(exportedSpan);
|
|
236
|
+
}
|
|
237
|
+
return void 0;
|
|
169
238
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
239
|
+
/**
|
|
240
|
+
* End a generic child span durably (for control-flow operations).
|
|
241
|
+
*/
|
|
242
|
+
async endChildSpan(params) {
|
|
243
|
+
const { span, operationId, endOptions } = params;
|
|
244
|
+
if (!span) return;
|
|
245
|
+
await this.wrapDurableOperation(operationId, async () => {
|
|
246
|
+
span.end(endOptions);
|
|
177
247
|
});
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
stepResults: snapshot?.context,
|
|
188
|
-
resume: {
|
|
189
|
-
steps,
|
|
190
|
-
stepResults: snapshot?.context,
|
|
191
|
-
resumePayload: resumeDataToUse,
|
|
192
|
-
// @ts-ignore
|
|
193
|
-
resumePath: snapshot?.suspendedPaths?.[steps?.[0]]
|
|
194
|
-
}
|
|
195
|
-
}
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Record error on a generic child span durably (for control-flow operations).
|
|
251
|
+
*/
|
|
252
|
+
async errorChildSpan(params) {
|
|
253
|
+
const { span, operationId, errorOptions } = params;
|
|
254
|
+
if (!span) return;
|
|
255
|
+
await this.wrapDurableOperation(operationId, async () => {
|
|
256
|
+
span.error(errorOptions);
|
|
196
257
|
});
|
|
197
|
-
const eventId = eventOutput.ids[0];
|
|
198
|
-
if (!eventId) {
|
|
199
|
-
throw new Error("Event ID is not set");
|
|
200
|
-
}
|
|
201
|
-
const runOutput = await this.getRunOutput(eventId);
|
|
202
|
-
const result = runOutput?.output?.result;
|
|
203
|
-
if (result.status === "failed") {
|
|
204
|
-
result.error = new Error(result.error);
|
|
205
|
-
}
|
|
206
|
-
return result;
|
|
207
258
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
259
|
+
/**
|
|
260
|
+
* Execute nested InngestWorkflow using inngestStep.invoke() for durability.
|
|
261
|
+
* This MUST be called directly (not inside step.run()) due to Inngest constraints.
|
|
262
|
+
*/
|
|
263
|
+
async executeWorkflowStep(params) {
|
|
264
|
+
if (!(params.step instanceof InngestWorkflow)) {
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
const {
|
|
268
|
+
step,
|
|
269
|
+
stepResults,
|
|
270
|
+
executionContext,
|
|
271
|
+
resume,
|
|
272
|
+
timeTravel,
|
|
273
|
+
prevOutput,
|
|
274
|
+
inputData,
|
|
275
|
+
pubsub,
|
|
276
|
+
startedAt,
|
|
277
|
+
perStep,
|
|
278
|
+
stepSpan
|
|
279
|
+
} = params;
|
|
280
|
+
const nestedTracingContext = executionContext.tracingIds?.traceId ? {
|
|
281
|
+
traceId: executionContext.tracingIds.traceId,
|
|
282
|
+
parentSpanId: stepSpan?.id
|
|
283
|
+
} : void 0;
|
|
284
|
+
const isResume = !!resume?.steps?.length;
|
|
285
|
+
let result;
|
|
286
|
+
let runId;
|
|
287
|
+
const isTimeTravel = !!(timeTravel && timeTravel.steps?.length > 1 && timeTravel.steps[0] === step.id);
|
|
288
|
+
try {
|
|
289
|
+
if (isResume) {
|
|
290
|
+
runId = stepResults[resume?.steps?.[0] ?? ""]?.suspendPayload?.__workflow_meta?.runId ?? crypto$1.randomUUID();
|
|
291
|
+
const workflowsStore = await this.mastra?.getStorage()?.getStore("workflows");
|
|
292
|
+
const snapshot = await workflowsStore?.loadWorkflowSnapshot({
|
|
293
|
+
workflowName: step.id,
|
|
294
|
+
runId
|
|
295
|
+
});
|
|
296
|
+
const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
|
|
297
|
+
function: step.getFunction(),
|
|
298
|
+
data: {
|
|
299
|
+
inputData,
|
|
300
|
+
initialState: executionContext.state ?? snapshot?.value ?? {},
|
|
301
|
+
runId,
|
|
302
|
+
resume: {
|
|
303
|
+
runId,
|
|
304
|
+
steps: resume.steps.slice(1),
|
|
305
|
+
stepResults: snapshot?.context,
|
|
306
|
+
resumePayload: resume.resumePayload,
|
|
307
|
+
resumePath: resume.steps?.[1] ? snapshot?.suspendedPaths?.[resume.steps?.[1]] : void 0
|
|
308
|
+
},
|
|
309
|
+
outputOptions: { includeState: true },
|
|
310
|
+
perStep,
|
|
311
|
+
tracingOptions: nestedTracingContext
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
result = invokeResp.result;
|
|
315
|
+
runId = invokeResp.runId;
|
|
316
|
+
executionContext.state = invokeResp.result.state;
|
|
317
|
+
} else if (isTimeTravel) {
|
|
318
|
+
const workflowsStoreForTimeTravel = await this.mastra?.getStorage()?.getStore("workflows");
|
|
319
|
+
const snapshot = await workflowsStoreForTimeTravel?.loadWorkflowSnapshot({
|
|
320
|
+
workflowName: step.id,
|
|
321
|
+
runId: executionContext.runId
|
|
322
|
+
}) ?? { context: {} };
|
|
323
|
+
const timeTravelParams = workflows.createTimeTravelExecutionParams({
|
|
324
|
+
steps: timeTravel.steps.slice(1),
|
|
325
|
+
inputData: timeTravel.inputData,
|
|
326
|
+
resumeData: timeTravel.resumeData,
|
|
327
|
+
context: timeTravel.nestedStepResults?.[step.id] ?? {},
|
|
328
|
+
nestedStepsContext: timeTravel.nestedStepResults ?? {},
|
|
329
|
+
snapshot,
|
|
330
|
+
graph: step.buildExecutionGraph()
|
|
331
|
+
});
|
|
332
|
+
const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
|
|
333
|
+
function: step.getFunction(),
|
|
334
|
+
data: {
|
|
335
|
+
timeTravel: timeTravelParams,
|
|
336
|
+
initialState: executionContext.state ?? {},
|
|
337
|
+
runId: executionContext.runId,
|
|
338
|
+
outputOptions: { includeState: true },
|
|
339
|
+
perStep,
|
|
340
|
+
tracingOptions: nestedTracingContext
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
result = invokeResp.result;
|
|
344
|
+
runId = invokeResp.runId;
|
|
345
|
+
executionContext.state = invokeResp.result.state;
|
|
346
|
+
} else {
|
|
347
|
+
const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
|
|
348
|
+
function: step.getFunction(),
|
|
349
|
+
data: {
|
|
350
|
+
inputData,
|
|
351
|
+
initialState: executionContext.state ?? {},
|
|
352
|
+
outputOptions: { includeState: true },
|
|
353
|
+
perStep,
|
|
354
|
+
tracingOptions: nestedTracingContext
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
result = invokeResp.result;
|
|
358
|
+
runId = invokeResp.runId;
|
|
359
|
+
executionContext.state = invokeResp.result.state;
|
|
220
360
|
}
|
|
221
|
-
)
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
const writer = writable.getWriter();
|
|
234
|
-
const unwatch = this.watch(async (event) => {
|
|
235
|
-
try {
|
|
236
|
-
const e = {
|
|
237
|
-
...event,
|
|
238
|
-
type: event.type.replace("workflow-", "")
|
|
361
|
+
} catch (e) {
|
|
362
|
+
const errorCause = e?.cause;
|
|
363
|
+
if (errorCause && typeof errorCause === "object") {
|
|
364
|
+
result = errorCause;
|
|
365
|
+
runId = errorCause.runId || crypto$1.randomUUID();
|
|
366
|
+
} else {
|
|
367
|
+
runId = crypto$1.randomUUID();
|
|
368
|
+
result = {
|
|
369
|
+
status: "failed",
|
|
370
|
+
error: e instanceof Error ? e : new Error(String(e)),
|
|
371
|
+
steps: {},
|
|
372
|
+
input: inputData
|
|
239
373
|
};
|
|
240
|
-
await writer.write(e);
|
|
241
|
-
} catch {
|
|
242
374
|
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
return result;
|
|
260
|
-
});
|
|
261
|
-
return {
|
|
262
|
-
stream: readable,
|
|
263
|
-
getWorkflowState: () => this.executionResults
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
stream({
|
|
267
|
-
inputData,
|
|
268
|
-
runtimeContext,
|
|
269
|
-
closeOnSuspend = true
|
|
270
|
-
} = {}) {
|
|
271
|
-
const self = this;
|
|
272
|
-
let streamOutput;
|
|
273
|
-
const stream$1 = new web.ReadableStream({
|
|
274
|
-
async start(controller) {
|
|
275
|
-
const unwatch = self.watch(async ({ type, from = stream.ChunkFrom.WORKFLOW, payload }) => {
|
|
276
|
-
controller.enqueue({
|
|
277
|
-
type,
|
|
278
|
-
runId: self.runId,
|
|
279
|
-
from,
|
|
280
|
-
payload: {
|
|
281
|
-
stepName: payload.id,
|
|
282
|
-
...payload
|
|
375
|
+
}
|
|
376
|
+
const res = await this.inngestStep.run(
|
|
377
|
+
`workflow.${executionContext.workflowId}.step.${step.id}.nestedwf-results`,
|
|
378
|
+
async () => {
|
|
379
|
+
if (result.status === "failed") {
|
|
380
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
381
|
+
type: "watch",
|
|
382
|
+
runId: executionContext.runId,
|
|
383
|
+
data: {
|
|
384
|
+
type: "workflow-step-result",
|
|
385
|
+
payload: {
|
|
386
|
+
id: step.id,
|
|
387
|
+
status: "failed",
|
|
388
|
+
error: result?.error,
|
|
389
|
+
payload: prevOutput
|
|
390
|
+
}
|
|
283
391
|
}
|
|
284
392
|
});
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
}
|
|
291
|
-
|
|
393
|
+
return { executionContext, result: { status: "failed", error: result?.error, endedAt: Date.now() } };
|
|
394
|
+
} else if (result.status === "suspended") {
|
|
395
|
+
const suspendedSteps = Object.entries(result.steps).filter(([_stepName, stepResult]) => {
|
|
396
|
+
const stepRes = stepResult;
|
|
397
|
+
return stepRes?.status === "suspended";
|
|
398
|
+
});
|
|
399
|
+
for (const [stepName, stepResult] of suspendedSteps) {
|
|
400
|
+
const suspendPath = [stepName, ...stepResult?.suspendPayload?.__workflow_meta?.path ?? []];
|
|
401
|
+
executionContext.suspendedPaths[step.id] = executionContext.executionPath;
|
|
402
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
403
|
+
type: "watch",
|
|
404
|
+
runId: executionContext.runId,
|
|
405
|
+
data: {
|
|
406
|
+
type: "workflow-step-suspended",
|
|
407
|
+
payload: {
|
|
408
|
+
id: step.id,
|
|
409
|
+
status: "suspended"
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
return {
|
|
414
|
+
executionContext,
|
|
415
|
+
result: {
|
|
416
|
+
status: "suspended",
|
|
417
|
+
suspendedAt: Date.now(),
|
|
418
|
+
payload: stepResult.payload,
|
|
419
|
+
suspendPayload: {
|
|
420
|
+
...stepResult?.suspendPayload,
|
|
421
|
+
__workflow_meta: { runId, path: suspendPath }
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
};
|
|
292
425
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
426
|
+
return {
|
|
427
|
+
executionContext,
|
|
428
|
+
result: {
|
|
429
|
+
status: "suspended",
|
|
430
|
+
suspendedAt: Date.now(),
|
|
431
|
+
payload: {}
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
} else if (result.status === "tripwire") {
|
|
435
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
436
|
+
type: "watch",
|
|
437
|
+
runId: executionContext.runId,
|
|
438
|
+
data: {
|
|
439
|
+
type: "workflow-step-result",
|
|
440
|
+
payload: {
|
|
441
|
+
id: step.id,
|
|
442
|
+
status: "tripwire",
|
|
443
|
+
error: result?.tripwire?.reason,
|
|
444
|
+
payload: prevOutput
|
|
445
|
+
}
|
|
446
|
+
}
|
|
301
447
|
});
|
|
302
|
-
|
|
303
|
-
|
|
448
|
+
return {
|
|
449
|
+
executionContext,
|
|
450
|
+
result: {
|
|
451
|
+
status: "tripwire",
|
|
452
|
+
tripwire: result?.tripwire,
|
|
453
|
+
endedAt: Date.now()
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
} else if (perStep || result.status === "paused") {
|
|
457
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
458
|
+
type: "watch",
|
|
459
|
+
runId: executionContext.runId,
|
|
460
|
+
data: {
|
|
461
|
+
type: "workflow-step-result",
|
|
462
|
+
payload: {
|
|
463
|
+
id: step.id,
|
|
464
|
+
status: "paused"
|
|
465
|
+
}
|
|
466
|
+
}
|
|
304
467
|
});
|
|
468
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
469
|
+
type: "watch",
|
|
470
|
+
runId: executionContext.runId,
|
|
471
|
+
data: {
|
|
472
|
+
type: "workflow-step-finish",
|
|
473
|
+
payload: {
|
|
474
|
+
id: step.id,
|
|
475
|
+
metadata: {}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
return { executionContext, result: { status: "paused" } };
|
|
305
480
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
481
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
482
|
+
type: "watch",
|
|
483
|
+
runId: executionContext.runId,
|
|
484
|
+
data: {
|
|
485
|
+
type: "workflow-step-result",
|
|
486
|
+
payload: {
|
|
487
|
+
id: step.id,
|
|
488
|
+
status: "success",
|
|
489
|
+
output: result?.result
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
await pubsub.publish(`workflow.events.v2.${executionContext.runId}`, {
|
|
494
|
+
type: "watch",
|
|
495
|
+
runId: executionContext.runId,
|
|
496
|
+
data: {
|
|
497
|
+
type: "workflow-step-finish",
|
|
498
|
+
payload: {
|
|
499
|
+
id: step.id,
|
|
500
|
+
metadata: {}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
return { executionContext, result: { status: "success", output: result?.result, endedAt: Date.now() } };
|
|
309
505
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
506
|
+
);
|
|
507
|
+
Object.assign(executionContext, res.executionContext);
|
|
508
|
+
return {
|
|
509
|
+
...res.result,
|
|
510
|
+
startedAt,
|
|
511
|
+
payload: inputData,
|
|
512
|
+
resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
|
|
513
|
+
resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
|
|
514
|
+
};
|
|
317
515
|
}
|
|
318
516
|
};
|
|
319
|
-
var
|
|
320
|
-
#mastra;
|
|
517
|
+
var InngestPubSub = class extends events.PubSub {
|
|
321
518
|
inngest;
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
super(
|
|
327
|
-
const flowControlEntries = Object.entries({ concurrency, rateLimit, throttle, debounce, priority }).filter(
|
|
328
|
-
([_, value]) => value !== void 0
|
|
329
|
-
);
|
|
330
|
-
this.flowControlConfig = flowControlEntries.length > 0 ? Object.fromEntries(flowControlEntries) : void 0;
|
|
331
|
-
this.#mastra = params.mastra;
|
|
519
|
+
workflowId;
|
|
520
|
+
publishFn;
|
|
521
|
+
subscriptions = /* @__PURE__ */ new Map();
|
|
522
|
+
constructor(inngest, workflowId, publishFn) {
|
|
523
|
+
super();
|
|
332
524
|
this.inngest = inngest;
|
|
525
|
+
this.workflowId = workflowId;
|
|
526
|
+
this.publishFn = publishFn;
|
|
333
527
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
528
|
+
/**
|
|
529
|
+
* Publish an event to Inngest's realtime system.
|
|
530
|
+
*
|
|
531
|
+
* Topic format: "workflow.events.v2.{runId}"
|
|
532
|
+
* Maps to Inngest channel: "workflow:{workflowId}:{runId}"
|
|
533
|
+
*/
|
|
534
|
+
async publish(topic, event) {
|
|
535
|
+
if (!this.publishFn) {
|
|
536
|
+
return;
|
|
339
537
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
const storage = this.#mastra?.getStorage();
|
|
344
|
-
if (!storage) {
|
|
345
|
-
this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
|
|
346
|
-
return this.runs.get(runId) ? { ...this.runs.get(runId), workflowName: this.id } : null;
|
|
538
|
+
const match = topic.match(/^workflow\.events\.v2\.(.+)$/);
|
|
539
|
+
if (!match) {
|
|
540
|
+
return;
|
|
347
541
|
}
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
} else if (step.type === "parallel" || step.type === "conditional") {
|
|
358
|
-
for (const subStep of step.steps) {
|
|
359
|
-
updateNested(subStep);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
};
|
|
363
|
-
if (this.executionGraph.steps.length) {
|
|
364
|
-
for (const step of this.executionGraph.steps) {
|
|
365
|
-
updateNested(step);
|
|
366
|
-
}
|
|
542
|
+
const runId = match[1];
|
|
543
|
+
try {
|
|
544
|
+
await this.publishFn({
|
|
545
|
+
channel: `workflow:${this.workflowId}:${runId}`,
|
|
546
|
+
topic: "watch",
|
|
547
|
+
data: event.data
|
|
548
|
+
});
|
|
549
|
+
} catch (err) {
|
|
550
|
+
console.error("InngestPubSub publish error:", err?.message ?? err);
|
|
367
551
|
}
|
|
368
552
|
}
|
|
369
553
|
/**
|
|
370
|
-
*
|
|
371
|
-
*
|
|
554
|
+
* Subscribe to events from Inngest's realtime system.
|
|
555
|
+
*
|
|
556
|
+
* Topic format: "workflow.events.v2.{runId}"
|
|
557
|
+
* Maps to Inngest channel: "workflow:{workflowId}:{runId}"
|
|
372
558
|
*/
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
}
|
|
378
|
-
async createRunAsync(options) {
|
|
379
|
-
const runIdToUse = options?.runId || crypto.randomUUID();
|
|
380
|
-
const run = this.runs.get(runIdToUse) ?? new InngestRun(
|
|
381
|
-
{
|
|
382
|
-
workflowId: this.id,
|
|
383
|
-
runId: runIdToUse,
|
|
384
|
-
resourceId: options?.resourceId,
|
|
385
|
-
executionEngine: this.executionEngine,
|
|
386
|
-
executionGraph: this.executionGraph,
|
|
387
|
-
serializedStepGraph: this.serializedStepGraph,
|
|
388
|
-
mastra: this.#mastra,
|
|
389
|
-
retryConfig: this.retryConfig,
|
|
390
|
-
cleanup: () => this.runs.delete(runIdToUse),
|
|
391
|
-
workflowSteps: this.steps
|
|
392
|
-
},
|
|
393
|
-
this.inngest
|
|
394
|
-
);
|
|
395
|
-
this.runs.set(runIdToUse, run);
|
|
396
|
-
const shouldPersistSnapshot = this.options.shouldPersistSnapshot({
|
|
397
|
-
workflowStatus: run.workflowRunStatus,
|
|
398
|
-
stepResults: {}
|
|
399
|
-
});
|
|
400
|
-
const workflowSnapshotInStorage = await this.getWorkflowRunExecutionResult(runIdToUse, false);
|
|
401
|
-
if (!workflowSnapshotInStorage && shouldPersistSnapshot) {
|
|
402
|
-
await this.mastra?.getStorage()?.persistWorkflowSnapshot({
|
|
403
|
-
workflowName: this.id,
|
|
404
|
-
runId: runIdToUse,
|
|
405
|
-
resourceId: options?.resourceId,
|
|
406
|
-
snapshot: {
|
|
407
|
-
runId: runIdToUse,
|
|
408
|
-
status: "pending",
|
|
409
|
-
value: {},
|
|
410
|
-
context: {},
|
|
411
|
-
activePaths: [],
|
|
412
|
-
waitingPaths: {},
|
|
413
|
-
serializedStepGraph: this.serializedStepGraph,
|
|
414
|
-
suspendedPaths: {},
|
|
415
|
-
resumeLabels: {},
|
|
416
|
-
result: void 0,
|
|
417
|
-
error: void 0,
|
|
418
|
-
// @ts-ignore
|
|
419
|
-
timestamp: Date.now()
|
|
420
|
-
}
|
|
421
|
-
});
|
|
559
|
+
async subscribe(topic, cb) {
|
|
560
|
+
const match = topic.match(/^workflow\.events\.v2\.(.+)$/);
|
|
561
|
+
if (!match || !match[1]) {
|
|
562
|
+
return;
|
|
422
563
|
}
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
return this.function;
|
|
564
|
+
const runId = match[1];
|
|
565
|
+
if (this.subscriptions.has(topic)) {
|
|
566
|
+
this.subscriptions.get(topic).callbacks.add(cb);
|
|
567
|
+
return;
|
|
428
568
|
}
|
|
429
|
-
|
|
569
|
+
const callbacks = /* @__PURE__ */ new Set([cb]);
|
|
570
|
+
const channel = `workflow:${this.workflowId}:${runId}`;
|
|
571
|
+
const streamPromise = realtime.subscribe(
|
|
430
572
|
{
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
cancelOn: [{ event: `cancel.workflow.${this.id}` }],
|
|
435
|
-
// Spread flow control configuration
|
|
436
|
-
...this.flowControlConfig
|
|
573
|
+
channel,
|
|
574
|
+
topics: ["watch"],
|
|
575
|
+
app: this.inngest
|
|
437
576
|
},
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
runId = await step.run(`workflow.${this.id}.runIdGen`, async () => {
|
|
443
|
-
return crypto.randomUUID();
|
|
444
|
-
});
|
|
445
|
-
}
|
|
446
|
-
const emitter = {
|
|
447
|
-
emit: async (event2, data) => {
|
|
448
|
-
if (!publish) {
|
|
449
|
-
return;
|
|
450
|
-
}
|
|
451
|
-
try {
|
|
452
|
-
await publish({
|
|
453
|
-
channel: `workflow:${this.id}:${runId}`,
|
|
454
|
-
topic: event2,
|
|
455
|
-
data
|
|
456
|
-
});
|
|
457
|
-
} catch (err) {
|
|
458
|
-
this.logger.error("Error emitting event: " + (err?.stack ?? err?.message ?? err));
|
|
459
|
-
}
|
|
460
|
-
},
|
|
461
|
-
on: (_event, _callback) => {
|
|
462
|
-
},
|
|
463
|
-
off: (_event, _callback) => {
|
|
464
|
-
},
|
|
465
|
-
once: (_event, _callback) => {
|
|
466
|
-
}
|
|
467
|
-
};
|
|
468
|
-
const engine = new InngestExecutionEngine(this.#mastra, step, attempt, this.options);
|
|
469
|
-
const result = await engine.execute({
|
|
470
|
-
workflowId: this.id,
|
|
577
|
+
(message) => {
|
|
578
|
+
const event = {
|
|
579
|
+
id: crypto.randomUUID(),
|
|
580
|
+
type: "watch",
|
|
471
581
|
runId,
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
retryConfig: this.retryConfig,
|
|
479
|
-
runtimeContext: new di.RuntimeContext(),
|
|
480
|
-
// TODO
|
|
481
|
-
resume,
|
|
482
|
-
abortController: new AbortController(),
|
|
483
|
-
currentSpan: void 0,
|
|
484
|
-
// TODO: Pass actual parent AI span from workflow execution context
|
|
485
|
-
outputOptions
|
|
486
|
-
});
|
|
487
|
-
await step.run(`workflow.${this.id}.finalize`, async () => {
|
|
488
|
-
if (result.status === "failed") {
|
|
489
|
-
throw new inngest.NonRetriableError(`Workflow failed`, {
|
|
490
|
-
cause: result
|
|
491
|
-
});
|
|
492
|
-
}
|
|
493
|
-
return result;
|
|
494
|
-
});
|
|
495
|
-
return { result, runId };
|
|
582
|
+
data: message.data,
|
|
583
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
584
|
+
};
|
|
585
|
+
for (const callback of callbacks) {
|
|
586
|
+
callback(event);
|
|
587
|
+
}
|
|
496
588
|
}
|
|
497
589
|
);
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
}
|
|
506
|
-
return [];
|
|
507
|
-
} else if (step.type === "parallel" || step.type === "conditional") {
|
|
508
|
-
return this.getNestedFunctions(step.steps);
|
|
509
|
-
}
|
|
510
|
-
return [];
|
|
590
|
+
this.subscriptions.set(topic, {
|
|
591
|
+
unsubscribe: () => {
|
|
592
|
+
streamPromise.then((stream) => stream.cancel()).catch((err) => {
|
|
593
|
+
console.error("InngestPubSub unsubscribe error:", err);
|
|
594
|
+
});
|
|
595
|
+
},
|
|
596
|
+
callbacks
|
|
511
597
|
});
|
|
512
598
|
}
|
|
513
|
-
|
|
514
|
-
|
|
599
|
+
/**
|
|
600
|
+
* Unsubscribe a callback from a topic.
|
|
601
|
+
* If no callbacks remain, the underlying Inngest subscription is cancelled.
|
|
602
|
+
*/
|
|
603
|
+
async unsubscribe(topic, cb) {
|
|
604
|
+
const sub = this.subscriptions.get(topic);
|
|
605
|
+
if (!sub) {
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
sub.callbacks.delete(cb);
|
|
609
|
+
if (sub.callbacks.size === 0) {
|
|
610
|
+
sub.unsubscribe();
|
|
611
|
+
this.subscriptions.delete(topic);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Flush any pending operations. No-op for Inngest.
|
|
616
|
+
*/
|
|
617
|
+
async flush() {
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Clean up all subscriptions during graceful shutdown.
|
|
621
|
+
*/
|
|
622
|
+
async close() {
|
|
623
|
+
for (const [, sub] of this.subscriptions) {
|
|
624
|
+
sub.unsubscribe();
|
|
625
|
+
}
|
|
626
|
+
this.subscriptions.clear();
|
|
515
627
|
}
|
|
516
628
|
};
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
let streamPromise = {};
|
|
538
|
-
streamPromise.promise = new Promise((resolve, reject) => {
|
|
539
|
-
streamPromise.resolve = resolve;
|
|
540
|
-
streamPromise.reject = reject;
|
|
541
|
-
});
|
|
542
|
-
const toolData = {
|
|
543
|
-
name: params.name,
|
|
544
|
-
args: inputData
|
|
545
|
-
};
|
|
546
|
-
if ((await params.getLLM()).getModel().specificationVersion === `v2`) {
|
|
547
|
-
const { fullStream } = await params.stream(inputData.prompt, {
|
|
548
|
-
runtimeContext,
|
|
549
|
-
tracingContext,
|
|
550
|
-
onFinish: (result) => {
|
|
551
|
-
streamPromise.resolve(result.text);
|
|
552
|
-
},
|
|
553
|
-
abortSignal
|
|
554
|
-
});
|
|
555
|
-
if (abortSignal.aborted) {
|
|
556
|
-
return abort();
|
|
557
|
-
}
|
|
558
|
-
await emitter.emit("watch-v2", {
|
|
559
|
-
type: "tool-call-streaming-start",
|
|
560
|
-
...toolData ?? {}
|
|
561
|
-
});
|
|
562
|
-
for await (const chunk of fullStream) {
|
|
563
|
-
if (chunk.type === "text-delta") {
|
|
564
|
-
await emitter.emit("watch-v2", {
|
|
565
|
-
type: "tool-call-delta",
|
|
566
|
-
...toolData ?? {},
|
|
567
|
-
argsTextDelta: chunk.payload.text
|
|
568
|
-
});
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
} else {
|
|
572
|
-
const { fullStream } = await params.streamLegacy(inputData.prompt, {
|
|
573
|
-
runtimeContext,
|
|
574
|
-
tracingContext,
|
|
575
|
-
onFinish: (result) => {
|
|
576
|
-
streamPromise.resolve(result.text);
|
|
577
|
-
},
|
|
578
|
-
abortSignal
|
|
579
|
-
});
|
|
580
|
-
if (abortSignal.aborted) {
|
|
581
|
-
return abort();
|
|
582
|
-
}
|
|
583
|
-
await emitter.emit("watch-v2", {
|
|
584
|
-
type: "tool-call-streaming-start",
|
|
585
|
-
...toolData ?? {}
|
|
586
|
-
});
|
|
587
|
-
for await (const chunk of fullStream) {
|
|
588
|
-
if (chunk.type === "text-delta") {
|
|
589
|
-
await emitter.emit("watch-v2", {
|
|
590
|
-
type: "tool-call-delta",
|
|
591
|
-
...toolData ?? {},
|
|
592
|
-
argsTextDelta: chunk.textDelta
|
|
593
|
-
});
|
|
629
|
+
var InngestRun = class extends workflows.Run {
|
|
630
|
+
inngest;
|
|
631
|
+
serializedStepGraph;
|
|
632
|
+
#mastra;
|
|
633
|
+
constructor(params, inngest) {
|
|
634
|
+
super(params);
|
|
635
|
+
this.inngest = inngest;
|
|
636
|
+
this.serializedStepGraph = params.serializedStepGraph;
|
|
637
|
+
this.#mastra = params.mastra;
|
|
638
|
+
}
|
|
639
|
+
async getRuns(eventId) {
|
|
640
|
+
const maxRetries = 3;
|
|
641
|
+
let lastError = null;
|
|
642
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
643
|
+
try {
|
|
644
|
+
const response = await fetch(
|
|
645
|
+
`${this.inngest.apiBaseUrl ?? "https://api.inngest.com"}/v1/events/${eventId}/runs`,
|
|
646
|
+
{
|
|
647
|
+
headers: {
|
|
648
|
+
Authorization: `Bearer ${process.env.INNGEST_SIGNING_KEY}`
|
|
594
649
|
}
|
|
595
650
|
}
|
|
651
|
+
);
|
|
652
|
+
if (response.status === 429) {
|
|
653
|
+
const retryAfter = parseInt(response.headers.get("retry-after") || "2", 10);
|
|
654
|
+
await new Promise((resolve) => setTimeout(resolve, retryAfter * 1e3));
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
if (!response.ok) {
|
|
658
|
+
throw new Error(`Inngest API error: ${response.status} ${response.statusText}`);
|
|
659
|
+
}
|
|
660
|
+
const text = await response.text();
|
|
661
|
+
if (!text) {
|
|
662
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3 * (attempt + 1)));
|
|
663
|
+
continue;
|
|
664
|
+
}
|
|
665
|
+
const json = JSON.parse(text);
|
|
666
|
+
return json.data;
|
|
667
|
+
} catch (error) {
|
|
668
|
+
lastError = error;
|
|
669
|
+
if (attempt < maxRetries - 1) {
|
|
670
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3 * Math.pow(2, attempt)));
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
throw new inngest.NonRetriableError(`Failed to get runs after ${maxRetries} attempts: ${lastError?.message}`);
|
|
675
|
+
}
|
|
676
|
+
async getRunOutput(eventId, maxWaitMs = 3e5) {
|
|
677
|
+
const startTime = Date.now();
|
|
678
|
+
const storage = this.#mastra?.getStorage();
|
|
679
|
+
const workflowsStore = await storage?.getStore("workflows");
|
|
680
|
+
while (Date.now() - startTime < maxWaitMs) {
|
|
681
|
+
let runs;
|
|
682
|
+
try {
|
|
683
|
+
runs = await this.getRuns(eventId);
|
|
684
|
+
} catch (error) {
|
|
685
|
+
if (error instanceof inngest.NonRetriableError) {
|
|
686
|
+
throw error;
|
|
596
687
|
}
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
688
|
+
throw new inngest.NonRetriableError(
|
|
689
|
+
`Failed to poll workflow status: ${error instanceof Error ? error.message : String(error)}`
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
if (runs?.[0]?.status === "Completed" && runs?.[0]?.event_id === eventId) {
|
|
693
|
+
return runs[0];
|
|
694
|
+
}
|
|
695
|
+
if (runs?.[0]?.status === "Failed") {
|
|
696
|
+
const snapshot = await workflowsStore?.loadWorkflowSnapshot({
|
|
697
|
+
workflowName: this.workflowId,
|
|
698
|
+
runId: this.runId
|
|
600
699
|
});
|
|
700
|
+
if (snapshot?.context) {
|
|
701
|
+
snapshot.context = workflows.hydrateSerializedStepErrors(snapshot.context);
|
|
702
|
+
}
|
|
601
703
|
return {
|
|
602
|
-
|
|
704
|
+
output: {
|
|
705
|
+
result: {
|
|
706
|
+
steps: snapshot?.context,
|
|
707
|
+
status: "failed",
|
|
708
|
+
// Get the original error from NonRetriableError's cause (which contains the workflow result)
|
|
709
|
+
error: error.getErrorFromUnknown(runs?.[0]?.output?.cause?.error, { serializeStack: false })
|
|
710
|
+
}
|
|
711
|
+
}
|
|
603
712
|
};
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
if (!params.inputSchema || !params.outputSchema) {
|
|
610
|
-
throw new Error("Tool must have input and output schemas defined");
|
|
611
|
-
}
|
|
612
|
-
return {
|
|
613
|
-
// TODO: tool probably should have strong id type
|
|
614
|
-
// @ts-ignore
|
|
615
|
-
id: params.id,
|
|
616
|
-
description: params.description,
|
|
617
|
-
inputSchema: params.inputSchema,
|
|
618
|
-
outputSchema: params.outputSchema,
|
|
619
|
-
execute: async ({ inputData, mastra, runtimeContext, tracingContext, suspend, resumeData }) => {
|
|
620
|
-
return params.execute({
|
|
621
|
-
context: inputData,
|
|
622
|
-
mastra: aiTracing.wrapMastra(mastra, tracingContext),
|
|
623
|
-
runtimeContext,
|
|
624
|
-
tracingContext,
|
|
625
|
-
suspend,
|
|
626
|
-
resumeData
|
|
713
|
+
}
|
|
714
|
+
if (runs?.[0]?.status === "Cancelled") {
|
|
715
|
+
const snapshot = await workflowsStore?.loadWorkflowSnapshot({
|
|
716
|
+
workflowName: this.workflowId,
|
|
717
|
+
runId: this.runId
|
|
627
718
|
});
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
}
|
|
632
|
-
return {
|
|
633
|
-
id: params.id,
|
|
634
|
-
description: params.description,
|
|
635
|
-
inputSchema: params.inputSchema,
|
|
636
|
-
outputSchema: params.outputSchema,
|
|
637
|
-
resumeSchema: params.resumeSchema,
|
|
638
|
-
suspendSchema: params.suspendSchema,
|
|
639
|
-
execute: params.execute
|
|
640
|
-
};
|
|
641
|
-
}
|
|
642
|
-
function init(inngest) {
|
|
643
|
-
return {
|
|
644
|
-
createWorkflow(params) {
|
|
645
|
-
return new InngestWorkflow(
|
|
646
|
-
params,
|
|
647
|
-
inngest
|
|
648
|
-
);
|
|
649
|
-
},
|
|
650
|
-
createStep,
|
|
651
|
-
cloneStep(step, opts) {
|
|
652
|
-
return {
|
|
653
|
-
id: opts.id,
|
|
654
|
-
description: step.description,
|
|
655
|
-
inputSchema: step.inputSchema,
|
|
656
|
-
outputSchema: step.outputSchema,
|
|
657
|
-
resumeSchema: step.resumeSchema,
|
|
658
|
-
suspendSchema: step.suspendSchema,
|
|
659
|
-
stateSchema: step.stateSchema,
|
|
660
|
-
execute: step.execute,
|
|
661
|
-
component: step.component
|
|
662
|
-
};
|
|
663
|
-
},
|
|
664
|
-
cloneWorkflow(workflow, opts) {
|
|
665
|
-
const wf = new workflows.Workflow({
|
|
666
|
-
id: opts.id,
|
|
667
|
-
inputSchema: workflow.inputSchema,
|
|
668
|
-
outputSchema: workflow.outputSchema,
|
|
669
|
-
steps: workflow.stepDefs,
|
|
670
|
-
mastra: workflow.mastra
|
|
671
|
-
});
|
|
672
|
-
wf.setStepFlow(workflow.stepGraph);
|
|
673
|
-
wf.commit();
|
|
674
|
-
return wf;
|
|
719
|
+
return { output: { result: { steps: snapshot?.context, status: "canceled" } } };
|
|
720
|
+
}
|
|
721
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3 + Math.random() * 1e3));
|
|
675
722
|
}
|
|
676
|
-
|
|
677
|
-
}
|
|
678
|
-
var InngestExecutionEngine = class extends workflows.DefaultExecutionEngine {
|
|
679
|
-
inngestStep;
|
|
680
|
-
inngestAttempts;
|
|
681
|
-
constructor(mastra, inngestStep, inngestAttempts = 0, options) {
|
|
682
|
-
super({ mastra, options });
|
|
683
|
-
this.inngestStep = inngestStep;
|
|
684
|
-
this.inngestAttempts = inngestAttempts;
|
|
723
|
+
throw new inngest.NonRetriableError(`Workflow did not complete within ${maxWaitMs}ms`);
|
|
685
724
|
}
|
|
686
|
-
async
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
725
|
+
async cancel() {
|
|
726
|
+
const storage = this.#mastra?.getStorage();
|
|
727
|
+
await this.inngest.send({
|
|
728
|
+
name: `cancel.workflow.${this.workflowId}`,
|
|
729
|
+
data: {
|
|
730
|
+
runId: this.runId
|
|
731
|
+
}
|
|
690
732
|
});
|
|
691
|
-
const
|
|
692
|
-
await
|
|
693
|
-
|
|
694
|
-
|
|
733
|
+
const workflowsStore = await storage?.getStore("workflows");
|
|
734
|
+
const snapshot = await workflowsStore?.loadWorkflowSnapshot({
|
|
735
|
+
workflowName: this.workflowId,
|
|
736
|
+
runId: this.runId
|
|
695
737
|
});
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
type: "watch",
|
|
706
|
-
payload: {
|
|
707
|
-
workflowState: {
|
|
708
|
-
status: lastOutput.status,
|
|
709
|
-
steps: stepResults,
|
|
710
|
-
result: lastOutput.output
|
|
711
|
-
}
|
|
712
|
-
},
|
|
713
|
-
eventTimestamp: Date.now()
|
|
714
|
-
});
|
|
715
|
-
base.result = lastOutput.output;
|
|
716
|
-
} else if (lastOutput.status === "failed") {
|
|
717
|
-
base.error = error instanceof Error ? error?.stack ?? error.message : lastOutput?.error instanceof Error ? lastOutput.error.message : lastOutput.error ?? error ?? "Unknown error";
|
|
718
|
-
await emitter.emit("watch", {
|
|
719
|
-
type: "watch",
|
|
720
|
-
payload: {
|
|
721
|
-
workflowState: {
|
|
722
|
-
status: lastOutput.status,
|
|
723
|
-
steps: stepResults,
|
|
724
|
-
result: null,
|
|
725
|
-
error: base.error
|
|
726
|
-
}
|
|
727
|
-
},
|
|
728
|
-
eventTimestamp: Date.now()
|
|
729
|
-
});
|
|
730
|
-
} else if (lastOutput.status === "suspended") {
|
|
731
|
-
await emitter.emit("watch", {
|
|
732
|
-
type: "watch",
|
|
733
|
-
payload: {
|
|
734
|
-
workflowState: {
|
|
735
|
-
status: lastOutput.status,
|
|
736
|
-
steps: stepResults,
|
|
737
|
-
result: null,
|
|
738
|
-
error: null
|
|
739
|
-
}
|
|
740
|
-
},
|
|
741
|
-
eventTimestamp: Date.now()
|
|
742
|
-
});
|
|
743
|
-
const suspendedStepIds = Object.entries(stepResults).flatMap(([stepId, stepResult]) => {
|
|
744
|
-
if (stepResult?.status === "suspended") {
|
|
745
|
-
const nestedPath = stepResult?.suspendPayload?.__workflow_meta?.path;
|
|
746
|
-
return nestedPath ? [[stepId, ...nestedPath]] : [[stepId]];
|
|
738
|
+
if (snapshot) {
|
|
739
|
+
await workflowsStore?.persistWorkflowSnapshot({
|
|
740
|
+
workflowName: this.workflowId,
|
|
741
|
+
runId: this.runId,
|
|
742
|
+
resourceId: this.resourceId,
|
|
743
|
+
snapshot: {
|
|
744
|
+
...snapshot,
|
|
745
|
+
status: "canceled",
|
|
746
|
+
value: snapshot.value
|
|
747
747
|
}
|
|
748
|
-
return [];
|
|
749
748
|
});
|
|
750
|
-
base.suspended = suspendedStepIds;
|
|
751
749
|
}
|
|
752
|
-
return base;
|
|
753
750
|
}
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
// }
|
|
757
|
-
async executeSleep({
|
|
758
|
-
workflowId,
|
|
759
|
-
runId,
|
|
760
|
-
entry,
|
|
761
|
-
prevOutput,
|
|
762
|
-
stepResults,
|
|
763
|
-
emitter,
|
|
764
|
-
abortController,
|
|
765
|
-
runtimeContext,
|
|
766
|
-
executionContext,
|
|
767
|
-
writableStream,
|
|
768
|
-
tracingContext
|
|
769
|
-
}) {
|
|
770
|
-
let { duration, fn } = entry;
|
|
771
|
-
const sleepSpan = tracingContext?.currentSpan?.createChildSpan({
|
|
772
|
-
type: aiTracing.AISpanType.WORKFLOW_SLEEP,
|
|
773
|
-
name: `sleep: ${duration ? `${duration}ms` : "dynamic"}`,
|
|
774
|
-
attributes: {
|
|
775
|
-
durationMs: duration,
|
|
776
|
-
sleepType: fn ? "dynamic" : "fixed"
|
|
777
|
-
},
|
|
778
|
-
tracingPolicy: this.options?.tracingPolicy
|
|
779
|
-
});
|
|
780
|
-
if (fn) {
|
|
781
|
-
const stepCallId = crypto.randomUUID();
|
|
782
|
-
duration = await this.inngestStep.run(`workflow.${workflowId}.sleep.${entry.id}`, async () => {
|
|
783
|
-
return await fn(
|
|
784
|
-
workflows.createDeprecationProxy(
|
|
785
|
-
{
|
|
786
|
-
runId,
|
|
787
|
-
workflowId,
|
|
788
|
-
mastra: this.mastra,
|
|
789
|
-
runtimeContext,
|
|
790
|
-
inputData: prevOutput,
|
|
791
|
-
state: executionContext.state,
|
|
792
|
-
setState: (state) => {
|
|
793
|
-
executionContext.state = state;
|
|
794
|
-
},
|
|
795
|
-
runCount: -1,
|
|
796
|
-
retryCount: -1,
|
|
797
|
-
tracingContext: {
|
|
798
|
-
currentSpan: sleepSpan
|
|
799
|
-
},
|
|
800
|
-
getInitData: () => stepResults?.input,
|
|
801
|
-
getStepResult: workflows.getStepResult.bind(this, stepResults),
|
|
802
|
-
// TODO: this function shouldn't have suspend probably?
|
|
803
|
-
suspend: async (_suspendPayload) => {
|
|
804
|
-
},
|
|
805
|
-
bail: () => {
|
|
806
|
-
},
|
|
807
|
-
abort: () => {
|
|
808
|
-
abortController?.abort();
|
|
809
|
-
},
|
|
810
|
-
[_constants.EMITTER_SYMBOL]: emitter,
|
|
811
|
-
// TODO: add streamVNext support
|
|
812
|
-
[_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
|
|
813
|
-
engine: { step: this.inngestStep },
|
|
814
|
-
abortSignal: abortController?.signal,
|
|
815
|
-
writer: new tools.ToolStream(
|
|
816
|
-
{
|
|
817
|
-
prefix: "workflow-step",
|
|
818
|
-
callId: stepCallId,
|
|
819
|
-
name: "sleep",
|
|
820
|
-
runId
|
|
821
|
-
},
|
|
822
|
-
writableStream
|
|
823
|
-
)
|
|
824
|
-
},
|
|
825
|
-
{
|
|
826
|
-
paramName: "runCount",
|
|
827
|
-
deprecationMessage: workflows.runCountDeprecationMessage,
|
|
828
|
-
logger: this.logger
|
|
829
|
-
}
|
|
830
|
-
)
|
|
831
|
-
);
|
|
832
|
-
});
|
|
833
|
-
sleepSpan?.update({
|
|
834
|
-
attributes: {
|
|
835
|
-
durationMs: duration
|
|
836
|
-
}
|
|
837
|
-
});
|
|
838
|
-
}
|
|
839
|
-
try {
|
|
840
|
-
await this.inngestStep.sleep(entry.id, !duration || duration < 0 ? 0 : duration);
|
|
841
|
-
sleepSpan?.end();
|
|
842
|
-
} catch (e) {
|
|
843
|
-
sleepSpan?.error({ error: e });
|
|
844
|
-
throw e;
|
|
845
|
-
}
|
|
751
|
+
async start(args) {
|
|
752
|
+
return this._start(args);
|
|
846
753
|
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
754
|
+
/**
|
|
755
|
+
* Starts the workflow execution without waiting for completion (fire-and-forget).
|
|
756
|
+
* Returns immediately with the runId after sending the event to Inngest.
|
|
757
|
+
* The workflow executes independently in Inngest.
|
|
758
|
+
* Use this when you don't need to wait for the result or want to avoid polling failures.
|
|
759
|
+
*/
|
|
760
|
+
async startAsync(args) {
|
|
761
|
+
const workflowsStore = await this.#mastra.getStorage()?.getStore("workflows");
|
|
762
|
+
await workflowsStore?.persistWorkflowSnapshot({
|
|
763
|
+
workflowName: this.workflowId,
|
|
764
|
+
runId: this.runId,
|
|
765
|
+
resourceId: this.resourceId,
|
|
766
|
+
snapshot: {
|
|
767
|
+
runId: this.runId,
|
|
768
|
+
serializedStepGraph: this.serializedStepGraph,
|
|
769
|
+
status: "running",
|
|
770
|
+
value: {},
|
|
771
|
+
context: {},
|
|
772
|
+
activePaths: [],
|
|
773
|
+
suspendedPaths: {},
|
|
774
|
+
activeStepsPath: {},
|
|
775
|
+
resumeLabels: {},
|
|
776
|
+
waitingPaths: {},
|
|
777
|
+
timestamp: Date.now()
|
|
778
|
+
}
|
|
870
779
|
});
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
executionContext.state = state;
|
|
885
|
-
},
|
|
886
|
-
runCount: -1,
|
|
887
|
-
retryCount: -1,
|
|
888
|
-
tracingContext: {
|
|
889
|
-
currentSpan: sleepUntilSpan
|
|
890
|
-
},
|
|
891
|
-
getInitData: () => stepResults?.input,
|
|
892
|
-
getStepResult: workflows.getStepResult.bind(this, stepResults),
|
|
893
|
-
// TODO: this function shouldn't have suspend probably?
|
|
894
|
-
suspend: async (_suspendPayload) => {
|
|
895
|
-
},
|
|
896
|
-
bail: () => {
|
|
897
|
-
},
|
|
898
|
-
abort: () => {
|
|
899
|
-
abortController?.abort();
|
|
900
|
-
},
|
|
901
|
-
[_constants.EMITTER_SYMBOL]: emitter,
|
|
902
|
-
[_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
|
|
903
|
-
// TODO: add streamVNext support
|
|
904
|
-
engine: { step: this.inngestStep },
|
|
905
|
-
abortSignal: abortController?.signal,
|
|
906
|
-
writer: new tools.ToolStream(
|
|
907
|
-
{
|
|
908
|
-
prefix: "workflow-step",
|
|
909
|
-
callId: stepCallId,
|
|
910
|
-
name: "sleep",
|
|
911
|
-
runId
|
|
912
|
-
},
|
|
913
|
-
writableStream
|
|
914
|
-
)
|
|
915
|
-
},
|
|
916
|
-
{
|
|
917
|
-
paramName: "runCount",
|
|
918
|
-
deprecationMessage: workflows.runCountDeprecationMessage,
|
|
919
|
-
logger: this.logger
|
|
920
|
-
}
|
|
921
|
-
)
|
|
922
|
-
);
|
|
923
|
-
});
|
|
924
|
-
if (date && !(date instanceof Date)) {
|
|
925
|
-
date = new Date(date);
|
|
780
|
+
const inputDataToUse = await this._validateInput(args.inputData);
|
|
781
|
+
const initialStateToUse = await this._validateInitialState(args.initialState ?? {});
|
|
782
|
+
const eventOutput = await this.inngest.send({
|
|
783
|
+
name: `workflow.${this.workflowId}`,
|
|
784
|
+
data: {
|
|
785
|
+
inputData: inputDataToUse,
|
|
786
|
+
initialState: initialStateToUse,
|
|
787
|
+
runId: this.runId,
|
|
788
|
+
resourceId: this.resourceId,
|
|
789
|
+
outputOptions: args.outputOptions,
|
|
790
|
+
tracingOptions: args.tracingOptions,
|
|
791
|
+
requestContext: args.requestContext ? Object.fromEntries(args.requestContext.entries()) : {},
|
|
792
|
+
perStep: args.perStep
|
|
926
793
|
}
|
|
927
|
-
const time = !date ? 0 : date.getTime() - Date.now();
|
|
928
|
-
sleepUntilSpan?.update({
|
|
929
|
-
attributes: {
|
|
930
|
-
durationMs: Math.max(0, time)
|
|
931
|
-
}
|
|
932
|
-
});
|
|
933
|
-
}
|
|
934
|
-
if (!(date instanceof Date)) {
|
|
935
|
-
sleepUntilSpan?.end();
|
|
936
|
-
return;
|
|
937
|
-
}
|
|
938
|
-
try {
|
|
939
|
-
await this.inngestStep.sleepUntil(entry.id, date);
|
|
940
|
-
sleepUntilSpan?.end();
|
|
941
|
-
} catch (e) {
|
|
942
|
-
sleepUntilSpan?.error({ error: e });
|
|
943
|
-
throw e;
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
async executeWaitForEvent({ event, timeout }) {
|
|
947
|
-
const eventData = await this.inngestStep.waitForEvent(`user-event-${event}`, {
|
|
948
|
-
event: `user-event-${event}`,
|
|
949
|
-
timeout: timeout ?? 5e3
|
|
950
794
|
});
|
|
951
|
-
|
|
952
|
-
|
|
795
|
+
const eventId = eventOutput.ids[0];
|
|
796
|
+
if (!eventId) {
|
|
797
|
+
throw new Error("Event ID is not set");
|
|
953
798
|
}
|
|
954
|
-
return
|
|
799
|
+
return { runId: this.runId };
|
|
955
800
|
}
|
|
956
|
-
async
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
runtimeContext,
|
|
965
|
-
tracingContext,
|
|
966
|
-
writableStream,
|
|
967
|
-
disableScorers
|
|
801
|
+
async _start({
|
|
802
|
+
inputData,
|
|
803
|
+
initialState,
|
|
804
|
+
outputOptions,
|
|
805
|
+
tracingOptions,
|
|
806
|
+
format,
|
|
807
|
+
requestContext,
|
|
808
|
+
perStep
|
|
968
809
|
}) {
|
|
969
|
-
const
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
810
|
+
const workflowsStore = await this.#mastra.getStorage()?.getStore("workflows");
|
|
811
|
+
await workflowsStore?.persistWorkflowSnapshot({
|
|
812
|
+
workflowName: this.workflowId,
|
|
813
|
+
runId: this.runId,
|
|
814
|
+
resourceId: this.resourceId,
|
|
815
|
+
snapshot: {
|
|
816
|
+
runId: this.runId,
|
|
817
|
+
serializedStepGraph: this.serializedStepGraph,
|
|
818
|
+
status: "running",
|
|
819
|
+
value: {},
|
|
820
|
+
context: {},
|
|
821
|
+
activePaths: [],
|
|
822
|
+
suspendedPaths: {},
|
|
823
|
+
activeStepsPath: {},
|
|
824
|
+
resumeLabels: {},
|
|
825
|
+
waitingPaths: {},
|
|
826
|
+
timestamp: Date.now()
|
|
827
|
+
}
|
|
977
828
|
});
|
|
978
|
-
const
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
829
|
+
const inputDataToUse = await this._validateInput(inputData);
|
|
830
|
+
const initialStateToUse = await this._validateInitialState(initialState ?? {});
|
|
831
|
+
const eventOutput = await this.inngest.send({
|
|
832
|
+
name: `workflow.${this.workflowId}`,
|
|
833
|
+
data: {
|
|
834
|
+
inputData: inputDataToUse,
|
|
835
|
+
initialState: initialStateToUse,
|
|
836
|
+
runId: this.runId,
|
|
837
|
+
resourceId: this.resourceId,
|
|
838
|
+
outputOptions,
|
|
839
|
+
tracingOptions,
|
|
840
|
+
format,
|
|
841
|
+
requestContext: requestContext ? Object.fromEntries(requestContext.entries()) : {},
|
|
842
|
+
perStep
|
|
843
|
+
}
|
|
982
844
|
});
|
|
983
|
-
const
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
status: "running"
|
|
1000
|
-
}
|
|
1001
|
-
},
|
|
1002
|
-
result: null,
|
|
1003
|
-
error: null
|
|
1004
|
-
}
|
|
1005
|
-
},
|
|
1006
|
-
eventTimestamp: Date.now()
|
|
845
|
+
const eventId = eventOutput.ids[0];
|
|
846
|
+
if (!eventId) {
|
|
847
|
+
throw new Error("Event ID is not set");
|
|
848
|
+
}
|
|
849
|
+
const runOutput = await this.getRunOutput(eventId);
|
|
850
|
+
const result = runOutput?.output?.result;
|
|
851
|
+
this.hydrateFailedResult(result);
|
|
852
|
+
if (result.status !== "suspended") {
|
|
853
|
+
this.cleanup?.();
|
|
854
|
+
}
|
|
855
|
+
return result;
|
|
856
|
+
}
|
|
857
|
+
async resume(params) {
|
|
858
|
+
const p = this._resume(params).then((result) => {
|
|
859
|
+
if (result.status !== "suspended") {
|
|
860
|
+
this.closeStreamAction?.().catch(() => {
|
|
1007
861
|
});
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
862
|
+
}
|
|
863
|
+
return result;
|
|
864
|
+
});
|
|
865
|
+
this.executionResults = p;
|
|
866
|
+
return p;
|
|
867
|
+
}
|
|
868
|
+
async _resume(params) {
|
|
869
|
+
const storage = this.#mastra?.getStorage();
|
|
870
|
+
let steps = [];
|
|
871
|
+
if (typeof params.step === "string") {
|
|
872
|
+
steps = params.step.split(".");
|
|
873
|
+
} else {
|
|
874
|
+
steps = (Array.isArray(params.step) ? params.step : [params.step]).map(
|
|
875
|
+
(step) => typeof step === "string" ? step : step?.id
|
|
876
|
+
);
|
|
877
|
+
}
|
|
878
|
+
const workflowsStore = await storage?.getStore("workflows");
|
|
879
|
+
const snapshot = await workflowsStore?.loadWorkflowSnapshot({
|
|
880
|
+
workflowName: this.workflowId,
|
|
881
|
+
runId: this.runId
|
|
882
|
+
});
|
|
883
|
+
const suspendedStep = this.workflowSteps[steps?.[0] ?? ""];
|
|
884
|
+
const resumeDataToUse = await this._validateResumeData(params.resumeData, suspendedStep);
|
|
885
|
+
const persistedRequestContext = snapshot?.requestContext ?? {};
|
|
886
|
+
const newRequestContext = params.requestContext ? Object.fromEntries(params.requestContext.entries()) : {};
|
|
887
|
+
const mergedRequestContext = { ...persistedRequestContext, ...newRequestContext };
|
|
888
|
+
const eventOutput = await this.inngest.send({
|
|
889
|
+
name: `workflow.${this.workflowId}`,
|
|
890
|
+
data: {
|
|
891
|
+
inputData: resumeDataToUse,
|
|
892
|
+
initialState: snapshot?.value ?? {},
|
|
893
|
+
runId: this.runId,
|
|
894
|
+
workflowId: this.workflowId,
|
|
895
|
+
stepResults: snapshot?.context,
|
|
896
|
+
resume: {
|
|
897
|
+
steps,
|
|
898
|
+
stepResults: snapshot?.context,
|
|
899
|
+
resumePayload: resumeDataToUse,
|
|
900
|
+
resumePath: steps?.[0] ? snapshot?.suspendedPaths?.[steps?.[0]] : void 0
|
|
901
|
+
},
|
|
902
|
+
requestContext: mergedRequestContext,
|
|
903
|
+
perStep: params.perStep
|
|
904
|
+
}
|
|
905
|
+
});
|
|
906
|
+
const eventId = eventOutput.ids[0];
|
|
907
|
+
if (!eventId) {
|
|
908
|
+
throw new Error("Event ID is not set");
|
|
909
|
+
}
|
|
910
|
+
const runOutput = await this.getRunOutput(eventId);
|
|
911
|
+
const result = runOutput?.output?.result;
|
|
912
|
+
this.hydrateFailedResult(result);
|
|
913
|
+
return result;
|
|
914
|
+
}
|
|
915
|
+
async timeTravel(params) {
|
|
916
|
+
const p = this._timeTravel(params).then((result) => {
|
|
917
|
+
if (result.status !== "suspended") {
|
|
918
|
+
this.closeStreamAction?.().catch(() => {
|
|
1016
919
|
});
|
|
1017
|
-
|
|
920
|
+
}
|
|
921
|
+
return result;
|
|
922
|
+
});
|
|
923
|
+
this.executionResults = p;
|
|
924
|
+
return p;
|
|
925
|
+
}
|
|
926
|
+
async _timeTravel(params) {
|
|
927
|
+
if (!params.step || Array.isArray(params.step) && params.step?.length === 0) {
|
|
928
|
+
throw new Error("Step is required and must be a valid step or array of steps");
|
|
929
|
+
}
|
|
930
|
+
let steps = [];
|
|
931
|
+
if (typeof params.step === "string") {
|
|
932
|
+
steps = params.step.split(".");
|
|
933
|
+
} else {
|
|
934
|
+
steps = (Array.isArray(params.step) ? params.step : [params.step]).map(
|
|
935
|
+
(step) => typeof step === "string" ? step : step?.id
|
|
936
|
+
);
|
|
937
|
+
}
|
|
938
|
+
if (steps.length === 0) {
|
|
939
|
+
throw new Error("No steps provided to timeTravel");
|
|
940
|
+
}
|
|
941
|
+
const storage = this.#mastra?.getStorage();
|
|
942
|
+
const workflowsStore = await storage?.getStore("workflows");
|
|
943
|
+
const snapshot = await workflowsStore?.loadWorkflowSnapshot({
|
|
944
|
+
workflowName: this.workflowId,
|
|
945
|
+
runId: this.runId
|
|
946
|
+
});
|
|
947
|
+
if (!snapshot) {
|
|
948
|
+
await workflowsStore?.persistWorkflowSnapshot({
|
|
949
|
+
workflowName: this.workflowId,
|
|
950
|
+
runId: this.runId,
|
|
951
|
+
resourceId: this.resourceId,
|
|
952
|
+
snapshot: {
|
|
953
|
+
runId: this.runId,
|
|
954
|
+
serializedStepGraph: this.serializedStepGraph,
|
|
955
|
+
status: "pending",
|
|
956
|
+
value: {},
|
|
957
|
+
context: {},
|
|
958
|
+
activePaths: [],
|
|
959
|
+
suspendedPaths: {},
|
|
960
|
+
activeStepsPath: {},
|
|
961
|
+
resumeLabels: {},
|
|
962
|
+
waitingPaths: {},
|
|
963
|
+
timestamp: Date.now()
|
|
964
|
+
}
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
if (snapshot?.status === "running") {
|
|
968
|
+
throw new Error("This workflow run is still running, cannot time travel");
|
|
969
|
+
}
|
|
970
|
+
let inputDataToUse = params.inputData;
|
|
971
|
+
if (inputDataToUse && steps.length === 1) {
|
|
972
|
+
inputDataToUse = await this._validateTimetravelInputData(params.inputData, this.workflowSteps[steps[0]]);
|
|
973
|
+
}
|
|
974
|
+
const timeTravelData = workflows.createTimeTravelExecutionParams({
|
|
975
|
+
steps,
|
|
976
|
+
inputData: inputDataToUse,
|
|
977
|
+
resumeData: params.resumeData,
|
|
978
|
+
context: params.context,
|
|
979
|
+
nestedStepsContext: params.nestedStepsContext,
|
|
980
|
+
snapshot: snapshot ?? { context: {} },
|
|
981
|
+
graph: this.executionGraph,
|
|
982
|
+
initialState: params.initialState,
|
|
983
|
+
perStep: params.perStep
|
|
984
|
+
});
|
|
985
|
+
const eventOutput = await this.inngest.send({
|
|
986
|
+
name: `workflow.${this.workflowId}`,
|
|
987
|
+
data: {
|
|
988
|
+
initialState: timeTravelData.state,
|
|
989
|
+
runId: this.runId,
|
|
990
|
+
workflowId: this.workflowId,
|
|
991
|
+
stepResults: timeTravelData.stepResults,
|
|
992
|
+
timeTravel: timeTravelData,
|
|
993
|
+
tracingOptions: params.tracingOptions,
|
|
994
|
+
outputOptions: params.outputOptions,
|
|
995
|
+
requestContext: params.requestContext ? Object.fromEntries(params.requestContext.entries()) : {},
|
|
996
|
+
perStep: params.perStep
|
|
997
|
+
}
|
|
998
|
+
});
|
|
999
|
+
const eventId = eventOutput.ids[0];
|
|
1000
|
+
if (!eventId) {
|
|
1001
|
+
throw new Error("Event ID is not set");
|
|
1002
|
+
}
|
|
1003
|
+
const runOutput = await this.getRunOutput(eventId);
|
|
1004
|
+
const result = runOutput?.output?.result;
|
|
1005
|
+
this.hydrateFailedResult(result);
|
|
1006
|
+
return result;
|
|
1007
|
+
}
|
|
1008
|
+
watch(cb) {
|
|
1009
|
+
let active = true;
|
|
1010
|
+
const streamPromise = realtime.subscribe(
|
|
1011
|
+
{
|
|
1012
|
+
channel: `workflow:${this.workflowId}:${this.runId}`,
|
|
1013
|
+
topics: ["watch"],
|
|
1014
|
+
app: this.inngest
|
|
1015
|
+
},
|
|
1016
|
+
(message) => {
|
|
1017
|
+
if (active) {
|
|
1018
|
+
cb(message.data);
|
|
1019
|
+
}
|
|
1018
1020
|
}
|
|
1019
1021
|
);
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1022
|
+
return () => {
|
|
1023
|
+
active = false;
|
|
1024
|
+
streamPromise.then(async (stream) => {
|
|
1025
|
+
return stream.cancel();
|
|
1026
|
+
}).catch((err) => {
|
|
1027
|
+
console.error(err);
|
|
1028
|
+
});
|
|
1029
|
+
};
|
|
1030
|
+
}
|
|
1031
|
+
streamLegacy({ inputData, requestContext } = {}) {
|
|
1032
|
+
const { readable, writable } = new TransformStream();
|
|
1033
|
+
const writer = writable.getWriter();
|
|
1034
|
+
void writer.write({
|
|
1035
|
+
// @ts-expect-error
|
|
1036
|
+
type: "start",
|
|
1037
|
+
payload: { runId: this.runId }
|
|
1038
|
+
});
|
|
1039
|
+
const unwatch = this.watch(async (event) => {
|
|
1024
1040
|
try {
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
function: step.getFunction(),
|
|
1033
|
-
data: {
|
|
1034
|
-
inputData,
|
|
1035
|
-
initialState: executionContext.state ?? snapshot?.value ?? {},
|
|
1036
|
-
runId,
|
|
1037
|
-
resume: {
|
|
1038
|
-
runId,
|
|
1039
|
-
steps: resume.steps.slice(1),
|
|
1040
|
-
stepResults: snapshot?.context,
|
|
1041
|
-
resumePayload: resume.resumePayload,
|
|
1042
|
-
// @ts-ignore
|
|
1043
|
-
resumePath: snapshot?.suspendedPaths?.[resume.steps?.[1]]
|
|
1044
|
-
},
|
|
1045
|
-
outputOptions: { includeState: true }
|
|
1046
|
-
}
|
|
1047
|
-
});
|
|
1048
|
-
result = invokeResp.result;
|
|
1049
|
-
runId = invokeResp.runId;
|
|
1050
|
-
executionContext.state = invokeResp.result.state;
|
|
1051
|
-
} else {
|
|
1052
|
-
const invokeResp = await this.inngestStep.invoke(`workflow.${executionContext.workflowId}.step.${step.id}`, {
|
|
1053
|
-
function: step.getFunction(),
|
|
1054
|
-
data: {
|
|
1055
|
-
inputData,
|
|
1056
|
-
initialState: executionContext.state ?? {},
|
|
1057
|
-
outputOptions: { includeState: true }
|
|
1058
|
-
}
|
|
1059
|
-
});
|
|
1060
|
-
result = invokeResp.result;
|
|
1061
|
-
runId = invokeResp.runId;
|
|
1062
|
-
executionContext.state = invokeResp.result.state;
|
|
1063
|
-
}
|
|
1064
|
-
} catch (e) {
|
|
1065
|
-
const errorCause = e?.cause;
|
|
1066
|
-
if (errorCause && typeof errorCause === "object") {
|
|
1067
|
-
result = errorCause;
|
|
1068
|
-
runId = errorCause.runId || crypto.randomUUID();
|
|
1069
|
-
} else {
|
|
1070
|
-
runId = crypto.randomUUID();
|
|
1071
|
-
result = {
|
|
1072
|
-
status: "failed",
|
|
1073
|
-
error: e instanceof Error ? e : new Error(String(e)),
|
|
1074
|
-
steps: {},
|
|
1075
|
-
input: inputData
|
|
1076
|
-
};
|
|
1041
|
+
const e = {
|
|
1042
|
+
...event,
|
|
1043
|
+
type: event.type.replace("workflow-", "")
|
|
1044
|
+
};
|
|
1045
|
+
if (e.type === "step-output") {
|
|
1046
|
+
e.type = e.payload.output.type;
|
|
1047
|
+
e.payload = e.payload.output.payload;
|
|
1077
1048
|
}
|
|
1049
|
+
await writer.write(e);
|
|
1050
|
+
} catch {
|
|
1078
1051
|
}
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
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
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
},
|
|
1130
|
-
workflowState: {
|
|
1131
|
-
status: "running",
|
|
1132
|
-
steps: stepResults,
|
|
1133
|
-
result: null,
|
|
1134
|
-
error: null
|
|
1135
|
-
}
|
|
1136
|
-
},
|
|
1137
|
-
eventTimestamp: Date.now()
|
|
1138
|
-
});
|
|
1139
|
-
await emitter.emit("watch-v2", {
|
|
1140
|
-
type: "workflow-step-suspended",
|
|
1141
|
-
payload: {
|
|
1142
|
-
id: step.id,
|
|
1143
|
-
status: "suspended"
|
|
1144
|
-
}
|
|
1145
|
-
});
|
|
1146
|
-
return {
|
|
1147
|
-
executionContext,
|
|
1148
|
-
result: {
|
|
1149
|
-
status: "suspended",
|
|
1150
|
-
payload: stepResult.payload,
|
|
1151
|
-
suspendPayload: {
|
|
1152
|
-
...stepResult?.suspendPayload,
|
|
1153
|
-
__workflow_meta: { runId, path: suspendPath }
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
};
|
|
1157
|
-
}
|
|
1158
|
-
await emitter.emit("watch", {
|
|
1159
|
-
type: "watch",
|
|
1160
|
-
payload: {
|
|
1161
|
-
currentStep: {
|
|
1162
|
-
id: step.id,
|
|
1163
|
-
status: "suspended",
|
|
1164
|
-
payload: {}
|
|
1165
|
-
},
|
|
1166
|
-
workflowState: {
|
|
1167
|
-
status: "running",
|
|
1168
|
-
steps: stepResults,
|
|
1169
|
-
result: null,
|
|
1170
|
-
error: null
|
|
1171
|
-
}
|
|
1172
|
-
},
|
|
1173
|
-
eventTimestamp: Date.now()
|
|
1174
|
-
});
|
|
1175
|
-
return {
|
|
1176
|
-
executionContext,
|
|
1177
|
-
result: {
|
|
1178
|
-
status: "suspended",
|
|
1179
|
-
payload: {}
|
|
1180
|
-
}
|
|
1181
|
-
};
|
|
1182
|
-
}
|
|
1183
|
-
await emitter.emit("watch", {
|
|
1184
|
-
type: "watch",
|
|
1185
|
-
payload: {
|
|
1186
|
-
currentStep: {
|
|
1187
|
-
id: step.id,
|
|
1188
|
-
status: "success",
|
|
1189
|
-
output: result?.result
|
|
1190
|
-
},
|
|
1191
|
-
workflowState: {
|
|
1192
|
-
status: "running",
|
|
1193
|
-
steps: stepResults,
|
|
1194
|
-
result: null,
|
|
1195
|
-
error: null
|
|
1196
|
-
}
|
|
1197
|
-
},
|
|
1198
|
-
eventTimestamp: Date.now()
|
|
1199
|
-
});
|
|
1200
|
-
await emitter.emit("watch-v2", {
|
|
1201
|
-
type: "workflow-step-result",
|
|
1202
|
-
payload: {
|
|
1203
|
-
id: step.id,
|
|
1204
|
-
status: "success",
|
|
1205
|
-
output: result?.result
|
|
1206
|
-
}
|
|
1207
|
-
});
|
|
1208
|
-
await emitter.emit("watch-v2", {
|
|
1209
|
-
type: "workflow-step-finish",
|
|
1052
|
+
});
|
|
1053
|
+
this.closeStreamAction = async () => {
|
|
1054
|
+
await writer.write({
|
|
1055
|
+
type: "finish",
|
|
1056
|
+
// @ts-expect-error
|
|
1057
|
+
payload: { runId: this.runId }
|
|
1058
|
+
});
|
|
1059
|
+
unwatch();
|
|
1060
|
+
try {
|
|
1061
|
+
await writer.close();
|
|
1062
|
+
} catch (err) {
|
|
1063
|
+
console.error("Error closing stream:", err);
|
|
1064
|
+
} finally {
|
|
1065
|
+
writer.releaseLock();
|
|
1066
|
+
}
|
|
1067
|
+
};
|
|
1068
|
+
this.executionResults = this._start({ inputData, requestContext, format: "legacy" }).then((result) => {
|
|
1069
|
+
if (result.status !== "suspended") {
|
|
1070
|
+
this.closeStreamAction?.().catch(() => {
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
return result;
|
|
1074
|
+
});
|
|
1075
|
+
return {
|
|
1076
|
+
stream: readable,
|
|
1077
|
+
getWorkflowState: () => this.executionResults
|
|
1078
|
+
};
|
|
1079
|
+
}
|
|
1080
|
+
stream({
|
|
1081
|
+
inputData,
|
|
1082
|
+
requestContext,
|
|
1083
|
+
tracingOptions,
|
|
1084
|
+
closeOnSuspend = true,
|
|
1085
|
+
initialState,
|
|
1086
|
+
outputOptions,
|
|
1087
|
+
perStep
|
|
1088
|
+
} = {}) {
|
|
1089
|
+
if (this.closeStreamAction && this.streamOutput) {
|
|
1090
|
+
return this.streamOutput;
|
|
1091
|
+
}
|
|
1092
|
+
this.closeStreamAction = async () => {
|
|
1093
|
+
};
|
|
1094
|
+
const self = this;
|
|
1095
|
+
const stream$1 = new web.ReadableStream({
|
|
1096
|
+
async start(controller) {
|
|
1097
|
+
const unwatch = self.watch(async ({ type, from = stream.ChunkFrom.WORKFLOW, payload }) => {
|
|
1098
|
+
controller.enqueue({
|
|
1099
|
+
type,
|
|
1100
|
+
runId: self.runId,
|
|
1101
|
+
from,
|
|
1210
1102
|
payload: {
|
|
1211
|
-
|
|
1212
|
-
|
|
1103
|
+
stepName: payload?.id,
|
|
1104
|
+
...payload
|
|
1213
1105
|
}
|
|
1214
1106
|
});
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1107
|
+
});
|
|
1108
|
+
self.closeStreamAction = async () => {
|
|
1109
|
+
unwatch();
|
|
1110
|
+
try {
|
|
1111
|
+
await controller.close();
|
|
1112
|
+
} catch (err) {
|
|
1113
|
+
console.error("Error closing stream:", err);
|
|
1114
|
+
}
|
|
1115
|
+
};
|
|
1116
|
+
const executionResultsPromise = self._start({
|
|
1117
|
+
inputData,
|
|
1118
|
+
requestContext,
|
|
1119
|
+
// tracingContext, // We are not able to pass a reference to a span here, what to do?
|
|
1120
|
+
initialState,
|
|
1121
|
+
tracingOptions,
|
|
1122
|
+
outputOptions,
|
|
1123
|
+
format: "vnext",
|
|
1124
|
+
perStep
|
|
1125
|
+
});
|
|
1126
|
+
let executionResults;
|
|
1234
1127
|
try {
|
|
1235
|
-
|
|
1236
|
-
|
|
1128
|
+
executionResults = await executionResultsPromise;
|
|
1129
|
+
if (closeOnSuspend) {
|
|
1130
|
+
self.closeStreamAction?.().catch(() => {
|
|
1131
|
+
});
|
|
1132
|
+
} else if (executionResults.status !== "suspended") {
|
|
1133
|
+
self.closeStreamAction?.().catch(() => {
|
|
1134
|
+
});
|
|
1237
1135
|
}
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
},
|
|
1247
|
-
inputData,
|
|
1248
|
-
resumeData: resume?.steps[0] === step.id ? resume?.resumePayload : void 0,
|
|
1249
|
-
tracingContext: {
|
|
1250
|
-
currentSpan: stepAISpan
|
|
1251
|
-
},
|
|
1252
|
-
getInitData: () => stepResults?.input,
|
|
1253
|
-
getStepResult: workflows.getStepResult.bind(this, stepResults),
|
|
1254
|
-
suspend: async (suspendPayload, suspendOptions) => {
|
|
1255
|
-
executionContext.suspendedPaths[step.id] = executionContext.executionPath;
|
|
1256
|
-
if (suspendOptions?.resumeLabel) {
|
|
1257
|
-
const resumeLabel = Array.isArray(suspendOptions.resumeLabel) ? suspendOptions.resumeLabel : [suspendOptions.resumeLabel];
|
|
1258
|
-
for (const label of resumeLabel) {
|
|
1259
|
-
executionContext.resumeLabels[label] = {
|
|
1260
|
-
stepId: step.id,
|
|
1261
|
-
foreachIndex: executionContext.foreachIndex
|
|
1262
|
-
};
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1265
|
-
suspended = { payload: suspendPayload };
|
|
1266
|
-
},
|
|
1267
|
-
bail: (result2) => {
|
|
1268
|
-
bailed = { payload: result2 };
|
|
1269
|
-
},
|
|
1270
|
-
resume: {
|
|
1271
|
-
steps: resume?.steps?.slice(1) || [],
|
|
1272
|
-
resumePayload: resume?.resumePayload,
|
|
1273
|
-
// @ts-ignore
|
|
1274
|
-
runId: stepResults[step.id]?.suspendPayload?.__workflow_meta?.runId
|
|
1275
|
-
},
|
|
1276
|
-
[_constants.EMITTER_SYMBOL]: emitter,
|
|
1277
|
-
engine: {
|
|
1278
|
-
step: this.inngestStep
|
|
1279
|
-
},
|
|
1280
|
-
abortSignal: abortController.signal
|
|
1281
|
-
});
|
|
1282
|
-
const endedAt = Date.now();
|
|
1283
|
-
execResults = {
|
|
1284
|
-
status: "success",
|
|
1285
|
-
output: result,
|
|
1286
|
-
startedAt,
|
|
1287
|
-
endedAt,
|
|
1288
|
-
payload: inputData,
|
|
1289
|
-
resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
|
|
1290
|
-
resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
|
|
1291
|
-
};
|
|
1292
|
-
} catch (e) {
|
|
1293
|
-
const stepFailure = {
|
|
1294
|
-
status: "failed",
|
|
1295
|
-
payload: inputData,
|
|
1296
|
-
error: e instanceof Error ? e.message : String(e),
|
|
1297
|
-
endedAt: Date.now(),
|
|
1298
|
-
startedAt,
|
|
1299
|
-
resumedAt: resume?.steps[0] === step.id ? startedAt : void 0,
|
|
1300
|
-
resumePayload: resume?.steps[0] === step.id ? resume?.resumePayload : void 0
|
|
1301
|
-
};
|
|
1302
|
-
execResults = stepFailure;
|
|
1303
|
-
const fallbackErrorMessage = `Step ${step.id} failed`;
|
|
1304
|
-
stepAISpan?.error({ error: new Error(execResults.error ?? fallbackErrorMessage) });
|
|
1305
|
-
throw new inngest.RetryAfterError(execResults.error ?? fallbackErrorMessage, executionContext.retryConfig.delay, {
|
|
1306
|
-
cause: execResults
|
|
1136
|
+
if (self.streamOutput) {
|
|
1137
|
+
self.streamOutput.updateResults(
|
|
1138
|
+
executionResults
|
|
1139
|
+
);
|
|
1140
|
+
}
|
|
1141
|
+
} catch (err) {
|
|
1142
|
+
self.streamOutput?.rejectResults(err);
|
|
1143
|
+
self.closeStreamAction?.().catch(() => {
|
|
1307
1144
|
});
|
|
1308
1145
|
}
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
},
|
|
1342
|
-
eventTimestamp: Date.now()
|
|
1343
|
-
});
|
|
1344
|
-
if (execResults.status === "suspended") {
|
|
1345
|
-
await emitter.emit("watch-v2", {
|
|
1346
|
-
type: "workflow-step-suspended",
|
|
1146
|
+
}
|
|
1147
|
+
});
|
|
1148
|
+
this.streamOutput = new stream.WorkflowRunOutput({
|
|
1149
|
+
runId: this.runId,
|
|
1150
|
+
workflowId: this.workflowId,
|
|
1151
|
+
stream: stream$1
|
|
1152
|
+
});
|
|
1153
|
+
return this.streamOutput;
|
|
1154
|
+
}
|
|
1155
|
+
timeTravelStream({
|
|
1156
|
+
inputData,
|
|
1157
|
+
resumeData,
|
|
1158
|
+
initialState,
|
|
1159
|
+
step,
|
|
1160
|
+
context,
|
|
1161
|
+
nestedStepsContext,
|
|
1162
|
+
requestContext,
|
|
1163
|
+
// tracingContext,
|
|
1164
|
+
tracingOptions,
|
|
1165
|
+
outputOptions,
|
|
1166
|
+
perStep
|
|
1167
|
+
}) {
|
|
1168
|
+
this.closeStreamAction = async () => {
|
|
1169
|
+
};
|
|
1170
|
+
const self = this;
|
|
1171
|
+
const stream$1 = new web.ReadableStream({
|
|
1172
|
+
async start(controller) {
|
|
1173
|
+
const unwatch = self.watch(async ({ type, from = stream.ChunkFrom.WORKFLOW, payload }) => {
|
|
1174
|
+
controller.enqueue({
|
|
1175
|
+
type,
|
|
1176
|
+
runId: self.runId,
|
|
1177
|
+
from,
|
|
1347
1178
|
payload: {
|
|
1348
|
-
|
|
1349
|
-
...
|
|
1179
|
+
stepName: payload?.id,
|
|
1180
|
+
...payload
|
|
1350
1181
|
}
|
|
1351
1182
|
});
|
|
1352
|
-
}
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1183
|
+
});
|
|
1184
|
+
self.closeStreamAction = async () => {
|
|
1185
|
+
unwatch();
|
|
1186
|
+
try {
|
|
1187
|
+
controller.close();
|
|
1188
|
+
} catch (err) {
|
|
1189
|
+
console.error("Error closing stream:", err);
|
|
1190
|
+
}
|
|
1191
|
+
};
|
|
1192
|
+
const executionResultsPromise = self._timeTravel({
|
|
1193
|
+
inputData,
|
|
1194
|
+
step,
|
|
1195
|
+
context,
|
|
1196
|
+
nestedStepsContext,
|
|
1197
|
+
resumeData,
|
|
1198
|
+
initialState,
|
|
1199
|
+
requestContext,
|
|
1200
|
+
tracingOptions,
|
|
1201
|
+
outputOptions,
|
|
1202
|
+
perStep
|
|
1203
|
+
});
|
|
1204
|
+
self.executionResults = executionResultsPromise;
|
|
1205
|
+
let executionResults;
|
|
1206
|
+
try {
|
|
1207
|
+
executionResults = await executionResultsPromise;
|
|
1208
|
+
self.closeStreamAction?.().catch(() => {
|
|
1359
1209
|
});
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1210
|
+
if (self.streamOutput) {
|
|
1211
|
+
self.streamOutput.updateResults(executionResults);
|
|
1212
|
+
}
|
|
1213
|
+
} catch (err) {
|
|
1214
|
+
self.streamOutput?.rejectResults(err);
|
|
1215
|
+
self.closeStreamAction?.().catch(() => {
|
|
1366
1216
|
});
|
|
1367
1217
|
}
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1218
|
+
}
|
|
1219
|
+
});
|
|
1220
|
+
this.streamOutput = new stream.WorkflowRunOutput({
|
|
1221
|
+
runId: this.runId,
|
|
1222
|
+
workflowId: this.workflowId,
|
|
1223
|
+
stream: stream$1
|
|
1224
|
+
});
|
|
1225
|
+
return this.streamOutput;
|
|
1226
|
+
}
|
|
1227
|
+
/**
|
|
1228
|
+
* Hydrates errors in a failed workflow result back to proper Error instances.
|
|
1229
|
+
* This ensures error.cause chains and custom properties are preserved.
|
|
1230
|
+
*/
|
|
1231
|
+
hydrateFailedResult(result) {
|
|
1232
|
+
if (result.status === "failed") {
|
|
1233
|
+
result.error = error.getErrorFromUnknown(result.error, { serializeStack: false });
|
|
1234
|
+
if (result.steps) {
|
|
1235
|
+
workflows.hydrateSerializedStepErrors(result.steps);
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
};
|
|
1240
|
+
|
|
1241
|
+
// src/workflow.ts
|
|
1242
|
+
var InngestWorkflow = class _InngestWorkflow extends workflows.Workflow {
|
|
1243
|
+
#mastra;
|
|
1244
|
+
inngest;
|
|
1245
|
+
function;
|
|
1246
|
+
cronFunction;
|
|
1247
|
+
flowControlConfig;
|
|
1248
|
+
cronConfig;
|
|
1249
|
+
constructor(params, inngest) {
|
|
1250
|
+
const { concurrency, rateLimit, throttle, debounce, priority, cron, inputData, initialState, ...workflowParams } = params;
|
|
1251
|
+
super(workflowParams);
|
|
1252
|
+
this.engineType = "inngest";
|
|
1253
|
+
const flowControlEntries = Object.entries({ concurrency, rateLimit, throttle, debounce, priority }).filter(
|
|
1254
|
+
([_, value]) => value !== void 0
|
|
1255
|
+
);
|
|
1256
|
+
this.flowControlConfig = flowControlEntries.length > 0 ? Object.fromEntries(flowControlEntries) : void 0;
|
|
1257
|
+
this.#mastra = params.mastra;
|
|
1258
|
+
this.inngest = inngest;
|
|
1259
|
+
if (cron) {
|
|
1260
|
+
this.cronConfig = { cron, inputData, initialState };
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
async listWorkflowRuns(args) {
|
|
1264
|
+
const storage = this.#mastra?.getStorage();
|
|
1265
|
+
if (!storage) {
|
|
1266
|
+
this.logger.debug("Cannot get workflow runs. Mastra engine is not initialized");
|
|
1267
|
+
return { runs: [], total: 0 };
|
|
1268
|
+
}
|
|
1269
|
+
const workflowsStore = await storage.getStore("workflows");
|
|
1270
|
+
if (!workflowsStore) {
|
|
1271
|
+
return { runs: [], total: 0 };
|
|
1272
|
+
}
|
|
1273
|
+
return workflowsStore.listWorkflowRuns({ workflowName: this.id, ...args ?? {} });
|
|
1274
|
+
}
|
|
1275
|
+
__registerMastra(mastra) {
|
|
1276
|
+
super.__registerMastra(mastra);
|
|
1277
|
+
this.#mastra = mastra;
|
|
1278
|
+
this.executionEngine.__registerMastra(mastra);
|
|
1279
|
+
const updateNested = (step) => {
|
|
1280
|
+
if ((step.type === "step" || step.type === "loop" || step.type === "foreach") && step.step instanceof _InngestWorkflow) {
|
|
1281
|
+
step.step.__registerMastra(mastra);
|
|
1282
|
+
} else if (step.type === "parallel" || step.type === "conditional") {
|
|
1283
|
+
for (const subStep of step.steps) {
|
|
1284
|
+
updateNested(subStep);
|
|
1385
1285
|
}
|
|
1386
|
-
}
|
|
1286
|
+
}
|
|
1287
|
+
};
|
|
1288
|
+
if (this.executionGraph.steps.length) {
|
|
1289
|
+
for (const step of this.executionGraph.steps) {
|
|
1290
|
+
updateNested(step);
|
|
1291
|
+
}
|
|
1387
1292
|
}
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1293
|
+
}
|
|
1294
|
+
async createRun(options) {
|
|
1295
|
+
const runIdToUse = options?.runId || crypto$1.randomUUID();
|
|
1296
|
+
const existingInMemoryRun = this.runs.get(runIdToUse);
|
|
1297
|
+
const newRun = new InngestRun(
|
|
1298
|
+
{
|
|
1299
|
+
workflowId: this.id,
|
|
1300
|
+
runId: runIdToUse,
|
|
1301
|
+
resourceId: options?.resourceId,
|
|
1302
|
+
executionEngine: this.executionEngine,
|
|
1303
|
+
executionGraph: this.executionGraph,
|
|
1304
|
+
serializedStepGraph: this.serializedStepGraph,
|
|
1305
|
+
mastra: this.#mastra,
|
|
1306
|
+
retryConfig: this.retryConfig,
|
|
1307
|
+
cleanup: () => this.runs.delete(runIdToUse),
|
|
1308
|
+
workflowSteps: this.steps,
|
|
1309
|
+
workflowEngineType: this.engineType,
|
|
1310
|
+
validateInputs: this.options.validateInputs
|
|
1311
|
+
},
|
|
1312
|
+
this.inngest
|
|
1313
|
+
);
|
|
1314
|
+
const run = existingInMemoryRun ?? newRun;
|
|
1315
|
+
this.runs.set(runIdToUse, run);
|
|
1316
|
+
const shouldPersistSnapshot = this.options.shouldPersistSnapshot({
|
|
1317
|
+
workflowStatus: run.workflowRunStatus,
|
|
1318
|
+
stepResults: {}
|
|
1319
|
+
});
|
|
1320
|
+
const existingStoredRun = await this.getWorkflowRunById(runIdToUse, {
|
|
1321
|
+
withNestedWorkflows: false
|
|
1322
|
+
});
|
|
1323
|
+
const existsInStorage = existingStoredRun && !existingStoredRun.isFromInMemory;
|
|
1324
|
+
if (!existsInStorage && shouldPersistSnapshot) {
|
|
1325
|
+
const workflowsStore = await this.mastra?.getStorage()?.getStore("workflows");
|
|
1326
|
+
await workflowsStore?.persistWorkflowSnapshot({
|
|
1327
|
+
workflowName: this.id,
|
|
1328
|
+
runId: runIdToUse,
|
|
1329
|
+
resourceId: options?.resourceId,
|
|
1330
|
+
snapshot: {
|
|
1331
|
+
runId: runIdToUse,
|
|
1332
|
+
status: "pending",
|
|
1333
|
+
value: {},
|
|
1334
|
+
context: {},
|
|
1335
|
+
activePaths: [],
|
|
1336
|
+
activeStepsPath: {},
|
|
1337
|
+
waitingPaths: {},
|
|
1338
|
+
serializedStepGraph: this.serializedStepGraph,
|
|
1339
|
+
suspendedPaths: {},
|
|
1340
|
+
resumeLabels: {},
|
|
1341
|
+
result: void 0,
|
|
1342
|
+
error: void 0,
|
|
1343
|
+
timestamp: Date.now()
|
|
1402
1344
|
}
|
|
1403
1345
|
});
|
|
1404
1346
|
}
|
|
1405
|
-
|
|
1406
|
-
Object.assign(stepResults, stepRes.stepResults);
|
|
1407
|
-
executionContext.state = stepRes.executionContext.state;
|
|
1408
|
-
return stepRes.result;
|
|
1347
|
+
return run;
|
|
1409
1348
|
}
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1349
|
+
//createCronFunction is only called if cronConfig.cron is defined.
|
|
1350
|
+
createCronFunction() {
|
|
1351
|
+
if (this.cronFunction) {
|
|
1352
|
+
return this.cronFunction;
|
|
1353
|
+
}
|
|
1354
|
+
this.cronFunction = this.inngest.createFunction(
|
|
1355
|
+
{
|
|
1356
|
+
id: `workflow.${this.id}.cron`,
|
|
1357
|
+
retries: 0,
|
|
1358
|
+
cancelOn: [{ event: `cancel.workflow.${this.id}` }],
|
|
1359
|
+
...this.flowControlConfig
|
|
1360
|
+
},
|
|
1361
|
+
{ cron: this.cronConfig?.cron ?? "" },
|
|
1423
1362
|
async () => {
|
|
1424
|
-
const
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
await this.mastra?.getStorage()?.persistWorkflowSnapshot({
|
|
1429
|
-
workflowName: workflowId,
|
|
1430
|
-
runId,
|
|
1431
|
-
resourceId,
|
|
1432
|
-
snapshot: {
|
|
1433
|
-
runId,
|
|
1434
|
-
value: executionContext.state,
|
|
1435
|
-
context: stepResults,
|
|
1436
|
-
activePaths: [],
|
|
1437
|
-
suspendedPaths: executionContext.suspendedPaths,
|
|
1438
|
-
resumeLabels: executionContext.resumeLabels,
|
|
1439
|
-
waitingPaths: {},
|
|
1440
|
-
serializedStepGraph,
|
|
1441
|
-
status: workflowStatus,
|
|
1442
|
-
result,
|
|
1443
|
-
error,
|
|
1444
|
-
// @ts-ignore
|
|
1445
|
-
timestamp: Date.now()
|
|
1446
|
-
}
|
|
1363
|
+
const run = await this.createRun();
|
|
1364
|
+
const result = await run.start({
|
|
1365
|
+
inputData: this.cronConfig?.inputData,
|
|
1366
|
+
initialState: this.cronConfig?.initialState
|
|
1447
1367
|
});
|
|
1368
|
+
return { result, runId: run.runId };
|
|
1448
1369
|
}
|
|
1449
1370
|
);
|
|
1371
|
+
return this.cronFunction;
|
|
1450
1372
|
}
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
writableStream,
|
|
1463
|
-
disableScorers,
|
|
1464
|
-
tracingContext
|
|
1465
|
-
}) {
|
|
1466
|
-
const conditionalSpan = tracingContext?.currentSpan?.createChildSpan({
|
|
1467
|
-
type: aiTracing.AISpanType.WORKFLOW_CONDITIONAL,
|
|
1468
|
-
name: `conditional: '${entry.conditions.length} conditions'`,
|
|
1469
|
-
input: prevOutput,
|
|
1470
|
-
attributes: {
|
|
1471
|
-
conditionCount: entry.conditions.length
|
|
1373
|
+
getFunction() {
|
|
1374
|
+
if (this.function) {
|
|
1375
|
+
return this.function;
|
|
1376
|
+
}
|
|
1377
|
+
this.function = this.inngest.createFunction(
|
|
1378
|
+
{
|
|
1379
|
+
id: `workflow.${this.id}`,
|
|
1380
|
+
retries: 0,
|
|
1381
|
+
cancelOn: [{ event: `cancel.workflow.${this.id}` }],
|
|
1382
|
+
// Spread flow control configuration
|
|
1383
|
+
...this.flowControlConfig
|
|
1472
1384
|
},
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1385
|
+
{ event: `workflow.${this.id}` },
|
|
1386
|
+
async ({ event, step, attempt, publish }) => {
|
|
1387
|
+
let {
|
|
1388
|
+
inputData,
|
|
1389
|
+
initialState,
|
|
1390
|
+
runId,
|
|
1391
|
+
resourceId,
|
|
1392
|
+
resume,
|
|
1393
|
+
outputOptions,
|
|
1394
|
+
format,
|
|
1395
|
+
timeTravel,
|
|
1396
|
+
perStep,
|
|
1397
|
+
tracingOptions
|
|
1398
|
+
} = event.data;
|
|
1399
|
+
if (!runId) {
|
|
1400
|
+
runId = await step.run(`workflow.${this.id}.runIdGen`, async () => {
|
|
1401
|
+
return crypto$1.randomUUID();
|
|
1402
|
+
});
|
|
1403
|
+
}
|
|
1404
|
+
const pubsub = new InngestPubSub(this.inngest, this.id, publish);
|
|
1405
|
+
const requestContext = new di.RequestContext(Object.entries(event.data.requestContext ?? {}));
|
|
1406
|
+
const mastra = this.#mastra;
|
|
1407
|
+
const tracingPolicy = this.options.tracingPolicy;
|
|
1408
|
+
const workflowSpanData = await step.run(`workflow.${this.id}.span.start`, async () => {
|
|
1409
|
+
const observability$1 = mastra?.observability?.getSelectedInstance({ requestContext });
|
|
1410
|
+
if (!observability$1) return void 0;
|
|
1411
|
+
const span = observability$1.startSpan({
|
|
1412
|
+
type: observability.SpanType.WORKFLOW_RUN,
|
|
1413
|
+
name: `workflow run: '${this.id}'`,
|
|
1414
|
+
entityType: observability.EntityType.WORKFLOW_RUN,
|
|
1415
|
+
entityId: this.id,
|
|
1416
|
+
input: inputData,
|
|
1417
|
+
metadata: {
|
|
1418
|
+
resourceId,
|
|
1419
|
+
runId
|
|
1485
1420
|
},
|
|
1486
|
-
tracingPolicy
|
|
1421
|
+
tracingPolicy,
|
|
1422
|
+
tracingOptions,
|
|
1423
|
+
requestContext
|
|
1487
1424
|
});
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1425
|
+
return span?.exportSpan();
|
|
1426
|
+
});
|
|
1427
|
+
const engine = new InngestExecutionEngine(this.#mastra, step, attempt, this.options);
|
|
1428
|
+
let result;
|
|
1429
|
+
try {
|
|
1430
|
+
result = await engine.execute({
|
|
1431
|
+
workflowId: this.id,
|
|
1432
|
+
runId,
|
|
1433
|
+
resourceId,
|
|
1434
|
+
graph: this.executionGraph,
|
|
1435
|
+
serializedStepGraph: this.serializedStepGraph,
|
|
1436
|
+
input: inputData,
|
|
1437
|
+
initialState,
|
|
1438
|
+
pubsub,
|
|
1439
|
+
retryConfig: this.retryConfig,
|
|
1440
|
+
requestContext,
|
|
1441
|
+
resume,
|
|
1442
|
+
timeTravel,
|
|
1443
|
+
perStep,
|
|
1444
|
+
format,
|
|
1445
|
+
abortController: new AbortController(),
|
|
1446
|
+
// For Inngest, we don't pass workflowSpan - step spans use tracingIds instead
|
|
1447
|
+
workflowSpan: void 0,
|
|
1448
|
+
// Pass tracing IDs for durable span operations
|
|
1449
|
+
tracingIds: workflowSpanData ? {
|
|
1450
|
+
traceId: workflowSpanData.traceId,
|
|
1451
|
+
workflowSpanId: workflowSpanData.id
|
|
1452
|
+
} : void 0,
|
|
1453
|
+
outputOptions,
|
|
1454
|
+
outputWriter: async (chunk) => {
|
|
1455
|
+
try {
|
|
1456
|
+
await pubsub.publish(`workflow.events.v2.${runId}`, {
|
|
1457
|
+
type: "watch",
|
|
1492
1458
|
runId,
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
retryCount: -1,
|
|
1498
|
-
inputData: prevOutput,
|
|
1499
|
-
state: executionContext.state,
|
|
1500
|
-
setState: (state) => {
|
|
1501
|
-
executionContext.state = state;
|
|
1502
|
-
},
|
|
1503
|
-
tracingContext: {
|
|
1504
|
-
currentSpan: evalSpan
|
|
1505
|
-
},
|
|
1506
|
-
getInitData: () => stepResults?.input,
|
|
1507
|
-
getStepResult: workflows.getStepResult.bind(this, stepResults),
|
|
1508
|
-
// TODO: this function shouldn't have suspend probably?
|
|
1509
|
-
suspend: async (_suspendPayload) => {
|
|
1510
|
-
},
|
|
1511
|
-
bail: () => {
|
|
1512
|
-
},
|
|
1513
|
-
abort: () => {
|
|
1514
|
-
abortController.abort();
|
|
1515
|
-
},
|
|
1516
|
-
[_constants.EMITTER_SYMBOL]: emitter,
|
|
1517
|
-
[_constants.STREAM_FORMAT_SYMBOL]: executionContext.format,
|
|
1518
|
-
// TODO: add streamVNext support
|
|
1519
|
-
engine: {
|
|
1520
|
-
step: this.inngestStep
|
|
1521
|
-
},
|
|
1522
|
-
abortSignal: abortController.signal,
|
|
1523
|
-
writer: new tools.ToolStream(
|
|
1524
|
-
{
|
|
1525
|
-
prefix: "workflow-step",
|
|
1526
|
-
callId: crypto.randomUUID(),
|
|
1527
|
-
name: "conditional",
|
|
1528
|
-
runId
|
|
1529
|
-
},
|
|
1530
|
-
writableStream
|
|
1531
|
-
)
|
|
1532
|
-
},
|
|
1533
|
-
{
|
|
1534
|
-
paramName: "runCount",
|
|
1535
|
-
deprecationMessage: workflows.runCountDeprecationMessage,
|
|
1536
|
-
logger: this.logger
|
|
1537
|
-
}
|
|
1538
|
-
)
|
|
1539
|
-
);
|
|
1540
|
-
evalSpan?.end({
|
|
1541
|
-
output: result,
|
|
1542
|
-
attributes: {
|
|
1543
|
-
result: !!result
|
|
1459
|
+
data: chunk
|
|
1460
|
+
});
|
|
1461
|
+
} catch (err) {
|
|
1462
|
+
this.logger.debug?.("Failed to publish watch event:", err);
|
|
1544
1463
|
}
|
|
1464
|
+
}
|
|
1465
|
+
});
|
|
1466
|
+
} catch (error) {
|
|
1467
|
+
throw error;
|
|
1468
|
+
}
|
|
1469
|
+
await step.run(`workflow.${this.id}.finalize`, async () => {
|
|
1470
|
+
if (result.status !== "paused") {
|
|
1471
|
+
await engine.invokeLifecycleCallbacksInternal({
|
|
1472
|
+
status: result.status,
|
|
1473
|
+
result: "result" in result ? result.result : void 0,
|
|
1474
|
+
error: "error" in result ? result.error : void 0,
|
|
1475
|
+
steps: result.steps,
|
|
1476
|
+
tripwire: "tripwire" in result ? result.tripwire : void 0,
|
|
1477
|
+
runId,
|
|
1478
|
+
workflowId: this.id,
|
|
1479
|
+
resourceId,
|
|
1480
|
+
input: inputData,
|
|
1481
|
+
requestContext,
|
|
1482
|
+
state: result.state ?? initialState ?? {}
|
|
1545
1483
|
});
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1484
|
+
}
|
|
1485
|
+
if (workflowSpanData) {
|
|
1486
|
+
const observability = mastra?.observability?.getSelectedInstance({ requestContext });
|
|
1487
|
+
if (observability) {
|
|
1488
|
+
const workflowSpan = observability.rebuildSpan(workflowSpanData);
|
|
1489
|
+
if (result.status === "failed") {
|
|
1490
|
+
workflowSpan.error({
|
|
1491
|
+
error: result.error instanceof Error ? result.error : new Error(String(result.error)),
|
|
1492
|
+
attributes: { status: "failed" }
|
|
1493
|
+
});
|
|
1494
|
+
} else {
|
|
1495
|
+
workflowSpan.end({
|
|
1496
|
+
output: result.status === "success" ? result.result : void 0,
|
|
1497
|
+
attributes: { status: result.status }
|
|
1498
|
+
});
|
|
1552
1499
|
}
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
if (result.status === "failed") {
|
|
1503
|
+
throw new inngest.NonRetriableError(`Workflow failed`, {
|
|
1504
|
+
cause: result
|
|
1553
1505
|
});
|
|
1554
|
-
return null;
|
|
1555
1506
|
}
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1507
|
+
return result;
|
|
1508
|
+
});
|
|
1509
|
+
return { result, runId };
|
|
1510
|
+
}
|
|
1511
|
+
);
|
|
1512
|
+
return this.function;
|
|
1513
|
+
}
|
|
1514
|
+
getNestedFunctions(steps) {
|
|
1515
|
+
return steps.flatMap((step) => {
|
|
1516
|
+
if (step.type === "step" || step.type === "loop" || step.type === "foreach") {
|
|
1517
|
+
if (step.step instanceof _InngestWorkflow) {
|
|
1518
|
+
return [step.step.getFunction(), ...step.step.getNestedFunctions(step.step.executionGraph.steps)];
|
|
1519
|
+
}
|
|
1520
|
+
return [];
|
|
1521
|
+
} else if (step.type === "parallel" || step.type === "conditional") {
|
|
1522
|
+
return this.getNestedFunctions(step.steps);
|
|
1564
1523
|
}
|
|
1524
|
+
return [];
|
|
1565
1525
|
});
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1526
|
+
}
|
|
1527
|
+
getFunctions() {
|
|
1528
|
+
return [
|
|
1529
|
+
this.getFunction(),
|
|
1530
|
+
...this.cronConfig?.cron ? [this.createCronFunction()] : [],
|
|
1531
|
+
...this.getNestedFunctions(this.executionGraph.steps)
|
|
1532
|
+
];
|
|
1533
|
+
}
|
|
1534
|
+
};
|
|
1535
|
+
function prepareServeOptions({ mastra, inngest, functions: userFunctions = [], registerOptions }) {
|
|
1536
|
+
const wfs = mastra.listWorkflows();
|
|
1537
|
+
const workflowFunctions = Array.from(
|
|
1538
|
+
new Set(
|
|
1539
|
+
Object.values(wfs).flatMap((wf) => {
|
|
1540
|
+
if (wf instanceof InngestWorkflow) {
|
|
1541
|
+
wf.__registerMastra(mastra);
|
|
1542
|
+
return wf.getFunctions();
|
|
1571
1543
|
}
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1544
|
+
return [];
|
|
1545
|
+
})
|
|
1546
|
+
)
|
|
1547
|
+
);
|
|
1548
|
+
return {
|
|
1549
|
+
...registerOptions,
|
|
1550
|
+
client: inngest,
|
|
1551
|
+
functions: [...workflowFunctions, ...userFunctions]
|
|
1552
|
+
};
|
|
1553
|
+
}
|
|
1554
|
+
function createServe(adapter) {
|
|
1555
|
+
return (options) => {
|
|
1556
|
+
const serveOptions = prepareServeOptions(options);
|
|
1557
|
+
return adapter(serveOptions);
|
|
1558
|
+
};
|
|
1559
|
+
}
|
|
1560
|
+
var serve = createServe(hono.serve);
|
|
1561
|
+
|
|
1562
|
+
// src/types.ts
|
|
1563
|
+
var _compatibilityCheck = true;
|
|
1564
|
+
|
|
1565
|
+
// src/index.ts
|
|
1566
|
+
function isInngestWorkflow(input) {
|
|
1567
|
+
return input instanceof InngestWorkflow;
|
|
1568
|
+
}
|
|
1569
|
+
function isAgent(input) {
|
|
1570
|
+
return input instanceof agent.Agent;
|
|
1571
|
+
}
|
|
1572
|
+
function isToolStep(input) {
|
|
1573
|
+
return input instanceof tools.Tool;
|
|
1574
|
+
}
|
|
1575
|
+
function isStepParams(input) {
|
|
1576
|
+
return input !== null && typeof input === "object" && "id" in input && "execute" in input && !(input instanceof agent.Agent) && !(input instanceof tools.Tool) && !(input instanceof InngestWorkflow);
|
|
1577
|
+
}
|
|
1578
|
+
function isProcessor(obj) {
|
|
1579
|
+
return obj !== null && typeof obj === "object" && "id" in obj && typeof obj.id === "string" && !(obj instanceof agent.Agent) && !(obj instanceof tools.Tool) && !(obj instanceof InngestWorkflow) && (typeof obj.processInput === "function" || typeof obj.processInputStep === "function" || typeof obj.processOutputStream === "function" || typeof obj.processOutputResult === "function" || typeof obj.processOutputStep === "function");
|
|
1580
|
+
}
|
|
1581
|
+
function createStep(params, agentOrToolOptions) {
|
|
1582
|
+
if (isInngestWorkflow(params)) {
|
|
1583
|
+
return params;
|
|
1584
|
+
}
|
|
1585
|
+
if (isAgent(params)) {
|
|
1586
|
+
return createStepFromAgent(params, agentOrToolOptions);
|
|
1587
|
+
}
|
|
1588
|
+
if (isToolStep(params)) {
|
|
1589
|
+
return createStepFromTool(params, agentOrToolOptions);
|
|
1590
|
+
}
|
|
1591
|
+
if (isStepParams(params)) {
|
|
1592
|
+
return createStepFromParams(params);
|
|
1593
|
+
}
|
|
1594
|
+
if (isProcessor(params)) {
|
|
1595
|
+
return createStepFromProcessor(params);
|
|
1596
|
+
}
|
|
1597
|
+
throw new Error("Invalid input: expected StepParams, Agent, ToolStep, Processor, or InngestWorkflow");
|
|
1598
|
+
}
|
|
1599
|
+
function createStepFromParams(params) {
|
|
1600
|
+
return {
|
|
1601
|
+
id: params.id,
|
|
1602
|
+
description: params.description,
|
|
1603
|
+
inputSchema: schema.toStandardSchema(params.inputSchema),
|
|
1604
|
+
stateSchema: params.stateSchema ? schema.toStandardSchema(params.stateSchema) : void 0,
|
|
1605
|
+
outputSchema: schema.toStandardSchema(params.outputSchema),
|
|
1606
|
+
resumeSchema: params.resumeSchema ? schema.toStandardSchema(params.resumeSchema) : void 0,
|
|
1607
|
+
suspendSchema: params.suspendSchema ? schema.toStandardSchema(params.suspendSchema) : void 0,
|
|
1608
|
+
scorers: params.scorers,
|
|
1609
|
+
retries: params.retries,
|
|
1610
|
+
execute: params.execute.bind(params)
|
|
1611
|
+
};
|
|
1612
|
+
}
|
|
1613
|
+
function createStepFromAgent(params, agentOrToolOptions) {
|
|
1614
|
+
const options = agentOrToolOptions ?? {};
|
|
1615
|
+
const outputSchema = options?.structuredOutput?.schema ?? zod.z.object({ text: zod.z.string() });
|
|
1616
|
+
const { retries, scorers, ...agentOptions } = options ?? {};
|
|
1617
|
+
return {
|
|
1618
|
+
id: params.name,
|
|
1619
|
+
description: params.getDescription(),
|
|
1620
|
+
inputSchema: schema.toStandardSchema(
|
|
1621
|
+
zod.z.object({
|
|
1622
|
+
prompt: zod.z.string()
|
|
1623
|
+
})
|
|
1624
|
+
),
|
|
1625
|
+
outputSchema: schema.toStandardSchema(outputSchema),
|
|
1626
|
+
retries,
|
|
1627
|
+
scorers,
|
|
1628
|
+
execute: async ({
|
|
1629
|
+
inputData,
|
|
1630
|
+
runId,
|
|
1631
|
+
[_constants.PUBSUB_SYMBOL]: pubsub,
|
|
1632
|
+
[_constants.STREAM_FORMAT_SYMBOL]: streamFormat,
|
|
1633
|
+
requestContext,
|
|
1634
|
+
tracingContext,
|
|
1635
|
+
abortSignal,
|
|
1636
|
+
abort,
|
|
1637
|
+
writer
|
|
1638
|
+
}) => {
|
|
1639
|
+
let streamPromise = {};
|
|
1640
|
+
streamPromise.promise = new Promise((resolve, reject) => {
|
|
1641
|
+
streamPromise.resolve = resolve;
|
|
1642
|
+
streamPromise.reject = reject;
|
|
1643
|
+
});
|
|
1644
|
+
let structuredResult = null;
|
|
1645
|
+
const toolData = {
|
|
1646
|
+
name: params.name,
|
|
1647
|
+
args: inputData
|
|
1648
|
+
};
|
|
1649
|
+
let stream;
|
|
1650
|
+
if ((await params.getModel()).specificationVersion === "v1") {
|
|
1651
|
+
const { fullStream } = await params.streamLegacy(inputData.prompt, {
|
|
1652
|
+
...agentOptions ?? {},
|
|
1653
|
+
requestContext,
|
|
1654
|
+
tracingContext,
|
|
1655
|
+
onFinish: (result) => {
|
|
1656
|
+
const resultWithObject = result;
|
|
1657
|
+
if (agentOptions?.structuredOutput?.schema && resultWithObject.object) {
|
|
1658
|
+
structuredResult = resultWithObject.object;
|
|
1659
|
+
}
|
|
1660
|
+
streamPromise.resolve(result.text);
|
|
1661
|
+
void agentOptions?.onFinish?.(result);
|
|
1585
1662
|
},
|
|
1586
|
-
|
|
1587
|
-
abortController,
|
|
1588
|
-
runtimeContext,
|
|
1589
|
-
writableStream,
|
|
1590
|
-
disableScorers,
|
|
1591
|
-
tracingContext: {
|
|
1592
|
-
currentSpan: conditionalSpan
|
|
1593
|
-
}
|
|
1663
|
+
abortSignal
|
|
1594
1664
|
});
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1665
|
+
stream = fullStream;
|
|
1666
|
+
} else {
|
|
1667
|
+
const modelOutput = await params.stream(inputData.prompt, {
|
|
1668
|
+
...agentOptions ?? {},
|
|
1669
|
+
requestContext,
|
|
1670
|
+
tracingContext,
|
|
1671
|
+
onFinish: (result) => {
|
|
1672
|
+
const resultWithObject = result;
|
|
1673
|
+
if (agentOptions?.structuredOutput?.schema && resultWithObject.object) {
|
|
1674
|
+
structuredResult = resultWithObject.object;
|
|
1675
|
+
}
|
|
1676
|
+
streamPromise.resolve(result.text);
|
|
1677
|
+
void agentOptions?.onFinish?.(result);
|
|
1678
|
+
},
|
|
1679
|
+
abortSignal
|
|
1680
|
+
});
|
|
1681
|
+
stream = modelOutput.fullStream;
|
|
1682
|
+
}
|
|
1683
|
+
if (streamFormat === "legacy") {
|
|
1684
|
+
await pubsub.publish(`workflow.events.v2.${runId}`, {
|
|
1685
|
+
type: "watch",
|
|
1686
|
+
runId,
|
|
1687
|
+
data: { type: "tool-call-streaming-start", ...toolData ?? {} }
|
|
1688
|
+
});
|
|
1689
|
+
for await (const chunk of stream) {
|
|
1690
|
+
if (chunk.type === "text-delta") {
|
|
1691
|
+
await pubsub.publish(`workflow.events.v2.${runId}`, {
|
|
1692
|
+
type: "watch",
|
|
1693
|
+
runId,
|
|
1694
|
+
data: { type: "tool-call-delta", ...toolData ?? {}, argsTextDelta: chunk.textDelta }
|
|
1695
|
+
});
|
|
1611
1696
|
}
|
|
1612
|
-
|
|
1613
|
-
}
|
|
1697
|
+
}
|
|
1698
|
+
await pubsub.publish(`workflow.events.v2.${runId}`, {
|
|
1699
|
+
type: "watch",
|
|
1700
|
+
runId,
|
|
1701
|
+
data: { type: "tool-call-streaming-finish", ...toolData ?? {} }
|
|
1702
|
+
});
|
|
1703
|
+
} else {
|
|
1704
|
+
for await (const chunk of stream) {
|
|
1705
|
+
await writer.write(chunk);
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
if (abortSignal.aborted) {
|
|
1709
|
+
return abort();
|
|
1710
|
+
}
|
|
1711
|
+
if (structuredResult !== null) {
|
|
1712
|
+
return structuredResult;
|
|
1713
|
+
}
|
|
1714
|
+
return {
|
|
1715
|
+
text: await streamPromise.promise
|
|
1716
|
+
};
|
|
1717
|
+
},
|
|
1718
|
+
component: params.component
|
|
1719
|
+
};
|
|
1720
|
+
}
|
|
1721
|
+
function createStepFromTool(params, agentOrToolOptions) {
|
|
1722
|
+
const toolOpts = agentOrToolOptions;
|
|
1723
|
+
if (!params.inputSchema || !params.outputSchema) {
|
|
1724
|
+
throw new Error("Tool must have input and output schemas defined");
|
|
1725
|
+
}
|
|
1726
|
+
return {
|
|
1727
|
+
id: params.id,
|
|
1728
|
+
description: params.description,
|
|
1729
|
+
inputSchema: params.inputSchema,
|
|
1730
|
+
outputSchema: params.outputSchema,
|
|
1731
|
+
resumeSchema: params.resumeSchema,
|
|
1732
|
+
suspendSchema: params.suspendSchema,
|
|
1733
|
+
retries: toolOpts?.retries,
|
|
1734
|
+
scorers: toolOpts?.scorers,
|
|
1735
|
+
execute: async ({
|
|
1736
|
+
inputData,
|
|
1737
|
+
mastra,
|
|
1738
|
+
requestContext,
|
|
1739
|
+
tracingContext,
|
|
1740
|
+
suspend,
|
|
1741
|
+
resumeData,
|
|
1742
|
+
runId,
|
|
1743
|
+
workflowId,
|
|
1744
|
+
state,
|
|
1745
|
+
setState
|
|
1746
|
+
}) => {
|
|
1747
|
+
const toolContext = {
|
|
1748
|
+
mastra,
|
|
1749
|
+
requestContext,
|
|
1750
|
+
tracingContext,
|
|
1751
|
+
workflow: {
|
|
1752
|
+
runId,
|
|
1753
|
+
resumeData,
|
|
1754
|
+
suspend,
|
|
1755
|
+
workflowId,
|
|
1756
|
+
state,
|
|
1757
|
+
setState
|
|
1758
|
+
}
|
|
1614
1759
|
};
|
|
1760
|
+
return params.execute(inputData, toolContext);
|
|
1761
|
+
},
|
|
1762
|
+
component: "TOOL"
|
|
1763
|
+
};
|
|
1764
|
+
}
|
|
1765
|
+
function createStepFromProcessor(processor) {
|
|
1766
|
+
const getProcessorEntityType = (phase) => {
|
|
1767
|
+
switch (phase) {
|
|
1768
|
+
case "input":
|
|
1769
|
+
return observability.EntityType.INPUT_PROCESSOR;
|
|
1770
|
+
case "inputStep":
|
|
1771
|
+
return observability.EntityType.INPUT_STEP_PROCESSOR;
|
|
1772
|
+
case "outputStream":
|
|
1773
|
+
case "outputResult":
|
|
1774
|
+
return observability.EntityType.OUTPUT_PROCESSOR;
|
|
1775
|
+
case "outputStep":
|
|
1776
|
+
return observability.EntityType.OUTPUT_STEP_PROCESSOR;
|
|
1777
|
+
default:
|
|
1778
|
+
return observability.EntityType.OUTPUT_PROCESSOR;
|
|
1779
|
+
}
|
|
1780
|
+
};
|
|
1781
|
+
const getSpanNamePrefix = (phase) => {
|
|
1782
|
+
switch (phase) {
|
|
1783
|
+
case "input":
|
|
1784
|
+
return "input processor";
|
|
1785
|
+
case "inputStep":
|
|
1786
|
+
return "input step processor";
|
|
1787
|
+
case "outputStream":
|
|
1788
|
+
return "output stream processor";
|
|
1789
|
+
case "outputResult":
|
|
1790
|
+
return "output processor";
|
|
1791
|
+
case "outputStep":
|
|
1792
|
+
return "output step processor";
|
|
1793
|
+
default:
|
|
1794
|
+
return "processor";
|
|
1615
1795
|
}
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1796
|
+
};
|
|
1797
|
+
const hasPhaseMethod = (phase) => {
|
|
1798
|
+
switch (phase) {
|
|
1799
|
+
case "input":
|
|
1800
|
+
return !!processor.processInput;
|
|
1801
|
+
case "inputStep":
|
|
1802
|
+
return !!processor.processInputStep;
|
|
1803
|
+
case "outputStream":
|
|
1804
|
+
return !!processor.processOutputStream;
|
|
1805
|
+
case "outputResult":
|
|
1806
|
+
return !!processor.processOutputResult;
|
|
1807
|
+
case "outputStep":
|
|
1808
|
+
return !!processor.processOutputStep;
|
|
1809
|
+
default:
|
|
1810
|
+
return false;
|
|
1811
|
+
}
|
|
1812
|
+
};
|
|
1813
|
+
return {
|
|
1814
|
+
id: `processor:${processor.id}`,
|
|
1815
|
+
description: processor.name ?? `Processor ${processor.id}`,
|
|
1816
|
+
inputSchema: processors.ProcessorStepSchema,
|
|
1817
|
+
outputSchema: processors.ProcessorStepOutputSchema,
|
|
1818
|
+
execute: async ({ inputData, requestContext, tracingContext }) => {
|
|
1819
|
+
const input = inputData;
|
|
1820
|
+
const {
|
|
1821
|
+
phase,
|
|
1822
|
+
messages,
|
|
1823
|
+
messageList,
|
|
1824
|
+
stepNumber,
|
|
1825
|
+
systemMessages,
|
|
1826
|
+
part,
|
|
1827
|
+
streamParts,
|
|
1828
|
+
state,
|
|
1829
|
+
finishReason,
|
|
1830
|
+
toolCalls,
|
|
1831
|
+
text,
|
|
1832
|
+
retryCount,
|
|
1833
|
+
// inputStep phase fields for model/tools configuration
|
|
1834
|
+
model,
|
|
1835
|
+
tools,
|
|
1836
|
+
toolChoice,
|
|
1837
|
+
activeTools,
|
|
1838
|
+
providerOptions,
|
|
1839
|
+
modelSettings,
|
|
1840
|
+
structuredOutput,
|
|
1841
|
+
steps
|
|
1842
|
+
} = input;
|
|
1843
|
+
const abort = (reason, options) => {
|
|
1844
|
+
throw new agent.TripWire(reason || `Tripwire triggered by ${processor.id}`, options, processor.id);
|
|
1845
|
+
};
|
|
1846
|
+
if (!hasPhaseMethod(phase)) {
|
|
1847
|
+
return input;
|
|
1848
|
+
}
|
|
1849
|
+
const currentSpan = tracingContext?.currentSpan;
|
|
1850
|
+
const parentSpan = phase === "inputStep" || phase === "outputStep" ? currentSpan?.findParent(observability.SpanType.MODEL_STEP) || currentSpan : currentSpan?.findParent(observability.SpanType.AGENT_RUN) || currentSpan;
|
|
1851
|
+
const processorSpan = phase !== "outputStream" ? parentSpan?.createChildSpan({
|
|
1852
|
+
type: observability.SpanType.PROCESSOR_RUN,
|
|
1853
|
+
name: `${getSpanNamePrefix(phase)}: ${processor.id}`,
|
|
1854
|
+
entityType: getProcessorEntityType(phase),
|
|
1855
|
+
entityId: processor.id,
|
|
1856
|
+
entityName: processor.name ?? processor.id,
|
|
1857
|
+
input: { phase, messageCount: messages?.length },
|
|
1858
|
+
attributes: {
|
|
1859
|
+
processorExecutor: "workflow",
|
|
1860
|
+
// Read processorIndex from processor (set in combineProcessorsIntoWorkflow)
|
|
1861
|
+
processorIndex: processor.processorIndex
|
|
1862
|
+
}
|
|
1863
|
+
}) : void 0;
|
|
1864
|
+
const processorTracingContext = processorSpan ? { currentSpan: processorSpan } : tracingContext;
|
|
1865
|
+
const baseContext = {
|
|
1866
|
+
abort,
|
|
1867
|
+
retryCount: retryCount ?? 0,
|
|
1868
|
+
requestContext,
|
|
1869
|
+
tracingContext: processorTracingContext
|
|
1870
|
+
};
|
|
1871
|
+
const passThrough = {
|
|
1872
|
+
phase,
|
|
1873
|
+
// Auto-create MessageList from messages if not provided
|
|
1874
|
+
// This enables running processor workflows from the UI where messageList can't be serialized
|
|
1875
|
+
messageList: messageList ?? (Array.isArray(messages) ? new agent.MessageList().add(messages, "input").addSystem(systemMessages ?? []) : void 0),
|
|
1876
|
+
stepNumber,
|
|
1877
|
+
systemMessages,
|
|
1878
|
+
streamParts,
|
|
1879
|
+
state,
|
|
1880
|
+
finishReason,
|
|
1881
|
+
toolCalls,
|
|
1882
|
+
text,
|
|
1883
|
+
retryCount,
|
|
1884
|
+
// inputStep phase fields for model/tools configuration
|
|
1885
|
+
model,
|
|
1886
|
+
tools,
|
|
1887
|
+
toolChoice,
|
|
1888
|
+
activeTools,
|
|
1889
|
+
providerOptions,
|
|
1890
|
+
modelSettings,
|
|
1891
|
+
structuredOutput,
|
|
1892
|
+
steps
|
|
1893
|
+
};
|
|
1894
|
+
const executePhaseWithSpan = async (fn) => {
|
|
1895
|
+
try {
|
|
1896
|
+
const result = await fn();
|
|
1897
|
+
processorSpan?.end({ output: result });
|
|
1898
|
+
return result;
|
|
1899
|
+
} catch (error) {
|
|
1900
|
+
if (error instanceof agent.TripWire) {
|
|
1901
|
+
processorSpan?.end({ output: { tripwire: error.message } });
|
|
1902
|
+
} else {
|
|
1903
|
+
processorSpan?.error({ error, endSpan: true });
|
|
1904
|
+
}
|
|
1905
|
+
throw error;
|
|
1906
|
+
}
|
|
1907
|
+
};
|
|
1908
|
+
return executePhaseWithSpan(async () => {
|
|
1909
|
+
switch (phase) {
|
|
1910
|
+
case "input": {
|
|
1911
|
+
if (processor.processInput) {
|
|
1912
|
+
if (!passThrough.messageList) {
|
|
1913
|
+
throw new error.MastraError({
|
|
1914
|
+
category: error.ErrorCategory.USER,
|
|
1915
|
+
domain: error.ErrorDomain.MASTRA_WORKFLOW,
|
|
1916
|
+
id: "PROCESSOR_MISSING_MESSAGE_LIST",
|
|
1917
|
+
text: `Processor ${processor.id} requires messageList or messages for processInput phase`
|
|
1918
|
+
});
|
|
1919
|
+
}
|
|
1920
|
+
const idsBeforeProcessing = messages.map((m) => m.id);
|
|
1921
|
+
const check = passThrough.messageList.makeMessageSourceChecker();
|
|
1922
|
+
const result = await processor.processInput({
|
|
1923
|
+
...baseContext,
|
|
1924
|
+
messages,
|
|
1925
|
+
messageList: passThrough.messageList,
|
|
1926
|
+
systemMessages: systemMessages ?? []
|
|
1927
|
+
});
|
|
1928
|
+
if (result instanceof agent.MessageList) {
|
|
1929
|
+
if (result !== passThrough.messageList) {
|
|
1930
|
+
throw new error.MastraError({
|
|
1931
|
+
category: error.ErrorCategory.USER,
|
|
1932
|
+
domain: error.ErrorDomain.MASTRA_WORKFLOW,
|
|
1933
|
+
id: "PROCESSOR_RETURNED_EXTERNAL_MESSAGE_LIST",
|
|
1934
|
+
text: `Processor ${processor.id} returned a MessageList instance other than the one passed in. Use the messageList argument instead.`
|
|
1935
|
+
});
|
|
1936
|
+
}
|
|
1937
|
+
return {
|
|
1938
|
+
...passThrough,
|
|
1939
|
+
messages: result.get.all.db(),
|
|
1940
|
+
systemMessages: result.getAllSystemMessages()
|
|
1941
|
+
};
|
|
1942
|
+
} else if (Array.isArray(result)) {
|
|
1943
|
+
processors.ProcessorRunner.applyMessagesToMessageList(
|
|
1944
|
+
result,
|
|
1945
|
+
passThrough.messageList,
|
|
1946
|
+
idsBeforeProcessing,
|
|
1947
|
+
check,
|
|
1948
|
+
"input"
|
|
1949
|
+
);
|
|
1950
|
+
return { ...passThrough, messages: result };
|
|
1951
|
+
} else if (result && "messages" in result && "systemMessages" in result) {
|
|
1952
|
+
const typedResult = result;
|
|
1953
|
+
processors.ProcessorRunner.applyMessagesToMessageList(
|
|
1954
|
+
typedResult.messages,
|
|
1955
|
+
passThrough.messageList,
|
|
1956
|
+
idsBeforeProcessing,
|
|
1957
|
+
check,
|
|
1958
|
+
"input"
|
|
1959
|
+
);
|
|
1960
|
+
passThrough.messageList.replaceAllSystemMessages(typedResult.systemMessages);
|
|
1961
|
+
return {
|
|
1962
|
+
...passThrough,
|
|
1963
|
+
messages: typedResult.messages,
|
|
1964
|
+
systemMessages: typedResult.systemMessages
|
|
1965
|
+
};
|
|
1966
|
+
}
|
|
1967
|
+
return { ...passThrough, messages };
|
|
1968
|
+
}
|
|
1969
|
+
return { ...passThrough, messages };
|
|
1970
|
+
}
|
|
1971
|
+
case "inputStep": {
|
|
1972
|
+
if (processor.processInputStep) {
|
|
1973
|
+
if (!passThrough.messageList) {
|
|
1974
|
+
throw new error.MastraError({
|
|
1975
|
+
category: error.ErrorCategory.USER,
|
|
1976
|
+
domain: error.ErrorDomain.MASTRA_WORKFLOW,
|
|
1977
|
+
id: "PROCESSOR_MISSING_MESSAGE_LIST",
|
|
1978
|
+
text: `Processor ${processor.id} requires messageList or messages for processInputStep phase`
|
|
1979
|
+
});
|
|
1980
|
+
}
|
|
1981
|
+
const idsBeforeProcessing = messages.map((m) => m.id);
|
|
1982
|
+
const check = passThrough.messageList.makeMessageSourceChecker();
|
|
1983
|
+
const result = await processor.processInputStep({
|
|
1984
|
+
...baseContext,
|
|
1985
|
+
messages,
|
|
1986
|
+
messageList: passThrough.messageList,
|
|
1987
|
+
stepNumber: stepNumber ?? 0,
|
|
1988
|
+
systemMessages: systemMessages ?? [],
|
|
1989
|
+
// Pass model/tools configuration fields - types match ProcessInputStepArgs
|
|
1990
|
+
model,
|
|
1991
|
+
tools,
|
|
1992
|
+
toolChoice,
|
|
1993
|
+
activeTools,
|
|
1994
|
+
providerOptions,
|
|
1995
|
+
modelSettings,
|
|
1996
|
+
structuredOutput,
|
|
1997
|
+
steps: steps ?? []
|
|
1998
|
+
});
|
|
1999
|
+
const validatedResult = await processors.ProcessorRunner.validateAndFormatProcessInputStepResult(result, {
|
|
2000
|
+
messageList: passThrough.messageList,
|
|
2001
|
+
processor,
|
|
2002
|
+
stepNumber: stepNumber ?? 0
|
|
2003
|
+
});
|
|
2004
|
+
if (validatedResult.messages) {
|
|
2005
|
+
processors.ProcessorRunner.applyMessagesToMessageList(
|
|
2006
|
+
validatedResult.messages,
|
|
2007
|
+
passThrough.messageList,
|
|
2008
|
+
idsBeforeProcessing,
|
|
2009
|
+
check
|
|
2010
|
+
);
|
|
2011
|
+
}
|
|
2012
|
+
if (validatedResult.systemMessages) {
|
|
2013
|
+
passThrough.messageList.replaceAllSystemMessages(validatedResult.systemMessages);
|
|
2014
|
+
}
|
|
2015
|
+
return { ...passThrough, messages, ...validatedResult };
|
|
2016
|
+
}
|
|
2017
|
+
return { ...passThrough, messages };
|
|
2018
|
+
}
|
|
2019
|
+
case "outputStream": {
|
|
2020
|
+
if (processor.processOutputStream) {
|
|
2021
|
+
const spanKey = `__outputStreamSpan_${processor.id}`;
|
|
2022
|
+
const mutableState = state ?? {};
|
|
2023
|
+
let processorSpan2 = mutableState[spanKey];
|
|
2024
|
+
if (!processorSpan2 && parentSpan) {
|
|
2025
|
+
processorSpan2 = parentSpan.createChildSpan({
|
|
2026
|
+
type: observability.SpanType.PROCESSOR_RUN,
|
|
2027
|
+
name: `output stream processor: ${processor.id}`,
|
|
2028
|
+
entityType: observability.EntityType.OUTPUT_PROCESSOR,
|
|
2029
|
+
entityId: processor.id,
|
|
2030
|
+
entityName: processor.name ?? processor.id,
|
|
2031
|
+
input: { phase, streamParts: [] },
|
|
2032
|
+
attributes: {
|
|
2033
|
+
processorExecutor: "workflow",
|
|
2034
|
+
processorIndex: processor.processorIndex
|
|
2035
|
+
}
|
|
2036
|
+
});
|
|
2037
|
+
mutableState[spanKey] = processorSpan2;
|
|
2038
|
+
}
|
|
2039
|
+
if (processorSpan2) {
|
|
2040
|
+
processorSpan2.input = {
|
|
2041
|
+
phase,
|
|
2042
|
+
streamParts: streamParts ?? [],
|
|
2043
|
+
totalChunks: (streamParts ?? []).length
|
|
2044
|
+
};
|
|
2045
|
+
}
|
|
2046
|
+
const processorTracingContext2 = processorSpan2 ? { currentSpan: processorSpan2 } : baseContext.tracingContext;
|
|
2047
|
+
let result;
|
|
2048
|
+
try {
|
|
2049
|
+
result = await processor.processOutputStream({
|
|
2050
|
+
...baseContext,
|
|
2051
|
+
tracingContext: processorTracingContext2,
|
|
2052
|
+
part,
|
|
2053
|
+
streamParts: streamParts ?? [],
|
|
2054
|
+
state: mutableState,
|
|
2055
|
+
messageList: passThrough.messageList
|
|
2056
|
+
// Optional for stream processing
|
|
2057
|
+
});
|
|
2058
|
+
if (part && part.type === "finish") {
|
|
2059
|
+
processorSpan2?.end({ output: result });
|
|
2060
|
+
delete mutableState[spanKey];
|
|
2061
|
+
}
|
|
2062
|
+
} catch (error) {
|
|
2063
|
+
if (error instanceof agent.TripWire) {
|
|
2064
|
+
processorSpan2?.end({ output: { tripwire: error.message } });
|
|
2065
|
+
} else {
|
|
2066
|
+
processorSpan2?.error({ error, endSpan: true });
|
|
2067
|
+
}
|
|
2068
|
+
delete mutableState[spanKey];
|
|
2069
|
+
throw error;
|
|
2070
|
+
}
|
|
2071
|
+
return { ...passThrough, state: mutableState, part: result };
|
|
2072
|
+
}
|
|
2073
|
+
return { ...passThrough, part };
|
|
2074
|
+
}
|
|
2075
|
+
case "outputResult": {
|
|
2076
|
+
if (processor.processOutputResult) {
|
|
2077
|
+
if (!passThrough.messageList) {
|
|
2078
|
+
throw new error.MastraError({
|
|
2079
|
+
category: error.ErrorCategory.USER,
|
|
2080
|
+
domain: error.ErrorDomain.MASTRA_WORKFLOW,
|
|
2081
|
+
id: "PROCESSOR_MISSING_MESSAGE_LIST",
|
|
2082
|
+
text: `Processor ${processor.id} requires messageList or messages for processOutputResult phase`
|
|
2083
|
+
});
|
|
2084
|
+
}
|
|
2085
|
+
const idsBeforeProcessing = messages.map((m) => m.id);
|
|
2086
|
+
const check = passThrough.messageList.makeMessageSourceChecker();
|
|
2087
|
+
const result = await processor.processOutputResult({
|
|
2088
|
+
...baseContext,
|
|
2089
|
+
messages,
|
|
2090
|
+
messageList: passThrough.messageList
|
|
2091
|
+
});
|
|
2092
|
+
if (result instanceof agent.MessageList) {
|
|
2093
|
+
if (result !== passThrough.messageList) {
|
|
2094
|
+
throw new error.MastraError({
|
|
2095
|
+
category: error.ErrorCategory.USER,
|
|
2096
|
+
domain: error.ErrorDomain.MASTRA_WORKFLOW,
|
|
2097
|
+
id: "PROCESSOR_RETURNED_EXTERNAL_MESSAGE_LIST",
|
|
2098
|
+
text: `Processor ${processor.id} returned a MessageList instance other than the one passed in. Use the messageList argument instead.`
|
|
2099
|
+
});
|
|
2100
|
+
}
|
|
2101
|
+
return {
|
|
2102
|
+
...passThrough,
|
|
2103
|
+
messages: result.get.all.db(),
|
|
2104
|
+
systemMessages: result.getAllSystemMessages()
|
|
2105
|
+
};
|
|
2106
|
+
} else if (Array.isArray(result)) {
|
|
2107
|
+
processors.ProcessorRunner.applyMessagesToMessageList(
|
|
2108
|
+
result,
|
|
2109
|
+
passThrough.messageList,
|
|
2110
|
+
idsBeforeProcessing,
|
|
2111
|
+
check,
|
|
2112
|
+
"response"
|
|
2113
|
+
);
|
|
2114
|
+
return { ...passThrough, messages: result };
|
|
2115
|
+
} else if (result && "messages" in result && "systemMessages" in result) {
|
|
2116
|
+
const typedResult = result;
|
|
2117
|
+
processors.ProcessorRunner.applyMessagesToMessageList(
|
|
2118
|
+
typedResult.messages,
|
|
2119
|
+
passThrough.messageList,
|
|
2120
|
+
idsBeforeProcessing,
|
|
2121
|
+
check,
|
|
2122
|
+
"response"
|
|
2123
|
+
);
|
|
2124
|
+
passThrough.messageList.replaceAllSystemMessages(typedResult.systemMessages);
|
|
2125
|
+
return {
|
|
2126
|
+
...passThrough,
|
|
2127
|
+
messages: typedResult.messages,
|
|
2128
|
+
systemMessages: typedResult.systemMessages
|
|
2129
|
+
};
|
|
2130
|
+
}
|
|
2131
|
+
return { ...passThrough, messages };
|
|
2132
|
+
}
|
|
2133
|
+
return { ...passThrough, messages };
|
|
2134
|
+
}
|
|
2135
|
+
case "outputStep": {
|
|
2136
|
+
if (processor.processOutputStep) {
|
|
2137
|
+
if (!passThrough.messageList) {
|
|
2138
|
+
throw new error.MastraError({
|
|
2139
|
+
category: error.ErrorCategory.USER,
|
|
2140
|
+
domain: error.ErrorDomain.MASTRA_WORKFLOW,
|
|
2141
|
+
id: "PROCESSOR_MISSING_MESSAGE_LIST",
|
|
2142
|
+
text: `Processor ${processor.id} requires messageList or messages for processOutputStep phase`
|
|
2143
|
+
});
|
|
2144
|
+
}
|
|
2145
|
+
const idsBeforeProcessing = messages.map((m) => m.id);
|
|
2146
|
+
const check = passThrough.messageList.makeMessageSourceChecker();
|
|
2147
|
+
const result = await processor.processOutputStep({
|
|
2148
|
+
...baseContext,
|
|
2149
|
+
messages,
|
|
2150
|
+
messageList: passThrough.messageList,
|
|
2151
|
+
stepNumber: stepNumber ?? 0,
|
|
2152
|
+
finishReason,
|
|
2153
|
+
toolCalls,
|
|
2154
|
+
text,
|
|
2155
|
+
systemMessages: systemMessages ?? [],
|
|
2156
|
+
steps: steps ?? []
|
|
2157
|
+
});
|
|
2158
|
+
if (result instanceof agent.MessageList) {
|
|
2159
|
+
if (result !== passThrough.messageList) {
|
|
2160
|
+
throw new error.MastraError({
|
|
2161
|
+
category: error.ErrorCategory.USER,
|
|
2162
|
+
domain: error.ErrorDomain.MASTRA_WORKFLOW,
|
|
2163
|
+
id: "PROCESSOR_RETURNED_EXTERNAL_MESSAGE_LIST",
|
|
2164
|
+
text: `Processor ${processor.id} returned a MessageList instance other than the one passed in. Use the messageList argument instead.`
|
|
2165
|
+
});
|
|
2166
|
+
}
|
|
2167
|
+
return {
|
|
2168
|
+
...passThrough,
|
|
2169
|
+
messages: result.get.all.db(),
|
|
2170
|
+
systemMessages: result.getAllSystemMessages()
|
|
2171
|
+
};
|
|
2172
|
+
} else if (Array.isArray(result)) {
|
|
2173
|
+
processors.ProcessorRunner.applyMessagesToMessageList(
|
|
2174
|
+
result,
|
|
2175
|
+
passThrough.messageList,
|
|
2176
|
+
idsBeforeProcessing,
|
|
2177
|
+
check,
|
|
2178
|
+
"response"
|
|
2179
|
+
);
|
|
2180
|
+
return { ...passThrough, messages: result };
|
|
2181
|
+
} else if (result && "messages" in result && "systemMessages" in result) {
|
|
2182
|
+
const typedResult = result;
|
|
2183
|
+
processors.ProcessorRunner.applyMessagesToMessageList(
|
|
2184
|
+
typedResult.messages,
|
|
2185
|
+
passThrough.messageList,
|
|
2186
|
+
idsBeforeProcessing,
|
|
2187
|
+
check,
|
|
2188
|
+
"response"
|
|
2189
|
+
);
|
|
2190
|
+
passThrough.messageList.replaceAllSystemMessages(typedResult.systemMessages);
|
|
2191
|
+
return {
|
|
2192
|
+
...passThrough,
|
|
2193
|
+
messages: typedResult.messages,
|
|
2194
|
+
systemMessages: typedResult.systemMessages
|
|
2195
|
+
};
|
|
2196
|
+
}
|
|
2197
|
+
return { ...passThrough, messages };
|
|
2198
|
+
}
|
|
2199
|
+
return { ...passThrough, messages };
|
|
2200
|
+
}
|
|
2201
|
+
default:
|
|
2202
|
+
return { ...passThrough, messages };
|
|
2203
|
+
}
|
|
1619
2204
|
});
|
|
1620
|
-
}
|
|
1621
|
-
|
|
1622
|
-
|
|
2205
|
+
},
|
|
2206
|
+
component: "PROCESSOR"
|
|
2207
|
+
};
|
|
2208
|
+
}
|
|
2209
|
+
function init(inngest) {
|
|
2210
|
+
return {
|
|
2211
|
+
createWorkflow(params) {
|
|
2212
|
+
return new InngestWorkflow(
|
|
2213
|
+
params,
|
|
2214
|
+
inngest
|
|
2215
|
+
);
|
|
2216
|
+
},
|
|
2217
|
+
createStep,
|
|
2218
|
+
cloneStep(step, opts) {
|
|
2219
|
+
return {
|
|
2220
|
+
id: opts.id,
|
|
2221
|
+
description: step.description,
|
|
2222
|
+
inputSchema: step.inputSchema,
|
|
2223
|
+
outputSchema: step.outputSchema,
|
|
2224
|
+
resumeSchema: step.resumeSchema,
|
|
2225
|
+
suspendSchema: step.suspendSchema,
|
|
2226
|
+
stateSchema: step.stateSchema,
|
|
2227
|
+
execute: step.execute,
|
|
2228
|
+
retries: step.retries,
|
|
2229
|
+
scorers: step.scorers,
|
|
2230
|
+
component: step.component
|
|
2231
|
+
};
|
|
2232
|
+
},
|
|
2233
|
+
cloneWorkflow(workflow, opts) {
|
|
2234
|
+
const wf = new workflows.Workflow({
|
|
2235
|
+
id: opts.id,
|
|
2236
|
+
inputSchema: workflow.inputSchema,
|
|
2237
|
+
outputSchema: workflow.outputSchema,
|
|
2238
|
+
steps: workflow.stepDefs,
|
|
2239
|
+
mastra: workflow.mastra,
|
|
2240
|
+
options: workflow.options
|
|
1623
2241
|
});
|
|
2242
|
+
wf.setStepFlow(workflow.stepGraph);
|
|
2243
|
+
wf.commit();
|
|
2244
|
+
return wf;
|
|
1624
2245
|
}
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
};
|
|
2246
|
+
};
|
|
2247
|
+
}
|
|
1628
2248
|
|
|
1629
2249
|
exports.InngestExecutionEngine = InngestExecutionEngine;
|
|
2250
|
+
exports.InngestPubSub = InngestPubSub;
|
|
1630
2251
|
exports.InngestRun = InngestRun;
|
|
1631
2252
|
exports.InngestWorkflow = InngestWorkflow;
|
|
2253
|
+
exports._compatibilityCheck = _compatibilityCheck;
|
|
2254
|
+
exports.createServe = createServe;
|
|
1632
2255
|
exports.createStep = createStep;
|
|
1633
2256
|
exports.init = init;
|
|
1634
2257
|
exports.serve = serve;
|