@kortyx/hooks 0.21.0 → 0.23.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 +32 -0
- package/dist/index.d.ts +10 -2
- package/dist/index.js +974 -772
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +2 -1
- package/dist/internal.js +22 -3
- package/dist/internal.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
var index_exports = {};
|
|
21
21
|
__export(index_exports, {
|
|
22
22
|
WorkflowCallError: () => WorkflowCallError,
|
|
23
|
+
completeResponse: () => completeResponse,
|
|
23
24
|
createWorkflowHooks: () => createWorkflowHooks,
|
|
24
25
|
emitWorkflowCall: () => emitWorkflowCall,
|
|
25
26
|
runWithHookContext: () => runWithHookContext,
|
|
@@ -35,6 +36,9 @@ __export(index_exports, {
|
|
|
35
36
|
});
|
|
36
37
|
module.exports = __toCommonJS(index_exports);
|
|
37
38
|
|
|
39
|
+
// src/complete-response.ts
|
|
40
|
+
var import_zod = require("zod");
|
|
41
|
+
|
|
38
42
|
// src/context.ts
|
|
39
43
|
var import_node_async_hooks = require("async_hooks");
|
|
40
44
|
var storage = new import_node_async_hooks.AsyncLocalStorage();
|
|
@@ -125,7 +129,7 @@ var toTokenUsageDelta = (usage) => {
|
|
|
125
129
|
if (!usage) return void 0;
|
|
126
130
|
const input = usage.input ?? 0;
|
|
127
131
|
const output = usage.output ?? 0;
|
|
128
|
-
const total = usage.total ?? input + output + (usage.reasoning ?? 0);
|
|
132
|
+
const total = usage.total ?? input + output + (usage.outputIncludesReasoning ? 0 : usage.reasoning ?? 0);
|
|
129
133
|
if (input === 0 && output === 0 && total === 0) {
|
|
130
134
|
return void 0;
|
|
131
135
|
}
|
|
@@ -188,6 +192,30 @@ function getHookContext() {
|
|
|
188
192
|
}
|
|
189
193
|
return ctx;
|
|
190
194
|
}
|
|
195
|
+
function snapshotHookState() {
|
|
196
|
+
const ctx = getHookContext();
|
|
197
|
+
return {
|
|
198
|
+
...ctx.state,
|
|
199
|
+
lastNode: ctx.node.graph.node,
|
|
200
|
+
runtime: { ...ctx.state.runtime, ...buildRuntimeStateUpdates(ctx) }
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// src/complete-response.ts
|
|
205
|
+
var optionsSchema = import_zod.z.object({
|
|
206
|
+
message: import_zod.z.string().optional(),
|
|
207
|
+
data: import_zod.z.json().optional()
|
|
208
|
+
}).strict();
|
|
209
|
+
async function completeResponse(options = {}) {
|
|
210
|
+
const ctx = getHookContext();
|
|
211
|
+
const parsed = optionsSchema.parse(options);
|
|
212
|
+
if (!ctx.node.completeResponse) {
|
|
213
|
+
throw new Error(
|
|
214
|
+
"completeResponse requires an agent-managed root workflow."
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
await ctx.node.completeResponse(parsed, snapshotHookState());
|
|
218
|
+
}
|
|
191
219
|
|
|
192
220
|
// src/interrupt.ts
|
|
193
221
|
var import_core = require("@kortyx/core");
|
|
@@ -399,7 +427,7 @@ var emitNodeEvent = (emitEvent, nodeId, event, payload) => {
|
|
|
399
427
|
});
|
|
400
428
|
};
|
|
401
429
|
async function runReasonEngine(args) {
|
|
402
|
-
|
|
430
|
+
let stream = args.stream ?? args.model.options?.streaming ?? true;
|
|
403
431
|
const emit = args.emit ?? true;
|
|
404
432
|
const temperature = args.temperature ?? args.model.options?.temperature ?? args.defaultTemperature;
|
|
405
433
|
const maxOutputTokens = args.maxOutputTokens ?? args.model.options?.maxOutputTokens;
|
|
@@ -407,7 +435,14 @@ async function runReasonEngine(args) {
|
|
|
407
435
|
const abortSignal = args.abortSignal ?? args.model.options?.abortSignal;
|
|
408
436
|
const reasoning = args.reasoning ?? args.model.options?.reasoning;
|
|
409
437
|
const responseFormat = args.responseFormat ?? args.model.options?.responseFormat;
|
|
410
|
-
const
|
|
438
|
+
const defaults = args.model.options?.providerOptions;
|
|
439
|
+
const providerOptions = defaults || args.providerOptions ? { ...defaults, ...args.providerOptions } : void 0;
|
|
440
|
+
for (const [key, value] of Object.entries(args.providerOptions ?? {})) {
|
|
441
|
+
const previous = defaults?.[key];
|
|
442
|
+
if (previous && value && typeof previous === "object" && typeof value === "object" && !Array.isArray(previous) && !Array.isArray(value)) {
|
|
443
|
+
if (providerOptions) providerOptions[key] = { ...previous, ...value };
|
|
444
|
+
}
|
|
445
|
+
}
|
|
411
446
|
const tools = args.tools ?? args.model.options?.tools;
|
|
412
447
|
(0, import_core2.throwIfExecutionAborted)(abortSignal);
|
|
413
448
|
const model = args.model.provider.getModel(args.model.modelId, {
|
|
@@ -421,6 +456,7 @@ async function runReasonEngine(args) {
|
|
|
421
456
|
...tools !== void 0 ? { tools } : {},
|
|
422
457
|
...providerOptions !== void 0 ? { providerOptions } : {}
|
|
423
458
|
});
|
|
459
|
+
if (tools?.length && !model.supportsToolStreaming) stream = false;
|
|
424
460
|
const messages = args.messages !== void 0 ? args.messages : [
|
|
425
461
|
...typeof args.system === "string" && args.system.length > 0 ? [{ role: "system", content: args.system }] : [],
|
|
426
462
|
{ role: "user", content: String(args.input ?? "") }
|
|
@@ -448,7 +484,9 @@ async function runReasonEngine(args) {
|
|
|
448
484
|
},
|
|
449
485
|
telemetry: {
|
|
450
486
|
...args.telemetry ?? {},
|
|
451
|
-
input: args.telemetry?.input ?? messages
|
|
487
|
+
input: args.telemetry?.input ?? messages.map(
|
|
488
|
+
({ continuation: _continuation, raw: _raw, ...message }) => message
|
|
489
|
+
)
|
|
452
490
|
}
|
|
453
491
|
});
|
|
454
492
|
const modelRequestStartedAt = Date.now();
|
|
@@ -479,7 +517,11 @@ async function runReasonEngine(args) {
|
|
|
479
517
|
});
|
|
480
518
|
};
|
|
481
519
|
const failTrace = (span, error) => {
|
|
520
|
+
const detail = error;
|
|
482
521
|
span?.fail?.(error, {
|
|
522
|
+
...detail?.usage ? { usage: detail.usage } : {},
|
|
523
|
+
...detail?.finishReason ? { finishReason: detail.finishReason } : {},
|
|
524
|
+
...detail?.providerMetadata ? { providerMetadata: detail.providerMetadata } : {},
|
|
483
525
|
attributes: {
|
|
484
526
|
providerId: args.model.provider.id,
|
|
485
527
|
modelId: args.model.modelId
|
|
@@ -488,6 +530,7 @@ async function runReasonEngine(args) {
|
|
|
488
530
|
};
|
|
489
531
|
try {
|
|
490
532
|
if (stream) {
|
|
533
|
+
let continuation;
|
|
491
534
|
let final = "";
|
|
492
535
|
let raw;
|
|
493
536
|
let usage;
|
|
@@ -526,6 +569,7 @@ async function runReasonEngine(args) {
|
|
|
526
569
|
break;
|
|
527
570
|
}
|
|
528
571
|
case "finish": {
|
|
572
|
+
continuation = chunk.continuation;
|
|
529
573
|
if (chunk.raw !== void 0) {
|
|
530
574
|
raw = chunk.raw;
|
|
531
575
|
}
|
|
@@ -574,6 +618,7 @@ async function runReasonEngine(args) {
|
|
|
574
618
|
}
|
|
575
619
|
const result2 = {
|
|
576
620
|
text: final,
|
|
621
|
+
...continuation ? { continuation } : {},
|
|
577
622
|
...raw !== void 0 ? { raw } : {},
|
|
578
623
|
...usage !== void 0 ? { usage } : {},
|
|
579
624
|
...finishReason !== void 0 ? { finishReason } : {},
|
|
@@ -587,6 +632,7 @@ async function runReasonEngine(args) {
|
|
|
587
632
|
}
|
|
588
633
|
const response = await model.invoke(messages);
|
|
589
634
|
(0, import_core2.throwIfExecutionAborted)(abortSignal);
|
|
635
|
+
if (response.content) args.onTextChunk?.(response.content);
|
|
590
636
|
if (emit) {
|
|
591
637
|
emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {
|
|
592
638
|
...commonMeta
|
|
@@ -603,6 +649,7 @@ async function runReasonEngine(args) {
|
|
|
603
649
|
}
|
|
604
650
|
const result = {
|
|
605
651
|
text: response.content,
|
|
652
|
+
...response.continuation ? { continuation: response.continuation } : {},
|
|
606
653
|
...response.raw !== void 0 ? { raw: response.raw } : {},
|
|
607
654
|
...response.usage !== void 0 ? { usage: response.usage } : {},
|
|
608
655
|
...response.finishReason !== void 0 ? { finishReason: response.finishReason } : {},
|
|
@@ -636,480 +683,188 @@ async function reasonEngine(args, meta, inputOverride) {
|
|
|
636
683
|
);
|
|
637
684
|
(0, import_core3.throwIfExecutionAborted)(abortSignal);
|
|
638
685
|
ctx.node.consumeExecution?.("maxModelPasses");
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
686
|
+
try {
|
|
687
|
+
return await runReasonEngine({
|
|
688
|
+
model: args.model,
|
|
689
|
+
input: inputOverride ?? args.input,
|
|
690
|
+
system: args.system,
|
|
691
|
+
temperature: args.temperature,
|
|
692
|
+
maxOutputTokens: args.maxOutputTokens,
|
|
693
|
+
stopSequences: args.stopSequences,
|
|
694
|
+
abortSignal,
|
|
695
|
+
reasoning: args.reasoning,
|
|
696
|
+
responseFormat: args.responseFormat,
|
|
697
|
+
providerOptions: args.providerOptions,
|
|
698
|
+
tools: args.tools,
|
|
699
|
+
messages: args.messages,
|
|
700
|
+
defaultTemperature: ctx.node.config?.model?.temperature,
|
|
701
|
+
stream: args.stream,
|
|
702
|
+
emit: args.emit,
|
|
703
|
+
onTextChunk: args.onTextChunk,
|
|
704
|
+
telemetry: args.telemetry,
|
|
705
|
+
nodeId: ctx.node.graph.node,
|
|
706
|
+
emitEvent: ctx.node.emit,
|
|
707
|
+
reasonTrace: ctx.reasonTrace,
|
|
708
|
+
id: meta.id,
|
|
709
|
+
opId: meta.opId
|
|
710
|
+
});
|
|
711
|
+
} catch (error) {
|
|
712
|
+
const usage = error?.usage;
|
|
713
|
+
if (usage) accumulateTokenUsage(usage);
|
|
714
|
+
throw error;
|
|
715
|
+
}
|
|
663
716
|
}
|
|
664
717
|
|
|
665
|
-
// src/reason/
|
|
666
|
-
var
|
|
718
|
+
// src/reason/output-format.ts
|
|
719
|
+
var import_zod2 = require("zod");
|
|
720
|
+
function inferOutputFormat(schema, explicit) {
|
|
721
|
+
if (explicit) return { responseFormat: explicit };
|
|
722
|
+
if (!schema) return {};
|
|
667
723
|
try {
|
|
668
|
-
|
|
669
|
-
|
|
724
|
+
const json = (0, import_zod2.toJSONSchema)(schema);
|
|
725
|
+
const clean = (value) => {
|
|
726
|
+
if (Array.isArray(value)) return value.map(clean);
|
|
727
|
+
if (!value || typeof value !== "object") return value;
|
|
728
|
+
const object = value;
|
|
729
|
+
if ([
|
|
730
|
+
"allOf",
|
|
731
|
+
"not",
|
|
732
|
+
"dependentRequired",
|
|
733
|
+
"dependentSchemas",
|
|
734
|
+
"if",
|
|
735
|
+
"then",
|
|
736
|
+
"else",
|
|
737
|
+
"prefixItems"
|
|
738
|
+
].some((key) => key in object))
|
|
739
|
+
throw new Error(
|
|
740
|
+
"Schema composition requires an explicit provider schema"
|
|
741
|
+
);
|
|
742
|
+
if (object.type === "object") {
|
|
743
|
+
if (object.additionalProperties !== false)
|
|
744
|
+
throw new Error(
|
|
745
|
+
"Dynamic properties require an explicit provider schema"
|
|
746
|
+
);
|
|
747
|
+
const properties = object.properties;
|
|
748
|
+
const required = object.required;
|
|
749
|
+
if (properties && Object.keys(properties).some((key) => !required?.includes(key))) {
|
|
750
|
+
throw new Error(
|
|
751
|
+
"Optional properties require an explicit provider schema"
|
|
752
|
+
);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
return Object.fromEntries(
|
|
756
|
+
Object.entries(object).filter(([key]) => key !== "$schema" && key !== "default").map(([key, child]) => {
|
|
757
|
+
if ((key === "properties" || key === "$defs" || key === "definitions") && child && typeof child === "object")
|
|
758
|
+
return [
|
|
759
|
+
key,
|
|
760
|
+
Object.fromEntries(
|
|
761
|
+
Object.entries(child).map(([name, definition]) => [
|
|
762
|
+
name,
|
|
763
|
+
clean(definition)
|
|
764
|
+
])
|
|
765
|
+
)
|
|
766
|
+
];
|
|
767
|
+
return [
|
|
768
|
+
key,
|
|
769
|
+
key === "enum" || key === "const" ? child : clean(child)
|
|
770
|
+
];
|
|
771
|
+
})
|
|
772
|
+
);
|
|
773
|
+
};
|
|
774
|
+
if (json.type !== "object")
|
|
775
|
+
throw new Error("The provider schema must have an object root");
|
|
776
|
+
return { responseFormat: { type: "json", schema: clean(json) } };
|
|
777
|
+
} catch {
|
|
670
778
|
return {
|
|
671
|
-
|
|
672
|
-
|
|
779
|
+
responseFormat: { type: "json" },
|
|
780
|
+
warnings: [
|
|
781
|
+
{
|
|
782
|
+
type: "compatibility",
|
|
783
|
+
feature: "outputSchema",
|
|
784
|
+
details: "This validator cannot be inferred as a strict provider JSON schema. Kortyx still validates the final output locally. Supply responseFormat.schema to enforce a wire schema."
|
|
785
|
+
}
|
|
786
|
+
]
|
|
673
787
|
};
|
|
674
788
|
}
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
const
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
try {
|
|
685
|
-
return JSON.stringify(value, null, 2);
|
|
686
|
-
} catch {
|
|
687
|
-
return String(value);
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
// src/reason/structured-stream.ts
|
|
792
|
+
var resolveAppendFieldPaths = (cfg) => {
|
|
793
|
+
const fields = cfg?.fields;
|
|
794
|
+
if (!fields) return [];
|
|
795
|
+
const appendPaths = Object.entries(fields).filter(([, mode]) => mode === "append").map(([path]) => path);
|
|
796
|
+
for (const path of appendPaths) {
|
|
797
|
+
assertStructuredPath(path, "useReason structured append");
|
|
688
798
|
}
|
|
799
|
+
return appendPaths;
|
|
689
800
|
};
|
|
690
|
-
var
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
const
|
|
695
|
-
|
|
696
|
-
const block = extractJsonCodeBlock(trimmed);
|
|
697
|
-
if (block) {
|
|
698
|
-
const parsedBlock = tryJsonParse(block);
|
|
699
|
-
if (parsedBlock.ok) return parsedBlock.value;
|
|
801
|
+
var resolveSetFieldPaths = (cfg) => {
|
|
802
|
+
const fields = cfg?.fields;
|
|
803
|
+
if (!fields) return [];
|
|
804
|
+
const setPaths = Object.entries(fields).filter(([, mode]) => mode === "set").map(([path]) => path);
|
|
805
|
+
for (const path of setPaths) {
|
|
806
|
+
assertStructuredPath(path, "useReason structured set");
|
|
700
807
|
}
|
|
701
|
-
return
|
|
702
|
-
};
|
|
703
|
-
var looksLikeStructuredJsonAttempt = (value) => {
|
|
704
|
-
const trimmed = value.trim();
|
|
705
|
-
if (!trimmed) return false;
|
|
706
|
-
return trimmed.startsWith("{") || trimmed.startsWith("[") || /^```(?:json)?/i.test(trimmed);
|
|
707
|
-
};
|
|
708
|
-
var isLikelyTruncatedStructuredOutput = (text) => {
|
|
709
|
-
const trimmed = text.trim();
|
|
710
|
-
if (!looksLikeStructuredJsonAttempt(trimmed)) return false;
|
|
711
|
-
const direct = tryJsonParse(trimmed);
|
|
712
|
-
if (!direct.ok && direct.error) return true;
|
|
713
|
-
const block = extractJsonCodeBlock(trimmed);
|
|
714
|
-
if (!block) return false;
|
|
715
|
-
const parsedBlock = tryJsonParse(block);
|
|
716
|
-
return !parsedBlock.ok && Boolean(parsedBlock.error);
|
|
808
|
+
return setPaths;
|
|
717
809
|
};
|
|
718
|
-
var
|
|
719
|
-
|
|
720
|
-
);
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
);
|
|
724
|
-
var parseReasonOutputWithSchema = (args) => {
|
|
725
|
-
const candidate = resolveOutputCandidate(args.text);
|
|
726
|
-
try {
|
|
727
|
-
return parseWithSchema(args.schema, candidate, args.label);
|
|
728
|
-
} catch (error) {
|
|
729
|
-
if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
|
|
730
|
-
throw createTruncatedStructuredOutputError(args.label);
|
|
731
|
-
}
|
|
732
|
-
if (typeof candidate === "string" && candidate.trim().length > 0) {
|
|
733
|
-
throw createInvalidStructuredOutputError(args.label);
|
|
734
|
-
}
|
|
735
|
-
throw error;
|
|
810
|
+
var resolveTextDeltaFieldPaths = (cfg) => {
|
|
811
|
+
const fields = cfg?.fields;
|
|
812
|
+
if (!fields) return [];
|
|
813
|
+
const textPaths = Object.entries(fields).filter(([, mode]) => mode === "text-delta").map(([path]) => path);
|
|
814
|
+
for (const path of textPaths) {
|
|
815
|
+
assertStructuredPath(path, "useReason structured text-delta");
|
|
736
816
|
}
|
|
817
|
+
return textPaths;
|
|
737
818
|
};
|
|
738
|
-
var
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
819
|
+
var extractCompletedStringValueAt = (args) => {
|
|
820
|
+
let value = "";
|
|
821
|
+
for (let i = args.start; i < args.text.length; i += 1) {
|
|
822
|
+
const ch = args.text[i];
|
|
823
|
+
if (ch === void 0) break;
|
|
824
|
+
if (ch === '"') {
|
|
825
|
+
return {
|
|
826
|
+
complete: true,
|
|
827
|
+
value
|
|
828
|
+
};
|
|
743
829
|
}
|
|
744
|
-
if (
|
|
745
|
-
|
|
830
|
+
if (ch !== "\\") {
|
|
831
|
+
value += ch;
|
|
832
|
+
continue;
|
|
746
833
|
}
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
let interruptRequired = true;
|
|
753
|
-
if (mode === "optional") {
|
|
754
|
-
if (candidate.decision !== "continue" && candidate.decision !== "interrupt") {
|
|
755
|
-
throw new Error(
|
|
756
|
-
'useReason optional interrupt first pass must include decision "continue" or "interrupt".'
|
|
757
|
-
);
|
|
834
|
+
const next = args.text[i + 1];
|
|
835
|
+
if (!next) {
|
|
836
|
+
return {
|
|
837
|
+
complete: false
|
|
838
|
+
};
|
|
758
839
|
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
candidate.request,
|
|
764
|
-
candidate.interrupt
|
|
765
|
-
].some((value) => value !== null && value !== void 0);
|
|
766
|
-
if (hasUnexpectedRequest) {
|
|
767
|
-
throw new Error(
|
|
768
|
-
'useReason optional interrupt first pass with decision "continue" must not include an interrupt request.'
|
|
769
|
-
);
|
|
770
|
-
}
|
|
840
|
+
if (next === '"' || next === "\\" || next === "/" || next === "b" || next === "f" || next === "n" || next === "r" || next === "t") {
|
|
841
|
+
value += next === "b" ? "\b" : next === "f" ? "\f" : next === "n" ? "\n" : next === "r" ? "\r" : next === "t" ? " " : next;
|
|
842
|
+
i += 1;
|
|
843
|
+
continue;
|
|
771
844
|
}
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
candidate.interrupt,
|
|
779
|
-
mode === "required" ? candidate : void 0
|
|
780
|
-
];
|
|
781
|
-
const requestPayload = requestCandidates.find(
|
|
782
|
-
(value) => value !== null && value !== void 0
|
|
783
|
-
);
|
|
784
|
-
request = parseWithSchema(
|
|
785
|
-
args.requestSchema,
|
|
786
|
-
requestPayload,
|
|
787
|
-
"useReason interrupt.request"
|
|
788
|
-
);
|
|
789
|
-
}
|
|
790
|
-
let output;
|
|
791
|
-
if (args.outputSchema) {
|
|
792
|
-
const outputPayload = Object.hasOwn(candidate, "output") ? candidate.output : candidate;
|
|
793
|
-
try {
|
|
794
|
-
output = parseWithSchema(
|
|
795
|
-
args.outputSchema,
|
|
796
|
-
outputPayload,
|
|
797
|
-
"useReason output"
|
|
798
|
-
);
|
|
799
|
-
} catch (error) {
|
|
800
|
-
if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
|
|
801
|
-
throw createTruncatedStructuredOutputError("useReason output");
|
|
802
|
-
}
|
|
803
|
-
if (typeof outputPayload === "string" && outputPayload.trim().length > 0) {
|
|
804
|
-
throw createInvalidStructuredOutputError("useReason output");
|
|
845
|
+
if (next === "u") {
|
|
846
|
+
const hex = args.text.slice(i + 2, i + 6);
|
|
847
|
+
if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
|
|
848
|
+
return {
|
|
849
|
+
complete: false
|
|
850
|
+
};
|
|
805
851
|
}
|
|
806
|
-
|
|
852
|
+
value += String.fromCharCode(Number.parseInt(hex, 16));
|
|
853
|
+
i += 5;
|
|
854
|
+
continue;
|
|
807
855
|
}
|
|
808
|
-
}
|
|
809
|
-
const explicitDraft = typeof candidate.draftText === "string" && candidate.draftText.length > 0 ? candidate.draftText : typeof candidate.text === "string" && candidate.text.length > 0 ? candidate.text : void 0;
|
|
810
|
-
if (!args.outputSchema && !explicitDraft) {
|
|
811
|
-
throw new Error(
|
|
812
|
-
"useReason first pass with interrupt requires `draftText` when outputSchema is not provided."
|
|
813
|
-
);
|
|
814
|
-
}
|
|
815
|
-
const draftText = explicitDraft ?? (output !== void 0 ? tryStringify(output) : String(args.text ?? ""));
|
|
816
|
-
if (!interruptRequired) {
|
|
817
856
|
return {
|
|
818
|
-
|
|
819
|
-
interruptRequired: false,
|
|
820
|
-
...output !== void 0 ? { output } : {}
|
|
857
|
+
complete: false
|
|
821
858
|
};
|
|
822
859
|
}
|
|
823
860
|
return {
|
|
824
|
-
|
|
825
|
-
interruptRequired: true,
|
|
826
|
-
request,
|
|
827
|
-
...output !== void 0 ? { output } : {}
|
|
861
|
+
complete: false
|
|
828
862
|
};
|
|
829
863
|
};
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
if (typeof value === "string") return value;
|
|
835
|
-
try {
|
|
836
|
-
return JSON.stringify(value, null, 2);
|
|
837
|
-
} catch {
|
|
838
|
-
return String(value);
|
|
839
|
-
}
|
|
840
|
-
};
|
|
841
|
-
var tryGetSchemaHint = (schema) => {
|
|
842
|
-
try {
|
|
843
|
-
const hint = (0, import_zod.toJSONSchema)(schema);
|
|
844
|
-
return JSON.stringify(hint, null, 2);
|
|
845
|
-
} catch {
|
|
846
|
-
return void 0;
|
|
847
|
-
}
|
|
848
|
-
};
|
|
849
|
-
var withSchemaHint = (input, label, schema) => {
|
|
850
|
-
const hint = tryGetSchemaHint(schema);
|
|
851
|
-
if (!hint) return input;
|
|
852
|
-
return `${input}
|
|
853
|
-
|
|
854
|
-
${label}:
|
|
855
|
-
${hint}`;
|
|
856
|
-
};
|
|
857
|
-
var defaultInterruptFirstPassInput = (args) => {
|
|
858
|
-
const mode = args.mode ?? "required";
|
|
859
|
-
const base = mode === "optional" ? args.outputSchema ? [
|
|
860
|
-
args.input,
|
|
861
|
-
"Output rules:",
|
|
862
|
-
"- Return JSON only. No markdown fences.",
|
|
863
|
-
"- Return an object with keys: decision, output, interruptRequest, draftText.",
|
|
864
|
-
'- decision must be either "continue" or "interrupt".',
|
|
865
|
-
'- Use decision "continue" when you can produce the final output without asking the user.',
|
|
866
|
-
'- Use decision "interrupt" only when user input is needed before finalizing.',
|
|
867
|
-
"- output must match the expected output JSON schema.",
|
|
868
|
-
'- When decision is "interrupt", interruptRequest must match the expected interrupt request JSON schema and must be a non-null object.',
|
|
869
|
-
'- When decision is "continue", interruptRequest must be null.',
|
|
870
|
-
"- draftText should be a concise plain-language summary of output."
|
|
871
|
-
].join("\n\n") : [
|
|
872
|
-
args.input,
|
|
873
|
-
"Output rules:",
|
|
874
|
-
"- Return JSON only. No markdown fences.",
|
|
875
|
-
"- Return an object with keys: decision, draftText, interruptRequest.",
|
|
876
|
-
'- decision must be either "continue" or "interrupt".',
|
|
877
|
-
'- Use decision "continue" when you can produce the final answer without asking the user.',
|
|
878
|
-
'- Use decision "interrupt" only when user input is needed before finalizing.',
|
|
879
|
-
"- draftText should be the current answer text.",
|
|
880
|
-
'- When decision is "interrupt", interruptRequest must match the expected interrupt request JSON schema and must be a non-null object.',
|
|
881
|
-
'- When decision is "continue", interruptRequest must be null.'
|
|
882
|
-
].join("\n\n") : args.outputSchema ? [
|
|
883
|
-
args.input,
|
|
884
|
-
"Output rules:",
|
|
885
|
-
"- Return JSON only. No markdown fences.",
|
|
886
|
-
"- Return an object with keys: output, interruptRequest, draftText.",
|
|
887
|
-
"- output must match the expected output JSON schema.",
|
|
888
|
-
"- interruptRequest must match the expected interrupt request JSON schema.",
|
|
889
|
-
"- interruptRequest must be a non-null object.",
|
|
890
|
-
"- draftText should be a concise plain-language summary of output.",
|
|
891
|
-
"- If uncertain, still return a valid interruptRequest with safe default options."
|
|
892
|
-
].join("\n\n") : [
|
|
893
|
-
args.input,
|
|
894
|
-
"Output rules:",
|
|
895
|
-
"- Return JSON only. No markdown fences.",
|
|
896
|
-
"- Return an object with keys: draftText, interruptRequest.",
|
|
897
|
-
"- draftText should be the current draft answer text.",
|
|
898
|
-
"- interruptRequest must match the expected interrupt request JSON schema.",
|
|
899
|
-
"- interruptRequest must be a non-null object.",
|
|
900
|
-
"- If uncertain, still return a valid interruptRequest with safe default options."
|
|
901
|
-
].join("\n\n");
|
|
902
|
-
const withInterruptSchema = withSchemaHint(
|
|
903
|
-
base,
|
|
904
|
-
"Expected interrupt request JSON schema (for `interruptRequest`)",
|
|
905
|
-
args.requestSchema
|
|
906
|
-
);
|
|
907
|
-
if (!args.outputSchema) return withInterruptSchema;
|
|
908
|
-
const withOutputSchema = withSchemaHint(
|
|
909
|
-
withInterruptSchema,
|
|
910
|
-
"Expected output JSON schema (for `output`)",
|
|
911
|
-
args.outputSchema
|
|
912
|
-
);
|
|
913
|
-
return [
|
|
914
|
-
withOutputSchema,
|
|
915
|
-
"JSON template:",
|
|
916
|
-
mode === "optional" ? '{ "decision": "continue", "output": { ... }, "interruptRequest": null, "draftText": "..." }' : '{ "output": { ... }, "interruptRequest": { ... }, "draftText": "..." }'
|
|
917
|
-
].join("\n\n");
|
|
918
|
-
};
|
|
919
|
-
var defaultContinuationInput = (args) => [
|
|
920
|
-
"You are continuing a paused reasoning task after user input.",
|
|
921
|
-
"Update the previous result using the user response and return the final answer only.",
|
|
922
|
-
"If the previous output was JSON, return JSON in the same schema.",
|
|
923
|
-
"",
|
|
924
|
-
`Original request:
|
|
925
|
-
${args.input}`,
|
|
926
|
-
`Draft answer text:
|
|
927
|
-
${args.draftText}`,
|
|
928
|
-
`Draft output object:
|
|
929
|
-
${tryStringify2(args.draftOutput ?? args.draftText)}`,
|
|
930
|
-
`Interrupt request shown to user:
|
|
931
|
-
${tryStringify2(args.request)}`,
|
|
932
|
-
`User response:
|
|
933
|
-
${tryStringify2(args.response)}`
|
|
934
|
-
].join("\n\n");
|
|
935
|
-
var withOutputGuardrails = (input, schema) => {
|
|
936
|
-
const base = [
|
|
937
|
-
input,
|
|
938
|
-
"Output rules:",
|
|
939
|
-
"- Return only JSON, no markdown fences.",
|
|
940
|
-
"- Ensure the response conforms to the expected output schema.",
|
|
941
|
-
"- Do not include explanatory text outside the JSON object."
|
|
942
|
-
].join("\n\n");
|
|
943
|
-
return withSchemaHint(base, "Expected output JSON schema", schema);
|
|
944
|
-
};
|
|
945
|
-
var withStructuredStreamHints = (input, args) => {
|
|
946
|
-
const rules = [];
|
|
947
|
-
for (const path of args.setFieldPaths ?? []) {
|
|
948
|
-
rules.push(
|
|
949
|
-
`- For the field path pattern \`${path}\`, write one stable final value and do not rewrite it after it is complete.`
|
|
950
|
-
);
|
|
951
|
-
}
|
|
952
|
-
for (const path of args.appendFieldPaths ?? []) {
|
|
953
|
-
rules.push(
|
|
954
|
-
`- For the array field path pattern \`${path}\`, write items in final order and finish each item before starting the next.`
|
|
955
|
-
);
|
|
956
|
-
rules.push(
|
|
957
|
-
`- Once an item in \`${path}\` is complete, do not rewrite or reorder earlier items.`
|
|
958
|
-
);
|
|
959
|
-
}
|
|
960
|
-
for (const path of args.textDeltaFieldPaths ?? []) {
|
|
961
|
-
rules.push(
|
|
962
|
-
`- For the string field path pattern \`${path}\`, write the value progressively by appending text to the end.`
|
|
963
|
-
);
|
|
964
|
-
rules.push(
|
|
965
|
-
`- Do not rewrite or replace the earlier prefix of \`${path}\` once emitted.`
|
|
966
|
-
);
|
|
967
|
-
}
|
|
968
|
-
if (rules.length === 0) return input;
|
|
969
|
-
return [
|
|
970
|
-
input,
|
|
971
|
-
"Streaming rules:",
|
|
972
|
-
...rules,
|
|
973
|
-
"- Keep the overall response as valid JSON."
|
|
974
|
-
].join("\n\n");
|
|
975
|
-
};
|
|
976
|
-
|
|
977
|
-
// src/reason/result.ts
|
|
978
|
-
var mergeUsage = (left, right) => {
|
|
979
|
-
if (!left) return right;
|
|
980
|
-
if (!right) return left;
|
|
981
|
-
const sum = (a, b) => a === void 0 && b === void 0 ? void 0 : (a ?? 0) + (b ?? 0);
|
|
982
|
-
const raw = left.raw || right.raw ? {
|
|
983
|
-
...left.raw ?? {},
|
|
984
|
-
...right.raw ?? {}
|
|
985
|
-
} : void 0;
|
|
986
|
-
const input = sum(left.input, right.input);
|
|
987
|
-
const output = sum(left.output, right.output);
|
|
988
|
-
const total = sum(left.total, right.total);
|
|
989
|
-
const reasoning = sum(left.reasoning, right.reasoning);
|
|
990
|
-
const cacheRead = sum(left.cacheRead, right.cacheRead);
|
|
991
|
-
const cacheWrite = sum(left.cacheWrite, right.cacheWrite);
|
|
992
|
-
const merged = {};
|
|
993
|
-
if (input !== void 0) merged.input = input;
|
|
994
|
-
if (output !== void 0) merged.output = output;
|
|
995
|
-
if (total !== void 0) merged.total = total;
|
|
996
|
-
if (reasoning !== void 0) merged.reasoning = reasoning;
|
|
997
|
-
if (cacheRead !== void 0) merged.cacheRead = cacheRead;
|
|
998
|
-
if (cacheWrite !== void 0) merged.cacheWrite = cacheWrite;
|
|
999
|
-
if (raw) merged.raw = raw;
|
|
1000
|
-
return merged;
|
|
1001
|
-
};
|
|
1002
|
-
var mergeWarnings = (left, right) => {
|
|
1003
|
-
if (!left?.length) return right;
|
|
1004
|
-
if (!right?.length) return left;
|
|
1005
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1006
|
-
const merged = [];
|
|
1007
|
-
for (const warning of [...left, ...right]) {
|
|
1008
|
-
const key = JSON.stringify(warning);
|
|
1009
|
-
if (seen.has(key)) continue;
|
|
1010
|
-
seen.add(key);
|
|
1011
|
-
merged.push(warning);
|
|
1012
|
-
}
|
|
1013
|
-
return merged;
|
|
1014
|
-
};
|
|
1015
|
-
var mergeProviderMetadata = (left, right) => {
|
|
1016
|
-
if (!left) return right;
|
|
1017
|
-
if (!right) return left;
|
|
1018
|
-
return {
|
|
1019
|
-
...left,
|
|
1020
|
-
...right
|
|
1021
|
-
};
|
|
1022
|
-
};
|
|
1023
|
-
var emitReasonStructuredOutput = (args) => {
|
|
1024
|
-
if (!args.emit || !shouldEmitStructured(args.structured)) return;
|
|
1025
|
-
emitStructuredData({
|
|
1026
|
-
dataType: args.structured?.dataType ?? "reason-output",
|
|
1027
|
-
data: args.output,
|
|
1028
|
-
kind: "final",
|
|
1029
|
-
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
1030
|
-
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
1031
|
-
...args.id ? { id: args.id } : {},
|
|
1032
|
-
streamId: args.opId
|
|
1033
|
-
});
|
|
1034
|
-
};
|
|
1035
|
-
|
|
1036
|
-
// src/reason/structured-stream.ts
|
|
1037
|
-
var resolveAppendFieldPaths = (cfg) => {
|
|
1038
|
-
const fields = cfg?.fields;
|
|
1039
|
-
if (!fields) return [];
|
|
1040
|
-
const appendPaths = Object.entries(fields).filter(([, mode]) => mode === "append").map(([path]) => path);
|
|
1041
|
-
for (const path of appendPaths) {
|
|
1042
|
-
assertStructuredPath(path, "useReason structured append");
|
|
1043
|
-
}
|
|
1044
|
-
return appendPaths;
|
|
1045
|
-
};
|
|
1046
|
-
var resolveSetFieldPaths = (cfg) => {
|
|
1047
|
-
const fields = cfg?.fields;
|
|
1048
|
-
if (!fields) return [];
|
|
1049
|
-
const setPaths = Object.entries(fields).filter(([, mode]) => mode === "set").map(([path]) => path);
|
|
1050
|
-
for (const path of setPaths) {
|
|
1051
|
-
assertStructuredPath(path, "useReason structured set");
|
|
1052
|
-
}
|
|
1053
|
-
return setPaths;
|
|
1054
|
-
};
|
|
1055
|
-
var resolveTextDeltaFieldPaths = (cfg) => {
|
|
1056
|
-
const fields = cfg?.fields;
|
|
1057
|
-
if (!fields) return [];
|
|
1058
|
-
const textPaths = Object.entries(fields).filter(([, mode]) => mode === "text-delta").map(([path]) => path);
|
|
1059
|
-
for (const path of textPaths) {
|
|
1060
|
-
assertStructuredPath(path, "useReason structured text-delta");
|
|
1061
|
-
}
|
|
1062
|
-
return textPaths;
|
|
1063
|
-
};
|
|
1064
|
-
var extractCompletedStringValueAt = (args) => {
|
|
1065
|
-
let value = "";
|
|
1066
|
-
for (let i = args.start; i < args.text.length; i += 1) {
|
|
1067
|
-
const ch = args.text[i];
|
|
1068
|
-
if (ch === void 0) break;
|
|
1069
|
-
if (ch === '"') {
|
|
1070
|
-
return {
|
|
1071
|
-
complete: true,
|
|
1072
|
-
value
|
|
1073
|
-
};
|
|
1074
|
-
}
|
|
1075
|
-
if (ch !== "\\") {
|
|
1076
|
-
value += ch;
|
|
1077
|
-
continue;
|
|
1078
|
-
}
|
|
1079
|
-
const next = args.text[i + 1];
|
|
1080
|
-
if (!next) {
|
|
1081
|
-
return {
|
|
1082
|
-
complete: false
|
|
1083
|
-
};
|
|
1084
|
-
}
|
|
1085
|
-
if (next === '"' || next === "\\" || next === "/" || next === "b" || next === "f" || next === "n" || next === "r" || next === "t") {
|
|
1086
|
-
value += next === "b" ? "\b" : next === "f" ? "\f" : next === "n" ? "\n" : next === "r" ? "\r" : next === "t" ? " " : next;
|
|
1087
|
-
i += 1;
|
|
1088
|
-
continue;
|
|
1089
|
-
}
|
|
1090
|
-
if (next === "u") {
|
|
1091
|
-
const hex = args.text.slice(i + 2, i + 6);
|
|
1092
|
-
if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
|
|
1093
|
-
return {
|
|
1094
|
-
complete: false
|
|
1095
|
-
};
|
|
1096
|
-
}
|
|
1097
|
-
value += String.fromCharCode(Number.parseInt(hex, 16));
|
|
1098
|
-
i += 5;
|
|
1099
|
-
continue;
|
|
1100
|
-
}
|
|
1101
|
-
return {
|
|
1102
|
-
complete: false
|
|
1103
|
-
};
|
|
1104
|
-
}
|
|
1105
|
-
return {
|
|
1106
|
-
complete: false
|
|
1107
|
-
};
|
|
1108
|
-
};
|
|
1109
|
-
var skipWhitespace = (text, start) => {
|
|
1110
|
-
let i = start;
|
|
1111
|
-
while (i < text.length && /\s/.test(text[i] ?? "")) i += 1;
|
|
1112
|
-
return i;
|
|
864
|
+
var skipWhitespace = (text, start) => {
|
|
865
|
+
let i = start;
|
|
866
|
+
while (i < text.length && /\s/.test(text[i] ?? "")) i += 1;
|
|
867
|
+
return i;
|
|
1113
868
|
};
|
|
1114
869
|
var pathSegmentMatches = (patternPart, actualPart) => patternPart === "*" || patternPart === actualPart;
|
|
1115
870
|
var pathMatchesPattern = (actualParts, patternParts) => actualParts.length === patternParts.length && actualParts.every(
|
|
@@ -1357,13 +1112,86 @@ var extractCompletedValueAt = (args) => {
|
|
|
1357
1112
|
return { complete: false };
|
|
1358
1113
|
}
|
|
1359
1114
|
}
|
|
1360
|
-
return { complete: false };
|
|
1115
|
+
return { complete: false };
|
|
1116
|
+
};
|
|
1117
|
+
var extractCompletedNestedValueAt = (args) => {
|
|
1118
|
+
let inString = false;
|
|
1119
|
+
let isEscaped = false;
|
|
1120
|
+
let nesting = 0;
|
|
1121
|
+
for (let i = args.start; i < args.text.length; i += 1) {
|
|
1122
|
+
const ch = args.text[i];
|
|
1123
|
+
if (ch === void 0) break;
|
|
1124
|
+
if (inString) {
|
|
1125
|
+
if (isEscaped) {
|
|
1126
|
+
isEscaped = false;
|
|
1127
|
+
continue;
|
|
1128
|
+
}
|
|
1129
|
+
if (ch === "\\") {
|
|
1130
|
+
isEscaped = true;
|
|
1131
|
+
continue;
|
|
1132
|
+
}
|
|
1133
|
+
if (ch === '"') {
|
|
1134
|
+
inString = false;
|
|
1135
|
+
}
|
|
1136
|
+
continue;
|
|
1137
|
+
}
|
|
1138
|
+
if (ch === '"') {
|
|
1139
|
+
inString = true;
|
|
1140
|
+
continue;
|
|
1141
|
+
}
|
|
1142
|
+
if (ch === args.opener) {
|
|
1143
|
+
nesting += 1;
|
|
1144
|
+
continue;
|
|
1145
|
+
}
|
|
1146
|
+
if (ch === args.closer) {
|
|
1147
|
+
nesting -= 1;
|
|
1148
|
+
if (nesting === 0) {
|
|
1149
|
+
const raw = args.text.slice(args.start, i + 1);
|
|
1150
|
+
try {
|
|
1151
|
+
return {
|
|
1152
|
+
complete: true,
|
|
1153
|
+
value: JSON.parse(raw)
|
|
1154
|
+
};
|
|
1155
|
+
} catch {
|
|
1156
|
+
return {
|
|
1157
|
+
complete: false
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
return {
|
|
1164
|
+
complete: false
|
|
1165
|
+
};
|
|
1361
1166
|
};
|
|
1362
|
-
var
|
|
1167
|
+
var extractCompletedFieldValues = (args) => {
|
|
1168
|
+
const located = locateValueStarts(args);
|
|
1169
|
+
const values = [];
|
|
1170
|
+
for (const match of located) {
|
|
1171
|
+
const parsed = extractCompletedValueAt({
|
|
1172
|
+
text: args.text,
|
|
1173
|
+
start: match.start
|
|
1174
|
+
});
|
|
1175
|
+
if (!parsed.complete) continue;
|
|
1176
|
+
values.push({
|
|
1177
|
+
path: match.path,
|
|
1178
|
+
value: parsed.value
|
|
1179
|
+
});
|
|
1180
|
+
}
|
|
1181
|
+
return values;
|
|
1182
|
+
};
|
|
1183
|
+
var extractCompletedArrayItems = (args) => {
|
|
1184
|
+
const located = locateValueStart(args);
|
|
1185
|
+
if (!located.found || located.start === void 0) return [];
|
|
1186
|
+
const arrayStart = skipWhitespace(args.text, located.start);
|
|
1187
|
+
if (args.text[arrayStart] !== "[") return [];
|
|
1188
|
+
const start = arrayStart + 1;
|
|
1189
|
+
const items = [];
|
|
1363
1190
|
let inString = false;
|
|
1364
1191
|
let isEscaped = false;
|
|
1365
1192
|
let nesting = 0;
|
|
1366
|
-
|
|
1193
|
+
let itemStart = -1;
|
|
1194
|
+
for (let i = start; i < args.text.length; i += 1) {
|
|
1367
1195
|
const ch = args.text[i];
|
|
1368
1196
|
if (ch === void 0) break;
|
|
1369
1197
|
if (inString) {
|
|
@@ -1382,214 +1210,598 @@ var extractCompletedNestedValueAt = (args) => {
|
|
|
1382
1210
|
}
|
|
1383
1211
|
if (ch === '"') {
|
|
1384
1212
|
inString = true;
|
|
1213
|
+
if (itemStart < 0) itemStart = i;
|
|
1385
1214
|
continue;
|
|
1386
1215
|
}
|
|
1387
|
-
if (
|
|
1216
|
+
if (itemStart < 0) {
|
|
1217
|
+
if (/\s/.test(ch) || ch === ",") continue;
|
|
1218
|
+
if (ch === "]") break;
|
|
1219
|
+
itemStart = i;
|
|
1220
|
+
if (ch === "{" || ch === "[") {
|
|
1221
|
+
nesting = 1;
|
|
1222
|
+
} else {
|
|
1223
|
+
nesting = 0;
|
|
1224
|
+
}
|
|
1225
|
+
continue;
|
|
1226
|
+
}
|
|
1227
|
+
if (ch === "{" || ch === "[") {
|
|
1388
1228
|
nesting += 1;
|
|
1389
1229
|
continue;
|
|
1390
1230
|
}
|
|
1391
|
-
if (ch ===
|
|
1392
|
-
nesting
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1231
|
+
if (ch === "}" || ch === "]") {
|
|
1232
|
+
if (nesting > 0) {
|
|
1233
|
+
nesting -= 1;
|
|
1234
|
+
continue;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
if (nesting === 0 && (ch === "," || ch === "]")) {
|
|
1238
|
+
const raw = args.text.slice(itemStart, i).trim();
|
|
1239
|
+
if (raw) {
|
|
1240
|
+
try {
|
|
1241
|
+
items.push(JSON.parse(raw));
|
|
1242
|
+
} catch {
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
itemStart = -1;
|
|
1246
|
+
if (ch === "]") break;
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
return items;
|
|
1250
|
+
};
|
|
1251
|
+
var extractCompletedArrayItemGroups = (args) => {
|
|
1252
|
+
return locateValueStarts(args).flatMap((match) => {
|
|
1253
|
+
const items = extractCompletedArrayItems({
|
|
1254
|
+
text: args.text,
|
|
1255
|
+
path: match.path
|
|
1256
|
+
});
|
|
1257
|
+
return items.length > 0 ? [
|
|
1258
|
+
{
|
|
1259
|
+
path: match.path,
|
|
1260
|
+
items
|
|
1261
|
+
}
|
|
1262
|
+
] : [];
|
|
1263
|
+
});
|
|
1264
|
+
};
|
|
1265
|
+
var extractStreamingStringValue = (args) => {
|
|
1266
|
+
const located = locateValueStart(args);
|
|
1267
|
+
if (!located.found || located.start === void 0) {
|
|
1268
|
+
return {
|
|
1269
|
+
found: located.found,
|
|
1270
|
+
complete: false,
|
|
1271
|
+
value: ""
|
|
1272
|
+
};
|
|
1273
|
+
}
|
|
1274
|
+
const stringStart = skipWhitespace(args.text, located.start);
|
|
1275
|
+
if (args.text[stringStart] !== '"') {
|
|
1276
|
+
return {
|
|
1277
|
+
found: true,
|
|
1278
|
+
complete: false,
|
|
1279
|
+
value: ""
|
|
1280
|
+
};
|
|
1281
|
+
}
|
|
1282
|
+
let value = "";
|
|
1283
|
+
for (let i = stringStart + 1; i < args.text.length; i += 1) {
|
|
1284
|
+
const ch = args.text[i];
|
|
1285
|
+
if (ch === void 0) break;
|
|
1286
|
+
if (ch === '"') {
|
|
1287
|
+
return {
|
|
1288
|
+
found: true,
|
|
1289
|
+
complete: true,
|
|
1290
|
+
value
|
|
1291
|
+
};
|
|
1292
|
+
}
|
|
1293
|
+
if (ch !== "\\") {
|
|
1294
|
+
value += ch;
|
|
1295
|
+
continue;
|
|
1296
|
+
}
|
|
1297
|
+
const next = args.text[i + 1];
|
|
1298
|
+
if (!next) {
|
|
1299
|
+
return {
|
|
1300
|
+
found: true,
|
|
1301
|
+
complete: false,
|
|
1302
|
+
value
|
|
1303
|
+
};
|
|
1304
|
+
}
|
|
1305
|
+
if (next === '"' || next === "\\" || next === "/" || next === "b" || next === "f" || next === "n" || next === "r" || next === "t") {
|
|
1306
|
+
value += next === "b" ? "\b" : next === "f" ? "\f" : next === "n" ? "\n" : next === "r" ? "\r" : next === "t" ? " " : next;
|
|
1307
|
+
i += 1;
|
|
1308
|
+
continue;
|
|
1309
|
+
}
|
|
1310
|
+
if (next === "u") {
|
|
1311
|
+
const hex = args.text.slice(i + 2, i + 6);
|
|
1312
|
+
if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
|
|
1313
|
+
return {
|
|
1314
|
+
found: true,
|
|
1315
|
+
complete: false,
|
|
1316
|
+
value
|
|
1317
|
+
};
|
|
1318
|
+
}
|
|
1319
|
+
value += String.fromCharCode(Number.parseInt(hex, 16));
|
|
1320
|
+
i += 5;
|
|
1321
|
+
continue;
|
|
1322
|
+
}
|
|
1323
|
+
return {
|
|
1324
|
+
found: true,
|
|
1325
|
+
complete: false,
|
|
1326
|
+
value
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
1329
|
+
return {
|
|
1330
|
+
found: true,
|
|
1331
|
+
complete: false,
|
|
1332
|
+
value
|
|
1333
|
+
};
|
|
1334
|
+
};
|
|
1335
|
+
var extractStreamingStringValues = (args) => {
|
|
1336
|
+
return locateValueStarts(args).flatMap((match) => {
|
|
1337
|
+
const current = extractStreamingStringValue({
|
|
1338
|
+
text: args.text,
|
|
1339
|
+
path: match.path
|
|
1340
|
+
});
|
|
1341
|
+
return current.found ? [
|
|
1342
|
+
{
|
|
1343
|
+
path: match.path,
|
|
1344
|
+
complete: current.complete,
|
|
1345
|
+
value: current.value
|
|
1346
|
+
}
|
|
1347
|
+
] : [];
|
|
1348
|
+
});
|
|
1349
|
+
};
|
|
1350
|
+
|
|
1351
|
+
// src/reason/output-stream.ts
|
|
1352
|
+
function createStructuredOutputStreamer(args, id, opId) {
|
|
1353
|
+
let firstText = "";
|
|
1354
|
+
const setFieldPaths = resolveSetFieldPaths(args.structured);
|
|
1355
|
+
const appendFieldPaths = resolveAppendFieldPaths(args.structured);
|
|
1356
|
+
const textDeltaFieldPaths = resolveTextDeltaFieldPaths(args.structured);
|
|
1357
|
+
const emittedSetValues = /* @__PURE__ */ new Map();
|
|
1358
|
+
const emittedAppendCounts = /* @__PURE__ */ new Map();
|
|
1359
|
+
const emittedTextValues = /* @__PURE__ */ new Map();
|
|
1360
|
+
return (delta) => {
|
|
1361
|
+
firstText += delta;
|
|
1362
|
+
for (const fieldPath of setFieldPaths) {
|
|
1363
|
+
const values = extractCompletedFieldValues({
|
|
1364
|
+
text: firstText,
|
|
1365
|
+
path: fieldPath
|
|
1366
|
+
});
|
|
1367
|
+
for (const { path, value } of values) {
|
|
1368
|
+
const nextSerialized = JSON.stringify(value);
|
|
1369
|
+
if (emittedSetValues.get(path) === nextSerialized) continue;
|
|
1370
|
+
emittedSetValues.set(path, nextSerialized);
|
|
1371
|
+
emitStructuredData({
|
|
1372
|
+
kind: "set",
|
|
1373
|
+
path,
|
|
1374
|
+
value,
|
|
1375
|
+
dataType: args.structured?.dataType ?? "reason-output",
|
|
1376
|
+
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
1377
|
+
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
1378
|
+
...id ? { id } : {},
|
|
1379
|
+
streamId: opId
|
|
1380
|
+
});
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
for (const fieldPath of appendFieldPaths) {
|
|
1384
|
+
const groups = extractCompletedArrayItemGroups({
|
|
1385
|
+
text: firstText,
|
|
1386
|
+
path: fieldPath
|
|
1387
|
+
});
|
|
1388
|
+
for (const { path, items } of groups) {
|
|
1389
|
+
const emittedAppendCount = emittedAppendCounts.get(path) ?? 0;
|
|
1390
|
+
if (items.length <= emittedAppendCount) continue;
|
|
1391
|
+
const nextItems = items.slice(emittedAppendCount);
|
|
1392
|
+
emittedAppendCounts.set(path, items.length);
|
|
1393
|
+
emitStructuredData({
|
|
1394
|
+
kind: "append",
|
|
1395
|
+
path,
|
|
1396
|
+
items: nextItems,
|
|
1397
|
+
dataType: args.structured?.dataType ?? "reason-output",
|
|
1398
|
+
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
1399
|
+
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
1400
|
+
...id ? { id } : {},
|
|
1401
|
+
streamId: opId
|
|
1402
|
+
});
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
for (const fieldPath of textDeltaFieldPaths) {
|
|
1406
|
+
const values = extractStreamingStringValues({
|
|
1407
|
+
text: firstText,
|
|
1408
|
+
path: fieldPath
|
|
1409
|
+
});
|
|
1410
|
+
for (const { path, value } of values) {
|
|
1411
|
+
const emittedTextValue = emittedTextValues.get(path) ?? "";
|
|
1412
|
+
if (value.length <= emittedTextValue.length || !value.startsWith(emittedTextValue)) {
|
|
1413
|
+
continue;
|
|
1414
|
+
}
|
|
1415
|
+
const nextDelta = value.slice(emittedTextValue.length);
|
|
1416
|
+
emittedTextValues.set(path, value);
|
|
1417
|
+
if (nextDelta.length === 0) continue;
|
|
1418
|
+
emitStructuredData({
|
|
1419
|
+
kind: "text-delta",
|
|
1420
|
+
path,
|
|
1421
|
+
delta: nextDelta,
|
|
1422
|
+
dataType: args.structured?.dataType ?? "reason-output",
|
|
1423
|
+
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
1424
|
+
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
1425
|
+
...id ? { id } : {},
|
|
1426
|
+
streamId: opId
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
};
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
// src/reason/parsing.ts
|
|
1434
|
+
var tryJsonParse = (value) => {
|
|
1435
|
+
try {
|
|
1436
|
+
return { ok: true, value: JSON.parse(value) };
|
|
1437
|
+
} catch (error) {
|
|
1438
|
+
return {
|
|
1439
|
+
ok: false,
|
|
1440
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1441
|
+
};
|
|
1442
|
+
}
|
|
1443
|
+
};
|
|
1444
|
+
var extractJsonCodeBlock = (value) => {
|
|
1445
|
+
const match = value.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
1446
|
+
if (!match || typeof match[1] !== "string") return null;
|
|
1447
|
+
const inner = match[1].trim();
|
|
1448
|
+
return inner.length > 0 ? inner : null;
|
|
1449
|
+
};
|
|
1450
|
+
var tryStringify = (value) => {
|
|
1451
|
+
if (typeof value === "string") return value;
|
|
1452
|
+
try {
|
|
1453
|
+
return JSON.stringify(value, null, 2);
|
|
1454
|
+
} catch {
|
|
1455
|
+
return String(value);
|
|
1456
|
+
}
|
|
1457
|
+
};
|
|
1458
|
+
var isRecord2 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1459
|
+
var resolveOutputCandidate = (text) => {
|
|
1460
|
+
const trimmed = text.trim();
|
|
1461
|
+
if (!trimmed) return text;
|
|
1462
|
+
const direct = tryJsonParse(trimmed);
|
|
1463
|
+
if (direct.ok) return direct.value;
|
|
1464
|
+
const block = extractJsonCodeBlock(trimmed);
|
|
1465
|
+
if (block) {
|
|
1466
|
+
const parsedBlock = tryJsonParse(block);
|
|
1467
|
+
if (parsedBlock.ok) return parsedBlock.value;
|
|
1468
|
+
}
|
|
1469
|
+
return text;
|
|
1470
|
+
};
|
|
1471
|
+
var looksLikeStructuredJsonAttempt = (value) => {
|
|
1472
|
+
const trimmed = value.trim();
|
|
1473
|
+
if (!trimmed) return false;
|
|
1474
|
+
return trimmed.startsWith("{") || trimmed.startsWith("[") || /^```(?:json)?/i.test(trimmed);
|
|
1475
|
+
};
|
|
1476
|
+
var isLikelyTruncatedStructuredOutput = (text) => {
|
|
1477
|
+
const trimmed = text.trim();
|
|
1478
|
+
if (!looksLikeStructuredJsonAttempt(trimmed)) return false;
|
|
1479
|
+
const direct = tryJsonParse(trimmed);
|
|
1480
|
+
if (!direct.ok && direct.error) return true;
|
|
1481
|
+
const block = extractJsonCodeBlock(trimmed);
|
|
1482
|
+
if (!block) return false;
|
|
1483
|
+
const parsedBlock = tryJsonParse(block);
|
|
1484
|
+
return !parsedBlock.ok && Boolean(parsedBlock.error);
|
|
1485
|
+
};
|
|
1486
|
+
var createTruncatedStructuredOutputError = (label) => new Error(
|
|
1487
|
+
`${label} was truncated before producing valid structured output. The model stopped due to output length. Increase maxOutputTokens or simplify the requested output.`
|
|
1488
|
+
);
|
|
1489
|
+
var createInvalidStructuredOutputError = (label) => new Error(
|
|
1490
|
+
`${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.`
|
|
1491
|
+
);
|
|
1492
|
+
var parseReasonOutputWithSchema = (args) => {
|
|
1493
|
+
const candidate = resolveOutputCandidate(args.text);
|
|
1494
|
+
try {
|
|
1495
|
+
return parseWithSchema(args.schema, candidate, args.label);
|
|
1496
|
+
} catch (error) {
|
|
1497
|
+
if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
|
|
1498
|
+
throw createTruncatedStructuredOutputError(args.label);
|
|
1499
|
+
}
|
|
1500
|
+
if (typeof candidate === "string" && candidate.trim().length > 0) {
|
|
1501
|
+
throw createInvalidStructuredOutputError(args.label);
|
|
1406
1502
|
}
|
|
1503
|
+
throw error;
|
|
1407
1504
|
}
|
|
1408
|
-
return {
|
|
1409
|
-
complete: false
|
|
1410
|
-
};
|
|
1411
|
-
};
|
|
1412
|
-
var extractCompletedFieldValues = (args) => {
|
|
1413
|
-
const located = locateValueStarts(args);
|
|
1414
|
-
const values = [];
|
|
1415
|
-
for (const match of located) {
|
|
1416
|
-
const parsed = extractCompletedValueAt({
|
|
1417
|
-
text: args.text,
|
|
1418
|
-
start: match.start
|
|
1419
|
-
});
|
|
1420
|
-
if (!parsed.complete) continue;
|
|
1421
|
-
values.push({
|
|
1422
|
-
path: match.path,
|
|
1423
|
-
value: parsed.value
|
|
1424
|
-
});
|
|
1425
|
-
}
|
|
1426
|
-
return values;
|
|
1427
1505
|
};
|
|
1428
|
-
var
|
|
1429
|
-
const
|
|
1430
|
-
if (!
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
const start = arrayStart + 1;
|
|
1434
|
-
const items = [];
|
|
1435
|
-
let inString = false;
|
|
1436
|
-
let isEscaped = false;
|
|
1437
|
-
let nesting = 0;
|
|
1438
|
-
let itemStart = -1;
|
|
1439
|
-
for (let i = start; i < args.text.length; i += 1) {
|
|
1440
|
-
const ch = args.text[i];
|
|
1441
|
-
if (ch === void 0) break;
|
|
1442
|
-
if (inString) {
|
|
1443
|
-
if (isEscaped) {
|
|
1444
|
-
isEscaped = false;
|
|
1445
|
-
continue;
|
|
1446
|
-
}
|
|
1447
|
-
if (ch === "\\") {
|
|
1448
|
-
isEscaped = true;
|
|
1449
|
-
continue;
|
|
1450
|
-
}
|
|
1451
|
-
if (ch === '"') {
|
|
1452
|
-
inString = false;
|
|
1453
|
-
}
|
|
1454
|
-
continue;
|
|
1455
|
-
}
|
|
1456
|
-
if (ch === '"') {
|
|
1457
|
-
inString = true;
|
|
1458
|
-
if (itemStart < 0) itemStart = i;
|
|
1459
|
-
continue;
|
|
1506
|
+
var parseInterruptFirstPassResult = (args) => {
|
|
1507
|
+
const candidate = resolveOutputCandidate(args.text);
|
|
1508
|
+
if (!isRecord2(candidate)) {
|
|
1509
|
+
if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
|
|
1510
|
+
throw createTruncatedStructuredOutputError("useReason output");
|
|
1460
1511
|
}
|
|
1461
|
-
if (
|
|
1462
|
-
|
|
1463
|
-
if (ch === "]") break;
|
|
1464
|
-
itemStart = i;
|
|
1465
|
-
if (ch === "{" || ch === "[") {
|
|
1466
|
-
nesting = 1;
|
|
1467
|
-
} else {
|
|
1468
|
-
nesting = 0;
|
|
1469
|
-
}
|
|
1470
|
-
continue;
|
|
1512
|
+
if (typeof candidate === "string" && candidate.trim().length > 0) {
|
|
1513
|
+
throw createInvalidStructuredOutputError("useReason output");
|
|
1471
1514
|
}
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1515
|
+
throw new Error(
|
|
1516
|
+
"useReason first pass with interrupt must return a JSON object."
|
|
1517
|
+
);
|
|
1518
|
+
}
|
|
1519
|
+
const mode = args.mode ?? "required";
|
|
1520
|
+
let interruptRequired = true;
|
|
1521
|
+
if (mode === "optional") {
|
|
1522
|
+
if (candidate.decision !== "continue" && candidate.decision !== "interrupt") {
|
|
1523
|
+
throw new Error(
|
|
1524
|
+
'useReason optional interrupt first pass must include decision "continue" or "interrupt".'
|
|
1525
|
+
);
|
|
1475
1526
|
}
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1527
|
+
interruptRequired = candidate.decision === "interrupt";
|
|
1528
|
+
if (!interruptRequired) {
|
|
1529
|
+
const hasUnexpectedRequest = [
|
|
1530
|
+
candidate.interruptRequest,
|
|
1531
|
+
candidate.request,
|
|
1532
|
+
candidate.interrupt
|
|
1533
|
+
].some((value) => value !== null && value !== void 0);
|
|
1534
|
+
if (hasUnexpectedRequest) {
|
|
1535
|
+
throw new Error(
|
|
1536
|
+
'useReason optional interrupt first pass with decision "continue" must not include an interrupt request.'
|
|
1537
|
+
);
|
|
1480
1538
|
}
|
|
1481
1539
|
}
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1540
|
+
}
|
|
1541
|
+
let request;
|
|
1542
|
+
if (interruptRequired) {
|
|
1543
|
+
const requestCandidates = [
|
|
1544
|
+
candidate.interruptRequest,
|
|
1545
|
+
candidate.request,
|
|
1546
|
+
candidate.interrupt,
|
|
1547
|
+
mode === "required" ? candidate : void 0
|
|
1548
|
+
];
|
|
1549
|
+
const requestPayload = requestCandidates.find(
|
|
1550
|
+
(value) => value !== null && value !== void 0
|
|
1551
|
+
);
|
|
1552
|
+
request = parseWithSchema(
|
|
1553
|
+
args.requestSchema,
|
|
1554
|
+
requestPayload,
|
|
1555
|
+
"useReason interrupt.request"
|
|
1556
|
+
);
|
|
1557
|
+
}
|
|
1558
|
+
let output;
|
|
1559
|
+
if (args.outputSchema) {
|
|
1560
|
+
const outputPayload = Object.hasOwn(candidate, "output") ? candidate.output : candidate;
|
|
1561
|
+
try {
|
|
1562
|
+
output = parseWithSchema(
|
|
1563
|
+
args.outputSchema,
|
|
1564
|
+
outputPayload,
|
|
1565
|
+
"useReason output"
|
|
1566
|
+
);
|
|
1567
|
+
} catch (error) {
|
|
1568
|
+
if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
|
|
1569
|
+
throw createTruncatedStructuredOutputError("useReason output");
|
|
1489
1570
|
}
|
|
1490
|
-
|
|
1491
|
-
|
|
1571
|
+
if (typeof outputPayload === "string" && outputPayload.trim().length > 0) {
|
|
1572
|
+
throw createInvalidStructuredOutputError("useReason output");
|
|
1573
|
+
}
|
|
1574
|
+
throw error;
|
|
1492
1575
|
}
|
|
1493
1576
|
}
|
|
1494
|
-
|
|
1577
|
+
const explicitDraft = typeof candidate.draftText === "string" && candidate.draftText.length > 0 ? candidate.draftText : typeof candidate.text === "string" && candidate.text.length > 0 ? candidate.text : void 0;
|
|
1578
|
+
if (!args.outputSchema && !explicitDraft) {
|
|
1579
|
+
throw new Error(
|
|
1580
|
+
"useReason first pass with interrupt requires `draftText` when outputSchema is not provided."
|
|
1581
|
+
);
|
|
1582
|
+
}
|
|
1583
|
+
const draftText = explicitDraft ?? (output !== void 0 ? tryStringify(output) : String(args.text ?? ""));
|
|
1584
|
+
if (!interruptRequired) {
|
|
1585
|
+
return {
|
|
1586
|
+
draftText,
|
|
1587
|
+
interruptRequired: false,
|
|
1588
|
+
...output !== void 0 ? { output } : {}
|
|
1589
|
+
};
|
|
1590
|
+
}
|
|
1591
|
+
return {
|
|
1592
|
+
draftText,
|
|
1593
|
+
interruptRequired: true,
|
|
1594
|
+
request,
|
|
1595
|
+
...output !== void 0 ? { output } : {}
|
|
1596
|
+
};
|
|
1597
|
+
};
|
|
1598
|
+
|
|
1599
|
+
// src/reason/prompting.ts
|
|
1600
|
+
var import_zod3 = require("zod");
|
|
1601
|
+
var tryStringify2 = (value) => {
|
|
1602
|
+
if (typeof value === "string") return value;
|
|
1603
|
+
try {
|
|
1604
|
+
return JSON.stringify(value, null, 2);
|
|
1605
|
+
} catch {
|
|
1606
|
+
return String(value);
|
|
1607
|
+
}
|
|
1608
|
+
};
|
|
1609
|
+
var tryGetSchemaHint = (schema) => {
|
|
1610
|
+
try {
|
|
1611
|
+
const hint = (0, import_zod3.toJSONSchema)(schema);
|
|
1612
|
+
return JSON.stringify(hint, null, 2);
|
|
1613
|
+
} catch {
|
|
1614
|
+
return void 0;
|
|
1615
|
+
}
|
|
1616
|
+
};
|
|
1617
|
+
var withSchemaHint = (input, label, schema) => {
|
|
1618
|
+
const hint = tryGetSchemaHint(schema);
|
|
1619
|
+
if (!hint) return input;
|
|
1620
|
+
return `${input}
|
|
1621
|
+
|
|
1622
|
+
${label}:
|
|
1623
|
+
${hint}`;
|
|
1624
|
+
};
|
|
1625
|
+
var defaultInterruptFirstPassInput = (args) => {
|
|
1626
|
+
const mode = args.mode ?? "required";
|
|
1627
|
+
const base = mode === "optional" ? args.outputSchema ? [
|
|
1628
|
+
args.input,
|
|
1629
|
+
"Output rules:",
|
|
1630
|
+
"- Return JSON only. No markdown fences.",
|
|
1631
|
+
"- Return an object with keys: decision, output, interruptRequest, draftText.",
|
|
1632
|
+
'- decision must be either "continue" or "interrupt".',
|
|
1633
|
+
'- Use decision "continue" when you can produce the final output without asking the user.',
|
|
1634
|
+
'- Use decision "interrupt" only when user input is needed before finalizing.',
|
|
1635
|
+
"- output must match the expected output JSON schema.",
|
|
1636
|
+
'- When decision is "interrupt", interruptRequest must match the expected interrupt request JSON schema and must be a non-null object.',
|
|
1637
|
+
'- When decision is "continue", interruptRequest must be null.',
|
|
1638
|
+
"- draftText should be a concise plain-language summary of output."
|
|
1639
|
+
].join("\n\n") : [
|
|
1640
|
+
args.input,
|
|
1641
|
+
"Output rules:",
|
|
1642
|
+
"- Return JSON only. No markdown fences.",
|
|
1643
|
+
"- Return an object with keys: decision, draftText, interruptRequest.",
|
|
1644
|
+
'- decision must be either "continue" or "interrupt".',
|
|
1645
|
+
'- Use decision "continue" when you can produce the final answer without asking the user.',
|
|
1646
|
+
'- Use decision "interrupt" only when user input is needed before finalizing.',
|
|
1647
|
+
"- draftText should be the current answer text.",
|
|
1648
|
+
'- When decision is "interrupt", interruptRequest must match the expected interrupt request JSON schema and must be a non-null object.',
|
|
1649
|
+
'- When decision is "continue", interruptRequest must be null.'
|
|
1650
|
+
].join("\n\n") : args.outputSchema ? [
|
|
1651
|
+
args.input,
|
|
1652
|
+
"Output rules:",
|
|
1653
|
+
"- Return JSON only. No markdown fences.",
|
|
1654
|
+
"- Return an object with keys: output, interruptRequest, draftText.",
|
|
1655
|
+
"- output must match the expected output JSON schema.",
|
|
1656
|
+
"- interruptRequest must match the expected interrupt request JSON schema.",
|
|
1657
|
+
"- interruptRequest must be a non-null object.",
|
|
1658
|
+
"- draftText should be a concise plain-language summary of output.",
|
|
1659
|
+
"- If uncertain, still return a valid interruptRequest with safe default options."
|
|
1660
|
+
].join("\n\n") : [
|
|
1661
|
+
args.input,
|
|
1662
|
+
"Output rules:",
|
|
1663
|
+
"- Return JSON only. No markdown fences.",
|
|
1664
|
+
"- Return an object with keys: draftText, interruptRequest.",
|
|
1665
|
+
"- draftText should be the current draft answer text.",
|
|
1666
|
+
"- interruptRequest must match the expected interrupt request JSON schema.",
|
|
1667
|
+
"- interruptRequest must be a non-null object.",
|
|
1668
|
+
"- If uncertain, still return a valid interruptRequest with safe default options."
|
|
1669
|
+
].join("\n\n");
|
|
1670
|
+
const withInterruptSchema = withSchemaHint(
|
|
1671
|
+
base,
|
|
1672
|
+
"Expected interrupt request JSON schema (for `interruptRequest`)",
|
|
1673
|
+
args.requestSchema
|
|
1674
|
+
);
|
|
1675
|
+
if (!args.outputSchema) return withInterruptSchema;
|
|
1676
|
+
const withOutputSchema = withSchemaHint(
|
|
1677
|
+
withInterruptSchema,
|
|
1678
|
+
"Expected output JSON schema (for `output`)",
|
|
1679
|
+
args.outputSchema
|
|
1680
|
+
);
|
|
1681
|
+
return [
|
|
1682
|
+
withOutputSchema,
|
|
1683
|
+
"JSON template:",
|
|
1684
|
+
mode === "optional" ? '{ "decision": "continue", "output": { ... }, "interruptRequest": null, "draftText": "..." }' : '{ "output": { ... }, "interruptRequest": { ... }, "draftText": "..." }'
|
|
1685
|
+
].join("\n\n");
|
|
1495
1686
|
};
|
|
1496
|
-
var
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1687
|
+
var defaultContinuationInput = (args) => [
|
|
1688
|
+
"You are continuing a paused reasoning task after user input.",
|
|
1689
|
+
"Update the previous result using the user response and return the final answer only.",
|
|
1690
|
+
"If the previous output was JSON, return JSON in the same schema.",
|
|
1691
|
+
"",
|
|
1692
|
+
`Original request:
|
|
1693
|
+
${args.input}`,
|
|
1694
|
+
`Draft answer text:
|
|
1695
|
+
${args.draftText}`,
|
|
1696
|
+
`Draft output object:
|
|
1697
|
+
${tryStringify2(args.draftOutput ?? args.draftText)}`,
|
|
1698
|
+
`Interrupt request shown to user:
|
|
1699
|
+
${tryStringify2(args.request)}`,
|
|
1700
|
+
`User response:
|
|
1701
|
+
${tryStringify2(args.response)}`
|
|
1702
|
+
].join("\n\n");
|
|
1703
|
+
var withOutputGuardrails = (input, schema) => {
|
|
1704
|
+
const base = [
|
|
1705
|
+
input,
|
|
1706
|
+
"Output rules:",
|
|
1707
|
+
"- Return only JSON, no markdown fences.",
|
|
1708
|
+
"- Ensure the response conforms to the expected output schema.",
|
|
1709
|
+
"- Do not include explanatory text outside the JSON object."
|
|
1710
|
+
].join("\n\n");
|
|
1711
|
+
return withSchemaHint(base, "Expected output JSON schema", schema);
|
|
1509
1712
|
};
|
|
1510
|
-
var
|
|
1511
|
-
const
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
value: ""
|
|
1517
|
-
};
|
|
1713
|
+
var withStructuredStreamHints = (input, args) => {
|
|
1714
|
+
const rules = [];
|
|
1715
|
+
for (const path of args.setFieldPaths ?? []) {
|
|
1716
|
+
rules.push(
|
|
1717
|
+
`- For the field path pattern \`${path}\`, write one stable final value and do not rewrite it after it is complete.`
|
|
1718
|
+
);
|
|
1518
1719
|
}
|
|
1519
|
-
const
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1720
|
+
for (const path of args.appendFieldPaths ?? []) {
|
|
1721
|
+
rules.push(
|
|
1722
|
+
`- For the array field path pattern \`${path}\`, write items in final order and finish each item before starting the next.`
|
|
1723
|
+
);
|
|
1724
|
+
rules.push(
|
|
1725
|
+
`- Once an item in \`${path}\` is complete, do not rewrite or reorder earlier items.`
|
|
1726
|
+
);
|
|
1526
1727
|
}
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1728
|
+
for (const path of args.textDeltaFieldPaths ?? []) {
|
|
1729
|
+
rules.push(
|
|
1730
|
+
`- For the string field path pattern \`${path}\`, write the value progressively by appending text to the end.`
|
|
1731
|
+
);
|
|
1732
|
+
rules.push(
|
|
1733
|
+
`- Do not rewrite or replace the earlier prefix of \`${path}\` once emitted.`
|
|
1734
|
+
);
|
|
1735
|
+
}
|
|
1736
|
+
if (rules.length === 0) return input;
|
|
1737
|
+
return [
|
|
1738
|
+
input,
|
|
1739
|
+
"Streaming rules:",
|
|
1740
|
+
...rules,
|
|
1741
|
+
"- Keep the overall response as valid JSON."
|
|
1742
|
+
].join("\n\n");
|
|
1743
|
+
};
|
|
1744
|
+
|
|
1745
|
+
// src/reason/result.ts
|
|
1746
|
+
var mergeUsage = (left, right) => {
|
|
1747
|
+
if (!left) return right;
|
|
1748
|
+
if (!right) return left;
|
|
1749
|
+
const sum = (a, b) => a === void 0 && b === void 0 ? void 0 : (a ?? 0) + (b ?? 0);
|
|
1750
|
+
const raw = left.raw || right.raw ? {
|
|
1751
|
+
...left.raw ?? {},
|
|
1752
|
+
...right.raw ?? {}
|
|
1753
|
+
} : void 0;
|
|
1754
|
+
const input = sum(left.input, right.input);
|
|
1755
|
+
const output = sum(left.output, right.output);
|
|
1756
|
+
const total = sum(left.total, right.total);
|
|
1757
|
+
const reasoning = sum(left.reasoning, right.reasoning);
|
|
1758
|
+
const cacheRead = sum(left.cacheRead, right.cacheRead);
|
|
1759
|
+
const cacheWrite = sum(left.cacheWrite, right.cacheWrite);
|
|
1760
|
+
const merged = {
|
|
1761
|
+
...right.outputIncludesReasoning !== void 0 ? { outputIncludesReasoning: right.outputIncludesReasoning } : {},
|
|
1762
|
+
...right.inputIncludesCacheWrite !== void 0 ? { inputIncludesCacheWrite: right.inputIncludesCacheWrite } : {},
|
|
1763
|
+
...right.inputIncludesCacheRead !== void 0 ? { inputIncludesCacheRead: right.inputIncludesCacheRead } : {}
|
|
1764
|
+
};
|
|
1765
|
+
if (input !== void 0) merged.input = input;
|
|
1766
|
+
if (output !== void 0) merged.output = output;
|
|
1767
|
+
if (total !== void 0) merged.total = total;
|
|
1768
|
+
if (reasoning !== void 0) merged.reasoning = reasoning;
|
|
1769
|
+
if (cacheRead !== void 0) merged.cacheRead = cacheRead;
|
|
1770
|
+
if (cacheWrite !== void 0) merged.cacheWrite = cacheWrite;
|
|
1771
|
+
if (raw) merged.raw = raw;
|
|
1772
|
+
return merged;
|
|
1773
|
+
};
|
|
1774
|
+
var mergeWarnings = (left, right) => {
|
|
1775
|
+
if (!left?.length) return right;
|
|
1776
|
+
if (!right?.length) return left;
|
|
1777
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1778
|
+
const merged = [];
|
|
1779
|
+
for (const warning of [...left, ...right]) {
|
|
1780
|
+
const key = JSON.stringify(warning);
|
|
1781
|
+
if (seen.has(key)) continue;
|
|
1782
|
+
seen.add(key);
|
|
1783
|
+
merged.push(warning);
|
|
1573
1784
|
}
|
|
1785
|
+
return merged;
|
|
1786
|
+
};
|
|
1787
|
+
var mergeProviderMetadata = (left, right) => {
|
|
1788
|
+
if (!left) return right;
|
|
1789
|
+
if (!right) return left;
|
|
1574
1790
|
return {
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
value
|
|
1791
|
+
...left,
|
|
1792
|
+
...right
|
|
1578
1793
|
};
|
|
1579
1794
|
};
|
|
1580
|
-
var
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
value: current.value
|
|
1591
|
-
}
|
|
1592
|
-
] : [];
|
|
1795
|
+
var emitReasonStructuredOutput = (args) => {
|
|
1796
|
+
if (!args.emit || !shouldEmitStructured(args.structured)) return;
|
|
1797
|
+
emitStructuredData({
|
|
1798
|
+
dataType: args.structured?.dataType ?? "reason-output",
|
|
1799
|
+
data: args.output,
|
|
1800
|
+
kind: "final",
|
|
1801
|
+
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
1802
|
+
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
1803
|
+
...args.id ? { id: args.id } : {},
|
|
1804
|
+
streamId: args.opId
|
|
1593
1805
|
});
|
|
1594
1806
|
};
|
|
1595
1807
|
|
|
@@ -1646,13 +1858,16 @@ var createInitialMessages = (args) => [
|
|
|
1646
1858
|
{ role: "user", content: String(args.input ?? "") }
|
|
1647
1859
|
];
|
|
1648
1860
|
var runReasonToolLoop = async (args) => {
|
|
1649
|
-
const { useReasonArgs, id,
|
|
1861
|
+
const { useReasonArgs, id, traceSpan, checkpointKey } = args;
|
|
1650
1862
|
const ctx = getHookContext();
|
|
1651
1863
|
const abortSignal = (0, import_core4.combineAbortSignals)(
|
|
1652
1864
|
ctx.node.abortSignal,
|
|
1653
1865
|
useReasonArgs.abortSignal,
|
|
1654
1866
|
useReasonArgs.model.options?.abortSignal
|
|
1655
1867
|
);
|
|
1868
|
+
const saved = ctx.currentNodeState.byKey[checkpointKey];
|
|
1869
|
+
const checkpoint = saved?.status === "tool_loop" ? saved : void 0;
|
|
1870
|
+
const opId = checkpoint?.opId ?? args.opId;
|
|
1656
1871
|
const tools = useReasonArgs.tools ?? [];
|
|
1657
1872
|
const toolByName = /* @__PURE__ */ new Map();
|
|
1658
1873
|
let validationCompleted = false;
|
|
@@ -1678,20 +1893,43 @@ var runReasonToolLoop = async (args) => {
|
|
|
1678
1893
|
}
|
|
1679
1894
|
const toolDefinitions = toToolDefinitions(tools);
|
|
1680
1895
|
const maxSteps = Math.max(1, useReasonArgs.toolExecution?.maxSteps ?? 3);
|
|
1681
|
-
const messages = createInitialMessages({
|
|
1896
|
+
const messages = checkpoint?.messages ?? createInitialMessages({
|
|
1682
1897
|
system: useReasonArgs.system,
|
|
1683
|
-
input: useReasonArgs.input
|
|
1898
|
+
input: useReasonArgs.outputSchema ? withOutputGuardrails(useReasonArgs.input, useReasonArgs.outputSchema) : useReasonArgs.input
|
|
1684
1899
|
});
|
|
1685
|
-
const steps = [];
|
|
1686
|
-
const allToolCalls =
|
|
1687
|
-
|
|
1900
|
+
const steps = checkpoint?.steps ?? [];
|
|
1901
|
+
const allToolCalls = steps.flatMap(
|
|
1902
|
+
(step) => step.toolCalls
|
|
1903
|
+
);
|
|
1904
|
+
const allToolResults = steps.flatMap(
|
|
1905
|
+
(step) => step.toolResults
|
|
1906
|
+
);
|
|
1688
1907
|
let finalText = "";
|
|
1689
1908
|
let finalRaw;
|
|
1690
1909
|
let finalOutput;
|
|
1691
|
-
let aggregatedUsage
|
|
1910
|
+
let aggregatedUsage = steps.reduce(
|
|
1911
|
+
(usage, step) => mergeUsage(usage, step.usage),
|
|
1912
|
+
void 0
|
|
1913
|
+
);
|
|
1692
1914
|
let finalFinishReason;
|
|
1693
1915
|
let aggregatedProviderMetadata;
|
|
1694
|
-
let aggregatedWarnings
|
|
1916
|
+
let aggregatedWarnings = steps.reduce(
|
|
1917
|
+
(warnings, step) => mergeWarnings(warnings, step.warnings),
|
|
1918
|
+
args.initialWarnings
|
|
1919
|
+
);
|
|
1920
|
+
let pending = checkpoint?.pending;
|
|
1921
|
+
const approvedCalls = checkpoint?.approvedCalls ?? [];
|
|
1922
|
+
const save = () => {
|
|
1923
|
+
ctx.currentNodeState.byKey[checkpointKey] = {
|
|
1924
|
+
status: "tool_loop",
|
|
1925
|
+
opId,
|
|
1926
|
+
messages,
|
|
1927
|
+
steps,
|
|
1928
|
+
approvedCalls,
|
|
1929
|
+
...pending ? { pending } : {}
|
|
1930
|
+
};
|
|
1931
|
+
ctx.stateDirty = true;
|
|
1932
|
+
};
|
|
1695
1933
|
const emitToolEvent = (event, payload) => {
|
|
1696
1934
|
ctx.node.emit(event, {
|
|
1697
1935
|
...payload,
|
|
@@ -1701,26 +1939,43 @@ var runReasonToolLoop = async (args) => {
|
|
|
1701
1939
|
});
|
|
1702
1940
|
};
|
|
1703
1941
|
try {
|
|
1704
|
-
for (let stepIndex =
|
|
1942
|
+
for (let stepIndex = pending ? steps.length - 1 : steps.length; stepIndex < maxSteps; stepIndex += 1) {
|
|
1705
1943
|
(0, import_core4.throwIfExecutionAborted)(abortSignal);
|
|
1706
1944
|
traceSpan?.addEvent?.("useReason.tool-step.start", {
|
|
1707
1945
|
stepIndex,
|
|
1708
1946
|
toolCount: toolDefinitions.length
|
|
1709
1947
|
});
|
|
1710
|
-
const
|
|
1948
|
+
const reused = Boolean(pending);
|
|
1949
|
+
const stream = useReasonArgs.stream ?? useReasonArgs.model.options?.streaming ?? true;
|
|
1950
|
+
const emit = useReasonArgs.emit ?? true;
|
|
1951
|
+
let textStarted = false;
|
|
1952
|
+
const structuredChunk = useReasonArgs.outputSchema && stream && emit && shouldStreamStructured(useReasonArgs.structured) ? createStructuredOutputStreamer(useReasonArgs, id, opId) : void 0;
|
|
1953
|
+
const step = pending ?? await reasonEngine(
|
|
1711
1954
|
{
|
|
1712
1955
|
...useReasonArgs,
|
|
1713
1956
|
tools: toolDefinitions,
|
|
1714
1957
|
messages,
|
|
1715
1958
|
emit: false,
|
|
1716
|
-
stream
|
|
1959
|
+
stream,
|
|
1960
|
+
onTextChunk: (delta) => {
|
|
1961
|
+
if (structuredChunk) structuredChunk(delta);
|
|
1962
|
+
if (!emit || useReasonArgs.outputSchema || !delta) return;
|
|
1963
|
+
if (!textStarted) {
|
|
1964
|
+
emitToolEvent("text-start", {});
|
|
1965
|
+
textStarted = true;
|
|
1966
|
+
}
|
|
1967
|
+
emitToolEvent("text-delta", { delta });
|
|
1968
|
+
}
|
|
1717
1969
|
},
|
|
1718
1970
|
{ ...id ? { id } : {}, opId }
|
|
1719
1971
|
);
|
|
1972
|
+
if (textStarted) emitToolEvent("text-end", {});
|
|
1720
1973
|
finalText = step.text;
|
|
1721
1974
|
finalRaw = step.raw;
|
|
1722
|
-
|
|
1723
|
-
|
|
1975
|
+
if (!reused) {
|
|
1976
|
+
aggregatedUsage = mergeUsage(aggregatedUsage, step.usage);
|
|
1977
|
+
accumulateTokenUsage(step.usage);
|
|
1978
|
+
}
|
|
1724
1979
|
finalFinishReason = step.finishReason;
|
|
1725
1980
|
aggregatedProviderMetadata = mergeProviderMetadata(
|
|
1726
1981
|
aggregatedProviderMetadata,
|
|
@@ -1728,32 +1983,35 @@ var runReasonToolLoop = async (args) => {
|
|
|
1728
1983
|
);
|
|
1729
1984
|
aggregatedWarnings = mergeWarnings(aggregatedWarnings, step.warnings);
|
|
1730
1985
|
const toolCalls = step.toolCalls ?? [];
|
|
1731
|
-
const toolResults = [];
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1986
|
+
const toolResults = reused ? steps[stepIndex]?.toolResults ?? [] : [];
|
|
1987
|
+
if (!reused)
|
|
1988
|
+
steps.push({
|
|
1989
|
+
stepIndex,
|
|
1990
|
+
text: step.text,
|
|
1991
|
+
toolCalls,
|
|
1992
|
+
toolResults,
|
|
1993
|
+
...step.usage ? { usage: step.usage } : {},
|
|
1994
|
+
...step.finishReason ? { finishReason: step.finishReason } : {},
|
|
1995
|
+
...step.providerMetadata ? { providerMetadata: step.providerMetadata } : {},
|
|
1996
|
+
...step.warnings ? { warnings: step.warnings } : {}
|
|
1997
|
+
});
|
|
1742
1998
|
if (toolCalls.length === 0) {
|
|
1743
|
-
if ((useReasonArgs.emit ?? true) && step.text.length > 0) {
|
|
1744
|
-
emitToolEvent("text-start", {});
|
|
1745
|
-
emitToolEvent("text-delta", { delta: step.text });
|
|
1746
|
-
emitToolEvent("text-end", {});
|
|
1747
|
-
}
|
|
1748
1999
|
break;
|
|
1749
2000
|
}
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
2001
|
+
if (!reused) {
|
|
2002
|
+
allToolCalls.push(...toolCalls);
|
|
2003
|
+
messages.push({
|
|
2004
|
+
role: "assistant",
|
|
2005
|
+
content: step.text,
|
|
2006
|
+
toolCalls,
|
|
2007
|
+
...step.continuation ? { continuation: step.continuation } : {}
|
|
2008
|
+
});
|
|
2009
|
+
}
|
|
2010
|
+
pending = step;
|
|
2011
|
+
save();
|
|
1756
2012
|
for (const toolCall of toolCalls) {
|
|
2013
|
+
if (toolResults.some((result2) => result2.toolCallId === toolCall.id))
|
|
2014
|
+
continue;
|
|
1757
2015
|
(0, import_core4.throwIfExecutionAborted)(abortSignal);
|
|
1758
2016
|
const tool = toolByName.get(toolCall.name);
|
|
1759
2017
|
if (!tool) {
|
|
@@ -1783,7 +2041,7 @@ var runReasonToolLoop = async (args) => {
|
|
|
1783
2041
|
input: toolCall.input
|
|
1784
2042
|
});
|
|
1785
2043
|
}
|
|
1786
|
-
if (shouldApproveTool) {
|
|
2044
|
+
if (shouldApproveTool && !approvedCalls.includes(toolCall.id)) {
|
|
1787
2045
|
const approval = await awaitInterruptInternal({
|
|
1788
2046
|
id: `tool:${toolCall.id}`,
|
|
1789
2047
|
request: {
|
|
@@ -1818,8 +2076,18 @@ var runReasonToolLoop = async (args) => {
|
|
|
1818
2076
|
isError: true
|
|
1819
2077
|
});
|
|
1820
2078
|
}
|
|
2079
|
+
messages.push({
|
|
2080
|
+
role: "tool",
|
|
2081
|
+
content: denied.content,
|
|
2082
|
+
toolCallId: denied.toolCallId,
|
|
2083
|
+
name: denied.name,
|
|
2084
|
+
isError: true
|
|
2085
|
+
});
|
|
2086
|
+
save();
|
|
1821
2087
|
continue;
|
|
1822
2088
|
}
|
|
2089
|
+
approvedCalls.push(toolCall.id);
|
|
2090
|
+
save();
|
|
1823
2091
|
}
|
|
1824
2092
|
try {
|
|
1825
2093
|
(0, import_core4.throwIfExecutionAborted)(abortSignal);
|
|
@@ -1873,25 +2141,19 @@ var runReasonToolLoop = async (args) => {
|
|
|
1873
2141
|
message: result2.content
|
|
1874
2142
|
});
|
|
1875
2143
|
}
|
|
2144
|
+
const completed = toolResults.at(-1);
|
|
2145
|
+
if (completed)
|
|
2146
|
+
messages.push({
|
|
2147
|
+
role: "tool",
|
|
2148
|
+
content: completed.content,
|
|
2149
|
+
toolCallId: completed.toolCallId,
|
|
2150
|
+
name: completed.name,
|
|
2151
|
+
...completed.isError !== void 0 ? { isError: completed.isError } : {}
|
|
2152
|
+
});
|
|
2153
|
+
save();
|
|
1876
2154
|
}
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
steps[steps.length - 1] = {
|
|
1880
|
-
...currentStep,
|
|
1881
|
-
toolResults
|
|
1882
|
-
};
|
|
1883
|
-
}
|
|
1884
|
-
for (const result2 of toolResults) {
|
|
1885
|
-
messages.push({
|
|
1886
|
-
role: "tool",
|
|
1887
|
-
content: result2.content,
|
|
1888
|
-
toolCallId: result2.toolCallId,
|
|
1889
|
-
name: result2.name,
|
|
1890
|
-
...result2.structuredContent !== void 0 ? { structuredContent: result2.structuredContent } : {},
|
|
1891
|
-
...result2.isError !== void 0 ? { isError: result2.isError } : {},
|
|
1892
|
-
...result2.raw !== void 0 ? { raw: result2.raw } : {}
|
|
1893
|
-
});
|
|
1894
|
-
}
|
|
2155
|
+
pending = void 0;
|
|
2156
|
+
save();
|
|
1895
2157
|
}
|
|
1896
2158
|
if (steps.length >= maxSteps && steps.at(-1)?.toolCalls.length) {
|
|
1897
2159
|
throw new Error(
|
|
@@ -1945,6 +2207,8 @@ var runReasonToolLoop = async (args) => {
|
|
|
1945
2207
|
output: useReasonArgs.telemetry?.output ?? finalOutput ?? finalText
|
|
1946
2208
|
}
|
|
1947
2209
|
});
|
|
2210
|
+
ctx.currentNodeState.byKey[checkpointKey] = { status: "completed", result };
|
|
2211
|
+
ctx.stateDirty = true;
|
|
1948
2212
|
return result;
|
|
1949
2213
|
} catch (error) {
|
|
1950
2214
|
traceSpan?.fail?.(error, {
|
|
@@ -1980,7 +2244,15 @@ async function useReason(args) {
|
|
|
1980
2244
|
args.model.options?.abortSignal
|
|
1981
2245
|
);
|
|
1982
2246
|
(0, import_core5.throwIfExecutionAborted)(abortSignal);
|
|
1983
|
-
|
|
2247
|
+
const inferred = inferOutputFormat(
|
|
2248
|
+
args.outputSchema,
|
|
2249
|
+
args.responseFormat ?? args.model.options?.responseFormat
|
|
2250
|
+
);
|
|
2251
|
+
args = {
|
|
2252
|
+
...args,
|
|
2253
|
+
abortSignal,
|
|
2254
|
+
...!args.interrupt && inferred.responseFormat ? { responseFormat: inferred.responseFormat } : {}
|
|
2255
|
+
};
|
|
1984
2256
|
const id = typeof args.id === "string" && args.id.length > 0 ? args.id : void 0;
|
|
1985
2257
|
const opId = createRuntimeId2();
|
|
1986
2258
|
const checkpointKey = resolveReasonCheckpointKey({
|
|
@@ -2024,6 +2296,8 @@ async function useReason(args) {
|
|
|
2024
2296
|
if (args.tools?.length) {
|
|
2025
2297
|
return runReasonToolLoop({
|
|
2026
2298
|
useReasonArgs: args,
|
|
2299
|
+
checkpointKey,
|
|
2300
|
+
initialWarnings: inferred.warnings,
|
|
2027
2301
|
...id ? { id } : {},
|
|
2028
2302
|
opId,
|
|
2029
2303
|
...traceSpan ? { traceSpan } : {}
|
|
@@ -2047,10 +2321,7 @@ async function useReason(args) {
|
|
|
2047
2321
|
let aggregatedUsage;
|
|
2048
2322
|
let finalFinishReason;
|
|
2049
2323
|
let aggregatedProviderMetadata;
|
|
2050
|
-
let aggregatedWarnings;
|
|
2051
|
-
const emittedSetValues = /* @__PURE__ */ new Map();
|
|
2052
|
-
const emittedAppendCounts = /* @__PURE__ */ new Map();
|
|
2053
|
-
const emittedTextValues = /* @__PURE__ */ new Map();
|
|
2324
|
+
let aggregatedWarnings = inferred.warnings;
|
|
2054
2325
|
if (existingCheckpoint) {
|
|
2055
2326
|
firstText = existingCheckpoint.firstText;
|
|
2056
2327
|
firstRaw = existingCheckpoint.firstRaw;
|
|
@@ -2072,78 +2343,7 @@ async function useReason(args) {
|
|
|
2072
2343
|
emit: suppressTextStream ? false : args.emit,
|
|
2073
2344
|
stream: useStructuredIncrementalStreaming ? true : suppressTextStream ? false : args.stream,
|
|
2074
2345
|
...useStructuredIncrementalStreaming ? {
|
|
2075
|
-
onTextChunk: (
|
|
2076
|
-
firstText += delta;
|
|
2077
|
-
finalText = firstText;
|
|
2078
|
-
for (const fieldPath of setFieldPaths) {
|
|
2079
|
-
const values = extractCompletedFieldValues({
|
|
2080
|
-
text: firstText,
|
|
2081
|
-
path: fieldPath
|
|
2082
|
-
});
|
|
2083
|
-
for (const { path, value } of values) {
|
|
2084
|
-
const nextSerialized = JSON.stringify(value);
|
|
2085
|
-
if (emittedSetValues.get(path) === nextSerialized) continue;
|
|
2086
|
-
emittedSetValues.set(path, nextSerialized);
|
|
2087
|
-
emitStructuredData({
|
|
2088
|
-
kind: "set",
|
|
2089
|
-
path,
|
|
2090
|
-
value,
|
|
2091
|
-
dataType: args.structured?.dataType ?? "reason-output",
|
|
2092
|
-
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
2093
|
-
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
2094
|
-
...id ? { id } : {},
|
|
2095
|
-
streamId: opId
|
|
2096
|
-
});
|
|
2097
|
-
}
|
|
2098
|
-
}
|
|
2099
|
-
for (const fieldPath of appendFieldPaths) {
|
|
2100
|
-
const groups = extractCompletedArrayItemGroups({
|
|
2101
|
-
text: firstText,
|
|
2102
|
-
path: fieldPath
|
|
2103
|
-
});
|
|
2104
|
-
for (const { path, items } of groups) {
|
|
2105
|
-
const emittedAppendCount = emittedAppendCounts.get(path) ?? 0;
|
|
2106
|
-
if (items.length <= emittedAppendCount) continue;
|
|
2107
|
-
const nextItems = items.slice(emittedAppendCount);
|
|
2108
|
-
emittedAppendCounts.set(path, items.length);
|
|
2109
|
-
emitStructuredData({
|
|
2110
|
-
kind: "append",
|
|
2111
|
-
path,
|
|
2112
|
-
items: nextItems,
|
|
2113
|
-
dataType: args.structured?.dataType ?? "reason-output",
|
|
2114
|
-
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
2115
|
-
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
2116
|
-
...id ? { id } : {},
|
|
2117
|
-
streamId: opId
|
|
2118
|
-
});
|
|
2119
|
-
}
|
|
2120
|
-
}
|
|
2121
|
-
for (const fieldPath of textDeltaFieldPaths) {
|
|
2122
|
-
const values = extractStreamingStringValues({
|
|
2123
|
-
text: firstText,
|
|
2124
|
-
path: fieldPath
|
|
2125
|
-
});
|
|
2126
|
-
for (const { path, value } of values) {
|
|
2127
|
-
const emittedTextValue = emittedTextValues.get(path) ?? "";
|
|
2128
|
-
if (value.length <= emittedTextValue.length || !value.startsWith(emittedTextValue)) {
|
|
2129
|
-
continue;
|
|
2130
|
-
}
|
|
2131
|
-
const nextDelta = value.slice(emittedTextValue.length);
|
|
2132
|
-
emittedTextValues.set(path, value);
|
|
2133
|
-
if (nextDelta.length === 0) continue;
|
|
2134
|
-
emitStructuredData({
|
|
2135
|
-
kind: "text-delta",
|
|
2136
|
-
path,
|
|
2137
|
-
delta: nextDelta,
|
|
2138
|
-
dataType: args.structured?.dataType ?? "reason-output",
|
|
2139
|
-
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
2140
|
-
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
2141
|
-
...id ? { id } : {},
|
|
2142
|
-
streamId: opId
|
|
2143
|
-
});
|
|
2144
|
-
}
|
|
2145
|
-
}
|
|
2146
|
-
}
|
|
2346
|
+
onTextChunk: createStructuredOutputStreamer(args, id, opId)
|
|
2147
2347
|
} : {}
|
|
2148
2348
|
},
|
|
2149
2349
|
reasonMeta,
|
|
@@ -2263,6 +2463,7 @@ async function useReason(args) {
|
|
|
2263
2463
|
const second = await reasonEngine(
|
|
2264
2464
|
{
|
|
2265
2465
|
...args,
|
|
2466
|
+
...inferred.responseFormat ? { responseFormat: inferred.responseFormat } : {},
|
|
2266
2467
|
emit: suppressTextStream ? false : args.emit,
|
|
2267
2468
|
stream: suppressTextStream ? false : args.stream
|
|
2268
2469
|
},
|
|
@@ -2647,6 +2848,7 @@ async function useWorkflow(args) {
|
|
|
2647
2848
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2648
2849
|
0 && (module.exports = {
|
|
2649
2850
|
WorkflowCallError,
|
|
2851
|
+
completeResponse,
|
|
2650
2852
|
createWorkflowHooks,
|
|
2651
2853
|
emitWorkflowCall,
|
|
2652
2854
|
runWithHookContext,
|