@kortyx/hooks 0.22.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 +18 -0
- package/dist/index.js +848 -675
- 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 +2 -2
package/dist/index.js
CHANGED
|
@@ -129,7 +129,7 @@ var toTokenUsageDelta = (usage) => {
|
|
|
129
129
|
if (!usage) return void 0;
|
|
130
130
|
const input = usage.input ?? 0;
|
|
131
131
|
const output = usage.output ?? 0;
|
|
132
|
-
const total = usage.total ?? input + output + (usage.reasoning ?? 0);
|
|
132
|
+
const total = usage.total ?? input + output + (usage.outputIncludesReasoning ? 0 : usage.reasoning ?? 0);
|
|
133
133
|
if (input === 0 && output === 0 && total === 0) {
|
|
134
134
|
return void 0;
|
|
135
135
|
}
|
|
@@ -427,7 +427,7 @@ var emitNodeEvent = (emitEvent, nodeId, event, payload) => {
|
|
|
427
427
|
});
|
|
428
428
|
};
|
|
429
429
|
async function runReasonEngine(args) {
|
|
430
|
-
|
|
430
|
+
let stream = args.stream ?? args.model.options?.streaming ?? true;
|
|
431
431
|
const emit = args.emit ?? true;
|
|
432
432
|
const temperature = args.temperature ?? args.model.options?.temperature ?? args.defaultTemperature;
|
|
433
433
|
const maxOutputTokens = args.maxOutputTokens ?? args.model.options?.maxOutputTokens;
|
|
@@ -435,7 +435,14 @@ async function runReasonEngine(args) {
|
|
|
435
435
|
const abortSignal = args.abortSignal ?? args.model.options?.abortSignal;
|
|
436
436
|
const reasoning = args.reasoning ?? args.model.options?.reasoning;
|
|
437
437
|
const responseFormat = args.responseFormat ?? args.model.options?.responseFormat;
|
|
438
|
-
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
|
+
}
|
|
439
446
|
const tools = args.tools ?? args.model.options?.tools;
|
|
440
447
|
(0, import_core2.throwIfExecutionAborted)(abortSignal);
|
|
441
448
|
const model = args.model.provider.getModel(args.model.modelId, {
|
|
@@ -449,6 +456,7 @@ async function runReasonEngine(args) {
|
|
|
449
456
|
...tools !== void 0 ? { tools } : {},
|
|
450
457
|
...providerOptions !== void 0 ? { providerOptions } : {}
|
|
451
458
|
});
|
|
459
|
+
if (tools?.length && !model.supportsToolStreaming) stream = false;
|
|
452
460
|
const messages = args.messages !== void 0 ? args.messages : [
|
|
453
461
|
...typeof args.system === "string" && args.system.length > 0 ? [{ role: "system", content: args.system }] : [],
|
|
454
462
|
{ role: "user", content: String(args.input ?? "") }
|
|
@@ -476,7 +484,9 @@ async function runReasonEngine(args) {
|
|
|
476
484
|
},
|
|
477
485
|
telemetry: {
|
|
478
486
|
...args.telemetry ?? {},
|
|
479
|
-
input: args.telemetry?.input ?? messages
|
|
487
|
+
input: args.telemetry?.input ?? messages.map(
|
|
488
|
+
({ continuation: _continuation, raw: _raw, ...message }) => message
|
|
489
|
+
)
|
|
480
490
|
}
|
|
481
491
|
});
|
|
482
492
|
const modelRequestStartedAt = Date.now();
|
|
@@ -507,7 +517,11 @@ async function runReasonEngine(args) {
|
|
|
507
517
|
});
|
|
508
518
|
};
|
|
509
519
|
const failTrace = (span, error) => {
|
|
520
|
+
const detail = error;
|
|
510
521
|
span?.fail?.(error, {
|
|
522
|
+
...detail?.usage ? { usage: detail.usage } : {},
|
|
523
|
+
...detail?.finishReason ? { finishReason: detail.finishReason } : {},
|
|
524
|
+
...detail?.providerMetadata ? { providerMetadata: detail.providerMetadata } : {},
|
|
511
525
|
attributes: {
|
|
512
526
|
providerId: args.model.provider.id,
|
|
513
527
|
modelId: args.model.modelId
|
|
@@ -516,6 +530,7 @@ async function runReasonEngine(args) {
|
|
|
516
530
|
};
|
|
517
531
|
try {
|
|
518
532
|
if (stream) {
|
|
533
|
+
let continuation;
|
|
519
534
|
let final = "";
|
|
520
535
|
let raw;
|
|
521
536
|
let usage;
|
|
@@ -554,6 +569,7 @@ async function runReasonEngine(args) {
|
|
|
554
569
|
break;
|
|
555
570
|
}
|
|
556
571
|
case "finish": {
|
|
572
|
+
continuation = chunk.continuation;
|
|
557
573
|
if (chunk.raw !== void 0) {
|
|
558
574
|
raw = chunk.raw;
|
|
559
575
|
}
|
|
@@ -602,6 +618,7 @@ async function runReasonEngine(args) {
|
|
|
602
618
|
}
|
|
603
619
|
const result2 = {
|
|
604
620
|
text: final,
|
|
621
|
+
...continuation ? { continuation } : {},
|
|
605
622
|
...raw !== void 0 ? { raw } : {},
|
|
606
623
|
...usage !== void 0 ? { usage } : {},
|
|
607
624
|
...finishReason !== void 0 ? { finishReason } : {},
|
|
@@ -615,6 +632,7 @@ async function runReasonEngine(args) {
|
|
|
615
632
|
}
|
|
616
633
|
const response = await model.invoke(messages);
|
|
617
634
|
(0, import_core2.throwIfExecutionAborted)(abortSignal);
|
|
635
|
+
if (response.content) args.onTextChunk?.(response.content);
|
|
618
636
|
if (emit) {
|
|
619
637
|
emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {
|
|
620
638
|
...commonMeta
|
|
@@ -631,6 +649,7 @@ async function runReasonEngine(args) {
|
|
|
631
649
|
}
|
|
632
650
|
const result = {
|
|
633
651
|
text: response.content,
|
|
652
|
+
...response.continuation ? { continuation: response.continuation } : {},
|
|
634
653
|
...response.raw !== void 0 ? { raw: response.raw } : {},
|
|
635
654
|
...response.usage !== void 0 ? { usage: response.usage } : {},
|
|
636
655
|
...response.finishReason !== void 0 ? { finishReason: response.finishReason } : {},
|
|
@@ -664,480 +683,188 @@ async function reasonEngine(args, meta, inputOverride) {
|
|
|
664
683
|
);
|
|
665
684
|
(0, import_core3.throwIfExecutionAborted)(abortSignal);
|
|
666
685
|
ctx.node.consumeExecution?.("maxModelPasses");
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
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
|
+
}
|
|
691
716
|
}
|
|
692
717
|
|
|
693
|
-
// src/reason/
|
|
694
|
-
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 {};
|
|
695
723
|
try {
|
|
696
|
-
|
|
697
|
-
|
|
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 {
|
|
698
778
|
return {
|
|
699
|
-
|
|
700
|
-
|
|
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
|
+
]
|
|
701
787
|
};
|
|
702
788
|
}
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
const
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
try {
|
|
713
|
-
return JSON.stringify(value, null, 2);
|
|
714
|
-
} catch {
|
|
715
|
-
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");
|
|
716
798
|
}
|
|
799
|
+
return appendPaths;
|
|
717
800
|
};
|
|
718
|
-
var
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
const
|
|
723
|
-
|
|
724
|
-
const block = extractJsonCodeBlock(trimmed);
|
|
725
|
-
if (block) {
|
|
726
|
-
const parsedBlock = tryJsonParse(block);
|
|
727
|
-
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");
|
|
728
807
|
}
|
|
729
|
-
return
|
|
730
|
-
};
|
|
731
|
-
var looksLikeStructuredJsonAttempt = (value) => {
|
|
732
|
-
const trimmed = value.trim();
|
|
733
|
-
if (!trimmed) return false;
|
|
734
|
-
return trimmed.startsWith("{") || trimmed.startsWith("[") || /^```(?:json)?/i.test(trimmed);
|
|
735
|
-
};
|
|
736
|
-
var isLikelyTruncatedStructuredOutput = (text) => {
|
|
737
|
-
const trimmed = text.trim();
|
|
738
|
-
if (!looksLikeStructuredJsonAttempt(trimmed)) return false;
|
|
739
|
-
const direct = tryJsonParse(trimmed);
|
|
740
|
-
if (!direct.ok && direct.error) return true;
|
|
741
|
-
const block = extractJsonCodeBlock(trimmed);
|
|
742
|
-
if (!block) return false;
|
|
743
|
-
const parsedBlock = tryJsonParse(block);
|
|
744
|
-
return !parsedBlock.ok && Boolean(parsedBlock.error);
|
|
808
|
+
return setPaths;
|
|
745
809
|
};
|
|
746
|
-
var
|
|
747
|
-
|
|
748
|
-
);
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
);
|
|
752
|
-
var parseReasonOutputWithSchema = (args) => {
|
|
753
|
-
const candidate = resolveOutputCandidate(args.text);
|
|
754
|
-
try {
|
|
755
|
-
return parseWithSchema(args.schema, candidate, args.label);
|
|
756
|
-
} catch (error) {
|
|
757
|
-
if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
|
|
758
|
-
throw createTruncatedStructuredOutputError(args.label);
|
|
759
|
-
}
|
|
760
|
-
if (typeof candidate === "string" && candidate.trim().length > 0) {
|
|
761
|
-
throw createInvalidStructuredOutputError(args.label);
|
|
762
|
-
}
|
|
763
|
-
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");
|
|
764
816
|
}
|
|
817
|
+
return textPaths;
|
|
765
818
|
};
|
|
766
|
-
var
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
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
|
+
};
|
|
771
829
|
}
|
|
772
|
-
if (
|
|
773
|
-
|
|
830
|
+
if (ch !== "\\") {
|
|
831
|
+
value += ch;
|
|
832
|
+
continue;
|
|
774
833
|
}
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
let interruptRequired = true;
|
|
781
|
-
if (mode === "optional") {
|
|
782
|
-
if (candidate.decision !== "continue" && candidate.decision !== "interrupt") {
|
|
783
|
-
throw new Error(
|
|
784
|
-
'useReason optional interrupt first pass must include decision "continue" or "interrupt".'
|
|
785
|
-
);
|
|
834
|
+
const next = args.text[i + 1];
|
|
835
|
+
if (!next) {
|
|
836
|
+
return {
|
|
837
|
+
complete: false
|
|
838
|
+
};
|
|
786
839
|
}
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
candidate.request,
|
|
792
|
-
candidate.interrupt
|
|
793
|
-
].some((value) => value !== null && value !== void 0);
|
|
794
|
-
if (hasUnexpectedRequest) {
|
|
795
|
-
throw new Error(
|
|
796
|
-
'useReason optional interrupt first pass with decision "continue" must not include an interrupt request.'
|
|
797
|
-
);
|
|
798
|
-
}
|
|
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;
|
|
799
844
|
}
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
candidate.interrupt,
|
|
807
|
-
mode === "required" ? candidate : void 0
|
|
808
|
-
];
|
|
809
|
-
const requestPayload = requestCandidates.find(
|
|
810
|
-
(value) => value !== null && value !== void 0
|
|
811
|
-
);
|
|
812
|
-
request = parseWithSchema(
|
|
813
|
-
args.requestSchema,
|
|
814
|
-
requestPayload,
|
|
815
|
-
"useReason interrupt.request"
|
|
816
|
-
);
|
|
817
|
-
}
|
|
818
|
-
let output;
|
|
819
|
-
if (args.outputSchema) {
|
|
820
|
-
const outputPayload = Object.hasOwn(candidate, "output") ? candidate.output : candidate;
|
|
821
|
-
try {
|
|
822
|
-
output = parseWithSchema(
|
|
823
|
-
args.outputSchema,
|
|
824
|
-
outputPayload,
|
|
825
|
-
"useReason output"
|
|
826
|
-
);
|
|
827
|
-
} catch (error) {
|
|
828
|
-
if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
|
|
829
|
-
throw createTruncatedStructuredOutputError("useReason output");
|
|
830
|
-
}
|
|
831
|
-
if (typeof outputPayload === "string" && outputPayload.trim().length > 0) {
|
|
832
|
-
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
|
+
};
|
|
833
851
|
}
|
|
834
|
-
|
|
852
|
+
value += String.fromCharCode(Number.parseInt(hex, 16));
|
|
853
|
+
i += 5;
|
|
854
|
+
continue;
|
|
835
855
|
}
|
|
836
|
-
}
|
|
837
|
-
const explicitDraft = typeof candidate.draftText === "string" && candidate.draftText.length > 0 ? candidate.draftText : typeof candidate.text === "string" && candidate.text.length > 0 ? candidate.text : void 0;
|
|
838
|
-
if (!args.outputSchema && !explicitDraft) {
|
|
839
|
-
throw new Error(
|
|
840
|
-
"useReason first pass with interrupt requires `draftText` when outputSchema is not provided."
|
|
841
|
-
);
|
|
842
|
-
}
|
|
843
|
-
const draftText = explicitDraft ?? (output !== void 0 ? tryStringify(output) : String(args.text ?? ""));
|
|
844
|
-
if (!interruptRequired) {
|
|
845
856
|
return {
|
|
846
|
-
|
|
847
|
-
interruptRequired: false,
|
|
848
|
-
...output !== void 0 ? { output } : {}
|
|
857
|
+
complete: false
|
|
849
858
|
};
|
|
850
859
|
}
|
|
851
860
|
return {
|
|
852
|
-
|
|
853
|
-
interruptRequired: true,
|
|
854
|
-
request,
|
|
855
|
-
...output !== void 0 ? { output } : {}
|
|
861
|
+
complete: false
|
|
856
862
|
};
|
|
857
863
|
};
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
if (typeof value === "string") return value;
|
|
863
|
-
try {
|
|
864
|
-
return JSON.stringify(value, null, 2);
|
|
865
|
-
} catch {
|
|
866
|
-
return String(value);
|
|
867
|
-
}
|
|
868
|
-
};
|
|
869
|
-
var tryGetSchemaHint = (schema) => {
|
|
870
|
-
try {
|
|
871
|
-
const hint = (0, import_zod2.toJSONSchema)(schema);
|
|
872
|
-
return JSON.stringify(hint, null, 2);
|
|
873
|
-
} catch {
|
|
874
|
-
return void 0;
|
|
875
|
-
}
|
|
876
|
-
};
|
|
877
|
-
var withSchemaHint = (input, label, schema) => {
|
|
878
|
-
const hint = tryGetSchemaHint(schema);
|
|
879
|
-
if (!hint) return input;
|
|
880
|
-
return `${input}
|
|
881
|
-
|
|
882
|
-
${label}:
|
|
883
|
-
${hint}`;
|
|
884
|
-
};
|
|
885
|
-
var defaultInterruptFirstPassInput = (args) => {
|
|
886
|
-
const mode = args.mode ?? "required";
|
|
887
|
-
const base = mode === "optional" ? args.outputSchema ? [
|
|
888
|
-
args.input,
|
|
889
|
-
"Output rules:",
|
|
890
|
-
"- Return JSON only. No markdown fences.",
|
|
891
|
-
"- Return an object with keys: decision, output, interruptRequest, draftText.",
|
|
892
|
-
'- decision must be either "continue" or "interrupt".',
|
|
893
|
-
'- Use decision "continue" when you can produce the final output without asking the user.',
|
|
894
|
-
'- Use decision "interrupt" only when user input is needed before finalizing.',
|
|
895
|
-
"- output must match the expected output JSON schema.",
|
|
896
|
-
'- When decision is "interrupt", interruptRequest must match the expected interrupt request JSON schema and must be a non-null object.',
|
|
897
|
-
'- When decision is "continue", interruptRequest must be null.',
|
|
898
|
-
"- draftText should be a concise plain-language summary of output."
|
|
899
|
-
].join("\n\n") : [
|
|
900
|
-
args.input,
|
|
901
|
-
"Output rules:",
|
|
902
|
-
"- Return JSON only. No markdown fences.",
|
|
903
|
-
"- Return an object with keys: decision, draftText, interruptRequest.",
|
|
904
|
-
'- decision must be either "continue" or "interrupt".',
|
|
905
|
-
'- Use decision "continue" when you can produce the final answer without asking the user.',
|
|
906
|
-
'- Use decision "interrupt" only when user input is needed before finalizing.',
|
|
907
|
-
"- draftText should be the current answer text.",
|
|
908
|
-
'- When decision is "interrupt", interruptRequest must match the expected interrupt request JSON schema and must be a non-null object.',
|
|
909
|
-
'- When decision is "continue", interruptRequest must be null.'
|
|
910
|
-
].join("\n\n") : args.outputSchema ? [
|
|
911
|
-
args.input,
|
|
912
|
-
"Output rules:",
|
|
913
|
-
"- Return JSON only. No markdown fences.",
|
|
914
|
-
"- Return an object with keys: output, interruptRequest, draftText.",
|
|
915
|
-
"- output must match the expected output JSON schema.",
|
|
916
|
-
"- interruptRequest must match the expected interrupt request JSON schema.",
|
|
917
|
-
"- interruptRequest must be a non-null object.",
|
|
918
|
-
"- draftText should be a concise plain-language summary of output.",
|
|
919
|
-
"- If uncertain, still return a valid interruptRequest with safe default options."
|
|
920
|
-
].join("\n\n") : [
|
|
921
|
-
args.input,
|
|
922
|
-
"Output rules:",
|
|
923
|
-
"- Return JSON only. No markdown fences.",
|
|
924
|
-
"- Return an object with keys: draftText, interruptRequest.",
|
|
925
|
-
"- draftText should be the current draft answer text.",
|
|
926
|
-
"- interruptRequest must match the expected interrupt request JSON schema.",
|
|
927
|
-
"- interruptRequest must be a non-null object.",
|
|
928
|
-
"- If uncertain, still return a valid interruptRequest with safe default options."
|
|
929
|
-
].join("\n\n");
|
|
930
|
-
const withInterruptSchema = withSchemaHint(
|
|
931
|
-
base,
|
|
932
|
-
"Expected interrupt request JSON schema (for `interruptRequest`)",
|
|
933
|
-
args.requestSchema
|
|
934
|
-
);
|
|
935
|
-
if (!args.outputSchema) return withInterruptSchema;
|
|
936
|
-
const withOutputSchema = withSchemaHint(
|
|
937
|
-
withInterruptSchema,
|
|
938
|
-
"Expected output JSON schema (for `output`)",
|
|
939
|
-
args.outputSchema
|
|
940
|
-
);
|
|
941
|
-
return [
|
|
942
|
-
withOutputSchema,
|
|
943
|
-
"JSON template:",
|
|
944
|
-
mode === "optional" ? '{ "decision": "continue", "output": { ... }, "interruptRequest": null, "draftText": "..." }' : '{ "output": { ... }, "interruptRequest": { ... }, "draftText": "..." }'
|
|
945
|
-
].join("\n\n");
|
|
946
|
-
};
|
|
947
|
-
var defaultContinuationInput = (args) => [
|
|
948
|
-
"You are continuing a paused reasoning task after user input.",
|
|
949
|
-
"Update the previous result using the user response and return the final answer only.",
|
|
950
|
-
"If the previous output was JSON, return JSON in the same schema.",
|
|
951
|
-
"",
|
|
952
|
-
`Original request:
|
|
953
|
-
${args.input}`,
|
|
954
|
-
`Draft answer text:
|
|
955
|
-
${args.draftText}`,
|
|
956
|
-
`Draft output object:
|
|
957
|
-
${tryStringify2(args.draftOutput ?? args.draftText)}`,
|
|
958
|
-
`Interrupt request shown to user:
|
|
959
|
-
${tryStringify2(args.request)}`,
|
|
960
|
-
`User response:
|
|
961
|
-
${tryStringify2(args.response)}`
|
|
962
|
-
].join("\n\n");
|
|
963
|
-
var withOutputGuardrails = (input, schema) => {
|
|
964
|
-
const base = [
|
|
965
|
-
input,
|
|
966
|
-
"Output rules:",
|
|
967
|
-
"- Return only JSON, no markdown fences.",
|
|
968
|
-
"- Ensure the response conforms to the expected output schema.",
|
|
969
|
-
"- Do not include explanatory text outside the JSON object."
|
|
970
|
-
].join("\n\n");
|
|
971
|
-
return withSchemaHint(base, "Expected output JSON schema", schema);
|
|
972
|
-
};
|
|
973
|
-
var withStructuredStreamHints = (input, args) => {
|
|
974
|
-
const rules = [];
|
|
975
|
-
for (const path of args.setFieldPaths ?? []) {
|
|
976
|
-
rules.push(
|
|
977
|
-
`- For the field path pattern \`${path}\`, write one stable final value and do not rewrite it after it is complete.`
|
|
978
|
-
);
|
|
979
|
-
}
|
|
980
|
-
for (const path of args.appendFieldPaths ?? []) {
|
|
981
|
-
rules.push(
|
|
982
|
-
`- For the array field path pattern \`${path}\`, write items in final order and finish each item before starting the next.`
|
|
983
|
-
);
|
|
984
|
-
rules.push(
|
|
985
|
-
`- Once an item in \`${path}\` is complete, do not rewrite or reorder earlier items.`
|
|
986
|
-
);
|
|
987
|
-
}
|
|
988
|
-
for (const path of args.textDeltaFieldPaths ?? []) {
|
|
989
|
-
rules.push(
|
|
990
|
-
`- For the string field path pattern \`${path}\`, write the value progressively by appending text to the end.`
|
|
991
|
-
);
|
|
992
|
-
rules.push(
|
|
993
|
-
`- Do not rewrite or replace the earlier prefix of \`${path}\` once emitted.`
|
|
994
|
-
);
|
|
995
|
-
}
|
|
996
|
-
if (rules.length === 0) return input;
|
|
997
|
-
return [
|
|
998
|
-
input,
|
|
999
|
-
"Streaming rules:",
|
|
1000
|
-
...rules,
|
|
1001
|
-
"- Keep the overall response as valid JSON."
|
|
1002
|
-
].join("\n\n");
|
|
1003
|
-
};
|
|
1004
|
-
|
|
1005
|
-
// src/reason/result.ts
|
|
1006
|
-
var mergeUsage = (left, right) => {
|
|
1007
|
-
if (!left) return right;
|
|
1008
|
-
if (!right) return left;
|
|
1009
|
-
const sum = (a, b) => a === void 0 && b === void 0 ? void 0 : (a ?? 0) + (b ?? 0);
|
|
1010
|
-
const raw = left.raw || right.raw ? {
|
|
1011
|
-
...left.raw ?? {},
|
|
1012
|
-
...right.raw ?? {}
|
|
1013
|
-
} : void 0;
|
|
1014
|
-
const input = sum(left.input, right.input);
|
|
1015
|
-
const output = sum(left.output, right.output);
|
|
1016
|
-
const total = sum(left.total, right.total);
|
|
1017
|
-
const reasoning = sum(left.reasoning, right.reasoning);
|
|
1018
|
-
const cacheRead = sum(left.cacheRead, right.cacheRead);
|
|
1019
|
-
const cacheWrite = sum(left.cacheWrite, right.cacheWrite);
|
|
1020
|
-
const merged = {};
|
|
1021
|
-
if (input !== void 0) merged.input = input;
|
|
1022
|
-
if (output !== void 0) merged.output = output;
|
|
1023
|
-
if (total !== void 0) merged.total = total;
|
|
1024
|
-
if (reasoning !== void 0) merged.reasoning = reasoning;
|
|
1025
|
-
if (cacheRead !== void 0) merged.cacheRead = cacheRead;
|
|
1026
|
-
if (cacheWrite !== void 0) merged.cacheWrite = cacheWrite;
|
|
1027
|
-
if (raw) merged.raw = raw;
|
|
1028
|
-
return merged;
|
|
1029
|
-
};
|
|
1030
|
-
var mergeWarnings = (left, right) => {
|
|
1031
|
-
if (!left?.length) return right;
|
|
1032
|
-
if (!right?.length) return left;
|
|
1033
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1034
|
-
const merged = [];
|
|
1035
|
-
for (const warning of [...left, ...right]) {
|
|
1036
|
-
const key = JSON.stringify(warning);
|
|
1037
|
-
if (seen.has(key)) continue;
|
|
1038
|
-
seen.add(key);
|
|
1039
|
-
merged.push(warning);
|
|
1040
|
-
}
|
|
1041
|
-
return merged;
|
|
1042
|
-
};
|
|
1043
|
-
var mergeProviderMetadata = (left, right) => {
|
|
1044
|
-
if (!left) return right;
|
|
1045
|
-
if (!right) return left;
|
|
1046
|
-
return {
|
|
1047
|
-
...left,
|
|
1048
|
-
...right
|
|
1049
|
-
};
|
|
1050
|
-
};
|
|
1051
|
-
var emitReasonStructuredOutput = (args) => {
|
|
1052
|
-
if (!args.emit || !shouldEmitStructured(args.structured)) return;
|
|
1053
|
-
emitStructuredData({
|
|
1054
|
-
dataType: args.structured?.dataType ?? "reason-output",
|
|
1055
|
-
data: args.output,
|
|
1056
|
-
kind: "final",
|
|
1057
|
-
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
1058
|
-
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
1059
|
-
...args.id ? { id: args.id } : {},
|
|
1060
|
-
streamId: args.opId
|
|
1061
|
-
});
|
|
1062
|
-
};
|
|
1063
|
-
|
|
1064
|
-
// src/reason/structured-stream.ts
|
|
1065
|
-
var resolveAppendFieldPaths = (cfg) => {
|
|
1066
|
-
const fields = cfg?.fields;
|
|
1067
|
-
if (!fields) return [];
|
|
1068
|
-
const appendPaths = Object.entries(fields).filter(([, mode]) => mode === "append").map(([path]) => path);
|
|
1069
|
-
for (const path of appendPaths) {
|
|
1070
|
-
assertStructuredPath(path, "useReason structured append");
|
|
1071
|
-
}
|
|
1072
|
-
return appendPaths;
|
|
1073
|
-
};
|
|
1074
|
-
var resolveSetFieldPaths = (cfg) => {
|
|
1075
|
-
const fields = cfg?.fields;
|
|
1076
|
-
if (!fields) return [];
|
|
1077
|
-
const setPaths = Object.entries(fields).filter(([, mode]) => mode === "set").map(([path]) => path);
|
|
1078
|
-
for (const path of setPaths) {
|
|
1079
|
-
assertStructuredPath(path, "useReason structured set");
|
|
1080
|
-
}
|
|
1081
|
-
return setPaths;
|
|
1082
|
-
};
|
|
1083
|
-
var resolveTextDeltaFieldPaths = (cfg) => {
|
|
1084
|
-
const fields = cfg?.fields;
|
|
1085
|
-
if (!fields) return [];
|
|
1086
|
-
const textPaths = Object.entries(fields).filter(([, mode]) => mode === "text-delta").map(([path]) => path);
|
|
1087
|
-
for (const path of textPaths) {
|
|
1088
|
-
assertStructuredPath(path, "useReason structured text-delta");
|
|
1089
|
-
}
|
|
1090
|
-
return textPaths;
|
|
1091
|
-
};
|
|
1092
|
-
var extractCompletedStringValueAt = (args) => {
|
|
1093
|
-
let value = "";
|
|
1094
|
-
for (let i = args.start; i < args.text.length; i += 1) {
|
|
1095
|
-
const ch = args.text[i];
|
|
1096
|
-
if (ch === void 0) break;
|
|
1097
|
-
if (ch === '"') {
|
|
1098
|
-
return {
|
|
1099
|
-
complete: true,
|
|
1100
|
-
value
|
|
1101
|
-
};
|
|
1102
|
-
}
|
|
1103
|
-
if (ch !== "\\") {
|
|
1104
|
-
value += ch;
|
|
1105
|
-
continue;
|
|
1106
|
-
}
|
|
1107
|
-
const next = args.text[i + 1];
|
|
1108
|
-
if (!next) {
|
|
1109
|
-
return {
|
|
1110
|
-
complete: false
|
|
1111
|
-
};
|
|
1112
|
-
}
|
|
1113
|
-
if (next === '"' || next === "\\" || next === "/" || next === "b" || next === "f" || next === "n" || next === "r" || next === "t") {
|
|
1114
|
-
value += next === "b" ? "\b" : next === "f" ? "\f" : next === "n" ? "\n" : next === "r" ? "\r" : next === "t" ? " " : next;
|
|
1115
|
-
i += 1;
|
|
1116
|
-
continue;
|
|
1117
|
-
}
|
|
1118
|
-
if (next === "u") {
|
|
1119
|
-
const hex = args.text.slice(i + 2, i + 6);
|
|
1120
|
-
if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
|
|
1121
|
-
return {
|
|
1122
|
-
complete: false
|
|
1123
|
-
};
|
|
1124
|
-
}
|
|
1125
|
-
value += String.fromCharCode(Number.parseInt(hex, 16));
|
|
1126
|
-
i += 5;
|
|
1127
|
-
continue;
|
|
1128
|
-
}
|
|
1129
|
-
return {
|
|
1130
|
-
complete: false
|
|
1131
|
-
};
|
|
1132
|
-
}
|
|
1133
|
-
return {
|
|
1134
|
-
complete: false
|
|
1135
|
-
};
|
|
1136
|
-
};
|
|
1137
|
-
var skipWhitespace = (text, start) => {
|
|
1138
|
-
let i = start;
|
|
1139
|
-
while (i < text.length && /\s/.test(text[i] ?? "")) i += 1;
|
|
1140
|
-
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;
|
|
1141
868
|
};
|
|
1142
869
|
var pathSegmentMatches = (patternPart, actualPart) => patternPart === "*" || patternPart === actualPart;
|
|
1143
870
|
var pathMatchesPattern = (actualParts, patternParts) => actualParts.length === patternParts.length && actualParts.every(
|
|
@@ -1521,103 +1248,560 @@ var extractCompletedArrayItems = (args) => {
|
|
|
1521
1248
|
}
|
|
1522
1249
|
return items;
|
|
1523
1250
|
};
|
|
1524
|
-
var extractCompletedArrayItemGroups = (args) => {
|
|
1525
|
-
return locateValueStarts(args).flatMap((match) => {
|
|
1526
|
-
const items = extractCompletedArrayItems({
|
|
1527
|
-
text: args.text,
|
|
1528
|
-
path: match.path
|
|
1529
|
-
});
|
|
1530
|
-
return items.length > 0 ? [
|
|
1531
|
-
{
|
|
1532
|
-
path: match.path,
|
|
1533
|
-
items
|
|
1534
|
-
}
|
|
1535
|
-
] : [];
|
|
1536
|
-
});
|
|
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);
|
|
1502
|
+
}
|
|
1503
|
+
throw error;
|
|
1504
|
+
}
|
|
1505
|
+
};
|
|
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");
|
|
1511
|
+
}
|
|
1512
|
+
if (typeof candidate === "string" && candidate.trim().length > 0) {
|
|
1513
|
+
throw createInvalidStructuredOutputError("useReason output");
|
|
1514
|
+
}
|
|
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
|
+
);
|
|
1526
|
+
}
|
|
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
|
+
);
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
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");
|
|
1570
|
+
}
|
|
1571
|
+
if (typeof outputPayload === "string" && outputPayload.trim().length > 0) {
|
|
1572
|
+
throw createInvalidStructuredOutputError("useReason output");
|
|
1573
|
+
}
|
|
1574
|
+
throw error;
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
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");
|
|
1686
|
+
};
|
|
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);
|
|
1537
1712
|
};
|
|
1538
|
-
var
|
|
1539
|
-
const
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
value: ""
|
|
1545
|
-
};
|
|
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
|
+
);
|
|
1546
1719
|
}
|
|
1547
|
-
const
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
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
|
+
);
|
|
1554
1727
|
}
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
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);
|
|
1601
1784
|
}
|
|
1785
|
+
return merged;
|
|
1786
|
+
};
|
|
1787
|
+
var mergeProviderMetadata = (left, right) => {
|
|
1788
|
+
if (!left) return right;
|
|
1789
|
+
if (!right) return left;
|
|
1602
1790
|
return {
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
value
|
|
1791
|
+
...left,
|
|
1792
|
+
...right
|
|
1606
1793
|
};
|
|
1607
1794
|
};
|
|
1608
|
-
var
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
value: current.value
|
|
1619
|
-
}
|
|
1620
|
-
] : [];
|
|
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
|
|
1621
1805
|
});
|
|
1622
1806
|
};
|
|
1623
1807
|
|
|
@@ -1674,13 +1858,16 @@ var createInitialMessages = (args) => [
|
|
|
1674
1858
|
{ role: "user", content: String(args.input ?? "") }
|
|
1675
1859
|
];
|
|
1676
1860
|
var runReasonToolLoop = async (args) => {
|
|
1677
|
-
const { useReasonArgs, id,
|
|
1861
|
+
const { useReasonArgs, id, traceSpan, checkpointKey } = args;
|
|
1678
1862
|
const ctx = getHookContext();
|
|
1679
1863
|
const abortSignal = (0, import_core4.combineAbortSignals)(
|
|
1680
1864
|
ctx.node.abortSignal,
|
|
1681
1865
|
useReasonArgs.abortSignal,
|
|
1682
1866
|
useReasonArgs.model.options?.abortSignal
|
|
1683
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;
|
|
1684
1871
|
const tools = useReasonArgs.tools ?? [];
|
|
1685
1872
|
const toolByName = /* @__PURE__ */ new Map();
|
|
1686
1873
|
let validationCompleted = false;
|
|
@@ -1706,20 +1893,43 @@ var runReasonToolLoop = async (args) => {
|
|
|
1706
1893
|
}
|
|
1707
1894
|
const toolDefinitions = toToolDefinitions(tools);
|
|
1708
1895
|
const maxSteps = Math.max(1, useReasonArgs.toolExecution?.maxSteps ?? 3);
|
|
1709
|
-
const messages = createInitialMessages({
|
|
1896
|
+
const messages = checkpoint?.messages ?? createInitialMessages({
|
|
1710
1897
|
system: useReasonArgs.system,
|
|
1711
|
-
input: useReasonArgs.input
|
|
1898
|
+
input: useReasonArgs.outputSchema ? withOutputGuardrails(useReasonArgs.input, useReasonArgs.outputSchema) : useReasonArgs.input
|
|
1712
1899
|
});
|
|
1713
|
-
const steps = [];
|
|
1714
|
-
const allToolCalls =
|
|
1715
|
-
|
|
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
|
+
);
|
|
1716
1907
|
let finalText = "";
|
|
1717
1908
|
let finalRaw;
|
|
1718
1909
|
let finalOutput;
|
|
1719
|
-
let aggregatedUsage
|
|
1910
|
+
let aggregatedUsage = steps.reduce(
|
|
1911
|
+
(usage, step) => mergeUsage(usage, step.usage),
|
|
1912
|
+
void 0
|
|
1913
|
+
);
|
|
1720
1914
|
let finalFinishReason;
|
|
1721
1915
|
let aggregatedProviderMetadata;
|
|
1722
|
-
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
|
+
};
|
|
1723
1933
|
const emitToolEvent = (event, payload) => {
|
|
1724
1934
|
ctx.node.emit(event, {
|
|
1725
1935
|
...payload,
|
|
@@ -1729,26 +1939,43 @@ var runReasonToolLoop = async (args) => {
|
|
|
1729
1939
|
});
|
|
1730
1940
|
};
|
|
1731
1941
|
try {
|
|
1732
|
-
for (let stepIndex =
|
|
1942
|
+
for (let stepIndex = pending ? steps.length - 1 : steps.length; stepIndex < maxSteps; stepIndex += 1) {
|
|
1733
1943
|
(0, import_core4.throwIfExecutionAborted)(abortSignal);
|
|
1734
1944
|
traceSpan?.addEvent?.("useReason.tool-step.start", {
|
|
1735
1945
|
stepIndex,
|
|
1736
1946
|
toolCount: toolDefinitions.length
|
|
1737
1947
|
});
|
|
1738
|
-
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(
|
|
1739
1954
|
{
|
|
1740
1955
|
...useReasonArgs,
|
|
1741
1956
|
tools: toolDefinitions,
|
|
1742
1957
|
messages,
|
|
1743
1958
|
emit: false,
|
|
1744
|
-
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
|
+
}
|
|
1745
1969
|
},
|
|
1746
1970
|
{ ...id ? { id } : {}, opId }
|
|
1747
1971
|
);
|
|
1972
|
+
if (textStarted) emitToolEvent("text-end", {});
|
|
1748
1973
|
finalText = step.text;
|
|
1749
1974
|
finalRaw = step.raw;
|
|
1750
|
-
|
|
1751
|
-
|
|
1975
|
+
if (!reused) {
|
|
1976
|
+
aggregatedUsage = mergeUsage(aggregatedUsage, step.usage);
|
|
1977
|
+
accumulateTokenUsage(step.usage);
|
|
1978
|
+
}
|
|
1752
1979
|
finalFinishReason = step.finishReason;
|
|
1753
1980
|
aggregatedProviderMetadata = mergeProviderMetadata(
|
|
1754
1981
|
aggregatedProviderMetadata,
|
|
@@ -1756,32 +1983,35 @@ var runReasonToolLoop = async (args) => {
|
|
|
1756
1983
|
);
|
|
1757
1984
|
aggregatedWarnings = mergeWarnings(aggregatedWarnings, step.warnings);
|
|
1758
1985
|
const toolCalls = step.toolCalls ?? [];
|
|
1759
|
-
const toolResults = [];
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
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
|
+
});
|
|
1770
1998
|
if (toolCalls.length === 0) {
|
|
1771
|
-
if ((useReasonArgs.emit ?? true) && step.text.length > 0) {
|
|
1772
|
-
emitToolEvent("text-start", {});
|
|
1773
|
-
emitToolEvent("text-delta", { delta: step.text });
|
|
1774
|
-
emitToolEvent("text-end", {});
|
|
1775
|
-
}
|
|
1776
1999
|
break;
|
|
1777
2000
|
}
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
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();
|
|
1784
2012
|
for (const toolCall of toolCalls) {
|
|
2013
|
+
if (toolResults.some((result2) => result2.toolCallId === toolCall.id))
|
|
2014
|
+
continue;
|
|
1785
2015
|
(0, import_core4.throwIfExecutionAborted)(abortSignal);
|
|
1786
2016
|
const tool = toolByName.get(toolCall.name);
|
|
1787
2017
|
if (!tool) {
|
|
@@ -1811,7 +2041,7 @@ var runReasonToolLoop = async (args) => {
|
|
|
1811
2041
|
input: toolCall.input
|
|
1812
2042
|
});
|
|
1813
2043
|
}
|
|
1814
|
-
if (shouldApproveTool) {
|
|
2044
|
+
if (shouldApproveTool && !approvedCalls.includes(toolCall.id)) {
|
|
1815
2045
|
const approval = await awaitInterruptInternal({
|
|
1816
2046
|
id: `tool:${toolCall.id}`,
|
|
1817
2047
|
request: {
|
|
@@ -1846,8 +2076,18 @@ var runReasonToolLoop = async (args) => {
|
|
|
1846
2076
|
isError: true
|
|
1847
2077
|
});
|
|
1848
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();
|
|
1849
2087
|
continue;
|
|
1850
2088
|
}
|
|
2089
|
+
approvedCalls.push(toolCall.id);
|
|
2090
|
+
save();
|
|
1851
2091
|
}
|
|
1852
2092
|
try {
|
|
1853
2093
|
(0, import_core4.throwIfExecutionAborted)(abortSignal);
|
|
@@ -1901,25 +2141,19 @@ var runReasonToolLoop = async (args) => {
|
|
|
1901
2141
|
message: result2.content
|
|
1902
2142
|
});
|
|
1903
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();
|
|
1904
2154
|
}
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
steps[steps.length - 1] = {
|
|
1908
|
-
...currentStep,
|
|
1909
|
-
toolResults
|
|
1910
|
-
};
|
|
1911
|
-
}
|
|
1912
|
-
for (const result2 of toolResults) {
|
|
1913
|
-
messages.push({
|
|
1914
|
-
role: "tool",
|
|
1915
|
-
content: result2.content,
|
|
1916
|
-
toolCallId: result2.toolCallId,
|
|
1917
|
-
name: result2.name,
|
|
1918
|
-
...result2.structuredContent !== void 0 ? { structuredContent: result2.structuredContent } : {},
|
|
1919
|
-
...result2.isError !== void 0 ? { isError: result2.isError } : {},
|
|
1920
|
-
...result2.raw !== void 0 ? { raw: result2.raw } : {}
|
|
1921
|
-
});
|
|
1922
|
-
}
|
|
2155
|
+
pending = void 0;
|
|
2156
|
+
save();
|
|
1923
2157
|
}
|
|
1924
2158
|
if (steps.length >= maxSteps && steps.at(-1)?.toolCalls.length) {
|
|
1925
2159
|
throw new Error(
|
|
@@ -1973,6 +2207,8 @@ var runReasonToolLoop = async (args) => {
|
|
|
1973
2207
|
output: useReasonArgs.telemetry?.output ?? finalOutput ?? finalText
|
|
1974
2208
|
}
|
|
1975
2209
|
});
|
|
2210
|
+
ctx.currentNodeState.byKey[checkpointKey] = { status: "completed", result };
|
|
2211
|
+
ctx.stateDirty = true;
|
|
1976
2212
|
return result;
|
|
1977
2213
|
} catch (error) {
|
|
1978
2214
|
traceSpan?.fail?.(error, {
|
|
@@ -2008,7 +2244,15 @@ async function useReason(args) {
|
|
|
2008
2244
|
args.model.options?.abortSignal
|
|
2009
2245
|
);
|
|
2010
2246
|
(0, import_core5.throwIfExecutionAborted)(abortSignal);
|
|
2011
|
-
|
|
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
|
+
};
|
|
2012
2256
|
const id = typeof args.id === "string" && args.id.length > 0 ? args.id : void 0;
|
|
2013
2257
|
const opId = createRuntimeId2();
|
|
2014
2258
|
const checkpointKey = resolveReasonCheckpointKey({
|
|
@@ -2052,6 +2296,8 @@ async function useReason(args) {
|
|
|
2052
2296
|
if (args.tools?.length) {
|
|
2053
2297
|
return runReasonToolLoop({
|
|
2054
2298
|
useReasonArgs: args,
|
|
2299
|
+
checkpointKey,
|
|
2300
|
+
initialWarnings: inferred.warnings,
|
|
2055
2301
|
...id ? { id } : {},
|
|
2056
2302
|
opId,
|
|
2057
2303
|
...traceSpan ? { traceSpan } : {}
|
|
@@ -2075,10 +2321,7 @@ async function useReason(args) {
|
|
|
2075
2321
|
let aggregatedUsage;
|
|
2076
2322
|
let finalFinishReason;
|
|
2077
2323
|
let aggregatedProviderMetadata;
|
|
2078
|
-
let aggregatedWarnings;
|
|
2079
|
-
const emittedSetValues = /* @__PURE__ */ new Map();
|
|
2080
|
-
const emittedAppendCounts = /* @__PURE__ */ new Map();
|
|
2081
|
-
const emittedTextValues = /* @__PURE__ */ new Map();
|
|
2324
|
+
let aggregatedWarnings = inferred.warnings;
|
|
2082
2325
|
if (existingCheckpoint) {
|
|
2083
2326
|
firstText = existingCheckpoint.firstText;
|
|
2084
2327
|
firstRaw = existingCheckpoint.firstRaw;
|
|
@@ -2100,78 +2343,7 @@ async function useReason(args) {
|
|
|
2100
2343
|
emit: suppressTextStream ? false : args.emit,
|
|
2101
2344
|
stream: useStructuredIncrementalStreaming ? true : suppressTextStream ? false : args.stream,
|
|
2102
2345
|
...useStructuredIncrementalStreaming ? {
|
|
2103
|
-
onTextChunk: (
|
|
2104
|
-
firstText += delta;
|
|
2105
|
-
finalText = firstText;
|
|
2106
|
-
for (const fieldPath of setFieldPaths) {
|
|
2107
|
-
const values = extractCompletedFieldValues({
|
|
2108
|
-
text: firstText,
|
|
2109
|
-
path: fieldPath
|
|
2110
|
-
});
|
|
2111
|
-
for (const { path, value } of values) {
|
|
2112
|
-
const nextSerialized = JSON.stringify(value);
|
|
2113
|
-
if (emittedSetValues.get(path) === nextSerialized) continue;
|
|
2114
|
-
emittedSetValues.set(path, nextSerialized);
|
|
2115
|
-
emitStructuredData({
|
|
2116
|
-
kind: "set",
|
|
2117
|
-
path,
|
|
2118
|
-
value,
|
|
2119
|
-
dataType: args.structured?.dataType ?? "reason-output",
|
|
2120
|
-
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
2121
|
-
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
2122
|
-
...id ? { id } : {},
|
|
2123
|
-
streamId: opId
|
|
2124
|
-
});
|
|
2125
|
-
}
|
|
2126
|
-
}
|
|
2127
|
-
for (const fieldPath of appendFieldPaths) {
|
|
2128
|
-
const groups = extractCompletedArrayItemGroups({
|
|
2129
|
-
text: firstText,
|
|
2130
|
-
path: fieldPath
|
|
2131
|
-
});
|
|
2132
|
-
for (const { path, items } of groups) {
|
|
2133
|
-
const emittedAppendCount = emittedAppendCounts.get(path) ?? 0;
|
|
2134
|
-
if (items.length <= emittedAppendCount) continue;
|
|
2135
|
-
const nextItems = items.slice(emittedAppendCount);
|
|
2136
|
-
emittedAppendCounts.set(path, items.length);
|
|
2137
|
-
emitStructuredData({
|
|
2138
|
-
kind: "append",
|
|
2139
|
-
path,
|
|
2140
|
-
items: nextItems,
|
|
2141
|
-
dataType: args.structured?.dataType ?? "reason-output",
|
|
2142
|
-
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
2143
|
-
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
2144
|
-
...id ? { id } : {},
|
|
2145
|
-
streamId: opId
|
|
2146
|
-
});
|
|
2147
|
-
}
|
|
2148
|
-
}
|
|
2149
|
-
for (const fieldPath of textDeltaFieldPaths) {
|
|
2150
|
-
const values = extractStreamingStringValues({
|
|
2151
|
-
text: firstText,
|
|
2152
|
-
path: fieldPath
|
|
2153
|
-
});
|
|
2154
|
-
for (const { path, value } of values) {
|
|
2155
|
-
const emittedTextValue = emittedTextValues.get(path) ?? "";
|
|
2156
|
-
if (value.length <= emittedTextValue.length || !value.startsWith(emittedTextValue)) {
|
|
2157
|
-
continue;
|
|
2158
|
-
}
|
|
2159
|
-
const nextDelta = value.slice(emittedTextValue.length);
|
|
2160
|
-
emittedTextValues.set(path, value);
|
|
2161
|
-
if (nextDelta.length === 0) continue;
|
|
2162
|
-
emitStructuredData({
|
|
2163
|
-
kind: "text-delta",
|
|
2164
|
-
path,
|
|
2165
|
-
delta: nextDelta,
|
|
2166
|
-
dataType: args.structured?.dataType ?? "reason-output",
|
|
2167
|
-
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
2168
|
-
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
2169
|
-
...id ? { id } : {},
|
|
2170
|
-
streamId: opId
|
|
2171
|
-
});
|
|
2172
|
-
}
|
|
2173
|
-
}
|
|
2174
|
-
}
|
|
2346
|
+
onTextChunk: createStructuredOutputStreamer(args, id, opId)
|
|
2175
2347
|
} : {}
|
|
2176
2348
|
},
|
|
2177
2349
|
reasonMeta,
|
|
@@ -2291,6 +2463,7 @@ async function useReason(args) {
|
|
|
2291
2463
|
const second = await reasonEngine(
|
|
2292
2464
|
{
|
|
2293
2465
|
...args,
|
|
2466
|
+
...inferred.responseFormat ? { responseFormat: inferred.responseFormat } : {},
|
|
2294
2467
|
emit: suppressTextStream ? false : args.emit,
|
|
2295
2468
|
stream: suppressTextStream ? false : args.stream
|
|
2296
2469
|
},
|