@kortyx/hooks 0.4.1 → 0.5.1
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 +22 -0
- package/LICENSE +201 -0
- package/README.md +21 -4
- package/dist/index.d.ts +61 -10
- package/dist/index.js +513 -30
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +3 -0
- package/dist/internal.js +29 -4
- package/dist/internal.js.map +1 -1
- package/package.json +20 -14
package/dist/index.js
CHANGED
|
@@ -20,11 +20,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
var index_exports = {};
|
|
21
21
|
__export(index_exports, {
|
|
22
22
|
runWithHookContext: () => runWithHookContext,
|
|
23
|
-
useAiInterrupt: () => useAiInterrupt,
|
|
24
23
|
useAiMemory: () => useAiMemory,
|
|
25
24
|
useEmit: () => useEmit,
|
|
25
|
+
useInterrupt: () => useInterrupt,
|
|
26
26
|
useNodeState: () => useNodeState,
|
|
27
|
-
useReason: () =>
|
|
27
|
+
useReason: () => useReason2,
|
|
28
28
|
useStructuredData: () => useStructuredData,
|
|
29
29
|
useWorkflowState: () => useWorkflowState
|
|
30
30
|
});
|
|
@@ -65,6 +65,7 @@ var createInternalContext = (ctx) => {
|
|
|
65
65
|
return {
|
|
66
66
|
...ctx,
|
|
67
67
|
nodeStateIndex: 0,
|
|
68
|
+
reasonCallIndex: 0,
|
|
68
69
|
currentNodeState,
|
|
69
70
|
workflowState: cloneWorkflowState(hookMemory.workflowState),
|
|
70
71
|
dirty: false
|
|
@@ -89,8 +90,15 @@ async function runWithHookContext(ctx, fn) {
|
|
|
89
90
|
return { result, memoryUpdates: buildMemoryUpdates(internal) };
|
|
90
91
|
} catch (err) {
|
|
91
92
|
const memoryUpdates = buildMemoryUpdates(internal);
|
|
92
|
-
if (memoryUpdates
|
|
93
|
-
|
|
93
|
+
if (memoryUpdates) {
|
|
94
|
+
const currentMemory = isRecord(internal.state.memory) ? internal.state.memory : {};
|
|
95
|
+
internal.state.memory = {
|
|
96
|
+
...currentMemory,
|
|
97
|
+
...memoryUpdates
|
|
98
|
+
};
|
|
99
|
+
if (err && typeof err === "object") {
|
|
100
|
+
err.__kortyxMemoryUpdates = memoryUpdates;
|
|
101
|
+
}
|
|
94
102
|
}
|
|
95
103
|
throw err;
|
|
96
104
|
}
|
|
@@ -103,13 +111,111 @@ function getHookContext() {
|
|
|
103
111
|
return ctx;
|
|
104
112
|
}
|
|
105
113
|
|
|
114
|
+
// src/validation.ts
|
|
115
|
+
var parseWithSchema = (schema, value, label) => {
|
|
116
|
+
if (!schema) return value;
|
|
117
|
+
const parsed = schema.safeParse(value);
|
|
118
|
+
if (parsed.success) return parsed.data;
|
|
119
|
+
const parsedError = "error" in parsed && parsed.error ? parsed.error : void 0;
|
|
120
|
+
const reason = typeof parsedError?.message === "string" && parsedError.message.length > 0 ? parsedError.message : "invalid payload";
|
|
121
|
+
throw new Error(`${label} validation failed: ${reason}`);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// src/interrupt.ts
|
|
125
|
+
var awaitInterruptInternal = (args) => {
|
|
126
|
+
const ctx = getHookContext();
|
|
127
|
+
const request = parseWithSchema(
|
|
128
|
+
args.requestSchema,
|
|
129
|
+
args.request,
|
|
130
|
+
"useInterrupt request"
|
|
131
|
+
);
|
|
132
|
+
const enrichedRequest = {
|
|
133
|
+
...request,
|
|
134
|
+
...typeof args.schemaId === "string" && args.schemaId.length > 0 ? { schemaId: args.schemaId } : {},
|
|
135
|
+
...typeof args.schemaVersion === "string" && args.schemaVersion.length > 0 ? { schemaVersion: args.schemaVersion } : {},
|
|
136
|
+
...typeof args.id === "string" && args.id.length > 0 ? { id: args.id } : {},
|
|
137
|
+
...args.meta && typeof args.meta === "object" && !Array.isArray(args.meta) ? { meta: args.meta } : {}
|
|
138
|
+
};
|
|
139
|
+
const raw = ctx.node.awaitInterrupt(enrichedRequest);
|
|
140
|
+
const response = parseWithSchema(
|
|
141
|
+
args.responseSchema,
|
|
142
|
+
raw,
|
|
143
|
+
"useInterrupt response"
|
|
144
|
+
);
|
|
145
|
+
return Promise.resolve(response);
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
// src/structured.ts
|
|
149
|
+
var emitStructuredData = (args) => {
|
|
150
|
+
const ctx = getHookContext();
|
|
151
|
+
const validated = parseWithSchema(
|
|
152
|
+
args.dataSchema,
|
|
153
|
+
args.data,
|
|
154
|
+
"useStructuredData data"
|
|
155
|
+
);
|
|
156
|
+
ctx.node.emit("structured_data", {
|
|
157
|
+
node: ctx.node.graph.node,
|
|
158
|
+
...typeof args.dataType === "string" && args.dataType.length > 0 ? { dataType: args.dataType } : {},
|
|
159
|
+
...typeof args.mode === "string" ? { mode: args.mode } : {},
|
|
160
|
+
...typeof args.schemaId === "string" && args.schemaId.length > 0 ? { schemaId: args.schemaId } : {},
|
|
161
|
+
...typeof args.schemaVersion === "string" && args.schemaVersion.length > 0 ? { schemaVersion: args.schemaVersion } : {},
|
|
162
|
+
...typeof args.id === "string" && args.id.length > 0 ? { id: args.id } : {},
|
|
163
|
+
...typeof args.opId === "string" && args.opId.length > 0 ? { opId: args.opId } : {},
|
|
164
|
+
data: validated
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
var shouldEmitStructured = (cfg) => (cfg?.stream ?? "patch") !== "off";
|
|
168
|
+
var resolveStructuredMode = (cfg) => {
|
|
169
|
+
const mode = cfg?.stream ?? "patch";
|
|
170
|
+
if (mode === "snapshot") return "snapshot";
|
|
171
|
+
if (mode === "patch") return "patch";
|
|
172
|
+
return "final";
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
// src/reason/checkpoint.ts
|
|
176
|
+
var resolveReasonCheckpointKey = (args) => typeof args.id === "string" && args.id.length > 0 ? `__useReason:${args.id}` : `__useReason:auto:${args.autoIndex}`;
|
|
177
|
+
var readReasonCheckpoint = (raw) => {
|
|
178
|
+
if (!raw || typeof raw !== "object") return void 0;
|
|
179
|
+
const value = raw;
|
|
180
|
+
if (value.status !== "awaiting_interrupt") return void 0;
|
|
181
|
+
if (typeof value.firstText !== "string") return void 0;
|
|
182
|
+
if (!value.request || typeof value.request !== "object") return void 0;
|
|
183
|
+
return {
|
|
184
|
+
status: "awaiting_interrupt",
|
|
185
|
+
request: value.request,
|
|
186
|
+
firstText: value.firstText,
|
|
187
|
+
...Object.hasOwn(value, "firstRaw") ? { firstRaw: value.firstRaw } : {},
|
|
188
|
+
...Object.hasOwn(value, "firstOutput") ? { firstOutput: value.firstOutput } : {}
|
|
189
|
+
};
|
|
190
|
+
};
|
|
191
|
+
var resolveHookMemoryPatch = (args) => ({
|
|
192
|
+
__kortyx: {
|
|
193
|
+
nodeState: {
|
|
194
|
+
nodeId: args.nodeId,
|
|
195
|
+
state: args.currentNodeState
|
|
196
|
+
},
|
|
197
|
+
workflowState: args.workflowState
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// src/reason/engine.ts
|
|
202
|
+
var import_node_crypto2 = require("crypto");
|
|
203
|
+
|
|
106
204
|
// src/reason-engine.ts
|
|
205
|
+
var import_node_crypto = require("crypto");
|
|
107
206
|
var chunkToText = (chunk) => {
|
|
108
207
|
if (typeof chunk === "string") return chunk;
|
|
109
208
|
if (typeof chunk.text === "string") return chunk.text;
|
|
110
209
|
if (typeof chunk.content === "string") return chunk.content;
|
|
111
210
|
return "";
|
|
112
211
|
};
|
|
212
|
+
var createRuntimeId = () => {
|
|
213
|
+
try {
|
|
214
|
+
return (0, import_node_crypto.randomUUID)();
|
|
215
|
+
} catch {
|
|
216
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
217
|
+
}
|
|
218
|
+
};
|
|
113
219
|
var emitNodeEvent = (emitEvent, nodeId, event, payload) => {
|
|
114
220
|
if (!emitEvent) return;
|
|
115
221
|
emitEvent(event, {
|
|
@@ -130,11 +236,20 @@ async function runReasonEngine(args) {
|
|
|
130
236
|
messages.push({ role: "system", content: args.system });
|
|
131
237
|
}
|
|
132
238
|
messages.push({ role: "user", content: String(args.input ?? "") });
|
|
239
|
+
const opId = typeof args.opId === "string" && args.opId.length > 0 ? args.opId : createRuntimeId();
|
|
240
|
+
const segmentId = typeof args.segmentId === "string" && args.segmentId.length > 0 ? args.segmentId : createRuntimeId();
|
|
241
|
+
const commonMeta = {
|
|
242
|
+
...typeof args.id === "string" && args.id.length > 0 ? { id: args.id } : {},
|
|
243
|
+
opId,
|
|
244
|
+
segmentId
|
|
245
|
+
};
|
|
133
246
|
if (stream) {
|
|
134
247
|
let final = "";
|
|
135
248
|
let raw;
|
|
136
249
|
if (emit) {
|
|
137
|
-
emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {
|
|
250
|
+
emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {
|
|
251
|
+
...commonMeta
|
|
252
|
+
});
|
|
138
253
|
}
|
|
139
254
|
const response2 = await model.stream(messages);
|
|
140
255
|
for await (const chunk of response2) {
|
|
@@ -146,12 +261,15 @@ async function runReasonEngine(args) {
|
|
|
146
261
|
}
|
|
147
262
|
if (emit) {
|
|
148
263
|
emitNodeEvent(args.emitEvent, args.nodeId, "text-delta", {
|
|
264
|
+
...commonMeta,
|
|
149
265
|
delta: text
|
|
150
266
|
});
|
|
151
267
|
}
|
|
152
268
|
}
|
|
153
269
|
if (emit) {
|
|
154
|
-
emitNodeEvent(args.emitEvent, args.nodeId, "text-end", {
|
|
270
|
+
emitNodeEvent(args.emitEvent, args.nodeId, "text-end", {
|
|
271
|
+
...commonMeta
|
|
272
|
+
});
|
|
155
273
|
}
|
|
156
274
|
return {
|
|
157
275
|
text: final,
|
|
@@ -160,13 +278,18 @@ async function runReasonEngine(args) {
|
|
|
160
278
|
}
|
|
161
279
|
const response = await model.invoke(messages);
|
|
162
280
|
if (emit) {
|
|
163
|
-
emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {
|
|
281
|
+
emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {
|
|
282
|
+
...commonMeta
|
|
283
|
+
});
|
|
164
284
|
if (response.content) {
|
|
165
285
|
emitNodeEvent(args.emitEvent, args.nodeId, "text-delta", {
|
|
286
|
+
...commonMeta,
|
|
166
287
|
delta: response.content
|
|
167
288
|
});
|
|
168
289
|
}
|
|
169
|
-
emitNodeEvent(args.emitEvent, args.nodeId, "text-end", {
|
|
290
|
+
emitNodeEvent(args.emitEvent, args.nodeId, "text-end", {
|
|
291
|
+
...commonMeta
|
|
292
|
+
});
|
|
170
293
|
}
|
|
171
294
|
return {
|
|
172
295
|
text: response.content,
|
|
@@ -174,20 +297,15 @@ async function runReasonEngine(args) {
|
|
|
174
297
|
};
|
|
175
298
|
}
|
|
176
299
|
|
|
177
|
-
// src/
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
...typeof args.dataType === "string" && args.dataType.length > 0 ? { dataType: args.dataType } : {},
|
|
187
|
-
data: args.data
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
async function reasonEngine(args) {
|
|
300
|
+
// src/reason/engine.ts
|
|
301
|
+
var createRuntimeId2 = () => {
|
|
302
|
+
try {
|
|
303
|
+
return (0, import_node_crypto2.randomUUID)();
|
|
304
|
+
} catch {
|
|
305
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
async function reasonEngine(args, meta, inputOverride) {
|
|
191
309
|
const ctx = getHookContext();
|
|
192
310
|
const getProvider = ctx.getProvider;
|
|
193
311
|
if (!getProvider) {
|
|
@@ -196,18 +314,384 @@ async function reasonEngine(args) {
|
|
|
196
314
|
return runReasonEngine({
|
|
197
315
|
getProvider,
|
|
198
316
|
model: args.model,
|
|
199
|
-
input: args.input,
|
|
317
|
+
input: inputOverride ?? args.input,
|
|
200
318
|
system: args.system,
|
|
201
319
|
temperature: args.temperature,
|
|
202
320
|
defaultTemperature: ctx.node.config?.model?.temperature,
|
|
203
321
|
stream: args.stream,
|
|
204
322
|
emit: args.emit,
|
|
205
323
|
nodeId: ctx.node.graph.node,
|
|
206
|
-
emitEvent: ctx.node.emit
|
|
324
|
+
emitEvent: ctx.node.emit,
|
|
325
|
+
id: meta.id,
|
|
326
|
+
opId: meta.opId
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// src/reason/parsing.ts
|
|
331
|
+
var tryJsonParse = (value) => {
|
|
332
|
+
try {
|
|
333
|
+
return { ok: true, value: JSON.parse(value) };
|
|
334
|
+
} catch {
|
|
335
|
+
return { ok: false };
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
var extractJsonCodeBlock = (value) => {
|
|
339
|
+
const match = value.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
340
|
+
if (!match || typeof match[1] !== "string") return null;
|
|
341
|
+
const inner = match[1].trim();
|
|
342
|
+
return inner.length > 0 ? inner : null;
|
|
343
|
+
};
|
|
344
|
+
var tryStringify = (value) => {
|
|
345
|
+
if (typeof value === "string") return value;
|
|
346
|
+
try {
|
|
347
|
+
return JSON.stringify(value, null, 2);
|
|
348
|
+
} catch {
|
|
349
|
+
return String(value);
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
var isRecord2 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
353
|
+
var resolveOutputCandidate = (text) => {
|
|
354
|
+
const trimmed = text.trim();
|
|
355
|
+
if (!trimmed) return text;
|
|
356
|
+
const direct = tryJsonParse(trimmed);
|
|
357
|
+
if (direct.ok) return direct.value;
|
|
358
|
+
const block = extractJsonCodeBlock(trimmed);
|
|
359
|
+
if (block) {
|
|
360
|
+
const parsedBlock = tryJsonParse(block);
|
|
361
|
+
if (parsedBlock.ok) return parsedBlock.value;
|
|
362
|
+
}
|
|
363
|
+
return text;
|
|
364
|
+
};
|
|
365
|
+
var parseInterruptFirstPassResult = (args) => {
|
|
366
|
+
const candidate = resolveOutputCandidate(args.text);
|
|
367
|
+
if (!isRecord2(candidate)) {
|
|
368
|
+
throw new Error(
|
|
369
|
+
"useReason first pass with interrupt must return a JSON object."
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
const requestCandidates = [
|
|
373
|
+
candidate.interruptRequest,
|
|
374
|
+
candidate.request,
|
|
375
|
+
candidate.interrupt,
|
|
376
|
+
candidate
|
|
377
|
+
];
|
|
378
|
+
const requestPayload = requestCandidates.find(
|
|
379
|
+
(value) => value !== null && value !== void 0
|
|
380
|
+
);
|
|
381
|
+
const request = parseWithSchema(
|
|
382
|
+
args.requestSchema,
|
|
383
|
+
requestPayload,
|
|
384
|
+
"useReason interrupt.request"
|
|
385
|
+
);
|
|
386
|
+
let output;
|
|
387
|
+
if (args.outputSchema) {
|
|
388
|
+
const outputPayload = Object.hasOwn(candidate, "output") ? candidate.output : candidate;
|
|
389
|
+
output = parseWithSchema(
|
|
390
|
+
args.outputSchema,
|
|
391
|
+
outputPayload,
|
|
392
|
+
"useReason output"
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
const explicitDraft = typeof candidate.draftText === "string" && candidate.draftText.length > 0 ? candidate.draftText : typeof candidate.text === "string" && candidate.text.length > 0 ? candidate.text : void 0;
|
|
396
|
+
if (!args.outputSchema && !explicitDraft) {
|
|
397
|
+
throw new Error(
|
|
398
|
+
"useReason first pass with interrupt requires `draftText` when outputSchema is not provided."
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
const draftText = explicitDraft ?? (output !== void 0 ? tryStringify(output) : String(args.text ?? ""));
|
|
402
|
+
return {
|
|
403
|
+
draftText,
|
|
404
|
+
request,
|
|
405
|
+
...output !== void 0 ? { output } : {}
|
|
406
|
+
};
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
// src/reason/prompting.ts
|
|
410
|
+
var import_zod = require("zod");
|
|
411
|
+
var tryStringify2 = (value) => {
|
|
412
|
+
if (typeof value === "string") return value;
|
|
413
|
+
try {
|
|
414
|
+
return JSON.stringify(value, null, 2);
|
|
415
|
+
} catch {
|
|
416
|
+
return String(value);
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
var tryGetSchemaHint = (schema) => {
|
|
420
|
+
try {
|
|
421
|
+
const hint = (0, import_zod.toJSONSchema)(schema);
|
|
422
|
+
return JSON.stringify(hint, null, 2);
|
|
423
|
+
} catch {
|
|
424
|
+
return void 0;
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
var withSchemaHint = (input, label, schema) => {
|
|
428
|
+
const hint = tryGetSchemaHint(schema);
|
|
429
|
+
if (!hint) return input;
|
|
430
|
+
return `${input}
|
|
431
|
+
|
|
432
|
+
${label}:
|
|
433
|
+
${hint}`;
|
|
434
|
+
};
|
|
435
|
+
var defaultInterruptFirstPassInput = (args) => {
|
|
436
|
+
const base = args.outputSchema ? [
|
|
437
|
+
args.input,
|
|
438
|
+
"Output rules:",
|
|
439
|
+
"- Return JSON only. No markdown fences.",
|
|
440
|
+
"- Return an object with keys: output, interruptRequest, draftText.",
|
|
441
|
+
"- output must match the expected output JSON schema.",
|
|
442
|
+
"- interruptRequest must match the expected interrupt request JSON schema.",
|
|
443
|
+
"- interruptRequest must be a non-null object.",
|
|
444
|
+
"- draftText should be a concise plain-language summary of output.",
|
|
445
|
+
"- If uncertain, still return a valid interruptRequest with safe default options."
|
|
446
|
+
].join("\n\n") : [
|
|
447
|
+
args.input,
|
|
448
|
+
"Output rules:",
|
|
449
|
+
"- Return JSON only. No markdown fences.",
|
|
450
|
+
"- Return an object with keys: draftText, interruptRequest.",
|
|
451
|
+
"- draftText should be the current draft answer text.",
|
|
452
|
+
"- interruptRequest must match the expected interrupt request JSON schema.",
|
|
453
|
+
"- interruptRequest must be a non-null object.",
|
|
454
|
+
"- If uncertain, still return a valid interruptRequest with safe default options."
|
|
455
|
+
].join("\n\n");
|
|
456
|
+
const withInterruptSchema = withSchemaHint(
|
|
457
|
+
base,
|
|
458
|
+
"Expected interrupt request JSON schema (for `interruptRequest`)",
|
|
459
|
+
args.requestSchema
|
|
460
|
+
);
|
|
461
|
+
if (!args.outputSchema) return withInterruptSchema;
|
|
462
|
+
const withOutputSchema = withSchemaHint(
|
|
463
|
+
withInterruptSchema,
|
|
464
|
+
"Expected output JSON schema (for `output`)",
|
|
465
|
+
args.outputSchema
|
|
466
|
+
);
|
|
467
|
+
return [
|
|
468
|
+
withOutputSchema,
|
|
469
|
+
"JSON template:",
|
|
470
|
+
'{ "output": { ... }, "interruptRequest": { ... }, "draftText": "..." }'
|
|
471
|
+
].join("\n\n");
|
|
472
|
+
};
|
|
473
|
+
var defaultContinuationInput = (args) => [
|
|
474
|
+
"You are continuing a paused reasoning task after user input.",
|
|
475
|
+
"Update the previous result using the user response and return the final answer only.",
|
|
476
|
+
"If the previous output was JSON, return JSON in the same schema.",
|
|
477
|
+
"",
|
|
478
|
+
`Original request:
|
|
479
|
+
${args.input}`,
|
|
480
|
+
`Draft answer text:
|
|
481
|
+
${args.draftText}`,
|
|
482
|
+
`Draft output object:
|
|
483
|
+
${tryStringify2(args.draftOutput ?? args.draftText)}`,
|
|
484
|
+
`Interrupt request shown to user:
|
|
485
|
+
${tryStringify2(args.request)}`,
|
|
486
|
+
`User response:
|
|
487
|
+
${tryStringify2(args.response)}`
|
|
488
|
+
].join("\n\n");
|
|
489
|
+
var withOutputGuardrails = (input, schema) => {
|
|
490
|
+
const base = [
|
|
491
|
+
input,
|
|
492
|
+
"Output rules:",
|
|
493
|
+
"- Return only JSON, no markdown fences.",
|
|
494
|
+
"- Ensure the response conforms to the expected output schema.",
|
|
495
|
+
"- Do not include explanatory text outside the JSON object."
|
|
496
|
+
].join("\n\n");
|
|
497
|
+
return withSchemaHint(base, "Expected output JSON schema", schema);
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
// src/reason/use-reason.ts
|
|
501
|
+
var emitReasonStructuredOutput = (args) => {
|
|
502
|
+
if (!shouldEmitStructured(args.structured)) return;
|
|
503
|
+
emitStructuredData({
|
|
504
|
+
dataType: args.structured?.dataType ?? "reason-output",
|
|
505
|
+
data: args.output,
|
|
506
|
+
mode: resolveStructuredMode(args.structured),
|
|
507
|
+
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
508
|
+
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
509
|
+
...args.id ? { id: args.id } : {},
|
|
510
|
+
opId: args.opId
|
|
207
511
|
});
|
|
512
|
+
};
|
|
513
|
+
async function useReason(args) {
|
|
514
|
+
const ctx = getHookContext();
|
|
515
|
+
const id = typeof args.id === "string" && args.id.length > 0 ? args.id : void 0;
|
|
516
|
+
const opId = createRuntimeId2();
|
|
517
|
+
const checkpointKey = resolveReasonCheckpointKey({
|
|
518
|
+
...id ? { id } : {},
|
|
519
|
+
autoIndex: ctx.reasonCallIndex++
|
|
520
|
+
});
|
|
521
|
+
const existingCheckpoint = readReasonCheckpoint(
|
|
522
|
+
ctx.currentNodeState.byKey[checkpointKey]
|
|
523
|
+
);
|
|
524
|
+
const reasonMeta = typeof id === "string" ? { id, opId } : { opId };
|
|
525
|
+
const suppressTextStream = Boolean(args.outputSchema || args.interrupt);
|
|
526
|
+
let firstText = "";
|
|
527
|
+
let firstRaw;
|
|
528
|
+
let firstOutput;
|
|
529
|
+
let firstInterruptRequest;
|
|
530
|
+
let finalText = "";
|
|
531
|
+
let finalRaw;
|
|
532
|
+
let finalOutput;
|
|
533
|
+
if (existingCheckpoint) {
|
|
534
|
+
firstText = existingCheckpoint.firstText;
|
|
535
|
+
firstRaw = existingCheckpoint.firstRaw;
|
|
536
|
+
firstOutput = existingCheckpoint.firstOutput;
|
|
537
|
+
finalText = firstText;
|
|
538
|
+
finalRaw = firstRaw;
|
|
539
|
+
finalOutput = firstOutput;
|
|
540
|
+
} else {
|
|
541
|
+
const first = await reasonEngine(
|
|
542
|
+
{
|
|
543
|
+
...args,
|
|
544
|
+
emit: suppressTextStream ? false : args.emit,
|
|
545
|
+
stream: suppressTextStream ? false : args.stream
|
|
546
|
+
},
|
|
547
|
+
reasonMeta,
|
|
548
|
+
args.interrupt ? defaultInterruptFirstPassInput({
|
|
549
|
+
input: args.input,
|
|
550
|
+
requestSchema: args.interrupt.requestSchema,
|
|
551
|
+
...args.outputSchema ? { outputSchema: args.outputSchema } : {}
|
|
552
|
+
}) : args.outputSchema ? withOutputGuardrails(
|
|
553
|
+
args.input,
|
|
554
|
+
args.outputSchema
|
|
555
|
+
) : void 0
|
|
556
|
+
);
|
|
557
|
+
firstRaw = first.raw;
|
|
558
|
+
finalRaw = first.raw;
|
|
559
|
+
if (args.interrupt) {
|
|
560
|
+
const firstPass = parseInterruptFirstPassResult({
|
|
561
|
+
text: first.text,
|
|
562
|
+
requestSchema: args.interrupt.requestSchema,
|
|
563
|
+
...args.outputSchema ? { outputSchema: args.outputSchema } : {}
|
|
564
|
+
});
|
|
565
|
+
firstText = firstPass.draftText;
|
|
566
|
+
finalText = firstPass.draftText;
|
|
567
|
+
firstInterruptRequest = firstPass.request;
|
|
568
|
+
firstOutput = firstPass.output;
|
|
569
|
+
finalOutput = firstPass.output;
|
|
570
|
+
if (firstOutput !== void 0) {
|
|
571
|
+
emitReasonStructuredOutput({
|
|
572
|
+
...id ? { id } : {},
|
|
573
|
+
opId,
|
|
574
|
+
output: firstOutput,
|
|
575
|
+
structured: args.structured
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
} else {
|
|
579
|
+
firstText = first.text;
|
|
580
|
+
finalText = first.text;
|
|
581
|
+
if (args.outputSchema) {
|
|
582
|
+
firstOutput = parseWithSchema(
|
|
583
|
+
args.outputSchema,
|
|
584
|
+
resolveOutputCandidate(first.text),
|
|
585
|
+
"useReason output"
|
|
586
|
+
);
|
|
587
|
+
emitReasonStructuredOutput({
|
|
588
|
+
...id ? { id } : {},
|
|
589
|
+
opId,
|
|
590
|
+
output: firstOutput,
|
|
591
|
+
structured: args.structured
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
finalOutput = firstOutput;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
let interruptResponse;
|
|
598
|
+
if (args.interrupt) {
|
|
599
|
+
const requestSchema = args.interrupt.requestSchema;
|
|
600
|
+
const interruptRequest = existingCheckpoint ? parseWithSchema(
|
|
601
|
+
requestSchema,
|
|
602
|
+
existingCheckpoint.request,
|
|
603
|
+
"useReason interrupt.request"
|
|
604
|
+
) : firstInterruptRequest;
|
|
605
|
+
if (!interruptRequest) {
|
|
606
|
+
throw new Error(
|
|
607
|
+
"useReason interrupt request is missing; first pass did not produce a valid interrupt payload."
|
|
608
|
+
);
|
|
609
|
+
}
|
|
610
|
+
if (!existingCheckpoint) {
|
|
611
|
+
ctx.currentNodeState.byKey[checkpointKey] = {
|
|
612
|
+
status: "awaiting_interrupt",
|
|
613
|
+
request: interruptRequest,
|
|
614
|
+
firstText,
|
|
615
|
+
...firstRaw !== void 0 ? { firstRaw } : {},
|
|
616
|
+
...firstOutput !== void 0 ? { firstOutput } : {}
|
|
617
|
+
};
|
|
618
|
+
ctx.dirty = true;
|
|
619
|
+
}
|
|
620
|
+
const resumeMemoryPatch = resolveHookMemoryPatch({
|
|
621
|
+
nodeId: ctx.node.graph.node,
|
|
622
|
+
currentNodeState: ctx.currentNodeState,
|
|
623
|
+
workflowState: ctx.workflowState
|
|
624
|
+
});
|
|
625
|
+
interruptResponse = await awaitInterruptInternal({
|
|
626
|
+
request: interruptRequest,
|
|
627
|
+
requestSchema,
|
|
628
|
+
responseSchema: args.interrupt.responseSchema,
|
|
629
|
+
...args.interrupt.schemaId ? { schemaId: args.interrupt.schemaId } : {},
|
|
630
|
+
...args.interrupt.schemaVersion ? { schemaVersion: args.interrupt.schemaVersion } : {},
|
|
631
|
+
...id ? { id } : {},
|
|
632
|
+
meta: {
|
|
633
|
+
__kortyxResumeMemory: resumeMemoryPatch
|
|
634
|
+
}
|
|
635
|
+
});
|
|
636
|
+
if (Object.hasOwn(ctx.currentNodeState.byKey, checkpointKey)) {
|
|
637
|
+
delete ctx.currentNodeState.byKey[checkpointKey];
|
|
638
|
+
ctx.dirty = true;
|
|
639
|
+
}
|
|
640
|
+
const continuationInput = defaultContinuationInput({
|
|
641
|
+
input: args.input,
|
|
642
|
+
draftText: firstText,
|
|
643
|
+
...firstOutput !== void 0 ? { draftOutput: firstOutput } : {},
|
|
644
|
+
request: interruptRequest,
|
|
645
|
+
response: interruptResponse
|
|
646
|
+
});
|
|
647
|
+
const second = await reasonEngine(
|
|
648
|
+
{
|
|
649
|
+
...args,
|
|
650
|
+
emit: suppressTextStream ? false : args.emit,
|
|
651
|
+
stream: suppressTextStream ? false : args.stream
|
|
652
|
+
},
|
|
653
|
+
reasonMeta,
|
|
654
|
+
args.outputSchema ? withOutputGuardrails(
|
|
655
|
+
continuationInput,
|
|
656
|
+
args.outputSchema
|
|
657
|
+
) : continuationInput
|
|
658
|
+
);
|
|
659
|
+
finalText = second.text;
|
|
660
|
+
finalRaw = second.raw;
|
|
661
|
+
if (args.outputSchema) {
|
|
662
|
+
finalOutput = parseWithSchema(
|
|
663
|
+
args.outputSchema,
|
|
664
|
+
resolveOutputCandidate(finalText),
|
|
665
|
+
"useReason output"
|
|
666
|
+
);
|
|
667
|
+
emitReasonStructuredOutput({
|
|
668
|
+
...id ? { id } : {},
|
|
669
|
+
opId,
|
|
670
|
+
output: finalOutput,
|
|
671
|
+
structured: args.structured
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
return {
|
|
676
|
+
...id ? { id } : {},
|
|
677
|
+
opId,
|
|
678
|
+
text: finalText,
|
|
679
|
+
...finalRaw !== void 0 ? { raw: finalRaw } : {},
|
|
680
|
+
...finalOutput !== void 0 ? { output: finalOutput } : {},
|
|
681
|
+
...interruptResponse !== void 0 ? { interruptResponse } : {}
|
|
682
|
+
};
|
|
208
683
|
}
|
|
209
|
-
|
|
210
|
-
|
|
684
|
+
|
|
685
|
+
// src/hooks.ts
|
|
686
|
+
function useEmit() {
|
|
687
|
+
const ctx = getHookContext();
|
|
688
|
+
return ctx.node.emit;
|
|
689
|
+
}
|
|
690
|
+
function useStructuredData(args) {
|
|
691
|
+
emitStructuredData(args);
|
|
692
|
+
}
|
|
693
|
+
function useReason2(args) {
|
|
694
|
+
return useReason(args);
|
|
211
695
|
}
|
|
212
696
|
function useAiMemory() {
|
|
213
697
|
const ctx = getHookContext();
|
|
@@ -216,9 +700,8 @@ function useAiMemory() {
|
|
|
216
700
|
}
|
|
217
701
|
return ctx.memoryAdapter;
|
|
218
702
|
}
|
|
219
|
-
function
|
|
220
|
-
|
|
221
|
-
return Promise.resolve(ctx.node.awaitInterrupt(input));
|
|
703
|
+
function useInterrupt(args) {
|
|
704
|
+
return awaitInterruptInternal(args);
|
|
222
705
|
}
|
|
223
706
|
function useNodeState(keyOrInitial, initialValue) {
|
|
224
707
|
const ctx = getHookContext();
|
|
@@ -273,9 +756,9 @@ function useWorkflowState(key, initialValue) {
|
|
|
273
756
|
// Annotate the CommonJS export names for ESM import in node:
|
|
274
757
|
0 && (module.exports = {
|
|
275
758
|
runWithHookContext,
|
|
276
|
-
useAiInterrupt,
|
|
277
759
|
useAiMemory,
|
|
278
760
|
useEmit,
|
|
761
|
+
useInterrupt,
|
|
279
762
|
useNodeState,
|
|
280
763
|
useReason,
|
|
281
764
|
useStructuredData,
|