@kortyx/hooks 0.9.1 → 0.10.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.d.ts +15 -3
- package/dist/index.js +434 -84
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +14 -3
- package/dist/internal.js +149 -47
- package/dist/internal.js.map +1 -1
- package/dist/tracing-BG-j1cer.d.ts +25 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -44,6 +44,20 @@ var normalizeNodeState = (value) => {
|
|
|
44
44
|
return { byIndex: [], byKey: {} };
|
|
45
45
|
};
|
|
46
46
|
var cloneWorkflowState = (value) => isRecord(value) ? { ...value } : {};
|
|
47
|
+
var cloneTokenUsage = (value) => {
|
|
48
|
+
if (!isRecord(value)) return void 0;
|
|
49
|
+
const input = typeof value.input === "number" ? value.input : void 0;
|
|
50
|
+
const output = typeof value.output === "number" ? value.output : void 0;
|
|
51
|
+
const total = typeof value.total === "number" ? value.total : void 0;
|
|
52
|
+
if (input === void 0 && output === void 0 && total === void 0) {
|
|
53
|
+
return void 0;
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
input: input ?? 0,
|
|
57
|
+
output: output ?? 0,
|
|
58
|
+
total: total ?? 0
|
|
59
|
+
};
|
|
60
|
+
};
|
|
47
61
|
var getHookRuntimeState = (state) => {
|
|
48
62
|
const runtime = state.runtime ?? {};
|
|
49
63
|
const internal = isRecord(runtime.__kortyx) ? runtime.__kortyx : void 0;
|
|
@@ -67,21 +81,60 @@ var createInternalContext = (ctx) => {
|
|
|
67
81
|
reasonCallIndex: 0,
|
|
68
82
|
currentNodeState,
|
|
69
83
|
workflowState: cloneWorkflowState(hookRuntimeState.workflowState),
|
|
70
|
-
|
|
84
|
+
tokenUsage: cloneTokenUsage(ctx.state.runtime?.tokenUsage),
|
|
85
|
+
stateDirty: false,
|
|
86
|
+
runtimeDirty: false
|
|
71
87
|
};
|
|
72
88
|
};
|
|
73
89
|
var buildRuntimeStateUpdates = (ctx) => {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
__kortyx
|
|
90
|
+
const updates = {};
|
|
91
|
+
if (ctx.stateDirty) {
|
|
92
|
+
updates.__kortyx = {
|
|
77
93
|
nodeState: {
|
|
78
94
|
nodeId: ctx.node.graph.node,
|
|
79
95
|
state: ctx.currentNodeState
|
|
80
96
|
},
|
|
81
97
|
workflowState: ctx.workflowState
|
|
82
|
-
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (ctx.runtimeDirty && ctx.tokenUsage) {
|
|
101
|
+
updates.tokenUsage = ctx.tokenUsage;
|
|
102
|
+
}
|
|
103
|
+
return Object.keys(updates).length > 0 ? updates : null;
|
|
104
|
+
};
|
|
105
|
+
var toTokenUsageDelta = (usage) => {
|
|
106
|
+
if (!usage) return void 0;
|
|
107
|
+
const input = usage.input ?? 0;
|
|
108
|
+
const output = usage.output ?? 0;
|
|
109
|
+
const total = usage.total ?? input + output + (usage.reasoning ?? 0);
|
|
110
|
+
if (input === 0 && output === 0 && total === 0) {
|
|
111
|
+
return void 0;
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
input,
|
|
115
|
+
output,
|
|
116
|
+
total
|
|
83
117
|
};
|
|
84
118
|
};
|
|
119
|
+
function accumulateTokenUsage(usage) {
|
|
120
|
+
const delta = toTokenUsageDelta(usage);
|
|
121
|
+
if (!delta) return;
|
|
122
|
+
const ctx = getHookContext();
|
|
123
|
+
const current = ctx.tokenUsage ?? {
|
|
124
|
+
input: 0,
|
|
125
|
+
output: 0,
|
|
126
|
+
total: 0
|
|
127
|
+
};
|
|
128
|
+
ctx.tokenUsage = {
|
|
129
|
+
input: current.input + delta.input,
|
|
130
|
+
output: current.output + delta.output,
|
|
131
|
+
total: current.total + delta.total
|
|
132
|
+
};
|
|
133
|
+
ctx.runtimeDirty = true;
|
|
134
|
+
}
|
|
135
|
+
function getReasonTraceAdapter() {
|
|
136
|
+
return getHookContext().reasonTrace;
|
|
137
|
+
}
|
|
85
138
|
async function runWithHookContext(ctx, fn) {
|
|
86
139
|
const internal = createInternalContext(ctx);
|
|
87
140
|
try {
|
|
@@ -248,6 +301,12 @@ var readReasonCheckpoint = (raw) => {
|
|
|
248
301
|
request: value.request,
|
|
249
302
|
firstText: value.firstText,
|
|
250
303
|
...Object.hasOwn(value, "firstRaw") ? { firstRaw: value.firstRaw } : {},
|
|
304
|
+
...Object.hasOwn(value, "firstUsage") ? { firstUsage: value.firstUsage } : {},
|
|
305
|
+
...Object.hasOwn(value, "firstFinishReason") ? { firstFinishReason: value.firstFinishReason } : {},
|
|
306
|
+
...Object.hasOwn(value, "firstProviderMetadata") ? {
|
|
307
|
+
firstProviderMetadata: value.firstProviderMetadata
|
|
308
|
+
} : {},
|
|
309
|
+
...Object.hasOwn(value, "firstWarnings") ? { firstWarnings: value.firstWarnings } : {},
|
|
251
310
|
...Object.hasOwn(value, "firstOutput") ? { firstOutput: value.firstOutput } : {}
|
|
252
311
|
};
|
|
253
312
|
};
|
|
@@ -266,12 +325,7 @@ var import_node_crypto3 = require("crypto");
|
|
|
266
325
|
|
|
267
326
|
// src/reason-engine.ts
|
|
268
327
|
var import_node_crypto2 = require("crypto");
|
|
269
|
-
var
|
|
270
|
-
if (typeof chunk === "string") return chunk;
|
|
271
|
-
if (typeof chunk.text === "string") return chunk.text;
|
|
272
|
-
if (typeof chunk.content === "string") return chunk.content;
|
|
273
|
-
return "";
|
|
274
|
-
};
|
|
328
|
+
var toError = (value) => value instanceof Error ? value : new Error(String(value));
|
|
275
329
|
var createRuntimeId = () => {
|
|
276
330
|
try {
|
|
277
331
|
return (0, import_node_crypto2.randomUUID)();
|
|
@@ -290,9 +344,21 @@ async function runReasonEngine(args) {
|
|
|
290
344
|
const stream = args.stream ?? args.model.options?.streaming ?? true;
|
|
291
345
|
const emit = args.emit ?? true;
|
|
292
346
|
const temperature = args.temperature ?? args.model.options?.temperature ?? args.defaultTemperature;
|
|
293
|
-
const
|
|
347
|
+
const maxOutputTokens = args.maxOutputTokens ?? args.model.options?.maxOutputTokens;
|
|
348
|
+
const stopSequences = args.stopSequences ?? args.model.options?.stopSequences;
|
|
349
|
+
const abortSignal = args.abortSignal ?? args.model.options?.abortSignal;
|
|
350
|
+
const reasoning = args.reasoning ?? args.model.options?.reasoning;
|
|
351
|
+
const responseFormat = args.responseFormat ?? args.model.options?.responseFormat;
|
|
352
|
+
const providerOptions = args.providerOptions ?? args.model.options?.providerOptions;
|
|
353
|
+
const model = args.model.provider.getModel(args.model.modelId, {
|
|
294
354
|
...temperature !== void 0 ? { temperature } : {},
|
|
295
|
-
streaming: stream
|
|
355
|
+
streaming: stream,
|
|
356
|
+
...maxOutputTokens !== void 0 ? { maxOutputTokens } : {},
|
|
357
|
+
...stopSequences !== void 0 ? { stopSequences } : {},
|
|
358
|
+
...abortSignal !== void 0 ? { abortSignal } : {},
|
|
359
|
+
...reasoning !== void 0 ? { reasoning } : {},
|
|
360
|
+
...responseFormat !== void 0 ? { responseFormat } : {},
|
|
361
|
+
...providerOptions !== void 0 ? { providerOptions } : {}
|
|
296
362
|
});
|
|
297
363
|
const messages = [];
|
|
298
364
|
if (typeof args.system === "string" && args.system.length > 0) {
|
|
@@ -306,59 +372,154 @@ async function runReasonEngine(args) {
|
|
|
306
372
|
opId,
|
|
307
373
|
segmentId
|
|
308
374
|
};
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
375
|
+
const traceSpan = args.reasonTrace?.startSpan({
|
|
376
|
+
name: "runReasonEngine",
|
|
377
|
+
attributes: {
|
|
378
|
+
...commonMeta,
|
|
379
|
+
providerId: args.model.provider.id,
|
|
380
|
+
modelId: args.model.modelId,
|
|
381
|
+
stream,
|
|
382
|
+
emit,
|
|
383
|
+
...typeof args.nodeId === "string" && args.nodeId.length > 0 ? { nodeId: args.nodeId } : {}
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
const endTrace = (span, result) => {
|
|
387
|
+
span?.end?.({
|
|
388
|
+
...result.usage !== void 0 ? { usage: result.usage } : {},
|
|
389
|
+
...result.finishReason !== void 0 ? { finishReason: result.finishReason } : {},
|
|
390
|
+
...result.providerMetadata !== void 0 ? { providerMetadata: result.providerMetadata } : {},
|
|
391
|
+
...result.warnings !== void 0 ? { warnings: result.warnings } : {},
|
|
392
|
+
attributes: {
|
|
393
|
+
textLength: result.text.length
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
};
|
|
397
|
+
const failTrace = (span, error) => {
|
|
398
|
+
span?.fail?.(error, {
|
|
399
|
+
attributes: {
|
|
400
|
+
providerId: args.model.provider.id,
|
|
401
|
+
modelId: args.model.modelId
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
};
|
|
405
|
+
try {
|
|
406
|
+
if (stream) {
|
|
407
|
+
let final = "";
|
|
408
|
+
let raw;
|
|
409
|
+
let usage;
|
|
410
|
+
let finishReason;
|
|
411
|
+
let providerMetadata;
|
|
412
|
+
let warnings;
|
|
413
|
+
if (emit) {
|
|
414
|
+
emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {
|
|
415
|
+
...commonMeta
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
const response2 = await model.stream(messages);
|
|
419
|
+
for await (const chunk of response2) {
|
|
420
|
+
switch (chunk.type) {
|
|
421
|
+
case "text-delta": {
|
|
422
|
+
if (chunk.delta.length === 0) break;
|
|
423
|
+
final += chunk.delta;
|
|
424
|
+
args.onTextChunk?.(chunk.delta);
|
|
425
|
+
if (chunk.raw !== void 0) {
|
|
426
|
+
raw = chunk.raw;
|
|
427
|
+
}
|
|
428
|
+
if (chunk.providerMetadata !== void 0) {
|
|
429
|
+
providerMetadata = chunk.providerMetadata;
|
|
430
|
+
}
|
|
431
|
+
if (emit) {
|
|
432
|
+
emitNodeEvent(args.emitEvent, args.nodeId, "text-delta", {
|
|
433
|
+
...commonMeta,
|
|
434
|
+
delta: chunk.delta
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
break;
|
|
438
|
+
}
|
|
439
|
+
case "finish": {
|
|
440
|
+
if (chunk.raw !== void 0) {
|
|
441
|
+
raw = chunk.raw;
|
|
442
|
+
}
|
|
443
|
+
if (chunk.usage !== void 0) {
|
|
444
|
+
usage = chunk.usage;
|
|
445
|
+
}
|
|
446
|
+
if (chunk.finishReason !== void 0) {
|
|
447
|
+
finishReason = chunk.finishReason;
|
|
448
|
+
}
|
|
449
|
+
if (chunk.providerMetadata !== void 0) {
|
|
450
|
+
providerMetadata = chunk.providerMetadata;
|
|
451
|
+
}
|
|
452
|
+
if (chunk.warnings !== void 0) {
|
|
453
|
+
warnings = chunk.warnings;
|
|
454
|
+
}
|
|
455
|
+
break;
|
|
456
|
+
}
|
|
457
|
+
case "raw": {
|
|
458
|
+
raw = chunk.raw;
|
|
459
|
+
if (chunk.providerMetadata !== void 0) {
|
|
460
|
+
providerMetadata = chunk.providerMetadata;
|
|
461
|
+
}
|
|
462
|
+
break;
|
|
463
|
+
}
|
|
464
|
+
case "error": {
|
|
465
|
+
if (chunk.raw !== void 0) {
|
|
466
|
+
raw = chunk.raw;
|
|
467
|
+
}
|
|
468
|
+
if (chunk.providerMetadata !== void 0) {
|
|
469
|
+
providerMetadata = chunk.providerMetadata;
|
|
470
|
+
}
|
|
471
|
+
if (chunk.warnings !== void 0) {
|
|
472
|
+
warnings = chunk.warnings;
|
|
473
|
+
}
|
|
474
|
+
throw toError(chunk.error);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
if (emit) {
|
|
479
|
+
emitNodeEvent(args.emitEvent, args.nodeId, "text-end", {
|
|
480
|
+
...commonMeta
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
const result2 = {
|
|
484
|
+
text: final,
|
|
485
|
+
...raw !== void 0 ? { raw } : {},
|
|
486
|
+
...usage !== void 0 ? { usage } : {},
|
|
487
|
+
...finishReason !== void 0 ? { finishReason } : {},
|
|
488
|
+
...providerMetadata !== void 0 ? { providerMetadata } : {},
|
|
489
|
+
...warnings !== void 0 ? { warnings } : {}
|
|
490
|
+
};
|
|
491
|
+
endTrace(traceSpan, result2);
|
|
492
|
+
return result2;
|
|
493
|
+
}
|
|
494
|
+
const response = await model.invoke(messages);
|
|
312
495
|
if (emit) {
|
|
313
496
|
emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {
|
|
314
497
|
...commonMeta
|
|
315
498
|
});
|
|
316
|
-
|
|
317
|
-
const response2 = await model.stream(messages);
|
|
318
|
-
for await (const chunk of response2) {
|
|
319
|
-
const text = chunkToText(chunk);
|
|
320
|
-
if (!text) continue;
|
|
321
|
-
final += text;
|
|
322
|
-
args.onTextChunk?.(text);
|
|
323
|
-
if (typeof chunk === "object" && chunk && "raw" in chunk) {
|
|
324
|
-
raw = chunk.raw;
|
|
325
|
-
}
|
|
326
|
-
if (emit) {
|
|
499
|
+
if (response.content) {
|
|
327
500
|
emitNodeEvent(args.emitEvent, args.nodeId, "text-delta", {
|
|
328
501
|
...commonMeta,
|
|
329
|
-
delta:
|
|
502
|
+
delta: response.content
|
|
330
503
|
});
|
|
331
504
|
}
|
|
332
|
-
}
|
|
333
|
-
if (emit) {
|
|
334
505
|
emitNodeEvent(args.emitEvent, args.nodeId, "text-end", {
|
|
335
506
|
...commonMeta
|
|
336
507
|
});
|
|
337
508
|
}
|
|
338
|
-
|
|
339
|
-
text:
|
|
340
|
-
...raw !== void 0 ? { raw } : {}
|
|
509
|
+
const result = {
|
|
510
|
+
text: response.content,
|
|
511
|
+
...response.raw !== void 0 ? { raw: response.raw } : {},
|
|
512
|
+
...response.usage !== void 0 ? { usage: response.usage } : {},
|
|
513
|
+
...response.finishReason !== void 0 ? { finishReason: response.finishReason } : {},
|
|
514
|
+
...response.providerMetadata !== void 0 ? { providerMetadata: response.providerMetadata } : {},
|
|
515
|
+
...response.warnings !== void 0 ? { warnings: response.warnings } : {}
|
|
341
516
|
};
|
|
517
|
+
endTrace(traceSpan, result);
|
|
518
|
+
return result;
|
|
519
|
+
} catch (error) {
|
|
520
|
+
failTrace(traceSpan, error);
|
|
521
|
+
throw error;
|
|
342
522
|
}
|
|
343
|
-
const response = await model.invoke(messages);
|
|
344
|
-
if (emit) {
|
|
345
|
-
emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {
|
|
346
|
-
...commonMeta
|
|
347
|
-
});
|
|
348
|
-
if (response.content) {
|
|
349
|
-
emitNodeEvent(args.emitEvent, args.nodeId, "text-delta", {
|
|
350
|
-
...commonMeta,
|
|
351
|
-
delta: response.content
|
|
352
|
-
});
|
|
353
|
-
}
|
|
354
|
-
emitNodeEvent(args.emitEvent, args.nodeId, "text-end", {
|
|
355
|
-
...commonMeta
|
|
356
|
-
});
|
|
357
|
-
}
|
|
358
|
-
return {
|
|
359
|
-
text: response.content,
|
|
360
|
-
...response.raw !== void 0 ? { raw: response.raw } : {}
|
|
361
|
-
};
|
|
362
523
|
}
|
|
363
524
|
|
|
364
525
|
// src/reason/engine.ts
|
|
@@ -371,22 +532,24 @@ var createRuntimeId2 = () => {
|
|
|
371
532
|
};
|
|
372
533
|
async function reasonEngine(args, meta, inputOverride) {
|
|
373
534
|
const ctx = getHookContext();
|
|
374
|
-
const getProvider = ctx.getProvider;
|
|
375
|
-
if (!getProvider) {
|
|
376
|
-
throw new Error("useReason requires a provider factory in runtime config.");
|
|
377
|
-
}
|
|
378
535
|
return runReasonEngine({
|
|
379
|
-
getProvider,
|
|
380
536
|
model: args.model,
|
|
381
537
|
input: inputOverride ?? args.input,
|
|
382
538
|
system: args.system,
|
|
383
539
|
temperature: args.temperature,
|
|
540
|
+
maxOutputTokens: args.maxOutputTokens,
|
|
541
|
+
stopSequences: args.stopSequences,
|
|
542
|
+
abortSignal: args.abortSignal,
|
|
543
|
+
reasoning: args.reasoning,
|
|
544
|
+
responseFormat: args.responseFormat,
|
|
545
|
+
providerOptions: args.providerOptions,
|
|
384
546
|
defaultTemperature: ctx.node.config?.model?.temperature,
|
|
385
547
|
stream: args.stream,
|
|
386
548
|
emit: args.emit,
|
|
387
549
|
onTextChunk: args.onTextChunk,
|
|
388
550
|
nodeId: ctx.node.graph.node,
|
|
389
551
|
emitEvent: ctx.node.emit,
|
|
552
|
+
reasonTrace: ctx.reasonTrace,
|
|
390
553
|
id: meta.id,
|
|
391
554
|
opId: meta.opId
|
|
392
555
|
});
|
|
@@ -396,8 +559,11 @@ async function reasonEngine(args, meta, inputOverride) {
|
|
|
396
559
|
var tryJsonParse = (value) => {
|
|
397
560
|
try {
|
|
398
561
|
return { ok: true, value: JSON.parse(value) };
|
|
399
|
-
} catch {
|
|
400
|
-
return {
|
|
562
|
+
} catch (error) {
|
|
563
|
+
return {
|
|
564
|
+
ok: false,
|
|
565
|
+
error: error instanceof Error ? error.message : String(error)
|
|
566
|
+
};
|
|
401
567
|
}
|
|
402
568
|
};
|
|
403
569
|
var extractJsonCodeBlock = (value) => {
|
|
@@ -427,9 +593,50 @@ var resolveOutputCandidate = (text) => {
|
|
|
427
593
|
}
|
|
428
594
|
return text;
|
|
429
595
|
};
|
|
596
|
+
var looksLikeStructuredJsonAttempt = (value) => {
|
|
597
|
+
const trimmed = value.trim();
|
|
598
|
+
if (!trimmed) return false;
|
|
599
|
+
return trimmed.startsWith("{") || trimmed.startsWith("[") || /^```(?:json)?/i.test(trimmed);
|
|
600
|
+
};
|
|
601
|
+
var isLikelyTruncatedStructuredOutput = (text) => {
|
|
602
|
+
const trimmed = text.trim();
|
|
603
|
+
if (!looksLikeStructuredJsonAttempt(trimmed)) return false;
|
|
604
|
+
const direct = tryJsonParse(trimmed);
|
|
605
|
+
if (!direct.ok && direct.error) return true;
|
|
606
|
+
const block = extractJsonCodeBlock(trimmed);
|
|
607
|
+
if (!block) return false;
|
|
608
|
+
const parsedBlock = tryJsonParse(block);
|
|
609
|
+
return !parsedBlock.ok && Boolean(parsedBlock.error);
|
|
610
|
+
};
|
|
611
|
+
var createTruncatedStructuredOutputError = (label) => new Error(
|
|
612
|
+
`${label} was truncated before producing valid structured output. The model stopped due to output length. Increase maxOutputTokens or simplify the requested output.`
|
|
613
|
+
);
|
|
614
|
+
var createInvalidStructuredOutputError = (label) => new Error(
|
|
615
|
+
`${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.`
|
|
616
|
+
);
|
|
617
|
+
var parseReasonOutputWithSchema = (args) => {
|
|
618
|
+
const candidate = resolveOutputCandidate(args.text);
|
|
619
|
+
try {
|
|
620
|
+
return parseWithSchema(args.schema, candidate, args.label);
|
|
621
|
+
} catch (error) {
|
|
622
|
+
if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
|
|
623
|
+
throw createTruncatedStructuredOutputError(args.label);
|
|
624
|
+
}
|
|
625
|
+
if (typeof candidate === "string" && candidate.trim().length > 0) {
|
|
626
|
+
throw createInvalidStructuredOutputError(args.label);
|
|
627
|
+
}
|
|
628
|
+
throw error;
|
|
629
|
+
}
|
|
630
|
+
};
|
|
430
631
|
var parseInterruptFirstPassResult = (args) => {
|
|
431
632
|
const candidate = resolveOutputCandidate(args.text);
|
|
432
633
|
if (!isRecord2(candidate)) {
|
|
634
|
+
if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
|
|
635
|
+
throw createTruncatedStructuredOutputError("useReason output");
|
|
636
|
+
}
|
|
637
|
+
if (typeof candidate === "string" && candidate.trim().length > 0) {
|
|
638
|
+
throw createInvalidStructuredOutputError("useReason output");
|
|
639
|
+
}
|
|
433
640
|
throw new Error(
|
|
434
641
|
"useReason first pass with interrupt must return a JSON object."
|
|
435
642
|
);
|
|
@@ -451,11 +658,21 @@ var parseInterruptFirstPassResult = (args) => {
|
|
|
451
658
|
let output;
|
|
452
659
|
if (args.outputSchema) {
|
|
453
660
|
const outputPayload = Object.hasOwn(candidate, "output") ? candidate.output : candidate;
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
661
|
+
try {
|
|
662
|
+
output = parseWithSchema(
|
|
663
|
+
args.outputSchema,
|
|
664
|
+
outputPayload,
|
|
665
|
+
"useReason output"
|
|
666
|
+
);
|
|
667
|
+
} catch (error) {
|
|
668
|
+
if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
|
|
669
|
+
throw createTruncatedStructuredOutputError("useReason output");
|
|
670
|
+
}
|
|
671
|
+
if (typeof outputPayload === "string" && outputPayload.trim().length > 0) {
|
|
672
|
+
throw createInvalidStructuredOutputError("useReason output");
|
|
673
|
+
}
|
|
674
|
+
throw error;
|
|
675
|
+
}
|
|
459
676
|
}
|
|
460
677
|
const explicitDraft = typeof candidate.draftText === "string" && candidate.draftText.length > 0 ? candidate.draftText : typeof candidate.text === "string" && candidate.text.length > 0 ? candidate.text : void 0;
|
|
461
678
|
if (!args.outputSchema && !explicitDraft) {
|
|
@@ -973,8 +1190,53 @@ var extractStreamingStringValue = (args) => {
|
|
|
973
1190
|
};
|
|
974
1191
|
|
|
975
1192
|
// src/reason/use-reason.ts
|
|
1193
|
+
var mergeUsage = (left, right) => {
|
|
1194
|
+
if (!left) return right;
|
|
1195
|
+
if (!right) return left;
|
|
1196
|
+
const sum = (a, b) => a === void 0 && b === void 0 ? void 0 : (a ?? 0) + (b ?? 0);
|
|
1197
|
+
const raw = left.raw || right.raw ? {
|
|
1198
|
+
...left.raw ?? {},
|
|
1199
|
+
...right.raw ?? {}
|
|
1200
|
+
} : void 0;
|
|
1201
|
+
const input = sum(left.input, right.input);
|
|
1202
|
+
const output = sum(left.output, right.output);
|
|
1203
|
+
const total = sum(left.total, right.total);
|
|
1204
|
+
const reasoning = sum(left.reasoning, right.reasoning);
|
|
1205
|
+
const cacheRead = sum(left.cacheRead, right.cacheRead);
|
|
1206
|
+
const cacheWrite = sum(left.cacheWrite, right.cacheWrite);
|
|
1207
|
+
const merged = {};
|
|
1208
|
+
if (input !== void 0) merged.input = input;
|
|
1209
|
+
if (output !== void 0) merged.output = output;
|
|
1210
|
+
if (total !== void 0) merged.total = total;
|
|
1211
|
+
if (reasoning !== void 0) merged.reasoning = reasoning;
|
|
1212
|
+
if (cacheRead !== void 0) merged.cacheRead = cacheRead;
|
|
1213
|
+
if (cacheWrite !== void 0) merged.cacheWrite = cacheWrite;
|
|
1214
|
+
if (raw) merged.raw = raw;
|
|
1215
|
+
return merged;
|
|
1216
|
+
};
|
|
1217
|
+
var mergeWarnings = (left, right) => {
|
|
1218
|
+
if (!left?.length) return right;
|
|
1219
|
+
if (!right?.length) return left;
|
|
1220
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1221
|
+
const merged = [];
|
|
1222
|
+
for (const warning of [...left, ...right]) {
|
|
1223
|
+
const key = JSON.stringify(warning);
|
|
1224
|
+
if (seen.has(key)) continue;
|
|
1225
|
+
seen.add(key);
|
|
1226
|
+
merged.push(warning);
|
|
1227
|
+
}
|
|
1228
|
+
return merged;
|
|
1229
|
+
};
|
|
1230
|
+
var mergeProviderMetadata = (left, right) => {
|
|
1231
|
+
if (!left) return right;
|
|
1232
|
+
if (!right) return left;
|
|
1233
|
+
return {
|
|
1234
|
+
...left,
|
|
1235
|
+
...right
|
|
1236
|
+
};
|
|
1237
|
+
};
|
|
976
1238
|
var emitReasonStructuredOutput = (args) => {
|
|
977
|
-
if (!shouldEmitStructured(args.structured)) return;
|
|
1239
|
+
if (!args.emit || !shouldEmitStructured(args.structured)) return;
|
|
978
1240
|
emitStructuredData({
|
|
979
1241
|
dataType: args.structured?.dataType ?? "reason-output",
|
|
980
1242
|
data: args.output,
|
|
@@ -985,7 +1247,19 @@ var emitReasonStructuredOutput = (args) => {
|
|
|
985
1247
|
streamId: args.opId
|
|
986
1248
|
});
|
|
987
1249
|
};
|
|
1250
|
+
var shouldEmitReasonStructured = (args) => (args.emit ?? true) && shouldEmitStructured(args.structured);
|
|
1251
|
+
var resolveEffectiveReasoningIncludeThoughts = (args) => args.reasoning?.includeThoughts ?? args.model.options?.reasoning?.includeThoughts ?? false;
|
|
1252
|
+
var resolveEffectiveResponseFormatType = (args) => args.responseFormat?.type ?? args.model.options?.responseFormat?.type;
|
|
1253
|
+
var assertReasoningThoughtsCompatibility = (args) => {
|
|
1254
|
+
if (!resolveEffectiveReasoningIncludeThoughts(args)) return;
|
|
1255
|
+
const usesStructuredOutput = Boolean(args.outputSchema) || Boolean(args.interrupt) || Boolean(args.structured) || resolveEffectiveResponseFormatType(args) === "json";
|
|
1256
|
+
if (!usesStructuredOutput) return;
|
|
1257
|
+
throw new Error(
|
|
1258
|
+
"useReason does not support reasoning.includeThoughts with structured output, interrupt mode, or JSON responseFormat. Disable includeThoughts for this call."
|
|
1259
|
+
);
|
|
1260
|
+
};
|
|
988
1261
|
async function useReason(args) {
|
|
1262
|
+
assertReasoningThoughtsCompatibility(args);
|
|
989
1263
|
const ctx = getHookContext();
|
|
990
1264
|
const id = typeof args.id === "string" && args.id.length > 0 ? args.id : void 0;
|
|
991
1265
|
const opId = createRuntimeId2();
|
|
@@ -997,12 +1271,27 @@ async function useReason(args) {
|
|
|
997
1271
|
ctx.currentNodeState.byKey[checkpointKey]
|
|
998
1272
|
);
|
|
999
1273
|
const reasonMeta = typeof id === "string" ? { id, opId } : { opId };
|
|
1274
|
+
const traceSpan = getReasonTraceAdapter()?.startSpan({
|
|
1275
|
+
name: "useReason",
|
|
1276
|
+
attributes: {
|
|
1277
|
+
...id ? { id } : {},
|
|
1278
|
+
opId,
|
|
1279
|
+
nodeId: ctx.node.graph.node,
|
|
1280
|
+
providerId: args.model.provider.id,
|
|
1281
|
+
modelId: args.model.modelId,
|
|
1282
|
+
stream: args.stream ?? args.model.options?.streaming ?? true,
|
|
1283
|
+
emit: args.emit ?? true,
|
|
1284
|
+
hasOutputSchema: Boolean(args.outputSchema),
|
|
1285
|
+
hasInterrupt: Boolean(args.interrupt),
|
|
1286
|
+
hasStructured: Boolean(args.structured)
|
|
1287
|
+
}
|
|
1288
|
+
});
|
|
1000
1289
|
const suppressTextStream = Boolean(args.outputSchema || args.interrupt);
|
|
1001
1290
|
const setFieldPaths = resolveSetFieldPaths(args.structured);
|
|
1002
1291
|
const appendFieldPaths = resolveAppendFieldPaths(args.structured);
|
|
1003
1292
|
const textDeltaFieldPaths = resolveTextDeltaFieldPaths(args.structured);
|
|
1004
1293
|
const useStructuredIncrementalStreaming = Boolean(
|
|
1005
|
-
args.outputSchema && !args.interrupt && (setFieldPaths.length > 0 || appendFieldPaths.length > 0 || textDeltaFieldPaths.length > 0) && args.stream !== false &&
|
|
1294
|
+
args.outputSchema && !args.interrupt && (setFieldPaths.length > 0 || appendFieldPaths.length > 0 || textDeltaFieldPaths.length > 0) && args.stream !== false && shouldEmitReasonStructured(args)
|
|
1006
1295
|
);
|
|
1007
1296
|
let firstText = "";
|
|
1008
1297
|
let firstRaw;
|
|
@@ -1011,6 +1300,10 @@ async function useReason(args) {
|
|
|
1011
1300
|
let finalText = "";
|
|
1012
1301
|
let finalRaw;
|
|
1013
1302
|
let finalOutput;
|
|
1303
|
+
let aggregatedUsage;
|
|
1304
|
+
let finalFinishReason;
|
|
1305
|
+
let aggregatedProviderMetadata;
|
|
1306
|
+
let aggregatedWarnings;
|
|
1014
1307
|
const emittedSetValues = /* @__PURE__ */ new Map();
|
|
1015
1308
|
const emittedAppendCounts = /* @__PURE__ */ new Map();
|
|
1016
1309
|
const emittedTextValues = /* @__PURE__ */ new Map();
|
|
@@ -1021,6 +1314,13 @@ async function useReason(args) {
|
|
|
1021
1314
|
finalText = firstText;
|
|
1022
1315
|
finalRaw = firstRaw;
|
|
1023
1316
|
finalOutput = firstOutput;
|
|
1317
|
+
aggregatedUsage = existingCheckpoint.firstUsage;
|
|
1318
|
+
finalFinishReason = existingCheckpoint.firstFinishReason;
|
|
1319
|
+
aggregatedProviderMetadata = existingCheckpoint.firstProviderMetadata;
|
|
1320
|
+
aggregatedWarnings = existingCheckpoint.firstWarnings;
|
|
1321
|
+
traceSpan?.addEvent?.("useReason.resume", {
|
|
1322
|
+
checkpointKey
|
|
1323
|
+
});
|
|
1024
1324
|
} else {
|
|
1025
1325
|
const first = await reasonEngine(
|
|
1026
1326
|
{
|
|
@@ -1115,10 +1415,23 @@ async function useReason(args) {
|
|
|
1115
1415
|
);
|
|
1116
1416
|
firstRaw = first.raw;
|
|
1117
1417
|
finalRaw = first.raw;
|
|
1418
|
+
accumulateTokenUsage(first.usage);
|
|
1419
|
+
aggregatedUsage = mergeUsage(aggregatedUsage, first.usage);
|
|
1420
|
+
finalFinishReason = first.finishReason;
|
|
1421
|
+
aggregatedProviderMetadata = mergeProviderMetadata(
|
|
1422
|
+
aggregatedProviderMetadata,
|
|
1423
|
+
first.providerMetadata
|
|
1424
|
+
);
|
|
1425
|
+
aggregatedWarnings = mergeWarnings(aggregatedWarnings, first.warnings);
|
|
1426
|
+
traceSpan?.addEvent?.("useReason.first-pass.complete", {
|
|
1427
|
+
textLength: first.text.length,
|
|
1428
|
+
hasInterrupt: Boolean(args.interrupt)
|
|
1429
|
+
});
|
|
1118
1430
|
if (args.interrupt) {
|
|
1119
1431
|
const firstPass = parseInterruptFirstPassResult({
|
|
1120
1432
|
text: first.text,
|
|
1121
1433
|
requestSchema: args.interrupt.requestSchema,
|
|
1434
|
+
...first.finishReason ? { finishReason: first.finishReason } : {},
|
|
1122
1435
|
...args.outputSchema ? { outputSchema: args.outputSchema } : {}
|
|
1123
1436
|
});
|
|
1124
1437
|
firstText = firstPass.draftText;
|
|
@@ -1130,11 +1443,12 @@ async function useReason(args) {
|
|
|
1130
1443
|
firstText = first.text;
|
|
1131
1444
|
finalText = first.text;
|
|
1132
1445
|
if (args.outputSchema) {
|
|
1133
|
-
firstOutput =
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1446
|
+
firstOutput = parseReasonOutputWithSchema({
|
|
1447
|
+
text: first.text,
|
|
1448
|
+
schema: args.outputSchema,
|
|
1449
|
+
...first.finishReason ? { finishReason: first.finishReason } : {},
|
|
1450
|
+
label: "useReason output"
|
|
1451
|
+
});
|
|
1138
1452
|
}
|
|
1139
1453
|
finalOutput = firstOutput;
|
|
1140
1454
|
}
|
|
@@ -1158,9 +1472,13 @@ async function useReason(args) {
|
|
|
1158
1472
|
request: interruptRequest,
|
|
1159
1473
|
firstText,
|
|
1160
1474
|
...firstRaw !== void 0 ? { firstRaw } : {},
|
|
1475
|
+
...aggregatedUsage !== void 0 ? { firstUsage: aggregatedUsage } : {},
|
|
1476
|
+
...finalFinishReason !== void 0 ? { firstFinishReason: finalFinishReason } : {},
|
|
1477
|
+
...aggregatedProviderMetadata !== void 0 ? { firstProviderMetadata: aggregatedProviderMetadata } : {},
|
|
1478
|
+
...aggregatedWarnings !== void 0 ? { firstWarnings: aggregatedWarnings } : {},
|
|
1161
1479
|
...firstOutput !== void 0 ? { firstOutput } : {}
|
|
1162
1480
|
};
|
|
1163
|
-
ctx.
|
|
1481
|
+
ctx.stateDirty = true;
|
|
1164
1482
|
}
|
|
1165
1483
|
const resumeStatePatch = resolveHookStatePatch({
|
|
1166
1484
|
nodeId: ctx.node.graph.node,
|
|
@@ -1178,9 +1496,12 @@ async function useReason(args) {
|
|
|
1178
1496
|
__kortyxResumeStatePatch: resumeStatePatch
|
|
1179
1497
|
}
|
|
1180
1498
|
});
|
|
1499
|
+
traceSpan?.addEvent?.("useReason.interrupt.resolved", {
|
|
1500
|
+
checkpointKey
|
|
1501
|
+
});
|
|
1181
1502
|
if (Object.hasOwn(ctx.currentNodeState.byKey, checkpointKey)) {
|
|
1182
1503
|
delete ctx.currentNodeState.byKey[checkpointKey];
|
|
1183
|
-
ctx.
|
|
1504
|
+
ctx.stateDirty = true;
|
|
1184
1505
|
}
|
|
1185
1506
|
const continuationInput = defaultContinuationInput({
|
|
1186
1507
|
input: args.input,
|
|
@@ -1203,12 +1524,24 @@ async function useReason(args) {
|
|
|
1203
1524
|
);
|
|
1204
1525
|
finalText = second.text;
|
|
1205
1526
|
finalRaw = second.raw;
|
|
1527
|
+
accumulateTokenUsage(second.usage);
|
|
1528
|
+
aggregatedUsage = mergeUsage(aggregatedUsage, second.usage);
|
|
1529
|
+
finalFinishReason = second.finishReason;
|
|
1530
|
+
aggregatedProviderMetadata = mergeProviderMetadata(
|
|
1531
|
+
aggregatedProviderMetadata,
|
|
1532
|
+
second.providerMetadata
|
|
1533
|
+
);
|
|
1534
|
+
aggregatedWarnings = mergeWarnings(aggregatedWarnings, second.warnings);
|
|
1535
|
+
traceSpan?.addEvent?.("useReason.continuation.complete", {
|
|
1536
|
+
textLength: second.text.length
|
|
1537
|
+
});
|
|
1206
1538
|
if (args.outputSchema) {
|
|
1207
|
-
finalOutput =
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1539
|
+
finalOutput = parseReasonOutputWithSchema({
|
|
1540
|
+
text: finalText,
|
|
1541
|
+
schema: args.outputSchema,
|
|
1542
|
+
...second.finishReason ? { finishReason: second.finishReason } : {},
|
|
1543
|
+
label: "useReason output"
|
|
1544
|
+
});
|
|
1212
1545
|
}
|
|
1213
1546
|
}
|
|
1214
1547
|
if (finalOutput !== void 0) {
|
|
@@ -1216,17 +1549,34 @@ async function useReason(args) {
|
|
|
1216
1549
|
...id ? { id } : {},
|
|
1217
1550
|
opId,
|
|
1218
1551
|
output: finalOutput,
|
|
1219
|
-
structured: args.structured
|
|
1552
|
+
structured: args.structured,
|
|
1553
|
+
emit: args.emit ?? true
|
|
1220
1554
|
});
|
|
1221
1555
|
}
|
|
1222
|
-
|
|
1556
|
+
const result = {
|
|
1223
1557
|
...id ? { id } : {},
|
|
1224
1558
|
opId,
|
|
1225
1559
|
text: finalText,
|
|
1226
1560
|
...finalRaw !== void 0 ? { raw: finalRaw } : {},
|
|
1561
|
+
...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
|
|
1562
|
+
...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
|
|
1563
|
+
...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
|
|
1564
|
+
...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
|
|
1227
1565
|
...finalOutput !== void 0 ? { output: finalOutput } : {},
|
|
1228
1566
|
...interruptResponse !== void 0 ? { interruptResponse } : {}
|
|
1229
1567
|
};
|
|
1568
|
+
traceSpan?.end?.({
|
|
1569
|
+
...aggregatedUsage !== void 0 ? { usage: aggregatedUsage } : {},
|
|
1570
|
+
...finalFinishReason !== void 0 ? { finishReason: finalFinishReason } : {},
|
|
1571
|
+
...aggregatedProviderMetadata !== void 0 ? { providerMetadata: aggregatedProviderMetadata } : {},
|
|
1572
|
+
...aggregatedWarnings !== void 0 ? { warnings: aggregatedWarnings } : {},
|
|
1573
|
+
attributes: {
|
|
1574
|
+
textLength: finalText.length,
|
|
1575
|
+
resumedFromCheckpoint: Boolean(existingCheckpoint),
|
|
1576
|
+
interrupted: Boolean(interruptResponse !== void 0)
|
|
1577
|
+
}
|
|
1578
|
+
});
|
|
1579
|
+
return result;
|
|
1230
1580
|
}
|
|
1231
1581
|
|
|
1232
1582
|
// src/hooks.ts
|
|
@@ -1245,14 +1595,14 @@ function useNodeState(initialValue) {
|
|
|
1245
1595
|
const index = ctx.nodeStateIndex++;
|
|
1246
1596
|
if (index >= nodeState.byIndex.length) {
|
|
1247
1597
|
nodeState.byIndex[index] = initialValue;
|
|
1248
|
-
ctx.
|
|
1598
|
+
ctx.stateDirty = true;
|
|
1249
1599
|
}
|
|
1250
1600
|
const getValue = () => nodeState.byIndex[index];
|
|
1251
1601
|
const setValue = (next) => {
|
|
1252
1602
|
const prev = getValue();
|
|
1253
1603
|
const resolved = typeof next === "function" ? next(prev) : next;
|
|
1254
1604
|
nodeState.byIndex[index] = resolved;
|
|
1255
|
-
ctx.
|
|
1605
|
+
ctx.stateDirty = true;
|
|
1256
1606
|
};
|
|
1257
1607
|
return [getValue(), setValue];
|
|
1258
1608
|
}
|
|
@@ -1263,14 +1613,14 @@ function useWorkflowState(key, ...rest) {
|
|
|
1263
1613
|
const initialValue = rest[0];
|
|
1264
1614
|
if (!Object.hasOwn(workflowState, key) && hasInitial) {
|
|
1265
1615
|
workflowState[key] = initialValue;
|
|
1266
|
-
ctx.
|
|
1616
|
+
ctx.stateDirty = true;
|
|
1267
1617
|
}
|
|
1268
1618
|
const getValue = () => workflowState[key];
|
|
1269
1619
|
const setValue = (next) => {
|
|
1270
1620
|
const prev = getValue();
|
|
1271
1621
|
const resolved = typeof next === "function" ? next(prev) : next;
|
|
1272
1622
|
workflowState[key] = resolved;
|
|
1273
|
-
ctx.
|
|
1623
|
+
ctx.stateDirty = true;
|
|
1274
1624
|
};
|
|
1275
1625
|
return [getValue(), setValue];
|
|
1276
1626
|
}
|