@kortyx/agent 0.15.0 → 0.18.0
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 +54 -0
- package/dist/index.d.ts +128 -7
- package/dist/index.js +3649 -295
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
1
2
|
var __defProp = Object.defineProperty;
|
|
2
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
8
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
9
|
+
};
|
|
5
10
|
var __export = (target, all) => {
|
|
6
11
|
for (var name in all)
|
|
7
12
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -12,13 +17,2628 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
12
17
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
18
|
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
19
|
}
|
|
15
|
-
return to;
|
|
16
|
-
};
|
|
17
|
-
var
|
|
20
|
+
return to;
|
|
21
|
+
};
|
|
22
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
23
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
24
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
25
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
26
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
27
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
28
|
+
mod
|
|
29
|
+
));
|
|
30
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
31
|
+
|
|
32
|
+
// ../hooks/dist/index.js
|
|
33
|
+
var require_dist = __commonJS({
|
|
34
|
+
"../hooks/dist/index.js"(exports2, module2) {
|
|
35
|
+
var __defProp2 = Object.defineProperty;
|
|
36
|
+
var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
|
|
37
|
+
var __getOwnPropNames2 = Object.getOwnPropertyNames;
|
|
38
|
+
var __hasOwnProp2 = Object.prototype.hasOwnProperty;
|
|
39
|
+
var __export2 = (target, all) => {
|
|
40
|
+
for (var name in all)
|
|
41
|
+
__defProp2(target, name, { get: all[name], enumerable: true });
|
|
42
|
+
};
|
|
43
|
+
var __copyProps2 = (to, from, except, desc) => {
|
|
44
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
45
|
+
for (let key of __getOwnPropNames2(from))
|
|
46
|
+
if (!__hasOwnProp2.call(to, key) && key !== except)
|
|
47
|
+
__defProp2(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc2(from, key)) || desc.enumerable });
|
|
48
|
+
}
|
|
49
|
+
return to;
|
|
50
|
+
};
|
|
51
|
+
var __toCommonJS2 = (mod) => __copyProps2(__defProp2({}, "__esModule", { value: true }), mod);
|
|
52
|
+
var index_exports2 = {};
|
|
53
|
+
__export2(index_exports2, {
|
|
54
|
+
WorkflowCallError: () => WorkflowCallError,
|
|
55
|
+
createWorkflowHooks: () => createWorkflowHooks,
|
|
56
|
+
emitWorkflowCall: () => emitWorkflowCall2,
|
|
57
|
+
runWithHookContext: () => runWithHookContext,
|
|
58
|
+
useAbortSignal: () => useAbortSignal,
|
|
59
|
+
useInterrupt: () => useInterrupt,
|
|
60
|
+
useNodeState: () => useNodeState,
|
|
61
|
+
useReason: () => useReason2,
|
|
62
|
+
useRuntimeContext: () => useRuntimeContext,
|
|
63
|
+
useStructuredData: () => useStructuredData,
|
|
64
|
+
useWorkflow: () => useWorkflow,
|
|
65
|
+
useWorkflowState: () => useWorkflowState,
|
|
66
|
+
workflowCallFingerprint: () => workflowCallFingerprint
|
|
67
|
+
});
|
|
68
|
+
module2.exports = __toCommonJS2(index_exports2);
|
|
69
|
+
var import_node_async_hooks = require("async_hooks");
|
|
70
|
+
var storage = new import_node_async_hooks.AsyncLocalStorage();
|
|
71
|
+
var isRecord5 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
72
|
+
var normalizeNodeState = (value) => {
|
|
73
|
+
if (Array.isArray(value)) {
|
|
74
|
+
return { byIndex: [...value], byKey: {} };
|
|
75
|
+
}
|
|
76
|
+
if (isRecord5(value)) {
|
|
77
|
+
const byIndex = Array.isArray(value.byIndex) ? [...value.byIndex] : [];
|
|
78
|
+
const byKey = isRecord5(value.byKey) ? { ...value.byKey } : {};
|
|
79
|
+
return { byIndex, byKey };
|
|
80
|
+
}
|
|
81
|
+
return { byIndex: [], byKey: {} };
|
|
82
|
+
};
|
|
83
|
+
var cloneWorkflowState = (value) => isRecord5(value) ? { ...value } : {};
|
|
84
|
+
var cloneTokenUsage = (value) => {
|
|
85
|
+
if (!isRecord5(value)) return void 0;
|
|
86
|
+
const input = typeof value.input === "number" ? value.input : void 0;
|
|
87
|
+
const output = typeof value.output === "number" ? value.output : void 0;
|
|
88
|
+
const total = typeof value.total === "number" ? value.total : void 0;
|
|
89
|
+
if (input === void 0 && output === void 0 && total === void 0) {
|
|
90
|
+
return void 0;
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
input: input ?? 0,
|
|
94
|
+
output: output ?? 0,
|
|
95
|
+
total: total ?? 0
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
var getHookRuntimeState = (state) => {
|
|
99
|
+
const runtime = state.runtime ?? {};
|
|
100
|
+
const internal = isRecord5(runtime.__kortyx) ? runtime.__kortyx : void 0;
|
|
101
|
+
const result = {};
|
|
102
|
+
if (isRecord5(internal?.nodeState)) result.nodeState = internal?.nodeState;
|
|
103
|
+
if (isRecord5(internal?.workflowState)) {
|
|
104
|
+
result.workflowState = internal?.workflowState;
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
107
|
+
};
|
|
108
|
+
var createInternalContext = (ctx) => {
|
|
109
|
+
const hookRuntimeState = getHookRuntimeState(ctx.state);
|
|
110
|
+
const nodeId = ctx.node.graph.node;
|
|
111
|
+
const storedNodeState = isRecord5(hookRuntimeState.nodeState) ? hookRuntimeState.nodeState : void 0;
|
|
112
|
+
const storedNodeId = typeof storedNodeState?.nodeId === "string" ? String(storedNodeState.nodeId) : "";
|
|
113
|
+
const storedState = storedNodeState?.state;
|
|
114
|
+
const currentNodeState = storedNodeId && storedNodeId === nodeId ? normalizeNodeState(storedState) : { byIndex: [], byKey: {} };
|
|
115
|
+
return {
|
|
116
|
+
...ctx,
|
|
117
|
+
workflowCallActive: false,
|
|
118
|
+
workflowCallIds: /* @__PURE__ */ new Set(),
|
|
119
|
+
nodeStateIndex: 0,
|
|
120
|
+
reasonCallIndex: 0,
|
|
121
|
+
currentNodeState,
|
|
122
|
+
workflowState: cloneWorkflowState(hookRuntimeState.workflowState),
|
|
123
|
+
tokenUsage: cloneTokenUsage(ctx.state.runtime?.tokenUsage),
|
|
124
|
+
stateDirty: false,
|
|
125
|
+
runtimeDirty: false
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
var buildRuntimeStateUpdates = (ctx) => {
|
|
129
|
+
const updates = {};
|
|
130
|
+
if (ctx.stateDirty) {
|
|
131
|
+
updates.__kortyx = {
|
|
132
|
+
nodeState: {
|
|
133
|
+
nodeId: ctx.node.graph.node,
|
|
134
|
+
state: ctx.currentNodeState
|
|
135
|
+
},
|
|
136
|
+
workflowState: ctx.workflowState
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
if (ctx.runtimeDirty && ctx.tokenUsage) {
|
|
140
|
+
updates.tokenUsage = ctx.tokenUsage;
|
|
141
|
+
}
|
|
142
|
+
return Object.keys(updates).length > 0 ? updates : null;
|
|
143
|
+
};
|
|
144
|
+
var cleanupCompletedReasonCheckpoints = (ctx) => {
|
|
145
|
+
let changed = false;
|
|
146
|
+
for (const [key, value] of Object.entries(ctx.currentNodeState.byKey)) {
|
|
147
|
+
if (!key.startsWith("__useReason:")) continue;
|
|
148
|
+
if (!isRecord5(value) || value.status !== "completed") continue;
|
|
149
|
+
delete ctx.currentNodeState.byKey[key];
|
|
150
|
+
changed = true;
|
|
151
|
+
}
|
|
152
|
+
if (changed) ctx.stateDirty = true;
|
|
153
|
+
};
|
|
154
|
+
var toTokenUsageDelta = (usage) => {
|
|
155
|
+
if (!usage) return void 0;
|
|
156
|
+
const input = usage.input ?? 0;
|
|
157
|
+
const output = usage.output ?? 0;
|
|
158
|
+
const total = usage.total ?? input + output + (usage.reasoning ?? 0);
|
|
159
|
+
if (input === 0 && output === 0 && total === 0) {
|
|
160
|
+
return void 0;
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
input,
|
|
164
|
+
output,
|
|
165
|
+
total
|
|
166
|
+
};
|
|
167
|
+
};
|
|
168
|
+
function accumulateTokenUsage(usage) {
|
|
169
|
+
const delta = toTokenUsageDelta(usage);
|
|
170
|
+
if (!delta) return;
|
|
171
|
+
const ctx = getHookContext();
|
|
172
|
+
const current = ctx.tokenUsage ?? {
|
|
173
|
+
input: 0,
|
|
174
|
+
output: 0,
|
|
175
|
+
total: 0
|
|
176
|
+
};
|
|
177
|
+
ctx.tokenUsage = {
|
|
178
|
+
input: current.input + delta.input,
|
|
179
|
+
output: current.output + delta.output,
|
|
180
|
+
total: current.total + delta.total
|
|
181
|
+
};
|
|
182
|
+
ctx.runtimeDirty = true;
|
|
183
|
+
}
|
|
184
|
+
function getReasonTraceAdapter() {
|
|
185
|
+
return getHookContext().reasonTrace;
|
|
186
|
+
}
|
|
187
|
+
async function runWithHookContext(ctx, fn) {
|
|
188
|
+
const internal = createInternalContext(ctx);
|
|
189
|
+
try {
|
|
190
|
+
const result = await storage.run(internal, fn);
|
|
191
|
+
cleanupCompletedReasonCheckpoints(internal);
|
|
192
|
+
for (const key of Object.keys(internal.currentNodeState.byKey)) {
|
|
193
|
+
if (key.startsWith("__useWorkflow:") || key === "__useWorkflowActivation") {
|
|
194
|
+
internal.currentNodeState.byKey[key] = null;
|
|
195
|
+
internal.stateDirty = true;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return { result, runtimeUpdates: buildRuntimeStateUpdates(internal) };
|
|
199
|
+
} catch (err) {
|
|
200
|
+
const runtimeUpdates = buildRuntimeStateUpdates(internal);
|
|
201
|
+
if (runtimeUpdates) {
|
|
202
|
+
const currentRuntime = isRecord5(internal.state.runtime) ? internal.state.runtime : {};
|
|
203
|
+
internal.state.runtime = {
|
|
204
|
+
...currentRuntime,
|
|
205
|
+
...runtimeUpdates
|
|
206
|
+
};
|
|
207
|
+
if (err && typeof err === "object") {
|
|
208
|
+
err.__kortyxHookStatePatch = runtimeUpdates;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
throw err;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function getHookContext() {
|
|
215
|
+
const ctx = storage.getStore();
|
|
216
|
+
if (!ctx) {
|
|
217
|
+
throw new Error("Hooks can only be used while a node is executing.");
|
|
218
|
+
}
|
|
219
|
+
return ctx;
|
|
220
|
+
}
|
|
221
|
+
var import_core5 = require("@kortyx/core");
|
|
222
|
+
var resolveReasonCheckpointKey = (args) => typeof args.id === "string" && args.id.length > 0 ? `__useReason:${args.id}` : `__useReason:auto:${args.autoIndex}`;
|
|
223
|
+
var readReasonCheckpoint = (raw) => {
|
|
224
|
+
if (!raw || typeof raw !== "object") return void 0;
|
|
225
|
+
const value = raw;
|
|
226
|
+
if (value.status !== "awaiting_interrupt") return void 0;
|
|
227
|
+
if (typeof value.firstText !== "string") return void 0;
|
|
228
|
+
if (!value.request || typeof value.request !== "object") return void 0;
|
|
229
|
+
return {
|
|
230
|
+
status: "awaiting_interrupt",
|
|
231
|
+
request: value.request,
|
|
232
|
+
firstText: value.firstText,
|
|
233
|
+
...Object.hasOwn(value, "firstRaw") ? { firstRaw: value.firstRaw } : {},
|
|
234
|
+
...Object.hasOwn(value, "firstUsage") ? { firstUsage: value.firstUsage } : {},
|
|
235
|
+
...Object.hasOwn(value, "firstFinishReason") ? { firstFinishReason: value.firstFinishReason } : {},
|
|
236
|
+
...Object.hasOwn(value, "firstProviderMetadata") ? {
|
|
237
|
+
firstProviderMetadata: value.firstProviderMetadata
|
|
238
|
+
} : {},
|
|
239
|
+
...Object.hasOwn(value, "firstWarnings") ? { firstWarnings: value.firstWarnings } : {},
|
|
240
|
+
...Object.hasOwn(value, "firstOutput") ? { firstOutput: value.firstOutput } : {}
|
|
241
|
+
};
|
|
242
|
+
};
|
|
243
|
+
var readReasonCompletedCheckpoint = (raw) => {
|
|
244
|
+
if (!raw || typeof raw !== "object") return void 0;
|
|
245
|
+
const value = raw;
|
|
246
|
+
if (value.status !== "completed") return void 0;
|
|
247
|
+
if (!value.result || typeof value.result !== "object") return void 0;
|
|
248
|
+
return {
|
|
249
|
+
status: "completed",
|
|
250
|
+
result: value.result
|
|
251
|
+
};
|
|
252
|
+
};
|
|
253
|
+
var resolveHookStatePatch = (args) => ({
|
|
254
|
+
__kortyx: {
|
|
255
|
+
nodeState: {
|
|
256
|
+
nodeId: args.nodeId,
|
|
257
|
+
state: args.currentNodeState
|
|
258
|
+
},
|
|
259
|
+
workflowState: args.workflowState
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
var parseWithSchema = (schema, value, label) => {
|
|
263
|
+
if (!schema) return value;
|
|
264
|
+
const parsed = schema.safeParse(value);
|
|
265
|
+
if (parsed.success) return parsed.data;
|
|
266
|
+
const parsedError = "error" in parsed && parsed.error ? parsed.error : void 0;
|
|
267
|
+
const reason = typeof parsedError?.message === "string" && parsedError.message.length > 0 ? parsedError.message : "invalid payload";
|
|
268
|
+
throw new Error(`${label} validation failed: ${reason}`);
|
|
269
|
+
};
|
|
270
|
+
var awaitInterruptInternal = (args) => {
|
|
271
|
+
const ctx = getHookContext();
|
|
272
|
+
(0, import_core5.throwIfExecutionAborted)(ctx.node.abortSignal);
|
|
273
|
+
const request = parseWithSchema(
|
|
274
|
+
args.requestSchema,
|
|
275
|
+
args.request,
|
|
276
|
+
"useInterrupt request"
|
|
277
|
+
);
|
|
278
|
+
const userMeta = args.meta && typeof args.meta === "object" && !Array.isArray(args.meta) ? args.meta : {};
|
|
279
|
+
const resumeStatePatch = resolveHookStatePatch({
|
|
280
|
+
nodeId: ctx.node.graph.node,
|
|
281
|
+
currentNodeState: ctx.currentNodeState,
|
|
282
|
+
workflowState: ctx.workflowState
|
|
283
|
+
});
|
|
284
|
+
const enrichedRequest = {
|
|
285
|
+
...request,
|
|
286
|
+
...typeof args.schemaId === "string" && args.schemaId.length > 0 ? { schemaId: args.schemaId } : {},
|
|
287
|
+
...typeof args.schemaVersion === "string" && args.schemaVersion.length > 0 ? { schemaVersion: args.schemaVersion } : {},
|
|
288
|
+
...typeof args.id === "string" && args.id.length > 0 ? { id: args.id } : {},
|
|
289
|
+
meta: {
|
|
290
|
+
...userMeta,
|
|
291
|
+
__kortyxResumeStatePatch: {
|
|
292
|
+
...resumeStatePatch,
|
|
293
|
+
...ctx.tokenUsage ? { tokenUsage: ctx.tokenUsage } : {}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
const raw = ctx.node.awaitInterrupt(enrichedRequest);
|
|
298
|
+
const response = parseWithSchema(
|
|
299
|
+
args.responseSchema,
|
|
300
|
+
raw,
|
|
301
|
+
"useInterrupt response"
|
|
302
|
+
);
|
|
303
|
+
return Promise.resolve(response);
|
|
304
|
+
};
|
|
305
|
+
var import_core52 = require("@kortyx/core");
|
|
306
|
+
var import_node_crypto4 = require("crypto");
|
|
307
|
+
var parseStructuredPath = (path, source) => {
|
|
308
|
+
if (path.length === 0) {
|
|
309
|
+
throw new Error(`${source} path must be a non-empty dot-separated string.`);
|
|
310
|
+
}
|
|
311
|
+
const parts = path.split(".");
|
|
312
|
+
if (parts.some((part) => part.length === 0)) {
|
|
313
|
+
throw new Error(
|
|
314
|
+
`${source} path "${path}" must not contain empty segments.`
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
return parts;
|
|
318
|
+
};
|
|
319
|
+
var assertStructuredPath = (path, source) => {
|
|
320
|
+
parseStructuredPath(path, source);
|
|
321
|
+
};
|
|
322
|
+
var createStructuredStreamId = () => {
|
|
323
|
+
try {
|
|
324
|
+
return (0, import_node_crypto4.randomUUID)();
|
|
325
|
+
} catch {
|
|
326
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
var emitStructuredData = (args) => {
|
|
330
|
+
const ctx = getHookContext();
|
|
331
|
+
const streamId = typeof args.streamId === "string" && args.streamId.length > 0 ? args.streamId : createStructuredStreamId();
|
|
332
|
+
const dataType = typeof args.dataType === "string" && args.dataType.length > 0 ? args.dataType : "generic";
|
|
333
|
+
const base = {
|
|
334
|
+
node: ctx.node.graph.node,
|
|
335
|
+
streamId,
|
|
336
|
+
dataType,
|
|
337
|
+
...typeof args.schemaId === "string" && args.schemaId.length > 0 ? { schemaId: args.schemaId } : {},
|
|
338
|
+
...typeof args.schemaVersion === "string" && args.schemaVersion.length > 0 ? { schemaVersion: args.schemaVersion } : {},
|
|
339
|
+
...typeof args.id === "string" && args.id.length > 0 ? { id: args.id } : {}
|
|
340
|
+
};
|
|
341
|
+
if (args.kind === "set") {
|
|
342
|
+
assertStructuredPath(args.path, "useStructuredData");
|
|
343
|
+
const value = parseWithSchema(
|
|
344
|
+
args.valueSchema,
|
|
345
|
+
args.value,
|
|
346
|
+
"useStructuredData value"
|
|
347
|
+
);
|
|
348
|
+
ctx.node.emit("structured_data", {
|
|
349
|
+
...base,
|
|
350
|
+
kind: "set",
|
|
351
|
+
path: args.path,
|
|
352
|
+
value
|
|
353
|
+
});
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
if (args.kind === "append") {
|
|
357
|
+
assertStructuredPath(args.path, "useStructuredData");
|
|
358
|
+
const items = args.items.map(
|
|
359
|
+
(item) => parseWithSchema(args.itemSchema, item, "useStructuredData append item")
|
|
360
|
+
);
|
|
361
|
+
ctx.node.emit("structured_data", {
|
|
362
|
+
...base,
|
|
363
|
+
kind: "append",
|
|
364
|
+
path: args.path,
|
|
365
|
+
items
|
|
366
|
+
});
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
if (args.kind === "text-delta") {
|
|
370
|
+
assertStructuredPath(args.path, "useStructuredData");
|
|
371
|
+
ctx.node.emit("structured_data", {
|
|
372
|
+
...base,
|
|
373
|
+
kind: "text-delta",
|
|
374
|
+
path: args.path,
|
|
375
|
+
delta: args.delta
|
|
376
|
+
});
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
const validated = parseWithSchema(
|
|
380
|
+
args.dataSchema,
|
|
381
|
+
args.data,
|
|
382
|
+
"useStructuredData data"
|
|
383
|
+
);
|
|
384
|
+
ctx.node.emit("structured_data", {
|
|
385
|
+
...base,
|
|
386
|
+
kind: "final",
|
|
387
|
+
data: validated
|
|
388
|
+
});
|
|
389
|
+
};
|
|
390
|
+
var shouldEmitStructured = (cfg) => Boolean(cfg);
|
|
391
|
+
var shouldStreamStructured = (cfg) => Boolean(cfg) && (cfg?.stream ?? true);
|
|
392
|
+
var import_node_crypto32 = require("crypto");
|
|
393
|
+
var import_core32 = require("@kortyx/core");
|
|
394
|
+
var import_node_crypto22 = require("crypto");
|
|
395
|
+
var import_core22 = require("@kortyx/core");
|
|
396
|
+
var toError = (value) => value instanceof Error ? value : new Error(String(value));
|
|
397
|
+
var createRuntimeId = () => {
|
|
398
|
+
try {
|
|
399
|
+
return (0, import_node_crypto22.randomUUID)();
|
|
400
|
+
} catch {
|
|
401
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
var emitNodeEvent = (emitEvent, nodeId, event, payload) => {
|
|
405
|
+
if (!emitEvent) return;
|
|
406
|
+
emitEvent(event, {
|
|
407
|
+
...payload,
|
|
408
|
+
...typeof nodeId === "string" && nodeId.length > 0 ? { node: nodeId } : {}
|
|
409
|
+
});
|
|
410
|
+
};
|
|
411
|
+
async function runReasonEngine(args) {
|
|
412
|
+
const stream = args.stream ?? args.model.options?.streaming ?? true;
|
|
413
|
+
const emit = args.emit ?? true;
|
|
414
|
+
const temperature = args.temperature ?? args.model.options?.temperature ?? args.defaultTemperature;
|
|
415
|
+
const maxOutputTokens = args.maxOutputTokens ?? args.model.options?.maxOutputTokens;
|
|
416
|
+
const stopSequences = args.stopSequences ?? args.model.options?.stopSequences;
|
|
417
|
+
const abortSignal = args.abortSignal ?? args.model.options?.abortSignal;
|
|
418
|
+
const reasoning = args.reasoning ?? args.model.options?.reasoning;
|
|
419
|
+
const responseFormat = args.responseFormat ?? args.model.options?.responseFormat;
|
|
420
|
+
const providerOptions = args.providerOptions ?? args.model.options?.providerOptions;
|
|
421
|
+
const tools = args.tools ?? args.model.options?.tools;
|
|
422
|
+
(0, import_core22.throwIfExecutionAborted)(abortSignal);
|
|
423
|
+
const model = args.model.provider.getModel(args.model.modelId, {
|
|
424
|
+
...temperature !== void 0 ? { temperature } : {},
|
|
425
|
+
streaming: stream,
|
|
426
|
+
...maxOutputTokens !== void 0 ? { maxOutputTokens } : {},
|
|
427
|
+
...stopSequences !== void 0 ? { stopSequences } : {},
|
|
428
|
+
...abortSignal !== void 0 ? { abortSignal } : {},
|
|
429
|
+
...reasoning !== void 0 ? { reasoning } : {},
|
|
430
|
+
...responseFormat !== void 0 ? { responseFormat } : {},
|
|
431
|
+
...tools !== void 0 ? { tools } : {},
|
|
432
|
+
...providerOptions !== void 0 ? { providerOptions } : {}
|
|
433
|
+
});
|
|
434
|
+
const messages = args.messages !== void 0 ? args.messages : [
|
|
435
|
+
...typeof args.system === "string" && args.system.length > 0 ? [{ role: "system", content: args.system }] : [],
|
|
436
|
+
{ role: "user", content: String(args.input ?? "") }
|
|
437
|
+
];
|
|
438
|
+
const opId = typeof args.opId === "string" && args.opId.length > 0 ? args.opId : createRuntimeId();
|
|
439
|
+
const segmentId = typeof args.segmentId === "string" && args.segmentId.length > 0 ? args.segmentId : createRuntimeId();
|
|
440
|
+
const commonMeta = {
|
|
441
|
+
...typeof args.id === "string" && args.id.length > 0 ? { id: args.id } : {},
|
|
442
|
+
opId,
|
|
443
|
+
segmentId
|
|
444
|
+
};
|
|
445
|
+
const traceSpan = args.reasonTrace?.startSpan({
|
|
446
|
+
name: "runReasonEngine",
|
|
447
|
+
attributes: {
|
|
448
|
+
...commonMeta,
|
|
449
|
+
providerId: args.model.provider.id,
|
|
450
|
+
modelId: args.model.modelId,
|
|
451
|
+
stream,
|
|
452
|
+
emit,
|
|
453
|
+
...args.telemetry?.operation ? { operation: args.telemetry.operation } : {},
|
|
454
|
+
...args.telemetry?.prompt?.name ? { promptName: args.telemetry.prompt.name } : {},
|
|
455
|
+
...args.telemetry?.prompt?.version !== void 0 ? { promptVersion: args.telemetry.prompt.version } : {},
|
|
456
|
+
...args.telemetry?.prompt?.type ? { promptType: args.telemetry.prompt.type } : {},
|
|
457
|
+
...typeof args.nodeId === "string" && args.nodeId.length > 0 ? { nodeId: args.nodeId } : {}
|
|
458
|
+
},
|
|
459
|
+
telemetry: {
|
|
460
|
+
...args.telemetry ?? {},
|
|
461
|
+
input: args.telemetry?.input ?? messages
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
const modelRequestStartedAt = Date.now();
|
|
465
|
+
let firstTextTokenAt;
|
|
466
|
+
let lastTextTokenAt;
|
|
467
|
+
const generationTimingAttributes = () => firstTextTokenAt === void 0 || lastTextTokenAt === void 0 ? {} : {
|
|
468
|
+
ttftMs: Math.max(0, firstTextTokenAt - modelRequestStartedAt),
|
|
469
|
+
streamDurationMs: Math.max(0, lastTextTokenAt - firstTextTokenAt),
|
|
470
|
+
timeToLastTokenMs: Math.max(
|
|
471
|
+
0,
|
|
472
|
+
lastTextTokenAt - modelRequestStartedAt
|
|
473
|
+
)
|
|
474
|
+
};
|
|
475
|
+
const endTrace = (span, result) => {
|
|
476
|
+
span?.end?.({
|
|
477
|
+
...result.usage !== void 0 ? { usage: result.usage } : {},
|
|
478
|
+
...result.finishReason !== void 0 ? { finishReason: result.finishReason } : {},
|
|
479
|
+
...result.providerMetadata !== void 0 ? { providerMetadata: result.providerMetadata } : {},
|
|
480
|
+
...result.warnings !== void 0 ? { warnings: result.warnings } : {},
|
|
481
|
+
attributes: {
|
|
482
|
+
textLength: result.text.length,
|
|
483
|
+
...generationTimingAttributes()
|
|
484
|
+
},
|
|
485
|
+
telemetry: {
|
|
486
|
+
...args.telemetry ?? {},
|
|
487
|
+
output: args.telemetry?.output ?? result.text
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
};
|
|
491
|
+
const failTrace = (span, error) => {
|
|
492
|
+
span?.fail?.(error, {
|
|
493
|
+
attributes: {
|
|
494
|
+
providerId: args.model.provider.id,
|
|
495
|
+
modelId: args.model.modelId
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
};
|
|
499
|
+
try {
|
|
500
|
+
if (stream) {
|
|
501
|
+
let final = "";
|
|
502
|
+
let raw;
|
|
503
|
+
let usage;
|
|
504
|
+
let finishReason;
|
|
505
|
+
let toolCalls;
|
|
506
|
+
let providerMetadata;
|
|
507
|
+
let warnings;
|
|
508
|
+
if (emit) {
|
|
509
|
+
emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {
|
|
510
|
+
...commonMeta
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
const response2 = await model.stream(messages);
|
|
514
|
+
for await (const chunk of response2) {
|
|
515
|
+
(0, import_core22.throwIfExecutionAborted)(abortSignal);
|
|
516
|
+
switch (chunk.type) {
|
|
517
|
+
case "text-delta": {
|
|
518
|
+
if (chunk.delta.length === 0) break;
|
|
519
|
+
const tokenAt = Date.now();
|
|
520
|
+
firstTextTokenAt ??= tokenAt;
|
|
521
|
+
lastTextTokenAt = tokenAt;
|
|
522
|
+
final += chunk.delta;
|
|
523
|
+
args.onTextChunk?.(chunk.delta);
|
|
524
|
+
if (chunk.raw !== void 0) {
|
|
525
|
+
raw = chunk.raw;
|
|
526
|
+
}
|
|
527
|
+
if (chunk.providerMetadata !== void 0) {
|
|
528
|
+
providerMetadata = chunk.providerMetadata;
|
|
529
|
+
}
|
|
530
|
+
if (emit) {
|
|
531
|
+
emitNodeEvent(args.emitEvent, args.nodeId, "text-delta", {
|
|
532
|
+
...commonMeta,
|
|
533
|
+
delta: chunk.delta
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
break;
|
|
537
|
+
}
|
|
538
|
+
case "finish": {
|
|
539
|
+
if (chunk.raw !== void 0) {
|
|
540
|
+
raw = chunk.raw;
|
|
541
|
+
}
|
|
542
|
+
if (chunk.usage !== void 0) {
|
|
543
|
+
usage = chunk.usage;
|
|
544
|
+
}
|
|
545
|
+
if (chunk.finishReason !== void 0) {
|
|
546
|
+
finishReason = chunk.finishReason;
|
|
547
|
+
}
|
|
548
|
+
if (chunk.toolCalls !== void 0) {
|
|
549
|
+
toolCalls = chunk.toolCalls;
|
|
550
|
+
}
|
|
551
|
+
if (chunk.providerMetadata !== void 0) {
|
|
552
|
+
providerMetadata = chunk.providerMetadata;
|
|
553
|
+
}
|
|
554
|
+
if (chunk.warnings !== void 0) {
|
|
555
|
+
warnings = chunk.warnings;
|
|
556
|
+
}
|
|
557
|
+
break;
|
|
558
|
+
}
|
|
559
|
+
case "raw": {
|
|
560
|
+
raw = chunk.raw;
|
|
561
|
+
if (chunk.providerMetadata !== void 0) {
|
|
562
|
+
providerMetadata = chunk.providerMetadata;
|
|
563
|
+
}
|
|
564
|
+
break;
|
|
565
|
+
}
|
|
566
|
+
case "error": {
|
|
567
|
+
if (chunk.raw !== void 0) {
|
|
568
|
+
raw = chunk.raw;
|
|
569
|
+
}
|
|
570
|
+
if (chunk.providerMetadata !== void 0) {
|
|
571
|
+
providerMetadata = chunk.providerMetadata;
|
|
572
|
+
}
|
|
573
|
+
if (chunk.warnings !== void 0) {
|
|
574
|
+
warnings = chunk.warnings;
|
|
575
|
+
}
|
|
576
|
+
throw toError(chunk.error);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
if (emit) {
|
|
581
|
+
emitNodeEvent(args.emitEvent, args.nodeId, "text-end", {
|
|
582
|
+
...commonMeta
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
const result2 = {
|
|
586
|
+
text: final,
|
|
587
|
+
...raw !== void 0 ? { raw } : {},
|
|
588
|
+
...usage !== void 0 ? { usage } : {},
|
|
589
|
+
...finishReason !== void 0 ? { finishReason } : {},
|
|
590
|
+
...toolCalls !== void 0 ? { toolCalls } : {},
|
|
591
|
+
...providerMetadata !== void 0 ? { providerMetadata } : {},
|
|
592
|
+
...warnings !== void 0 ? { warnings } : {}
|
|
593
|
+
};
|
|
594
|
+
(0, import_core22.throwIfExecutionAborted)(abortSignal);
|
|
595
|
+
endTrace(traceSpan, result2);
|
|
596
|
+
return result2;
|
|
597
|
+
}
|
|
598
|
+
const response = await model.invoke(messages);
|
|
599
|
+
(0, import_core22.throwIfExecutionAborted)(abortSignal);
|
|
600
|
+
if (emit) {
|
|
601
|
+
emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {
|
|
602
|
+
...commonMeta
|
|
603
|
+
});
|
|
604
|
+
if (response.content) {
|
|
605
|
+
emitNodeEvent(args.emitEvent, args.nodeId, "text-delta", {
|
|
606
|
+
...commonMeta,
|
|
607
|
+
delta: response.content
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
emitNodeEvent(args.emitEvent, args.nodeId, "text-end", {
|
|
611
|
+
...commonMeta
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
const result = {
|
|
615
|
+
text: response.content,
|
|
616
|
+
...response.raw !== void 0 ? { raw: response.raw } : {},
|
|
617
|
+
...response.usage !== void 0 ? { usage: response.usage } : {},
|
|
618
|
+
...response.finishReason !== void 0 ? { finishReason: response.finishReason } : {},
|
|
619
|
+
...response.toolCalls !== void 0 ? { toolCalls: response.toolCalls } : {},
|
|
620
|
+
...response.providerMetadata !== void 0 ? { providerMetadata: response.providerMetadata } : {},
|
|
621
|
+
...response.warnings !== void 0 ? { warnings: response.warnings } : {}
|
|
622
|
+
};
|
|
623
|
+
endTrace(traceSpan, result);
|
|
624
|
+
return result;
|
|
625
|
+
} catch (error) {
|
|
626
|
+
const failure = abortSignal?.aborted ? (0, import_core22.createExecutionCancelledError)() : error;
|
|
627
|
+
failTrace(traceSpan, failure);
|
|
628
|
+
throw failure;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
var createRuntimeId2 = () => {
|
|
632
|
+
try {
|
|
633
|
+
return (0, import_node_crypto32.randomUUID)();
|
|
634
|
+
} catch {
|
|
635
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
async function reasonEngine(args, meta, inputOverride) {
|
|
639
|
+
const ctx = getHookContext();
|
|
640
|
+
const abortSignal = (0, import_core32.combineAbortSignals)(
|
|
641
|
+
ctx.node.abortSignal,
|
|
642
|
+
args.abortSignal,
|
|
643
|
+
args.model.options?.abortSignal
|
|
644
|
+
);
|
|
645
|
+
(0, import_core32.throwIfExecutionAborted)(abortSignal);
|
|
646
|
+
ctx.node.consumeExecution?.("maxModelPasses");
|
|
647
|
+
return runReasonEngine({
|
|
648
|
+
model: args.model,
|
|
649
|
+
input: inputOverride ?? args.input,
|
|
650
|
+
system: args.system,
|
|
651
|
+
temperature: args.temperature,
|
|
652
|
+
maxOutputTokens: args.maxOutputTokens,
|
|
653
|
+
stopSequences: args.stopSequences,
|
|
654
|
+
abortSignal,
|
|
655
|
+
reasoning: args.reasoning,
|
|
656
|
+
responseFormat: args.responseFormat,
|
|
657
|
+
providerOptions: args.providerOptions,
|
|
658
|
+
tools: args.tools,
|
|
659
|
+
messages: args.messages,
|
|
660
|
+
defaultTemperature: ctx.node.config?.model?.temperature,
|
|
661
|
+
stream: args.stream,
|
|
662
|
+
emit: args.emit,
|
|
663
|
+
onTextChunk: args.onTextChunk,
|
|
664
|
+
telemetry: args.telemetry,
|
|
665
|
+
nodeId: ctx.node.graph.node,
|
|
666
|
+
emitEvent: ctx.node.emit,
|
|
667
|
+
reasonTrace: ctx.reasonTrace,
|
|
668
|
+
id: meta.id,
|
|
669
|
+
opId: meta.opId
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
var tryJsonParse = (value) => {
|
|
673
|
+
try {
|
|
674
|
+
return { ok: true, value: JSON.parse(value) };
|
|
675
|
+
} catch (error) {
|
|
676
|
+
return {
|
|
677
|
+
ok: false,
|
|
678
|
+
error: error instanceof Error ? error.message : String(error)
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
var extractJsonCodeBlock = (value) => {
|
|
683
|
+
const match = value.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
684
|
+
if (!match || typeof match[1] !== "string") return null;
|
|
685
|
+
const inner = match[1].trim();
|
|
686
|
+
return inner.length > 0 ? inner : null;
|
|
687
|
+
};
|
|
688
|
+
var tryStringify = (value) => {
|
|
689
|
+
if (typeof value === "string") return value;
|
|
690
|
+
try {
|
|
691
|
+
return JSON.stringify(value, null, 2);
|
|
692
|
+
} catch {
|
|
693
|
+
return String(value);
|
|
694
|
+
}
|
|
695
|
+
};
|
|
696
|
+
var isRecord22 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
697
|
+
var resolveOutputCandidate = (text) => {
|
|
698
|
+
const trimmed = text.trim();
|
|
699
|
+
if (!trimmed) return text;
|
|
700
|
+
const direct = tryJsonParse(trimmed);
|
|
701
|
+
if (direct.ok) return direct.value;
|
|
702
|
+
const block = extractJsonCodeBlock(trimmed);
|
|
703
|
+
if (block) {
|
|
704
|
+
const parsedBlock = tryJsonParse(block);
|
|
705
|
+
if (parsedBlock.ok) return parsedBlock.value;
|
|
706
|
+
}
|
|
707
|
+
return text;
|
|
708
|
+
};
|
|
709
|
+
var looksLikeStructuredJsonAttempt = (value) => {
|
|
710
|
+
const trimmed = value.trim();
|
|
711
|
+
if (!trimmed) return false;
|
|
712
|
+
return trimmed.startsWith("{") || trimmed.startsWith("[") || /^```(?:json)?/i.test(trimmed);
|
|
713
|
+
};
|
|
714
|
+
var isLikelyTruncatedStructuredOutput = (text) => {
|
|
715
|
+
const trimmed = text.trim();
|
|
716
|
+
if (!looksLikeStructuredJsonAttempt(trimmed)) return false;
|
|
717
|
+
const direct = tryJsonParse(trimmed);
|
|
718
|
+
if (!direct.ok && direct.error) return true;
|
|
719
|
+
const block = extractJsonCodeBlock(trimmed);
|
|
720
|
+
if (!block) return false;
|
|
721
|
+
const parsedBlock = tryJsonParse(block);
|
|
722
|
+
return !parsedBlock.ok && Boolean(parsedBlock.error);
|
|
723
|
+
};
|
|
724
|
+
var createTruncatedStructuredOutputError = (label) => new Error(
|
|
725
|
+
`${label} was truncated before producing valid structured output. The model stopped due to output length. Increase maxOutputTokens or simplify the requested output.`
|
|
726
|
+
);
|
|
727
|
+
var createInvalidStructuredOutputError = (label) => new Error(
|
|
728
|
+
`${label} did not produce valid structured output. The model returned text instead of the expected JSON payload. This often happens when output is truncated or the provider does not follow JSON mode. Increase maxOutputTokens or simplify the requested output.`
|
|
729
|
+
);
|
|
730
|
+
var parseReasonOutputWithSchema = (args) => {
|
|
731
|
+
const candidate = resolveOutputCandidate(args.text);
|
|
732
|
+
try {
|
|
733
|
+
return parseWithSchema(args.schema, candidate, args.label);
|
|
734
|
+
} catch (error) {
|
|
735
|
+
if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
|
|
736
|
+
throw createTruncatedStructuredOutputError(args.label);
|
|
737
|
+
}
|
|
738
|
+
if (typeof candidate === "string" && candidate.trim().length > 0) {
|
|
739
|
+
throw createInvalidStructuredOutputError(args.label);
|
|
740
|
+
}
|
|
741
|
+
throw error;
|
|
742
|
+
}
|
|
743
|
+
};
|
|
744
|
+
var parseInterruptFirstPassResult = (args) => {
|
|
745
|
+
const candidate = resolveOutputCandidate(args.text);
|
|
746
|
+
if (!isRecord22(candidate)) {
|
|
747
|
+
if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
|
|
748
|
+
throw createTruncatedStructuredOutputError("useReason output");
|
|
749
|
+
}
|
|
750
|
+
if (typeof candidate === "string" && candidate.trim().length > 0) {
|
|
751
|
+
throw createInvalidStructuredOutputError("useReason output");
|
|
752
|
+
}
|
|
753
|
+
throw new Error(
|
|
754
|
+
"useReason first pass with interrupt must return a JSON object."
|
|
755
|
+
);
|
|
756
|
+
}
|
|
757
|
+
const mode = args.mode ?? "required";
|
|
758
|
+
let interruptRequired = true;
|
|
759
|
+
if (mode === "optional") {
|
|
760
|
+
if (candidate.decision !== "continue" && candidate.decision !== "interrupt") {
|
|
761
|
+
throw new Error(
|
|
762
|
+
'useReason optional interrupt first pass must include decision "continue" or "interrupt".'
|
|
763
|
+
);
|
|
764
|
+
}
|
|
765
|
+
interruptRequired = candidate.decision === "interrupt";
|
|
766
|
+
if (!interruptRequired) {
|
|
767
|
+
const hasUnexpectedRequest = [
|
|
768
|
+
candidate.interruptRequest,
|
|
769
|
+
candidate.request,
|
|
770
|
+
candidate.interrupt
|
|
771
|
+
].some((value) => value !== null && value !== void 0);
|
|
772
|
+
if (hasUnexpectedRequest) {
|
|
773
|
+
throw new Error(
|
|
774
|
+
'useReason optional interrupt first pass with decision "continue" must not include an interrupt request.'
|
|
775
|
+
);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
let request;
|
|
780
|
+
if (interruptRequired) {
|
|
781
|
+
const requestCandidates = [
|
|
782
|
+
candidate.interruptRequest,
|
|
783
|
+
candidate.request,
|
|
784
|
+
candidate.interrupt,
|
|
785
|
+
mode === "required" ? candidate : void 0
|
|
786
|
+
];
|
|
787
|
+
const requestPayload = requestCandidates.find(
|
|
788
|
+
(value) => value !== null && value !== void 0
|
|
789
|
+
);
|
|
790
|
+
request = parseWithSchema(
|
|
791
|
+
args.requestSchema,
|
|
792
|
+
requestPayload,
|
|
793
|
+
"useReason interrupt.request"
|
|
794
|
+
);
|
|
795
|
+
}
|
|
796
|
+
let output;
|
|
797
|
+
if (args.outputSchema) {
|
|
798
|
+
const outputPayload = Object.hasOwn(candidate, "output") ? candidate.output : candidate;
|
|
799
|
+
try {
|
|
800
|
+
output = parseWithSchema(
|
|
801
|
+
args.outputSchema,
|
|
802
|
+
outputPayload,
|
|
803
|
+
"useReason output"
|
|
804
|
+
);
|
|
805
|
+
} catch (error) {
|
|
806
|
+
if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
|
|
807
|
+
throw createTruncatedStructuredOutputError("useReason output");
|
|
808
|
+
}
|
|
809
|
+
if (typeof outputPayload === "string" && outputPayload.trim().length > 0) {
|
|
810
|
+
throw createInvalidStructuredOutputError("useReason output");
|
|
811
|
+
}
|
|
812
|
+
throw error;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
const explicitDraft = typeof candidate.draftText === "string" && candidate.draftText.length > 0 ? candidate.draftText : typeof candidate.text === "string" && candidate.text.length > 0 ? candidate.text : void 0;
|
|
816
|
+
if (!args.outputSchema && !explicitDraft) {
|
|
817
|
+
throw new Error(
|
|
818
|
+
"useReason first pass with interrupt requires `draftText` when outputSchema is not provided."
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
const draftText = explicitDraft ?? (output !== void 0 ? tryStringify(output) : String(args.text ?? ""));
|
|
822
|
+
if (!interruptRequired) {
|
|
823
|
+
return {
|
|
824
|
+
draftText,
|
|
825
|
+
interruptRequired: false,
|
|
826
|
+
...output !== void 0 ? { output } : {}
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
return {
|
|
830
|
+
draftText,
|
|
831
|
+
interruptRequired: true,
|
|
832
|
+
request,
|
|
833
|
+
...output !== void 0 ? { output } : {}
|
|
834
|
+
};
|
|
835
|
+
};
|
|
836
|
+
var import_zod5 = require("zod");
|
|
837
|
+
var tryStringify2 = (value) => {
|
|
838
|
+
if (typeof value === "string") return value;
|
|
839
|
+
try {
|
|
840
|
+
return JSON.stringify(value, null, 2);
|
|
841
|
+
} catch {
|
|
842
|
+
return String(value);
|
|
843
|
+
}
|
|
844
|
+
};
|
|
845
|
+
var tryGetSchemaHint = (schema) => {
|
|
846
|
+
try {
|
|
847
|
+
const hint = (0, import_zod5.toJSONSchema)(schema);
|
|
848
|
+
return JSON.stringify(hint, null, 2);
|
|
849
|
+
} catch {
|
|
850
|
+
return void 0;
|
|
851
|
+
}
|
|
852
|
+
};
|
|
853
|
+
var withSchemaHint = (input, label, schema) => {
|
|
854
|
+
const hint = tryGetSchemaHint(schema);
|
|
855
|
+
if (!hint) return input;
|
|
856
|
+
return `${input}
|
|
857
|
+
|
|
858
|
+
${label}:
|
|
859
|
+
${hint}`;
|
|
860
|
+
};
|
|
861
|
+
var defaultInterruptFirstPassInput = (args) => {
|
|
862
|
+
const mode = args.mode ?? "required";
|
|
863
|
+
const base = mode === "optional" ? args.outputSchema ? [
|
|
864
|
+
args.input,
|
|
865
|
+
"Output rules:",
|
|
866
|
+
"- Return JSON only. No markdown fences.",
|
|
867
|
+
"- Return an object with keys: decision, output, interruptRequest, draftText.",
|
|
868
|
+
'- decision must be either "continue" or "interrupt".',
|
|
869
|
+
'- Use decision "continue" when you can produce the final output without asking the user.',
|
|
870
|
+
'- Use decision "interrupt" only when user input is needed before finalizing.',
|
|
871
|
+
"- output must match the expected output JSON schema.",
|
|
872
|
+
'- When decision is "interrupt", interruptRequest must match the expected interrupt request JSON schema and must be a non-null object.',
|
|
873
|
+
'- When decision is "continue", interruptRequest must be null.',
|
|
874
|
+
"- draftText should be a concise plain-language summary of output."
|
|
875
|
+
].join("\n\n") : [
|
|
876
|
+
args.input,
|
|
877
|
+
"Output rules:",
|
|
878
|
+
"- Return JSON only. No markdown fences.",
|
|
879
|
+
"- Return an object with keys: decision, draftText, interruptRequest.",
|
|
880
|
+
'- decision must be either "continue" or "interrupt".',
|
|
881
|
+
'- Use decision "continue" when you can produce the final answer without asking the user.',
|
|
882
|
+
'- Use decision "interrupt" only when user input is needed before finalizing.',
|
|
883
|
+
"- draftText should be the current answer text.",
|
|
884
|
+
'- When decision is "interrupt", interruptRequest must match the expected interrupt request JSON schema and must be a non-null object.',
|
|
885
|
+
'- When decision is "continue", interruptRequest must be null.'
|
|
886
|
+
].join("\n\n") : args.outputSchema ? [
|
|
887
|
+
args.input,
|
|
888
|
+
"Output rules:",
|
|
889
|
+
"- Return JSON only. No markdown fences.",
|
|
890
|
+
"- Return an object with keys: output, interruptRequest, draftText.",
|
|
891
|
+
"- output must match the expected output JSON schema.",
|
|
892
|
+
"- interruptRequest must match the expected interrupt request JSON schema.",
|
|
893
|
+
"- interruptRequest must be a non-null object.",
|
|
894
|
+
"- draftText should be a concise plain-language summary of output.",
|
|
895
|
+
"- If uncertain, still return a valid interruptRequest with safe default options."
|
|
896
|
+
].join("\n\n") : [
|
|
897
|
+
args.input,
|
|
898
|
+
"Output rules:",
|
|
899
|
+
"- Return JSON only. No markdown fences.",
|
|
900
|
+
"- Return an object with keys: draftText, interruptRequest.",
|
|
901
|
+
"- draftText should be the current draft answer text.",
|
|
902
|
+
"- interruptRequest must match the expected interrupt request JSON schema.",
|
|
903
|
+
"- interruptRequest must be a non-null object.",
|
|
904
|
+
"- If uncertain, still return a valid interruptRequest with safe default options."
|
|
905
|
+
].join("\n\n");
|
|
906
|
+
const withInterruptSchema = withSchemaHint(
|
|
907
|
+
base,
|
|
908
|
+
"Expected interrupt request JSON schema (for `interruptRequest`)",
|
|
909
|
+
args.requestSchema
|
|
910
|
+
);
|
|
911
|
+
if (!args.outputSchema) return withInterruptSchema;
|
|
912
|
+
const withOutputSchema = withSchemaHint(
|
|
913
|
+
withInterruptSchema,
|
|
914
|
+
"Expected output JSON schema (for `output`)",
|
|
915
|
+
args.outputSchema
|
|
916
|
+
);
|
|
917
|
+
return [
|
|
918
|
+
withOutputSchema,
|
|
919
|
+
"JSON template:",
|
|
920
|
+
mode === "optional" ? '{ "decision": "continue", "output": { ... }, "interruptRequest": null, "draftText": "..." }' : '{ "output": { ... }, "interruptRequest": { ... }, "draftText": "..." }'
|
|
921
|
+
].join("\n\n");
|
|
922
|
+
};
|
|
923
|
+
var defaultContinuationInput = (args) => [
|
|
924
|
+
"You are continuing a paused reasoning task after user input.",
|
|
925
|
+
"Update the previous result using the user response and return the final answer only.",
|
|
926
|
+
"If the previous output was JSON, return JSON in the same schema.",
|
|
927
|
+
"",
|
|
928
|
+
`Original request:
|
|
929
|
+
${args.input}`,
|
|
930
|
+
`Draft answer text:
|
|
931
|
+
${args.draftText}`,
|
|
932
|
+
`Draft output object:
|
|
933
|
+
${tryStringify2(args.draftOutput ?? args.draftText)}`,
|
|
934
|
+
`Interrupt request shown to user:
|
|
935
|
+
${tryStringify2(args.request)}`,
|
|
936
|
+
`User response:
|
|
937
|
+
${tryStringify2(args.response)}`
|
|
938
|
+
].join("\n\n");
|
|
939
|
+
var withOutputGuardrails = (input, schema) => {
|
|
940
|
+
const base = [
|
|
941
|
+
input,
|
|
942
|
+
"Output rules:",
|
|
943
|
+
"- Return only JSON, no markdown fences.",
|
|
944
|
+
"- Ensure the response conforms to the expected output schema.",
|
|
945
|
+
"- Do not include explanatory text outside the JSON object."
|
|
946
|
+
].join("\n\n");
|
|
947
|
+
return withSchemaHint(base, "Expected output JSON schema", schema);
|
|
948
|
+
};
|
|
949
|
+
var withStructuredStreamHints = (input, args) => {
|
|
950
|
+
const rules = [];
|
|
951
|
+
for (const path of args.setFieldPaths ?? []) {
|
|
952
|
+
rules.push(
|
|
953
|
+
`- For the field path pattern \`${path}\`, write one stable final value and do not rewrite it after it is complete.`
|
|
954
|
+
);
|
|
955
|
+
}
|
|
956
|
+
for (const path of args.appendFieldPaths ?? []) {
|
|
957
|
+
rules.push(
|
|
958
|
+
`- For the array field path pattern \`${path}\`, write items in final order and finish each item before starting the next.`
|
|
959
|
+
);
|
|
960
|
+
rules.push(
|
|
961
|
+
`- Once an item in \`${path}\` is complete, do not rewrite or reorder earlier items.`
|
|
962
|
+
);
|
|
963
|
+
}
|
|
964
|
+
for (const path of args.textDeltaFieldPaths ?? []) {
|
|
965
|
+
rules.push(
|
|
966
|
+
`- For the string field path pattern \`${path}\`, write the value progressively by appending text to the end.`
|
|
967
|
+
);
|
|
968
|
+
rules.push(
|
|
969
|
+
`- Do not rewrite or replace the earlier prefix of \`${path}\` once emitted.`
|
|
970
|
+
);
|
|
971
|
+
}
|
|
972
|
+
if (rules.length === 0) return input;
|
|
973
|
+
return [
|
|
974
|
+
input,
|
|
975
|
+
"Streaming rules:",
|
|
976
|
+
...rules,
|
|
977
|
+
"- Keep the overall response as valid JSON."
|
|
978
|
+
].join("\n\n");
|
|
979
|
+
};
|
|
980
|
+
var mergeUsage = (left, right) => {
|
|
981
|
+
if (!left) return right;
|
|
982
|
+
if (!right) return left;
|
|
983
|
+
const sum = (a, b) => a === void 0 && b === void 0 ? void 0 : (a ?? 0) + (b ?? 0);
|
|
984
|
+
const raw = left.raw || right.raw ? {
|
|
985
|
+
...left.raw ?? {},
|
|
986
|
+
...right.raw ?? {}
|
|
987
|
+
} : void 0;
|
|
988
|
+
const input = sum(left.input, right.input);
|
|
989
|
+
const output = sum(left.output, right.output);
|
|
990
|
+
const total = sum(left.total, right.total);
|
|
991
|
+
const reasoning = sum(left.reasoning, right.reasoning);
|
|
992
|
+
const cacheRead = sum(left.cacheRead, right.cacheRead);
|
|
993
|
+
const cacheWrite = sum(left.cacheWrite, right.cacheWrite);
|
|
994
|
+
const merged = {};
|
|
995
|
+
if (input !== void 0) merged.input = input;
|
|
996
|
+
if (output !== void 0) merged.output = output;
|
|
997
|
+
if (total !== void 0) merged.total = total;
|
|
998
|
+
if (reasoning !== void 0) merged.reasoning = reasoning;
|
|
999
|
+
if (cacheRead !== void 0) merged.cacheRead = cacheRead;
|
|
1000
|
+
if (cacheWrite !== void 0) merged.cacheWrite = cacheWrite;
|
|
1001
|
+
if (raw) merged.raw = raw;
|
|
1002
|
+
return merged;
|
|
1003
|
+
};
|
|
1004
|
+
var mergeWarnings = (left, right) => {
|
|
1005
|
+
if (!left?.length) return right;
|
|
1006
|
+
if (!right?.length) return left;
|
|
1007
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1008
|
+
const merged = [];
|
|
1009
|
+
for (const warning of [...left, ...right]) {
|
|
1010
|
+
const key = JSON.stringify(warning);
|
|
1011
|
+
if (seen.has(key)) continue;
|
|
1012
|
+
seen.add(key);
|
|
1013
|
+
merged.push(warning);
|
|
1014
|
+
}
|
|
1015
|
+
return merged;
|
|
1016
|
+
};
|
|
1017
|
+
var mergeProviderMetadata = (left, right) => {
|
|
1018
|
+
if (!left) return right;
|
|
1019
|
+
if (!right) return left;
|
|
1020
|
+
return {
|
|
1021
|
+
...left,
|
|
1022
|
+
...right
|
|
1023
|
+
};
|
|
1024
|
+
};
|
|
1025
|
+
var emitReasonStructuredOutput = (args) => {
|
|
1026
|
+
if (!args.emit || !shouldEmitStructured(args.structured)) return;
|
|
1027
|
+
emitStructuredData({
|
|
1028
|
+
dataType: args.structured?.dataType ?? "reason-output",
|
|
1029
|
+
data: args.output,
|
|
1030
|
+
kind: "final",
|
|
1031
|
+
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
1032
|
+
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
1033
|
+
...args.id ? { id: args.id } : {},
|
|
1034
|
+
streamId: args.opId
|
|
1035
|
+
});
|
|
1036
|
+
};
|
|
1037
|
+
var resolveAppendFieldPaths = (cfg) => {
|
|
1038
|
+
const fields = cfg?.fields;
|
|
1039
|
+
if (!fields) return [];
|
|
1040
|
+
const appendPaths = Object.entries(fields).filter(([, mode]) => mode === "append").map(([path]) => path);
|
|
1041
|
+
for (const path of appendPaths) {
|
|
1042
|
+
assertStructuredPath(path, "useReason structured append");
|
|
1043
|
+
}
|
|
1044
|
+
return appendPaths;
|
|
1045
|
+
};
|
|
1046
|
+
var resolveSetFieldPaths = (cfg) => {
|
|
1047
|
+
const fields = cfg?.fields;
|
|
1048
|
+
if (!fields) return [];
|
|
1049
|
+
const setPaths = Object.entries(fields).filter(([, mode]) => mode === "set").map(([path]) => path);
|
|
1050
|
+
for (const path of setPaths) {
|
|
1051
|
+
assertStructuredPath(path, "useReason structured set");
|
|
1052
|
+
}
|
|
1053
|
+
return setPaths;
|
|
1054
|
+
};
|
|
1055
|
+
var resolveTextDeltaFieldPaths = (cfg) => {
|
|
1056
|
+
const fields = cfg?.fields;
|
|
1057
|
+
if (!fields) return [];
|
|
1058
|
+
const textPaths = Object.entries(fields).filter(([, mode]) => mode === "text-delta").map(([path]) => path);
|
|
1059
|
+
for (const path of textPaths) {
|
|
1060
|
+
assertStructuredPath(path, "useReason structured text-delta");
|
|
1061
|
+
}
|
|
1062
|
+
return textPaths;
|
|
1063
|
+
};
|
|
1064
|
+
var extractCompletedStringValueAt = (args) => {
|
|
1065
|
+
let value = "";
|
|
1066
|
+
for (let i = args.start; i < args.text.length; i += 1) {
|
|
1067
|
+
const ch = args.text[i];
|
|
1068
|
+
if (ch === void 0) break;
|
|
1069
|
+
if (ch === '"') {
|
|
1070
|
+
return {
|
|
1071
|
+
complete: true,
|
|
1072
|
+
value
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
if (ch !== "\\") {
|
|
1076
|
+
value += ch;
|
|
1077
|
+
continue;
|
|
1078
|
+
}
|
|
1079
|
+
const next = args.text[i + 1];
|
|
1080
|
+
if (!next) {
|
|
1081
|
+
return {
|
|
1082
|
+
complete: false
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
if (next === '"' || next === "\\" || next === "/" || next === "b" || next === "f" || next === "n" || next === "r" || next === "t") {
|
|
1086
|
+
value += next === "b" ? "\b" : next === "f" ? "\f" : next === "n" ? "\n" : next === "r" ? "\r" : next === "t" ? " " : next;
|
|
1087
|
+
i += 1;
|
|
1088
|
+
continue;
|
|
1089
|
+
}
|
|
1090
|
+
if (next === "u") {
|
|
1091
|
+
const hex = args.text.slice(i + 2, i + 6);
|
|
1092
|
+
if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
|
|
1093
|
+
return {
|
|
1094
|
+
complete: false
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
value += String.fromCharCode(Number.parseInt(hex, 16));
|
|
1098
|
+
i += 5;
|
|
1099
|
+
continue;
|
|
1100
|
+
}
|
|
1101
|
+
return {
|
|
1102
|
+
complete: false
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
return {
|
|
1106
|
+
complete: false
|
|
1107
|
+
};
|
|
1108
|
+
};
|
|
1109
|
+
var skipWhitespace = (text, start) => {
|
|
1110
|
+
let i = start;
|
|
1111
|
+
while (i < text.length && /\s/.test(text[i] ?? "")) i += 1;
|
|
1112
|
+
return i;
|
|
1113
|
+
};
|
|
1114
|
+
var pathSegmentMatches = (patternPart, actualPart) => patternPart === "*" || patternPart === actualPart;
|
|
1115
|
+
var pathMatchesPattern = (actualParts, patternParts) => actualParts.length === patternParts.length && actualParts.every(
|
|
1116
|
+
(part, index) => pathSegmentMatches(patternParts[index] ?? "", part)
|
|
1117
|
+
);
|
|
1118
|
+
var isPathPrefix = (prefix, full) => prefix.length <= full.length && prefix.every((part, index) => pathSegmentMatches(full[index] ?? "", part));
|
|
1119
|
+
var parseStringToken = (text, start) => {
|
|
1120
|
+
if (text[start] !== '"') return { complete: false };
|
|
1121
|
+
const parsed = extractCompletedStringValueAt({ text, start: start + 1 });
|
|
1122
|
+
if (!parsed.complete || parsed.value === void 0)
|
|
1123
|
+
return { complete: false };
|
|
1124
|
+
let inEscape = false;
|
|
1125
|
+
for (let i = start + 1; i < text.length; i += 1) {
|
|
1126
|
+
const ch = text[i];
|
|
1127
|
+
if (ch === void 0) break;
|
|
1128
|
+
if (inEscape) {
|
|
1129
|
+
inEscape = false;
|
|
1130
|
+
if (ch === "u") i += 4;
|
|
1131
|
+
continue;
|
|
1132
|
+
}
|
|
1133
|
+
if (ch === "\\") {
|
|
1134
|
+
inEscape = true;
|
|
1135
|
+
continue;
|
|
1136
|
+
}
|
|
1137
|
+
if (ch === '"') {
|
|
1138
|
+
return {
|
|
1139
|
+
complete: true,
|
|
1140
|
+
end: i + 1,
|
|
1141
|
+
value: parsed.value
|
|
1142
|
+
};
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
return { complete: false };
|
|
1146
|
+
};
|
|
1147
|
+
var skipNestedValue = (text, start) => {
|
|
1148
|
+
const opener = text[start];
|
|
1149
|
+
const closer = opener === "{" ? "}" : "]";
|
|
1150
|
+
let inString = false;
|
|
1151
|
+
let isEscaped = false;
|
|
1152
|
+
let nesting = 0;
|
|
1153
|
+
for (let i = start; i < text.length; i += 1) {
|
|
1154
|
+
const ch = text[i];
|
|
1155
|
+
if (ch === void 0) break;
|
|
1156
|
+
if (inString) {
|
|
1157
|
+
if (isEscaped) {
|
|
1158
|
+
isEscaped = false;
|
|
1159
|
+
continue;
|
|
1160
|
+
}
|
|
1161
|
+
if (ch === "\\") {
|
|
1162
|
+
isEscaped = true;
|
|
1163
|
+
continue;
|
|
1164
|
+
}
|
|
1165
|
+
if (ch === '"') {
|
|
1166
|
+
inString = false;
|
|
1167
|
+
}
|
|
1168
|
+
continue;
|
|
1169
|
+
}
|
|
1170
|
+
if (ch === '"') {
|
|
1171
|
+
inString = true;
|
|
1172
|
+
continue;
|
|
1173
|
+
}
|
|
1174
|
+
if (ch === opener) {
|
|
1175
|
+
nesting += 1;
|
|
1176
|
+
continue;
|
|
1177
|
+
}
|
|
1178
|
+
if (ch === closer) {
|
|
1179
|
+
nesting -= 1;
|
|
1180
|
+
if (nesting === 0) {
|
|
1181
|
+
return { complete: true, end: i + 1 };
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
return { complete: false };
|
|
1186
|
+
};
|
|
1187
|
+
var skipCompleteValue = (text, start) => {
|
|
1188
|
+
const i = skipWhitespace(text, start);
|
|
1189
|
+
const first = text[i];
|
|
1190
|
+
if (!first) return { complete: false };
|
|
1191
|
+
if (first === '"') {
|
|
1192
|
+
const parsed = parseStringToken(text, i);
|
|
1193
|
+
return parsed.complete && parsed.end !== void 0 ? { complete: true, end: parsed.end } : { complete: false };
|
|
1194
|
+
}
|
|
1195
|
+
if (first === "{" || first === "[") {
|
|
1196
|
+
return skipNestedValue(text, i);
|
|
1197
|
+
}
|
|
1198
|
+
for (let j = i; j < text.length; j += 1) {
|
|
1199
|
+
const ch = text[j];
|
|
1200
|
+
if (ch !== "," && ch !== "}" && ch !== "]") continue;
|
|
1201
|
+
const raw = text.slice(i, j).trim();
|
|
1202
|
+
if (!raw) return { complete: false };
|
|
1203
|
+
try {
|
|
1204
|
+
JSON.parse(raw);
|
|
1205
|
+
return { complete: true, end: j };
|
|
1206
|
+
} catch {
|
|
1207
|
+
return { complete: false };
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
return { complete: false };
|
|
1211
|
+
};
|
|
1212
|
+
var locateValueStart = (args) => {
|
|
1213
|
+
const [first] = locateValueStarts(args);
|
|
1214
|
+
return first ? {
|
|
1215
|
+
found: true,
|
|
1216
|
+
start: first.start
|
|
1217
|
+
} : {
|
|
1218
|
+
found: false
|
|
1219
|
+
};
|
|
1220
|
+
};
|
|
1221
|
+
var locateValueStarts = (args) => {
|
|
1222
|
+
const targetParts = parseStructuredPath(
|
|
1223
|
+
args.path,
|
|
1224
|
+
"useReason structured field"
|
|
1225
|
+
);
|
|
1226
|
+
const matches = [];
|
|
1227
|
+
const visitValue = (start, currentParts) => {
|
|
1228
|
+
const valueStart = skipWhitespace(args.text, start);
|
|
1229
|
+
if (pathMatchesPattern(currentParts, targetParts)) {
|
|
1230
|
+
matches.push({
|
|
1231
|
+
path: currentParts.join("."),
|
|
1232
|
+
start: valueStart
|
|
1233
|
+
});
|
|
1234
|
+
return;
|
|
1235
|
+
}
|
|
1236
|
+
if (!isPathPrefix(currentParts, targetParts)) {
|
|
1237
|
+
return;
|
|
1238
|
+
}
|
|
1239
|
+
const first = args.text[valueStart];
|
|
1240
|
+
if (first === "{") visitObject(valueStart + 1, currentParts);
|
|
1241
|
+
if (first === "[") visitArray(valueStart + 1, currentParts);
|
|
1242
|
+
};
|
|
1243
|
+
const visitObject = (start, currentParts) => {
|
|
1244
|
+
let i = skipWhitespace(args.text, start);
|
|
1245
|
+
while (i < args.text.length) {
|
|
1246
|
+
const ch = args.text[i];
|
|
1247
|
+
if (ch === "}") return;
|
|
1248
|
+
if (ch === ",") {
|
|
1249
|
+
i = skipWhitespace(args.text, i + 1);
|
|
1250
|
+
continue;
|
|
1251
|
+
}
|
|
1252
|
+
if (ch !== '"') return;
|
|
1253
|
+
const key = parseStringToken(args.text, i);
|
|
1254
|
+
if (!key.complete || key.end === void 0 || key.value === void 0) {
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1257
|
+
i = skipWhitespace(args.text, key.end);
|
|
1258
|
+
if (args.text[i] !== ":") return;
|
|
1259
|
+
const valueStart = skipWhitespace(args.text, i + 1);
|
|
1260
|
+
const childParts = [...currentParts, key.value];
|
|
1261
|
+
if (pathMatchesPattern(childParts, targetParts)) {
|
|
1262
|
+
matches.push({
|
|
1263
|
+
path: childParts.join("."),
|
|
1264
|
+
start: valueStart
|
|
1265
|
+
});
|
|
1266
|
+
} else if (isPathPrefix(childParts, targetParts)) {
|
|
1267
|
+
visitValue(valueStart, childParts);
|
|
1268
|
+
}
|
|
1269
|
+
const skipped = skipCompleteValue(args.text, valueStart);
|
|
1270
|
+
if (!skipped.complete || skipped.end === void 0) {
|
|
1271
|
+
return;
|
|
1272
|
+
}
|
|
1273
|
+
i = skipWhitespace(args.text, skipped.end);
|
|
1274
|
+
if (args.text[i] === ",") {
|
|
1275
|
+
i = skipWhitespace(args.text, i + 1);
|
|
1276
|
+
continue;
|
|
1277
|
+
}
|
|
1278
|
+
if (args.text[i] === "}") return;
|
|
1279
|
+
return;
|
|
1280
|
+
}
|
|
1281
|
+
};
|
|
1282
|
+
const visitArray = (start, currentParts) => {
|
|
1283
|
+
let i = skipWhitespace(args.text, start);
|
|
1284
|
+
let index = 0;
|
|
1285
|
+
while (i < args.text.length) {
|
|
1286
|
+
if (args.text[i] === "]") return;
|
|
1287
|
+
const childParts = [...currentParts, String(index)];
|
|
1288
|
+
if (pathMatchesPattern(childParts, targetParts)) {
|
|
1289
|
+
matches.push({
|
|
1290
|
+
path: childParts.join("."),
|
|
1291
|
+
start: i
|
|
1292
|
+
});
|
|
1293
|
+
} else if (isPathPrefix(childParts, targetParts)) {
|
|
1294
|
+
visitValue(i, childParts);
|
|
1295
|
+
}
|
|
1296
|
+
const skipped = skipCompleteValue(args.text, i);
|
|
1297
|
+
if (!skipped.complete || skipped.end === void 0) {
|
|
1298
|
+
return;
|
|
1299
|
+
}
|
|
1300
|
+
i = skipWhitespace(args.text, skipped.end);
|
|
1301
|
+
if (args.text[i] === ",") {
|
|
1302
|
+
index += 1;
|
|
1303
|
+
i = skipWhitespace(args.text, i + 1);
|
|
1304
|
+
continue;
|
|
1305
|
+
}
|
|
1306
|
+
if (args.text[i] === "]") return;
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
1309
|
+
};
|
|
1310
|
+
visitValue(0, []);
|
|
1311
|
+
return matches;
|
|
1312
|
+
};
|
|
1313
|
+
var extractCompletedValueAt = (args) => {
|
|
1314
|
+
const start = skipWhitespace(args.text, args.start);
|
|
1315
|
+
const first = args.text[start];
|
|
1316
|
+
if (!first) return { complete: false };
|
|
1317
|
+
if (first === '"') {
|
|
1318
|
+
const parsed = extractCompletedStringValueAt({
|
|
1319
|
+
text: args.text,
|
|
1320
|
+
start: start + 1
|
|
1321
|
+
});
|
|
1322
|
+
return parsed.complete ? {
|
|
1323
|
+
complete: true,
|
|
1324
|
+
value: parsed.value
|
|
1325
|
+
} : {
|
|
1326
|
+
complete: false
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
1329
|
+
if (first === "{") {
|
|
1330
|
+
return extractCompletedNestedValueAt({
|
|
1331
|
+
text: args.text,
|
|
1332
|
+
start,
|
|
1333
|
+
opener: "{",
|
|
1334
|
+
closer: "}"
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
if (first === "[") {
|
|
1338
|
+
return extractCompletedNestedValueAt({
|
|
1339
|
+
text: args.text,
|
|
1340
|
+
start,
|
|
1341
|
+
opener: "[",
|
|
1342
|
+
closer: "]"
|
|
1343
|
+
});
|
|
1344
|
+
}
|
|
1345
|
+
for (let i = start; i < args.text.length; i += 1) {
|
|
1346
|
+
const ch = args.text[i];
|
|
1347
|
+
if (ch === void 0) break;
|
|
1348
|
+
if (ch !== "," && ch !== "}" && ch !== "]") continue;
|
|
1349
|
+
const raw = args.text.slice(start, i).trim();
|
|
1350
|
+
if (!raw) return { complete: false };
|
|
1351
|
+
try {
|
|
1352
|
+
return {
|
|
1353
|
+
complete: true,
|
|
1354
|
+
value: JSON.parse(raw)
|
|
1355
|
+
};
|
|
1356
|
+
} catch {
|
|
1357
|
+
return { complete: false };
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
return { complete: false };
|
|
1361
|
+
};
|
|
1362
|
+
var extractCompletedNestedValueAt = (args) => {
|
|
1363
|
+
let inString = false;
|
|
1364
|
+
let isEscaped = false;
|
|
1365
|
+
let nesting = 0;
|
|
1366
|
+
for (let i = args.start; i < args.text.length; i += 1) {
|
|
1367
|
+
const ch = args.text[i];
|
|
1368
|
+
if (ch === void 0) break;
|
|
1369
|
+
if (inString) {
|
|
1370
|
+
if (isEscaped) {
|
|
1371
|
+
isEscaped = false;
|
|
1372
|
+
continue;
|
|
1373
|
+
}
|
|
1374
|
+
if (ch === "\\") {
|
|
1375
|
+
isEscaped = true;
|
|
1376
|
+
continue;
|
|
1377
|
+
}
|
|
1378
|
+
if (ch === '"') {
|
|
1379
|
+
inString = false;
|
|
1380
|
+
}
|
|
1381
|
+
continue;
|
|
1382
|
+
}
|
|
1383
|
+
if (ch === '"') {
|
|
1384
|
+
inString = true;
|
|
1385
|
+
continue;
|
|
1386
|
+
}
|
|
1387
|
+
if (ch === args.opener) {
|
|
1388
|
+
nesting += 1;
|
|
1389
|
+
continue;
|
|
1390
|
+
}
|
|
1391
|
+
if (ch === args.closer) {
|
|
1392
|
+
nesting -= 1;
|
|
1393
|
+
if (nesting === 0) {
|
|
1394
|
+
const raw = args.text.slice(args.start, i + 1);
|
|
1395
|
+
try {
|
|
1396
|
+
return {
|
|
1397
|
+
complete: true,
|
|
1398
|
+
value: JSON.parse(raw)
|
|
1399
|
+
};
|
|
1400
|
+
} catch {
|
|
1401
|
+
return {
|
|
1402
|
+
complete: false
|
|
1403
|
+
};
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
return {
|
|
1409
|
+
complete: false
|
|
1410
|
+
};
|
|
1411
|
+
};
|
|
1412
|
+
var extractCompletedFieldValues = (args) => {
|
|
1413
|
+
const located = locateValueStarts(args);
|
|
1414
|
+
const values = [];
|
|
1415
|
+
for (const match of located) {
|
|
1416
|
+
const parsed = extractCompletedValueAt({
|
|
1417
|
+
text: args.text,
|
|
1418
|
+
start: match.start
|
|
1419
|
+
});
|
|
1420
|
+
if (!parsed.complete) continue;
|
|
1421
|
+
values.push({
|
|
1422
|
+
path: match.path,
|
|
1423
|
+
value: parsed.value
|
|
1424
|
+
});
|
|
1425
|
+
}
|
|
1426
|
+
return values;
|
|
1427
|
+
};
|
|
1428
|
+
var extractCompletedArrayItems = (args) => {
|
|
1429
|
+
const located = locateValueStart(args);
|
|
1430
|
+
if (!located.found || located.start === void 0) return [];
|
|
1431
|
+
const arrayStart = skipWhitespace(args.text, located.start);
|
|
1432
|
+
if (args.text[arrayStart] !== "[") return [];
|
|
1433
|
+
const start = arrayStart + 1;
|
|
1434
|
+
const items = [];
|
|
1435
|
+
let inString = false;
|
|
1436
|
+
let isEscaped = false;
|
|
1437
|
+
let nesting = 0;
|
|
1438
|
+
let itemStart = -1;
|
|
1439
|
+
for (let i = start; i < args.text.length; i += 1) {
|
|
1440
|
+
const ch = args.text[i];
|
|
1441
|
+
if (ch === void 0) break;
|
|
1442
|
+
if (inString) {
|
|
1443
|
+
if (isEscaped) {
|
|
1444
|
+
isEscaped = false;
|
|
1445
|
+
continue;
|
|
1446
|
+
}
|
|
1447
|
+
if (ch === "\\") {
|
|
1448
|
+
isEscaped = true;
|
|
1449
|
+
continue;
|
|
1450
|
+
}
|
|
1451
|
+
if (ch === '"') {
|
|
1452
|
+
inString = false;
|
|
1453
|
+
}
|
|
1454
|
+
continue;
|
|
1455
|
+
}
|
|
1456
|
+
if (ch === '"') {
|
|
1457
|
+
inString = true;
|
|
1458
|
+
if (itemStart < 0) itemStart = i;
|
|
1459
|
+
continue;
|
|
1460
|
+
}
|
|
1461
|
+
if (itemStart < 0) {
|
|
1462
|
+
if (/\s/.test(ch) || ch === ",") continue;
|
|
1463
|
+
if (ch === "]") break;
|
|
1464
|
+
itemStart = i;
|
|
1465
|
+
if (ch === "{" || ch === "[") {
|
|
1466
|
+
nesting = 1;
|
|
1467
|
+
} else {
|
|
1468
|
+
nesting = 0;
|
|
1469
|
+
}
|
|
1470
|
+
continue;
|
|
1471
|
+
}
|
|
1472
|
+
if (ch === "{" || ch === "[") {
|
|
1473
|
+
nesting += 1;
|
|
1474
|
+
continue;
|
|
1475
|
+
}
|
|
1476
|
+
if (ch === "}" || ch === "]") {
|
|
1477
|
+
if (nesting > 0) {
|
|
1478
|
+
nesting -= 1;
|
|
1479
|
+
continue;
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
if (nesting === 0 && (ch === "," || ch === "]")) {
|
|
1483
|
+
const raw = args.text.slice(itemStart, i).trim();
|
|
1484
|
+
if (raw) {
|
|
1485
|
+
try {
|
|
1486
|
+
items.push(JSON.parse(raw));
|
|
1487
|
+
} catch {
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
itemStart = -1;
|
|
1491
|
+
if (ch === "]") break;
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
return items;
|
|
1495
|
+
};
|
|
1496
|
+
var extractCompletedArrayItemGroups = (args) => {
|
|
1497
|
+
return locateValueStarts(args).flatMap((match) => {
|
|
1498
|
+
const items = extractCompletedArrayItems({
|
|
1499
|
+
text: args.text,
|
|
1500
|
+
path: match.path
|
|
1501
|
+
});
|
|
1502
|
+
return items.length > 0 ? [
|
|
1503
|
+
{
|
|
1504
|
+
path: match.path,
|
|
1505
|
+
items
|
|
1506
|
+
}
|
|
1507
|
+
] : [];
|
|
1508
|
+
});
|
|
1509
|
+
};
|
|
1510
|
+
var extractStreamingStringValue = (args) => {
|
|
1511
|
+
const located = locateValueStart(args);
|
|
1512
|
+
if (!located.found || located.start === void 0) {
|
|
1513
|
+
return {
|
|
1514
|
+
found: located.found,
|
|
1515
|
+
complete: false,
|
|
1516
|
+
value: ""
|
|
1517
|
+
};
|
|
1518
|
+
}
|
|
1519
|
+
const stringStart = skipWhitespace(args.text, located.start);
|
|
1520
|
+
if (args.text[stringStart] !== '"') {
|
|
1521
|
+
return {
|
|
1522
|
+
found: true,
|
|
1523
|
+
complete: false,
|
|
1524
|
+
value: ""
|
|
1525
|
+
};
|
|
1526
|
+
}
|
|
1527
|
+
let value = "";
|
|
1528
|
+
for (let i = stringStart + 1; i < args.text.length; i += 1) {
|
|
1529
|
+
const ch = args.text[i];
|
|
1530
|
+
if (ch === void 0) break;
|
|
1531
|
+
if (ch === '"') {
|
|
1532
|
+
return {
|
|
1533
|
+
found: true,
|
|
1534
|
+
complete: true,
|
|
1535
|
+
value
|
|
1536
|
+
};
|
|
1537
|
+
}
|
|
1538
|
+
if (ch !== "\\") {
|
|
1539
|
+
value += ch;
|
|
1540
|
+
continue;
|
|
1541
|
+
}
|
|
1542
|
+
const next = args.text[i + 1];
|
|
1543
|
+
if (!next) {
|
|
1544
|
+
return {
|
|
1545
|
+
found: true,
|
|
1546
|
+
complete: false,
|
|
1547
|
+
value
|
|
1548
|
+
};
|
|
1549
|
+
}
|
|
1550
|
+
if (next === '"' || next === "\\" || next === "/" || next === "b" || next === "f" || next === "n" || next === "r" || next === "t") {
|
|
1551
|
+
value += next === "b" ? "\b" : next === "f" ? "\f" : next === "n" ? "\n" : next === "r" ? "\r" : next === "t" ? " " : next;
|
|
1552
|
+
i += 1;
|
|
1553
|
+
continue;
|
|
1554
|
+
}
|
|
1555
|
+
if (next === "u") {
|
|
1556
|
+
const hex = args.text.slice(i + 2, i + 6);
|
|
1557
|
+
if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
|
|
1558
|
+
return {
|
|
1559
|
+
found: true,
|
|
1560
|
+
complete: false,
|
|
1561
|
+
value
|
|
1562
|
+
};
|
|
1563
|
+
}
|
|
1564
|
+
value += String.fromCharCode(Number.parseInt(hex, 16));
|
|
1565
|
+
i += 5;
|
|
1566
|
+
continue;
|
|
1567
|
+
}
|
|
1568
|
+
return {
|
|
1569
|
+
found: true,
|
|
1570
|
+
complete: false,
|
|
1571
|
+
value
|
|
1572
|
+
};
|
|
1573
|
+
}
|
|
1574
|
+
return {
|
|
1575
|
+
found: true,
|
|
1576
|
+
complete: false,
|
|
1577
|
+
value
|
|
1578
|
+
};
|
|
1579
|
+
};
|
|
1580
|
+
var extractStreamingStringValues = (args) => {
|
|
1581
|
+
return locateValueStarts(args).flatMap((match) => {
|
|
1582
|
+
const current = extractStreamingStringValue({
|
|
1583
|
+
text: args.text,
|
|
1584
|
+
path: match.path
|
|
1585
|
+
});
|
|
1586
|
+
return current.found ? [
|
|
1587
|
+
{
|
|
1588
|
+
path: match.path,
|
|
1589
|
+
complete: current.complete,
|
|
1590
|
+
value: current.value
|
|
1591
|
+
}
|
|
1592
|
+
] : [];
|
|
1593
|
+
});
|
|
1594
|
+
};
|
|
1595
|
+
var import_core42 = require("@kortyx/core");
|
|
1596
|
+
var toErrorMessage2 = (error) => error instanceof Error ? error.message : String(error);
|
|
1597
|
+
var toToolDefinitions = (tools) => tools.map((tool) => ({
|
|
1598
|
+
name: tool.name,
|
|
1599
|
+
...tool.title ? { title: tool.title } : {},
|
|
1600
|
+
...tool.description ? { description: tool.description } : {},
|
|
1601
|
+
inputSchema: tool.inputSchema,
|
|
1602
|
+
...tool.outputSchema !== void 0 ? { outputSchema: tool.outputSchema } : {},
|
|
1603
|
+
...tool.annotations !== void 0 ? { annotations: tool.annotations } : {},
|
|
1604
|
+
...tool.metadata !== void 0 ? { metadata: tool.metadata } : {}
|
|
1605
|
+
}));
|
|
1606
|
+
var resolveToolExecutionBoolean = (value, toolName, fallback) => {
|
|
1607
|
+
if (typeof value === "boolean") return value;
|
|
1608
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
1609
|
+
return value[toolName] ?? fallback;
|
|
1610
|
+
}
|
|
1611
|
+
return fallback;
|
|
1612
|
+
};
|
|
1613
|
+
var normalizeToolResult = (toolCall, value) => {
|
|
1614
|
+
if (value && typeof value === "object" && !Array.isArray(value) && "toolCallId" in value && "name" in value && "content" in value) {
|
|
1615
|
+
return value;
|
|
1616
|
+
}
|
|
1617
|
+
let content;
|
|
1618
|
+
try {
|
|
1619
|
+
content = typeof value === "string" ? value : JSON.stringify(value);
|
|
1620
|
+
} catch {
|
|
1621
|
+
content = String(value);
|
|
1622
|
+
}
|
|
1623
|
+
return {
|
|
1624
|
+
toolCallId: toolCall.id,
|
|
1625
|
+
name: toolCall.name,
|
|
1626
|
+
content,
|
|
1627
|
+
...value !== void 0 && value !== null && typeof value === "object" && !Array.isArray(value) ? { structuredContent: value } : {}
|
|
1628
|
+
};
|
|
1629
|
+
};
|
|
1630
|
+
var closeOwnedTools = async (tools) => {
|
|
1631
|
+
const closeFns = /* @__PURE__ */ new Set();
|
|
1632
|
+
for (const tool of tools) {
|
|
1633
|
+
if (tool.closeAfterUse === false || !tool.close) continue;
|
|
1634
|
+
closeFns.add(tool.close);
|
|
1635
|
+
}
|
|
1636
|
+
await Promise.all(
|
|
1637
|
+
[...closeFns].map(async (close) => {
|
|
1638
|
+
await close();
|
|
1639
|
+
})
|
|
1640
|
+
);
|
|
1641
|
+
};
|
|
1642
|
+
var createInitialMessages = (args) => [
|
|
1643
|
+
...typeof args.system === "string" && args.system.length > 0 ? [{ role: "system", content: args.system }] : [],
|
|
1644
|
+
{ role: "user", content: String(args.input ?? "") }
|
|
1645
|
+
];
|
|
1646
|
+
var runReasonToolLoop = async (args) => {
|
|
1647
|
+
const { useReasonArgs, id, opId, traceSpan } = args;
|
|
1648
|
+
const ctx = getHookContext();
|
|
1649
|
+
const abortSignal = (0, import_core42.combineAbortSignals)(
|
|
1650
|
+
ctx.node.abortSignal,
|
|
1651
|
+
useReasonArgs.abortSignal,
|
|
1652
|
+
useReasonArgs.model.options?.abortSignal
|
|
1653
|
+
);
|
|
1654
|
+
const tools = useReasonArgs.tools ?? [];
|
|
1655
|
+
const toolByName = /* @__PURE__ */ new Map();
|
|
1656
|
+
let validationCompleted = false;
|
|
1657
|
+
try {
|
|
1658
|
+
for (const tool of tools) {
|
|
1659
|
+
if (toolByName.has(tool.name)) {
|
|
1660
|
+
throw new Error(
|
|
1661
|
+
`useReason received duplicate tool name "${tool.name}".`
|
|
1662
|
+
);
|
|
1663
|
+
}
|
|
1664
|
+
toolByName.set(tool.name, tool);
|
|
1665
|
+
}
|
|
1666
|
+
if (useReasonArgs.interrupt) {
|
|
1667
|
+
throw new Error(
|
|
1668
|
+
"useReason tools cannot be combined with useReason interrupt mode yet. Use toolExecution.approval for tool approval."
|
|
1669
|
+
);
|
|
1670
|
+
}
|
|
1671
|
+
validationCompleted = true;
|
|
1672
|
+
} finally {
|
|
1673
|
+
if (!validationCompleted) {
|
|
1674
|
+
await closeOwnedTools(tools);
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
const toolDefinitions = toToolDefinitions(tools);
|
|
1678
|
+
const maxSteps = Math.max(1, useReasonArgs.toolExecution?.maxSteps ?? 3);
|
|
1679
|
+
const messages = createInitialMessages({
|
|
1680
|
+
system: useReasonArgs.system,
|
|
1681
|
+
input: useReasonArgs.input
|
|
1682
|
+
});
|
|
1683
|
+
const steps = [];
|
|
1684
|
+
const allToolCalls = [];
|
|
1685
|
+
const allToolResults = [];
|
|
1686
|
+
let finalText = "";
|
|
1687
|
+
let finalRaw;
|
|
1688
|
+
let finalOutput;
|
|
1689
|
+
let aggregatedUsage;
|
|
1690
|
+
let finalFinishReason;
|
|
1691
|
+
let aggregatedProviderMetadata;
|
|
1692
|
+
let aggregatedWarnings;
|
|
1693
|
+
const emitToolEvent = (event, payload) => {
|
|
1694
|
+
ctx.node.emit(event, {
|
|
1695
|
+
...payload,
|
|
1696
|
+
node: ctx.node.graph.node,
|
|
1697
|
+
...id ? { id } : {},
|
|
1698
|
+
opId
|
|
1699
|
+
});
|
|
1700
|
+
};
|
|
1701
|
+
try {
|
|
1702
|
+
for (let stepIndex = 0; stepIndex < maxSteps; stepIndex += 1) {
|
|
1703
|
+
(0, import_core42.throwIfExecutionAborted)(abortSignal);
|
|
1704
|
+
traceSpan?.addEvent?.("useReason.tool-step.start", {
|
|
1705
|
+
stepIndex,
|
|
1706
|
+
toolCount: toolDefinitions.length
|
|
1707
|
+
});
|
|
1708
|
+
const step = await reasonEngine(
|
|
1709
|
+
{
|
|
1710
|
+
...useReasonArgs,
|
|
1711
|
+
tools: toolDefinitions,
|
|
1712
|
+
messages,
|
|
1713
|
+
emit: false,
|
|
1714
|
+
stream: false
|
|
1715
|
+
},
|
|
1716
|
+
{ ...id ? { id } : {}, opId }
|
|
1717
|
+
);
|
|
1718
|
+
finalText = step.text;
|
|
1719
|
+
finalRaw = step.raw;
|
|
1720
|
+
aggregatedUsage = mergeUsage(aggregatedUsage, step.usage);
|
|
1721
|
+
accumulateTokenUsage(step.usage);
|
|
1722
|
+
finalFinishReason = step.finishReason;
|
|
1723
|
+
aggregatedProviderMetadata = mergeProviderMetadata(
|
|
1724
|
+
aggregatedProviderMetadata,
|
|
1725
|
+
step.providerMetadata
|
|
1726
|
+
);
|
|
1727
|
+
aggregatedWarnings = mergeWarnings(aggregatedWarnings, step.warnings);
|
|
1728
|
+
const toolCalls = step.toolCalls ?? [];
|
|
1729
|
+
const toolResults = [];
|
|
1730
|
+
steps.push({
|
|
1731
|
+
stepIndex,
|
|
1732
|
+
text: step.text,
|
|
1733
|
+
toolCalls,
|
|
1734
|
+
toolResults,
|
|
1735
|
+
...step.usage ? { usage: step.usage } : {},
|
|
1736
|
+
...step.finishReason ? { finishReason: step.finishReason } : {},
|
|
1737
|
+
...step.providerMetadata ? { providerMetadata: step.providerMetadata } : {},
|
|
1738
|
+
...step.warnings ? { warnings: step.warnings } : {}
|
|
1739
|
+
});
|
|
1740
|
+
if (toolCalls.length === 0) {
|
|
1741
|
+
if ((useReasonArgs.emit ?? true) && step.text.length > 0) {
|
|
1742
|
+
emitToolEvent("text-start", {});
|
|
1743
|
+
emitToolEvent("text-delta", { delta: step.text });
|
|
1744
|
+
emitToolEvent("text-end", {});
|
|
1745
|
+
}
|
|
1746
|
+
break;
|
|
1747
|
+
}
|
|
1748
|
+
allToolCalls.push(...toolCalls);
|
|
1749
|
+
messages.push({
|
|
1750
|
+
role: "assistant",
|
|
1751
|
+
content: step.text,
|
|
1752
|
+
toolCalls
|
|
1753
|
+
});
|
|
1754
|
+
for (const toolCall of toolCalls) {
|
|
1755
|
+
(0, import_core42.throwIfExecutionAborted)(abortSignal);
|
|
1756
|
+
const tool = toolByName.get(toolCall.name);
|
|
1757
|
+
if (!tool) {
|
|
1758
|
+
throw new Error(
|
|
1759
|
+
`Model requested unknown tool "${toolCall.name}" from useReason.`
|
|
1760
|
+
);
|
|
1761
|
+
}
|
|
1762
|
+
const shouldEmitTool = resolveToolExecutionBoolean(
|
|
1763
|
+
useReasonArgs.toolExecution?.emit,
|
|
1764
|
+
tool.name,
|
|
1765
|
+
false
|
|
1766
|
+
);
|
|
1767
|
+
const shouldApproveTool = resolveToolExecutionBoolean(
|
|
1768
|
+
useReasonArgs.toolExecution?.approval,
|
|
1769
|
+
tool.name,
|
|
1770
|
+
false
|
|
1771
|
+
);
|
|
1772
|
+
traceSpan?.addEvent?.("useReason.tool-call.start", {
|
|
1773
|
+
stepIndex,
|
|
1774
|
+
tool: tool.name,
|
|
1775
|
+
toolCallId: toolCall.id
|
|
1776
|
+
});
|
|
1777
|
+
if (shouldEmitTool) {
|
|
1778
|
+
emitToolEvent("tool-call-start", {
|
|
1779
|
+
tool: tool.name,
|
|
1780
|
+
toolCallId: toolCall.id,
|
|
1781
|
+
input: toolCall.input
|
|
1782
|
+
});
|
|
1783
|
+
}
|
|
1784
|
+
if (shouldApproveTool) {
|
|
1785
|
+
const approval = await awaitInterruptInternal({
|
|
1786
|
+
id: `tool:${toolCall.id}`,
|
|
1787
|
+
request: {
|
|
1788
|
+
kind: "choice",
|
|
1789
|
+
question: `Approve ${tool.name}?`,
|
|
1790
|
+
options: [
|
|
1791
|
+
{ id: "approve", label: "Approve" },
|
|
1792
|
+
{ id: "deny", label: "Deny" }
|
|
1793
|
+
],
|
|
1794
|
+
meta: {
|
|
1795
|
+
tool: tool.name,
|
|
1796
|
+
toolCallId: toolCall.id,
|
|
1797
|
+
input: toolCall.input
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
});
|
|
1801
|
+
const approved = Array.isArray(approval) ? approval.includes("approve") : approval === "approve";
|
|
1802
|
+
if (!approved) {
|
|
1803
|
+
const denied = {
|
|
1804
|
+
toolCallId: toolCall.id,
|
|
1805
|
+
name: tool.name,
|
|
1806
|
+
content: "Tool call denied by user.",
|
|
1807
|
+
isError: true
|
|
1808
|
+
};
|
|
1809
|
+
toolResults.push(denied);
|
|
1810
|
+
allToolResults.push(denied);
|
|
1811
|
+
if (shouldEmitTool) {
|
|
1812
|
+
emitToolEvent("tool-call-result", {
|
|
1813
|
+
tool: tool.name,
|
|
1814
|
+
toolCallId: toolCall.id,
|
|
1815
|
+
content: denied.content,
|
|
1816
|
+
isError: true
|
|
1817
|
+
});
|
|
1818
|
+
}
|
|
1819
|
+
continue;
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
try {
|
|
1823
|
+
(0, import_core42.throwIfExecutionAborted)(abortSignal);
|
|
1824
|
+
ctx.node.consumeExecution?.("maxToolCalls");
|
|
1825
|
+
const rawResult = await tool.execute(toolCall.input, {
|
|
1826
|
+
toolCallId: toolCall.id,
|
|
1827
|
+
...abortSignal ? { abortSignal } : {}
|
|
1828
|
+
});
|
|
1829
|
+
(0, import_core42.throwIfExecutionAborted)(abortSignal);
|
|
1830
|
+
const result2 = normalizeToolResult(toolCall, rawResult);
|
|
1831
|
+
toolResults.push(result2);
|
|
1832
|
+
allToolResults.push(result2);
|
|
1833
|
+
if (shouldEmitTool) {
|
|
1834
|
+
emitToolEvent("tool-call-result", {
|
|
1835
|
+
tool: result2.name,
|
|
1836
|
+
toolCallId: result2.toolCallId,
|
|
1837
|
+
content: result2.content,
|
|
1838
|
+
...result2.structuredContent !== void 0 ? { structuredContent: result2.structuredContent } : {},
|
|
1839
|
+
...result2.isError !== void 0 ? { isError: result2.isError } : {}
|
|
1840
|
+
});
|
|
1841
|
+
}
|
|
1842
|
+
traceSpan?.addEvent?.("useReason.tool-call.complete", {
|
|
1843
|
+
stepIndex,
|
|
1844
|
+
tool: result2.name,
|
|
1845
|
+
toolCallId: result2.toolCallId,
|
|
1846
|
+
isError: Boolean(result2.isError)
|
|
1847
|
+
});
|
|
1848
|
+
} catch (error) {
|
|
1849
|
+
(0, import_core42.throwIfExecutionAborted)(abortSignal);
|
|
1850
|
+
if ((0, import_core42.isExecutionLimitReached)(error) || (0, import_core42.isExecutionCancelled)(error))
|
|
1851
|
+
throw error;
|
|
1852
|
+
const result2 = {
|
|
1853
|
+
toolCallId: toolCall.id,
|
|
1854
|
+
name: tool.name,
|
|
1855
|
+
content: toErrorMessage2(error),
|
|
1856
|
+
isError: true
|
|
1857
|
+
};
|
|
1858
|
+
toolResults.push(result2);
|
|
1859
|
+
allToolResults.push(result2);
|
|
1860
|
+
if (shouldEmitTool) {
|
|
1861
|
+
emitToolEvent("tool-call-error", {
|
|
1862
|
+
tool: tool.name,
|
|
1863
|
+
toolCallId: toolCall.id,
|
|
1864
|
+
message: result2.content
|
|
1865
|
+
});
|
|
1866
|
+
}
|
|
1867
|
+
traceSpan?.addEvent?.("useReason.tool-call.error", {
|
|
1868
|
+
stepIndex,
|
|
1869
|
+
tool: tool.name,
|
|
1870
|
+
toolCallId: toolCall.id,
|
|
1871
|
+
message: result2.content
|
|
1872
|
+
});
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
const currentStep = steps[steps.length - 1];
|
|
1876
|
+
if (currentStep) {
|
|
1877
|
+
steps[steps.length - 1] = {
|
|
1878
|
+
...currentStep,
|
|
1879
|
+
toolResults
|
|
1880
|
+
};
|
|
1881
|
+
}
|
|
1882
|
+
for (const result2 of toolResults) {
|
|
1883
|
+
messages.push({
|
|
1884
|
+
role: "tool",
|
|
1885
|
+
content: result2.content,
|
|
1886
|
+
toolCallId: result2.toolCallId,
|
|
1887
|
+
name: result2.name,
|
|
1888
|
+
...result2.structuredContent !== void 0 ? { structuredContent: result2.structuredContent } : {},
|
|
1889
|
+
...result2.isError !== void 0 ? { isError: result2.isError } : {},
|
|
1890
|
+
...result2.raw !== void 0 ? { raw: result2.raw } : {}
|
|
1891
|
+
});
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
if (steps.length >= maxSteps && steps.at(-1)?.toolCalls.length) {
|
|
1895
|
+
throw new Error(
|
|
1896
|
+
`useReason tool loop reached maxSteps (${maxSteps}) before producing a final response.`
|
|
1897
|
+
);
|
|
1898
|
+
}
|
|
1899
|
+
if (useReasonArgs.outputSchema) {
|
|
1900
|
+
finalOutput = parseReasonOutputWithSchema({
|
|
1901
|
+
text: finalText,
|
|
1902
|
+
schema: useReasonArgs.outputSchema,
|
|
1903
|
+
...finalFinishReason ? { finishReason: finalFinishReason } : {},
|
|
1904
|
+
label: "useReason output"
|
|
1905
|
+
});
|
|
1906
|
+
}
|
|
1907
|
+
if (finalOutput !== void 0) {
|
|
1908
|
+
emitReasonStructuredOutput({
|
|
1909
|
+
...id ? { id } : {},
|
|
1910
|
+
opId,
|
|
1911
|
+
output: finalOutput,
|
|
1912
|
+
structured: useReasonArgs.structured,
|
|
1913
|
+
emit: useReasonArgs.emit ?? true
|
|
1914
|
+
});
|
|
1915
|
+
}
|
|
1916
|
+
const result = {
|
|
1917
|
+
...id ? { id } : {},
|
|
1918
|
+
opId,
|
|
1919
|
+
text: finalText,
|
|
1920
|
+
...finalRaw !== void 0 ? { raw: finalRaw } : {},
|
|
1921
|
+
...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
|
|
1922
|
+
...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
|
|
1923
|
+
...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
|
|
1924
|
+
...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
|
|
1925
|
+
...finalOutput !== void 0 ? { output: finalOutput } : {},
|
|
1926
|
+
toolCalls: allToolCalls,
|
|
1927
|
+
toolResults: allToolResults,
|
|
1928
|
+
steps
|
|
1929
|
+
};
|
|
1930
|
+
traceSpan?.end?.({
|
|
1931
|
+
...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
|
|
1932
|
+
...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
|
|
1933
|
+
...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
|
|
1934
|
+
...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
|
|
1935
|
+
attributes: {
|
|
1936
|
+
textLength: finalText.length,
|
|
1937
|
+
toolCallCount: allToolCalls.length,
|
|
1938
|
+
toolResultCount: allToolResults.length,
|
|
1939
|
+
toolStepCount: steps.length
|
|
1940
|
+
},
|
|
1941
|
+
telemetry: {
|
|
1942
|
+
...useReasonArgs.telemetry ?? {},
|
|
1943
|
+
output: useReasonArgs.telemetry?.output ?? finalOutput ?? finalText
|
|
1944
|
+
}
|
|
1945
|
+
});
|
|
1946
|
+
return result;
|
|
1947
|
+
} catch (error) {
|
|
1948
|
+
traceSpan?.fail?.(error, {
|
|
1949
|
+
attributes: {
|
|
1950
|
+
toolStepCount: steps.length,
|
|
1951
|
+
toolCallCount: allToolCalls.length
|
|
1952
|
+
}
|
|
1953
|
+
});
|
|
1954
|
+
throw error;
|
|
1955
|
+
} finally {
|
|
1956
|
+
await closeOwnedTools(tools);
|
|
1957
|
+
}
|
|
1958
|
+
};
|
|
1959
|
+
var shouldStreamReasonStructured = (args) => (args.emit ?? true) && shouldStreamStructured(args.structured);
|
|
1960
|
+
var resolveEffectiveReasoningIncludeThoughts = (args) => args.reasoning?.includeThoughts ?? args.model.options?.reasoning?.includeThoughts ?? false;
|
|
1961
|
+
var resolveEffectiveResponseFormatType = (args) => args.responseFormat?.type ?? args.model.options?.responseFormat?.type;
|
|
1962
|
+
var assertReasoningThoughtsCompatibility = (args) => {
|
|
1963
|
+
if (!resolveEffectiveReasoningIncludeThoughts(args)) return;
|
|
1964
|
+
const usesStructuredOutput = Boolean(args.outputSchema) || Boolean(args.interrupt) || Boolean(args.structured) || resolveEffectiveResponseFormatType(args) === "json";
|
|
1965
|
+
if (!usesStructuredOutput) return;
|
|
1966
|
+
throw new Error(
|
|
1967
|
+
"useReason does not support reasoning.includeThoughts with structured output, interrupt mode, or JSON responseFormat. Disable includeThoughts for this call."
|
|
1968
|
+
);
|
|
1969
|
+
};
|
|
1970
|
+
async function useReason(args) {
|
|
1971
|
+
assertReasoningThoughtsCompatibility(args);
|
|
1972
|
+
const ctx = getHookContext();
|
|
1973
|
+
const abortSignal = (0, import_core52.combineAbortSignals)(
|
|
1974
|
+
ctx.node.abortSignal,
|
|
1975
|
+
args.abortSignal,
|
|
1976
|
+
args.model.options?.abortSignal
|
|
1977
|
+
);
|
|
1978
|
+
(0, import_core52.throwIfExecutionAborted)(abortSignal);
|
|
1979
|
+
args = { ...args, abortSignal };
|
|
1980
|
+
const id = typeof args.id === "string" && args.id.length > 0 ? args.id : void 0;
|
|
1981
|
+
const opId = createRuntimeId2();
|
|
1982
|
+
const checkpointKey = resolveReasonCheckpointKey({
|
|
1983
|
+
...id ? { id } : {},
|
|
1984
|
+
autoIndex: ctx.reasonCallIndex++
|
|
1985
|
+
});
|
|
1986
|
+
const existingCompleted = readReasonCompletedCheckpoint(
|
|
1987
|
+
ctx.currentNodeState.byKey[checkpointKey]
|
|
1988
|
+
);
|
|
1989
|
+
if (existingCompleted) {
|
|
1990
|
+
return existingCompleted.result;
|
|
1991
|
+
}
|
|
1992
|
+
const existingCheckpoint = readReasonCheckpoint(
|
|
1993
|
+
ctx.currentNodeState.byKey[checkpointKey]
|
|
1994
|
+
);
|
|
1995
|
+
const reasonMeta = typeof id === "string" ? { id, opId } : { opId };
|
|
1996
|
+
const traceSpan = getReasonTraceAdapter()?.startSpan({
|
|
1997
|
+
name: "useReason",
|
|
1998
|
+
attributes: {
|
|
1999
|
+
...id ? { id } : {},
|
|
2000
|
+
opId,
|
|
2001
|
+
nodeId: ctx.node.graph.node,
|
|
2002
|
+
providerId: args.model.provider.id,
|
|
2003
|
+
modelId: args.model.modelId,
|
|
2004
|
+
stream: args.stream ?? args.model.options?.streaming ?? true,
|
|
2005
|
+
emit: args.emit ?? true,
|
|
2006
|
+
...args.telemetry?.operation ? { operation: args.telemetry.operation } : {},
|
|
2007
|
+
...args.telemetry?.prompt?.name ? { promptName: args.telemetry.prompt.name } : {},
|
|
2008
|
+
...args.telemetry?.prompt?.version !== void 0 ? { promptVersion: args.telemetry.prompt.version } : {},
|
|
2009
|
+
...args.telemetry?.prompt?.type ? { promptType: args.telemetry.prompt.type } : {},
|
|
2010
|
+
hasOutputSchema: Boolean(args.outputSchema),
|
|
2011
|
+
hasInterrupt: Boolean(args.interrupt),
|
|
2012
|
+
hasStructured: Boolean(args.structured),
|
|
2013
|
+
hasTools: Boolean(args.tools?.length)
|
|
2014
|
+
},
|
|
2015
|
+
telemetry: {
|
|
2016
|
+
...args.telemetry ?? {},
|
|
2017
|
+
input: args.telemetry?.input ?? args.input
|
|
2018
|
+
}
|
|
2019
|
+
});
|
|
2020
|
+
if (args.tools?.length) {
|
|
2021
|
+
return runReasonToolLoop({
|
|
2022
|
+
useReasonArgs: args,
|
|
2023
|
+
...id ? { id } : {},
|
|
2024
|
+
opId,
|
|
2025
|
+
...traceSpan ? { traceSpan } : {}
|
|
2026
|
+
});
|
|
2027
|
+
}
|
|
2028
|
+
const suppressTextStream = Boolean(args.outputSchema || args.interrupt);
|
|
2029
|
+
const setFieldPaths = resolveSetFieldPaths(args.structured);
|
|
2030
|
+
const appendFieldPaths = resolveAppendFieldPaths(args.structured);
|
|
2031
|
+
const textDeltaFieldPaths = resolveTextDeltaFieldPaths(args.structured);
|
|
2032
|
+
const useStructuredIncrementalStreaming = Boolean(
|
|
2033
|
+
args.outputSchema && !args.interrupt && (setFieldPaths.length > 0 || appendFieldPaths.length > 0 || textDeltaFieldPaths.length > 0) && args.stream !== false && shouldStreamReasonStructured(args)
|
|
2034
|
+
);
|
|
2035
|
+
let firstText = "";
|
|
2036
|
+
let firstRaw;
|
|
2037
|
+
let firstOutput;
|
|
2038
|
+
let firstInterruptRequest;
|
|
2039
|
+
let firstInterruptRequired = false;
|
|
2040
|
+
let finalText = "";
|
|
2041
|
+
let finalRaw;
|
|
2042
|
+
let finalOutput;
|
|
2043
|
+
let aggregatedUsage;
|
|
2044
|
+
let finalFinishReason;
|
|
2045
|
+
let aggregatedProviderMetadata;
|
|
2046
|
+
let aggregatedWarnings;
|
|
2047
|
+
const emittedSetValues = /* @__PURE__ */ new Map();
|
|
2048
|
+
const emittedAppendCounts = /* @__PURE__ */ new Map();
|
|
2049
|
+
const emittedTextValues = /* @__PURE__ */ new Map();
|
|
2050
|
+
if (existingCheckpoint) {
|
|
2051
|
+
firstText = existingCheckpoint.firstText;
|
|
2052
|
+
firstRaw = existingCheckpoint.firstRaw;
|
|
2053
|
+
firstOutput = existingCheckpoint.firstOutput;
|
|
2054
|
+
finalText = firstText;
|
|
2055
|
+
finalRaw = firstRaw;
|
|
2056
|
+
finalOutput = firstOutput;
|
|
2057
|
+
aggregatedUsage = existingCheckpoint.firstUsage;
|
|
2058
|
+
finalFinishReason = existingCheckpoint.firstFinishReason;
|
|
2059
|
+
aggregatedProviderMetadata = existingCheckpoint.firstProviderMetadata;
|
|
2060
|
+
aggregatedWarnings = existingCheckpoint.firstWarnings;
|
|
2061
|
+
traceSpan?.addEvent?.("useReason.resume", {
|
|
2062
|
+
checkpointKey
|
|
2063
|
+
});
|
|
2064
|
+
} else {
|
|
2065
|
+
const first = await reasonEngine(
|
|
2066
|
+
{
|
|
2067
|
+
...args,
|
|
2068
|
+
emit: suppressTextStream ? false : args.emit,
|
|
2069
|
+
stream: useStructuredIncrementalStreaming ? true : suppressTextStream ? false : args.stream,
|
|
2070
|
+
...useStructuredIncrementalStreaming ? {
|
|
2071
|
+
onTextChunk: (delta) => {
|
|
2072
|
+
firstText += delta;
|
|
2073
|
+
finalText = firstText;
|
|
2074
|
+
for (const fieldPath of setFieldPaths) {
|
|
2075
|
+
const values = extractCompletedFieldValues({
|
|
2076
|
+
text: firstText,
|
|
2077
|
+
path: fieldPath
|
|
2078
|
+
});
|
|
2079
|
+
for (const { path, value } of values) {
|
|
2080
|
+
const nextSerialized = JSON.stringify(value);
|
|
2081
|
+
if (emittedSetValues.get(path) === nextSerialized) continue;
|
|
2082
|
+
emittedSetValues.set(path, nextSerialized);
|
|
2083
|
+
emitStructuredData({
|
|
2084
|
+
kind: "set",
|
|
2085
|
+
path,
|
|
2086
|
+
value,
|
|
2087
|
+
dataType: args.structured?.dataType ?? "reason-output",
|
|
2088
|
+
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
2089
|
+
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
2090
|
+
...id ? { id } : {},
|
|
2091
|
+
streamId: opId
|
|
2092
|
+
});
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
for (const fieldPath of appendFieldPaths) {
|
|
2096
|
+
const groups = extractCompletedArrayItemGroups({
|
|
2097
|
+
text: firstText,
|
|
2098
|
+
path: fieldPath
|
|
2099
|
+
});
|
|
2100
|
+
for (const { path, items } of groups) {
|
|
2101
|
+
const emittedAppendCount = emittedAppendCounts.get(path) ?? 0;
|
|
2102
|
+
if (items.length <= emittedAppendCount) continue;
|
|
2103
|
+
const nextItems = items.slice(emittedAppendCount);
|
|
2104
|
+
emittedAppendCounts.set(path, items.length);
|
|
2105
|
+
emitStructuredData({
|
|
2106
|
+
kind: "append",
|
|
2107
|
+
path,
|
|
2108
|
+
items: nextItems,
|
|
2109
|
+
dataType: args.structured?.dataType ?? "reason-output",
|
|
2110
|
+
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
2111
|
+
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
2112
|
+
...id ? { id } : {},
|
|
2113
|
+
streamId: opId
|
|
2114
|
+
});
|
|
2115
|
+
}
|
|
2116
|
+
}
|
|
2117
|
+
for (const fieldPath of textDeltaFieldPaths) {
|
|
2118
|
+
const values = extractStreamingStringValues({
|
|
2119
|
+
text: firstText,
|
|
2120
|
+
path: fieldPath
|
|
2121
|
+
});
|
|
2122
|
+
for (const { path, value } of values) {
|
|
2123
|
+
const emittedTextValue = emittedTextValues.get(path) ?? "";
|
|
2124
|
+
if (value.length <= emittedTextValue.length || !value.startsWith(emittedTextValue)) {
|
|
2125
|
+
continue;
|
|
2126
|
+
}
|
|
2127
|
+
const nextDelta = value.slice(emittedTextValue.length);
|
|
2128
|
+
emittedTextValues.set(path, value);
|
|
2129
|
+
if (nextDelta.length === 0) continue;
|
|
2130
|
+
emitStructuredData({
|
|
2131
|
+
kind: "text-delta",
|
|
2132
|
+
path,
|
|
2133
|
+
delta: nextDelta,
|
|
2134
|
+
dataType: args.structured?.dataType ?? "reason-output",
|
|
2135
|
+
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
2136
|
+
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
2137
|
+
...id ? { id } : {},
|
|
2138
|
+
streamId: opId
|
|
2139
|
+
});
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
} : {}
|
|
2144
|
+
},
|
|
2145
|
+
reasonMeta,
|
|
2146
|
+
args.interrupt ? defaultInterruptFirstPassInput({
|
|
2147
|
+
input: args.input,
|
|
2148
|
+
requestSchema: args.interrupt.requestSchema,
|
|
2149
|
+
mode: args.interrupt.mode ?? "required",
|
|
2150
|
+
...args.outputSchema ? { outputSchema: args.outputSchema } : {}
|
|
2151
|
+
}) : args.outputSchema ? withOutputGuardrails(
|
|
2152
|
+
withStructuredStreamHints(args.input, {
|
|
2153
|
+
...setFieldPaths.length > 0 ? { setFieldPaths } : {},
|
|
2154
|
+
...appendFieldPaths.length > 0 ? { appendFieldPaths } : {},
|
|
2155
|
+
...textDeltaFieldPaths.length > 0 ? { textDeltaFieldPaths } : {}
|
|
2156
|
+
}),
|
|
2157
|
+
args.outputSchema
|
|
2158
|
+
) : void 0
|
|
2159
|
+
);
|
|
2160
|
+
firstRaw = first.raw;
|
|
2161
|
+
finalRaw = first.raw;
|
|
2162
|
+
accumulateTokenUsage(first.usage);
|
|
2163
|
+
aggregatedUsage = mergeUsage(aggregatedUsage, first.usage);
|
|
2164
|
+
finalFinishReason = first.finishReason;
|
|
2165
|
+
aggregatedProviderMetadata = mergeProviderMetadata(
|
|
2166
|
+
aggregatedProviderMetadata,
|
|
2167
|
+
first.providerMetadata
|
|
2168
|
+
);
|
|
2169
|
+
aggregatedWarnings = mergeWarnings(aggregatedWarnings, first.warnings);
|
|
2170
|
+
traceSpan?.addEvent?.("useReason.first-pass.complete", {
|
|
2171
|
+
textLength: first.text.length,
|
|
2172
|
+
hasInterrupt: Boolean(args.interrupt)
|
|
2173
|
+
});
|
|
2174
|
+
if (args.interrupt) {
|
|
2175
|
+
const firstPass = parseInterruptFirstPassResult({
|
|
2176
|
+
text: first.text,
|
|
2177
|
+
requestSchema: args.interrupt.requestSchema,
|
|
2178
|
+
...first.finishReason ? { finishReason: first.finishReason } : {},
|
|
2179
|
+
...args.outputSchema ? { outputSchema: args.outputSchema } : {},
|
|
2180
|
+
mode: args.interrupt.mode ?? "required"
|
|
2181
|
+
});
|
|
2182
|
+
firstText = firstPass.draftText;
|
|
2183
|
+
finalText = firstPass.draftText;
|
|
2184
|
+
firstInterruptRequired = firstPass.interruptRequired;
|
|
2185
|
+
firstInterruptRequest = firstPass.request;
|
|
2186
|
+
firstOutput = firstPass.output;
|
|
2187
|
+
finalOutput = firstPass.output;
|
|
2188
|
+
} else {
|
|
2189
|
+
firstText = first.text;
|
|
2190
|
+
finalText = first.text;
|
|
2191
|
+
if (args.outputSchema) {
|
|
2192
|
+
firstOutput = parseReasonOutputWithSchema({
|
|
2193
|
+
text: first.text,
|
|
2194
|
+
schema: args.outputSchema,
|
|
2195
|
+
...first.finishReason ? { finishReason: first.finishReason } : {},
|
|
2196
|
+
label: "useReason output"
|
|
2197
|
+
});
|
|
2198
|
+
}
|
|
2199
|
+
finalOutput = firstOutput;
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
let interruptResponse;
|
|
2203
|
+
if (args.interrupt && (existingCheckpoint || firstInterruptRequired)) {
|
|
2204
|
+
const requestSchema = args.interrupt.requestSchema;
|
|
2205
|
+
const interruptRequest = existingCheckpoint ? parseWithSchema(
|
|
2206
|
+
requestSchema,
|
|
2207
|
+
existingCheckpoint.request,
|
|
2208
|
+
"useReason interrupt.request"
|
|
2209
|
+
) : firstInterruptRequest;
|
|
2210
|
+
if (!interruptRequest) {
|
|
2211
|
+
throw new Error(
|
|
2212
|
+
"useReason interrupt request is missing; first pass did not produce a valid interrupt payload."
|
|
2213
|
+
);
|
|
2214
|
+
}
|
|
2215
|
+
if (!existingCheckpoint) {
|
|
2216
|
+
ctx.currentNodeState.byKey[checkpointKey] = {
|
|
2217
|
+
status: "awaiting_interrupt",
|
|
2218
|
+
request: interruptRequest,
|
|
2219
|
+
firstText,
|
|
2220
|
+
...firstRaw !== void 0 ? { firstRaw } : {},
|
|
2221
|
+
...aggregatedUsage !== void 0 ? { firstUsage: aggregatedUsage } : {},
|
|
2222
|
+
...finalFinishReason !== void 0 ? { firstFinishReason: finalFinishReason } : {},
|
|
2223
|
+
...aggregatedProviderMetadata !== void 0 ? { firstProviderMetadata: aggregatedProviderMetadata } : {},
|
|
2224
|
+
...aggregatedWarnings !== void 0 ? { firstWarnings: aggregatedWarnings } : {},
|
|
2225
|
+
...firstOutput !== void 0 ? { firstOutput } : {}
|
|
2226
|
+
};
|
|
2227
|
+
ctx.stateDirty = true;
|
|
2228
|
+
}
|
|
2229
|
+
const resumeStatePatch = resolveHookStatePatch({
|
|
2230
|
+
nodeId: ctx.node.graph.node,
|
|
2231
|
+
currentNodeState: ctx.currentNodeState,
|
|
2232
|
+
workflowState: ctx.workflowState
|
|
2233
|
+
});
|
|
2234
|
+
interruptResponse = await awaitInterruptInternal({
|
|
2235
|
+
request: interruptRequest,
|
|
2236
|
+
requestSchema,
|
|
2237
|
+
responseSchema: args.interrupt.responseSchema,
|
|
2238
|
+
...args.interrupt.schemaId ? { schemaId: args.interrupt.schemaId } : {},
|
|
2239
|
+
...args.interrupt.schemaVersion ? { schemaVersion: args.interrupt.schemaVersion } : {},
|
|
2240
|
+
...id ? { id } : {},
|
|
2241
|
+
meta: {
|
|
2242
|
+
__kortyxResumeStatePatch: resumeStatePatch
|
|
2243
|
+
}
|
|
2244
|
+
});
|
|
2245
|
+
traceSpan?.addEvent?.("useReason.interrupt.resolved", {
|
|
2246
|
+
checkpointKey
|
|
2247
|
+
});
|
|
2248
|
+
if (Object.hasOwn(ctx.currentNodeState.byKey, checkpointKey)) {
|
|
2249
|
+
delete ctx.currentNodeState.byKey[checkpointKey];
|
|
2250
|
+
ctx.stateDirty = true;
|
|
2251
|
+
}
|
|
2252
|
+
const continuationInput = defaultContinuationInput({
|
|
2253
|
+
input: args.input,
|
|
2254
|
+
draftText: firstText,
|
|
2255
|
+
...firstOutput !== void 0 ? { draftOutput: firstOutput } : {},
|
|
2256
|
+
request: interruptRequest,
|
|
2257
|
+
response: interruptResponse
|
|
2258
|
+
});
|
|
2259
|
+
const second = await reasonEngine(
|
|
2260
|
+
{
|
|
2261
|
+
...args,
|
|
2262
|
+
emit: suppressTextStream ? false : args.emit,
|
|
2263
|
+
stream: suppressTextStream ? false : args.stream
|
|
2264
|
+
},
|
|
2265
|
+
reasonMeta,
|
|
2266
|
+
args.outputSchema ? withOutputGuardrails(
|
|
2267
|
+
continuationInput,
|
|
2268
|
+
args.outputSchema
|
|
2269
|
+
) : continuationInput
|
|
2270
|
+
);
|
|
2271
|
+
finalText = second.text;
|
|
2272
|
+
finalRaw = second.raw;
|
|
2273
|
+
accumulateTokenUsage(second.usage);
|
|
2274
|
+
aggregatedUsage = mergeUsage(aggregatedUsage, second.usage);
|
|
2275
|
+
finalFinishReason = second.finishReason;
|
|
2276
|
+
aggregatedProviderMetadata = mergeProviderMetadata(
|
|
2277
|
+
aggregatedProviderMetadata,
|
|
2278
|
+
second.providerMetadata
|
|
2279
|
+
);
|
|
2280
|
+
aggregatedWarnings = mergeWarnings(aggregatedWarnings, second.warnings);
|
|
2281
|
+
traceSpan?.addEvent?.("useReason.continuation.complete", {
|
|
2282
|
+
textLength: second.text.length
|
|
2283
|
+
});
|
|
2284
|
+
if (args.outputSchema) {
|
|
2285
|
+
finalOutput = parseReasonOutputWithSchema({
|
|
2286
|
+
text: finalText,
|
|
2287
|
+
schema: args.outputSchema,
|
|
2288
|
+
...second.finishReason ? { finishReason: second.finishReason } : {},
|
|
2289
|
+
label: "useReason output"
|
|
2290
|
+
});
|
|
2291
|
+
}
|
|
2292
|
+
}
|
|
2293
|
+
if (finalOutput !== void 0) {
|
|
2294
|
+
emitReasonStructuredOutput({
|
|
2295
|
+
...id ? { id } : {},
|
|
2296
|
+
opId,
|
|
2297
|
+
output: finalOutput,
|
|
2298
|
+
structured: args.structured,
|
|
2299
|
+
emit: args.emit ?? true
|
|
2300
|
+
});
|
|
2301
|
+
}
|
|
2302
|
+
const result = {
|
|
2303
|
+
...id ? { id } : {},
|
|
2304
|
+
opId,
|
|
2305
|
+
text: finalText,
|
|
2306
|
+
...finalRaw !== void 0 ? { raw: finalRaw } : {},
|
|
2307
|
+
...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
|
|
2308
|
+
...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
|
|
2309
|
+
...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
|
|
2310
|
+
...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
|
|
2311
|
+
...finalOutput !== void 0 ? { output: finalOutput } : {},
|
|
2312
|
+
...interruptResponse !== void 0 ? { interruptResponse } : {}
|
|
2313
|
+
};
|
|
2314
|
+
if (args.interrupt) {
|
|
2315
|
+
ctx.currentNodeState.byKey[checkpointKey] = {
|
|
2316
|
+
status: "completed",
|
|
2317
|
+
result
|
|
2318
|
+
};
|
|
2319
|
+
ctx.stateDirty = true;
|
|
2320
|
+
}
|
|
2321
|
+
traceSpan?.end?.({
|
|
2322
|
+
...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
|
|
2323
|
+
...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
|
|
2324
|
+
...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
|
|
2325
|
+
...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
|
|
2326
|
+
attributes: {
|
|
2327
|
+
textLength: finalText.length,
|
|
2328
|
+
resumedFromCheckpoint: Boolean(existingCheckpoint),
|
|
2329
|
+
interrupted: Boolean(interruptResponse !== void 0)
|
|
2330
|
+
},
|
|
2331
|
+
telemetry: {
|
|
2332
|
+
...args.telemetry ?? {},
|
|
2333
|
+
output: args.telemetry?.output ?? finalOutput ?? finalText
|
|
2334
|
+
}
|
|
2335
|
+
});
|
|
2336
|
+
return result;
|
|
2337
|
+
}
|
|
2338
|
+
function useStructuredData(args) {
|
|
2339
|
+
emitStructuredData(args);
|
|
2340
|
+
}
|
|
2341
|
+
function useReason2(args) {
|
|
2342
|
+
return useReason(args);
|
|
2343
|
+
}
|
|
2344
|
+
function useInterrupt(args) {
|
|
2345
|
+
return awaitInterruptInternal(args);
|
|
2346
|
+
}
|
|
2347
|
+
function useAbortSignal() {
|
|
2348
|
+
return getHookContext().node.abortSignal;
|
|
2349
|
+
}
|
|
2350
|
+
function useRuntimeContext() {
|
|
2351
|
+
const ctx = getHookContext();
|
|
2352
|
+
const config = ctx.state.config;
|
|
2353
|
+
const context = config && typeof config === "object" && "context" in config ? config.context : void 0;
|
|
2354
|
+
return context && typeof context === "object" && !Array.isArray(context) ? context : {};
|
|
2355
|
+
}
|
|
2356
|
+
function useNodeState(initialValue) {
|
|
2357
|
+
const ctx = getHookContext();
|
|
2358
|
+
const nodeState = ctx.currentNodeState;
|
|
2359
|
+
const index = ctx.nodeStateIndex++;
|
|
2360
|
+
if (index >= nodeState.byIndex.length) {
|
|
2361
|
+
nodeState.byIndex[index] = initialValue;
|
|
2362
|
+
ctx.stateDirty = true;
|
|
2363
|
+
}
|
|
2364
|
+
const getValue = () => nodeState.byIndex[index];
|
|
2365
|
+
const setValue = (next) => {
|
|
2366
|
+
const prev = getValue();
|
|
2367
|
+
const resolved = typeof next === "function" ? next(prev) : next;
|
|
2368
|
+
nodeState.byIndex[index] = resolved;
|
|
2369
|
+
ctx.stateDirty = true;
|
|
2370
|
+
};
|
|
2371
|
+
return [getValue(), setValue];
|
|
2372
|
+
}
|
|
2373
|
+
function useWorkflowState(key, ...rest) {
|
|
2374
|
+
const ctx = getHookContext();
|
|
2375
|
+
const workflowState = ctx.workflowState;
|
|
2376
|
+
const hasInitial = rest.length > 0;
|
|
2377
|
+
const initialValue = rest[0];
|
|
2378
|
+
if (!Object.hasOwn(workflowState, key) && hasInitial) {
|
|
2379
|
+
workflowState[key] = initialValue;
|
|
2380
|
+
ctx.stateDirty = true;
|
|
2381
|
+
}
|
|
2382
|
+
const getValue = () => workflowState[key];
|
|
2383
|
+
const setValue = (next) => {
|
|
2384
|
+
const prev = getValue();
|
|
2385
|
+
const resolved = typeof next === "function" ? next(prev) : next;
|
|
2386
|
+
workflowState[key] = resolved;
|
|
2387
|
+
ctx.stateDirty = true;
|
|
2388
|
+
};
|
|
2389
|
+
return [getValue(), setValue];
|
|
2390
|
+
}
|
|
2391
|
+
var import_node_crypto5 = require("crypto");
|
|
2392
|
+
var import_core6 = require("@kortyx/core");
|
|
2393
|
+
var import_node_crypto42 = require("crypto");
|
|
2394
|
+
function emitWorkflowCall2(telemetry, type, payload) {
|
|
2395
|
+
const base = telemetry?.correlation;
|
|
2396
|
+
if (!telemetry?.reporter || !telemetry.environment || !telemetry.service || !base?.runId || !base.workflowId)
|
|
2397
|
+
return;
|
|
2398
|
+
try {
|
|
2399
|
+
const active = telemetry.trace?.getActiveContext?.();
|
|
2400
|
+
void Promise.resolve(
|
|
2401
|
+
telemetry.reporter.emit([
|
|
2402
|
+
{
|
|
2403
|
+
schemaVersion: 1,
|
|
2404
|
+
eventId: (0, import_node_crypto42.randomUUID)(),
|
|
2405
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2406
|
+
environment: telemetry.environment,
|
|
2407
|
+
service: telemetry.service,
|
|
2408
|
+
correlation: {
|
|
2409
|
+
...base,
|
|
2410
|
+
runId: base.runId,
|
|
2411
|
+
workflowId: base.workflowId,
|
|
2412
|
+
...active
|
|
2413
|
+
},
|
|
2414
|
+
type,
|
|
2415
|
+
payload
|
|
2416
|
+
}
|
|
2417
|
+
])
|
|
2418
|
+
).catch(() => {
|
|
2419
|
+
});
|
|
2420
|
+
} catch {
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
function workflowCallContent(telemetry, side, value) {
|
|
2424
|
+
const policy = telemetry?.captureContent;
|
|
2425
|
+
if (!(policy === true || policy && typeof policy === "object" && policy[side]))
|
|
2426
|
+
return {};
|
|
2427
|
+
const serialized = JSON.stringify(value);
|
|
2428
|
+
return serialized && Buffer.byteLength(serialized, "utf8") <= 16384 ? { [side]: JSON.parse(serialized) } : { [`${side}Omitted`]: "Content exceeds 16 KiB" };
|
|
2429
|
+
}
|
|
2430
|
+
var WorkflowCallError = class extends Error {
|
|
2431
|
+
constructor(workflow, message) {
|
|
2432
|
+
super(`Workflow '${workflow}': ${message}`);
|
|
2433
|
+
this.workflow = workflow;
|
|
2434
|
+
this.name = "WorkflowCallError";
|
|
2435
|
+
}
|
|
2436
|
+
workflow;
|
|
2437
|
+
};
|
|
2438
|
+
function workflowCallFingerprint(value) {
|
|
2439
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2440
|
+
const canonical = (v) => {
|
|
2441
|
+
if (v === null || typeof v === "string" || typeof v === "boolean") return v;
|
|
2442
|
+
if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
2443
|
+
if (typeof v !== "object" || !v || seen.has(v)) {
|
|
2444
|
+
throw new Error("Workflow input and output must be JSON serializable.");
|
|
2445
|
+
}
|
|
2446
|
+
if (!Array.isArray(v) && Object.getPrototypeOf(v) !== Object.prototype) {
|
|
2447
|
+
throw new Error(
|
|
2448
|
+
"Workflow input and output must contain plain JSON objects."
|
|
2449
|
+
);
|
|
2450
|
+
}
|
|
2451
|
+
seen.add(v);
|
|
2452
|
+
const result = Array.isArray(v) ? v.map(canonical) : Object.fromEntries(
|
|
2453
|
+
Object.keys(v).sort().map((key) => [
|
|
2454
|
+
key,
|
|
2455
|
+
canonical(v[key])
|
|
2456
|
+
])
|
|
2457
|
+
);
|
|
2458
|
+
seen.delete(v);
|
|
2459
|
+
return result;
|
|
2460
|
+
};
|
|
2461
|
+
return JSON.stringify(canonical(value));
|
|
2462
|
+
}
|
|
2463
|
+
function createWorkflowHooks(workflows) {
|
|
2464
|
+
return {
|
|
2465
|
+
useWorkflow: (args) => {
|
|
2466
|
+
const workflow = workflows[args.workflow];
|
|
2467
|
+
if (!workflow || workflow.id !== args.workflow)
|
|
2468
|
+
throw new Error("Workflow registry keys must match definition ids.");
|
|
2469
|
+
return useWorkflow({ ...args, workflow });
|
|
2470
|
+
}
|
|
2471
|
+
};
|
|
2472
|
+
}
|
|
2473
|
+
async function useWorkflow(args) {
|
|
2474
|
+
const ctx = getHookContext();
|
|
2475
|
+
(0, import_core6.throwIfExecutionAborted)(ctx.node.abortSignal);
|
|
2476
|
+
const service = ctx.node.callWorkflow;
|
|
2477
|
+
if (!service)
|
|
2478
|
+
throw new Error("useWorkflow requires an agent workflow registry.");
|
|
2479
|
+
if (!args.id.trim())
|
|
2480
|
+
throw new Error("useWorkflow requires a stable, nonempty id.");
|
|
2481
|
+
if (ctx.workflowCallActive)
|
|
2482
|
+
throw new Error(
|
|
2483
|
+
"Concurrent workflow calls are not supported; await each call."
|
|
2484
|
+
);
|
|
2485
|
+
if (ctx.workflowCallIds.has(args.id))
|
|
2486
|
+
throw new Error(
|
|
2487
|
+
`Duplicate workflow call id '${args.id}' in one node activation.`
|
|
2488
|
+
);
|
|
2489
|
+
if (ctx.workflowCallIds.size >= 64)
|
|
2490
|
+
throw new Error(
|
|
2491
|
+
"A node may call at most 64 child workflows per activation."
|
|
2492
|
+
);
|
|
2493
|
+
ctx.workflowCallIds.add(args.id);
|
|
2494
|
+
const definition = typeof args.workflow === "string" ? void 0 : args.workflow;
|
|
2495
|
+
const workflow = definition?.id ?? args.workflow;
|
|
2496
|
+
const input = args.input;
|
|
2497
|
+
const fingerprint = workflowCallFingerprint({
|
|
2498
|
+
workflow,
|
|
2499
|
+
input,
|
|
2500
|
+
version: definition?.version ?? null
|
|
2501
|
+
});
|
|
2502
|
+
const key = `__useWorkflow:${args.id}`;
|
|
2503
|
+
let record = ctx.currentNodeState.byKey[key];
|
|
2504
|
+
if (record && record.fingerprint !== fingerprint)
|
|
2505
|
+
throw new WorkflowCallError(
|
|
2506
|
+
workflow,
|
|
2507
|
+
"call input or target changed during replay"
|
|
2508
|
+
);
|
|
2509
|
+
if (!record) ctx.node.consumeExecution?.("maxChildInvocations");
|
|
2510
|
+
record ??= {
|
|
2511
|
+
fingerprint,
|
|
2512
|
+
invocationId: (0, import_node_crypto5.randomUUID)(),
|
|
2513
|
+
status: "running",
|
|
2514
|
+
interrupts: []
|
|
2515
|
+
};
|
|
2516
|
+
ctx.currentNodeState.byKey[key] = record;
|
|
2517
|
+
ctx.stateDirty = true;
|
|
2518
|
+
ctx.workflowCallActive = true;
|
|
2519
|
+
const telemetry = ctx.node.workflowCallTelemetry;
|
|
2520
|
+
const activationKey = "__useWorkflowActivation";
|
|
2521
|
+
ctx.currentNodeState.byKey[activationKey] ??= (0, import_node_crypto5.randomUUID)();
|
|
2522
|
+
const callerNodeExecutionId = ctx.currentNodeState.byKey[activationKey];
|
|
2523
|
+
const metadata2 = {
|
|
2524
|
+
invocationId: record.invocationId,
|
|
2525
|
+
parentInvocationId: telemetry?.correlation?.invocationId ?? null,
|
|
2526
|
+
branchId: telemetry?.correlation?.branchId ?? telemetry?.correlation?.runId ?? "unknown",
|
|
2527
|
+
callId: args.id,
|
|
2528
|
+
callerNodeId: ctx.node.graph.node,
|
|
2529
|
+
callerNodeExecutionId,
|
|
2530
|
+
sourceWorkflowId: ctx.node.graph.name,
|
|
2531
|
+
targetWorkflowId: workflow,
|
|
2532
|
+
targetVersion: definition?.version ?? null
|
|
2533
|
+
};
|
|
2534
|
+
record.telemetry = metadata2;
|
|
2535
|
+
const report = (status, content = {}) => {
|
|
2536
|
+
record.sequence = (record.sequence ?? 0) + 1;
|
|
2537
|
+
emitWorkflowCall2(telemetry, `workflow.call.${status}`, {
|
|
2538
|
+
...metadata2,
|
|
2539
|
+
sequence: record.sequence,
|
|
2540
|
+
...content
|
|
2541
|
+
});
|
|
2542
|
+
};
|
|
2543
|
+
const recordUsage = (usage) => {
|
|
2544
|
+
if (!usage) return;
|
|
2545
|
+
accumulateTokenUsage({
|
|
2546
|
+
input: usage.input - (record.usage?.input ?? 0),
|
|
2547
|
+
output: usage.output - (record.usage?.output ?? 0),
|
|
2548
|
+
total: usage.total - (record.usage?.total ?? 0)
|
|
2549
|
+
});
|
|
2550
|
+
record.usage = usage;
|
|
2551
|
+
};
|
|
2552
|
+
const cached = record.status === "completed";
|
|
2553
|
+
try {
|
|
2554
|
+
for (const interruption of record.interrupts) {
|
|
2555
|
+
interruption.response = await awaitInterruptInternal({
|
|
2556
|
+
request: interruption.request,
|
|
2557
|
+
...interruption.request.meta ? { meta: interruption.request.meta } : {}
|
|
2558
|
+
});
|
|
2559
|
+
}
|
|
2560
|
+
if (record.status === "failed")
|
|
2561
|
+
throw new WorkflowCallError(workflow, record.error ?? "child failed");
|
|
2562
|
+
while (record.status !== "completed") {
|
|
2563
|
+
let outcome;
|
|
2564
|
+
try {
|
|
2565
|
+
report(
|
|
2566
|
+
record.snapshot ? "resumed" : "started",
|
|
2567
|
+
workflowCallContent(telemetry, "input", input)
|
|
2568
|
+
);
|
|
2569
|
+
outcome = await service({
|
|
2570
|
+
callerNodeExecutionId,
|
|
2571
|
+
id: args.id,
|
|
2572
|
+
workflow,
|
|
2573
|
+
input,
|
|
2574
|
+
definition,
|
|
2575
|
+
invocationId: record.invocationId,
|
|
2576
|
+
...record.snapshot ? { snapshot: record.snapshot } : {},
|
|
2577
|
+
...record.interrupts.length ? { response: record.interrupts.at(-1)?.response } : {}
|
|
2578
|
+
});
|
|
2579
|
+
} catch (error) {
|
|
2580
|
+
(0, import_core6.throwIfExecutionAborted)(ctx.node.abortSignal);
|
|
2581
|
+
if ((0, import_core6.isExecutionCancelled)(error)) throw error;
|
|
2582
|
+
if ((0, import_core6.isExecutionLimitReached)(error)) {
|
|
2583
|
+
const child = error;
|
|
2584
|
+
if (child.__kortyxChildSnapshot)
|
|
2585
|
+
record.snapshot = child.__kortyxChildSnapshot;
|
|
2586
|
+
recordUsage(child.__kortyxChildUsage);
|
|
2587
|
+
report("suspended", {
|
|
2588
|
+
reason: "limit_reached",
|
|
2589
|
+
limit: error.limitReached
|
|
2590
|
+
});
|
|
2591
|
+
throw error;
|
|
2592
|
+
}
|
|
2593
|
+
record.status = "failed";
|
|
2594
|
+
record.error = error instanceof Error ? error.message : String(error);
|
|
2595
|
+
report("failed", { error: "Child workflow failed" });
|
|
2596
|
+
throw new WorkflowCallError(workflow, record.error);
|
|
2597
|
+
}
|
|
2598
|
+
(0, import_core6.throwIfExecutionAborted)(ctx.node.abortSignal);
|
|
2599
|
+
recordUsage(outcome.usage);
|
|
2600
|
+
if (outcome.status === "completed") {
|
|
2601
|
+
record.data = outcome.data;
|
|
2602
|
+
workflowCallFingerprint(record.data);
|
|
2603
|
+
record.status = "completed";
|
|
2604
|
+
delete record.snapshot;
|
|
2605
|
+
report(
|
|
2606
|
+
"completed",
|
|
2607
|
+
workflowCallContent(telemetry, "output", record.data)
|
|
2608
|
+
);
|
|
2609
|
+
} else {
|
|
2610
|
+
record.status = "interrupted";
|
|
2611
|
+
record.snapshot = outcome.snapshot;
|
|
2612
|
+
report("suspended", {
|
|
2613
|
+
leaf: outcome.request.meta?.workflowCall ?? null
|
|
2614
|
+
});
|
|
2615
|
+
const interruption = {
|
|
2616
|
+
request: outcome.request
|
|
2617
|
+
};
|
|
2618
|
+
record.interrupts.push(interruption);
|
|
2619
|
+
interruption.response = await awaitInterruptInternal({
|
|
2620
|
+
request: interruption.request,
|
|
2621
|
+
...interruption.request.meta ? { meta: interruption.request.meta } : {}
|
|
2622
|
+
});
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
if (cached)
|
|
2626
|
+
report("reused", workflowCallContent(telemetry, "output", record.data));
|
|
2627
|
+
const data = record.data ?? {};
|
|
2628
|
+
return {
|
|
2629
|
+
data: JSON.parse(JSON.stringify(data))
|
|
2630
|
+
};
|
|
2631
|
+
} finally {
|
|
2632
|
+
ctx.workflowCallActive = false;
|
|
2633
|
+
}
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
});
|
|
18
2637
|
|
|
19
2638
|
// src/index.ts
|
|
20
2639
|
var index_exports = {};
|
|
21
2640
|
__export(index_exports, {
|
|
2641
|
+
ExecutionRequestError: () => ExecutionRequestError,
|
|
22
2642
|
createAgent: () => createAgent,
|
|
23
2643
|
createChatRouteHandler: () => createChatRouteHandler,
|
|
24
2644
|
createCheckpointRouteHandler: () => createCheckpointRouteHandler,
|
|
@@ -99,6 +2719,7 @@ function parseCheckpointRequestBody(value) {
|
|
|
99
2719
|
async function handleChatRequestBody(args) {
|
|
100
2720
|
const { agent, body } = args;
|
|
101
2721
|
const stream = await agent.streamChat(body.messages, {
|
|
2722
|
+
...args.abortSignal ? { abortSignal: args.abortSignal } : {},
|
|
102
2723
|
sessionId: body.sessionId,
|
|
103
2724
|
workflowId: body.workflowId,
|
|
104
2725
|
context: body.context
|
|
@@ -118,7 +2739,11 @@ function createChatRouteHandler(args) {
|
|
|
118
2739
|
return async function POST(request) {
|
|
119
2740
|
try {
|
|
120
2741
|
const body = parseChatRequestBody(await request.json());
|
|
121
|
-
return await handleChatRequestBody({
|
|
2742
|
+
return await handleChatRequestBody({
|
|
2743
|
+
agent,
|
|
2744
|
+
body,
|
|
2745
|
+
abortSignal: request.signal
|
|
2746
|
+
});
|
|
122
2747
|
} catch (error) {
|
|
123
2748
|
return new Response(
|
|
124
2749
|
JSON.stringify({
|
|
@@ -184,10 +2809,168 @@ async function* streamChatFromRoute(args) {
|
|
|
184
2809
|
|
|
185
2810
|
// src/chat/create-agent.ts
|
|
186
2811
|
var import_node_path = require("path");
|
|
2812
|
+
var import_core4 = require("@kortyx/core");
|
|
187
2813
|
var import_providers = require("@kortyx/providers");
|
|
188
|
-
var
|
|
2814
|
+
var import_runtime5 = require("@kortyx/runtime");
|
|
2815
|
+
var import_zod4 = require("zod");
|
|
2816
|
+
|
|
2817
|
+
// src/execution/execute.ts
|
|
2818
|
+
var import_core3 = require("@kortyx/core");
|
|
2819
|
+
var import_runtime3 = require("@kortyx/runtime");
|
|
2820
|
+
var import_zod3 = require("zod");
|
|
2821
|
+
|
|
2822
|
+
// src/interrupt/resume-handler.ts
|
|
2823
|
+
var import_core2 = require("@kortyx/core");
|
|
2824
|
+
var import_runtime2 = require("@kortyx/runtime");
|
|
2825
|
+
|
|
2826
|
+
// src/orchestrator.ts
|
|
2827
|
+
var import_node_stream = require("stream");
|
|
2828
|
+
var import_core = require("@kortyx/core");
|
|
2829
|
+
var import_runtime = require("@kortyx/runtime");
|
|
2830
|
+
var import_langgraph = require("@langchain/langgraph");
|
|
2831
|
+
|
|
2832
|
+
// src/execution/contracts.ts
|
|
189
2833
|
var import_zod2 = require("zod");
|
|
190
2834
|
|
|
2835
|
+
// src/execution/types.ts
|
|
2836
|
+
var ExecutionRequestError = class extends Error {
|
|
2837
|
+
constructor(code, message) {
|
|
2838
|
+
super(message);
|
|
2839
|
+
this.code = code;
|
|
2840
|
+
}
|
|
2841
|
+
code;
|
|
2842
|
+
name = "ExecutionRequestError";
|
|
2843
|
+
};
|
|
2844
|
+
|
|
2845
|
+
// src/execution/contracts.ts
|
|
2846
|
+
async function resolveExecutionWorkflow(select, reference) {
|
|
2847
|
+
const id = typeof reference === "string" ? reference : reference.id;
|
|
2848
|
+
let workflow;
|
|
2849
|
+
try {
|
|
2850
|
+
workflow = await select(id);
|
|
2851
|
+
} catch {
|
|
2852
|
+
throw new ExecutionRequestError(
|
|
2853
|
+
"UNKNOWN_WORKFLOW",
|
|
2854
|
+
`Workflow '${id}' is not registered.`
|
|
2855
|
+
);
|
|
2856
|
+
}
|
|
2857
|
+
if (workflow.id !== id)
|
|
2858
|
+
throw new ExecutionRequestError(
|
|
2859
|
+
"UNKNOWN_WORKFLOW",
|
|
2860
|
+
`Workflow '${id}' is not registered.`
|
|
2861
|
+
);
|
|
2862
|
+
if (!workflow.inputSchema || !workflow.outputSchema)
|
|
2863
|
+
throw new ExecutionRequestError(
|
|
2864
|
+
"MISSING_CONTRACT",
|
|
2865
|
+
`Workflow '${id}' requires inputSchema and outputSchema.`
|
|
2866
|
+
);
|
|
2867
|
+
if (typeof reference !== "string" && (reference.version !== workflow.version || reference.inputSchema !== workflow.inputSchema || reference.outputSchema !== workflow.outputSchema))
|
|
2868
|
+
throw new ExecutionRequestError(
|
|
2869
|
+
"CONTRACT_MISMATCH",
|
|
2870
|
+
"The typed workflow contract does not match the registered definition."
|
|
2871
|
+
);
|
|
2872
|
+
return workflow;
|
|
2873
|
+
}
|
|
2874
|
+
function parseExecutionInput(workflow, input) {
|
|
2875
|
+
try {
|
|
2876
|
+
import_zod2.z.json().parse(input);
|
|
2877
|
+
const parsed = workflow.inputSchema ? workflow.inputSchema.parse(input) : input;
|
|
2878
|
+
import_zod2.z.json().parse(parsed);
|
|
2879
|
+
return parsed;
|
|
2880
|
+
} catch (error) {
|
|
2881
|
+
throw new ExecutionRequestError(
|
|
2882
|
+
"INVALID_INPUT",
|
|
2883
|
+
error instanceof Error ? error.message : String(error)
|
|
2884
|
+
);
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
async function validateExecutionOutput(state, select) {
|
|
2888
|
+
if (!state.config?.executionContract) return state;
|
|
2889
|
+
const terminal = await select(String(state.currentWorkflow));
|
|
2890
|
+
const contract = state.config.executionContract;
|
|
2891
|
+
const root = await select(contract.id);
|
|
2892
|
+
if (root.id !== contract.id || root.version !== contract.version)
|
|
2893
|
+
throw new Error("The root workflow changed since execution started.");
|
|
2894
|
+
let data = state.data ?? {};
|
|
2895
|
+
try {
|
|
2896
|
+
for (const schema of /* @__PURE__ */ new Set([terminal.outputSchema, root.outputSchema])) {
|
|
2897
|
+
if (schema) data = schema.parse(data);
|
|
2898
|
+
}
|
|
2899
|
+
if (terminal.outputSchema || root.outputSchema) import_zod2.z.json().parse(data);
|
|
2900
|
+
} catch (error) {
|
|
2901
|
+
const failure = new Error(
|
|
2902
|
+
`Workflow output validation failed: ${error instanceof Error ? error.message : String(error)}`
|
|
2903
|
+
);
|
|
2904
|
+
Object.assign(failure, { code: "INVALID_OUTPUT" });
|
|
2905
|
+
throw failure;
|
|
2906
|
+
}
|
|
2907
|
+
return { ...state, data };
|
|
2908
|
+
}
|
|
2909
|
+
|
|
2910
|
+
// src/stream/transform-graph-stream-for-ui.ts
|
|
2911
|
+
var isGraphStateOutput = (value) => value !== null && typeof value === "object" && !Array.isArray(value) && "currentWorkflow" in value;
|
|
2912
|
+
async function* transformGraphStreamForUI(stream, options = {}) {
|
|
2913
|
+
const { debug = false, emitStatus = debug } = options;
|
|
2914
|
+
const startedNodes = /* @__PURE__ */ new Set();
|
|
2915
|
+
const endedNodes = /* @__PURE__ */ new Set();
|
|
2916
|
+
let latestGraphState = null;
|
|
2917
|
+
for await (const event of stream) {
|
|
2918
|
+
const { event: type, name, data } = event ?? {};
|
|
2919
|
+
if (debug) console.log(`[debug:event]`, JSON.stringify(event, null, 2));
|
|
2920
|
+
switch (type) {
|
|
2921
|
+
case "on_chain_start":
|
|
2922
|
+
if (name && !name.startsWith("ChannelWrite")) {
|
|
2923
|
+
if (name === "__start__" || name === "__end__") break;
|
|
2924
|
+
if (startedNodes.has(name)) break;
|
|
2925
|
+
startedNodes.add(name);
|
|
2926
|
+
if (debug) console.log(`[debug:start] node=${name}`);
|
|
2927
|
+
if (emitStatus) {
|
|
2928
|
+
yield { type: "status", message: `Processing node: ${name}` };
|
|
2929
|
+
}
|
|
2930
|
+
}
|
|
2931
|
+
break;
|
|
2932
|
+
case "on_chain_end": {
|
|
2933
|
+
const nodeName = name;
|
|
2934
|
+
const output = data?.output;
|
|
2935
|
+
if (debug)
|
|
2936
|
+
console.log(
|
|
2937
|
+
`[debug:on_chain_end:${nodeName}] output=`,
|
|
2938
|
+
JSON.stringify(output, null, 2)
|
|
2939
|
+
);
|
|
2940
|
+
if (!output || nodeName?.startsWith("ChannelWrite")) break;
|
|
2941
|
+
if (isGraphStateOutput(output)) latestGraphState = output;
|
|
2942
|
+
if (nodeName !== "__start__" && nodeName !== "__end__") {
|
|
2943
|
+
if (!endedNodes.has(nodeName)) {
|
|
2944
|
+
if (emitStatus) {
|
|
2945
|
+
yield {
|
|
2946
|
+
type: "status",
|
|
2947
|
+
message: `\u2705 Completed node: ${nodeName}`
|
|
2948
|
+
};
|
|
2949
|
+
}
|
|
2950
|
+
endedNodes.add(nodeName);
|
|
2951
|
+
}
|
|
2952
|
+
}
|
|
2953
|
+
break;
|
|
2954
|
+
}
|
|
2955
|
+
case "on_graph_end": {
|
|
2956
|
+
if (debug)
|
|
2957
|
+
console.log(`[debug:on_graph_end]`, JSON.stringify(data, null, 2));
|
|
2958
|
+
const output = data?.output;
|
|
2959
|
+
yield {
|
|
2960
|
+
type: "done",
|
|
2961
|
+
data: isGraphStateOutput(output) ? output : latestGraphState
|
|
2962
|
+
};
|
|
2963
|
+
break;
|
|
2964
|
+
}
|
|
2965
|
+
default:
|
|
2966
|
+
if (debug) {
|
|
2967
|
+
console.warn(`[debug:unknown_event]`, type);
|
|
2968
|
+
}
|
|
2969
|
+
break;
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
|
|
191
2974
|
// src/telemetry/events.ts
|
|
192
2975
|
var import_node_crypto = require("crypto");
|
|
193
2976
|
var isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
@@ -237,7 +3020,10 @@ var emitTelemetryEvent = (args) => {
|
|
|
237
3020
|
}
|
|
238
3021
|
} : {},
|
|
239
3022
|
type: args.type,
|
|
240
|
-
payload:
|
|
3023
|
+
payload: {
|
|
3024
|
+
...args.payload,
|
|
3025
|
+
...args.type === "run.cancelled" ? { branchId: args.config.executionBranchId ?? runId } : {}
|
|
3026
|
+
}
|
|
241
3027
|
};
|
|
242
3028
|
try {
|
|
243
3029
|
void Promise.resolve(telemetry.reporter.emit([event])).then(async () => {
|
|
@@ -392,136 +3178,68 @@ var projectWorkflowTopology = (args) => {
|
|
|
392
3178
|
environment: args.environment,
|
|
393
3179
|
service: args.service,
|
|
394
3180
|
workflow: {
|
|
395
|
-
id: args.workflow.id,
|
|
396
|
-
declaredVersion: args.workflow.version,
|
|
397
|
-
...args.workflow.description ? { description: args.workflow.description } : {},
|
|
398
|
-
...tags ? { tags } : {},
|
|
399
|
-
topologyHash,
|
|
400
|
-
nodes: nodes.map(({ _execution: _ignored, ...node }) => node),
|
|
401
|
-
edges,
|
|
402
|
-
...transitions.length > 0 ? { transitions } : {}
|
|
403
|
-
}
|
|
404
|
-
};
|
|
405
|
-
};
|
|
406
|
-
var prepareWorkflowTelemetry = (args) => {
|
|
407
|
-
const telemetry = isRecord2(args.config.telemetry) ? args.config.telemetry : void 0;
|
|
408
|
-
if (!telemetry?.reporter || !telemetry.environment || !telemetry.service) {
|
|
409
|
-
return args.config;
|
|
410
|
-
}
|
|
411
|
-
let snapshot;
|
|
412
|
-
try {
|
|
413
|
-
snapshot = projectWorkflowTopology({
|
|
414
|
-
workflow: args.workflow,
|
|
415
|
-
environment: telemetry.environment,
|
|
416
|
-
service: telemetry.service,
|
|
417
|
-
knownWorkflowIds: args.knownWorkflowIds
|
|
418
|
-
});
|
|
419
|
-
} catch {
|
|
420
|
-
return args.config;
|
|
421
|
-
}
|
|
422
|
-
let workflowRevisionId;
|
|
423
|
-
try {
|
|
424
|
-
workflowRevisionId = telemetry.reporter.getWorkflowRevisionId?.({
|
|
425
|
-
environment: snapshot.environment,
|
|
426
|
-
workflowId: snapshot.workflow.id,
|
|
427
|
-
topologyHash: snapshot.workflow.topologyHash
|
|
428
|
-
});
|
|
429
|
-
void Promise.resolve(
|
|
430
|
-
telemetry.reporter.ensureWorkflowTopology(snapshot)
|
|
431
|
-
).catch(() => {
|
|
432
|
-
});
|
|
433
|
-
} catch {
|
|
434
|
-
}
|
|
435
|
-
return {
|
|
436
|
-
...args.config,
|
|
437
|
-
telemetry: {
|
|
438
|
-
...telemetry,
|
|
439
|
-
correlation: {
|
|
440
|
-
...telemetry.correlation ?? {},
|
|
441
|
-
runId: args.runId,
|
|
442
|
-
...args.sessionId ? { sessionId: args.sessionId } : {},
|
|
443
|
-
workflowId: snapshot.workflow.id,
|
|
444
|
-
topologyHash: snapshot.workflow.topologyHash,
|
|
445
|
-
...workflowRevisionId ? { workflowRevisionId } : {}
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
};
|
|
449
|
-
};
|
|
450
|
-
|
|
451
|
-
// src/chat/process-chat.ts
|
|
452
|
-
var import_runtime3 = require("@kortyx/runtime");
|
|
453
|
-
|
|
454
|
-
// src/interrupt/resume-handler.ts
|
|
455
|
-
var import_runtime2 = require("@kortyx/runtime");
|
|
456
|
-
|
|
457
|
-
// src/orchestrator.ts
|
|
458
|
-
var import_node_stream = require("stream");
|
|
459
|
-
var import_runtime = require("@kortyx/runtime");
|
|
460
|
-
var import_langgraph = require("@langchain/langgraph");
|
|
461
|
-
|
|
462
|
-
// src/stream/transform-graph-stream-for-ui.ts
|
|
463
|
-
var isGraphStateOutput = (value) => value !== null && typeof value === "object" && !Array.isArray(value) && "currentWorkflow" in value;
|
|
464
|
-
async function* transformGraphStreamForUI(stream, options = {}) {
|
|
465
|
-
const { debug = false, emitStatus = debug } = options;
|
|
466
|
-
const startedNodes = /* @__PURE__ */ new Set();
|
|
467
|
-
const endedNodes = /* @__PURE__ */ new Set();
|
|
468
|
-
let latestGraphState = null;
|
|
469
|
-
for await (const event of stream) {
|
|
470
|
-
const { event: type, name, data } = event ?? {};
|
|
471
|
-
if (debug) console.log(`[debug:event]`, JSON.stringify(event, null, 2));
|
|
472
|
-
switch (type) {
|
|
473
|
-
case "on_chain_start":
|
|
474
|
-
if (name && !name.startsWith("ChannelWrite")) {
|
|
475
|
-
if (name === "__start__" || name === "__end__") break;
|
|
476
|
-
if (startedNodes.has(name)) break;
|
|
477
|
-
startedNodes.add(name);
|
|
478
|
-
if (debug) console.log(`[debug:start] node=${name}`);
|
|
479
|
-
if (emitStatus) {
|
|
480
|
-
yield { type: "status", message: `Processing node: ${name}` };
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
break;
|
|
484
|
-
case "on_chain_end": {
|
|
485
|
-
const nodeName = name;
|
|
486
|
-
const output = data?.output;
|
|
487
|
-
if (debug)
|
|
488
|
-
console.log(
|
|
489
|
-
`[debug:on_chain_end:${nodeName}] output=`,
|
|
490
|
-
JSON.stringify(output, null, 2)
|
|
491
|
-
);
|
|
492
|
-
if (!output || nodeName?.startsWith("ChannelWrite")) break;
|
|
493
|
-
if (isGraphStateOutput(output)) latestGraphState = output;
|
|
494
|
-
if (nodeName !== "__start__" && nodeName !== "__end__") {
|
|
495
|
-
if (!endedNodes.has(nodeName)) {
|
|
496
|
-
if (emitStatus) {
|
|
497
|
-
yield {
|
|
498
|
-
type: "status",
|
|
499
|
-
message: `\u2705 Completed node: ${nodeName}`
|
|
500
|
-
};
|
|
501
|
-
}
|
|
502
|
-
endedNodes.add(nodeName);
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
break;
|
|
506
|
-
}
|
|
507
|
-
case "on_graph_end": {
|
|
508
|
-
if (debug)
|
|
509
|
-
console.log(`[debug:on_graph_end]`, JSON.stringify(data, null, 2));
|
|
510
|
-
const output = data?.output;
|
|
511
|
-
yield {
|
|
512
|
-
type: "done",
|
|
513
|
-
data: isGraphStateOutput(output) ? output : latestGraphState
|
|
514
|
-
};
|
|
515
|
-
break;
|
|
516
|
-
}
|
|
517
|
-
default:
|
|
518
|
-
if (debug) {
|
|
519
|
-
console.warn(`[debug:unknown_event]`, type);
|
|
520
|
-
}
|
|
521
|
-
break;
|
|
3181
|
+
id: args.workflow.id,
|
|
3182
|
+
declaredVersion: args.workflow.version,
|
|
3183
|
+
...args.workflow.description ? { description: args.workflow.description } : {},
|
|
3184
|
+
...tags ? { tags } : {},
|
|
3185
|
+
topologyHash,
|
|
3186
|
+
nodes: nodes.map(({ _execution: _ignored, ...node }) => node),
|
|
3187
|
+
edges,
|
|
3188
|
+
...transitions.length > 0 ? { transitions } : {}
|
|
522
3189
|
}
|
|
3190
|
+
};
|
|
3191
|
+
};
|
|
3192
|
+
var prepareWorkflowTelemetry = (args) => {
|
|
3193
|
+
const telemetry = isRecord2(args.config.telemetry) ? args.config.telemetry : void 0;
|
|
3194
|
+
if (!telemetry?.reporter || !telemetry.environment || !telemetry.service) {
|
|
3195
|
+
return args.config;
|
|
523
3196
|
}
|
|
524
|
-
|
|
3197
|
+
let snapshot;
|
|
3198
|
+
try {
|
|
3199
|
+
snapshot = projectWorkflowTopology({
|
|
3200
|
+
workflow: args.workflow,
|
|
3201
|
+
environment: telemetry.environment,
|
|
3202
|
+
service: telemetry.service,
|
|
3203
|
+
knownWorkflowIds: args.knownWorkflowIds
|
|
3204
|
+
});
|
|
3205
|
+
} catch {
|
|
3206
|
+
return args.config;
|
|
3207
|
+
}
|
|
3208
|
+
let workflowRevisionId;
|
|
3209
|
+
try {
|
|
3210
|
+
workflowRevisionId = telemetry.reporter.getWorkflowRevisionId?.({
|
|
3211
|
+
environment: snapshot.environment,
|
|
3212
|
+
workflowId: snapshot.workflow.id,
|
|
3213
|
+
topologyHash: snapshot.workflow.topologyHash
|
|
3214
|
+
});
|
|
3215
|
+
void Promise.resolve(
|
|
3216
|
+
telemetry.reporter.ensureWorkflowTopology(snapshot)
|
|
3217
|
+
).catch(() => {
|
|
3218
|
+
});
|
|
3219
|
+
} catch {
|
|
3220
|
+
}
|
|
3221
|
+
return {
|
|
3222
|
+
...args.config,
|
|
3223
|
+
prepareChildTelemetry: (workflow, config) => prepareWorkflowTelemetry({
|
|
3224
|
+
config,
|
|
3225
|
+
workflow,
|
|
3226
|
+
runId: args.runId,
|
|
3227
|
+
sessionId: args.sessionId,
|
|
3228
|
+
knownWorkflowIds: args.knownWorkflowIds
|
|
3229
|
+
}),
|
|
3230
|
+
telemetry: {
|
|
3231
|
+
...telemetry,
|
|
3232
|
+
correlation: {
|
|
3233
|
+
...telemetry.correlation ?? {},
|
|
3234
|
+
runId: args.runId,
|
|
3235
|
+
...args.sessionId ? { sessionId: args.sessionId } : {},
|
|
3236
|
+
workflowId: snapshot.workflow.id,
|
|
3237
|
+
topologyHash: snapshot.workflow.topologyHash,
|
|
3238
|
+
...workflowRevisionId ? { workflowRevisionId } : {}
|
|
3239
|
+
}
|
|
3240
|
+
}
|
|
3241
|
+
};
|
|
3242
|
+
};
|
|
525
3243
|
|
|
526
3244
|
// src/orchestrator.ts
|
|
527
3245
|
var isRecord3 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
@@ -544,15 +3262,30 @@ async function orchestrateGraphStream({
|
|
|
544
3262
|
graph,
|
|
545
3263
|
state,
|
|
546
3264
|
config: initialConfig,
|
|
3265
|
+
abortSignal: requestSignal,
|
|
3266
|
+
emitOutput = true,
|
|
3267
|
+
onOutcome,
|
|
547
3268
|
selectWorkflow,
|
|
548
3269
|
knownWorkflowIds,
|
|
549
3270
|
frameworkAdapter
|
|
550
3271
|
}) {
|
|
551
3272
|
const out = new import_node_stream.PassThrough({ objectMode: true });
|
|
3273
|
+
const write = (chunk) => {
|
|
3274
|
+
if (emitOutput && !out.destroyed) out.write(chunk);
|
|
3275
|
+
};
|
|
3276
|
+
let outcomeState = state;
|
|
3277
|
+
let outcomeError;
|
|
3278
|
+
let sessionCheckpointId;
|
|
552
3279
|
let config = initialConfig;
|
|
3280
|
+
const budget = config.executionBudget;
|
|
553
3281
|
let currentGraph = graph;
|
|
554
3282
|
let currentState = state;
|
|
555
3283
|
let finished = false;
|
|
3284
|
+
const controller = new AbortController();
|
|
3285
|
+
const abortSignal = (0, import_core.combineAbortSignals)(requestSignal, controller.signal);
|
|
3286
|
+
out.on("close", () => {
|
|
3287
|
+
if (!finished) controller.abort();
|
|
3288
|
+
});
|
|
556
3289
|
let structuredSeq = 0;
|
|
557
3290
|
const debugEnabled = Boolean(config?.features?.tracing);
|
|
558
3291
|
const traceAdapter = getTraceAdapter(config);
|
|
@@ -593,7 +3326,7 @@ async function orchestrateGraphStream({
|
|
|
593
3326
|
},
|
|
594
3327
|
tags: Array.isArray(telemetryConfig.tags) ? telemetryConfig.tags : void 0,
|
|
595
3328
|
captureContent: telemetryConfig.captureContent,
|
|
596
|
-
|
|
3329
|
+
input: typeof state.input === "string" ? state.input : JSON.stringify(state.input)
|
|
597
3330
|
}
|
|
598
3331
|
};
|
|
599
3332
|
const namespacesUsed = /* @__PURE__ */ new Set();
|
|
@@ -604,7 +3337,7 @@ async function orchestrateGraphStream({
|
|
|
604
3337
|
try {
|
|
605
3338
|
const sid = config?.session?.id;
|
|
606
3339
|
if (sid && typeof sid === "string") {
|
|
607
|
-
|
|
3340
|
+
write({ type: "session", sessionId: sid });
|
|
608
3341
|
}
|
|
609
3342
|
} catch {
|
|
610
3343
|
}
|
|
@@ -628,12 +3361,33 @@ async function orchestrateGraphStream({
|
|
|
628
3361
|
}
|
|
629
3362
|
lastOutputSegmentId = textStreamId;
|
|
630
3363
|
accumulatedOutput += delta;
|
|
3364
|
+
if (!emitOutput) accumulatedOutput = accumulatedOutput.slice(0, 65536);
|
|
631
3365
|
};
|
|
632
3366
|
const nextStructuredStreamId = () => `${runId}:structured:${structuredSeq++}`;
|
|
633
3367
|
const runTraceEndArgs = () => accumulatedOutput.length > 0 ? { telemetry: { output: accumulatedOutput } } : void 0;
|
|
634
3368
|
let pendingRecordToken = null;
|
|
635
3369
|
let wroteHumanInput = false;
|
|
636
3370
|
const pendingStore = frameworkAdapter?.pendingRequests;
|
|
3371
|
+
const sealPendingSnapshots = async () => {
|
|
3372
|
+
if (budget) outcomeState.config.executionBudget = budget;
|
|
3373
|
+
if (typeof frameworkAdapter?.checkpointer?.getTuple !== "function") return;
|
|
3374
|
+
for (const [token, request] of activePendingRequests) {
|
|
3375
|
+
if (budget) request.state.config.executionBudget = budget;
|
|
3376
|
+
const graphSnapshot = await (0, import_runtime.captureGraphSnapshot)(
|
|
3377
|
+
frameworkAdapter.checkpointer,
|
|
3378
|
+
runId,
|
|
3379
|
+
request.graphCheckpointId
|
|
3380
|
+
);
|
|
3381
|
+
if (graphSnapshot) {
|
|
3382
|
+
const sealed = { ...request, graphSnapshot };
|
|
3383
|
+
activePendingRequests.set(token, sealed);
|
|
3384
|
+
await pendingStore?.update(token, {
|
|
3385
|
+
graphSnapshot,
|
|
3386
|
+
state: request.state
|
|
3387
|
+
});
|
|
3388
|
+
}
|
|
3389
|
+
}
|
|
3390
|
+
};
|
|
637
3391
|
const pendingTtlMs = frameworkAdapter?.ttlMs ?? 15 * 60 * 1e3;
|
|
638
3392
|
const persistAndEmitInterrupt = async (payload) => {
|
|
639
3393
|
if (wroteHumanInput) return;
|
|
@@ -681,6 +3435,7 @@ async function orchestrateGraphStream({
|
|
|
681
3435
|
activePendingRequests.set(record.token, record);
|
|
682
3436
|
if (pendingStore) {
|
|
683
3437
|
const savePromise = pendingStore.save(record).catch((error) => {
|
|
3438
|
+
outcomeError = error;
|
|
684
3439
|
console.error("[orchestrator] failed to save pending request", error);
|
|
685
3440
|
});
|
|
686
3441
|
pendingRequestWrites.push(savePromise);
|
|
@@ -690,7 +3445,7 @@ async function orchestrateGraphStream({
|
|
|
690
3445
|
([key]) => !key.startsWith("__kortyx")
|
|
691
3446
|
)
|
|
692
3447
|
) : void 0;
|
|
693
|
-
|
|
3448
|
+
write({
|
|
694
3449
|
type: "interrupt",
|
|
695
3450
|
requestId: record.requestId,
|
|
696
3451
|
resumeToken: record.token,
|
|
@@ -719,6 +3474,9 @@ async function orchestrateGraphStream({
|
|
|
719
3474
|
type: "interrupt.created",
|
|
720
3475
|
correlation: { nodeId: record.node },
|
|
721
3476
|
payload: {
|
|
3477
|
+
workflowCall: record.schema.meta?.workflowCall ?? null,
|
|
3478
|
+
workflowCallPath: record.schema.meta?.workflowCallPath ?? null,
|
|
3479
|
+
branchId: config.executionBranchId ?? runId,
|
|
722
3480
|
interruptId: record.requestId,
|
|
723
3481
|
requestId: record.requestId,
|
|
724
3482
|
kind: record.schema.kind,
|
|
@@ -749,8 +3507,9 @@ async function orchestrateGraphStream({
|
|
|
749
3507
|
const msg = String(
|
|
750
3508
|
payload?.message ?? "Unexpected error"
|
|
751
3509
|
);
|
|
752
|
-
|
|
753
|
-
|
|
3510
|
+
outcomeError = new Error(msg);
|
|
3511
|
+
write({ type: "error", message: msg });
|
|
3512
|
+
write({ type: "done" });
|
|
754
3513
|
finished = true;
|
|
755
3514
|
out.end();
|
|
756
3515
|
return;
|
|
@@ -763,14 +3522,14 @@ async function orchestrateGraphStream({
|
|
|
763
3522
|
if (msg && msg === lastStatusMsg && now - lastStatusAt < 250) return;
|
|
764
3523
|
lastStatusMsg = msg;
|
|
765
3524
|
lastStatusAt = now;
|
|
766
|
-
|
|
3525
|
+
write({ type: "status", message: msg });
|
|
767
3526
|
return;
|
|
768
3527
|
}
|
|
769
3528
|
if (event === "text-start") {
|
|
770
3529
|
const node = payload?.node;
|
|
771
3530
|
if (!node) return;
|
|
772
3531
|
touchedNodes.add(node);
|
|
773
|
-
|
|
3532
|
+
write({
|
|
774
3533
|
type: "text-start",
|
|
775
3534
|
node,
|
|
776
3535
|
...typeof payload.id === "string" ? { id: payload.id } : {},
|
|
@@ -785,7 +3544,7 @@ async function orchestrateGraphStream({
|
|
|
785
3544
|
if (!node || !delta) return;
|
|
786
3545
|
touchedNodes.add(node);
|
|
787
3546
|
appendAccumulatedOutput(payload, node, delta);
|
|
788
|
-
|
|
3547
|
+
write({
|
|
789
3548
|
type: "text-delta",
|
|
790
3549
|
delta,
|
|
791
3550
|
node,
|
|
@@ -799,7 +3558,7 @@ async function orchestrateGraphStream({
|
|
|
799
3558
|
const node = payload?.node;
|
|
800
3559
|
if (!node) return;
|
|
801
3560
|
touchedNodes.add(node);
|
|
802
|
-
|
|
3561
|
+
write({
|
|
803
3562
|
type: "text-end",
|
|
804
3563
|
node,
|
|
805
3564
|
...typeof payload.id === "string" ? { id: payload.id } : {},
|
|
@@ -813,7 +3572,7 @@ async function orchestrateGraphStream({
|
|
|
813
3572
|
if (typeof payloadObj.tool !== "string" || typeof payloadObj.toolCallId !== "string") {
|
|
814
3573
|
return;
|
|
815
3574
|
}
|
|
816
|
-
|
|
3575
|
+
write({
|
|
817
3576
|
type: "tool-call-start",
|
|
818
3577
|
tool: payloadObj.tool,
|
|
819
3578
|
toolCallId: payloadObj.toolCallId,
|
|
@@ -830,7 +3589,7 @@ async function orchestrateGraphStream({
|
|
|
830
3589
|
if (typeof payloadObj.tool !== "string" || typeof payloadObj.toolCallId !== "string") {
|
|
831
3590
|
return;
|
|
832
3591
|
}
|
|
833
|
-
|
|
3592
|
+
write({
|
|
834
3593
|
type: "tool-call-result",
|
|
835
3594
|
tool: payloadObj.tool,
|
|
836
3595
|
toolCallId: payloadObj.toolCallId,
|
|
@@ -849,7 +3608,7 @@ async function orchestrateGraphStream({
|
|
|
849
3608
|
if (typeof payloadObj.tool !== "string" || typeof payloadObj.toolCallId !== "string") {
|
|
850
3609
|
return;
|
|
851
3610
|
}
|
|
852
|
-
|
|
3611
|
+
write({
|
|
853
3612
|
type: "tool-call-error",
|
|
854
3613
|
tool: payloadObj.tool,
|
|
855
3614
|
toolCallId: payloadObj.toolCallId,
|
|
@@ -865,7 +3624,7 @@ async function orchestrateGraphStream({
|
|
|
865
3624
|
const node = payload?.node;
|
|
866
3625
|
const text = String(payload?.content ?? "");
|
|
867
3626
|
if (node) touchedNodes.add(node);
|
|
868
|
-
|
|
3627
|
+
write({ type: "message", node, content: text });
|
|
869
3628
|
return;
|
|
870
3629
|
}
|
|
871
3630
|
if (event === "structured_data") {
|
|
@@ -874,7 +3633,7 @@ async function orchestrateGraphStream({
|
|
|
874
3633
|
const streamId = typeof payloadObj.streamId === "string" && payloadObj.streamId ? payloadObj.streamId : nextStructuredStreamId();
|
|
875
3634
|
structuredStreamIds.add(streamId);
|
|
876
3635
|
if (payloadObj.node) touchedNodes.add(payloadObj.node);
|
|
877
|
-
|
|
3636
|
+
write({
|
|
878
3637
|
type: "structured-data",
|
|
879
3638
|
node: payloadObj.node,
|
|
880
3639
|
streamId,
|
|
@@ -900,7 +3659,7 @@ async function orchestrateGraphStream({
|
|
|
900
3659
|
return;
|
|
901
3660
|
}
|
|
902
3661
|
if (event === "transition") {
|
|
903
|
-
|
|
3662
|
+
write({
|
|
904
3663
|
type: "transition",
|
|
905
3664
|
transitionTo: payload?.transitionTo,
|
|
906
3665
|
payload: payload?.payload ?? {}
|
|
@@ -928,12 +3687,17 @@ async function orchestrateGraphStream({
|
|
|
928
3687
|
};
|
|
929
3688
|
const runLoop = async (runTraceSpan) => {
|
|
930
3689
|
while (true) {
|
|
3690
|
+
(0, import_core.throwIfExecutionAborted)(abortSignal);
|
|
931
3691
|
let workflowFinalState = null;
|
|
932
3692
|
let workflowFinalGraphCheckpointId;
|
|
933
3693
|
let sawWorkflowDone = false;
|
|
934
3694
|
let workflowDoneData;
|
|
3695
|
+
currentGraph.execution ??= {};
|
|
3696
|
+
currentGraph.execution.abortSignal = abortSignal;
|
|
3697
|
+
currentGraph.execution.budget = budget;
|
|
935
3698
|
currentGraph.config = currentGraph.config || {};
|
|
936
3699
|
currentGraph.config.emit = forwardEmit;
|
|
3700
|
+
currentGraph.config.executionRunId = runId;
|
|
937
3701
|
const threadId = currentGraph.config?.session?.id || sessionId || "anonymous-session";
|
|
938
3702
|
const checkpointNs = "";
|
|
939
3703
|
namespacesUsed.add(checkpointNs);
|
|
@@ -963,7 +3727,7 @@ async function orchestrateGraphStream({
|
|
|
963
3727
|
};
|
|
964
3728
|
};
|
|
965
3729
|
if (debugEnabled) {
|
|
966
|
-
|
|
3730
|
+
write({
|
|
967
3731
|
type: "status",
|
|
968
3732
|
message: `\u{1F9F5} thread_id=${threadId} run_id=${runId} workflow=${currentState.currentWorkflow}`
|
|
969
3733
|
});
|
|
@@ -982,16 +3746,32 @@ async function orchestrateGraphStream({
|
|
|
982
3746
|
}
|
|
983
3747
|
});
|
|
984
3748
|
if (debugEnabled) {
|
|
985
|
-
|
|
3749
|
+
write({
|
|
986
3750
|
type: "status",
|
|
987
3751
|
message: `\u25B6\uFE0F streamEvents invoke: resume=${Boolean(currentGraph.config?.resume)} thread_id=${threadId} run_id=${runId} ns=${String(currentState.currentWorkflow || "default")}`
|
|
988
3752
|
});
|
|
989
3753
|
}
|
|
990
|
-
const
|
|
3754
|
+
const observedStream = (async function* () {
|
|
3755
|
+
for await (const event of runtimeStream) {
|
|
3756
|
+
const raw = event;
|
|
3757
|
+
if ((raw.event === "on_chain_end" || raw.event === "on_graph_end") && !raw.name?.startsWith("ChannelWrite") && isGraphState(raw.data?.output)) {
|
|
3758
|
+
outcomeState = raw.data.output;
|
|
3759
|
+
}
|
|
3760
|
+
yield event;
|
|
3761
|
+
}
|
|
3762
|
+
})();
|
|
3763
|
+
const uiStream = transformGraphStreamForUI(observedStream, {
|
|
991
3764
|
debug: debugEnabled,
|
|
992
3765
|
emitStatus: debugEnabled
|
|
993
3766
|
});
|
|
994
|
-
|
|
3767
|
+
if (!emitOutput) {
|
|
3768
|
+
for await (const _event of observedStream) {
|
|
3769
|
+
if (finished) break;
|
|
3770
|
+
}
|
|
3771
|
+
sawWorkflowDone = true;
|
|
3772
|
+
workflowDoneData = outcomeState;
|
|
3773
|
+
}
|
|
3774
|
+
for await (const chunk of emitOutput ? uiStream : []) {
|
|
995
3775
|
if (finished) break;
|
|
996
3776
|
if ("node" in chunk && typeof chunk.node === "string") {
|
|
997
3777
|
touchedNodes.add(chunk.node);
|
|
@@ -1001,14 +3781,18 @@ async function orchestrateGraphStream({
|
|
|
1001
3781
|
workflowDoneData = chunk.data;
|
|
1002
3782
|
continue;
|
|
1003
3783
|
}
|
|
1004
|
-
|
|
3784
|
+
write(chunk);
|
|
3785
|
+
}
|
|
3786
|
+
if (finished) {
|
|
3787
|
+
runTraceSpan.fail?.(outcomeError);
|
|
3788
|
+
return;
|
|
1005
3789
|
}
|
|
1006
|
-
if (finished) return;
|
|
1007
3790
|
if (sawWorkflowDone) {
|
|
1008
3791
|
const graphSnapshot = await readLatestGraphSnapshot();
|
|
1009
3792
|
workflowFinalGraphCheckpointId = graphSnapshot?.checkpointId;
|
|
1010
3793
|
workflowFinalState = isGraphState(workflowDoneData) ? workflowDoneData : graphSnapshot?.state ?? currentState;
|
|
1011
3794
|
}
|
|
3795
|
+
(0, import_core.throwIfExecutionAborted)(abortSignal);
|
|
1012
3796
|
const transitionTo = pending.to;
|
|
1013
3797
|
const transitionPayload = pending.payload;
|
|
1014
3798
|
const sourceNodeId = pending.sourceNodeId;
|
|
@@ -1045,6 +3829,10 @@ async function orchestrateGraphStream({
|
|
|
1045
3829
|
} : {}
|
|
1046
3830
|
}
|
|
1047
3831
|
});
|
|
3832
|
+
delete config.resume;
|
|
3833
|
+
delete config.resumeValue;
|
|
3834
|
+
delete config.resumeUpdate;
|
|
3835
|
+
delete config.resumeCheckpointId;
|
|
1048
3836
|
const nextGraph = await (0, import_runtime.createExecutionGraph)(nextWorkflow, {
|
|
1049
3837
|
...config,
|
|
1050
3838
|
emit: forwardEmit
|
|
@@ -1055,12 +3843,13 @@ async function orchestrateGraphStream({
|
|
|
1055
3843
|
...transitionPayload
|
|
1056
3844
|
};
|
|
1057
3845
|
const rawInputFromPayload = transitionPayload?.rawInput;
|
|
1058
|
-
const
|
|
3846
|
+
const rawNextInput = typeof rawInputFromPayload === "string" ? rawInputFromPayload : currentState.input;
|
|
1059
3847
|
currentState = {
|
|
1060
3848
|
...currentState,
|
|
3849
|
+
runtime: workflowFinalState?.runtime ?? currentState.runtime,
|
|
1061
3850
|
currentWorkflow: transitionTo,
|
|
1062
3851
|
config,
|
|
1063
|
-
input:
|
|
3852
|
+
input: nextWorkflow.inputSchema ? parseExecutionInput(nextWorkflow, rawNextInput) : rawNextInput,
|
|
1064
3853
|
data: mergedData,
|
|
1065
3854
|
ui: {}
|
|
1066
3855
|
// reset UI layer on new graph
|
|
@@ -1068,14 +3857,18 @@ async function orchestrateGraphStream({
|
|
|
1068
3857
|
currentGraph = nextGraph;
|
|
1069
3858
|
continue;
|
|
1070
3859
|
} catch (err) {
|
|
3860
|
+
(0, import_core.throwIfExecutionAborted)(abortSignal);
|
|
3861
|
+
if ((0, import_core.isExecutionCancelled)(err)) throw err;
|
|
3862
|
+
outcomeError = err;
|
|
3863
|
+
runTraceSpan.fail?.(err);
|
|
1071
3864
|
runTraceSpan.addEvent?.("kortyx.transition.error", {
|
|
1072
3865
|
transitionTo
|
|
1073
3866
|
});
|
|
1074
|
-
|
|
3867
|
+
write({
|
|
1075
3868
|
type: "error",
|
|
1076
3869
|
message: `Transition failed to '${transitionTo}': ${err instanceof Error ? err.message : String(err)}`
|
|
1077
3870
|
});
|
|
1078
|
-
|
|
3871
|
+
write({ type: "done" });
|
|
1079
3872
|
out.end();
|
|
1080
3873
|
return;
|
|
1081
3874
|
}
|
|
@@ -1105,6 +3898,10 @@ async function orchestrateGraphStream({
|
|
|
1105
3898
|
}
|
|
1106
3899
|
const shouldKeepFrameworkState = Boolean(pendingRecordToken) || Boolean(workflowFinalState?.awaitingHumanInput);
|
|
1107
3900
|
if (!shouldKeepFrameworkState) {
|
|
3901
|
+
workflowFinalState = await validateExecutionOutput(
|
|
3902
|
+
workflowFinalState,
|
|
3903
|
+
selectWorkflow
|
|
3904
|
+
);
|
|
1108
3905
|
try {
|
|
1109
3906
|
if (frameworkAdapter?.cleanupRun) {
|
|
1110
3907
|
await frameworkAdapter.cleanupRun(
|
|
@@ -1121,6 +3918,12 @@ async function orchestrateGraphStream({
|
|
|
1121
3918
|
console.error("[orchestrator] framework cleanup failed", e);
|
|
1122
3919
|
}
|
|
1123
3920
|
}
|
|
3921
|
+
outcomeState = workflowFinalState;
|
|
3922
|
+
if (!emitOutput && !shouldKeepFrameworkState)
|
|
3923
|
+
accumulatedOutput = JSON.stringify(workflowFinalState.data).slice(
|
|
3924
|
+
0,
|
|
3925
|
+
65536
|
|
3926
|
+
);
|
|
1124
3927
|
finished = true;
|
|
1125
3928
|
runTraceSpan.setAttributes?.({
|
|
1126
3929
|
"kortyx.run.awaiting_human_input": shouldKeepFrameworkState,
|
|
@@ -1128,6 +3931,7 @@ async function orchestrateGraphStream({
|
|
|
1128
3931
|
});
|
|
1129
3932
|
runTraceSpan.end?.(runTraceEndArgs());
|
|
1130
3933
|
const resolvedSessionId2 = config?.session?.id || sessionId || "";
|
|
3934
|
+
await sealPendingSnapshots();
|
|
1131
3935
|
if (resolvedSessionId2 && frameworkAdapter?.sessionCheckpoints) {
|
|
1132
3936
|
try {
|
|
1133
3937
|
const checkpoint = await frameworkAdapter.sessionCheckpoints.append(
|
|
@@ -1142,7 +3946,8 @@ async function orchestrateGraphStream({
|
|
|
1142
3946
|
pendingRequests: Array.from(activePendingRequests.values())
|
|
1143
3947
|
}
|
|
1144
3948
|
);
|
|
1145
|
-
|
|
3949
|
+
sessionCheckpointId = checkpoint.id;
|
|
3950
|
+
write({
|
|
1146
3951
|
type: "checkpoint",
|
|
1147
3952
|
id: checkpoint.id,
|
|
1148
3953
|
sessionId: checkpoint.sessionId,
|
|
@@ -1160,15 +3965,23 @@ async function orchestrateGraphStream({
|
|
|
1160
3965
|
}
|
|
1161
3966
|
});
|
|
1162
3967
|
} catch (error) {
|
|
3968
|
+
outcomeError = error;
|
|
1163
3969
|
console.error("[orchestrator] session checkpoint failed", error);
|
|
1164
3970
|
}
|
|
1165
3971
|
}
|
|
1166
|
-
|
|
3972
|
+
write({ type: "done", data: workflowFinalState });
|
|
1167
3973
|
out.end();
|
|
1168
3974
|
return;
|
|
1169
3975
|
}
|
|
1170
3976
|
const naturalGraphSnapshot = await readLatestGraphSnapshot();
|
|
1171
|
-
|
|
3977
|
+
let naturalFinalState = naturalGraphSnapshot?.state ?? currentState;
|
|
3978
|
+
if (!pendingRecordToken && !naturalFinalState.awaitingHumanInput) {
|
|
3979
|
+
naturalFinalState = await validateExecutionOutput(
|
|
3980
|
+
naturalFinalState,
|
|
3981
|
+
selectWorkflow
|
|
3982
|
+
);
|
|
3983
|
+
}
|
|
3984
|
+
outcomeState = naturalFinalState;
|
|
1172
3985
|
if (pendingRequestWrites.length > 0) {
|
|
1173
3986
|
await Promise.all(pendingRequestWrites);
|
|
1174
3987
|
}
|
|
@@ -1189,6 +4002,7 @@ async function orchestrateGraphStream({
|
|
|
1189
4002
|
}
|
|
1190
4003
|
}
|
|
1191
4004
|
const resolvedSessionId = config?.session?.id || sessionId || "";
|
|
4005
|
+
await sealPendingSnapshots();
|
|
1192
4006
|
if (resolvedSessionId && frameworkAdapter?.sessionCheckpoints) {
|
|
1193
4007
|
try {
|
|
1194
4008
|
const checkpoint = await frameworkAdapter.sessionCheckpoints.append({
|
|
@@ -1201,7 +4015,8 @@ async function orchestrateGraphStream({
|
|
|
1201
4015
|
structuredStreamIds: Array.from(structuredStreamIds),
|
|
1202
4016
|
pendingRequests: Array.from(activePendingRequests.values())
|
|
1203
4017
|
});
|
|
1204
|
-
|
|
4018
|
+
sessionCheckpointId = checkpoint.id;
|
|
4019
|
+
write({
|
|
1205
4020
|
type: "checkpoint",
|
|
1206
4021
|
id: checkpoint.id,
|
|
1207
4022
|
sessionId: checkpoint.sessionId,
|
|
@@ -1219,14 +4034,16 @@ async function orchestrateGraphStream({
|
|
|
1219
4034
|
}
|
|
1220
4035
|
});
|
|
1221
4036
|
} catch (error) {
|
|
4037
|
+
outcomeError = error;
|
|
1222
4038
|
console.error("[orchestrator] session checkpoint failed", error);
|
|
1223
4039
|
}
|
|
1224
4040
|
}
|
|
1225
4041
|
runTraceSpan.setAttributes?.({
|
|
1226
4042
|
"kortyx.run.final_workflow": String(naturalFinalState.currentWorkflow)
|
|
1227
4043
|
});
|
|
4044
|
+
finished = true;
|
|
1228
4045
|
runTraceSpan.end?.(runTraceEndArgs());
|
|
1229
|
-
|
|
4046
|
+
write({ type: "done", data: naturalFinalState });
|
|
1230
4047
|
out.end();
|
|
1231
4048
|
return;
|
|
1232
4049
|
}
|
|
@@ -1234,7 +4051,7 @@ async function orchestrateGraphStream({
|
|
|
1234
4051
|
const emitTraceChunk = () => {
|
|
1235
4052
|
const traceContext = traceAdapter?.getActiveContext?.();
|
|
1236
4053
|
if (!traceContext) return;
|
|
1237
|
-
|
|
4054
|
+
write({
|
|
1238
4055
|
type: "trace",
|
|
1239
4056
|
traceId: traceContext.traceId,
|
|
1240
4057
|
spanId: traceContext.spanId,
|
|
@@ -1250,16 +4067,165 @@ async function orchestrateGraphStream({
|
|
|
1250
4067
|
emitTraceChunk();
|
|
1251
4068
|
return runLoop(runTraceSpan);
|
|
1252
4069
|
}) : runLoop(fallbackRunSpan ?? {});
|
|
1253
|
-
|
|
4070
|
+
const finishCancelled = async () => {
|
|
4071
|
+
emitTelemetryEvent({
|
|
4072
|
+
config,
|
|
4073
|
+
type: "run.cancelled",
|
|
4074
|
+
correlation: { runId, workflowId: outcomeState.currentWorkflow },
|
|
4075
|
+
payload: { reason: "execution_aborted" },
|
|
4076
|
+
flush: true
|
|
4077
|
+
});
|
|
4078
|
+
await Promise.allSettled(pendingRequestWrites);
|
|
4079
|
+
await Promise.allSettled(
|
|
4080
|
+
[...activePendingRequests.keys()].map(
|
|
4081
|
+
(token) => pendingStore?.delete(token)
|
|
4082
|
+
)
|
|
4083
|
+
);
|
|
4084
|
+
activePendingRequests.clear();
|
|
4085
|
+
try {
|
|
4086
|
+
await frameworkAdapter?.cleanupRun?.(runId, [...namespacesUsed]);
|
|
4087
|
+
} catch (cleanupError) {
|
|
4088
|
+
console.error("[cancel:cleanupRun]", cleanupError);
|
|
4089
|
+
}
|
|
4090
|
+
outcomeError = (0, import_core.createExecutionCancelledError)();
|
|
4091
|
+
fallbackRunSpan?.setAttributes?.({ "kortyx.run.cancelled": true });
|
|
4092
|
+
fallbackRunSpan?.end?.();
|
|
4093
|
+
write({ type: "cancelled", runId, reason: "Execution cancelled." });
|
|
4094
|
+
write({ type: "done" });
|
|
4095
|
+
finished = true;
|
|
4096
|
+
out.end();
|
|
4097
|
+
};
|
|
4098
|
+
runPromise.catch(async (err) => {
|
|
4099
|
+
if (abortSignal?.aborted || (0, import_core.isExecutionCancelled)(err)) {
|
|
4100
|
+
await finishCancelled();
|
|
4101
|
+
return;
|
|
4102
|
+
}
|
|
4103
|
+
if ((0, import_core.isExecutionLimitReached)(err)) {
|
|
4104
|
+
try {
|
|
4105
|
+
const snapshot = frameworkAdapter && await (0, import_runtime.captureGraphSnapshot)(frameworkAdapter.checkpointer, runId);
|
|
4106
|
+
(0, import_core.throwIfExecutionAborted)(abortSignal);
|
|
4107
|
+
if (!snapshot)
|
|
4108
|
+
throw new Error(
|
|
4109
|
+
"Execution limit reached without a resumable checkpoint."
|
|
4110
|
+
);
|
|
4111
|
+
const savedState = snapshot.checkpoint.channel_values;
|
|
4112
|
+
const pausedState = {
|
|
4113
|
+
...savedState,
|
|
4114
|
+
config: {
|
|
4115
|
+
...savedState.config,
|
|
4116
|
+
...config,
|
|
4117
|
+
executionBudget: budget
|
|
4118
|
+
},
|
|
4119
|
+
awaitingHumanInput: true
|
|
4120
|
+
};
|
|
4121
|
+
const patch = err.__kortyxHookStatePatch;
|
|
4122
|
+
currentState = pausedState;
|
|
4123
|
+
outcomeState = pausedState;
|
|
4124
|
+
const limitReached = err.limitReached;
|
|
4125
|
+
write({ type: "limit-reached", runId, ...limitReached });
|
|
4126
|
+
emitTelemetryEvent({
|
|
4127
|
+
config,
|
|
4128
|
+
type: "run.limit_reached",
|
|
4129
|
+
payload: limitReached
|
|
4130
|
+
});
|
|
4131
|
+
await persistAndEmitInterrupt({
|
|
4132
|
+
node: String(
|
|
4133
|
+
err.nodeId ?? savedState.lastNode
|
|
4134
|
+
),
|
|
4135
|
+
workflow: String(savedState.currentWorkflow),
|
|
4136
|
+
input: {
|
|
4137
|
+
kind: "choice",
|
|
4138
|
+
question: "Limit reached \u2014 Continue?",
|
|
4139
|
+
options: [{ id: "continue", label: "Continue" }],
|
|
4140
|
+
meta: {
|
|
4141
|
+
__kortyxExecutionLimit: true,
|
|
4142
|
+
executionLimit: limitReached,
|
|
4143
|
+
...patch ? { __kortyxResumeStatePatch: patch } : {}
|
|
4144
|
+
}
|
|
4145
|
+
}
|
|
4146
|
+
});
|
|
4147
|
+
await Promise.all(pendingRequestWrites);
|
|
4148
|
+
(0, import_core.throwIfExecutionAborted)(abortSignal);
|
|
4149
|
+
if (outcomeError) throw outcomeError;
|
|
4150
|
+
const request = activePendingRequests.get(pendingRecordToken);
|
|
4151
|
+
const sealed = {
|
|
4152
|
+
...request,
|
|
4153
|
+
state: pausedState,
|
|
4154
|
+
graphCheckpointId: snapshot.checkpoint.id,
|
|
4155
|
+
graphSnapshot: snapshot
|
|
4156
|
+
};
|
|
4157
|
+
activePendingRequests.set(request.token, sealed);
|
|
4158
|
+
await pendingStore?.update(request.token, sealed);
|
|
4159
|
+
const checkpoint = await frameworkAdapter.sessionCheckpoints.append({
|
|
4160
|
+
sessionId,
|
|
4161
|
+
runId,
|
|
4162
|
+
graphCheckpointId: snapshot.checkpoint.id,
|
|
4163
|
+
workflow: String(pausedState.currentWorkflow),
|
|
4164
|
+
state: pausedState,
|
|
4165
|
+
nodes: Array.from(touchedNodes),
|
|
4166
|
+
structuredStreamIds: Array.from(structuredStreamIds),
|
|
4167
|
+
pendingRequests: [sealed],
|
|
4168
|
+
label: "Execution limit reached"
|
|
4169
|
+
});
|
|
4170
|
+
(0, import_core.throwIfExecutionAborted)(abortSignal);
|
|
4171
|
+
sessionCheckpointId = checkpoint.id;
|
|
4172
|
+
write({
|
|
4173
|
+
type: "checkpoint",
|
|
4174
|
+
id: checkpoint.id,
|
|
4175
|
+
sessionId: checkpoint.sessionId,
|
|
4176
|
+
turnIndex: checkpoint.turnIndex,
|
|
4177
|
+
label: checkpoint.label
|
|
4178
|
+
});
|
|
4179
|
+
emitTelemetryEvent({
|
|
4180
|
+
config,
|
|
4181
|
+
type: "session.checkpointed",
|
|
4182
|
+
payload: {
|
|
4183
|
+
checkpointId: checkpoint.id,
|
|
4184
|
+
turnIndex: checkpoint.turnIndex,
|
|
4185
|
+
nodes: checkpoint.nodes
|
|
4186
|
+
}
|
|
4187
|
+
});
|
|
4188
|
+
fallbackRunSpan?.setAttributes?.({
|
|
4189
|
+
"kortyx.run.awaiting_human_input": true,
|
|
4190
|
+
"kortyx.run.limit_reached": limitReached.limit
|
|
4191
|
+
});
|
|
4192
|
+
fallbackRunSpan?.end?.();
|
|
4193
|
+
finished = true;
|
|
4194
|
+
write({ type: "done" });
|
|
4195
|
+
out.end();
|
|
4196
|
+
return;
|
|
4197
|
+
} catch (pauseError) {
|
|
4198
|
+
if (abortSignal?.aborted || (0, import_core.isExecutionCancelled)(pauseError)) {
|
|
4199
|
+
await finishCancelled();
|
|
4200
|
+
return;
|
|
4201
|
+
}
|
|
4202
|
+
await Promise.allSettled(
|
|
4203
|
+
[...activePendingRequests.keys()].map(
|
|
4204
|
+
(token) => pendingStore?.delete(token)
|
|
4205
|
+
)
|
|
4206
|
+
);
|
|
4207
|
+
activePendingRequests.clear();
|
|
4208
|
+
err = pauseError;
|
|
4209
|
+
}
|
|
4210
|
+
}
|
|
4211
|
+
outcomeError = err;
|
|
1254
4212
|
fallbackRunSpan?.fail?.(err);
|
|
1255
4213
|
emitResumeFailure(err);
|
|
1256
4214
|
console.error("[error:orchestrateGraphStream]", err);
|
|
1257
|
-
|
|
4215
|
+
write({
|
|
1258
4216
|
type: "error",
|
|
1259
4217
|
message: err instanceof Error ? err.message : String(err)
|
|
1260
4218
|
});
|
|
1261
|
-
|
|
4219
|
+
write({ type: "done" });
|
|
4220
|
+
finished = true;
|
|
1262
4221
|
out.end();
|
|
4222
|
+
}).finally(() => {
|
|
4223
|
+
onOutcome?.({
|
|
4224
|
+
state: outcomeState,
|
|
4225
|
+
pending: pendingRecordToken ? activePendingRequests.get(pendingRecordToken) : void 0,
|
|
4226
|
+
checkpointId: sessionCheckpointId,
|
|
4227
|
+
...outcomeError ? { error: outcomeError } : {}
|
|
4228
|
+
});
|
|
1263
4229
|
});
|
|
1264
4230
|
return out;
|
|
1265
4231
|
}
|
|
@@ -1290,7 +4256,13 @@ function parseResumeMeta(msg) {
|
|
|
1290
4256
|
return { token, requestId, selected, cancel };
|
|
1291
4257
|
}
|
|
1292
4258
|
async function tryPrepareResumeStream({
|
|
4259
|
+
limits,
|
|
4260
|
+
abortSignal,
|
|
1293
4261
|
lastMessage,
|
|
4262
|
+
meta: suppliedMeta,
|
|
4263
|
+
emitOutput,
|
|
4264
|
+
onOutcome,
|
|
4265
|
+
validatePending,
|
|
1294
4266
|
sessionId,
|
|
1295
4267
|
config,
|
|
1296
4268
|
selectWorkflow,
|
|
@@ -1299,17 +4271,38 @@ async function tryPrepareResumeStream({
|
|
|
1299
4271
|
applyResumeSelection,
|
|
1300
4272
|
frameworkAdapter
|
|
1301
4273
|
}) {
|
|
1302
|
-
const meta = parseResumeMeta(lastMessage);
|
|
4274
|
+
const meta = suppliedMeta ?? parseResumeMeta(lastMessage);
|
|
1303
4275
|
if (!meta) return null;
|
|
1304
4276
|
const store = frameworkAdapter?.pendingRequests;
|
|
1305
4277
|
if (!store) return null;
|
|
1306
4278
|
const pending = await store.get(meta.token);
|
|
1307
4279
|
if (!pending || pending.requestId !== meta.requestId) {
|
|
1308
|
-
|
|
1309
|
-
|
|
4280
|
+
throw new Error(
|
|
4281
|
+
"Interrupt is expired, already consumed, or does not match the request."
|
|
1310
4282
|
);
|
|
1311
|
-
return null;
|
|
1312
4283
|
}
|
|
4284
|
+
if (pending.sessionId && pending.sessionId !== sessionId)
|
|
4285
|
+
throw new Error("Interrupt belongs to another session.");
|
|
4286
|
+
await validatePending?.(pending);
|
|
4287
|
+
const isLimitPause = Boolean(pending.schema.meta?.__kortyxExecutionLimit);
|
|
4288
|
+
const savedBudget = pending.state?.config?.executionBudget;
|
|
4289
|
+
const resumedBudget = savedBudget && isLimitPause && !meta.cancel ? (0, import_core2.restartExecutionBudget)(savedBudget, limits) : savedBudget;
|
|
4290
|
+
if (abortSignal?.aborted) {
|
|
4291
|
+
onOutcome?.({
|
|
4292
|
+
state: pending.state,
|
|
4293
|
+
error: (0, import_core2.createExecutionCancelledError)()
|
|
4294
|
+
});
|
|
4295
|
+
return (async function* () {
|
|
4296
|
+
yield {
|
|
4297
|
+
type: "cancelled",
|
|
4298
|
+
runId: pending.runId,
|
|
4299
|
+
reason: "Execution cancelled."
|
|
4300
|
+
};
|
|
4301
|
+
yield { type: "done" };
|
|
4302
|
+
})();
|
|
4303
|
+
}
|
|
4304
|
+
if (store.take && !await store.take(meta.token))
|
|
4305
|
+
throw new Error("Interrupt has already been resumed or cancelled.");
|
|
1313
4306
|
if (meta.cancel) {
|
|
1314
4307
|
await store.delete(pending.token);
|
|
1315
4308
|
emitTelemetryEvent({
|
|
@@ -1327,109 +4320,412 @@ async function tryPrepareResumeStream({
|
|
|
1327
4320
|
},
|
|
1328
4321
|
flush: true
|
|
1329
4322
|
});
|
|
1330
|
-
|
|
4323
|
+
onOutcome?.({ state: pending.state, cancelled: true });
|
|
4324
|
+
return (async function* () {
|
|
4325
|
+
yield { type: "done" };
|
|
4326
|
+
})();
|
|
1331
4327
|
}
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
)
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
data: {
|
|
1354
|
-
...pendingData,
|
|
1355
|
-
...resumeDataPatch
|
|
1356
|
-
}
|
|
1357
|
-
};
|
|
1358
|
-
const wf = await selectWorkflow(resumedState.currentWorkflow);
|
|
1359
|
-
const telemetryConfig = prepareWorkflowTelemetry({
|
|
1360
|
-
config,
|
|
1361
|
-
workflow: wf,
|
|
1362
|
-
runId: pending.runId,
|
|
1363
|
-
sessionId,
|
|
1364
|
-
knownWorkflowIds
|
|
1365
|
-
});
|
|
1366
|
-
resumedState.config = telemetryConfig;
|
|
1367
|
-
const resumeUpdate = {};
|
|
1368
|
-
if (Object.keys(resumeDataPatch).length > 0) {
|
|
1369
|
-
resumeUpdate.data = {
|
|
1370
|
-
...pendingData,
|
|
1371
|
-
...resumeDataPatch
|
|
4328
|
+
try {
|
|
4329
|
+
const resumeData = isLimitPause ? {} : applyResumeSelection ? applyResumeSelection({ pending, selected: meta.selected }) : meta.selected?.length ? { coordinates: String(meta.selected[0]) } : {};
|
|
4330
|
+
const resumeDataPatch = isRecord4(resumeData) ? resumeData : {};
|
|
4331
|
+
const pendingMeta = isRecord4(pending.schema?.meta) ? pending.schema.meta : {};
|
|
4332
|
+
const resumeStatePatch = isRecord4(pendingMeta.__kortyxResumeStatePatch) ? pendingMeta.__kortyxResumeStatePatch : void 0;
|
|
4333
|
+
const pendingData = isRecord4(pending.state?.data) ? pending.state?.data : {};
|
|
4334
|
+
const workflowId = typeof pending.workflow === "string" && pending.workflow.trim() ? pending.workflow : typeof defaultWorkflowId === "string" && defaultWorkflowId.trim() ? defaultWorkflowId : "job-search";
|
|
4335
|
+
const resumedState = {
|
|
4336
|
+
// For static breakpoints, resume with null input (set in orchestrator),
|
|
4337
|
+
// and stash the user selection into data so the next node can read it.
|
|
4338
|
+
input: pending.state ? pending.state.input : "",
|
|
4339
|
+
lastNode: "__start__",
|
|
4340
|
+
currentWorkflow: workflowId,
|
|
4341
|
+
config,
|
|
4342
|
+
runtime: {},
|
|
4343
|
+
conversationHistory: [],
|
|
4344
|
+
awaitingHumanInput: false,
|
|
4345
|
+
data: {
|
|
4346
|
+
...pendingData,
|
|
4347
|
+
...resumeDataPatch
|
|
4348
|
+
}
|
|
1372
4349
|
};
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
...resumeValue !== void 0 ? { resumeValue } : {},
|
|
1384
|
-
...resumeCheckpointId ? { resumeCheckpointId } : {},
|
|
1385
|
-
...hasResumeUpdate ? { resumeUpdate } : {}
|
|
1386
|
-
});
|
|
1387
|
-
await store.delete(pending.token);
|
|
1388
|
-
const response = responseFromSelection(meta.selected);
|
|
1389
|
-
const telemetry = isRecord4(telemetryConfig.telemetry) ? telemetryConfig.telemetry : {};
|
|
1390
|
-
const responseCaptured = Boolean(
|
|
1391
|
-
response && shouldCaptureTelemetryContent(telemetry.captureContent, "input")
|
|
1392
|
-
);
|
|
1393
|
-
emitTelemetryEvent({
|
|
1394
|
-
config: telemetryConfig,
|
|
1395
|
-
type: "interrupt.resolved",
|
|
1396
|
-
correlation: {
|
|
4350
|
+
const wf = await selectWorkflow(resumedState.currentWorkflow);
|
|
4351
|
+
const telemetryConfig = prepareWorkflowTelemetry({
|
|
4352
|
+
config: {
|
|
4353
|
+
...config,
|
|
4354
|
+
...resumedBudget ? { executionBudget: resumedBudget } : {},
|
|
4355
|
+
...pending.state?.config?.context ? { context: pending.state.config.context } : {},
|
|
4356
|
+
...pending.state?.config?.executionContract ? { executionContract: pending.state.config.executionContract } : {},
|
|
4357
|
+
...pending.state?.config?.executionBranchId ? { executionBranchId: pending.state.config.executionBranchId } : {}
|
|
4358
|
+
},
|
|
4359
|
+
workflow: wf,
|
|
1397
4360
|
runId: pending.runId,
|
|
1398
4361
|
sessionId,
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
4362
|
+
knownWorkflowIds
|
|
4363
|
+
});
|
|
4364
|
+
resumedState.config = telemetryConfig;
|
|
4365
|
+
const resumeUpdate = pending.graphSnapshot ? {
|
|
4366
|
+
config: {
|
|
4367
|
+
...telemetryConfig,
|
|
4368
|
+
context: pending.state?.config?.context ?? telemetryConfig.context
|
|
4369
|
+
}
|
|
4370
|
+
} : {};
|
|
4371
|
+
if (isLimitPause && pending.graphSnapshot) {
|
|
4372
|
+
pending.graphSnapshot.checkpoint.channel_values.config = telemetryConfig;
|
|
4373
|
+
if (resumeStatePatch)
|
|
4374
|
+
pending.graphSnapshot.checkpoint.channel_values.runtime = {
|
|
4375
|
+
...pending.graphSnapshot.checkpoint.channel_values.runtime,
|
|
4376
|
+
...resumeStatePatch
|
|
4377
|
+
};
|
|
4378
|
+
}
|
|
4379
|
+
if (pending.graphSnapshot && frameworkAdapter)
|
|
4380
|
+
await (0, import_runtime2.restoreGraphSnapshot)(
|
|
4381
|
+
frameworkAdapter.checkpointer,
|
|
4382
|
+
pending.graphSnapshot,
|
|
4383
|
+
pending.runId
|
|
4384
|
+
);
|
|
4385
|
+
if (Object.keys(resumeDataPatch).length > 0) {
|
|
4386
|
+
resumeUpdate.data = {
|
|
4387
|
+
...pendingData,
|
|
4388
|
+
...resumeDataPatch
|
|
4389
|
+
};
|
|
4390
|
+
}
|
|
4391
|
+
if (resumeStatePatch) {
|
|
4392
|
+
resumeUpdate.runtime = resumeStatePatch;
|
|
4393
|
+
}
|
|
4394
|
+
const hasResumeUpdate = !isLimitPause && Object.keys(resumeUpdate).length > 0;
|
|
4395
|
+
const resumeValue = isLimitPause ? void 0 : meta.selected?.length && pending.schema.kind === "multi-choice" ? meta.selected.map((x) => String(x)) : meta.selected?.length ? String(meta.selected[0]) : void 0;
|
|
4396
|
+
const resumeCheckpointId = typeof pending.graphCheckpointId === "string" && pending.graphCheckpointId.length > 0 ? pending.graphCheckpointId : void 0;
|
|
4397
|
+
const resumedGraph = await (0, import_runtime2.createExecutionGraph)(wf, {
|
|
1417
4398
|
...telemetryConfig,
|
|
1418
4399
|
resume: true,
|
|
1419
|
-
telemetryInterruptId: pending.requestId,
|
|
1420
|
-
telemetryInterruptNodeId: pending.node,
|
|
1421
4400
|
...resumeValue !== void 0 ? { resumeValue } : {},
|
|
1422
4401
|
...resumeCheckpointId ? { resumeCheckpointId } : {},
|
|
1423
4402
|
...hasResumeUpdate ? { resumeUpdate } : {}
|
|
4403
|
+
});
|
|
4404
|
+
await store.delete(pending.token);
|
|
4405
|
+
const response = responseFromSelection(meta.selected);
|
|
4406
|
+
const telemetry = isRecord4(telemetryConfig.telemetry) ? telemetryConfig.telemetry : {};
|
|
4407
|
+
const responseCaptured = Boolean(
|
|
4408
|
+
response && shouldCaptureTelemetryContent(telemetry.captureContent, "input")
|
|
4409
|
+
);
|
|
4410
|
+
emitTelemetryEvent({
|
|
4411
|
+
config: telemetryConfig,
|
|
4412
|
+
type: "interrupt.resolved",
|
|
4413
|
+
correlation: {
|
|
4414
|
+
runId: pending.runId,
|
|
4415
|
+
sessionId,
|
|
4416
|
+
workflowId,
|
|
4417
|
+
nodeId: pending.node
|
|
4418
|
+
},
|
|
4419
|
+
payload: {
|
|
4420
|
+
interruptId: pending.requestId,
|
|
4421
|
+
resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4422
|
+
resumeOutcome: "resumed",
|
|
4423
|
+
responseCaptured,
|
|
4424
|
+
...responseCaptured && response ? { response } : {}
|
|
4425
|
+
},
|
|
4426
|
+
flush: true
|
|
4427
|
+
});
|
|
4428
|
+
const args = {
|
|
4429
|
+
abortSignal,
|
|
4430
|
+
emitOutput,
|
|
4431
|
+
onOutcome,
|
|
4432
|
+
sessionId,
|
|
4433
|
+
runId: pending.runId,
|
|
4434
|
+
graph: resumedGraph,
|
|
4435
|
+
state: resumedState,
|
|
4436
|
+
config: {
|
|
4437
|
+
...telemetryConfig,
|
|
4438
|
+
resume: true,
|
|
4439
|
+
telemetryInterruptId: pending.requestId,
|
|
4440
|
+
telemetryInterruptNodeId: pending.node,
|
|
4441
|
+
...resumeValue !== void 0 ? { resumeValue } : {},
|
|
4442
|
+
...resumeCheckpointId ? { resumeCheckpointId } : {},
|
|
4443
|
+
...hasResumeUpdate ? { resumeUpdate } : {}
|
|
4444
|
+
},
|
|
4445
|
+
selectWorkflow,
|
|
4446
|
+
knownWorkflowIds,
|
|
4447
|
+
frameworkAdapter
|
|
4448
|
+
};
|
|
4449
|
+
const stream = await orchestrateGraphStream(args);
|
|
4450
|
+
return stream;
|
|
4451
|
+
} catch (error) {
|
|
4452
|
+
await store.save(pending);
|
|
4453
|
+
throw error;
|
|
4454
|
+
}
|
|
4455
|
+
}
|
|
4456
|
+
|
|
4457
|
+
// src/execution/execute.ts
|
|
4458
|
+
var handleSchema = import_zod3.z.object({
|
|
4459
|
+
token: import_zod3.z.string().min(1),
|
|
4460
|
+
requestId: import_zod3.z.string().min(1),
|
|
4461
|
+
sessionId: import_zod3.z.string().min(1),
|
|
4462
|
+
runId: import_zod3.z.string().min(1)
|
|
4463
|
+
}).strict();
|
|
4464
|
+
var responseSchema = import_zod3.z.discriminatedUnion("type", [
|
|
4465
|
+
import_zod3.z.object({ type: import_zod3.z.literal("text"), text: import_zod3.z.string().min(1) }).strict(),
|
|
4466
|
+
import_zod3.z.object({
|
|
4467
|
+
type: import_zod3.z.literal("select"),
|
|
4468
|
+
ids: import_zod3.z.array(import_zod3.z.string().min(1)).min(1)
|
|
4469
|
+
}).strict(),
|
|
4470
|
+
import_zod3.z.object({ type: import_zod3.z.literal("cancel") }).strict()
|
|
4471
|
+
]);
|
|
4472
|
+
function resultFromOutcome(outcome, runId, sessionId) {
|
|
4473
|
+
const usage = outcome.pending?.schema.meta?.__kortyxResumeStatePatch?.tokenUsage ?? outcome.state.runtime?.tokenUsage;
|
|
4474
|
+
const info = {
|
|
4475
|
+
runId,
|
|
4476
|
+
sessionId,
|
|
4477
|
+
...outcome.checkpointId ? { checkpointId: outcome.checkpointId } : {},
|
|
4478
|
+
...usage ? { usage } : {}
|
|
4479
|
+
};
|
|
4480
|
+
if ((0, import_core3.isExecutionCancelled)(outcome.error))
|
|
4481
|
+
return { ...info, status: "cancelled", reason: "Execution cancelled." };
|
|
4482
|
+
if (outcome.error) {
|
|
4483
|
+
const error = outcome.error;
|
|
4484
|
+
return {
|
|
4485
|
+
...info,
|
|
4486
|
+
status: "failed",
|
|
4487
|
+
error: {
|
|
4488
|
+
code: typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : "EXECUTION_FAILED",
|
|
4489
|
+
message: error instanceof Error ? error.message : String(error)
|
|
4490
|
+
}
|
|
4491
|
+
};
|
|
4492
|
+
}
|
|
4493
|
+
if (outcome.cancelled)
|
|
4494
|
+
return { ...info, status: "cancelled", reason: "cancelled_by_client" };
|
|
4495
|
+
if (outcome.pending) {
|
|
4496
|
+
const pending = outcome.pending;
|
|
4497
|
+
const meta = Object.fromEntries(
|
|
4498
|
+
Object.entries(pending.schema.meta ?? {}).filter(
|
|
4499
|
+
([key]) => !key.startsWith("__kortyx")
|
|
4500
|
+
)
|
|
4501
|
+
);
|
|
4502
|
+
return {
|
|
4503
|
+
...info,
|
|
4504
|
+
status: "suspended",
|
|
4505
|
+
...pending.schema.meta?.__kortyxExecutionLimit ? {
|
|
4506
|
+
reason: "limit_reached",
|
|
4507
|
+
limit: pending.schema.meta.executionLimit
|
|
4508
|
+
} : {},
|
|
4509
|
+
interrupt: {
|
|
4510
|
+
workflow: pending.workflow,
|
|
4511
|
+
node: pending.node,
|
|
4512
|
+
input: {
|
|
4513
|
+
...pending.schema,
|
|
4514
|
+
meta,
|
|
4515
|
+
options: pending.options.map(({ id, label, description }) => ({
|
|
4516
|
+
id,
|
|
4517
|
+
label,
|
|
4518
|
+
description
|
|
4519
|
+
}))
|
|
4520
|
+
}
|
|
4521
|
+
},
|
|
4522
|
+
resume: {
|
|
4523
|
+
token: pending.token,
|
|
4524
|
+
requestId: pending.requestId,
|
|
4525
|
+
sessionId,
|
|
4526
|
+
runId
|
|
4527
|
+
}
|
|
4528
|
+
};
|
|
4529
|
+
}
|
|
4530
|
+
return { ...info, status: "completed", data: outcome.state.data ?? {} };
|
|
4531
|
+
}
|
|
4532
|
+
function runtimeConfig(services, sessionId, context) {
|
|
4533
|
+
return {
|
|
4534
|
+
session: { id: sessionId },
|
|
4535
|
+
...context ? { context } : {},
|
|
4536
|
+
getProvider: services.getProvider,
|
|
4537
|
+
checkpointer: services.frameworkAdapter.checkpointer,
|
|
4538
|
+
selectWorkflow: (id) => services.registry.select(id),
|
|
4539
|
+
...services.telemetry ? { telemetry: services.telemetry } : {}
|
|
4540
|
+
};
|
|
4541
|
+
}
|
|
4542
|
+
async function executeWorkflow(services, args) {
|
|
4543
|
+
const select = (id) => services.registry.select(id);
|
|
4544
|
+
const workflow = await resolveExecutionWorkflow(select, args.workflow);
|
|
4545
|
+
const input = parseExecutionInput(workflow, args.input);
|
|
4546
|
+
if (args.sessionId !== void 0 && !args.sessionId.trim())
|
|
4547
|
+
throw new ExecutionRequestError(
|
|
4548
|
+
"INVALID_SESSION",
|
|
4549
|
+
"sessionId must not be empty."
|
|
4550
|
+
);
|
|
4551
|
+
const sessionId = args.sessionId ?? (0, import_runtime3.makeRequestId)("session");
|
|
4552
|
+
const runId = (0, import_runtime3.makeRequestId)("run");
|
|
4553
|
+
const config = prepareWorkflowTelemetry({
|
|
4554
|
+
config: {
|
|
4555
|
+
...runtimeConfig(services, sessionId, args.context),
|
|
4556
|
+
...services.limits || args.limits ? {
|
|
4557
|
+
executionBudget: (0, import_core3.createExecutionBudget)(
|
|
4558
|
+
services.limits,
|
|
4559
|
+
args.limits
|
|
4560
|
+
)
|
|
4561
|
+
} : {},
|
|
4562
|
+
executionContract: { id: workflow.id, version: workflow.version }
|
|
1424
4563
|
},
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
4564
|
+
workflow,
|
|
4565
|
+
runId,
|
|
4566
|
+
sessionId,
|
|
4567
|
+
knownWorkflowIds: services.knownWorkflowIds
|
|
4568
|
+
});
|
|
4569
|
+
const state = await (0, import_runtime3.buildInitialGraphState)({
|
|
4570
|
+
input,
|
|
4571
|
+
runtime: {},
|
|
4572
|
+
config,
|
|
4573
|
+
defaultWorkflowId: workflow.id
|
|
4574
|
+
});
|
|
4575
|
+
return new Promise((resolve2) => {
|
|
4576
|
+
const onOutcome = (outcome) => resolve2(resultFromOutcome(outcome, runId, sessionId));
|
|
4577
|
+
void (async () => {
|
|
4578
|
+
(0, import_core3.throwIfExecutionAborted)(args.abortSignal);
|
|
4579
|
+
const graph = await (0, import_runtime3.createExecutionGraph)(workflow, config);
|
|
4580
|
+
await orchestrateGraphStream({
|
|
4581
|
+
graph,
|
|
4582
|
+
state,
|
|
4583
|
+
config,
|
|
4584
|
+
runId,
|
|
4585
|
+
sessionId,
|
|
4586
|
+
selectWorkflow: select,
|
|
4587
|
+
frameworkAdapter: services.frameworkAdapter,
|
|
4588
|
+
knownWorkflowIds: services.knownWorkflowIds,
|
|
4589
|
+
abortSignal: args.abortSignal,
|
|
4590
|
+
emitOutput: false,
|
|
4591
|
+
onOutcome
|
|
4592
|
+
});
|
|
4593
|
+
})().catch((error) => onOutcome({ state, error }));
|
|
4594
|
+
});
|
|
4595
|
+
}
|
|
4596
|
+
async function resumeWorkflow(services, args) {
|
|
4597
|
+
const parsed = import_zod3.z.object({ resume: handleSchema, response: responseSchema }).safeParse(args);
|
|
4598
|
+
if (!parsed.success)
|
|
4599
|
+
throw new ExecutionRequestError("INVALID_RESUME", parsed.error.message);
|
|
4600
|
+
const { resume: handle, response } = parsed.data;
|
|
4601
|
+
const select = (id) => services.registry.select(id);
|
|
4602
|
+
const workflow = await resolveExecutionWorkflow(select, args.workflow);
|
|
4603
|
+
const validatePending = async (pending) => {
|
|
4604
|
+
const contract = pending.state?.config?.executionContract ?? {
|
|
4605
|
+
id: pending.workflow
|
|
4606
|
+
};
|
|
4607
|
+
if (pending.runId !== handle.runId || contract.id !== workflow.id || contract.version && contract.version !== workflow.version)
|
|
4608
|
+
throw new ExecutionRequestError(
|
|
4609
|
+
"RESUME_MISMATCH",
|
|
4610
|
+
"Resume handle does not match the root execution and workflow version."
|
|
4611
|
+
);
|
|
4612
|
+
if (response.type === "cancel") return;
|
|
4613
|
+
if (response.type === "text") {
|
|
4614
|
+
if (pending.schema.kind !== "text")
|
|
4615
|
+
throw new ExecutionRequestError(
|
|
4616
|
+
"INVALID_RESPONSE",
|
|
4617
|
+
"This interrupt requires a selection."
|
|
4618
|
+
);
|
|
4619
|
+
} else {
|
|
4620
|
+
const multi = pending.schema.kind === "multi-choice" || pending.schema.multiple;
|
|
4621
|
+
if (pending.schema.kind === "text" || !multi && response.ids.length !== 1 || new Set(response.ids).size !== response.ids.length)
|
|
4622
|
+
throw new ExecutionRequestError(
|
|
4623
|
+
"INVALID_RESPONSE",
|
|
4624
|
+
"Invalid selection for this interrupt."
|
|
4625
|
+
);
|
|
4626
|
+
if (pending.options.length && response.ids.some(
|
|
4627
|
+
(id) => !pending.options.some((option) => option.id === id)
|
|
4628
|
+
))
|
|
4629
|
+
throw new ExecutionRequestError(
|
|
4630
|
+
"INVALID_RESPONSE",
|
|
4631
|
+
"Selection contains an unknown option."
|
|
4632
|
+
);
|
|
4633
|
+
}
|
|
1428
4634
|
};
|
|
1429
|
-
|
|
1430
|
-
|
|
4635
|
+
return new Promise((resolve2, reject) => {
|
|
4636
|
+
void tryPrepareResumeStream({
|
|
4637
|
+
limits: args.limits,
|
|
4638
|
+
abortSignal: args.abortSignal,
|
|
4639
|
+
meta: {
|
|
4640
|
+
token: handle.token,
|
|
4641
|
+
requestId: handle.requestId,
|
|
4642
|
+
selected: response.type === "text" ? [response.text] : response.type === "select" ? response.ids : [],
|
|
4643
|
+
cancel: response.type === "cancel"
|
|
4644
|
+
},
|
|
4645
|
+
sessionId: handle.sessionId,
|
|
4646
|
+
config: runtimeConfig(services, handle.sessionId),
|
|
4647
|
+
selectWorkflow: select,
|
|
4648
|
+
frameworkAdapter: services.frameworkAdapter,
|
|
4649
|
+
knownWorkflowIds: services.knownWorkflowIds,
|
|
4650
|
+
validatePending,
|
|
4651
|
+
applyResumeSelection: () => ({}),
|
|
4652
|
+
emitOutput: false,
|
|
4653
|
+
onOutcome: (outcome) => resolve2(resultFromOutcome(outcome, handle.runId, handle.sessionId))
|
|
4654
|
+
}).then((stream) => {
|
|
4655
|
+
if (!stream)
|
|
4656
|
+
reject(
|
|
4657
|
+
new ExecutionRequestError(
|
|
4658
|
+
"INVALID_RESUME",
|
|
4659
|
+
"No waiting execution found."
|
|
4660
|
+
)
|
|
4661
|
+
);
|
|
4662
|
+
}).catch(
|
|
4663
|
+
(error) => reject(
|
|
4664
|
+
error instanceof ExecutionRequestError ? error : new ExecutionRequestError(
|
|
4665
|
+
"INVALID_RESUME",
|
|
4666
|
+
error instanceof Error ? error.message : String(error)
|
|
4667
|
+
)
|
|
4668
|
+
)
|
|
4669
|
+
);
|
|
4670
|
+
});
|
|
4671
|
+
}
|
|
4672
|
+
|
|
4673
|
+
// src/telemetry/workflow-call-branch.ts
|
|
4674
|
+
var import_node_crypto3 = require("crypto");
|
|
4675
|
+
var import_hooks = __toESM(require_dist());
|
|
4676
|
+
function restoreWorkflowCallBranch(requests, telemetry, sourceRunId) {
|
|
4677
|
+
const branchId = (0, import_node_crypto3.randomUUID)();
|
|
4678
|
+
const emitted = /* @__PURE__ */ new Set();
|
|
4679
|
+
for (const request of requests) {
|
|
4680
|
+
if (request.state)
|
|
4681
|
+
request.state.config = {
|
|
4682
|
+
...request.state.config,
|
|
4683
|
+
executionBranchId: branchId
|
|
4684
|
+
};
|
|
4685
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4686
|
+
const visit = (value) => {
|
|
4687
|
+
if (!value || typeof value !== "object" || seen.has(value)) return;
|
|
4688
|
+
seen.add(value);
|
|
4689
|
+
const record = value;
|
|
4690
|
+
if (typeof record.invocationId === "string" && typeof record.fingerprint === "string" && Array.isArray(record.interrupts)) {
|
|
4691
|
+
const previous = record.telemetry;
|
|
4692
|
+
if (previous) {
|
|
4693
|
+
record.sequence = 0;
|
|
4694
|
+
record.telemetry = { ...previous, branchId };
|
|
4695
|
+
if (!emitted.has(record.invocationId)) {
|
|
4696
|
+
emitted.add(record.invocationId);
|
|
4697
|
+
(0, import_hooks.emitWorkflowCall)(
|
|
4698
|
+
telemetry ? {
|
|
4699
|
+
...telemetry,
|
|
4700
|
+
correlation: {
|
|
4701
|
+
runId: request.runId,
|
|
4702
|
+
workflowId: request.workflow,
|
|
4703
|
+
...request.sessionId ? { sessionId: request.sessionId } : {}
|
|
4704
|
+
}
|
|
4705
|
+
} : void 0,
|
|
4706
|
+
"workflow.call.restored",
|
|
4707
|
+
{
|
|
4708
|
+
...previous,
|
|
4709
|
+
branchId,
|
|
4710
|
+
sourceBranchId: previous.branchId,
|
|
4711
|
+
sourceRunId,
|
|
4712
|
+
sequence: 0,
|
|
4713
|
+
status: record.status
|
|
4714
|
+
}
|
|
4715
|
+
);
|
|
4716
|
+
}
|
|
4717
|
+
}
|
|
4718
|
+
}
|
|
4719
|
+
for (const child of Object.values(record)) visit(child);
|
|
4720
|
+
};
|
|
4721
|
+
visit(request);
|
|
4722
|
+
}
|
|
4723
|
+
return branchId;
|
|
1431
4724
|
}
|
|
1432
4725
|
|
|
4726
|
+
// src/chat/process-chat.ts
|
|
4727
|
+
var import_runtime4 = require("@kortyx/runtime");
|
|
4728
|
+
|
|
1433
4729
|
// src/utils/extract-latest-message.ts
|
|
1434
4730
|
function extractLatestUserMessage(messages) {
|
|
1435
4731
|
if (!messages || messages.length === 0) return "";
|
|
@@ -1445,6 +4741,7 @@ function extractLatestUserMessage(messages) {
|
|
|
1445
4741
|
// src/chat/process-chat.ts
|
|
1446
4742
|
async function streamChat({
|
|
1447
4743
|
messages,
|
|
4744
|
+
abortSignal,
|
|
1448
4745
|
options,
|
|
1449
4746
|
sessionId,
|
|
1450
4747
|
defaultWorkflowId,
|
|
@@ -1457,7 +4754,7 @@ async function streamChat({
|
|
|
1457
4754
|
applyResumeSelection
|
|
1458
4755
|
}) {
|
|
1459
4756
|
const config = await loadRuntimeConfig(options);
|
|
1460
|
-
let
|
|
4757
|
+
let runtimeConfig2 = {
|
|
1461
4758
|
...config,
|
|
1462
4759
|
getProvider,
|
|
1463
4760
|
...frameworkAdapter ? { checkpointer: frameworkAdapter.checkpointer } : {}
|
|
@@ -1468,6 +4765,7 @@ async function streamChat({
|
|
|
1468
4765
|
"streamChat requires selectWorkflow or workflowRegistry to resolve workflows."
|
|
1469
4766
|
);
|
|
1470
4767
|
}
|
|
4768
|
+
runtimeConfig2.selectWorkflow = workflowSelector;
|
|
1471
4769
|
const fallbackSessionId = options?.sessionId;
|
|
1472
4770
|
const resolvedSessionId = sessionId || fallbackSessionId || "anonymous-session";
|
|
1473
4771
|
const last = messages[messages.length - 1];
|
|
@@ -1489,16 +4787,11 @@ async function streamChat({
|
|
|
1489
4787
|
if (requestedWorkflowId.trim() === "") delete runtime.requestedWorkflow;
|
|
1490
4788
|
else runtime.requestedWorkflow = requestedWorkflowId;
|
|
1491
4789
|
}
|
|
1492
|
-
const baseState = await (0, import_runtime3.buildInitialGraphState)({
|
|
1493
|
-
input,
|
|
1494
|
-
config: runtimeConfig,
|
|
1495
|
-
runtime,
|
|
1496
|
-
...defaultWorkflowId ? { defaultWorkflowId } : {}
|
|
1497
|
-
});
|
|
1498
4790
|
const resumeStream = await tryPrepareResumeStream({
|
|
4791
|
+
abortSignal,
|
|
1499
4792
|
lastMessage: last,
|
|
1500
4793
|
sessionId: resolvedSessionId,
|
|
1501
|
-
config:
|
|
4794
|
+
config: runtimeConfig2,
|
|
1502
4795
|
selectWorkflow: workflowSelector,
|
|
1503
4796
|
knownWorkflowIds,
|
|
1504
4797
|
...frameworkAdapter ? { frameworkAdapter } : {},
|
|
@@ -1506,7 +4799,13 @@ async function streamChat({
|
|
|
1506
4799
|
...applyResumeSelection ? { applyResumeSelection } : {}
|
|
1507
4800
|
});
|
|
1508
4801
|
if (resumeStream) return resumeStream;
|
|
1509
|
-
const
|
|
4802
|
+
const baseState = await (0, import_runtime4.buildInitialGraphState)({
|
|
4803
|
+
input,
|
|
4804
|
+
config: runtimeConfig2,
|
|
4805
|
+
runtime,
|
|
4806
|
+
...defaultWorkflowId ? { defaultWorkflowId } : {}
|
|
4807
|
+
});
|
|
4808
|
+
const runId = (0, import_runtime4.makeRequestId)("run");
|
|
1510
4809
|
let headCheckpoint = !isResumeRequest && frameworkAdapter?.sessionCheckpoints ? await frameworkAdapter.sessionCheckpoints.getHead(resolvedSessionId) : null;
|
|
1511
4810
|
if (!isResumeRequest && !headCheckpoint && frameworkAdapter?.sessionCheckpoints) {
|
|
1512
4811
|
headCheckpoint = await frameworkAdapter.sessionCheckpoints.append({
|
|
@@ -1526,18 +4825,26 @@ async function streamChat({
|
|
|
1526
4825
|
}
|
|
1527
4826
|
const currentWorkflow = requestedWorkflowId ? baseState.currentWorkflow : headCheckpoint?.state.currentWorkflow ?? baseState.currentWorkflow;
|
|
1528
4827
|
const selectedWorkflow = await workflowSelector(currentWorkflow);
|
|
1529
|
-
|
|
1530
|
-
config:
|
|
4828
|
+
runtimeConfig2 = prepareWorkflowTelemetry({
|
|
4829
|
+
config: runtimeConfig2,
|
|
1531
4830
|
workflow: selectedWorkflow,
|
|
1532
4831
|
runId,
|
|
1533
4832
|
sessionId: resolvedSessionId,
|
|
1534
4833
|
knownWorkflowIds
|
|
1535
4834
|
});
|
|
1536
|
-
|
|
4835
|
+
runtimeConfig2 = {
|
|
4836
|
+
...runtimeConfig2,
|
|
4837
|
+
executionContract: {
|
|
4838
|
+
id: selectedWorkflow.id,
|
|
4839
|
+
version: selectedWorkflow.version
|
|
4840
|
+
}
|
|
4841
|
+
};
|
|
4842
|
+
const parsedInput = selectedWorkflow.inputSchema ? parseExecutionInput(selectedWorkflow, input) : input;
|
|
4843
|
+
const graph = await (0, import_runtime4.createExecutionGraph)(selectedWorkflow, runtimeConfig2);
|
|
1537
4844
|
const initialState = headCheckpoint ? {
|
|
1538
4845
|
...headCheckpoint.state,
|
|
1539
|
-
input,
|
|
1540
|
-
config:
|
|
4846
|
+
input: parsedInput,
|
|
4847
|
+
config: runtimeConfig2,
|
|
1541
4848
|
currentWorkflow,
|
|
1542
4849
|
awaitingHumanInput: false,
|
|
1543
4850
|
runtime: {
|
|
@@ -1545,13 +4852,19 @@ async function streamChat({
|
|
|
1545
4852
|
...runtime.requestedWorkflow ? { requestedWorkflow: runtime.requestedWorkflow } : {},
|
|
1546
4853
|
...runtime.priorMessages ? { priorMessages: runtime.priorMessages } : {}
|
|
1547
4854
|
}
|
|
1548
|
-
} : {
|
|
4855
|
+
} : {
|
|
4856
|
+
...baseState,
|
|
4857
|
+
input: parsedInput,
|
|
4858
|
+
config: runtimeConfig2,
|
|
4859
|
+
currentWorkflow
|
|
4860
|
+
};
|
|
1549
4861
|
const orchestratedStream = await orchestrateGraphStream({
|
|
4862
|
+
abortSignal,
|
|
1550
4863
|
sessionId: resolvedSessionId,
|
|
1551
4864
|
runId,
|
|
1552
4865
|
graph,
|
|
1553
4866
|
state: initialState,
|
|
1554
|
-
config:
|
|
4867
|
+
config: runtimeConfig2,
|
|
1555
4868
|
selectWorkflow: workflowSelector,
|
|
1556
4869
|
knownWorkflowIds,
|
|
1557
4870
|
...frameworkAdapter ? { frameworkAdapter } : {}
|
|
@@ -1560,19 +4873,22 @@ async function streamChat({
|
|
|
1560
4873
|
}
|
|
1561
4874
|
|
|
1562
4875
|
// src/chat/create-agent.ts
|
|
1563
|
-
var agentProcessOptionsSchema =
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
4876
|
+
var agentProcessOptionsSchema = import_zod4.z.object({
|
|
4877
|
+
limits: import_core4.ExecutionLimitsSchema.optional(),
|
|
4878
|
+
abortSignal: import_zod4.z.custom((value) => value instanceof AbortSignal).optional(),
|
|
4879
|
+
sessionId: import_zod4.z.string().optional(),
|
|
4880
|
+
workflowId: import_zod4.z.string().optional(),
|
|
4881
|
+
context: import_zod4.z.record(import_zod4.z.string(), import_zod4.z.unknown()).optional()
|
|
1567
4882
|
}).strict();
|
|
1568
|
-
var createAgentArgsBaseSchema =
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
4883
|
+
var createAgentArgsBaseSchema = import_zod4.z.object({
|
|
4884
|
+
limits: import_core4.ExecutionLimitsSchema.optional(),
|
|
4885
|
+
getProvider: import_zod4.z.unknown().optional(),
|
|
4886
|
+
workflows: import_zod4.z.array(import_zod4.z.unknown()).optional(),
|
|
4887
|
+
workflowsDir: import_zod4.z.string().optional(),
|
|
4888
|
+
workflowRegistry: import_zod4.z.unknown().optional(),
|
|
4889
|
+
defaultWorkflowId: import_zod4.z.string().optional(),
|
|
4890
|
+
frameworkAdapter: import_zod4.z.unknown().optional(),
|
|
4891
|
+
telemetry: import_zod4.z.unknown().optional()
|
|
1576
4892
|
}).strict();
|
|
1577
4893
|
var createAgentArgsSchema = createAgentArgsBaseSchema.superRefine(
|
|
1578
4894
|
(value, ctx) => {
|
|
@@ -1617,7 +4933,7 @@ var hydratePendingGraphCheckpoint = async (frameworkAdapter, request) => {
|
|
|
1617
4933
|
return graphCheckpointId ? { ...request, graphCheckpointId } : request;
|
|
1618
4934
|
};
|
|
1619
4935
|
var clearPendingGraphWrites = async (frameworkAdapter, request) => {
|
|
1620
|
-
if (!request.graphCheckpointId) return;
|
|
4936
|
+
if (request.graphSnapshot || !request.graphCheckpointId) return;
|
|
1621
4937
|
const checkpointer = frameworkAdapter.checkpointer;
|
|
1622
4938
|
if (!checkpointer?.deleteCheckpointWrites) return;
|
|
1623
4939
|
await Promise.all([
|
|
@@ -1636,6 +4952,7 @@ var clearPendingGraphWrites = async (frameworkAdapter, request) => {
|
|
|
1636
4952
|
function createAgent(args) {
|
|
1637
4953
|
const parsedArgs = parseCreateAgentArgs(args);
|
|
1638
4954
|
const {
|
|
4955
|
+
limits,
|
|
1639
4956
|
getProvider,
|
|
1640
4957
|
workflows,
|
|
1641
4958
|
workflowsDir,
|
|
@@ -1645,24 +4962,24 @@ function createAgent(args) {
|
|
|
1645
4962
|
telemetry
|
|
1646
4963
|
} = parsedArgs;
|
|
1647
4964
|
const resolvedDefaultWorkflowId = defaultWorkflowId;
|
|
1648
|
-
const resolvedFrameworkAdapter = frameworkAdapter ?? (0,
|
|
4965
|
+
const resolvedFrameworkAdapter = frameworkAdapter ?? (0, import_runtime5.createFrameworkAdapterFromEnv)();
|
|
1649
4966
|
const resolvedGetProvider = getProvider ?? import_providers.getProvider;
|
|
1650
4967
|
const resolvedCwd = process.cwd();
|
|
1651
4968
|
const registryPromise = (async () => {
|
|
1652
4969
|
if (workflowRegistry) return workflowRegistry;
|
|
1653
4970
|
if (workflows) {
|
|
1654
|
-
return (0,
|
|
4971
|
+
return (0, import_runtime5.createInMemoryWorkflowRegistry)(workflows, {
|
|
1655
4972
|
fallbackId: resolvedDefaultWorkflowId ?? "general-chat"
|
|
1656
4973
|
});
|
|
1657
4974
|
}
|
|
1658
4975
|
if (workflowsDir) {
|
|
1659
|
-
return (0,
|
|
4976
|
+
return (0, import_runtime5.createFileWorkflowRegistry)({
|
|
1660
4977
|
workflowsDir,
|
|
1661
4978
|
fallbackId: resolvedDefaultWorkflowId ?? "general-chat"
|
|
1662
4979
|
});
|
|
1663
4980
|
}
|
|
1664
4981
|
const resolvedWorkflowsDir = (0, import_node_path.resolve)(resolvedCwd, "src", "workflows");
|
|
1665
|
-
return (0,
|
|
4982
|
+
return (0, import_runtime5.createFileWorkflowRegistry)({
|
|
1666
4983
|
workflowsDir: resolvedWorkflowsDir,
|
|
1667
4984
|
fallbackId: resolvedDefaultWorkflowId ?? "general-chat"
|
|
1668
4985
|
});
|
|
@@ -1700,12 +5017,19 @@ function createAgent(args) {
|
|
|
1700
5017
|
return streamChat({
|
|
1701
5018
|
...resolvedDefaultWorkflowId ? { defaultWorkflowId: resolvedDefaultWorkflowId } : {},
|
|
1702
5019
|
messages,
|
|
5020
|
+
abortSignal: parsedOptions?.abortSignal,
|
|
1703
5021
|
options: parsedOptions,
|
|
1704
5022
|
workflowRegistry: registry,
|
|
1705
5023
|
...knownWorkflowIds ? { knownWorkflowIds } : {},
|
|
1706
5024
|
frameworkAdapter: resolvedFrameworkAdapter,
|
|
1707
5025
|
getProvider: resolvedGetProvider,
|
|
1708
5026
|
loadRuntimeConfig: (runtimeOptions) => ({
|
|
5027
|
+
...limits || runtimeOptions?.limits ? {
|
|
5028
|
+
executionBudget: (0, import_core4.createExecutionBudget)(
|
|
5029
|
+
limits,
|
|
5030
|
+
runtimeOptions?.limits
|
|
5031
|
+
)
|
|
5032
|
+
} : {},
|
|
1709
5033
|
...runtimeOptions?.sessionId ? {
|
|
1710
5034
|
session: {
|
|
1711
5035
|
id: runtimeOptions.sessionId
|
|
@@ -1726,6 +5050,11 @@ function createAgent(args) {
|
|
|
1726
5050
|
(request) => hydratePendingGraphCheckpoint(resolvedFrameworkAdapter, request)
|
|
1727
5051
|
)
|
|
1728
5052
|
);
|
|
5053
|
+
restoreWorkflowCallBranch(
|
|
5054
|
+
activePendingRequests,
|
|
5055
|
+
telemetry,
|
|
5056
|
+
target?.runId ?? "unknown"
|
|
5057
|
+
);
|
|
1729
5058
|
await Promise.all(
|
|
1730
5059
|
activePendingRequests.map(
|
|
1731
5060
|
(request) => clearPendingGraphWrites(resolvedFrameworkAdapter, request)
|
|
@@ -1757,6 +5086,7 @@ function createAgent(args) {
|
|
|
1757
5086
|
return { ...result, activePendingRequests };
|
|
1758
5087
|
};
|
|
1759
5088
|
const fork = async (id, options) => {
|
|
5089
|
+
const source = await resolvedFrameworkAdapter.sessionCheckpoints.get(id);
|
|
1760
5090
|
const result = await resolvedFrameworkAdapter.sessionCheckpoints.fork(
|
|
1761
5091
|
id,
|
|
1762
5092
|
options
|
|
@@ -1766,6 +5096,11 @@ function createAgent(args) {
|
|
|
1766
5096
|
(request) => hydratePendingGraphCheckpoint(resolvedFrameworkAdapter, request)
|
|
1767
5097
|
)
|
|
1768
5098
|
);
|
|
5099
|
+
restoreWorkflowCallBranch(
|
|
5100
|
+
activePendingRequests,
|
|
5101
|
+
telemetry,
|
|
5102
|
+
source?.runId ?? "unknown"
|
|
5103
|
+
);
|
|
1769
5104
|
await Promise.all(
|
|
1770
5105
|
activePendingRequests.map(
|
|
1771
5106
|
(request) => clearPendingGraphWrites(resolvedFrameworkAdapter, request)
|
|
@@ -1795,7 +5130,25 @@ function createAgent(args) {
|
|
|
1795
5130
|
checkpoint: { ...result.checkpoint, activePendingRequests }
|
|
1796
5131
|
};
|
|
1797
5132
|
};
|
|
5133
|
+
const services = async () => {
|
|
5134
|
+
const registry = await registryPromise;
|
|
5135
|
+
if (!registry) throw new Error("Workflow registry is unavailable.");
|
|
5136
|
+
return {
|
|
5137
|
+
registry,
|
|
5138
|
+
limits,
|
|
5139
|
+
frameworkAdapter: resolvedFrameworkAdapter,
|
|
5140
|
+
getProvider: resolvedGetProvider,
|
|
5141
|
+
telemetry,
|
|
5142
|
+
knownWorkflowIds: (await registeredWorkflowsPromise)?.map(
|
|
5143
|
+
(workflow) => workflow.id
|
|
5144
|
+
)
|
|
5145
|
+
};
|
|
5146
|
+
};
|
|
5147
|
+
const execute = (async (options) => executeWorkflow(await services(), options));
|
|
5148
|
+
const resume = (async (options) => resumeWorkflow(await services(), options));
|
|
1798
5149
|
return {
|
|
5150
|
+
execute,
|
|
5151
|
+
resume,
|
|
1799
5152
|
projectTopology,
|
|
1800
5153
|
streamChat: streamChat2,
|
|
1801
5154
|
listCheckpoints,
|
|
@@ -1806,6 +5159,7 @@ function createAgent(args) {
|
|
|
1806
5159
|
}
|
|
1807
5160
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1808
5161
|
0 && (module.exports = {
|
|
5162
|
+
ExecutionRequestError,
|
|
1809
5163
|
createAgent,
|
|
1810
5164
|
createChatRouteHandler,
|
|
1811
5165
|
createCheckpointRouteHandler,
|