@kortyx/hooks 0.4.0 → 0.5.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 +22 -0
- package/README.md +21 -4
- package/dist/index.d.ts +92 -5
- package/dist/index.js +767 -13
- package/dist/index.js.map +1 -0
- package/dist/internal.d.ts +24 -3
- package/dist/internal.js +125 -5
- package/dist/internal.js.map +1 -0
- package/package.json +11 -7
- package/dist/context.d.ts +0 -26
- package/dist/context.d.ts.map +0 -1
- package/dist/context.js +0 -82
- package/dist/hooks.d.ts +0 -29
- package/dist/hooks.d.ts.map +0 -1
- package/dist/hooks.js +0 -108
- package/dist/index.d.ts.map +0 -1
- package/dist/internal.d.ts.map +0 -1
- package/dist/reason-engine.d.ts +0 -19
- package/dist/reason-engine.d.ts.map +0 -1
- package/dist/reason-engine.js +0 -81
package/dist/index.js
CHANGED
|
@@ -1,13 +1,767 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
var
|
|
5
|
-
|
|
6
|
-
var
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// src/index.ts
|
|
20
|
+
var index_exports = {};
|
|
21
|
+
__export(index_exports, {
|
|
22
|
+
runWithHookContext: () => runWithHookContext,
|
|
23
|
+
useAiMemory: () => useAiMemory,
|
|
24
|
+
useEmit: () => useEmit,
|
|
25
|
+
useInterrupt: () => useInterrupt,
|
|
26
|
+
useNodeState: () => useNodeState,
|
|
27
|
+
useReason: () => useReason2,
|
|
28
|
+
useStructuredData: () => useStructuredData,
|
|
29
|
+
useWorkflowState: () => useWorkflowState
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(index_exports);
|
|
32
|
+
|
|
33
|
+
// src/context.ts
|
|
34
|
+
var import_node_async_hooks = require("async_hooks");
|
|
35
|
+
var storage = new import_node_async_hooks.AsyncLocalStorage();
|
|
36
|
+
var isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
37
|
+
var normalizeNodeState = (value) => {
|
|
38
|
+
if (Array.isArray(value)) {
|
|
39
|
+
return { byIndex: [...value], byKey: {} };
|
|
40
|
+
}
|
|
41
|
+
if (isRecord(value)) {
|
|
42
|
+
const byIndex = Array.isArray(value.byIndex) ? [...value.byIndex] : [];
|
|
43
|
+
const byKey = isRecord(value.byKey) ? { ...value.byKey } : {};
|
|
44
|
+
return { byIndex, byKey };
|
|
45
|
+
}
|
|
46
|
+
return { byIndex: [], byKey: {} };
|
|
47
|
+
};
|
|
48
|
+
var cloneWorkflowState = (value) => isRecord(value) ? { ...value } : {};
|
|
49
|
+
var getHookMemory = (state) => {
|
|
50
|
+
const memory = state.memory ?? {};
|
|
51
|
+
const internal = isRecord(memory.__kortyx) ? memory.__kortyx : void 0;
|
|
52
|
+
const result = {};
|
|
53
|
+
if (isRecord(internal?.nodeState)) result.nodeState = internal?.nodeState;
|
|
54
|
+
if (isRecord(internal?.workflowState)) {
|
|
55
|
+
result.workflowState = internal?.workflowState;
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
};
|
|
59
|
+
var createInternalContext = (ctx) => {
|
|
60
|
+
const hookMemory = getHookMemory(ctx.state);
|
|
61
|
+
const nodeId = ctx.node.graph.node;
|
|
62
|
+
const storedNodeId = typeof hookMemory.nodeState?.nodeId === "string" ? String(hookMemory.nodeState?.nodeId) : "";
|
|
63
|
+
const storedState = hookMemory.nodeState?.state;
|
|
64
|
+
const currentNodeState = storedNodeId && storedNodeId === nodeId ? normalizeNodeState(storedState) : { byIndex: [], byKey: {} };
|
|
65
|
+
return {
|
|
66
|
+
...ctx,
|
|
67
|
+
nodeStateIndex: 0,
|
|
68
|
+
reasonCallIndex: 0,
|
|
69
|
+
currentNodeState,
|
|
70
|
+
workflowState: cloneWorkflowState(hookMemory.workflowState),
|
|
71
|
+
dirty: false
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
var buildMemoryUpdates = (ctx) => {
|
|
75
|
+
if (!ctx.dirty) return null;
|
|
76
|
+
return {
|
|
77
|
+
__kortyx: {
|
|
78
|
+
nodeState: {
|
|
79
|
+
nodeId: ctx.node.graph.node,
|
|
80
|
+
state: ctx.currentNodeState
|
|
81
|
+
},
|
|
82
|
+
workflowState: ctx.workflowState
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
async function runWithHookContext(ctx, fn) {
|
|
87
|
+
const internal = createInternalContext(ctx);
|
|
88
|
+
try {
|
|
89
|
+
const result = await storage.run(internal, fn);
|
|
90
|
+
return { result, memoryUpdates: buildMemoryUpdates(internal) };
|
|
91
|
+
} catch (err) {
|
|
92
|
+
const memoryUpdates = buildMemoryUpdates(internal);
|
|
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
|
+
}
|
|
102
|
+
}
|
|
103
|
+
throw err;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function getHookContext() {
|
|
107
|
+
const ctx = storage.getStore();
|
|
108
|
+
if (!ctx) {
|
|
109
|
+
throw new Error("Hooks can only be used while a node is executing.");
|
|
110
|
+
}
|
|
111
|
+
return ctx;
|
|
112
|
+
}
|
|
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
|
+
|
|
204
|
+
// src/reason-engine.ts
|
|
205
|
+
var import_node_crypto = require("crypto");
|
|
206
|
+
var chunkToText = (chunk) => {
|
|
207
|
+
if (typeof chunk === "string") return chunk;
|
|
208
|
+
if (typeof chunk.text === "string") return chunk.text;
|
|
209
|
+
if (typeof chunk.content === "string") return chunk.content;
|
|
210
|
+
return "";
|
|
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
|
+
};
|
|
219
|
+
var emitNodeEvent = (emitEvent, nodeId, event, payload) => {
|
|
220
|
+
if (!emitEvent) return;
|
|
221
|
+
emitEvent(event, {
|
|
222
|
+
...payload,
|
|
223
|
+
...typeof nodeId === "string" && nodeId.length > 0 ? { node: nodeId } : {}
|
|
224
|
+
});
|
|
225
|
+
};
|
|
226
|
+
async function runReasonEngine(args) {
|
|
227
|
+
const stream = args.stream ?? args.model.options?.streaming ?? true;
|
|
228
|
+
const emit = args.emit ?? true;
|
|
229
|
+
const temperature = args.temperature ?? args.model.options?.temperature ?? args.defaultTemperature;
|
|
230
|
+
const model = args.getProvider(args.model.providerId, args.model.modelId, {
|
|
231
|
+
...temperature !== void 0 ? { temperature } : {},
|
|
232
|
+
streaming: stream
|
|
233
|
+
});
|
|
234
|
+
const messages = [];
|
|
235
|
+
if (typeof args.system === "string" && args.system.length > 0) {
|
|
236
|
+
messages.push({ role: "system", content: args.system });
|
|
237
|
+
}
|
|
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
|
+
};
|
|
246
|
+
if (stream) {
|
|
247
|
+
let final = "";
|
|
248
|
+
let raw;
|
|
249
|
+
if (emit) {
|
|
250
|
+
emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {
|
|
251
|
+
...commonMeta
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
const response2 = await model.stream(messages);
|
|
255
|
+
for await (const chunk of response2) {
|
|
256
|
+
const text = chunkToText(chunk);
|
|
257
|
+
if (!text) continue;
|
|
258
|
+
final += text;
|
|
259
|
+
if (typeof chunk === "object" && chunk && "raw" in chunk) {
|
|
260
|
+
raw = chunk.raw;
|
|
261
|
+
}
|
|
262
|
+
if (emit) {
|
|
263
|
+
emitNodeEvent(args.emitEvent, args.nodeId, "text-delta", {
|
|
264
|
+
...commonMeta,
|
|
265
|
+
delta: text
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (emit) {
|
|
270
|
+
emitNodeEvent(args.emitEvent, args.nodeId, "text-end", {
|
|
271
|
+
...commonMeta
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
text: final,
|
|
276
|
+
...raw !== void 0 ? { raw } : {}
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
const response = await model.invoke(messages);
|
|
280
|
+
if (emit) {
|
|
281
|
+
emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {
|
|
282
|
+
...commonMeta
|
|
283
|
+
});
|
|
284
|
+
if (response.content) {
|
|
285
|
+
emitNodeEvent(args.emitEvent, args.nodeId, "text-delta", {
|
|
286
|
+
...commonMeta,
|
|
287
|
+
delta: response.content
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
emitNodeEvent(args.emitEvent, args.nodeId, "text-end", {
|
|
291
|
+
...commonMeta
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
return {
|
|
295
|
+
text: response.content,
|
|
296
|
+
...response.raw !== void 0 ? { raw: response.raw } : {}
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
|
|
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) {
|
|
309
|
+
const ctx = getHookContext();
|
|
310
|
+
const getProvider = ctx.getProvider;
|
|
311
|
+
if (!getProvider) {
|
|
312
|
+
throw new Error("useReason requires a provider factory in runtime config.");
|
|
313
|
+
}
|
|
314
|
+
return runReasonEngine({
|
|
315
|
+
getProvider,
|
|
316
|
+
model: args.model,
|
|
317
|
+
input: inputOverride ?? args.input,
|
|
318
|
+
system: args.system,
|
|
319
|
+
temperature: args.temperature,
|
|
320
|
+
defaultTemperature: ctx.node.config?.model?.temperature,
|
|
321
|
+
stream: args.stream,
|
|
322
|
+
emit: args.emit,
|
|
323
|
+
nodeId: ctx.node.graph.node,
|
|
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
|
|
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
|
+
};
|
|
683
|
+
}
|
|
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);
|
|
695
|
+
}
|
|
696
|
+
function useAiMemory() {
|
|
697
|
+
const ctx = getHookContext();
|
|
698
|
+
if (!ctx.memoryAdapter) {
|
|
699
|
+
throw new Error("useAiMemory requires a memory adapter in runtime config.");
|
|
700
|
+
}
|
|
701
|
+
return ctx.memoryAdapter;
|
|
702
|
+
}
|
|
703
|
+
function useInterrupt(args) {
|
|
704
|
+
return awaitInterruptInternal(args);
|
|
705
|
+
}
|
|
706
|
+
function useNodeState(keyOrInitial, initialValue) {
|
|
707
|
+
const ctx = getHookContext();
|
|
708
|
+
const nodeState = ctx.currentNodeState;
|
|
709
|
+
if (typeof keyOrInitial === "string") {
|
|
710
|
+
const key = keyOrInitial;
|
|
711
|
+
const hasInitial = arguments.length > 1;
|
|
712
|
+
if (!Object.hasOwn(nodeState.byKey, key) && hasInitial) {
|
|
713
|
+
nodeState.byKey[key] = initialValue;
|
|
714
|
+
ctx.dirty = true;
|
|
715
|
+
}
|
|
716
|
+
const getValue2 = () => nodeState.byKey[key];
|
|
717
|
+
const setValue2 = (next) => {
|
|
718
|
+
const prev = getValue2();
|
|
719
|
+
const resolved = typeof next === "function" ? next(prev) : next;
|
|
720
|
+
nodeState.byKey[key] = resolved;
|
|
721
|
+
ctx.dirty = true;
|
|
722
|
+
};
|
|
723
|
+
return [getValue2(), setValue2];
|
|
724
|
+
}
|
|
725
|
+
const index = ctx.nodeStateIndex++;
|
|
726
|
+
if (index >= nodeState.byIndex.length) {
|
|
727
|
+
nodeState.byIndex[index] = keyOrInitial;
|
|
728
|
+
ctx.dirty = true;
|
|
729
|
+
}
|
|
730
|
+
const getValue = () => nodeState.byIndex[index];
|
|
731
|
+
const setValue = (next) => {
|
|
732
|
+
const prev = getValue();
|
|
733
|
+
const resolved = typeof next === "function" ? next(prev) : next;
|
|
734
|
+
nodeState.byIndex[index] = resolved;
|
|
735
|
+
ctx.dirty = true;
|
|
736
|
+
};
|
|
737
|
+
return [getValue(), setValue];
|
|
738
|
+
}
|
|
739
|
+
function useWorkflowState(key, initialValue) {
|
|
740
|
+
const ctx = getHookContext();
|
|
741
|
+
const workflowState = ctx.workflowState;
|
|
742
|
+
const hasInitial = arguments.length > 1;
|
|
743
|
+
if (!Object.hasOwn(workflowState, key) && hasInitial) {
|
|
744
|
+
workflowState[key] = initialValue;
|
|
745
|
+
ctx.dirty = true;
|
|
746
|
+
}
|
|
747
|
+
const getValue = () => workflowState[key];
|
|
748
|
+
const setValue = (next) => {
|
|
749
|
+
const prev = getValue();
|
|
750
|
+
const resolved = typeof next === "function" ? next(prev) : next;
|
|
751
|
+
workflowState[key] = resolved;
|
|
752
|
+
ctx.dirty = true;
|
|
753
|
+
};
|
|
754
|
+
return [getValue(), setValue];
|
|
755
|
+
}
|
|
756
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
757
|
+
0 && (module.exports = {
|
|
758
|
+
runWithHookContext,
|
|
759
|
+
useAiMemory,
|
|
760
|
+
useEmit,
|
|
761
|
+
useInterrupt,
|
|
762
|
+
useNodeState,
|
|
763
|
+
useReason,
|
|
764
|
+
useStructuredData,
|
|
765
|
+
useWorkflowState
|
|
766
|
+
});
|
|
767
|
+
//# sourceMappingURL=index.js.map
|