@kortyx/agent 0.15.0 → 0.16.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 +17 -0
- package/dist/index.js +2676 -11
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
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,9 +17,2554 @@ 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
|
+
useInterrupt: () => useInterrupt,
|
|
59
|
+
useNodeState: () => useNodeState,
|
|
60
|
+
useReason: () => useReason2,
|
|
61
|
+
useRuntimeContext: () => useRuntimeContext,
|
|
62
|
+
useStructuredData: () => useStructuredData,
|
|
63
|
+
useWorkflow: () => useWorkflow,
|
|
64
|
+
useWorkflowState: () => useWorkflowState,
|
|
65
|
+
workflowCallFingerprint: () => workflowCallFingerprint
|
|
66
|
+
});
|
|
67
|
+
module2.exports = __toCommonJS2(index_exports2);
|
|
68
|
+
var import_node_async_hooks = require("async_hooks");
|
|
69
|
+
var storage = new import_node_async_hooks.AsyncLocalStorage();
|
|
70
|
+
var isRecord5 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
71
|
+
var normalizeNodeState = (value) => {
|
|
72
|
+
if (Array.isArray(value)) {
|
|
73
|
+
return { byIndex: [...value], byKey: {} };
|
|
74
|
+
}
|
|
75
|
+
if (isRecord5(value)) {
|
|
76
|
+
const byIndex = Array.isArray(value.byIndex) ? [...value.byIndex] : [];
|
|
77
|
+
const byKey = isRecord5(value.byKey) ? { ...value.byKey } : {};
|
|
78
|
+
return { byIndex, byKey };
|
|
79
|
+
}
|
|
80
|
+
return { byIndex: [], byKey: {} };
|
|
81
|
+
};
|
|
82
|
+
var cloneWorkflowState = (value) => isRecord5(value) ? { ...value } : {};
|
|
83
|
+
var cloneTokenUsage = (value) => {
|
|
84
|
+
if (!isRecord5(value)) return void 0;
|
|
85
|
+
const input = typeof value.input === "number" ? value.input : void 0;
|
|
86
|
+
const output = typeof value.output === "number" ? value.output : void 0;
|
|
87
|
+
const total = typeof value.total === "number" ? value.total : void 0;
|
|
88
|
+
if (input === void 0 && output === void 0 && total === void 0) {
|
|
89
|
+
return void 0;
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
input: input ?? 0,
|
|
93
|
+
output: output ?? 0,
|
|
94
|
+
total: total ?? 0
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
var getHookRuntimeState = (state) => {
|
|
98
|
+
const runtime = state.runtime ?? {};
|
|
99
|
+
const internal = isRecord5(runtime.__kortyx) ? runtime.__kortyx : void 0;
|
|
100
|
+
const result = {};
|
|
101
|
+
if (isRecord5(internal?.nodeState)) result.nodeState = internal?.nodeState;
|
|
102
|
+
if (isRecord5(internal?.workflowState)) {
|
|
103
|
+
result.workflowState = internal?.workflowState;
|
|
104
|
+
}
|
|
105
|
+
return result;
|
|
106
|
+
};
|
|
107
|
+
var createInternalContext = (ctx) => {
|
|
108
|
+
const hookRuntimeState = getHookRuntimeState(ctx.state);
|
|
109
|
+
const nodeId = ctx.node.graph.node;
|
|
110
|
+
const storedNodeState = isRecord5(hookRuntimeState.nodeState) ? hookRuntimeState.nodeState : void 0;
|
|
111
|
+
const storedNodeId = typeof storedNodeState?.nodeId === "string" ? String(storedNodeState.nodeId) : "";
|
|
112
|
+
const storedState = storedNodeState?.state;
|
|
113
|
+
const currentNodeState = storedNodeId && storedNodeId === nodeId ? normalizeNodeState(storedState) : { byIndex: [], byKey: {} };
|
|
114
|
+
return {
|
|
115
|
+
...ctx,
|
|
116
|
+
workflowCallActive: false,
|
|
117
|
+
workflowCallIds: /* @__PURE__ */ new Set(),
|
|
118
|
+
nodeStateIndex: 0,
|
|
119
|
+
reasonCallIndex: 0,
|
|
120
|
+
currentNodeState,
|
|
121
|
+
workflowState: cloneWorkflowState(hookRuntimeState.workflowState),
|
|
122
|
+
tokenUsage: cloneTokenUsage(ctx.state.runtime?.tokenUsage),
|
|
123
|
+
stateDirty: false,
|
|
124
|
+
runtimeDirty: false
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
var buildRuntimeStateUpdates = (ctx) => {
|
|
128
|
+
const updates = {};
|
|
129
|
+
if (ctx.stateDirty) {
|
|
130
|
+
updates.__kortyx = {
|
|
131
|
+
nodeState: {
|
|
132
|
+
nodeId: ctx.node.graph.node,
|
|
133
|
+
state: ctx.currentNodeState
|
|
134
|
+
},
|
|
135
|
+
workflowState: ctx.workflowState
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
if (ctx.runtimeDirty && ctx.tokenUsage) {
|
|
139
|
+
updates.tokenUsage = ctx.tokenUsage;
|
|
140
|
+
}
|
|
141
|
+
return Object.keys(updates).length > 0 ? updates : null;
|
|
142
|
+
};
|
|
143
|
+
var cleanupCompletedReasonCheckpoints = (ctx) => {
|
|
144
|
+
let changed = false;
|
|
145
|
+
for (const [key, value] of Object.entries(ctx.currentNodeState.byKey)) {
|
|
146
|
+
if (!key.startsWith("__useReason:")) continue;
|
|
147
|
+
if (!isRecord5(value) || value.status !== "completed") continue;
|
|
148
|
+
delete ctx.currentNodeState.byKey[key];
|
|
149
|
+
changed = true;
|
|
150
|
+
}
|
|
151
|
+
if (changed) ctx.stateDirty = true;
|
|
152
|
+
};
|
|
153
|
+
var toTokenUsageDelta = (usage) => {
|
|
154
|
+
if (!usage) return void 0;
|
|
155
|
+
const input = usage.input ?? 0;
|
|
156
|
+
const output = usage.output ?? 0;
|
|
157
|
+
const total = usage.total ?? input + output + (usage.reasoning ?? 0);
|
|
158
|
+
if (input === 0 && output === 0 && total === 0) {
|
|
159
|
+
return void 0;
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
input,
|
|
163
|
+
output,
|
|
164
|
+
total
|
|
165
|
+
};
|
|
166
|
+
};
|
|
167
|
+
function accumulateTokenUsage(usage) {
|
|
168
|
+
const delta = toTokenUsageDelta(usage);
|
|
169
|
+
if (!delta) return;
|
|
170
|
+
const ctx = getHookContext();
|
|
171
|
+
const current = ctx.tokenUsage ?? {
|
|
172
|
+
input: 0,
|
|
173
|
+
output: 0,
|
|
174
|
+
total: 0
|
|
175
|
+
};
|
|
176
|
+
ctx.tokenUsage = {
|
|
177
|
+
input: current.input + delta.input,
|
|
178
|
+
output: current.output + delta.output,
|
|
179
|
+
total: current.total + delta.total
|
|
180
|
+
};
|
|
181
|
+
ctx.runtimeDirty = true;
|
|
182
|
+
}
|
|
183
|
+
function getReasonTraceAdapter() {
|
|
184
|
+
return getHookContext().reasonTrace;
|
|
185
|
+
}
|
|
186
|
+
async function runWithHookContext(ctx, fn) {
|
|
187
|
+
const internal = createInternalContext(ctx);
|
|
188
|
+
try {
|
|
189
|
+
const result = await storage.run(internal, fn);
|
|
190
|
+
cleanupCompletedReasonCheckpoints(internal);
|
|
191
|
+
for (const key of Object.keys(internal.currentNodeState.byKey)) {
|
|
192
|
+
if (key.startsWith("__useWorkflow:") || key === "__useWorkflowActivation") {
|
|
193
|
+
internal.currentNodeState.byKey[key] = null;
|
|
194
|
+
internal.stateDirty = true;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return { result, runtimeUpdates: buildRuntimeStateUpdates(internal) };
|
|
198
|
+
} catch (err) {
|
|
199
|
+
const runtimeUpdates = buildRuntimeStateUpdates(internal);
|
|
200
|
+
if (runtimeUpdates) {
|
|
201
|
+
const currentRuntime = isRecord5(internal.state.runtime) ? internal.state.runtime : {};
|
|
202
|
+
internal.state.runtime = {
|
|
203
|
+
...currentRuntime,
|
|
204
|
+
...runtimeUpdates
|
|
205
|
+
};
|
|
206
|
+
if (err && typeof err === "object") {
|
|
207
|
+
err.__kortyxHookStatePatch = runtimeUpdates;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
throw err;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
function getHookContext() {
|
|
214
|
+
const ctx = storage.getStore();
|
|
215
|
+
if (!ctx) {
|
|
216
|
+
throw new Error("Hooks can only be used while a node is executing.");
|
|
217
|
+
}
|
|
218
|
+
return ctx;
|
|
219
|
+
}
|
|
220
|
+
var resolveReasonCheckpointKey = (args) => typeof args.id === "string" && args.id.length > 0 ? `__useReason:${args.id}` : `__useReason:auto:${args.autoIndex}`;
|
|
221
|
+
var readReasonCheckpoint = (raw) => {
|
|
222
|
+
if (!raw || typeof raw !== "object") return void 0;
|
|
223
|
+
const value = raw;
|
|
224
|
+
if (value.status !== "awaiting_interrupt") return void 0;
|
|
225
|
+
if (typeof value.firstText !== "string") return void 0;
|
|
226
|
+
if (!value.request || typeof value.request !== "object") return void 0;
|
|
227
|
+
return {
|
|
228
|
+
status: "awaiting_interrupt",
|
|
229
|
+
request: value.request,
|
|
230
|
+
firstText: value.firstText,
|
|
231
|
+
...Object.hasOwn(value, "firstRaw") ? { firstRaw: value.firstRaw } : {},
|
|
232
|
+
...Object.hasOwn(value, "firstUsage") ? { firstUsage: value.firstUsage } : {},
|
|
233
|
+
...Object.hasOwn(value, "firstFinishReason") ? { firstFinishReason: value.firstFinishReason } : {},
|
|
234
|
+
...Object.hasOwn(value, "firstProviderMetadata") ? {
|
|
235
|
+
firstProviderMetadata: value.firstProviderMetadata
|
|
236
|
+
} : {},
|
|
237
|
+
...Object.hasOwn(value, "firstWarnings") ? { firstWarnings: value.firstWarnings } : {},
|
|
238
|
+
...Object.hasOwn(value, "firstOutput") ? { firstOutput: value.firstOutput } : {}
|
|
239
|
+
};
|
|
240
|
+
};
|
|
241
|
+
var readReasonCompletedCheckpoint = (raw) => {
|
|
242
|
+
if (!raw || typeof raw !== "object") return void 0;
|
|
243
|
+
const value = raw;
|
|
244
|
+
if (value.status !== "completed") return void 0;
|
|
245
|
+
if (!value.result || typeof value.result !== "object") return void 0;
|
|
246
|
+
return {
|
|
247
|
+
status: "completed",
|
|
248
|
+
result: value.result
|
|
249
|
+
};
|
|
250
|
+
};
|
|
251
|
+
var resolveHookStatePatch = (args) => ({
|
|
252
|
+
__kortyx: {
|
|
253
|
+
nodeState: {
|
|
254
|
+
nodeId: args.nodeId,
|
|
255
|
+
state: args.currentNodeState
|
|
256
|
+
},
|
|
257
|
+
workflowState: args.workflowState
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
var parseWithSchema = (schema, value, label) => {
|
|
261
|
+
if (!schema) return value;
|
|
262
|
+
const parsed = schema.safeParse(value);
|
|
263
|
+
if (parsed.success) return parsed.data;
|
|
264
|
+
const parsedError = "error" in parsed && parsed.error ? parsed.error : void 0;
|
|
265
|
+
const reason = typeof parsedError?.message === "string" && parsedError.message.length > 0 ? parsedError.message : "invalid payload";
|
|
266
|
+
throw new Error(`${label} validation failed: ${reason}`);
|
|
267
|
+
};
|
|
268
|
+
var awaitInterruptInternal = (args) => {
|
|
269
|
+
const ctx = getHookContext();
|
|
270
|
+
const request = parseWithSchema(
|
|
271
|
+
args.requestSchema,
|
|
272
|
+
args.request,
|
|
273
|
+
"useInterrupt request"
|
|
274
|
+
);
|
|
275
|
+
const userMeta = args.meta && typeof args.meta === "object" && !Array.isArray(args.meta) ? args.meta : {};
|
|
276
|
+
const resumeStatePatch = resolveHookStatePatch({
|
|
277
|
+
nodeId: ctx.node.graph.node,
|
|
278
|
+
currentNodeState: ctx.currentNodeState,
|
|
279
|
+
workflowState: ctx.workflowState
|
|
280
|
+
});
|
|
281
|
+
const enrichedRequest = {
|
|
282
|
+
...request,
|
|
283
|
+
...typeof args.schemaId === "string" && args.schemaId.length > 0 ? { schemaId: args.schemaId } : {},
|
|
284
|
+
...typeof args.schemaVersion === "string" && args.schemaVersion.length > 0 ? { schemaVersion: args.schemaVersion } : {},
|
|
285
|
+
...typeof args.id === "string" && args.id.length > 0 ? { id: args.id } : {},
|
|
286
|
+
meta: {
|
|
287
|
+
...userMeta,
|
|
288
|
+
__kortyxResumeStatePatch: {
|
|
289
|
+
...resumeStatePatch,
|
|
290
|
+
...ctx.tokenUsage ? { tokenUsage: ctx.tokenUsage } : {}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
const raw = ctx.node.awaitInterrupt(enrichedRequest);
|
|
295
|
+
const response = parseWithSchema(
|
|
296
|
+
args.responseSchema,
|
|
297
|
+
raw,
|
|
298
|
+
"useInterrupt response"
|
|
299
|
+
);
|
|
300
|
+
return Promise.resolve(response);
|
|
301
|
+
};
|
|
302
|
+
var import_node_crypto4 = require("crypto");
|
|
303
|
+
var parseStructuredPath = (path, source) => {
|
|
304
|
+
if (path.length === 0) {
|
|
305
|
+
throw new Error(`${source} path must be a non-empty dot-separated string.`);
|
|
306
|
+
}
|
|
307
|
+
const parts = path.split(".");
|
|
308
|
+
if (parts.some((part) => part.length === 0)) {
|
|
309
|
+
throw new Error(
|
|
310
|
+
`${source} path "${path}" must not contain empty segments.`
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
return parts;
|
|
314
|
+
};
|
|
315
|
+
var assertStructuredPath = (path, source) => {
|
|
316
|
+
parseStructuredPath(path, source);
|
|
317
|
+
};
|
|
318
|
+
var createStructuredStreamId = () => {
|
|
319
|
+
try {
|
|
320
|
+
return (0, import_node_crypto4.randomUUID)();
|
|
321
|
+
} catch {
|
|
322
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
var emitStructuredData = (args) => {
|
|
326
|
+
const ctx = getHookContext();
|
|
327
|
+
const streamId = typeof args.streamId === "string" && args.streamId.length > 0 ? args.streamId : createStructuredStreamId();
|
|
328
|
+
const dataType = typeof args.dataType === "string" && args.dataType.length > 0 ? args.dataType : "generic";
|
|
329
|
+
const base = {
|
|
330
|
+
node: ctx.node.graph.node,
|
|
331
|
+
streamId,
|
|
332
|
+
dataType,
|
|
333
|
+
...typeof args.schemaId === "string" && args.schemaId.length > 0 ? { schemaId: args.schemaId } : {},
|
|
334
|
+
...typeof args.schemaVersion === "string" && args.schemaVersion.length > 0 ? { schemaVersion: args.schemaVersion } : {},
|
|
335
|
+
...typeof args.id === "string" && args.id.length > 0 ? { id: args.id } : {}
|
|
336
|
+
};
|
|
337
|
+
if (args.kind === "set") {
|
|
338
|
+
assertStructuredPath(args.path, "useStructuredData");
|
|
339
|
+
const value = parseWithSchema(
|
|
340
|
+
args.valueSchema,
|
|
341
|
+
args.value,
|
|
342
|
+
"useStructuredData value"
|
|
343
|
+
);
|
|
344
|
+
ctx.node.emit("structured_data", {
|
|
345
|
+
...base,
|
|
346
|
+
kind: "set",
|
|
347
|
+
path: args.path,
|
|
348
|
+
value
|
|
349
|
+
});
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
if (args.kind === "append") {
|
|
353
|
+
assertStructuredPath(args.path, "useStructuredData");
|
|
354
|
+
const items = args.items.map(
|
|
355
|
+
(item) => parseWithSchema(args.itemSchema, item, "useStructuredData append item")
|
|
356
|
+
);
|
|
357
|
+
ctx.node.emit("structured_data", {
|
|
358
|
+
...base,
|
|
359
|
+
kind: "append",
|
|
360
|
+
path: args.path,
|
|
361
|
+
items
|
|
362
|
+
});
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
if (args.kind === "text-delta") {
|
|
366
|
+
assertStructuredPath(args.path, "useStructuredData");
|
|
367
|
+
ctx.node.emit("structured_data", {
|
|
368
|
+
...base,
|
|
369
|
+
kind: "text-delta",
|
|
370
|
+
path: args.path,
|
|
371
|
+
delta: args.delta
|
|
372
|
+
});
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
const validated = parseWithSchema(
|
|
376
|
+
args.dataSchema,
|
|
377
|
+
args.data,
|
|
378
|
+
"useStructuredData data"
|
|
379
|
+
);
|
|
380
|
+
ctx.node.emit("structured_data", {
|
|
381
|
+
...base,
|
|
382
|
+
kind: "final",
|
|
383
|
+
data: validated
|
|
384
|
+
});
|
|
385
|
+
};
|
|
386
|
+
var shouldEmitStructured = (cfg) => Boolean(cfg);
|
|
387
|
+
var shouldStreamStructured = (cfg) => Boolean(cfg) && (cfg?.stream ?? true);
|
|
388
|
+
var import_node_crypto32 = require("crypto");
|
|
389
|
+
var import_node_crypto22 = require("crypto");
|
|
390
|
+
var toError = (value) => value instanceof Error ? value : new Error(String(value));
|
|
391
|
+
var createRuntimeId = () => {
|
|
392
|
+
try {
|
|
393
|
+
return (0, import_node_crypto22.randomUUID)();
|
|
394
|
+
} catch {
|
|
395
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
var emitNodeEvent = (emitEvent, nodeId, event, payload) => {
|
|
399
|
+
if (!emitEvent) return;
|
|
400
|
+
emitEvent(event, {
|
|
401
|
+
...payload,
|
|
402
|
+
...typeof nodeId === "string" && nodeId.length > 0 ? { node: nodeId } : {}
|
|
403
|
+
});
|
|
404
|
+
};
|
|
405
|
+
async function runReasonEngine(args) {
|
|
406
|
+
const stream = args.stream ?? args.model.options?.streaming ?? true;
|
|
407
|
+
const emit = args.emit ?? true;
|
|
408
|
+
const temperature = args.temperature ?? args.model.options?.temperature ?? args.defaultTemperature;
|
|
409
|
+
const maxOutputTokens = args.maxOutputTokens ?? args.model.options?.maxOutputTokens;
|
|
410
|
+
const stopSequences = args.stopSequences ?? args.model.options?.stopSequences;
|
|
411
|
+
const abortSignal = args.abortSignal ?? args.model.options?.abortSignal;
|
|
412
|
+
const reasoning = args.reasoning ?? args.model.options?.reasoning;
|
|
413
|
+
const responseFormat = args.responseFormat ?? args.model.options?.responseFormat;
|
|
414
|
+
const providerOptions = args.providerOptions ?? args.model.options?.providerOptions;
|
|
415
|
+
const tools = args.tools ?? args.model.options?.tools;
|
|
416
|
+
const model = args.model.provider.getModel(args.model.modelId, {
|
|
417
|
+
...temperature !== void 0 ? { temperature } : {},
|
|
418
|
+
streaming: stream,
|
|
419
|
+
...maxOutputTokens !== void 0 ? { maxOutputTokens } : {},
|
|
420
|
+
...stopSequences !== void 0 ? { stopSequences } : {},
|
|
421
|
+
...abortSignal !== void 0 ? { abortSignal } : {},
|
|
422
|
+
...reasoning !== void 0 ? { reasoning } : {},
|
|
423
|
+
...responseFormat !== void 0 ? { responseFormat } : {},
|
|
424
|
+
...tools !== void 0 ? { tools } : {},
|
|
425
|
+
...providerOptions !== void 0 ? { providerOptions } : {}
|
|
426
|
+
});
|
|
427
|
+
const messages = args.messages !== void 0 ? args.messages : [
|
|
428
|
+
...typeof args.system === "string" && args.system.length > 0 ? [{ role: "system", content: args.system }] : [],
|
|
429
|
+
{ role: "user", content: String(args.input ?? "") }
|
|
430
|
+
];
|
|
431
|
+
const opId = typeof args.opId === "string" && args.opId.length > 0 ? args.opId : createRuntimeId();
|
|
432
|
+
const segmentId = typeof args.segmentId === "string" && args.segmentId.length > 0 ? args.segmentId : createRuntimeId();
|
|
433
|
+
const commonMeta = {
|
|
434
|
+
...typeof args.id === "string" && args.id.length > 0 ? { id: args.id } : {},
|
|
435
|
+
opId,
|
|
436
|
+
segmentId
|
|
437
|
+
};
|
|
438
|
+
const traceSpan = args.reasonTrace?.startSpan({
|
|
439
|
+
name: "runReasonEngine",
|
|
440
|
+
attributes: {
|
|
441
|
+
...commonMeta,
|
|
442
|
+
providerId: args.model.provider.id,
|
|
443
|
+
modelId: args.model.modelId,
|
|
444
|
+
stream,
|
|
445
|
+
emit,
|
|
446
|
+
...args.telemetry?.operation ? { operation: args.telemetry.operation } : {},
|
|
447
|
+
...args.telemetry?.prompt?.name ? { promptName: args.telemetry.prompt.name } : {},
|
|
448
|
+
...args.telemetry?.prompt?.version !== void 0 ? { promptVersion: args.telemetry.prompt.version } : {},
|
|
449
|
+
...args.telemetry?.prompt?.type ? { promptType: args.telemetry.prompt.type } : {},
|
|
450
|
+
...typeof args.nodeId === "string" && args.nodeId.length > 0 ? { nodeId: args.nodeId } : {}
|
|
451
|
+
},
|
|
452
|
+
telemetry: {
|
|
453
|
+
...args.telemetry ?? {},
|
|
454
|
+
input: args.telemetry?.input ?? messages
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
const modelRequestStartedAt = Date.now();
|
|
458
|
+
let firstTextTokenAt;
|
|
459
|
+
let lastTextTokenAt;
|
|
460
|
+
const generationTimingAttributes = () => firstTextTokenAt === void 0 || lastTextTokenAt === void 0 ? {} : {
|
|
461
|
+
ttftMs: Math.max(0, firstTextTokenAt - modelRequestStartedAt),
|
|
462
|
+
streamDurationMs: Math.max(0, lastTextTokenAt - firstTextTokenAt),
|
|
463
|
+
timeToLastTokenMs: Math.max(
|
|
464
|
+
0,
|
|
465
|
+
lastTextTokenAt - modelRequestStartedAt
|
|
466
|
+
)
|
|
467
|
+
};
|
|
468
|
+
const endTrace = (span, result) => {
|
|
469
|
+
span?.end?.({
|
|
470
|
+
...result.usage !== void 0 ? { usage: result.usage } : {},
|
|
471
|
+
...result.finishReason !== void 0 ? { finishReason: result.finishReason } : {},
|
|
472
|
+
...result.providerMetadata !== void 0 ? { providerMetadata: result.providerMetadata } : {},
|
|
473
|
+
...result.warnings !== void 0 ? { warnings: result.warnings } : {},
|
|
474
|
+
attributes: {
|
|
475
|
+
textLength: result.text.length,
|
|
476
|
+
...generationTimingAttributes()
|
|
477
|
+
},
|
|
478
|
+
telemetry: {
|
|
479
|
+
...args.telemetry ?? {},
|
|
480
|
+
output: args.telemetry?.output ?? result.text
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
};
|
|
484
|
+
const failTrace = (span, error) => {
|
|
485
|
+
span?.fail?.(error, {
|
|
486
|
+
attributes: {
|
|
487
|
+
providerId: args.model.provider.id,
|
|
488
|
+
modelId: args.model.modelId
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
};
|
|
492
|
+
try {
|
|
493
|
+
if (stream) {
|
|
494
|
+
let final = "";
|
|
495
|
+
let raw;
|
|
496
|
+
let usage;
|
|
497
|
+
let finishReason;
|
|
498
|
+
let toolCalls;
|
|
499
|
+
let providerMetadata;
|
|
500
|
+
let warnings;
|
|
501
|
+
if (emit) {
|
|
502
|
+
emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {
|
|
503
|
+
...commonMeta
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
const response2 = await model.stream(messages);
|
|
507
|
+
for await (const chunk of response2) {
|
|
508
|
+
switch (chunk.type) {
|
|
509
|
+
case "text-delta": {
|
|
510
|
+
if (chunk.delta.length === 0) break;
|
|
511
|
+
const tokenAt = Date.now();
|
|
512
|
+
firstTextTokenAt ??= tokenAt;
|
|
513
|
+
lastTextTokenAt = tokenAt;
|
|
514
|
+
final += chunk.delta;
|
|
515
|
+
args.onTextChunk?.(chunk.delta);
|
|
516
|
+
if (chunk.raw !== void 0) {
|
|
517
|
+
raw = chunk.raw;
|
|
518
|
+
}
|
|
519
|
+
if (chunk.providerMetadata !== void 0) {
|
|
520
|
+
providerMetadata = chunk.providerMetadata;
|
|
521
|
+
}
|
|
522
|
+
if (emit) {
|
|
523
|
+
emitNodeEvent(args.emitEvent, args.nodeId, "text-delta", {
|
|
524
|
+
...commonMeta,
|
|
525
|
+
delta: chunk.delta
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
break;
|
|
529
|
+
}
|
|
530
|
+
case "finish": {
|
|
531
|
+
if (chunk.raw !== void 0) {
|
|
532
|
+
raw = chunk.raw;
|
|
533
|
+
}
|
|
534
|
+
if (chunk.usage !== void 0) {
|
|
535
|
+
usage = chunk.usage;
|
|
536
|
+
}
|
|
537
|
+
if (chunk.finishReason !== void 0) {
|
|
538
|
+
finishReason = chunk.finishReason;
|
|
539
|
+
}
|
|
540
|
+
if (chunk.toolCalls !== void 0) {
|
|
541
|
+
toolCalls = chunk.toolCalls;
|
|
542
|
+
}
|
|
543
|
+
if (chunk.providerMetadata !== void 0) {
|
|
544
|
+
providerMetadata = chunk.providerMetadata;
|
|
545
|
+
}
|
|
546
|
+
if (chunk.warnings !== void 0) {
|
|
547
|
+
warnings = chunk.warnings;
|
|
548
|
+
}
|
|
549
|
+
break;
|
|
550
|
+
}
|
|
551
|
+
case "raw": {
|
|
552
|
+
raw = chunk.raw;
|
|
553
|
+
if (chunk.providerMetadata !== void 0) {
|
|
554
|
+
providerMetadata = chunk.providerMetadata;
|
|
555
|
+
}
|
|
556
|
+
break;
|
|
557
|
+
}
|
|
558
|
+
case "error": {
|
|
559
|
+
if (chunk.raw !== void 0) {
|
|
560
|
+
raw = chunk.raw;
|
|
561
|
+
}
|
|
562
|
+
if (chunk.providerMetadata !== void 0) {
|
|
563
|
+
providerMetadata = chunk.providerMetadata;
|
|
564
|
+
}
|
|
565
|
+
if (chunk.warnings !== void 0) {
|
|
566
|
+
warnings = chunk.warnings;
|
|
567
|
+
}
|
|
568
|
+
throw toError(chunk.error);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
if (emit) {
|
|
573
|
+
emitNodeEvent(args.emitEvent, args.nodeId, "text-end", {
|
|
574
|
+
...commonMeta
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
const result2 = {
|
|
578
|
+
text: final,
|
|
579
|
+
...raw !== void 0 ? { raw } : {},
|
|
580
|
+
...usage !== void 0 ? { usage } : {},
|
|
581
|
+
...finishReason !== void 0 ? { finishReason } : {},
|
|
582
|
+
...toolCalls !== void 0 ? { toolCalls } : {},
|
|
583
|
+
...providerMetadata !== void 0 ? { providerMetadata } : {},
|
|
584
|
+
...warnings !== void 0 ? { warnings } : {}
|
|
585
|
+
};
|
|
586
|
+
endTrace(traceSpan, result2);
|
|
587
|
+
return result2;
|
|
588
|
+
}
|
|
589
|
+
const response = await model.invoke(messages);
|
|
590
|
+
if (emit) {
|
|
591
|
+
emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {
|
|
592
|
+
...commonMeta
|
|
593
|
+
});
|
|
594
|
+
if (response.content) {
|
|
595
|
+
emitNodeEvent(args.emitEvent, args.nodeId, "text-delta", {
|
|
596
|
+
...commonMeta,
|
|
597
|
+
delta: response.content
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
emitNodeEvent(args.emitEvent, args.nodeId, "text-end", {
|
|
601
|
+
...commonMeta
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
const result = {
|
|
605
|
+
text: response.content,
|
|
606
|
+
...response.raw !== void 0 ? { raw: response.raw } : {},
|
|
607
|
+
...response.usage !== void 0 ? { usage: response.usage } : {},
|
|
608
|
+
...response.finishReason !== void 0 ? { finishReason: response.finishReason } : {},
|
|
609
|
+
...response.toolCalls !== void 0 ? { toolCalls: response.toolCalls } : {},
|
|
610
|
+
...response.providerMetadata !== void 0 ? { providerMetadata: response.providerMetadata } : {},
|
|
611
|
+
...response.warnings !== void 0 ? { warnings: response.warnings } : {}
|
|
612
|
+
};
|
|
613
|
+
endTrace(traceSpan, result);
|
|
614
|
+
return result;
|
|
615
|
+
} catch (error) {
|
|
616
|
+
failTrace(traceSpan, error);
|
|
617
|
+
throw error;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
var createRuntimeId2 = () => {
|
|
621
|
+
try {
|
|
622
|
+
return (0, import_node_crypto32.randomUUID)();
|
|
623
|
+
} catch {
|
|
624
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
async function reasonEngine(args, meta, inputOverride) {
|
|
628
|
+
const ctx = getHookContext();
|
|
629
|
+
return runReasonEngine({
|
|
630
|
+
model: args.model,
|
|
631
|
+
input: inputOverride ?? args.input,
|
|
632
|
+
system: args.system,
|
|
633
|
+
temperature: args.temperature,
|
|
634
|
+
maxOutputTokens: args.maxOutputTokens,
|
|
635
|
+
stopSequences: args.stopSequences,
|
|
636
|
+
abortSignal: args.abortSignal,
|
|
637
|
+
reasoning: args.reasoning,
|
|
638
|
+
responseFormat: args.responseFormat,
|
|
639
|
+
providerOptions: args.providerOptions,
|
|
640
|
+
tools: args.tools,
|
|
641
|
+
messages: args.messages,
|
|
642
|
+
defaultTemperature: ctx.node.config?.model?.temperature,
|
|
643
|
+
stream: args.stream,
|
|
644
|
+
emit: args.emit,
|
|
645
|
+
onTextChunk: args.onTextChunk,
|
|
646
|
+
telemetry: args.telemetry,
|
|
647
|
+
nodeId: ctx.node.graph.node,
|
|
648
|
+
emitEvent: ctx.node.emit,
|
|
649
|
+
reasonTrace: ctx.reasonTrace,
|
|
650
|
+
id: meta.id,
|
|
651
|
+
opId: meta.opId
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
var tryJsonParse = (value) => {
|
|
655
|
+
try {
|
|
656
|
+
return { ok: true, value: JSON.parse(value) };
|
|
657
|
+
} catch (error) {
|
|
658
|
+
return {
|
|
659
|
+
ok: false,
|
|
660
|
+
error: error instanceof Error ? error.message : String(error)
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
var extractJsonCodeBlock = (value) => {
|
|
665
|
+
const match = value.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
666
|
+
if (!match || typeof match[1] !== "string") return null;
|
|
667
|
+
const inner = match[1].trim();
|
|
668
|
+
return inner.length > 0 ? inner : null;
|
|
669
|
+
};
|
|
670
|
+
var tryStringify = (value) => {
|
|
671
|
+
if (typeof value === "string") return value;
|
|
672
|
+
try {
|
|
673
|
+
return JSON.stringify(value, null, 2);
|
|
674
|
+
} catch {
|
|
675
|
+
return String(value);
|
|
676
|
+
}
|
|
677
|
+
};
|
|
678
|
+
var isRecord22 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
679
|
+
var resolveOutputCandidate = (text) => {
|
|
680
|
+
const trimmed = text.trim();
|
|
681
|
+
if (!trimmed) return text;
|
|
682
|
+
const direct = tryJsonParse(trimmed);
|
|
683
|
+
if (direct.ok) return direct.value;
|
|
684
|
+
const block = extractJsonCodeBlock(trimmed);
|
|
685
|
+
if (block) {
|
|
686
|
+
const parsedBlock = tryJsonParse(block);
|
|
687
|
+
if (parsedBlock.ok) return parsedBlock.value;
|
|
688
|
+
}
|
|
689
|
+
return text;
|
|
690
|
+
};
|
|
691
|
+
var looksLikeStructuredJsonAttempt = (value) => {
|
|
692
|
+
const trimmed = value.trim();
|
|
693
|
+
if (!trimmed) return false;
|
|
694
|
+
return trimmed.startsWith("{") || trimmed.startsWith("[") || /^```(?:json)?/i.test(trimmed);
|
|
695
|
+
};
|
|
696
|
+
var isLikelyTruncatedStructuredOutput = (text) => {
|
|
697
|
+
const trimmed = text.trim();
|
|
698
|
+
if (!looksLikeStructuredJsonAttempt(trimmed)) return false;
|
|
699
|
+
const direct = tryJsonParse(trimmed);
|
|
700
|
+
if (!direct.ok && direct.error) return true;
|
|
701
|
+
const block = extractJsonCodeBlock(trimmed);
|
|
702
|
+
if (!block) return false;
|
|
703
|
+
const parsedBlock = tryJsonParse(block);
|
|
704
|
+
return !parsedBlock.ok && Boolean(parsedBlock.error);
|
|
705
|
+
};
|
|
706
|
+
var createTruncatedStructuredOutputError = (label) => new Error(
|
|
707
|
+
`${label} was truncated before producing valid structured output. The model stopped due to output length. Increase maxOutputTokens or simplify the requested output.`
|
|
708
|
+
);
|
|
709
|
+
var createInvalidStructuredOutputError = (label) => new Error(
|
|
710
|
+
`${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.`
|
|
711
|
+
);
|
|
712
|
+
var parseReasonOutputWithSchema = (args) => {
|
|
713
|
+
const candidate = resolveOutputCandidate(args.text);
|
|
714
|
+
try {
|
|
715
|
+
return parseWithSchema(args.schema, candidate, args.label);
|
|
716
|
+
} catch (error) {
|
|
717
|
+
if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
|
|
718
|
+
throw createTruncatedStructuredOutputError(args.label);
|
|
719
|
+
}
|
|
720
|
+
if (typeof candidate === "string" && candidate.trim().length > 0) {
|
|
721
|
+
throw createInvalidStructuredOutputError(args.label);
|
|
722
|
+
}
|
|
723
|
+
throw error;
|
|
724
|
+
}
|
|
725
|
+
};
|
|
726
|
+
var parseInterruptFirstPassResult = (args) => {
|
|
727
|
+
const candidate = resolveOutputCandidate(args.text);
|
|
728
|
+
if (!isRecord22(candidate)) {
|
|
729
|
+
if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
|
|
730
|
+
throw createTruncatedStructuredOutputError("useReason output");
|
|
731
|
+
}
|
|
732
|
+
if (typeof candidate === "string" && candidate.trim().length > 0) {
|
|
733
|
+
throw createInvalidStructuredOutputError("useReason output");
|
|
734
|
+
}
|
|
735
|
+
throw new Error(
|
|
736
|
+
"useReason first pass with interrupt must return a JSON object."
|
|
737
|
+
);
|
|
738
|
+
}
|
|
739
|
+
const mode = args.mode ?? "required";
|
|
740
|
+
let interruptRequired = true;
|
|
741
|
+
if (mode === "optional") {
|
|
742
|
+
if (candidate.decision !== "continue" && candidate.decision !== "interrupt") {
|
|
743
|
+
throw new Error(
|
|
744
|
+
'useReason optional interrupt first pass must include decision "continue" or "interrupt".'
|
|
745
|
+
);
|
|
746
|
+
}
|
|
747
|
+
interruptRequired = candidate.decision === "interrupt";
|
|
748
|
+
if (!interruptRequired) {
|
|
749
|
+
const hasUnexpectedRequest = [
|
|
750
|
+
candidate.interruptRequest,
|
|
751
|
+
candidate.request,
|
|
752
|
+
candidate.interrupt
|
|
753
|
+
].some((value) => value !== null && value !== void 0);
|
|
754
|
+
if (hasUnexpectedRequest) {
|
|
755
|
+
throw new Error(
|
|
756
|
+
'useReason optional interrupt first pass with decision "continue" must not include an interrupt request.'
|
|
757
|
+
);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
let request;
|
|
762
|
+
if (interruptRequired) {
|
|
763
|
+
const requestCandidates = [
|
|
764
|
+
candidate.interruptRequest,
|
|
765
|
+
candidate.request,
|
|
766
|
+
candidate.interrupt,
|
|
767
|
+
mode === "required" ? candidate : void 0
|
|
768
|
+
];
|
|
769
|
+
const requestPayload = requestCandidates.find(
|
|
770
|
+
(value) => value !== null && value !== void 0
|
|
771
|
+
);
|
|
772
|
+
request = parseWithSchema(
|
|
773
|
+
args.requestSchema,
|
|
774
|
+
requestPayload,
|
|
775
|
+
"useReason interrupt.request"
|
|
776
|
+
);
|
|
777
|
+
}
|
|
778
|
+
let output;
|
|
779
|
+
if (args.outputSchema) {
|
|
780
|
+
const outputPayload = Object.hasOwn(candidate, "output") ? candidate.output : candidate;
|
|
781
|
+
try {
|
|
782
|
+
output = parseWithSchema(
|
|
783
|
+
args.outputSchema,
|
|
784
|
+
outputPayload,
|
|
785
|
+
"useReason output"
|
|
786
|
+
);
|
|
787
|
+
} catch (error) {
|
|
788
|
+
if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
|
|
789
|
+
throw createTruncatedStructuredOutputError("useReason output");
|
|
790
|
+
}
|
|
791
|
+
if (typeof outputPayload === "string" && outputPayload.trim().length > 0) {
|
|
792
|
+
throw createInvalidStructuredOutputError("useReason output");
|
|
793
|
+
}
|
|
794
|
+
throw error;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
const explicitDraft = typeof candidate.draftText === "string" && candidate.draftText.length > 0 ? candidate.draftText : typeof candidate.text === "string" && candidate.text.length > 0 ? candidate.text : void 0;
|
|
798
|
+
if (!args.outputSchema && !explicitDraft) {
|
|
799
|
+
throw new Error(
|
|
800
|
+
"useReason first pass with interrupt requires `draftText` when outputSchema is not provided."
|
|
801
|
+
);
|
|
802
|
+
}
|
|
803
|
+
const draftText = explicitDraft ?? (output !== void 0 ? tryStringify(output) : String(args.text ?? ""));
|
|
804
|
+
if (!interruptRequired) {
|
|
805
|
+
return {
|
|
806
|
+
draftText,
|
|
807
|
+
interruptRequired: false,
|
|
808
|
+
...output !== void 0 ? { output } : {}
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
return {
|
|
812
|
+
draftText,
|
|
813
|
+
interruptRequired: true,
|
|
814
|
+
request,
|
|
815
|
+
...output !== void 0 ? { output } : {}
|
|
816
|
+
};
|
|
817
|
+
};
|
|
818
|
+
var import_zod3 = require("zod");
|
|
819
|
+
var tryStringify2 = (value) => {
|
|
820
|
+
if (typeof value === "string") return value;
|
|
821
|
+
try {
|
|
822
|
+
return JSON.stringify(value, null, 2);
|
|
823
|
+
} catch {
|
|
824
|
+
return String(value);
|
|
825
|
+
}
|
|
826
|
+
};
|
|
827
|
+
var tryGetSchemaHint = (schema) => {
|
|
828
|
+
try {
|
|
829
|
+
const hint = (0, import_zod3.toJSONSchema)(schema);
|
|
830
|
+
return JSON.stringify(hint, null, 2);
|
|
831
|
+
} catch {
|
|
832
|
+
return void 0;
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
var withSchemaHint = (input, label, schema) => {
|
|
836
|
+
const hint = tryGetSchemaHint(schema);
|
|
837
|
+
if (!hint) return input;
|
|
838
|
+
return `${input}
|
|
839
|
+
|
|
840
|
+
${label}:
|
|
841
|
+
${hint}`;
|
|
842
|
+
};
|
|
843
|
+
var defaultInterruptFirstPassInput = (args) => {
|
|
844
|
+
const mode = args.mode ?? "required";
|
|
845
|
+
const base = mode === "optional" ? args.outputSchema ? [
|
|
846
|
+
args.input,
|
|
847
|
+
"Output rules:",
|
|
848
|
+
"- Return JSON only. No markdown fences.",
|
|
849
|
+
"- Return an object with keys: decision, output, interruptRequest, draftText.",
|
|
850
|
+
'- decision must be either "continue" or "interrupt".',
|
|
851
|
+
'- Use decision "continue" when you can produce the final output without asking the user.',
|
|
852
|
+
'- Use decision "interrupt" only when user input is needed before finalizing.',
|
|
853
|
+
"- output must match the expected output JSON schema.",
|
|
854
|
+
'- When decision is "interrupt", interruptRequest must match the expected interrupt request JSON schema and must be a non-null object.',
|
|
855
|
+
'- When decision is "continue", interruptRequest must be null.',
|
|
856
|
+
"- draftText should be a concise plain-language summary of output."
|
|
857
|
+
].join("\n\n") : [
|
|
858
|
+
args.input,
|
|
859
|
+
"Output rules:",
|
|
860
|
+
"- Return JSON only. No markdown fences.",
|
|
861
|
+
"- Return an object with keys: decision, draftText, interruptRequest.",
|
|
862
|
+
'- decision must be either "continue" or "interrupt".',
|
|
863
|
+
'- Use decision "continue" when you can produce the final answer without asking the user.',
|
|
864
|
+
'- Use decision "interrupt" only when user input is needed before finalizing.',
|
|
865
|
+
"- draftText should be the current answer text.",
|
|
866
|
+
'- When decision is "interrupt", interruptRequest must match the expected interrupt request JSON schema and must be a non-null object.',
|
|
867
|
+
'- When decision is "continue", interruptRequest must be null.'
|
|
868
|
+
].join("\n\n") : args.outputSchema ? [
|
|
869
|
+
args.input,
|
|
870
|
+
"Output rules:",
|
|
871
|
+
"- Return JSON only. No markdown fences.",
|
|
872
|
+
"- Return an object with keys: output, interruptRequest, draftText.",
|
|
873
|
+
"- output must match the expected output JSON schema.",
|
|
874
|
+
"- interruptRequest must match the expected interrupt request JSON schema.",
|
|
875
|
+
"- interruptRequest must be a non-null object.",
|
|
876
|
+
"- draftText should be a concise plain-language summary of output.",
|
|
877
|
+
"- If uncertain, still return a valid interruptRequest with safe default options."
|
|
878
|
+
].join("\n\n") : [
|
|
879
|
+
args.input,
|
|
880
|
+
"Output rules:",
|
|
881
|
+
"- Return JSON only. No markdown fences.",
|
|
882
|
+
"- Return an object with keys: draftText, interruptRequest.",
|
|
883
|
+
"- draftText should be the current draft answer text.",
|
|
884
|
+
"- interruptRequest must match the expected interrupt request JSON schema.",
|
|
885
|
+
"- interruptRequest must be a non-null object.",
|
|
886
|
+
"- If uncertain, still return a valid interruptRequest with safe default options."
|
|
887
|
+
].join("\n\n");
|
|
888
|
+
const withInterruptSchema = withSchemaHint(
|
|
889
|
+
base,
|
|
890
|
+
"Expected interrupt request JSON schema (for `interruptRequest`)",
|
|
891
|
+
args.requestSchema
|
|
892
|
+
);
|
|
893
|
+
if (!args.outputSchema) return withInterruptSchema;
|
|
894
|
+
const withOutputSchema = withSchemaHint(
|
|
895
|
+
withInterruptSchema,
|
|
896
|
+
"Expected output JSON schema (for `output`)",
|
|
897
|
+
args.outputSchema
|
|
898
|
+
);
|
|
899
|
+
return [
|
|
900
|
+
withOutputSchema,
|
|
901
|
+
"JSON template:",
|
|
902
|
+
mode === "optional" ? '{ "decision": "continue", "output": { ... }, "interruptRequest": null, "draftText": "..." }' : '{ "output": { ... }, "interruptRequest": { ... }, "draftText": "..." }'
|
|
903
|
+
].join("\n\n");
|
|
904
|
+
};
|
|
905
|
+
var defaultContinuationInput = (args) => [
|
|
906
|
+
"You are continuing a paused reasoning task after user input.",
|
|
907
|
+
"Update the previous result using the user response and return the final answer only.",
|
|
908
|
+
"If the previous output was JSON, return JSON in the same schema.",
|
|
909
|
+
"",
|
|
910
|
+
`Original request:
|
|
911
|
+
${args.input}`,
|
|
912
|
+
`Draft answer text:
|
|
913
|
+
${args.draftText}`,
|
|
914
|
+
`Draft output object:
|
|
915
|
+
${tryStringify2(args.draftOutput ?? args.draftText)}`,
|
|
916
|
+
`Interrupt request shown to user:
|
|
917
|
+
${tryStringify2(args.request)}`,
|
|
918
|
+
`User response:
|
|
919
|
+
${tryStringify2(args.response)}`
|
|
920
|
+
].join("\n\n");
|
|
921
|
+
var withOutputGuardrails = (input, schema) => {
|
|
922
|
+
const base = [
|
|
923
|
+
input,
|
|
924
|
+
"Output rules:",
|
|
925
|
+
"- Return only JSON, no markdown fences.",
|
|
926
|
+
"- Ensure the response conforms to the expected output schema.",
|
|
927
|
+
"- Do not include explanatory text outside the JSON object."
|
|
928
|
+
].join("\n\n");
|
|
929
|
+
return withSchemaHint(base, "Expected output JSON schema", schema);
|
|
930
|
+
};
|
|
931
|
+
var withStructuredStreamHints = (input, args) => {
|
|
932
|
+
const rules = [];
|
|
933
|
+
for (const path of args.setFieldPaths ?? []) {
|
|
934
|
+
rules.push(
|
|
935
|
+
`- For the field path pattern \`${path}\`, write one stable final value and do not rewrite it after it is complete.`
|
|
936
|
+
);
|
|
937
|
+
}
|
|
938
|
+
for (const path of args.appendFieldPaths ?? []) {
|
|
939
|
+
rules.push(
|
|
940
|
+
`- For the array field path pattern \`${path}\`, write items in final order and finish each item before starting the next.`
|
|
941
|
+
);
|
|
942
|
+
rules.push(
|
|
943
|
+
`- Once an item in \`${path}\` is complete, do not rewrite or reorder earlier items.`
|
|
944
|
+
);
|
|
945
|
+
}
|
|
946
|
+
for (const path of args.textDeltaFieldPaths ?? []) {
|
|
947
|
+
rules.push(
|
|
948
|
+
`- For the string field path pattern \`${path}\`, write the value progressively by appending text to the end.`
|
|
949
|
+
);
|
|
950
|
+
rules.push(
|
|
951
|
+
`- Do not rewrite or replace the earlier prefix of \`${path}\` once emitted.`
|
|
952
|
+
);
|
|
953
|
+
}
|
|
954
|
+
if (rules.length === 0) return input;
|
|
955
|
+
return [
|
|
956
|
+
input,
|
|
957
|
+
"Streaming rules:",
|
|
958
|
+
...rules,
|
|
959
|
+
"- Keep the overall response as valid JSON."
|
|
960
|
+
].join("\n\n");
|
|
961
|
+
};
|
|
962
|
+
var mergeUsage = (left, right) => {
|
|
963
|
+
if (!left) return right;
|
|
964
|
+
if (!right) return left;
|
|
965
|
+
const sum = (a, b) => a === void 0 && b === void 0 ? void 0 : (a ?? 0) + (b ?? 0);
|
|
966
|
+
const raw = left.raw || right.raw ? {
|
|
967
|
+
...left.raw ?? {},
|
|
968
|
+
...right.raw ?? {}
|
|
969
|
+
} : void 0;
|
|
970
|
+
const input = sum(left.input, right.input);
|
|
971
|
+
const output = sum(left.output, right.output);
|
|
972
|
+
const total = sum(left.total, right.total);
|
|
973
|
+
const reasoning = sum(left.reasoning, right.reasoning);
|
|
974
|
+
const cacheRead = sum(left.cacheRead, right.cacheRead);
|
|
975
|
+
const cacheWrite = sum(left.cacheWrite, right.cacheWrite);
|
|
976
|
+
const merged = {};
|
|
977
|
+
if (input !== void 0) merged.input = input;
|
|
978
|
+
if (output !== void 0) merged.output = output;
|
|
979
|
+
if (total !== void 0) merged.total = total;
|
|
980
|
+
if (reasoning !== void 0) merged.reasoning = reasoning;
|
|
981
|
+
if (cacheRead !== void 0) merged.cacheRead = cacheRead;
|
|
982
|
+
if (cacheWrite !== void 0) merged.cacheWrite = cacheWrite;
|
|
983
|
+
if (raw) merged.raw = raw;
|
|
984
|
+
return merged;
|
|
985
|
+
};
|
|
986
|
+
var mergeWarnings = (left, right) => {
|
|
987
|
+
if (!left?.length) return right;
|
|
988
|
+
if (!right?.length) return left;
|
|
989
|
+
const seen = /* @__PURE__ */ new Set();
|
|
990
|
+
const merged = [];
|
|
991
|
+
for (const warning of [...left, ...right]) {
|
|
992
|
+
const key = JSON.stringify(warning);
|
|
993
|
+
if (seen.has(key)) continue;
|
|
994
|
+
seen.add(key);
|
|
995
|
+
merged.push(warning);
|
|
996
|
+
}
|
|
997
|
+
return merged;
|
|
998
|
+
};
|
|
999
|
+
var mergeProviderMetadata = (left, right) => {
|
|
1000
|
+
if (!left) return right;
|
|
1001
|
+
if (!right) return left;
|
|
1002
|
+
return {
|
|
1003
|
+
...left,
|
|
1004
|
+
...right
|
|
1005
|
+
};
|
|
1006
|
+
};
|
|
1007
|
+
var emitReasonStructuredOutput = (args) => {
|
|
1008
|
+
if (!args.emit || !shouldEmitStructured(args.structured)) return;
|
|
1009
|
+
emitStructuredData({
|
|
1010
|
+
dataType: args.structured?.dataType ?? "reason-output",
|
|
1011
|
+
data: args.output,
|
|
1012
|
+
kind: "final",
|
|
1013
|
+
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
1014
|
+
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
1015
|
+
...args.id ? { id: args.id } : {},
|
|
1016
|
+
streamId: args.opId
|
|
1017
|
+
});
|
|
1018
|
+
};
|
|
1019
|
+
var resolveAppendFieldPaths = (cfg) => {
|
|
1020
|
+
const fields = cfg?.fields;
|
|
1021
|
+
if (!fields) return [];
|
|
1022
|
+
const appendPaths = Object.entries(fields).filter(([, mode]) => mode === "append").map(([path]) => path);
|
|
1023
|
+
for (const path of appendPaths) {
|
|
1024
|
+
assertStructuredPath(path, "useReason structured append");
|
|
1025
|
+
}
|
|
1026
|
+
return appendPaths;
|
|
1027
|
+
};
|
|
1028
|
+
var resolveSetFieldPaths = (cfg) => {
|
|
1029
|
+
const fields = cfg?.fields;
|
|
1030
|
+
if (!fields) return [];
|
|
1031
|
+
const setPaths = Object.entries(fields).filter(([, mode]) => mode === "set").map(([path]) => path);
|
|
1032
|
+
for (const path of setPaths) {
|
|
1033
|
+
assertStructuredPath(path, "useReason structured set");
|
|
1034
|
+
}
|
|
1035
|
+
return setPaths;
|
|
1036
|
+
};
|
|
1037
|
+
var resolveTextDeltaFieldPaths = (cfg) => {
|
|
1038
|
+
const fields = cfg?.fields;
|
|
1039
|
+
if (!fields) return [];
|
|
1040
|
+
const textPaths = Object.entries(fields).filter(([, mode]) => mode === "text-delta").map(([path]) => path);
|
|
1041
|
+
for (const path of textPaths) {
|
|
1042
|
+
assertStructuredPath(path, "useReason structured text-delta");
|
|
1043
|
+
}
|
|
1044
|
+
return textPaths;
|
|
1045
|
+
};
|
|
1046
|
+
var extractCompletedStringValueAt = (args) => {
|
|
1047
|
+
let value = "";
|
|
1048
|
+
for (let i = args.start; i < args.text.length; i += 1) {
|
|
1049
|
+
const ch = args.text[i];
|
|
1050
|
+
if (ch === void 0) break;
|
|
1051
|
+
if (ch === '"') {
|
|
1052
|
+
return {
|
|
1053
|
+
complete: true,
|
|
1054
|
+
value
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
if (ch !== "\\") {
|
|
1058
|
+
value += ch;
|
|
1059
|
+
continue;
|
|
1060
|
+
}
|
|
1061
|
+
const next = args.text[i + 1];
|
|
1062
|
+
if (!next) {
|
|
1063
|
+
return {
|
|
1064
|
+
complete: false
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
if (next === '"' || next === "\\" || next === "/" || next === "b" || next === "f" || next === "n" || next === "r" || next === "t") {
|
|
1068
|
+
value += next === "b" ? "\b" : next === "f" ? "\f" : next === "n" ? "\n" : next === "r" ? "\r" : next === "t" ? " " : next;
|
|
1069
|
+
i += 1;
|
|
1070
|
+
continue;
|
|
1071
|
+
}
|
|
1072
|
+
if (next === "u") {
|
|
1073
|
+
const hex = args.text.slice(i + 2, i + 6);
|
|
1074
|
+
if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
|
|
1075
|
+
return {
|
|
1076
|
+
complete: false
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
value += String.fromCharCode(Number.parseInt(hex, 16));
|
|
1080
|
+
i += 5;
|
|
1081
|
+
continue;
|
|
1082
|
+
}
|
|
1083
|
+
return {
|
|
1084
|
+
complete: false
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1087
|
+
return {
|
|
1088
|
+
complete: false
|
|
1089
|
+
};
|
|
1090
|
+
};
|
|
1091
|
+
var skipWhitespace = (text, start) => {
|
|
1092
|
+
let i = start;
|
|
1093
|
+
while (i < text.length && /\s/.test(text[i] ?? "")) i += 1;
|
|
1094
|
+
return i;
|
|
1095
|
+
};
|
|
1096
|
+
var pathSegmentMatches = (patternPart, actualPart) => patternPart === "*" || patternPart === actualPart;
|
|
1097
|
+
var pathMatchesPattern = (actualParts, patternParts) => actualParts.length === patternParts.length && actualParts.every(
|
|
1098
|
+
(part, index) => pathSegmentMatches(patternParts[index] ?? "", part)
|
|
1099
|
+
);
|
|
1100
|
+
var isPathPrefix = (prefix, full) => prefix.length <= full.length && prefix.every((part, index) => pathSegmentMatches(full[index] ?? "", part));
|
|
1101
|
+
var parseStringToken = (text, start) => {
|
|
1102
|
+
if (text[start] !== '"') return { complete: false };
|
|
1103
|
+
const parsed = extractCompletedStringValueAt({ text, start: start + 1 });
|
|
1104
|
+
if (!parsed.complete || parsed.value === void 0)
|
|
1105
|
+
return { complete: false };
|
|
1106
|
+
let inEscape = false;
|
|
1107
|
+
for (let i = start + 1; i < text.length; i += 1) {
|
|
1108
|
+
const ch = text[i];
|
|
1109
|
+
if (ch === void 0) break;
|
|
1110
|
+
if (inEscape) {
|
|
1111
|
+
inEscape = false;
|
|
1112
|
+
if (ch === "u") i += 4;
|
|
1113
|
+
continue;
|
|
1114
|
+
}
|
|
1115
|
+
if (ch === "\\") {
|
|
1116
|
+
inEscape = true;
|
|
1117
|
+
continue;
|
|
1118
|
+
}
|
|
1119
|
+
if (ch === '"') {
|
|
1120
|
+
return {
|
|
1121
|
+
complete: true,
|
|
1122
|
+
end: i + 1,
|
|
1123
|
+
value: parsed.value
|
|
1124
|
+
};
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
return { complete: false };
|
|
1128
|
+
};
|
|
1129
|
+
var skipNestedValue = (text, start) => {
|
|
1130
|
+
const opener = text[start];
|
|
1131
|
+
const closer = opener === "{" ? "}" : "]";
|
|
1132
|
+
let inString = false;
|
|
1133
|
+
let isEscaped = false;
|
|
1134
|
+
let nesting = 0;
|
|
1135
|
+
for (let i = start; i < text.length; i += 1) {
|
|
1136
|
+
const ch = text[i];
|
|
1137
|
+
if (ch === void 0) break;
|
|
1138
|
+
if (inString) {
|
|
1139
|
+
if (isEscaped) {
|
|
1140
|
+
isEscaped = false;
|
|
1141
|
+
continue;
|
|
1142
|
+
}
|
|
1143
|
+
if (ch === "\\") {
|
|
1144
|
+
isEscaped = true;
|
|
1145
|
+
continue;
|
|
1146
|
+
}
|
|
1147
|
+
if (ch === '"') {
|
|
1148
|
+
inString = false;
|
|
1149
|
+
}
|
|
1150
|
+
continue;
|
|
1151
|
+
}
|
|
1152
|
+
if (ch === '"') {
|
|
1153
|
+
inString = true;
|
|
1154
|
+
continue;
|
|
1155
|
+
}
|
|
1156
|
+
if (ch === opener) {
|
|
1157
|
+
nesting += 1;
|
|
1158
|
+
continue;
|
|
1159
|
+
}
|
|
1160
|
+
if (ch === closer) {
|
|
1161
|
+
nesting -= 1;
|
|
1162
|
+
if (nesting === 0) {
|
|
1163
|
+
return { complete: true, end: i + 1 };
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
return { complete: false };
|
|
1168
|
+
};
|
|
1169
|
+
var skipCompleteValue = (text, start) => {
|
|
1170
|
+
const i = skipWhitespace(text, start);
|
|
1171
|
+
const first = text[i];
|
|
1172
|
+
if (!first) return { complete: false };
|
|
1173
|
+
if (first === '"') {
|
|
1174
|
+
const parsed = parseStringToken(text, i);
|
|
1175
|
+
return parsed.complete && parsed.end !== void 0 ? { complete: true, end: parsed.end } : { complete: false };
|
|
1176
|
+
}
|
|
1177
|
+
if (first === "{" || first === "[") {
|
|
1178
|
+
return skipNestedValue(text, i);
|
|
1179
|
+
}
|
|
1180
|
+
for (let j = i; j < text.length; j += 1) {
|
|
1181
|
+
const ch = text[j];
|
|
1182
|
+
if (ch !== "," && ch !== "}" && ch !== "]") continue;
|
|
1183
|
+
const raw = text.slice(i, j).trim();
|
|
1184
|
+
if (!raw) return { complete: false };
|
|
1185
|
+
try {
|
|
1186
|
+
JSON.parse(raw);
|
|
1187
|
+
return { complete: true, end: j };
|
|
1188
|
+
} catch {
|
|
1189
|
+
return { complete: false };
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
return { complete: false };
|
|
1193
|
+
};
|
|
1194
|
+
var locateValueStart = (args) => {
|
|
1195
|
+
const [first] = locateValueStarts(args);
|
|
1196
|
+
return first ? {
|
|
1197
|
+
found: true,
|
|
1198
|
+
start: first.start
|
|
1199
|
+
} : {
|
|
1200
|
+
found: false
|
|
1201
|
+
};
|
|
1202
|
+
};
|
|
1203
|
+
var locateValueStarts = (args) => {
|
|
1204
|
+
const targetParts = parseStructuredPath(
|
|
1205
|
+
args.path,
|
|
1206
|
+
"useReason structured field"
|
|
1207
|
+
);
|
|
1208
|
+
const matches = [];
|
|
1209
|
+
const visitValue = (start, currentParts) => {
|
|
1210
|
+
const valueStart = skipWhitespace(args.text, start);
|
|
1211
|
+
if (pathMatchesPattern(currentParts, targetParts)) {
|
|
1212
|
+
matches.push({
|
|
1213
|
+
path: currentParts.join("."),
|
|
1214
|
+
start: valueStart
|
|
1215
|
+
});
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
if (!isPathPrefix(currentParts, targetParts)) {
|
|
1219
|
+
return;
|
|
1220
|
+
}
|
|
1221
|
+
const first = args.text[valueStart];
|
|
1222
|
+
if (first === "{") visitObject(valueStart + 1, currentParts);
|
|
1223
|
+
if (first === "[") visitArray(valueStart + 1, currentParts);
|
|
1224
|
+
};
|
|
1225
|
+
const visitObject = (start, currentParts) => {
|
|
1226
|
+
let i = skipWhitespace(args.text, start);
|
|
1227
|
+
while (i < args.text.length) {
|
|
1228
|
+
const ch = args.text[i];
|
|
1229
|
+
if (ch === "}") return;
|
|
1230
|
+
if (ch === ",") {
|
|
1231
|
+
i = skipWhitespace(args.text, i + 1);
|
|
1232
|
+
continue;
|
|
1233
|
+
}
|
|
1234
|
+
if (ch !== '"') return;
|
|
1235
|
+
const key = parseStringToken(args.text, i);
|
|
1236
|
+
if (!key.complete || key.end === void 0 || key.value === void 0) {
|
|
1237
|
+
return;
|
|
1238
|
+
}
|
|
1239
|
+
i = skipWhitespace(args.text, key.end);
|
|
1240
|
+
if (args.text[i] !== ":") return;
|
|
1241
|
+
const valueStart = skipWhitespace(args.text, i + 1);
|
|
1242
|
+
const childParts = [...currentParts, key.value];
|
|
1243
|
+
if (pathMatchesPattern(childParts, targetParts)) {
|
|
1244
|
+
matches.push({
|
|
1245
|
+
path: childParts.join("."),
|
|
1246
|
+
start: valueStart
|
|
1247
|
+
});
|
|
1248
|
+
} else if (isPathPrefix(childParts, targetParts)) {
|
|
1249
|
+
visitValue(valueStart, childParts);
|
|
1250
|
+
}
|
|
1251
|
+
const skipped = skipCompleteValue(args.text, valueStart);
|
|
1252
|
+
if (!skipped.complete || skipped.end === void 0) {
|
|
1253
|
+
return;
|
|
1254
|
+
}
|
|
1255
|
+
i = skipWhitespace(args.text, skipped.end);
|
|
1256
|
+
if (args.text[i] === ",") {
|
|
1257
|
+
i = skipWhitespace(args.text, i + 1);
|
|
1258
|
+
continue;
|
|
1259
|
+
}
|
|
1260
|
+
if (args.text[i] === "}") return;
|
|
1261
|
+
return;
|
|
1262
|
+
}
|
|
1263
|
+
};
|
|
1264
|
+
const visitArray = (start, currentParts) => {
|
|
1265
|
+
let i = skipWhitespace(args.text, start);
|
|
1266
|
+
let index = 0;
|
|
1267
|
+
while (i < args.text.length) {
|
|
1268
|
+
if (args.text[i] === "]") return;
|
|
1269
|
+
const childParts = [...currentParts, String(index)];
|
|
1270
|
+
if (pathMatchesPattern(childParts, targetParts)) {
|
|
1271
|
+
matches.push({
|
|
1272
|
+
path: childParts.join("."),
|
|
1273
|
+
start: i
|
|
1274
|
+
});
|
|
1275
|
+
} else if (isPathPrefix(childParts, targetParts)) {
|
|
1276
|
+
visitValue(i, childParts);
|
|
1277
|
+
}
|
|
1278
|
+
const skipped = skipCompleteValue(args.text, i);
|
|
1279
|
+
if (!skipped.complete || skipped.end === void 0) {
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
i = skipWhitespace(args.text, skipped.end);
|
|
1283
|
+
if (args.text[i] === ",") {
|
|
1284
|
+
index += 1;
|
|
1285
|
+
i = skipWhitespace(args.text, i + 1);
|
|
1286
|
+
continue;
|
|
1287
|
+
}
|
|
1288
|
+
if (args.text[i] === "]") return;
|
|
1289
|
+
return;
|
|
1290
|
+
}
|
|
1291
|
+
};
|
|
1292
|
+
visitValue(0, []);
|
|
1293
|
+
return matches;
|
|
1294
|
+
};
|
|
1295
|
+
var extractCompletedValueAt = (args) => {
|
|
1296
|
+
const start = skipWhitespace(args.text, args.start);
|
|
1297
|
+
const first = args.text[start];
|
|
1298
|
+
if (!first) return { complete: false };
|
|
1299
|
+
if (first === '"') {
|
|
1300
|
+
const parsed = extractCompletedStringValueAt({
|
|
1301
|
+
text: args.text,
|
|
1302
|
+
start: start + 1
|
|
1303
|
+
});
|
|
1304
|
+
return parsed.complete ? {
|
|
1305
|
+
complete: true,
|
|
1306
|
+
value: parsed.value
|
|
1307
|
+
} : {
|
|
1308
|
+
complete: false
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
if (first === "{") {
|
|
1312
|
+
return extractCompletedNestedValueAt({
|
|
1313
|
+
text: args.text,
|
|
1314
|
+
start,
|
|
1315
|
+
opener: "{",
|
|
1316
|
+
closer: "}"
|
|
1317
|
+
});
|
|
1318
|
+
}
|
|
1319
|
+
if (first === "[") {
|
|
1320
|
+
return extractCompletedNestedValueAt({
|
|
1321
|
+
text: args.text,
|
|
1322
|
+
start,
|
|
1323
|
+
opener: "[",
|
|
1324
|
+
closer: "]"
|
|
1325
|
+
});
|
|
1326
|
+
}
|
|
1327
|
+
for (let i = start; i < args.text.length; i += 1) {
|
|
1328
|
+
const ch = args.text[i];
|
|
1329
|
+
if (ch === void 0) break;
|
|
1330
|
+
if (ch !== "," && ch !== "}" && ch !== "]") continue;
|
|
1331
|
+
const raw = args.text.slice(start, i).trim();
|
|
1332
|
+
if (!raw) return { complete: false };
|
|
1333
|
+
try {
|
|
1334
|
+
return {
|
|
1335
|
+
complete: true,
|
|
1336
|
+
value: JSON.parse(raw)
|
|
1337
|
+
};
|
|
1338
|
+
} catch {
|
|
1339
|
+
return { complete: false };
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
return { complete: false };
|
|
1343
|
+
};
|
|
1344
|
+
var extractCompletedNestedValueAt = (args) => {
|
|
1345
|
+
let inString = false;
|
|
1346
|
+
let isEscaped = false;
|
|
1347
|
+
let nesting = 0;
|
|
1348
|
+
for (let i = args.start; i < args.text.length; i += 1) {
|
|
1349
|
+
const ch = args.text[i];
|
|
1350
|
+
if (ch === void 0) break;
|
|
1351
|
+
if (inString) {
|
|
1352
|
+
if (isEscaped) {
|
|
1353
|
+
isEscaped = false;
|
|
1354
|
+
continue;
|
|
1355
|
+
}
|
|
1356
|
+
if (ch === "\\") {
|
|
1357
|
+
isEscaped = true;
|
|
1358
|
+
continue;
|
|
1359
|
+
}
|
|
1360
|
+
if (ch === '"') {
|
|
1361
|
+
inString = false;
|
|
1362
|
+
}
|
|
1363
|
+
continue;
|
|
1364
|
+
}
|
|
1365
|
+
if (ch === '"') {
|
|
1366
|
+
inString = true;
|
|
1367
|
+
continue;
|
|
1368
|
+
}
|
|
1369
|
+
if (ch === args.opener) {
|
|
1370
|
+
nesting += 1;
|
|
1371
|
+
continue;
|
|
1372
|
+
}
|
|
1373
|
+
if (ch === args.closer) {
|
|
1374
|
+
nesting -= 1;
|
|
1375
|
+
if (nesting === 0) {
|
|
1376
|
+
const raw = args.text.slice(args.start, i + 1);
|
|
1377
|
+
try {
|
|
1378
|
+
return {
|
|
1379
|
+
complete: true,
|
|
1380
|
+
value: JSON.parse(raw)
|
|
1381
|
+
};
|
|
1382
|
+
} catch {
|
|
1383
|
+
return {
|
|
1384
|
+
complete: false
|
|
1385
|
+
};
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
return {
|
|
1391
|
+
complete: false
|
|
1392
|
+
};
|
|
1393
|
+
};
|
|
1394
|
+
var extractCompletedFieldValues = (args) => {
|
|
1395
|
+
const located = locateValueStarts(args);
|
|
1396
|
+
const values = [];
|
|
1397
|
+
for (const match of located) {
|
|
1398
|
+
const parsed = extractCompletedValueAt({
|
|
1399
|
+
text: args.text,
|
|
1400
|
+
start: match.start
|
|
1401
|
+
});
|
|
1402
|
+
if (!parsed.complete) continue;
|
|
1403
|
+
values.push({
|
|
1404
|
+
path: match.path,
|
|
1405
|
+
value: parsed.value
|
|
1406
|
+
});
|
|
1407
|
+
}
|
|
1408
|
+
return values;
|
|
1409
|
+
};
|
|
1410
|
+
var extractCompletedArrayItems = (args) => {
|
|
1411
|
+
const located = locateValueStart(args);
|
|
1412
|
+
if (!located.found || located.start === void 0) return [];
|
|
1413
|
+
const arrayStart = skipWhitespace(args.text, located.start);
|
|
1414
|
+
if (args.text[arrayStart] !== "[") return [];
|
|
1415
|
+
const start = arrayStart + 1;
|
|
1416
|
+
const items = [];
|
|
1417
|
+
let inString = false;
|
|
1418
|
+
let isEscaped = false;
|
|
1419
|
+
let nesting = 0;
|
|
1420
|
+
let itemStart = -1;
|
|
1421
|
+
for (let i = start; i < args.text.length; i += 1) {
|
|
1422
|
+
const ch = args.text[i];
|
|
1423
|
+
if (ch === void 0) break;
|
|
1424
|
+
if (inString) {
|
|
1425
|
+
if (isEscaped) {
|
|
1426
|
+
isEscaped = false;
|
|
1427
|
+
continue;
|
|
1428
|
+
}
|
|
1429
|
+
if (ch === "\\") {
|
|
1430
|
+
isEscaped = true;
|
|
1431
|
+
continue;
|
|
1432
|
+
}
|
|
1433
|
+
if (ch === '"') {
|
|
1434
|
+
inString = false;
|
|
1435
|
+
}
|
|
1436
|
+
continue;
|
|
1437
|
+
}
|
|
1438
|
+
if (ch === '"') {
|
|
1439
|
+
inString = true;
|
|
1440
|
+
if (itemStart < 0) itemStart = i;
|
|
1441
|
+
continue;
|
|
1442
|
+
}
|
|
1443
|
+
if (itemStart < 0) {
|
|
1444
|
+
if (/\s/.test(ch) || ch === ",") continue;
|
|
1445
|
+
if (ch === "]") break;
|
|
1446
|
+
itemStart = i;
|
|
1447
|
+
if (ch === "{" || ch === "[") {
|
|
1448
|
+
nesting = 1;
|
|
1449
|
+
} else {
|
|
1450
|
+
nesting = 0;
|
|
1451
|
+
}
|
|
1452
|
+
continue;
|
|
1453
|
+
}
|
|
1454
|
+
if (ch === "{" || ch === "[") {
|
|
1455
|
+
nesting += 1;
|
|
1456
|
+
continue;
|
|
1457
|
+
}
|
|
1458
|
+
if (ch === "}" || ch === "]") {
|
|
1459
|
+
if (nesting > 0) {
|
|
1460
|
+
nesting -= 1;
|
|
1461
|
+
continue;
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
if (nesting === 0 && (ch === "," || ch === "]")) {
|
|
1465
|
+
const raw = args.text.slice(itemStart, i).trim();
|
|
1466
|
+
if (raw) {
|
|
1467
|
+
try {
|
|
1468
|
+
items.push(JSON.parse(raw));
|
|
1469
|
+
} catch {
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
itemStart = -1;
|
|
1473
|
+
if (ch === "]") break;
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
return items;
|
|
1477
|
+
};
|
|
1478
|
+
var extractCompletedArrayItemGroups = (args) => {
|
|
1479
|
+
return locateValueStarts(args).flatMap((match) => {
|
|
1480
|
+
const items = extractCompletedArrayItems({
|
|
1481
|
+
text: args.text,
|
|
1482
|
+
path: match.path
|
|
1483
|
+
});
|
|
1484
|
+
return items.length > 0 ? [
|
|
1485
|
+
{
|
|
1486
|
+
path: match.path,
|
|
1487
|
+
items
|
|
1488
|
+
}
|
|
1489
|
+
] : [];
|
|
1490
|
+
});
|
|
1491
|
+
};
|
|
1492
|
+
var extractStreamingStringValue = (args) => {
|
|
1493
|
+
const located = locateValueStart(args);
|
|
1494
|
+
if (!located.found || located.start === void 0) {
|
|
1495
|
+
return {
|
|
1496
|
+
found: located.found,
|
|
1497
|
+
complete: false,
|
|
1498
|
+
value: ""
|
|
1499
|
+
};
|
|
1500
|
+
}
|
|
1501
|
+
const stringStart = skipWhitespace(args.text, located.start);
|
|
1502
|
+
if (args.text[stringStart] !== '"') {
|
|
1503
|
+
return {
|
|
1504
|
+
found: true,
|
|
1505
|
+
complete: false,
|
|
1506
|
+
value: ""
|
|
1507
|
+
};
|
|
1508
|
+
}
|
|
1509
|
+
let value = "";
|
|
1510
|
+
for (let i = stringStart + 1; i < args.text.length; i += 1) {
|
|
1511
|
+
const ch = args.text[i];
|
|
1512
|
+
if (ch === void 0) break;
|
|
1513
|
+
if (ch === '"') {
|
|
1514
|
+
return {
|
|
1515
|
+
found: true,
|
|
1516
|
+
complete: true,
|
|
1517
|
+
value
|
|
1518
|
+
};
|
|
1519
|
+
}
|
|
1520
|
+
if (ch !== "\\") {
|
|
1521
|
+
value += ch;
|
|
1522
|
+
continue;
|
|
1523
|
+
}
|
|
1524
|
+
const next = args.text[i + 1];
|
|
1525
|
+
if (!next) {
|
|
1526
|
+
return {
|
|
1527
|
+
found: true,
|
|
1528
|
+
complete: false,
|
|
1529
|
+
value
|
|
1530
|
+
};
|
|
1531
|
+
}
|
|
1532
|
+
if (next === '"' || next === "\\" || next === "/" || next === "b" || next === "f" || next === "n" || next === "r" || next === "t") {
|
|
1533
|
+
value += next === "b" ? "\b" : next === "f" ? "\f" : next === "n" ? "\n" : next === "r" ? "\r" : next === "t" ? " " : next;
|
|
1534
|
+
i += 1;
|
|
1535
|
+
continue;
|
|
1536
|
+
}
|
|
1537
|
+
if (next === "u") {
|
|
1538
|
+
const hex = args.text.slice(i + 2, i + 6);
|
|
1539
|
+
if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
|
|
1540
|
+
return {
|
|
1541
|
+
found: true,
|
|
1542
|
+
complete: false,
|
|
1543
|
+
value
|
|
1544
|
+
};
|
|
1545
|
+
}
|
|
1546
|
+
value += String.fromCharCode(Number.parseInt(hex, 16));
|
|
1547
|
+
i += 5;
|
|
1548
|
+
continue;
|
|
1549
|
+
}
|
|
1550
|
+
return {
|
|
1551
|
+
found: true,
|
|
1552
|
+
complete: false,
|
|
1553
|
+
value
|
|
1554
|
+
};
|
|
1555
|
+
}
|
|
1556
|
+
return {
|
|
1557
|
+
found: true,
|
|
1558
|
+
complete: false,
|
|
1559
|
+
value
|
|
1560
|
+
};
|
|
1561
|
+
};
|
|
1562
|
+
var extractStreamingStringValues = (args) => {
|
|
1563
|
+
return locateValueStarts(args).flatMap((match) => {
|
|
1564
|
+
const current = extractStreamingStringValue({
|
|
1565
|
+
text: args.text,
|
|
1566
|
+
path: match.path
|
|
1567
|
+
});
|
|
1568
|
+
return current.found ? [
|
|
1569
|
+
{
|
|
1570
|
+
path: match.path,
|
|
1571
|
+
complete: current.complete,
|
|
1572
|
+
value: current.value
|
|
1573
|
+
}
|
|
1574
|
+
] : [];
|
|
1575
|
+
});
|
|
1576
|
+
};
|
|
1577
|
+
var toErrorMessage2 = (error) => error instanceof Error ? error.message : String(error);
|
|
1578
|
+
var toToolDefinitions = (tools) => tools.map((tool) => ({
|
|
1579
|
+
name: tool.name,
|
|
1580
|
+
...tool.title ? { title: tool.title } : {},
|
|
1581
|
+
...tool.description ? { description: tool.description } : {},
|
|
1582
|
+
inputSchema: tool.inputSchema,
|
|
1583
|
+
...tool.outputSchema !== void 0 ? { outputSchema: tool.outputSchema } : {},
|
|
1584
|
+
...tool.annotations !== void 0 ? { annotations: tool.annotations } : {},
|
|
1585
|
+
...tool.metadata !== void 0 ? { metadata: tool.metadata } : {}
|
|
1586
|
+
}));
|
|
1587
|
+
var resolveToolExecutionBoolean = (value, toolName, fallback) => {
|
|
1588
|
+
if (typeof value === "boolean") return value;
|
|
1589
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
1590
|
+
return value[toolName] ?? fallback;
|
|
1591
|
+
}
|
|
1592
|
+
return fallback;
|
|
1593
|
+
};
|
|
1594
|
+
var normalizeToolResult = (toolCall, value) => {
|
|
1595
|
+
if (value && typeof value === "object" && !Array.isArray(value) && "toolCallId" in value && "name" in value && "content" in value) {
|
|
1596
|
+
return value;
|
|
1597
|
+
}
|
|
1598
|
+
let content;
|
|
1599
|
+
try {
|
|
1600
|
+
content = typeof value === "string" ? value : JSON.stringify(value);
|
|
1601
|
+
} catch {
|
|
1602
|
+
content = String(value);
|
|
1603
|
+
}
|
|
1604
|
+
return {
|
|
1605
|
+
toolCallId: toolCall.id,
|
|
1606
|
+
name: toolCall.name,
|
|
1607
|
+
content,
|
|
1608
|
+
...value !== void 0 && value !== null && typeof value === "object" && !Array.isArray(value) ? { structuredContent: value } : {}
|
|
1609
|
+
};
|
|
1610
|
+
};
|
|
1611
|
+
var closeOwnedTools = async (tools) => {
|
|
1612
|
+
const closeFns = /* @__PURE__ */ new Set();
|
|
1613
|
+
for (const tool of tools) {
|
|
1614
|
+
if (tool.closeAfterUse === false || !tool.close) continue;
|
|
1615
|
+
closeFns.add(tool.close);
|
|
1616
|
+
}
|
|
1617
|
+
await Promise.all(
|
|
1618
|
+
[...closeFns].map(async (close) => {
|
|
1619
|
+
await close();
|
|
1620
|
+
})
|
|
1621
|
+
);
|
|
1622
|
+
};
|
|
1623
|
+
var createInitialMessages = (args) => [
|
|
1624
|
+
...typeof args.system === "string" && args.system.length > 0 ? [{ role: "system", content: args.system }] : [],
|
|
1625
|
+
{ role: "user", content: String(args.input ?? "") }
|
|
1626
|
+
];
|
|
1627
|
+
var runReasonToolLoop = async (args) => {
|
|
1628
|
+
const { useReasonArgs, id, opId, traceSpan } = args;
|
|
1629
|
+
const ctx = getHookContext();
|
|
1630
|
+
const tools = useReasonArgs.tools ?? [];
|
|
1631
|
+
const toolByName = /* @__PURE__ */ new Map();
|
|
1632
|
+
let validationCompleted = false;
|
|
1633
|
+
try {
|
|
1634
|
+
for (const tool of tools) {
|
|
1635
|
+
if (toolByName.has(tool.name)) {
|
|
1636
|
+
throw new Error(
|
|
1637
|
+
`useReason received duplicate tool name "${tool.name}".`
|
|
1638
|
+
);
|
|
1639
|
+
}
|
|
1640
|
+
toolByName.set(tool.name, tool);
|
|
1641
|
+
}
|
|
1642
|
+
if (useReasonArgs.interrupt) {
|
|
1643
|
+
throw new Error(
|
|
1644
|
+
"useReason tools cannot be combined with useReason interrupt mode yet. Use toolExecution.approval for tool approval."
|
|
1645
|
+
);
|
|
1646
|
+
}
|
|
1647
|
+
validationCompleted = true;
|
|
1648
|
+
} finally {
|
|
1649
|
+
if (!validationCompleted) {
|
|
1650
|
+
await closeOwnedTools(tools);
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
const toolDefinitions = toToolDefinitions(tools);
|
|
1654
|
+
const maxSteps = Math.max(1, useReasonArgs.toolExecution?.maxSteps ?? 3);
|
|
1655
|
+
const messages = createInitialMessages({
|
|
1656
|
+
system: useReasonArgs.system,
|
|
1657
|
+
input: useReasonArgs.input
|
|
1658
|
+
});
|
|
1659
|
+
const steps = [];
|
|
1660
|
+
const allToolCalls = [];
|
|
1661
|
+
const allToolResults = [];
|
|
1662
|
+
let finalText = "";
|
|
1663
|
+
let finalRaw;
|
|
1664
|
+
let finalOutput;
|
|
1665
|
+
let aggregatedUsage;
|
|
1666
|
+
let finalFinishReason;
|
|
1667
|
+
let aggregatedProviderMetadata;
|
|
1668
|
+
let aggregatedWarnings;
|
|
1669
|
+
const emitToolEvent = (event, payload) => {
|
|
1670
|
+
ctx.node.emit(event, {
|
|
1671
|
+
...payload,
|
|
1672
|
+
node: ctx.node.graph.node,
|
|
1673
|
+
...id ? { id } : {},
|
|
1674
|
+
opId
|
|
1675
|
+
});
|
|
1676
|
+
};
|
|
1677
|
+
try {
|
|
1678
|
+
for (let stepIndex = 0; stepIndex < maxSteps; stepIndex += 1) {
|
|
1679
|
+
traceSpan?.addEvent?.("useReason.tool-step.start", {
|
|
1680
|
+
stepIndex,
|
|
1681
|
+
toolCount: toolDefinitions.length
|
|
1682
|
+
});
|
|
1683
|
+
const step = await reasonEngine(
|
|
1684
|
+
{
|
|
1685
|
+
...useReasonArgs,
|
|
1686
|
+
tools: toolDefinitions,
|
|
1687
|
+
messages,
|
|
1688
|
+
emit: false,
|
|
1689
|
+
stream: false
|
|
1690
|
+
},
|
|
1691
|
+
{ ...id ? { id } : {}, opId }
|
|
1692
|
+
);
|
|
1693
|
+
finalText = step.text;
|
|
1694
|
+
finalRaw = step.raw;
|
|
1695
|
+
aggregatedUsage = mergeUsage(aggregatedUsage, step.usage);
|
|
1696
|
+
finalFinishReason = step.finishReason;
|
|
1697
|
+
aggregatedProviderMetadata = mergeProviderMetadata(
|
|
1698
|
+
aggregatedProviderMetadata,
|
|
1699
|
+
step.providerMetadata
|
|
1700
|
+
);
|
|
1701
|
+
aggregatedWarnings = mergeWarnings(aggregatedWarnings, step.warnings);
|
|
1702
|
+
const toolCalls = step.toolCalls ?? [];
|
|
1703
|
+
const toolResults = [];
|
|
1704
|
+
steps.push({
|
|
1705
|
+
stepIndex,
|
|
1706
|
+
text: step.text,
|
|
1707
|
+
toolCalls,
|
|
1708
|
+
toolResults,
|
|
1709
|
+
...step.usage ? { usage: step.usage } : {},
|
|
1710
|
+
...step.finishReason ? { finishReason: step.finishReason } : {},
|
|
1711
|
+
...step.providerMetadata ? { providerMetadata: step.providerMetadata } : {},
|
|
1712
|
+
...step.warnings ? { warnings: step.warnings } : {}
|
|
1713
|
+
});
|
|
1714
|
+
if (toolCalls.length === 0) {
|
|
1715
|
+
if ((useReasonArgs.emit ?? true) && step.text.length > 0) {
|
|
1716
|
+
emitToolEvent("text-start", {});
|
|
1717
|
+
emitToolEvent("text-delta", { delta: step.text });
|
|
1718
|
+
emitToolEvent("text-end", {});
|
|
1719
|
+
}
|
|
1720
|
+
break;
|
|
1721
|
+
}
|
|
1722
|
+
allToolCalls.push(...toolCalls);
|
|
1723
|
+
messages.push({
|
|
1724
|
+
role: "assistant",
|
|
1725
|
+
content: step.text,
|
|
1726
|
+
toolCalls
|
|
1727
|
+
});
|
|
1728
|
+
for (const toolCall of toolCalls) {
|
|
1729
|
+
const tool = toolByName.get(toolCall.name);
|
|
1730
|
+
if (!tool) {
|
|
1731
|
+
throw new Error(
|
|
1732
|
+
`Model requested unknown tool "${toolCall.name}" from useReason.`
|
|
1733
|
+
);
|
|
1734
|
+
}
|
|
1735
|
+
const shouldEmitTool = resolveToolExecutionBoolean(
|
|
1736
|
+
useReasonArgs.toolExecution?.emit,
|
|
1737
|
+
tool.name,
|
|
1738
|
+
false
|
|
1739
|
+
);
|
|
1740
|
+
const shouldApproveTool = resolveToolExecutionBoolean(
|
|
1741
|
+
useReasonArgs.toolExecution?.approval,
|
|
1742
|
+
tool.name,
|
|
1743
|
+
false
|
|
1744
|
+
);
|
|
1745
|
+
traceSpan?.addEvent?.("useReason.tool-call.start", {
|
|
1746
|
+
stepIndex,
|
|
1747
|
+
tool: tool.name,
|
|
1748
|
+
toolCallId: toolCall.id
|
|
1749
|
+
});
|
|
1750
|
+
if (shouldEmitTool) {
|
|
1751
|
+
emitToolEvent("tool-call-start", {
|
|
1752
|
+
tool: tool.name,
|
|
1753
|
+
toolCallId: toolCall.id,
|
|
1754
|
+
input: toolCall.input
|
|
1755
|
+
});
|
|
1756
|
+
}
|
|
1757
|
+
if (shouldApproveTool) {
|
|
1758
|
+
const approval = await awaitInterruptInternal({
|
|
1759
|
+
id: `tool:${toolCall.id}`,
|
|
1760
|
+
request: {
|
|
1761
|
+
kind: "choice",
|
|
1762
|
+
question: `Approve ${tool.name}?`,
|
|
1763
|
+
options: [
|
|
1764
|
+
{ id: "approve", label: "Approve" },
|
|
1765
|
+
{ id: "deny", label: "Deny" }
|
|
1766
|
+
],
|
|
1767
|
+
meta: {
|
|
1768
|
+
tool: tool.name,
|
|
1769
|
+
toolCallId: toolCall.id,
|
|
1770
|
+
input: toolCall.input
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
});
|
|
1774
|
+
const approved = Array.isArray(approval) ? approval.includes("approve") : approval === "approve";
|
|
1775
|
+
if (!approved) {
|
|
1776
|
+
const denied = {
|
|
1777
|
+
toolCallId: toolCall.id,
|
|
1778
|
+
name: tool.name,
|
|
1779
|
+
content: "Tool call denied by user.",
|
|
1780
|
+
isError: true
|
|
1781
|
+
};
|
|
1782
|
+
toolResults.push(denied);
|
|
1783
|
+
allToolResults.push(denied);
|
|
1784
|
+
if (shouldEmitTool) {
|
|
1785
|
+
emitToolEvent("tool-call-result", {
|
|
1786
|
+
tool: tool.name,
|
|
1787
|
+
toolCallId: toolCall.id,
|
|
1788
|
+
content: denied.content,
|
|
1789
|
+
isError: true
|
|
1790
|
+
});
|
|
1791
|
+
}
|
|
1792
|
+
continue;
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
try {
|
|
1796
|
+
const rawResult = await tool.execute(toolCall.input, {
|
|
1797
|
+
toolCallId: toolCall.id,
|
|
1798
|
+
...useReasonArgs.abortSignal ? { abortSignal: useReasonArgs.abortSignal } : {}
|
|
1799
|
+
});
|
|
1800
|
+
const result2 = normalizeToolResult(toolCall, rawResult);
|
|
1801
|
+
toolResults.push(result2);
|
|
1802
|
+
allToolResults.push(result2);
|
|
1803
|
+
if (shouldEmitTool) {
|
|
1804
|
+
emitToolEvent("tool-call-result", {
|
|
1805
|
+
tool: result2.name,
|
|
1806
|
+
toolCallId: result2.toolCallId,
|
|
1807
|
+
content: result2.content,
|
|
1808
|
+
...result2.structuredContent !== void 0 ? { structuredContent: result2.structuredContent } : {},
|
|
1809
|
+
...result2.isError !== void 0 ? { isError: result2.isError } : {}
|
|
1810
|
+
});
|
|
1811
|
+
}
|
|
1812
|
+
traceSpan?.addEvent?.("useReason.tool-call.complete", {
|
|
1813
|
+
stepIndex,
|
|
1814
|
+
tool: result2.name,
|
|
1815
|
+
toolCallId: result2.toolCallId,
|
|
1816
|
+
isError: Boolean(result2.isError)
|
|
1817
|
+
});
|
|
1818
|
+
} catch (error) {
|
|
1819
|
+
const result2 = {
|
|
1820
|
+
toolCallId: toolCall.id,
|
|
1821
|
+
name: tool.name,
|
|
1822
|
+
content: toErrorMessage2(error),
|
|
1823
|
+
isError: true
|
|
1824
|
+
};
|
|
1825
|
+
toolResults.push(result2);
|
|
1826
|
+
allToolResults.push(result2);
|
|
1827
|
+
if (shouldEmitTool) {
|
|
1828
|
+
emitToolEvent("tool-call-error", {
|
|
1829
|
+
tool: tool.name,
|
|
1830
|
+
toolCallId: toolCall.id,
|
|
1831
|
+
message: result2.content
|
|
1832
|
+
});
|
|
1833
|
+
}
|
|
1834
|
+
traceSpan?.addEvent?.("useReason.tool-call.error", {
|
|
1835
|
+
stepIndex,
|
|
1836
|
+
tool: tool.name,
|
|
1837
|
+
toolCallId: toolCall.id,
|
|
1838
|
+
message: result2.content
|
|
1839
|
+
});
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
const currentStep = steps[steps.length - 1];
|
|
1843
|
+
if (currentStep) {
|
|
1844
|
+
steps[steps.length - 1] = {
|
|
1845
|
+
...currentStep,
|
|
1846
|
+
toolResults
|
|
1847
|
+
};
|
|
1848
|
+
}
|
|
1849
|
+
for (const result2 of toolResults) {
|
|
1850
|
+
messages.push({
|
|
1851
|
+
role: "tool",
|
|
1852
|
+
content: result2.content,
|
|
1853
|
+
toolCallId: result2.toolCallId,
|
|
1854
|
+
name: result2.name,
|
|
1855
|
+
...result2.structuredContent !== void 0 ? { structuredContent: result2.structuredContent } : {},
|
|
1856
|
+
...result2.isError !== void 0 ? { isError: result2.isError } : {},
|
|
1857
|
+
...result2.raw !== void 0 ? { raw: result2.raw } : {}
|
|
1858
|
+
});
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
if (steps.length >= maxSteps && steps.at(-1)?.toolCalls.length) {
|
|
1862
|
+
throw new Error(
|
|
1863
|
+
`useReason tool loop reached maxSteps (${maxSteps}) before producing a final response.`
|
|
1864
|
+
);
|
|
1865
|
+
}
|
|
1866
|
+
if (useReasonArgs.outputSchema) {
|
|
1867
|
+
finalOutput = parseReasonOutputWithSchema({
|
|
1868
|
+
text: finalText,
|
|
1869
|
+
schema: useReasonArgs.outputSchema,
|
|
1870
|
+
...finalFinishReason ? { finishReason: finalFinishReason } : {},
|
|
1871
|
+
label: "useReason output"
|
|
1872
|
+
});
|
|
1873
|
+
}
|
|
1874
|
+
if (finalOutput !== void 0) {
|
|
1875
|
+
emitReasonStructuredOutput({
|
|
1876
|
+
...id ? { id } : {},
|
|
1877
|
+
opId,
|
|
1878
|
+
output: finalOutput,
|
|
1879
|
+
structured: useReasonArgs.structured,
|
|
1880
|
+
emit: useReasonArgs.emit ?? true
|
|
1881
|
+
});
|
|
1882
|
+
}
|
|
1883
|
+
const result = {
|
|
1884
|
+
...id ? { id } : {},
|
|
1885
|
+
opId,
|
|
1886
|
+
text: finalText,
|
|
1887
|
+
...finalRaw !== void 0 ? { raw: finalRaw } : {},
|
|
1888
|
+
...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
|
|
1889
|
+
...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
|
|
1890
|
+
...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
|
|
1891
|
+
...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
|
|
1892
|
+
...finalOutput !== void 0 ? { output: finalOutput } : {},
|
|
1893
|
+
toolCalls: allToolCalls,
|
|
1894
|
+
toolResults: allToolResults,
|
|
1895
|
+
steps
|
|
1896
|
+
};
|
|
1897
|
+
traceSpan?.end?.({
|
|
1898
|
+
...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
|
|
1899
|
+
...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
|
|
1900
|
+
...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
|
|
1901
|
+
...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
|
|
1902
|
+
attributes: {
|
|
1903
|
+
textLength: finalText.length,
|
|
1904
|
+
toolCallCount: allToolCalls.length,
|
|
1905
|
+
toolResultCount: allToolResults.length,
|
|
1906
|
+
toolStepCount: steps.length
|
|
1907
|
+
},
|
|
1908
|
+
telemetry: {
|
|
1909
|
+
...useReasonArgs.telemetry ?? {},
|
|
1910
|
+
output: useReasonArgs.telemetry?.output ?? finalOutput ?? finalText
|
|
1911
|
+
}
|
|
1912
|
+
});
|
|
1913
|
+
return result;
|
|
1914
|
+
} catch (error) {
|
|
1915
|
+
traceSpan?.fail?.(error, {
|
|
1916
|
+
attributes: {
|
|
1917
|
+
toolStepCount: steps.length,
|
|
1918
|
+
toolCallCount: allToolCalls.length
|
|
1919
|
+
}
|
|
1920
|
+
});
|
|
1921
|
+
throw error;
|
|
1922
|
+
} finally {
|
|
1923
|
+
await closeOwnedTools(tools);
|
|
1924
|
+
}
|
|
1925
|
+
};
|
|
1926
|
+
var shouldStreamReasonStructured = (args) => (args.emit ?? true) && shouldStreamStructured(args.structured);
|
|
1927
|
+
var resolveEffectiveReasoningIncludeThoughts = (args) => args.reasoning?.includeThoughts ?? args.model.options?.reasoning?.includeThoughts ?? false;
|
|
1928
|
+
var resolveEffectiveResponseFormatType = (args) => args.responseFormat?.type ?? args.model.options?.responseFormat?.type;
|
|
1929
|
+
var assertReasoningThoughtsCompatibility = (args) => {
|
|
1930
|
+
if (!resolveEffectiveReasoningIncludeThoughts(args)) return;
|
|
1931
|
+
const usesStructuredOutput = Boolean(args.outputSchema) || Boolean(args.interrupt) || Boolean(args.structured) || resolveEffectiveResponseFormatType(args) === "json";
|
|
1932
|
+
if (!usesStructuredOutput) return;
|
|
1933
|
+
throw new Error(
|
|
1934
|
+
"useReason does not support reasoning.includeThoughts with structured output, interrupt mode, or JSON responseFormat. Disable includeThoughts for this call."
|
|
1935
|
+
);
|
|
1936
|
+
};
|
|
1937
|
+
async function useReason(args) {
|
|
1938
|
+
assertReasoningThoughtsCompatibility(args);
|
|
1939
|
+
const ctx = getHookContext();
|
|
1940
|
+
const id = typeof args.id === "string" && args.id.length > 0 ? args.id : void 0;
|
|
1941
|
+
const opId = createRuntimeId2();
|
|
1942
|
+
const checkpointKey = resolveReasonCheckpointKey({
|
|
1943
|
+
...id ? { id } : {},
|
|
1944
|
+
autoIndex: ctx.reasonCallIndex++
|
|
1945
|
+
});
|
|
1946
|
+
const existingCompleted = readReasonCompletedCheckpoint(
|
|
1947
|
+
ctx.currentNodeState.byKey[checkpointKey]
|
|
1948
|
+
);
|
|
1949
|
+
if (existingCompleted) {
|
|
1950
|
+
return existingCompleted.result;
|
|
1951
|
+
}
|
|
1952
|
+
const existingCheckpoint = readReasonCheckpoint(
|
|
1953
|
+
ctx.currentNodeState.byKey[checkpointKey]
|
|
1954
|
+
);
|
|
1955
|
+
const reasonMeta = typeof id === "string" ? { id, opId } : { opId };
|
|
1956
|
+
const traceSpan = getReasonTraceAdapter()?.startSpan({
|
|
1957
|
+
name: "useReason",
|
|
1958
|
+
attributes: {
|
|
1959
|
+
...id ? { id } : {},
|
|
1960
|
+
opId,
|
|
1961
|
+
nodeId: ctx.node.graph.node,
|
|
1962
|
+
providerId: args.model.provider.id,
|
|
1963
|
+
modelId: args.model.modelId,
|
|
1964
|
+
stream: args.stream ?? args.model.options?.streaming ?? true,
|
|
1965
|
+
emit: args.emit ?? true,
|
|
1966
|
+
...args.telemetry?.operation ? { operation: args.telemetry.operation } : {},
|
|
1967
|
+
...args.telemetry?.prompt?.name ? { promptName: args.telemetry.prompt.name } : {},
|
|
1968
|
+
...args.telemetry?.prompt?.version !== void 0 ? { promptVersion: args.telemetry.prompt.version } : {},
|
|
1969
|
+
...args.telemetry?.prompt?.type ? { promptType: args.telemetry.prompt.type } : {},
|
|
1970
|
+
hasOutputSchema: Boolean(args.outputSchema),
|
|
1971
|
+
hasInterrupt: Boolean(args.interrupt),
|
|
1972
|
+
hasStructured: Boolean(args.structured),
|
|
1973
|
+
hasTools: Boolean(args.tools?.length)
|
|
1974
|
+
},
|
|
1975
|
+
telemetry: {
|
|
1976
|
+
...args.telemetry ?? {},
|
|
1977
|
+
input: args.telemetry?.input ?? args.input
|
|
1978
|
+
}
|
|
1979
|
+
});
|
|
1980
|
+
if (args.tools?.length) {
|
|
1981
|
+
return runReasonToolLoop({
|
|
1982
|
+
useReasonArgs: args,
|
|
1983
|
+
...id ? { id } : {},
|
|
1984
|
+
opId,
|
|
1985
|
+
...traceSpan ? { traceSpan } : {}
|
|
1986
|
+
});
|
|
1987
|
+
}
|
|
1988
|
+
const suppressTextStream = Boolean(args.outputSchema || args.interrupt);
|
|
1989
|
+
const setFieldPaths = resolveSetFieldPaths(args.structured);
|
|
1990
|
+
const appendFieldPaths = resolveAppendFieldPaths(args.structured);
|
|
1991
|
+
const textDeltaFieldPaths = resolveTextDeltaFieldPaths(args.structured);
|
|
1992
|
+
const useStructuredIncrementalStreaming = Boolean(
|
|
1993
|
+
args.outputSchema && !args.interrupt && (setFieldPaths.length > 0 || appendFieldPaths.length > 0 || textDeltaFieldPaths.length > 0) && args.stream !== false && shouldStreamReasonStructured(args)
|
|
1994
|
+
);
|
|
1995
|
+
let firstText = "";
|
|
1996
|
+
let firstRaw;
|
|
1997
|
+
let firstOutput;
|
|
1998
|
+
let firstInterruptRequest;
|
|
1999
|
+
let firstInterruptRequired = false;
|
|
2000
|
+
let finalText = "";
|
|
2001
|
+
let finalRaw;
|
|
2002
|
+
let finalOutput;
|
|
2003
|
+
let aggregatedUsage;
|
|
2004
|
+
let finalFinishReason;
|
|
2005
|
+
let aggregatedProviderMetadata;
|
|
2006
|
+
let aggregatedWarnings;
|
|
2007
|
+
const emittedSetValues = /* @__PURE__ */ new Map();
|
|
2008
|
+
const emittedAppendCounts = /* @__PURE__ */ new Map();
|
|
2009
|
+
const emittedTextValues = /* @__PURE__ */ new Map();
|
|
2010
|
+
if (existingCheckpoint) {
|
|
2011
|
+
firstText = existingCheckpoint.firstText;
|
|
2012
|
+
firstRaw = existingCheckpoint.firstRaw;
|
|
2013
|
+
firstOutput = existingCheckpoint.firstOutput;
|
|
2014
|
+
finalText = firstText;
|
|
2015
|
+
finalRaw = firstRaw;
|
|
2016
|
+
finalOutput = firstOutput;
|
|
2017
|
+
aggregatedUsage = existingCheckpoint.firstUsage;
|
|
2018
|
+
finalFinishReason = existingCheckpoint.firstFinishReason;
|
|
2019
|
+
aggregatedProviderMetadata = existingCheckpoint.firstProviderMetadata;
|
|
2020
|
+
aggregatedWarnings = existingCheckpoint.firstWarnings;
|
|
2021
|
+
traceSpan?.addEvent?.("useReason.resume", {
|
|
2022
|
+
checkpointKey
|
|
2023
|
+
});
|
|
2024
|
+
} else {
|
|
2025
|
+
const first = await reasonEngine(
|
|
2026
|
+
{
|
|
2027
|
+
...args,
|
|
2028
|
+
emit: suppressTextStream ? false : args.emit,
|
|
2029
|
+
stream: useStructuredIncrementalStreaming ? true : suppressTextStream ? false : args.stream,
|
|
2030
|
+
...useStructuredIncrementalStreaming ? {
|
|
2031
|
+
onTextChunk: (delta) => {
|
|
2032
|
+
firstText += delta;
|
|
2033
|
+
finalText = firstText;
|
|
2034
|
+
for (const fieldPath of setFieldPaths) {
|
|
2035
|
+
const values = extractCompletedFieldValues({
|
|
2036
|
+
text: firstText,
|
|
2037
|
+
path: fieldPath
|
|
2038
|
+
});
|
|
2039
|
+
for (const { path, value } of values) {
|
|
2040
|
+
const nextSerialized = JSON.stringify(value);
|
|
2041
|
+
if (emittedSetValues.get(path) === nextSerialized) continue;
|
|
2042
|
+
emittedSetValues.set(path, nextSerialized);
|
|
2043
|
+
emitStructuredData({
|
|
2044
|
+
kind: "set",
|
|
2045
|
+
path,
|
|
2046
|
+
value,
|
|
2047
|
+
dataType: args.structured?.dataType ?? "reason-output",
|
|
2048
|
+
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
2049
|
+
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
2050
|
+
...id ? { id } : {},
|
|
2051
|
+
streamId: opId
|
|
2052
|
+
});
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
for (const fieldPath of appendFieldPaths) {
|
|
2056
|
+
const groups = extractCompletedArrayItemGroups({
|
|
2057
|
+
text: firstText,
|
|
2058
|
+
path: fieldPath
|
|
2059
|
+
});
|
|
2060
|
+
for (const { path, items } of groups) {
|
|
2061
|
+
const emittedAppendCount = emittedAppendCounts.get(path) ?? 0;
|
|
2062
|
+
if (items.length <= emittedAppendCount) continue;
|
|
2063
|
+
const nextItems = items.slice(emittedAppendCount);
|
|
2064
|
+
emittedAppendCounts.set(path, items.length);
|
|
2065
|
+
emitStructuredData({
|
|
2066
|
+
kind: "append",
|
|
2067
|
+
path,
|
|
2068
|
+
items: nextItems,
|
|
2069
|
+
dataType: args.structured?.dataType ?? "reason-output",
|
|
2070
|
+
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
2071
|
+
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
2072
|
+
...id ? { id } : {},
|
|
2073
|
+
streamId: opId
|
|
2074
|
+
});
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
for (const fieldPath of textDeltaFieldPaths) {
|
|
2078
|
+
const values = extractStreamingStringValues({
|
|
2079
|
+
text: firstText,
|
|
2080
|
+
path: fieldPath
|
|
2081
|
+
});
|
|
2082
|
+
for (const { path, value } of values) {
|
|
2083
|
+
const emittedTextValue = emittedTextValues.get(path) ?? "";
|
|
2084
|
+
if (value.length <= emittedTextValue.length || !value.startsWith(emittedTextValue)) {
|
|
2085
|
+
continue;
|
|
2086
|
+
}
|
|
2087
|
+
const nextDelta = value.slice(emittedTextValue.length);
|
|
2088
|
+
emittedTextValues.set(path, value);
|
|
2089
|
+
if (nextDelta.length === 0) continue;
|
|
2090
|
+
emitStructuredData({
|
|
2091
|
+
kind: "text-delta",
|
|
2092
|
+
path,
|
|
2093
|
+
delta: nextDelta,
|
|
2094
|
+
dataType: args.structured?.dataType ?? "reason-output",
|
|
2095
|
+
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
2096
|
+
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
2097
|
+
...id ? { id } : {},
|
|
2098
|
+
streamId: opId
|
|
2099
|
+
});
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
} : {}
|
|
2104
|
+
},
|
|
2105
|
+
reasonMeta,
|
|
2106
|
+
args.interrupt ? defaultInterruptFirstPassInput({
|
|
2107
|
+
input: args.input,
|
|
2108
|
+
requestSchema: args.interrupt.requestSchema,
|
|
2109
|
+
mode: args.interrupt.mode ?? "required",
|
|
2110
|
+
...args.outputSchema ? { outputSchema: args.outputSchema } : {}
|
|
2111
|
+
}) : args.outputSchema ? withOutputGuardrails(
|
|
2112
|
+
withStructuredStreamHints(args.input, {
|
|
2113
|
+
...setFieldPaths.length > 0 ? { setFieldPaths } : {},
|
|
2114
|
+
...appendFieldPaths.length > 0 ? { appendFieldPaths } : {},
|
|
2115
|
+
...textDeltaFieldPaths.length > 0 ? { textDeltaFieldPaths } : {}
|
|
2116
|
+
}),
|
|
2117
|
+
args.outputSchema
|
|
2118
|
+
) : void 0
|
|
2119
|
+
);
|
|
2120
|
+
firstRaw = first.raw;
|
|
2121
|
+
finalRaw = first.raw;
|
|
2122
|
+
accumulateTokenUsage(first.usage);
|
|
2123
|
+
aggregatedUsage = mergeUsage(aggregatedUsage, first.usage);
|
|
2124
|
+
finalFinishReason = first.finishReason;
|
|
2125
|
+
aggregatedProviderMetadata = mergeProviderMetadata(
|
|
2126
|
+
aggregatedProviderMetadata,
|
|
2127
|
+
first.providerMetadata
|
|
2128
|
+
);
|
|
2129
|
+
aggregatedWarnings = mergeWarnings(aggregatedWarnings, first.warnings);
|
|
2130
|
+
traceSpan?.addEvent?.("useReason.first-pass.complete", {
|
|
2131
|
+
textLength: first.text.length,
|
|
2132
|
+
hasInterrupt: Boolean(args.interrupt)
|
|
2133
|
+
});
|
|
2134
|
+
if (args.interrupt) {
|
|
2135
|
+
const firstPass = parseInterruptFirstPassResult({
|
|
2136
|
+
text: first.text,
|
|
2137
|
+
requestSchema: args.interrupt.requestSchema,
|
|
2138
|
+
...first.finishReason ? { finishReason: first.finishReason } : {},
|
|
2139
|
+
...args.outputSchema ? { outputSchema: args.outputSchema } : {},
|
|
2140
|
+
mode: args.interrupt.mode ?? "required"
|
|
2141
|
+
});
|
|
2142
|
+
firstText = firstPass.draftText;
|
|
2143
|
+
finalText = firstPass.draftText;
|
|
2144
|
+
firstInterruptRequired = firstPass.interruptRequired;
|
|
2145
|
+
firstInterruptRequest = firstPass.request;
|
|
2146
|
+
firstOutput = firstPass.output;
|
|
2147
|
+
finalOutput = firstPass.output;
|
|
2148
|
+
} else {
|
|
2149
|
+
firstText = first.text;
|
|
2150
|
+
finalText = first.text;
|
|
2151
|
+
if (args.outputSchema) {
|
|
2152
|
+
firstOutput = parseReasonOutputWithSchema({
|
|
2153
|
+
text: first.text,
|
|
2154
|
+
schema: args.outputSchema,
|
|
2155
|
+
...first.finishReason ? { finishReason: first.finishReason } : {},
|
|
2156
|
+
label: "useReason output"
|
|
2157
|
+
});
|
|
2158
|
+
}
|
|
2159
|
+
finalOutput = firstOutput;
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
let interruptResponse;
|
|
2163
|
+
if (args.interrupt && (existingCheckpoint || firstInterruptRequired)) {
|
|
2164
|
+
const requestSchema = args.interrupt.requestSchema;
|
|
2165
|
+
const interruptRequest = existingCheckpoint ? parseWithSchema(
|
|
2166
|
+
requestSchema,
|
|
2167
|
+
existingCheckpoint.request,
|
|
2168
|
+
"useReason interrupt.request"
|
|
2169
|
+
) : firstInterruptRequest;
|
|
2170
|
+
if (!interruptRequest) {
|
|
2171
|
+
throw new Error(
|
|
2172
|
+
"useReason interrupt request is missing; first pass did not produce a valid interrupt payload."
|
|
2173
|
+
);
|
|
2174
|
+
}
|
|
2175
|
+
if (!existingCheckpoint) {
|
|
2176
|
+
ctx.currentNodeState.byKey[checkpointKey] = {
|
|
2177
|
+
status: "awaiting_interrupt",
|
|
2178
|
+
request: interruptRequest,
|
|
2179
|
+
firstText,
|
|
2180
|
+
...firstRaw !== void 0 ? { firstRaw } : {},
|
|
2181
|
+
...aggregatedUsage !== void 0 ? { firstUsage: aggregatedUsage } : {},
|
|
2182
|
+
...finalFinishReason !== void 0 ? { firstFinishReason: finalFinishReason } : {},
|
|
2183
|
+
...aggregatedProviderMetadata !== void 0 ? { firstProviderMetadata: aggregatedProviderMetadata } : {},
|
|
2184
|
+
...aggregatedWarnings !== void 0 ? { firstWarnings: aggregatedWarnings } : {},
|
|
2185
|
+
...firstOutput !== void 0 ? { firstOutput } : {}
|
|
2186
|
+
};
|
|
2187
|
+
ctx.stateDirty = true;
|
|
2188
|
+
}
|
|
2189
|
+
const resumeStatePatch = resolveHookStatePatch({
|
|
2190
|
+
nodeId: ctx.node.graph.node,
|
|
2191
|
+
currentNodeState: ctx.currentNodeState,
|
|
2192
|
+
workflowState: ctx.workflowState
|
|
2193
|
+
});
|
|
2194
|
+
interruptResponse = await awaitInterruptInternal({
|
|
2195
|
+
request: interruptRequest,
|
|
2196
|
+
requestSchema,
|
|
2197
|
+
responseSchema: args.interrupt.responseSchema,
|
|
2198
|
+
...args.interrupt.schemaId ? { schemaId: args.interrupt.schemaId } : {},
|
|
2199
|
+
...args.interrupt.schemaVersion ? { schemaVersion: args.interrupt.schemaVersion } : {},
|
|
2200
|
+
...id ? { id } : {},
|
|
2201
|
+
meta: {
|
|
2202
|
+
__kortyxResumeStatePatch: resumeStatePatch
|
|
2203
|
+
}
|
|
2204
|
+
});
|
|
2205
|
+
traceSpan?.addEvent?.("useReason.interrupt.resolved", {
|
|
2206
|
+
checkpointKey
|
|
2207
|
+
});
|
|
2208
|
+
if (Object.hasOwn(ctx.currentNodeState.byKey, checkpointKey)) {
|
|
2209
|
+
delete ctx.currentNodeState.byKey[checkpointKey];
|
|
2210
|
+
ctx.stateDirty = true;
|
|
2211
|
+
}
|
|
2212
|
+
const continuationInput = defaultContinuationInput({
|
|
2213
|
+
input: args.input,
|
|
2214
|
+
draftText: firstText,
|
|
2215
|
+
...firstOutput !== void 0 ? { draftOutput: firstOutput } : {},
|
|
2216
|
+
request: interruptRequest,
|
|
2217
|
+
response: interruptResponse
|
|
2218
|
+
});
|
|
2219
|
+
const second = await reasonEngine(
|
|
2220
|
+
{
|
|
2221
|
+
...args,
|
|
2222
|
+
emit: suppressTextStream ? false : args.emit,
|
|
2223
|
+
stream: suppressTextStream ? false : args.stream
|
|
2224
|
+
},
|
|
2225
|
+
reasonMeta,
|
|
2226
|
+
args.outputSchema ? withOutputGuardrails(
|
|
2227
|
+
continuationInput,
|
|
2228
|
+
args.outputSchema
|
|
2229
|
+
) : continuationInput
|
|
2230
|
+
);
|
|
2231
|
+
finalText = second.text;
|
|
2232
|
+
finalRaw = second.raw;
|
|
2233
|
+
accumulateTokenUsage(second.usage);
|
|
2234
|
+
aggregatedUsage = mergeUsage(aggregatedUsage, second.usage);
|
|
2235
|
+
finalFinishReason = second.finishReason;
|
|
2236
|
+
aggregatedProviderMetadata = mergeProviderMetadata(
|
|
2237
|
+
aggregatedProviderMetadata,
|
|
2238
|
+
second.providerMetadata
|
|
2239
|
+
);
|
|
2240
|
+
aggregatedWarnings = mergeWarnings(aggregatedWarnings, second.warnings);
|
|
2241
|
+
traceSpan?.addEvent?.("useReason.continuation.complete", {
|
|
2242
|
+
textLength: second.text.length
|
|
2243
|
+
});
|
|
2244
|
+
if (args.outputSchema) {
|
|
2245
|
+
finalOutput = parseReasonOutputWithSchema({
|
|
2246
|
+
text: finalText,
|
|
2247
|
+
schema: args.outputSchema,
|
|
2248
|
+
...second.finishReason ? { finishReason: second.finishReason } : {},
|
|
2249
|
+
label: "useReason output"
|
|
2250
|
+
});
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
if (finalOutput !== void 0) {
|
|
2254
|
+
emitReasonStructuredOutput({
|
|
2255
|
+
...id ? { id } : {},
|
|
2256
|
+
opId,
|
|
2257
|
+
output: finalOutput,
|
|
2258
|
+
structured: args.structured,
|
|
2259
|
+
emit: args.emit ?? true
|
|
2260
|
+
});
|
|
2261
|
+
}
|
|
2262
|
+
const result = {
|
|
2263
|
+
...id ? { id } : {},
|
|
2264
|
+
opId,
|
|
2265
|
+
text: finalText,
|
|
2266
|
+
...finalRaw !== void 0 ? { raw: finalRaw } : {},
|
|
2267
|
+
...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
|
|
2268
|
+
...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
|
|
2269
|
+
...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
|
|
2270
|
+
...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
|
|
2271
|
+
...finalOutput !== void 0 ? { output: finalOutput } : {},
|
|
2272
|
+
...interruptResponse !== void 0 ? { interruptResponse } : {}
|
|
2273
|
+
};
|
|
2274
|
+
if (args.interrupt) {
|
|
2275
|
+
ctx.currentNodeState.byKey[checkpointKey] = {
|
|
2276
|
+
status: "completed",
|
|
2277
|
+
result
|
|
2278
|
+
};
|
|
2279
|
+
ctx.stateDirty = true;
|
|
2280
|
+
}
|
|
2281
|
+
traceSpan?.end?.({
|
|
2282
|
+
...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
|
|
2283
|
+
...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
|
|
2284
|
+
...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
|
|
2285
|
+
...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
|
|
2286
|
+
attributes: {
|
|
2287
|
+
textLength: finalText.length,
|
|
2288
|
+
resumedFromCheckpoint: Boolean(existingCheckpoint),
|
|
2289
|
+
interrupted: Boolean(interruptResponse !== void 0)
|
|
2290
|
+
},
|
|
2291
|
+
telemetry: {
|
|
2292
|
+
...args.telemetry ?? {},
|
|
2293
|
+
output: args.telemetry?.output ?? finalOutput ?? finalText
|
|
2294
|
+
}
|
|
2295
|
+
});
|
|
2296
|
+
return result;
|
|
2297
|
+
}
|
|
2298
|
+
function useStructuredData(args) {
|
|
2299
|
+
emitStructuredData(args);
|
|
2300
|
+
}
|
|
2301
|
+
function useReason2(args) {
|
|
2302
|
+
return useReason(args);
|
|
2303
|
+
}
|
|
2304
|
+
function useInterrupt(args) {
|
|
2305
|
+
return awaitInterruptInternal(args);
|
|
2306
|
+
}
|
|
2307
|
+
function useRuntimeContext() {
|
|
2308
|
+
const ctx = getHookContext();
|
|
2309
|
+
const config = ctx.state.config;
|
|
2310
|
+
const context = config && typeof config === "object" && "context" in config ? config.context : void 0;
|
|
2311
|
+
return context && typeof context === "object" && !Array.isArray(context) ? context : {};
|
|
2312
|
+
}
|
|
2313
|
+
function useNodeState(initialValue) {
|
|
2314
|
+
const ctx = getHookContext();
|
|
2315
|
+
const nodeState = ctx.currentNodeState;
|
|
2316
|
+
const index = ctx.nodeStateIndex++;
|
|
2317
|
+
if (index >= nodeState.byIndex.length) {
|
|
2318
|
+
nodeState.byIndex[index] = initialValue;
|
|
2319
|
+
ctx.stateDirty = true;
|
|
2320
|
+
}
|
|
2321
|
+
const getValue = () => nodeState.byIndex[index];
|
|
2322
|
+
const setValue = (next) => {
|
|
2323
|
+
const prev = getValue();
|
|
2324
|
+
const resolved = typeof next === "function" ? next(prev) : next;
|
|
2325
|
+
nodeState.byIndex[index] = resolved;
|
|
2326
|
+
ctx.stateDirty = true;
|
|
2327
|
+
};
|
|
2328
|
+
return [getValue(), setValue];
|
|
2329
|
+
}
|
|
2330
|
+
function useWorkflowState(key, ...rest) {
|
|
2331
|
+
const ctx = getHookContext();
|
|
2332
|
+
const workflowState = ctx.workflowState;
|
|
2333
|
+
const hasInitial = rest.length > 0;
|
|
2334
|
+
const initialValue = rest[0];
|
|
2335
|
+
if (!Object.hasOwn(workflowState, key) && hasInitial) {
|
|
2336
|
+
workflowState[key] = initialValue;
|
|
2337
|
+
ctx.stateDirty = true;
|
|
2338
|
+
}
|
|
2339
|
+
const getValue = () => workflowState[key];
|
|
2340
|
+
const setValue = (next) => {
|
|
2341
|
+
const prev = getValue();
|
|
2342
|
+
const resolved = typeof next === "function" ? next(prev) : next;
|
|
2343
|
+
workflowState[key] = resolved;
|
|
2344
|
+
ctx.stateDirty = true;
|
|
2345
|
+
};
|
|
2346
|
+
return [getValue(), setValue];
|
|
2347
|
+
}
|
|
2348
|
+
var import_node_crypto5 = require("crypto");
|
|
2349
|
+
var import_node_crypto42 = require("crypto");
|
|
2350
|
+
function emitWorkflowCall2(telemetry, type, payload) {
|
|
2351
|
+
const base = telemetry?.correlation;
|
|
2352
|
+
if (!telemetry?.reporter || !telemetry.environment || !telemetry.service || !base?.runId || !base.workflowId)
|
|
2353
|
+
return;
|
|
2354
|
+
try {
|
|
2355
|
+
const active = telemetry.trace?.getActiveContext?.();
|
|
2356
|
+
void Promise.resolve(
|
|
2357
|
+
telemetry.reporter.emit([
|
|
2358
|
+
{
|
|
2359
|
+
schemaVersion: 1,
|
|
2360
|
+
eventId: (0, import_node_crypto42.randomUUID)(),
|
|
2361
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2362
|
+
environment: telemetry.environment,
|
|
2363
|
+
service: telemetry.service,
|
|
2364
|
+
correlation: {
|
|
2365
|
+
...base,
|
|
2366
|
+
runId: base.runId,
|
|
2367
|
+
workflowId: base.workflowId,
|
|
2368
|
+
...active
|
|
2369
|
+
},
|
|
2370
|
+
type,
|
|
2371
|
+
payload
|
|
2372
|
+
}
|
|
2373
|
+
])
|
|
2374
|
+
).catch(() => {
|
|
2375
|
+
});
|
|
2376
|
+
} catch {
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
function workflowCallContent(telemetry, side, value) {
|
|
2380
|
+
const policy = telemetry?.captureContent;
|
|
2381
|
+
if (!(policy === true || policy && typeof policy === "object" && policy[side]))
|
|
2382
|
+
return {};
|
|
2383
|
+
const serialized = JSON.stringify(value);
|
|
2384
|
+
return serialized && Buffer.byteLength(serialized, "utf8") <= 16384 ? { [side]: JSON.parse(serialized) } : { [`${side}Omitted`]: "Content exceeds 16 KiB" };
|
|
2385
|
+
}
|
|
2386
|
+
var WorkflowCallError = class extends Error {
|
|
2387
|
+
constructor(workflow, message) {
|
|
2388
|
+
super(`Workflow '${workflow}': ${message}`);
|
|
2389
|
+
this.workflow = workflow;
|
|
2390
|
+
this.name = "WorkflowCallError";
|
|
2391
|
+
}
|
|
2392
|
+
workflow;
|
|
2393
|
+
};
|
|
2394
|
+
function workflowCallFingerprint(value) {
|
|
2395
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2396
|
+
const canonical = (v) => {
|
|
2397
|
+
if (v === null || typeof v === "string" || typeof v === "boolean") return v;
|
|
2398
|
+
if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
2399
|
+
if (typeof v !== "object" || !v || seen.has(v)) {
|
|
2400
|
+
throw new Error("Workflow input and output must be JSON serializable.");
|
|
2401
|
+
}
|
|
2402
|
+
if (!Array.isArray(v) && Object.getPrototypeOf(v) !== Object.prototype) {
|
|
2403
|
+
throw new Error(
|
|
2404
|
+
"Workflow input and output must contain plain JSON objects."
|
|
2405
|
+
);
|
|
2406
|
+
}
|
|
2407
|
+
seen.add(v);
|
|
2408
|
+
const result = Array.isArray(v) ? v.map(canonical) : Object.fromEntries(
|
|
2409
|
+
Object.keys(v).sort().map((key) => [
|
|
2410
|
+
key,
|
|
2411
|
+
canonical(v[key])
|
|
2412
|
+
])
|
|
2413
|
+
);
|
|
2414
|
+
seen.delete(v);
|
|
2415
|
+
return result;
|
|
2416
|
+
};
|
|
2417
|
+
return JSON.stringify(canonical(value));
|
|
2418
|
+
}
|
|
2419
|
+
function createWorkflowHooks(workflows) {
|
|
2420
|
+
return {
|
|
2421
|
+
useWorkflow: (args) => {
|
|
2422
|
+
const workflow = workflows[args.workflow];
|
|
2423
|
+
if (!workflow || workflow.id !== args.workflow)
|
|
2424
|
+
throw new Error("Workflow registry keys must match definition ids.");
|
|
2425
|
+
return useWorkflow({ ...args, workflow });
|
|
2426
|
+
}
|
|
2427
|
+
};
|
|
2428
|
+
}
|
|
2429
|
+
async function useWorkflow(args) {
|
|
2430
|
+
const ctx = getHookContext();
|
|
2431
|
+
const service = ctx.node.callWorkflow;
|
|
2432
|
+
if (!service)
|
|
2433
|
+
throw new Error("useWorkflow requires an agent workflow registry.");
|
|
2434
|
+
if (!args.id.trim())
|
|
2435
|
+
throw new Error("useWorkflow requires a stable, nonempty id.");
|
|
2436
|
+
if (ctx.workflowCallActive)
|
|
2437
|
+
throw new Error(
|
|
2438
|
+
"Concurrent workflow calls are not supported; await each call."
|
|
2439
|
+
);
|
|
2440
|
+
if (ctx.workflowCallIds.has(args.id))
|
|
2441
|
+
throw new Error(
|
|
2442
|
+
`Duplicate workflow call id '${args.id}' in one node activation.`
|
|
2443
|
+
);
|
|
2444
|
+
if (ctx.workflowCallIds.size >= 64)
|
|
2445
|
+
throw new Error(
|
|
2446
|
+
"A node may call at most 64 child workflows per activation."
|
|
2447
|
+
);
|
|
2448
|
+
ctx.workflowCallIds.add(args.id);
|
|
2449
|
+
const definition = typeof args.workflow === "string" ? void 0 : args.workflow;
|
|
2450
|
+
const workflow = definition?.id ?? args.workflow;
|
|
2451
|
+
const input = args.input;
|
|
2452
|
+
const fingerprint = workflowCallFingerprint({
|
|
2453
|
+
workflow,
|
|
2454
|
+
input,
|
|
2455
|
+
version: definition?.version ?? null
|
|
2456
|
+
});
|
|
2457
|
+
const key = `__useWorkflow:${args.id}`;
|
|
2458
|
+
let record = ctx.currentNodeState.byKey[key];
|
|
2459
|
+
if (record && record.fingerprint !== fingerprint)
|
|
2460
|
+
throw new WorkflowCallError(
|
|
2461
|
+
workflow,
|
|
2462
|
+
"call input or target changed during replay"
|
|
2463
|
+
);
|
|
2464
|
+
record ??= {
|
|
2465
|
+
fingerprint,
|
|
2466
|
+
invocationId: (0, import_node_crypto5.randomUUID)(),
|
|
2467
|
+
status: "running",
|
|
2468
|
+
interrupts: []
|
|
2469
|
+
};
|
|
2470
|
+
ctx.currentNodeState.byKey[key] = record;
|
|
2471
|
+
ctx.stateDirty = true;
|
|
2472
|
+
ctx.workflowCallActive = true;
|
|
2473
|
+
const telemetry = ctx.node.workflowCallTelemetry;
|
|
2474
|
+
const activationKey = "__useWorkflowActivation";
|
|
2475
|
+
ctx.currentNodeState.byKey[activationKey] ??= (0, import_node_crypto5.randomUUID)();
|
|
2476
|
+
const callerNodeExecutionId = ctx.currentNodeState.byKey[activationKey];
|
|
2477
|
+
const metadata2 = {
|
|
2478
|
+
invocationId: record.invocationId,
|
|
2479
|
+
parentInvocationId: telemetry?.correlation?.invocationId ?? null,
|
|
2480
|
+
branchId: telemetry?.correlation?.branchId ?? telemetry?.correlation?.runId ?? "unknown",
|
|
2481
|
+
callId: args.id,
|
|
2482
|
+
callerNodeId: ctx.node.graph.node,
|
|
2483
|
+
callerNodeExecutionId,
|
|
2484
|
+
sourceWorkflowId: ctx.node.graph.name,
|
|
2485
|
+
targetWorkflowId: workflow,
|
|
2486
|
+
targetVersion: definition?.version ?? null
|
|
2487
|
+
};
|
|
2488
|
+
record.telemetry = metadata2;
|
|
2489
|
+
const report = (status, content = {}) => {
|
|
2490
|
+
record.sequence = (record.sequence ?? 0) + 1;
|
|
2491
|
+
emitWorkflowCall2(telemetry, `workflow.call.${status}`, {
|
|
2492
|
+
...metadata2,
|
|
2493
|
+
sequence: record.sequence,
|
|
2494
|
+
...content
|
|
2495
|
+
});
|
|
2496
|
+
};
|
|
2497
|
+
const cached = record.status === "completed";
|
|
2498
|
+
try {
|
|
2499
|
+
for (const interruption of record.interrupts) {
|
|
2500
|
+
interruption.response = await awaitInterruptInternal({
|
|
2501
|
+
request: interruption.request,
|
|
2502
|
+
...interruption.request.meta ? { meta: interruption.request.meta } : {}
|
|
2503
|
+
});
|
|
2504
|
+
}
|
|
2505
|
+
if (record.status === "failed")
|
|
2506
|
+
throw new WorkflowCallError(workflow, record.error ?? "child failed");
|
|
2507
|
+
while (record.status !== "completed") {
|
|
2508
|
+
let outcome;
|
|
2509
|
+
try {
|
|
2510
|
+
report(
|
|
2511
|
+
record.snapshot ? "resumed" : "started",
|
|
2512
|
+
workflowCallContent(telemetry, "input", input)
|
|
2513
|
+
);
|
|
2514
|
+
outcome = await service({
|
|
2515
|
+
callerNodeExecutionId,
|
|
2516
|
+
id: args.id,
|
|
2517
|
+
workflow,
|
|
2518
|
+
input,
|
|
2519
|
+
definition,
|
|
2520
|
+
invocationId: record.invocationId,
|
|
2521
|
+
...record.snapshot ? { snapshot: record.snapshot } : {},
|
|
2522
|
+
...record.interrupts.length ? { response: record.interrupts.at(-1)?.response } : {}
|
|
2523
|
+
});
|
|
2524
|
+
} catch (error) {
|
|
2525
|
+
record.status = "failed";
|
|
2526
|
+
record.error = error instanceof Error ? error.message : String(error);
|
|
2527
|
+
report("failed", { error: "Child workflow failed" });
|
|
2528
|
+
throw new WorkflowCallError(workflow, record.error);
|
|
2529
|
+
}
|
|
2530
|
+
if (outcome.status === "completed") {
|
|
2531
|
+
record.data = outcome.data;
|
|
2532
|
+
accumulateTokenUsage(outcome.usage);
|
|
2533
|
+
workflowCallFingerprint(record.data);
|
|
2534
|
+
record.status = "completed";
|
|
2535
|
+
delete record.snapshot;
|
|
2536
|
+
report(
|
|
2537
|
+
"completed",
|
|
2538
|
+
workflowCallContent(telemetry, "output", record.data)
|
|
2539
|
+
);
|
|
2540
|
+
} else {
|
|
2541
|
+
record.status = "interrupted";
|
|
2542
|
+
record.snapshot = outcome.snapshot;
|
|
2543
|
+
report("suspended", {
|
|
2544
|
+
leaf: outcome.request.meta?.workflowCall ?? null
|
|
2545
|
+
});
|
|
2546
|
+
const interruption = {
|
|
2547
|
+
request: outcome.request
|
|
2548
|
+
};
|
|
2549
|
+
record.interrupts.push(interruption);
|
|
2550
|
+
interruption.response = await awaitInterruptInternal({
|
|
2551
|
+
request: interruption.request,
|
|
2552
|
+
...interruption.request.meta ? { meta: interruption.request.meta } : {}
|
|
2553
|
+
});
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2556
|
+
if (cached)
|
|
2557
|
+
report("reused", workflowCallContent(telemetry, "output", record.data));
|
|
2558
|
+
const data = record.data ?? {};
|
|
2559
|
+
return {
|
|
2560
|
+
data: JSON.parse(JSON.stringify(data))
|
|
2561
|
+
};
|
|
2562
|
+
} finally {
|
|
2563
|
+
ctx.workflowCallActive = false;
|
|
2564
|
+
}
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
});
|
|
18
2568
|
|
|
19
2569
|
// src/index.ts
|
|
20
2570
|
var index_exports = {};
|
|
@@ -237,7 +2787,10 @@ var emitTelemetryEvent = (args) => {
|
|
|
237
2787
|
}
|
|
238
2788
|
} : {},
|
|
239
2789
|
type: args.type,
|
|
240
|
-
payload:
|
|
2790
|
+
payload: {
|
|
2791
|
+
...args.payload,
|
|
2792
|
+
...args.type === "run.cancelled" ? { branchId: args.config.executionBranchId ?? runId } : {}
|
|
2793
|
+
}
|
|
241
2794
|
};
|
|
242
2795
|
try {
|
|
243
2796
|
void Promise.resolve(telemetry.reporter.emit([event])).then(async () => {
|
|
@@ -434,6 +2987,13 @@ var prepareWorkflowTelemetry = (args) => {
|
|
|
434
2987
|
}
|
|
435
2988
|
return {
|
|
436
2989
|
...args.config,
|
|
2990
|
+
prepareChildTelemetry: (workflow, config) => prepareWorkflowTelemetry({
|
|
2991
|
+
config,
|
|
2992
|
+
workflow,
|
|
2993
|
+
runId: args.runId,
|
|
2994
|
+
sessionId: args.sessionId,
|
|
2995
|
+
knownWorkflowIds: args.knownWorkflowIds
|
|
2996
|
+
}),
|
|
437
2997
|
telemetry: {
|
|
438
2998
|
...telemetry,
|
|
439
2999
|
correlation: {
|
|
@@ -448,6 +3008,59 @@ var prepareWorkflowTelemetry = (args) => {
|
|
|
448
3008
|
};
|
|
449
3009
|
};
|
|
450
3010
|
|
|
3011
|
+
// src/telemetry/workflow-call-branch.ts
|
|
3012
|
+
var import_node_crypto3 = require("crypto");
|
|
3013
|
+
var import_hooks = __toESM(require_dist());
|
|
3014
|
+
function restoreWorkflowCallBranch(requests, telemetry, sourceRunId) {
|
|
3015
|
+
const branchId = (0, import_node_crypto3.randomUUID)();
|
|
3016
|
+
const emitted = /* @__PURE__ */ new Set();
|
|
3017
|
+
for (const request of requests) {
|
|
3018
|
+
if (request.state)
|
|
3019
|
+
request.state.config = {
|
|
3020
|
+
...request.state.config,
|
|
3021
|
+
executionBranchId: branchId
|
|
3022
|
+
};
|
|
3023
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3024
|
+
const visit = (value) => {
|
|
3025
|
+
if (!value || typeof value !== "object" || seen.has(value)) return;
|
|
3026
|
+
seen.add(value);
|
|
3027
|
+
const record = value;
|
|
3028
|
+
if (typeof record.invocationId === "string" && typeof record.fingerprint === "string" && Array.isArray(record.interrupts)) {
|
|
3029
|
+
const previous = record.telemetry;
|
|
3030
|
+
if (previous) {
|
|
3031
|
+
record.sequence = 0;
|
|
3032
|
+
record.telemetry = { ...previous, branchId };
|
|
3033
|
+
if (!emitted.has(record.invocationId)) {
|
|
3034
|
+
emitted.add(record.invocationId);
|
|
3035
|
+
(0, import_hooks.emitWorkflowCall)(
|
|
3036
|
+
telemetry ? {
|
|
3037
|
+
...telemetry,
|
|
3038
|
+
correlation: {
|
|
3039
|
+
runId: request.runId,
|
|
3040
|
+
workflowId: request.workflow,
|
|
3041
|
+
...request.sessionId ? { sessionId: request.sessionId } : {}
|
|
3042
|
+
}
|
|
3043
|
+
} : void 0,
|
|
3044
|
+
"workflow.call.restored",
|
|
3045
|
+
{
|
|
3046
|
+
...previous,
|
|
3047
|
+
branchId,
|
|
3048
|
+
sourceBranchId: previous.branchId,
|
|
3049
|
+
sourceRunId,
|
|
3050
|
+
sequence: 0,
|
|
3051
|
+
status: record.status
|
|
3052
|
+
}
|
|
3053
|
+
);
|
|
3054
|
+
}
|
|
3055
|
+
}
|
|
3056
|
+
}
|
|
3057
|
+
for (const child of Object.values(record)) visit(child);
|
|
3058
|
+
};
|
|
3059
|
+
visit(request);
|
|
3060
|
+
}
|
|
3061
|
+
return branchId;
|
|
3062
|
+
}
|
|
3063
|
+
|
|
451
3064
|
// src/chat/process-chat.ts
|
|
452
3065
|
var import_runtime3 = require("@kortyx/runtime");
|
|
453
3066
|
|
|
@@ -634,6 +3247,21 @@ async function orchestrateGraphStream({
|
|
|
634
3247
|
let pendingRecordToken = null;
|
|
635
3248
|
let wroteHumanInput = false;
|
|
636
3249
|
const pendingStore = frameworkAdapter?.pendingRequests;
|
|
3250
|
+
const sealPendingSnapshots = async () => {
|
|
3251
|
+
if (typeof frameworkAdapter?.checkpointer?.getTuple !== "function") return;
|
|
3252
|
+
for (const [token, request] of activePendingRequests) {
|
|
3253
|
+
const graphSnapshot = await (0, import_runtime.captureGraphSnapshot)(
|
|
3254
|
+
frameworkAdapter.checkpointer,
|
|
3255
|
+
runId,
|
|
3256
|
+
request.graphCheckpointId
|
|
3257
|
+
);
|
|
3258
|
+
if (graphSnapshot) {
|
|
3259
|
+
const sealed = { ...request, graphSnapshot };
|
|
3260
|
+
activePendingRequests.set(token, sealed);
|
|
3261
|
+
await pendingStore?.update(token, { graphSnapshot });
|
|
3262
|
+
}
|
|
3263
|
+
}
|
|
3264
|
+
};
|
|
637
3265
|
const pendingTtlMs = frameworkAdapter?.ttlMs ?? 15 * 60 * 1e3;
|
|
638
3266
|
const persistAndEmitInterrupt = async (payload) => {
|
|
639
3267
|
if (wroteHumanInput) return;
|
|
@@ -719,6 +3347,9 @@ async function orchestrateGraphStream({
|
|
|
719
3347
|
type: "interrupt.created",
|
|
720
3348
|
correlation: { nodeId: record.node },
|
|
721
3349
|
payload: {
|
|
3350
|
+
workflowCall: record.schema.meta?.workflowCall ?? null,
|
|
3351
|
+
workflowCallPath: record.schema.meta?.workflowCallPath ?? null,
|
|
3352
|
+
branchId: config.executionBranchId ?? runId,
|
|
722
3353
|
interruptId: record.requestId,
|
|
723
3354
|
requestId: record.requestId,
|
|
724
3355
|
kind: record.schema.kind,
|
|
@@ -934,6 +3565,7 @@ async function orchestrateGraphStream({
|
|
|
934
3565
|
let workflowDoneData;
|
|
935
3566
|
currentGraph.config = currentGraph.config || {};
|
|
936
3567
|
currentGraph.config.emit = forwardEmit;
|
|
3568
|
+
currentGraph.config.executionRunId = runId;
|
|
937
3569
|
const threadId = currentGraph.config?.session?.id || sessionId || "anonymous-session";
|
|
938
3570
|
const checkpointNs = "";
|
|
939
3571
|
namespacesUsed.add(checkpointNs);
|
|
@@ -1128,6 +3760,7 @@ async function orchestrateGraphStream({
|
|
|
1128
3760
|
});
|
|
1129
3761
|
runTraceSpan.end?.(runTraceEndArgs());
|
|
1130
3762
|
const resolvedSessionId2 = config?.session?.id || sessionId || "";
|
|
3763
|
+
await sealPendingSnapshots();
|
|
1131
3764
|
if (resolvedSessionId2 && frameworkAdapter?.sessionCheckpoints) {
|
|
1132
3765
|
try {
|
|
1133
3766
|
const checkpoint = await frameworkAdapter.sessionCheckpoints.append(
|
|
@@ -1189,6 +3822,7 @@ async function orchestrateGraphStream({
|
|
|
1189
3822
|
}
|
|
1190
3823
|
}
|
|
1191
3824
|
const resolvedSessionId = config?.session?.id || sessionId || "";
|
|
3825
|
+
await sealPendingSnapshots();
|
|
1192
3826
|
if (resolvedSessionId && frameworkAdapter?.sessionCheckpoints) {
|
|
1193
3827
|
try {
|
|
1194
3828
|
const checkpoint = await frameworkAdapter.sessionCheckpoints.append({
|
|
@@ -1305,11 +3939,14 @@ async function tryPrepareResumeStream({
|
|
|
1305
3939
|
if (!store) return null;
|
|
1306
3940
|
const pending = await store.get(meta.token);
|
|
1307
3941
|
if (!pending || pending.requestId !== meta.requestId) {
|
|
1308
|
-
|
|
1309
|
-
|
|
3942
|
+
throw new Error(
|
|
3943
|
+
"Interrupt is expired, already consumed, or does not match the request."
|
|
1310
3944
|
);
|
|
1311
|
-
return null;
|
|
1312
3945
|
}
|
|
3946
|
+
if (pending.sessionId && pending.sessionId !== sessionId)
|
|
3947
|
+
throw new Error("Interrupt belongs to another session.");
|
|
3948
|
+
if (store.take && !await store.take(meta.token))
|
|
3949
|
+
throw new Error("Interrupt has already been resumed or cancelled.");
|
|
1313
3950
|
if (meta.cancel) {
|
|
1314
3951
|
await store.delete(pending.token);
|
|
1315
3952
|
emitTelemetryEvent({
|
|
@@ -1327,7 +3964,9 @@ async function tryPrepareResumeStream({
|
|
|
1327
3964
|
},
|
|
1328
3965
|
flush: true
|
|
1329
3966
|
});
|
|
1330
|
-
return
|
|
3967
|
+
return (async function* () {
|
|
3968
|
+
yield { type: "done" };
|
|
3969
|
+
})();
|
|
1331
3970
|
}
|
|
1332
3971
|
console.log(
|
|
1333
3972
|
`[resume] token=${meta.token} requestId=${meta.requestId} selected=${JSON.stringify(
|
|
@@ -1357,14 +3996,28 @@ async function tryPrepareResumeStream({
|
|
|
1357
3996
|
};
|
|
1358
3997
|
const wf = await selectWorkflow(resumedState.currentWorkflow);
|
|
1359
3998
|
const telemetryConfig = prepareWorkflowTelemetry({
|
|
1360
|
-
config
|
|
3999
|
+
config: {
|
|
4000
|
+
...config,
|
|
4001
|
+
...pending.state?.config?.executionBranchId ? { executionBranchId: pending.state.config.executionBranchId } : {}
|
|
4002
|
+
},
|
|
1361
4003
|
workflow: wf,
|
|
1362
4004
|
runId: pending.runId,
|
|
1363
4005
|
sessionId,
|
|
1364
4006
|
knownWorkflowIds
|
|
1365
4007
|
});
|
|
1366
4008
|
resumedState.config = telemetryConfig;
|
|
1367
|
-
const resumeUpdate = {
|
|
4009
|
+
const resumeUpdate = pending.graphSnapshot ? {
|
|
4010
|
+
config: {
|
|
4011
|
+
...telemetryConfig,
|
|
4012
|
+
context: pending.state?.config?.context ?? telemetryConfig.context
|
|
4013
|
+
}
|
|
4014
|
+
} : {};
|
|
4015
|
+
if (pending.graphSnapshot && frameworkAdapter)
|
|
4016
|
+
await (0, import_runtime2.restoreGraphSnapshot)(
|
|
4017
|
+
frameworkAdapter.checkpointer,
|
|
4018
|
+
pending.graphSnapshot,
|
|
4019
|
+
pending.runId
|
|
4020
|
+
);
|
|
1368
4021
|
if (Object.keys(resumeDataPatch).length > 0) {
|
|
1369
4022
|
resumeUpdate.data = {
|
|
1370
4023
|
...pendingData,
|
|
@@ -1468,6 +4121,7 @@ async function streamChat({
|
|
|
1468
4121
|
"streamChat requires selectWorkflow or workflowRegistry to resolve workflows."
|
|
1469
4122
|
);
|
|
1470
4123
|
}
|
|
4124
|
+
runtimeConfig.selectWorkflow = workflowSelector;
|
|
1471
4125
|
const fallbackSessionId = options?.sessionId;
|
|
1472
4126
|
const resolvedSessionId = sessionId || fallbackSessionId || "anonymous-session";
|
|
1473
4127
|
const last = messages[messages.length - 1];
|
|
@@ -1617,7 +4271,7 @@ var hydratePendingGraphCheckpoint = async (frameworkAdapter, request) => {
|
|
|
1617
4271
|
return graphCheckpointId ? { ...request, graphCheckpointId } : request;
|
|
1618
4272
|
};
|
|
1619
4273
|
var clearPendingGraphWrites = async (frameworkAdapter, request) => {
|
|
1620
|
-
if (!request.graphCheckpointId) return;
|
|
4274
|
+
if (request.graphSnapshot || !request.graphCheckpointId) return;
|
|
1621
4275
|
const checkpointer = frameworkAdapter.checkpointer;
|
|
1622
4276
|
if (!checkpointer?.deleteCheckpointWrites) return;
|
|
1623
4277
|
await Promise.all([
|
|
@@ -1726,6 +4380,11 @@ function createAgent(args) {
|
|
|
1726
4380
|
(request) => hydratePendingGraphCheckpoint(resolvedFrameworkAdapter, request)
|
|
1727
4381
|
)
|
|
1728
4382
|
);
|
|
4383
|
+
restoreWorkflowCallBranch(
|
|
4384
|
+
activePendingRequests,
|
|
4385
|
+
telemetry,
|
|
4386
|
+
target?.runId ?? "unknown"
|
|
4387
|
+
);
|
|
1729
4388
|
await Promise.all(
|
|
1730
4389
|
activePendingRequests.map(
|
|
1731
4390
|
(request) => clearPendingGraphWrites(resolvedFrameworkAdapter, request)
|
|
@@ -1757,6 +4416,7 @@ function createAgent(args) {
|
|
|
1757
4416
|
return { ...result, activePendingRequests };
|
|
1758
4417
|
};
|
|
1759
4418
|
const fork = async (id, options) => {
|
|
4419
|
+
const source = await resolvedFrameworkAdapter.sessionCheckpoints.get(id);
|
|
1760
4420
|
const result = await resolvedFrameworkAdapter.sessionCheckpoints.fork(
|
|
1761
4421
|
id,
|
|
1762
4422
|
options
|
|
@@ -1766,6 +4426,11 @@ function createAgent(args) {
|
|
|
1766
4426
|
(request) => hydratePendingGraphCheckpoint(resolvedFrameworkAdapter, request)
|
|
1767
4427
|
)
|
|
1768
4428
|
);
|
|
4429
|
+
restoreWorkflowCallBranch(
|
|
4430
|
+
activePendingRequests,
|
|
4431
|
+
telemetry,
|
|
4432
|
+
source?.runId ?? "unknown"
|
|
4433
|
+
);
|
|
1769
4434
|
await Promise.all(
|
|
1770
4435
|
activePendingRequests.map(
|
|
1771
4436
|
(request) => clearPendingGraphWrites(resolvedFrameworkAdapter, request)
|