@kortyx/agent 0.19.0 → 0.20.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 +21 -0
- package/dist/index.js +838 -669
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -157,7 +157,7 @@ var require_dist = __commonJS({
|
|
|
157
157
|
if (!usage) return void 0;
|
|
158
158
|
const input = usage.input ?? 0;
|
|
159
159
|
const output = usage.output ?? 0;
|
|
160
|
-
const total = usage.total ?? input + output + (usage.reasoning ?? 0);
|
|
160
|
+
const total = usage.total ?? input + output + (usage.outputIncludesReasoning ? 0 : usage.reasoning ?? 0);
|
|
161
161
|
if (input === 0 && output === 0 && total === 0) {
|
|
162
162
|
return void 0;
|
|
163
163
|
}
|
|
@@ -433,7 +433,7 @@ var require_dist = __commonJS({
|
|
|
433
433
|
});
|
|
434
434
|
};
|
|
435
435
|
async function runReasonEngine(args) {
|
|
436
|
-
|
|
436
|
+
let stream = args.stream ?? args.model.options?.streaming ?? true;
|
|
437
437
|
const emit = args.emit ?? true;
|
|
438
438
|
const temperature = args.temperature ?? args.model.options?.temperature ?? args.defaultTemperature;
|
|
439
439
|
const maxOutputTokens = args.maxOutputTokens ?? args.model.options?.maxOutputTokens;
|
|
@@ -441,7 +441,14 @@ var require_dist = __commonJS({
|
|
|
441
441
|
const abortSignal = args.abortSignal ?? args.model.options?.abortSignal;
|
|
442
442
|
const reasoning = args.reasoning ?? args.model.options?.reasoning;
|
|
443
443
|
const responseFormat = args.responseFormat ?? args.model.options?.responseFormat;
|
|
444
|
-
const
|
|
444
|
+
const defaults = args.model.options?.providerOptions;
|
|
445
|
+
const providerOptions = defaults || args.providerOptions ? { ...defaults, ...args.providerOptions } : void 0;
|
|
446
|
+
for (const [key, value] of Object.entries(args.providerOptions ?? {})) {
|
|
447
|
+
const previous = defaults?.[key];
|
|
448
|
+
if (previous && value && typeof previous === "object" && typeof value === "object" && !Array.isArray(previous) && !Array.isArray(value)) {
|
|
449
|
+
if (providerOptions) providerOptions[key] = { ...previous, ...value };
|
|
450
|
+
}
|
|
451
|
+
}
|
|
445
452
|
const tools = args.tools ?? args.model.options?.tools;
|
|
446
453
|
(0, import_core22.throwIfExecutionAborted)(abortSignal);
|
|
447
454
|
const model = args.model.provider.getModel(args.model.modelId, {
|
|
@@ -455,6 +462,7 @@ var require_dist = __commonJS({
|
|
|
455
462
|
...tools !== void 0 ? { tools } : {},
|
|
456
463
|
...providerOptions !== void 0 ? { providerOptions } : {}
|
|
457
464
|
});
|
|
465
|
+
if (tools?.length && !model.supportsToolStreaming) stream = false;
|
|
458
466
|
const messages = args.messages !== void 0 ? args.messages : [
|
|
459
467
|
...typeof args.system === "string" && args.system.length > 0 ? [{ role: "system", content: args.system }] : [],
|
|
460
468
|
{ role: "user", content: String(args.input ?? "") }
|
|
@@ -482,7 +490,9 @@ var require_dist = __commonJS({
|
|
|
482
490
|
},
|
|
483
491
|
telemetry: {
|
|
484
492
|
...args.telemetry ?? {},
|
|
485
|
-
input: args.telemetry?.input ?? messages
|
|
493
|
+
input: args.telemetry?.input ?? messages.map(
|
|
494
|
+
({ continuation: _continuation, raw: _raw, ...message }) => message
|
|
495
|
+
)
|
|
486
496
|
}
|
|
487
497
|
});
|
|
488
498
|
const modelRequestStartedAt = Date.now();
|
|
@@ -513,7 +523,11 @@ var require_dist = __commonJS({
|
|
|
513
523
|
});
|
|
514
524
|
};
|
|
515
525
|
const failTrace = (span, error) => {
|
|
526
|
+
const detail = error;
|
|
516
527
|
span?.fail?.(error, {
|
|
528
|
+
...detail?.usage ? { usage: detail.usage } : {},
|
|
529
|
+
...detail?.finishReason ? { finishReason: detail.finishReason } : {},
|
|
530
|
+
...detail?.providerMetadata ? { providerMetadata: detail.providerMetadata } : {},
|
|
517
531
|
attributes: {
|
|
518
532
|
providerId: args.model.provider.id,
|
|
519
533
|
modelId: args.model.modelId
|
|
@@ -522,6 +536,7 @@ var require_dist = __commonJS({
|
|
|
522
536
|
};
|
|
523
537
|
try {
|
|
524
538
|
if (stream) {
|
|
539
|
+
let continuation;
|
|
525
540
|
let final = "";
|
|
526
541
|
let raw;
|
|
527
542
|
let usage;
|
|
@@ -560,6 +575,7 @@ var require_dist = __commonJS({
|
|
|
560
575
|
break;
|
|
561
576
|
}
|
|
562
577
|
case "finish": {
|
|
578
|
+
continuation = chunk.continuation;
|
|
563
579
|
if (chunk.raw !== void 0) {
|
|
564
580
|
raw = chunk.raw;
|
|
565
581
|
}
|
|
@@ -608,6 +624,7 @@ var require_dist = __commonJS({
|
|
|
608
624
|
}
|
|
609
625
|
const result2 = {
|
|
610
626
|
text: final,
|
|
627
|
+
...continuation ? { continuation } : {},
|
|
611
628
|
...raw !== void 0 ? { raw } : {},
|
|
612
629
|
...usage !== void 0 ? { usage } : {},
|
|
613
630
|
...finishReason !== void 0 ? { finishReason } : {},
|
|
@@ -621,6 +638,7 @@ var require_dist = __commonJS({
|
|
|
621
638
|
}
|
|
622
639
|
const response = await model.invoke(messages);
|
|
623
640
|
(0, import_core22.throwIfExecutionAborted)(abortSignal);
|
|
641
|
+
if (response.content) args.onTextChunk?.(response.content);
|
|
624
642
|
if (emit) {
|
|
625
643
|
emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {
|
|
626
644
|
...commonMeta
|
|
@@ -637,6 +655,7 @@ var require_dist = __commonJS({
|
|
|
637
655
|
}
|
|
638
656
|
const result = {
|
|
639
657
|
text: response.content,
|
|
658
|
+
...response.continuation ? { continuation: response.continuation } : {},
|
|
640
659
|
...response.raw !== void 0 ? { raw: response.raw } : {},
|
|
641
660
|
...response.usage !== void 0 ? { usage: response.usage } : {},
|
|
642
661
|
...response.finishReason !== void 0 ? { finishReason: response.finishReason } : {},
|
|
@@ -668,472 +687,184 @@ var require_dist = __commonJS({
|
|
|
668
687
|
);
|
|
669
688
|
(0, import_core32.throwIfExecutionAborted)(abortSignal);
|
|
670
689
|
ctx.node.consumeExecution?.("maxModelPasses");
|
|
671
|
-
return runReasonEngine({
|
|
672
|
-
model: args.model,
|
|
673
|
-
input: inputOverride ?? args.input,
|
|
674
|
-
system: args.system,
|
|
675
|
-
temperature: args.temperature,
|
|
676
|
-
maxOutputTokens: args.maxOutputTokens,
|
|
677
|
-
stopSequences: args.stopSequences,
|
|
678
|
-
abortSignal,
|
|
679
|
-
reasoning: args.reasoning,
|
|
680
|
-
responseFormat: args.responseFormat,
|
|
681
|
-
providerOptions: args.providerOptions,
|
|
682
|
-
tools: args.tools,
|
|
683
|
-
messages: args.messages,
|
|
684
|
-
defaultTemperature: ctx.node.config?.model?.temperature,
|
|
685
|
-
stream: args.stream,
|
|
686
|
-
emit: args.emit,
|
|
687
|
-
onTextChunk: args.onTextChunk,
|
|
688
|
-
telemetry: args.telemetry,
|
|
689
|
-
nodeId: ctx.node.graph.node,
|
|
690
|
-
emitEvent: ctx.node.emit,
|
|
691
|
-
reasonTrace: ctx.reasonTrace,
|
|
692
|
-
id: meta.id,
|
|
693
|
-
opId: meta.opId
|
|
694
|
-
});
|
|
695
|
-
}
|
|
696
|
-
var tryJsonParse = (value) => {
|
|
697
690
|
try {
|
|
698
|
-
return
|
|
691
|
+
return await runReasonEngine({
|
|
692
|
+
model: args.model,
|
|
693
|
+
input: inputOverride ?? args.input,
|
|
694
|
+
system: args.system,
|
|
695
|
+
temperature: args.temperature,
|
|
696
|
+
maxOutputTokens: args.maxOutputTokens,
|
|
697
|
+
stopSequences: args.stopSequences,
|
|
698
|
+
abortSignal,
|
|
699
|
+
reasoning: args.reasoning,
|
|
700
|
+
responseFormat: args.responseFormat,
|
|
701
|
+
providerOptions: args.providerOptions,
|
|
702
|
+
tools: args.tools,
|
|
703
|
+
messages: args.messages,
|
|
704
|
+
defaultTemperature: ctx.node.config?.model?.temperature,
|
|
705
|
+
stream: args.stream,
|
|
706
|
+
emit: args.emit,
|
|
707
|
+
onTextChunk: args.onTextChunk,
|
|
708
|
+
telemetry: args.telemetry,
|
|
709
|
+
nodeId: ctx.node.graph.node,
|
|
710
|
+
emitEvent: ctx.node.emit,
|
|
711
|
+
reasonTrace: ctx.reasonTrace,
|
|
712
|
+
id: meta.id,
|
|
713
|
+
opId: meta.opId
|
|
714
|
+
});
|
|
699
715
|
} catch (error) {
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
};
|
|
716
|
+
const usage = error?.usage;
|
|
717
|
+
if (usage) accumulateTokenUsage(usage);
|
|
718
|
+
throw error;
|
|
704
719
|
}
|
|
705
|
-
}
|
|
706
|
-
var
|
|
707
|
-
|
|
708
|
-
if (
|
|
709
|
-
|
|
710
|
-
return inner.length > 0 ? inner : null;
|
|
711
|
-
};
|
|
712
|
-
var tryStringify = (value) => {
|
|
713
|
-
if (typeof value === "string") return value;
|
|
720
|
+
}
|
|
721
|
+
var import_zod22 = require("zod");
|
|
722
|
+
function inferOutputFormat(schema, explicit) {
|
|
723
|
+
if (explicit) return { responseFormat: explicit };
|
|
724
|
+
if (!schema) return {};
|
|
714
725
|
try {
|
|
715
|
-
|
|
726
|
+
const json = (0, import_zod22.toJSONSchema)(schema);
|
|
727
|
+
const clean = (value) => {
|
|
728
|
+
if (Array.isArray(value)) return value.map(clean);
|
|
729
|
+
if (!value || typeof value !== "object") return value;
|
|
730
|
+
const object = value;
|
|
731
|
+
if ([
|
|
732
|
+
"allOf",
|
|
733
|
+
"not",
|
|
734
|
+
"dependentRequired",
|
|
735
|
+
"dependentSchemas",
|
|
736
|
+
"if",
|
|
737
|
+
"then",
|
|
738
|
+
"else",
|
|
739
|
+
"prefixItems"
|
|
740
|
+
].some((key) => key in object))
|
|
741
|
+
throw new Error(
|
|
742
|
+
"Schema composition requires an explicit provider schema"
|
|
743
|
+
);
|
|
744
|
+
if (object.type === "object") {
|
|
745
|
+
if (object.additionalProperties !== false)
|
|
746
|
+
throw new Error(
|
|
747
|
+
"Dynamic properties require an explicit provider schema"
|
|
748
|
+
);
|
|
749
|
+
const properties = object.properties;
|
|
750
|
+
const required = object.required;
|
|
751
|
+
if (properties && Object.keys(properties).some((key) => !required?.includes(key))) {
|
|
752
|
+
throw new Error(
|
|
753
|
+
"Optional properties require an explicit provider schema"
|
|
754
|
+
);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
return Object.fromEntries(
|
|
758
|
+
Object.entries(object).filter(([key]) => key !== "$schema" && key !== "default").map(([key, child]) => {
|
|
759
|
+
if ((key === "properties" || key === "$defs" || key === "definitions") && child && typeof child === "object")
|
|
760
|
+
return [
|
|
761
|
+
key,
|
|
762
|
+
Object.fromEntries(
|
|
763
|
+
Object.entries(child).map(([name, definition]) => [
|
|
764
|
+
name,
|
|
765
|
+
clean(definition)
|
|
766
|
+
])
|
|
767
|
+
)
|
|
768
|
+
];
|
|
769
|
+
return [
|
|
770
|
+
key,
|
|
771
|
+
key === "enum" || key === "const" ? child : clean(child)
|
|
772
|
+
];
|
|
773
|
+
})
|
|
774
|
+
);
|
|
775
|
+
};
|
|
776
|
+
if (json.type !== "object")
|
|
777
|
+
throw new Error("The provider schema must have an object root");
|
|
778
|
+
return { responseFormat: { type: "json", schema: clean(json) } };
|
|
716
779
|
} catch {
|
|
717
|
-
return
|
|
780
|
+
return {
|
|
781
|
+
responseFormat: { type: "json" },
|
|
782
|
+
warnings: [
|
|
783
|
+
{
|
|
784
|
+
type: "compatibility",
|
|
785
|
+
feature: "outputSchema",
|
|
786
|
+
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."
|
|
787
|
+
}
|
|
788
|
+
]
|
|
789
|
+
};
|
|
718
790
|
}
|
|
719
|
-
}
|
|
720
|
-
var
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
const
|
|
725
|
-
|
|
726
|
-
const block = extractJsonCodeBlock(trimmed);
|
|
727
|
-
if (block) {
|
|
728
|
-
const parsedBlock = tryJsonParse(block);
|
|
729
|
-
if (parsedBlock.ok) return parsedBlock.value;
|
|
791
|
+
}
|
|
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");
|
|
730
798
|
}
|
|
731
|
-
return
|
|
732
|
-
};
|
|
733
|
-
var looksLikeStructuredJsonAttempt = (value) => {
|
|
734
|
-
const trimmed = value.trim();
|
|
735
|
-
if (!trimmed) return false;
|
|
736
|
-
return trimmed.startsWith("{") || trimmed.startsWith("[") || /^```(?:json)?/i.test(trimmed);
|
|
799
|
+
return appendPaths;
|
|
737
800
|
};
|
|
738
|
-
var
|
|
739
|
-
const
|
|
740
|
-
if (!
|
|
741
|
-
const
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
return !parsedBlock.ok && Boolean(parsedBlock.error);
|
|
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");
|
|
807
|
+
}
|
|
808
|
+
return setPaths;
|
|
747
809
|
};
|
|
748
|
-
var
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
var parseReasonOutputWithSchema = (args) => {
|
|
755
|
-
const candidate = resolveOutputCandidate(args.text);
|
|
756
|
-
try {
|
|
757
|
-
return parseWithSchema(args.schema, candidate, args.label);
|
|
758
|
-
} catch (error) {
|
|
759
|
-
if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
|
|
760
|
-
throw createTruncatedStructuredOutputError(args.label);
|
|
761
|
-
}
|
|
762
|
-
if (typeof candidate === "string" && candidate.trim().length > 0) {
|
|
763
|
-
throw createInvalidStructuredOutputError(args.label);
|
|
764
|
-
}
|
|
765
|
-
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");
|
|
766
816
|
}
|
|
817
|
+
return textPaths;
|
|
767
818
|
};
|
|
768
|
-
var
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
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
|
+
};
|
|
773
829
|
}
|
|
774
|
-
if (
|
|
775
|
-
|
|
830
|
+
if (ch !== "\\") {
|
|
831
|
+
value += ch;
|
|
832
|
+
continue;
|
|
776
833
|
}
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
let interruptRequired = true;
|
|
783
|
-
if (mode === "optional") {
|
|
784
|
-
if (candidate.decision !== "continue" && candidate.decision !== "interrupt") {
|
|
785
|
-
throw new Error(
|
|
786
|
-
'useReason optional interrupt first pass must include decision "continue" or "interrupt".'
|
|
787
|
-
);
|
|
834
|
+
const next = args.text[i + 1];
|
|
835
|
+
if (!next) {
|
|
836
|
+
return {
|
|
837
|
+
complete: false
|
|
838
|
+
};
|
|
788
839
|
}
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
candidate.request,
|
|
794
|
-
candidate.interrupt
|
|
795
|
-
].some((value) => value !== null && value !== void 0);
|
|
796
|
-
if (hasUnexpectedRequest) {
|
|
797
|
-
throw new Error(
|
|
798
|
-
'useReason optional interrupt first pass with decision "continue" must not include an interrupt request.'
|
|
799
|
-
);
|
|
800
|
-
}
|
|
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;
|
|
801
844
|
}
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
candidate.interrupt,
|
|
809
|
-
mode === "required" ? candidate : void 0
|
|
810
|
-
];
|
|
811
|
-
const requestPayload = requestCandidates.find(
|
|
812
|
-
(value) => value !== null && value !== void 0
|
|
813
|
-
);
|
|
814
|
-
request = parseWithSchema(
|
|
815
|
-
args.requestSchema,
|
|
816
|
-
requestPayload,
|
|
817
|
-
"useReason interrupt.request"
|
|
818
|
-
);
|
|
819
|
-
}
|
|
820
|
-
let output;
|
|
821
|
-
if (args.outputSchema) {
|
|
822
|
-
const outputPayload = Object.hasOwn(candidate, "output") ? candidate.output : candidate;
|
|
823
|
-
try {
|
|
824
|
-
output = parseWithSchema(
|
|
825
|
-
args.outputSchema,
|
|
826
|
-
outputPayload,
|
|
827
|
-
"useReason output"
|
|
828
|
-
);
|
|
829
|
-
} catch (error) {
|
|
830
|
-
if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
|
|
831
|
-
throw createTruncatedStructuredOutputError("useReason output");
|
|
832
|
-
}
|
|
833
|
-
if (typeof outputPayload === "string" && outputPayload.trim().length > 0) {
|
|
834
|
-
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
|
+
};
|
|
835
851
|
}
|
|
836
|
-
|
|
852
|
+
value += String.fromCharCode(Number.parseInt(hex, 16));
|
|
853
|
+
i += 5;
|
|
854
|
+
continue;
|
|
837
855
|
}
|
|
838
|
-
}
|
|
839
|
-
const explicitDraft = typeof candidate.draftText === "string" && candidate.draftText.length > 0 ? candidate.draftText : typeof candidate.text === "string" && candidate.text.length > 0 ? candidate.text : void 0;
|
|
840
|
-
if (!args.outputSchema && !explicitDraft) {
|
|
841
|
-
throw new Error(
|
|
842
|
-
"useReason first pass with interrupt requires `draftText` when outputSchema is not provided."
|
|
843
|
-
);
|
|
844
|
-
}
|
|
845
|
-
const draftText = explicitDraft ?? (output !== void 0 ? tryStringify(output) : String(args.text ?? ""));
|
|
846
|
-
if (!interruptRequired) {
|
|
847
856
|
return {
|
|
848
|
-
|
|
849
|
-
interruptRequired: false,
|
|
850
|
-
...output !== void 0 ? { output } : {}
|
|
857
|
+
complete: false
|
|
851
858
|
};
|
|
852
859
|
}
|
|
853
860
|
return {
|
|
854
|
-
|
|
855
|
-
interruptRequired: true,
|
|
856
|
-
request,
|
|
857
|
-
...output !== void 0 ? { output } : {}
|
|
861
|
+
complete: false
|
|
858
862
|
};
|
|
859
863
|
};
|
|
860
|
-
var
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
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_zod22.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
|
-
var mergeUsage = (left, right) => {
|
|
1005
|
-
if (!left) return right;
|
|
1006
|
-
if (!right) return left;
|
|
1007
|
-
const sum = (a, b) => a === void 0 && b === void 0 ? void 0 : (a ?? 0) + (b ?? 0);
|
|
1008
|
-
const raw = left.raw || right.raw ? {
|
|
1009
|
-
...left.raw ?? {},
|
|
1010
|
-
...right.raw ?? {}
|
|
1011
|
-
} : void 0;
|
|
1012
|
-
const input = sum(left.input, right.input);
|
|
1013
|
-
const output = sum(left.output, right.output);
|
|
1014
|
-
const total = sum(left.total, right.total);
|
|
1015
|
-
const reasoning = sum(left.reasoning, right.reasoning);
|
|
1016
|
-
const cacheRead = sum(left.cacheRead, right.cacheRead);
|
|
1017
|
-
const cacheWrite = sum(left.cacheWrite, right.cacheWrite);
|
|
1018
|
-
const merged = {};
|
|
1019
|
-
if (input !== void 0) merged.input = input;
|
|
1020
|
-
if (output !== void 0) merged.output = output;
|
|
1021
|
-
if (total !== void 0) merged.total = total;
|
|
1022
|
-
if (reasoning !== void 0) merged.reasoning = reasoning;
|
|
1023
|
-
if (cacheRead !== void 0) merged.cacheRead = cacheRead;
|
|
1024
|
-
if (cacheWrite !== void 0) merged.cacheWrite = cacheWrite;
|
|
1025
|
-
if (raw) merged.raw = raw;
|
|
1026
|
-
return merged;
|
|
1027
|
-
};
|
|
1028
|
-
var mergeWarnings = (left, right) => {
|
|
1029
|
-
if (!left?.length) return right;
|
|
1030
|
-
if (!right?.length) return left;
|
|
1031
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1032
|
-
const merged = [];
|
|
1033
|
-
for (const warning of [...left, ...right]) {
|
|
1034
|
-
const key = JSON.stringify(warning);
|
|
1035
|
-
if (seen.has(key)) continue;
|
|
1036
|
-
seen.add(key);
|
|
1037
|
-
merged.push(warning);
|
|
1038
|
-
}
|
|
1039
|
-
return merged;
|
|
1040
|
-
};
|
|
1041
|
-
var mergeProviderMetadata = (left, right) => {
|
|
1042
|
-
if (!left) return right;
|
|
1043
|
-
if (!right) return left;
|
|
1044
|
-
return {
|
|
1045
|
-
...left,
|
|
1046
|
-
...right
|
|
1047
|
-
};
|
|
1048
|
-
};
|
|
1049
|
-
var emitReasonStructuredOutput = (args) => {
|
|
1050
|
-
if (!args.emit || !shouldEmitStructured(args.structured)) return;
|
|
1051
|
-
emitStructuredData({
|
|
1052
|
-
dataType: args.structured?.dataType ?? "reason-output",
|
|
1053
|
-
data: args.output,
|
|
1054
|
-
kind: "final",
|
|
1055
|
-
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
1056
|
-
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
1057
|
-
...args.id ? { id: args.id } : {},
|
|
1058
|
-
streamId: args.opId
|
|
1059
|
-
});
|
|
1060
|
-
};
|
|
1061
|
-
var resolveAppendFieldPaths = (cfg) => {
|
|
1062
|
-
const fields = cfg?.fields;
|
|
1063
|
-
if (!fields) return [];
|
|
1064
|
-
const appendPaths = Object.entries(fields).filter(([, mode]) => mode === "append").map(([path]) => path);
|
|
1065
|
-
for (const path of appendPaths) {
|
|
1066
|
-
assertStructuredPath(path, "useReason structured append");
|
|
1067
|
-
}
|
|
1068
|
-
return appendPaths;
|
|
1069
|
-
};
|
|
1070
|
-
var resolveSetFieldPaths = (cfg) => {
|
|
1071
|
-
const fields = cfg?.fields;
|
|
1072
|
-
if (!fields) return [];
|
|
1073
|
-
const setPaths = Object.entries(fields).filter(([, mode]) => mode === "set").map(([path]) => path);
|
|
1074
|
-
for (const path of setPaths) {
|
|
1075
|
-
assertStructuredPath(path, "useReason structured set");
|
|
1076
|
-
}
|
|
1077
|
-
return setPaths;
|
|
1078
|
-
};
|
|
1079
|
-
var resolveTextDeltaFieldPaths = (cfg) => {
|
|
1080
|
-
const fields = cfg?.fields;
|
|
1081
|
-
if (!fields) return [];
|
|
1082
|
-
const textPaths = Object.entries(fields).filter(([, mode]) => mode === "text-delta").map(([path]) => path);
|
|
1083
|
-
for (const path of textPaths) {
|
|
1084
|
-
assertStructuredPath(path, "useReason structured text-delta");
|
|
1085
|
-
}
|
|
1086
|
-
return textPaths;
|
|
1087
|
-
};
|
|
1088
|
-
var extractCompletedStringValueAt = (args) => {
|
|
1089
|
-
let value = "";
|
|
1090
|
-
for (let i = args.start; i < args.text.length; i += 1) {
|
|
1091
|
-
const ch = args.text[i];
|
|
1092
|
-
if (ch === void 0) break;
|
|
1093
|
-
if (ch === '"') {
|
|
1094
|
-
return {
|
|
1095
|
-
complete: true,
|
|
1096
|
-
value
|
|
1097
|
-
};
|
|
1098
|
-
}
|
|
1099
|
-
if (ch !== "\\") {
|
|
1100
|
-
value += ch;
|
|
1101
|
-
continue;
|
|
1102
|
-
}
|
|
1103
|
-
const next = args.text[i + 1];
|
|
1104
|
-
if (!next) {
|
|
1105
|
-
return {
|
|
1106
|
-
complete: false
|
|
1107
|
-
};
|
|
1108
|
-
}
|
|
1109
|
-
if (next === '"' || next === "\\" || next === "/" || next === "b" || next === "f" || next === "n" || next === "r" || next === "t") {
|
|
1110
|
-
value += next === "b" ? "\b" : next === "f" ? "\f" : next === "n" ? "\n" : next === "r" ? "\r" : next === "t" ? " " : next;
|
|
1111
|
-
i += 1;
|
|
1112
|
-
continue;
|
|
1113
|
-
}
|
|
1114
|
-
if (next === "u") {
|
|
1115
|
-
const hex = args.text.slice(i + 2, i + 6);
|
|
1116
|
-
if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
|
|
1117
|
-
return {
|
|
1118
|
-
complete: false
|
|
1119
|
-
};
|
|
1120
|
-
}
|
|
1121
|
-
value += String.fromCharCode(Number.parseInt(hex, 16));
|
|
1122
|
-
i += 5;
|
|
1123
|
-
continue;
|
|
1124
|
-
}
|
|
1125
|
-
return {
|
|
1126
|
-
complete: false
|
|
1127
|
-
};
|
|
1128
|
-
}
|
|
1129
|
-
return {
|
|
1130
|
-
complete: false
|
|
1131
|
-
};
|
|
1132
|
-
};
|
|
1133
|
-
var skipWhitespace = (text, start) => {
|
|
1134
|
-
let i = start;
|
|
1135
|
-
while (i < text.length && /\s/.test(text[i] ?? "")) i += 1;
|
|
1136
|
-
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;
|
|
1137
868
|
};
|
|
1138
869
|
var pathSegmentMatches = (patternPart, actualPart) => patternPart === "*" || patternPart === actualPart;
|
|
1139
870
|
var pathMatchesPattern = (actualParts, patternParts) => actualParts.length === patternParts.length && actualParts.every(
|
|
@@ -1517,103 +1248,552 @@ ${tryStringify2(args.response)}`
|
|
|
1517
1248
|
}
|
|
1518
1249
|
return items;
|
|
1519
1250
|
};
|
|
1520
|
-
var extractCompletedArrayItemGroups = (args) => {
|
|
1521
|
-
return locateValueStarts(args).flatMap((match) => {
|
|
1522
|
-
const items = extractCompletedArrayItems({
|
|
1523
|
-
text: args.text,
|
|
1524
|
-
path: match.path
|
|
1525
|
-
});
|
|
1526
|
-
return items.length > 0 ? [
|
|
1527
|
-
{
|
|
1528
|
-
path: match.path,
|
|
1529
|
-
items
|
|
1530
|
-
}
|
|
1531
|
-
] : [];
|
|
1532
|
-
});
|
|
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
|
+
function createStructuredOutputStreamer(args, id, opId) {
|
|
1351
|
+
let firstText = "";
|
|
1352
|
+
const setFieldPaths = resolveSetFieldPaths(args.structured);
|
|
1353
|
+
const appendFieldPaths = resolveAppendFieldPaths(args.structured);
|
|
1354
|
+
const textDeltaFieldPaths = resolveTextDeltaFieldPaths(args.structured);
|
|
1355
|
+
const emittedSetValues = /* @__PURE__ */ new Map();
|
|
1356
|
+
const emittedAppendCounts = /* @__PURE__ */ new Map();
|
|
1357
|
+
const emittedTextValues = /* @__PURE__ */ new Map();
|
|
1358
|
+
return (delta) => {
|
|
1359
|
+
firstText += delta;
|
|
1360
|
+
for (const fieldPath of setFieldPaths) {
|
|
1361
|
+
const values = extractCompletedFieldValues({
|
|
1362
|
+
text: firstText,
|
|
1363
|
+
path: fieldPath
|
|
1364
|
+
});
|
|
1365
|
+
for (const { path, value } of values) {
|
|
1366
|
+
const nextSerialized = JSON.stringify(value);
|
|
1367
|
+
if (emittedSetValues.get(path) === nextSerialized) continue;
|
|
1368
|
+
emittedSetValues.set(path, nextSerialized);
|
|
1369
|
+
emitStructuredData({
|
|
1370
|
+
kind: "set",
|
|
1371
|
+
path,
|
|
1372
|
+
value,
|
|
1373
|
+
dataType: args.structured?.dataType ?? "reason-output",
|
|
1374
|
+
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
1375
|
+
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
1376
|
+
...id ? { id } : {},
|
|
1377
|
+
streamId: opId
|
|
1378
|
+
});
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
for (const fieldPath of appendFieldPaths) {
|
|
1382
|
+
const groups = extractCompletedArrayItemGroups({
|
|
1383
|
+
text: firstText,
|
|
1384
|
+
path: fieldPath
|
|
1385
|
+
});
|
|
1386
|
+
for (const { path, items } of groups) {
|
|
1387
|
+
const emittedAppendCount = emittedAppendCounts.get(path) ?? 0;
|
|
1388
|
+
if (items.length <= emittedAppendCount) continue;
|
|
1389
|
+
const nextItems = items.slice(emittedAppendCount);
|
|
1390
|
+
emittedAppendCounts.set(path, items.length);
|
|
1391
|
+
emitStructuredData({
|
|
1392
|
+
kind: "append",
|
|
1393
|
+
path,
|
|
1394
|
+
items: nextItems,
|
|
1395
|
+
dataType: args.structured?.dataType ?? "reason-output",
|
|
1396
|
+
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
1397
|
+
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
1398
|
+
...id ? { id } : {},
|
|
1399
|
+
streamId: opId
|
|
1400
|
+
});
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
for (const fieldPath of textDeltaFieldPaths) {
|
|
1404
|
+
const values = extractStreamingStringValues({
|
|
1405
|
+
text: firstText,
|
|
1406
|
+
path: fieldPath
|
|
1407
|
+
});
|
|
1408
|
+
for (const { path, value } of values) {
|
|
1409
|
+
const emittedTextValue = emittedTextValues.get(path) ?? "";
|
|
1410
|
+
if (value.length <= emittedTextValue.length || !value.startsWith(emittedTextValue)) {
|
|
1411
|
+
continue;
|
|
1412
|
+
}
|
|
1413
|
+
const nextDelta = value.slice(emittedTextValue.length);
|
|
1414
|
+
emittedTextValues.set(path, value);
|
|
1415
|
+
if (nextDelta.length === 0) continue;
|
|
1416
|
+
emitStructuredData({
|
|
1417
|
+
kind: "text-delta",
|
|
1418
|
+
path,
|
|
1419
|
+
delta: nextDelta,
|
|
1420
|
+
dataType: args.structured?.dataType ?? "reason-output",
|
|
1421
|
+
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
1422
|
+
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
1423
|
+
...id ? { id } : {},
|
|
1424
|
+
streamId: opId
|
|
1425
|
+
});
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
};
|
|
1429
|
+
}
|
|
1430
|
+
var tryJsonParse = (value) => {
|
|
1431
|
+
try {
|
|
1432
|
+
return { ok: true, value: JSON.parse(value) };
|
|
1433
|
+
} catch (error) {
|
|
1434
|
+
return {
|
|
1435
|
+
ok: false,
|
|
1436
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1437
|
+
};
|
|
1438
|
+
}
|
|
1439
|
+
};
|
|
1440
|
+
var extractJsonCodeBlock = (value) => {
|
|
1441
|
+
const match = value.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
1442
|
+
if (!match || typeof match[1] !== "string") return null;
|
|
1443
|
+
const inner = match[1].trim();
|
|
1444
|
+
return inner.length > 0 ? inner : null;
|
|
1445
|
+
};
|
|
1446
|
+
var tryStringify = (value) => {
|
|
1447
|
+
if (typeof value === "string") return value;
|
|
1448
|
+
try {
|
|
1449
|
+
return JSON.stringify(value, null, 2);
|
|
1450
|
+
} catch {
|
|
1451
|
+
return String(value);
|
|
1452
|
+
}
|
|
1453
|
+
};
|
|
1454
|
+
var isRecord22 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1455
|
+
var resolveOutputCandidate = (text) => {
|
|
1456
|
+
const trimmed = text.trim();
|
|
1457
|
+
if (!trimmed) return text;
|
|
1458
|
+
const direct = tryJsonParse(trimmed);
|
|
1459
|
+
if (direct.ok) return direct.value;
|
|
1460
|
+
const block = extractJsonCodeBlock(trimmed);
|
|
1461
|
+
if (block) {
|
|
1462
|
+
const parsedBlock = tryJsonParse(block);
|
|
1463
|
+
if (parsedBlock.ok) return parsedBlock.value;
|
|
1464
|
+
}
|
|
1465
|
+
return text;
|
|
1466
|
+
};
|
|
1467
|
+
var looksLikeStructuredJsonAttempt = (value) => {
|
|
1468
|
+
const trimmed = value.trim();
|
|
1469
|
+
if (!trimmed) return false;
|
|
1470
|
+
return trimmed.startsWith("{") || trimmed.startsWith("[") || /^```(?:json)?/i.test(trimmed);
|
|
1471
|
+
};
|
|
1472
|
+
var isLikelyTruncatedStructuredOutput = (text) => {
|
|
1473
|
+
const trimmed = text.trim();
|
|
1474
|
+
if (!looksLikeStructuredJsonAttempt(trimmed)) return false;
|
|
1475
|
+
const direct = tryJsonParse(trimmed);
|
|
1476
|
+
if (!direct.ok && direct.error) return true;
|
|
1477
|
+
const block = extractJsonCodeBlock(trimmed);
|
|
1478
|
+
if (!block) return false;
|
|
1479
|
+
const parsedBlock = tryJsonParse(block);
|
|
1480
|
+
return !parsedBlock.ok && Boolean(parsedBlock.error);
|
|
1481
|
+
};
|
|
1482
|
+
var createTruncatedStructuredOutputError = (label) => new Error(
|
|
1483
|
+
`${label} was truncated before producing valid structured output. The model stopped due to output length. Increase maxOutputTokens or simplify the requested output.`
|
|
1484
|
+
);
|
|
1485
|
+
var createInvalidStructuredOutputError = (label) => new Error(
|
|
1486
|
+
`${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.`
|
|
1487
|
+
);
|
|
1488
|
+
var parseReasonOutputWithSchema = (args) => {
|
|
1489
|
+
const candidate = resolveOutputCandidate(args.text);
|
|
1490
|
+
try {
|
|
1491
|
+
return parseWithSchema(args.schema, candidate, args.label);
|
|
1492
|
+
} catch (error) {
|
|
1493
|
+
if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
|
|
1494
|
+
throw createTruncatedStructuredOutputError(args.label);
|
|
1495
|
+
}
|
|
1496
|
+
if (typeof candidate === "string" && candidate.trim().length > 0) {
|
|
1497
|
+
throw createInvalidStructuredOutputError(args.label);
|
|
1498
|
+
}
|
|
1499
|
+
throw error;
|
|
1500
|
+
}
|
|
1501
|
+
};
|
|
1502
|
+
var parseInterruptFirstPassResult = (args) => {
|
|
1503
|
+
const candidate = resolveOutputCandidate(args.text);
|
|
1504
|
+
if (!isRecord22(candidate)) {
|
|
1505
|
+
if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
|
|
1506
|
+
throw createTruncatedStructuredOutputError("useReason output");
|
|
1507
|
+
}
|
|
1508
|
+
if (typeof candidate === "string" && candidate.trim().length > 0) {
|
|
1509
|
+
throw createInvalidStructuredOutputError("useReason output");
|
|
1510
|
+
}
|
|
1511
|
+
throw new Error(
|
|
1512
|
+
"useReason first pass with interrupt must return a JSON object."
|
|
1513
|
+
);
|
|
1514
|
+
}
|
|
1515
|
+
const mode = args.mode ?? "required";
|
|
1516
|
+
let interruptRequired = true;
|
|
1517
|
+
if (mode === "optional") {
|
|
1518
|
+
if (candidate.decision !== "continue" && candidate.decision !== "interrupt") {
|
|
1519
|
+
throw new Error(
|
|
1520
|
+
'useReason optional interrupt first pass must include decision "continue" or "interrupt".'
|
|
1521
|
+
);
|
|
1522
|
+
}
|
|
1523
|
+
interruptRequired = candidate.decision === "interrupt";
|
|
1524
|
+
if (!interruptRequired) {
|
|
1525
|
+
const hasUnexpectedRequest = [
|
|
1526
|
+
candidate.interruptRequest,
|
|
1527
|
+
candidate.request,
|
|
1528
|
+
candidate.interrupt
|
|
1529
|
+
].some((value) => value !== null && value !== void 0);
|
|
1530
|
+
if (hasUnexpectedRequest) {
|
|
1531
|
+
throw new Error(
|
|
1532
|
+
'useReason optional interrupt first pass with decision "continue" must not include an interrupt request.'
|
|
1533
|
+
);
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
let request;
|
|
1538
|
+
if (interruptRequired) {
|
|
1539
|
+
const requestCandidates = [
|
|
1540
|
+
candidate.interruptRequest,
|
|
1541
|
+
candidate.request,
|
|
1542
|
+
candidate.interrupt,
|
|
1543
|
+
mode === "required" ? candidate : void 0
|
|
1544
|
+
];
|
|
1545
|
+
const requestPayload = requestCandidates.find(
|
|
1546
|
+
(value) => value !== null && value !== void 0
|
|
1547
|
+
);
|
|
1548
|
+
request = parseWithSchema(
|
|
1549
|
+
args.requestSchema,
|
|
1550
|
+
requestPayload,
|
|
1551
|
+
"useReason interrupt.request"
|
|
1552
|
+
);
|
|
1553
|
+
}
|
|
1554
|
+
let output;
|
|
1555
|
+
if (args.outputSchema) {
|
|
1556
|
+
const outputPayload = Object.hasOwn(candidate, "output") ? candidate.output : candidate;
|
|
1557
|
+
try {
|
|
1558
|
+
output = parseWithSchema(
|
|
1559
|
+
args.outputSchema,
|
|
1560
|
+
outputPayload,
|
|
1561
|
+
"useReason output"
|
|
1562
|
+
);
|
|
1563
|
+
} catch (error) {
|
|
1564
|
+
if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
|
|
1565
|
+
throw createTruncatedStructuredOutputError("useReason output");
|
|
1566
|
+
}
|
|
1567
|
+
if (typeof outputPayload === "string" && outputPayload.trim().length > 0) {
|
|
1568
|
+
throw createInvalidStructuredOutputError("useReason output");
|
|
1569
|
+
}
|
|
1570
|
+
throw error;
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
const explicitDraft = typeof candidate.draftText === "string" && candidate.draftText.length > 0 ? candidate.draftText : typeof candidate.text === "string" && candidate.text.length > 0 ? candidate.text : void 0;
|
|
1574
|
+
if (!args.outputSchema && !explicitDraft) {
|
|
1575
|
+
throw new Error(
|
|
1576
|
+
"useReason first pass with interrupt requires `draftText` when outputSchema is not provided."
|
|
1577
|
+
);
|
|
1578
|
+
}
|
|
1579
|
+
const draftText = explicitDraft ?? (output !== void 0 ? tryStringify(output) : String(args.text ?? ""));
|
|
1580
|
+
if (!interruptRequired) {
|
|
1581
|
+
return {
|
|
1582
|
+
draftText,
|
|
1583
|
+
interruptRequired: false,
|
|
1584
|
+
...output !== void 0 ? { output } : {}
|
|
1585
|
+
};
|
|
1586
|
+
}
|
|
1587
|
+
return {
|
|
1588
|
+
draftText,
|
|
1589
|
+
interruptRequired: true,
|
|
1590
|
+
request,
|
|
1591
|
+
...output !== void 0 ? { output } : {}
|
|
1592
|
+
};
|
|
1593
|
+
};
|
|
1594
|
+
var import_zod32 = require("zod");
|
|
1595
|
+
var tryStringify2 = (value) => {
|
|
1596
|
+
if (typeof value === "string") return value;
|
|
1597
|
+
try {
|
|
1598
|
+
return JSON.stringify(value, null, 2);
|
|
1599
|
+
} catch {
|
|
1600
|
+
return String(value);
|
|
1601
|
+
}
|
|
1602
|
+
};
|
|
1603
|
+
var tryGetSchemaHint = (schema) => {
|
|
1604
|
+
try {
|
|
1605
|
+
const hint = (0, import_zod32.toJSONSchema)(schema);
|
|
1606
|
+
return JSON.stringify(hint, null, 2);
|
|
1607
|
+
} catch {
|
|
1608
|
+
return void 0;
|
|
1609
|
+
}
|
|
1610
|
+
};
|
|
1611
|
+
var withSchemaHint = (input, label, schema) => {
|
|
1612
|
+
const hint = tryGetSchemaHint(schema);
|
|
1613
|
+
if (!hint) return input;
|
|
1614
|
+
return `${input}
|
|
1615
|
+
|
|
1616
|
+
${label}:
|
|
1617
|
+
${hint}`;
|
|
1618
|
+
};
|
|
1619
|
+
var defaultInterruptFirstPassInput = (args) => {
|
|
1620
|
+
const mode = args.mode ?? "required";
|
|
1621
|
+
const base = mode === "optional" ? args.outputSchema ? [
|
|
1622
|
+
args.input,
|
|
1623
|
+
"Output rules:",
|
|
1624
|
+
"- Return JSON only. No markdown fences.",
|
|
1625
|
+
"- Return an object with keys: decision, output, interruptRequest, draftText.",
|
|
1626
|
+
'- decision must be either "continue" or "interrupt".',
|
|
1627
|
+
'- Use decision "continue" when you can produce the final output without asking the user.',
|
|
1628
|
+
'- Use decision "interrupt" only when user input is needed before finalizing.',
|
|
1629
|
+
"- output must match the expected output JSON schema.",
|
|
1630
|
+
'- When decision is "interrupt", interruptRequest must match the expected interrupt request JSON schema and must be a non-null object.',
|
|
1631
|
+
'- When decision is "continue", interruptRequest must be null.',
|
|
1632
|
+
"- draftText should be a concise plain-language summary of output."
|
|
1633
|
+
].join("\n\n") : [
|
|
1634
|
+
args.input,
|
|
1635
|
+
"Output rules:",
|
|
1636
|
+
"- Return JSON only. No markdown fences.",
|
|
1637
|
+
"- Return an object with keys: decision, draftText, interruptRequest.",
|
|
1638
|
+
'- decision must be either "continue" or "interrupt".',
|
|
1639
|
+
'- Use decision "continue" when you can produce the final answer without asking the user.',
|
|
1640
|
+
'- Use decision "interrupt" only when user input is needed before finalizing.',
|
|
1641
|
+
"- draftText should be the current answer text.",
|
|
1642
|
+
'- When decision is "interrupt", interruptRequest must match the expected interrupt request JSON schema and must be a non-null object.',
|
|
1643
|
+
'- When decision is "continue", interruptRequest must be null.'
|
|
1644
|
+
].join("\n\n") : args.outputSchema ? [
|
|
1645
|
+
args.input,
|
|
1646
|
+
"Output rules:",
|
|
1647
|
+
"- Return JSON only. No markdown fences.",
|
|
1648
|
+
"- Return an object with keys: output, interruptRequest, draftText.",
|
|
1649
|
+
"- output must match the expected output JSON schema.",
|
|
1650
|
+
"- interruptRequest must match the expected interrupt request JSON schema.",
|
|
1651
|
+
"- interruptRequest must be a non-null object.",
|
|
1652
|
+
"- draftText should be a concise plain-language summary of output.",
|
|
1653
|
+
"- If uncertain, still return a valid interruptRequest with safe default options."
|
|
1654
|
+
].join("\n\n") : [
|
|
1655
|
+
args.input,
|
|
1656
|
+
"Output rules:",
|
|
1657
|
+
"- Return JSON only. No markdown fences.",
|
|
1658
|
+
"- Return an object with keys: draftText, interruptRequest.",
|
|
1659
|
+
"- draftText should be the current draft answer text.",
|
|
1660
|
+
"- interruptRequest must match the expected interrupt request JSON schema.",
|
|
1661
|
+
"- interruptRequest must be a non-null object.",
|
|
1662
|
+
"- If uncertain, still return a valid interruptRequest with safe default options."
|
|
1663
|
+
].join("\n\n");
|
|
1664
|
+
const withInterruptSchema = withSchemaHint(
|
|
1665
|
+
base,
|
|
1666
|
+
"Expected interrupt request JSON schema (for `interruptRequest`)",
|
|
1667
|
+
args.requestSchema
|
|
1668
|
+
);
|
|
1669
|
+
if (!args.outputSchema) return withInterruptSchema;
|
|
1670
|
+
const withOutputSchema = withSchemaHint(
|
|
1671
|
+
withInterruptSchema,
|
|
1672
|
+
"Expected output JSON schema (for `output`)",
|
|
1673
|
+
args.outputSchema
|
|
1674
|
+
);
|
|
1675
|
+
return [
|
|
1676
|
+
withOutputSchema,
|
|
1677
|
+
"JSON template:",
|
|
1678
|
+
mode === "optional" ? '{ "decision": "continue", "output": { ... }, "interruptRequest": null, "draftText": "..." }' : '{ "output": { ... }, "interruptRequest": { ... }, "draftText": "..." }'
|
|
1679
|
+
].join("\n\n");
|
|
1680
|
+
};
|
|
1681
|
+
var defaultContinuationInput = (args) => [
|
|
1682
|
+
"You are continuing a paused reasoning task after user input.",
|
|
1683
|
+
"Update the previous result using the user response and return the final answer only.",
|
|
1684
|
+
"If the previous output was JSON, return JSON in the same schema.",
|
|
1685
|
+
"",
|
|
1686
|
+
`Original request:
|
|
1687
|
+
${args.input}`,
|
|
1688
|
+
`Draft answer text:
|
|
1689
|
+
${args.draftText}`,
|
|
1690
|
+
`Draft output object:
|
|
1691
|
+
${tryStringify2(args.draftOutput ?? args.draftText)}`,
|
|
1692
|
+
`Interrupt request shown to user:
|
|
1693
|
+
${tryStringify2(args.request)}`,
|
|
1694
|
+
`User response:
|
|
1695
|
+
${tryStringify2(args.response)}`
|
|
1696
|
+
].join("\n\n");
|
|
1697
|
+
var withOutputGuardrails = (input, schema) => {
|
|
1698
|
+
const base = [
|
|
1699
|
+
input,
|
|
1700
|
+
"Output rules:",
|
|
1701
|
+
"- Return only JSON, no markdown fences.",
|
|
1702
|
+
"- Ensure the response conforms to the expected output schema.",
|
|
1703
|
+
"- Do not include explanatory text outside the JSON object."
|
|
1704
|
+
].join("\n\n");
|
|
1705
|
+
return withSchemaHint(base, "Expected output JSON schema", schema);
|
|
1533
1706
|
};
|
|
1534
|
-
var
|
|
1535
|
-
const
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
value: ""
|
|
1541
|
-
};
|
|
1707
|
+
var withStructuredStreamHints = (input, args) => {
|
|
1708
|
+
const rules = [];
|
|
1709
|
+
for (const path of args.setFieldPaths ?? []) {
|
|
1710
|
+
rules.push(
|
|
1711
|
+
`- For the field path pattern \`${path}\`, write one stable final value and do not rewrite it after it is complete.`
|
|
1712
|
+
);
|
|
1542
1713
|
}
|
|
1543
|
-
const
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1714
|
+
for (const path of args.appendFieldPaths ?? []) {
|
|
1715
|
+
rules.push(
|
|
1716
|
+
`- For the array field path pattern \`${path}\`, write items in final order and finish each item before starting the next.`
|
|
1717
|
+
);
|
|
1718
|
+
rules.push(
|
|
1719
|
+
`- Once an item in \`${path}\` is complete, do not rewrite or reorder earlier items.`
|
|
1720
|
+
);
|
|
1550
1721
|
}
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
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
|
-
|
|
1722
|
+
for (const path of args.textDeltaFieldPaths ?? []) {
|
|
1723
|
+
rules.push(
|
|
1724
|
+
`- For the string field path pattern \`${path}\`, write the value progressively by appending text to the end.`
|
|
1725
|
+
);
|
|
1726
|
+
rules.push(
|
|
1727
|
+
`- Do not rewrite or replace the earlier prefix of \`${path}\` once emitted.`
|
|
1728
|
+
);
|
|
1729
|
+
}
|
|
1730
|
+
if (rules.length === 0) return input;
|
|
1731
|
+
return [
|
|
1732
|
+
input,
|
|
1733
|
+
"Streaming rules:",
|
|
1734
|
+
...rules,
|
|
1735
|
+
"- Keep the overall response as valid JSON."
|
|
1736
|
+
].join("\n\n");
|
|
1737
|
+
};
|
|
1738
|
+
var mergeUsage = (left, right) => {
|
|
1739
|
+
if (!left) return right;
|
|
1740
|
+
if (!right) return left;
|
|
1741
|
+
const sum = (a, b) => a === void 0 && b === void 0 ? void 0 : (a ?? 0) + (b ?? 0);
|
|
1742
|
+
const raw = left.raw || right.raw ? {
|
|
1743
|
+
...left.raw ?? {},
|
|
1744
|
+
...right.raw ?? {}
|
|
1745
|
+
} : void 0;
|
|
1746
|
+
const input = sum(left.input, right.input);
|
|
1747
|
+
const output = sum(left.output, right.output);
|
|
1748
|
+
const total = sum(left.total, right.total);
|
|
1749
|
+
const reasoning = sum(left.reasoning, right.reasoning);
|
|
1750
|
+
const cacheRead = sum(left.cacheRead, right.cacheRead);
|
|
1751
|
+
const cacheWrite = sum(left.cacheWrite, right.cacheWrite);
|
|
1752
|
+
const merged = {
|
|
1753
|
+
...right.outputIncludesReasoning !== void 0 ? { outputIncludesReasoning: right.outputIncludesReasoning } : {},
|
|
1754
|
+
...right.inputIncludesCacheWrite !== void 0 ? { inputIncludesCacheWrite: right.inputIncludesCacheWrite } : {},
|
|
1755
|
+
...right.inputIncludesCacheRead !== void 0 ? { inputIncludesCacheRead: right.inputIncludesCacheRead } : {}
|
|
1756
|
+
};
|
|
1757
|
+
if (input !== void 0) merged.input = input;
|
|
1758
|
+
if (output !== void 0) merged.output = output;
|
|
1759
|
+
if (total !== void 0) merged.total = total;
|
|
1760
|
+
if (reasoning !== void 0) merged.reasoning = reasoning;
|
|
1761
|
+
if (cacheRead !== void 0) merged.cacheRead = cacheRead;
|
|
1762
|
+
if (cacheWrite !== void 0) merged.cacheWrite = cacheWrite;
|
|
1763
|
+
if (raw) merged.raw = raw;
|
|
1764
|
+
return merged;
|
|
1765
|
+
};
|
|
1766
|
+
var mergeWarnings = (left, right) => {
|
|
1767
|
+
if (!left?.length) return right;
|
|
1768
|
+
if (!right?.length) return left;
|
|
1769
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1770
|
+
const merged = [];
|
|
1771
|
+
for (const warning of [...left, ...right]) {
|
|
1772
|
+
const key = JSON.stringify(warning);
|
|
1773
|
+
if (seen.has(key)) continue;
|
|
1774
|
+
seen.add(key);
|
|
1775
|
+
merged.push(warning);
|
|
1597
1776
|
}
|
|
1777
|
+
return merged;
|
|
1778
|
+
};
|
|
1779
|
+
var mergeProviderMetadata = (left, right) => {
|
|
1780
|
+
if (!left) return right;
|
|
1781
|
+
if (!right) return left;
|
|
1598
1782
|
return {
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
value
|
|
1783
|
+
...left,
|
|
1784
|
+
...right
|
|
1602
1785
|
};
|
|
1603
1786
|
};
|
|
1604
|
-
var
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
value: current.value
|
|
1615
|
-
}
|
|
1616
|
-
] : [];
|
|
1787
|
+
var emitReasonStructuredOutput = (args) => {
|
|
1788
|
+
if (!args.emit || !shouldEmitStructured(args.structured)) return;
|
|
1789
|
+
emitStructuredData({
|
|
1790
|
+
dataType: args.structured?.dataType ?? "reason-output",
|
|
1791
|
+
data: args.output,
|
|
1792
|
+
kind: "final",
|
|
1793
|
+
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
1794
|
+
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
1795
|
+
...args.id ? { id: args.id } : {},
|
|
1796
|
+
streamId: args.opId
|
|
1617
1797
|
});
|
|
1618
1798
|
};
|
|
1619
1799
|
var import_core42 = require("@kortyx/core");
|
|
@@ -1668,13 +1848,16 @@ ${tryStringify2(args.response)}`
|
|
|
1668
1848
|
{ role: "user", content: String(args.input ?? "") }
|
|
1669
1849
|
];
|
|
1670
1850
|
var runReasonToolLoop = async (args) => {
|
|
1671
|
-
const { useReasonArgs, id,
|
|
1851
|
+
const { useReasonArgs, id, traceSpan, checkpointKey } = args;
|
|
1672
1852
|
const ctx = getHookContext();
|
|
1673
1853
|
const abortSignal = (0, import_core42.combineAbortSignals)(
|
|
1674
1854
|
ctx.node.abortSignal,
|
|
1675
1855
|
useReasonArgs.abortSignal,
|
|
1676
1856
|
useReasonArgs.model.options?.abortSignal
|
|
1677
1857
|
);
|
|
1858
|
+
const saved = ctx.currentNodeState.byKey[checkpointKey];
|
|
1859
|
+
const checkpoint = saved?.status === "tool_loop" ? saved : void 0;
|
|
1860
|
+
const opId = checkpoint?.opId ?? args.opId;
|
|
1678
1861
|
const tools = useReasonArgs.tools ?? [];
|
|
1679
1862
|
const toolByName = /* @__PURE__ */ new Map();
|
|
1680
1863
|
let validationCompleted = false;
|
|
@@ -1700,20 +1883,43 @@ ${tryStringify2(args.response)}`
|
|
|
1700
1883
|
}
|
|
1701
1884
|
const toolDefinitions = toToolDefinitions(tools);
|
|
1702
1885
|
const maxSteps = Math.max(1, useReasonArgs.toolExecution?.maxSteps ?? 3);
|
|
1703
|
-
const messages = createInitialMessages({
|
|
1886
|
+
const messages = checkpoint?.messages ?? createInitialMessages({
|
|
1704
1887
|
system: useReasonArgs.system,
|
|
1705
|
-
input: useReasonArgs.input
|
|
1888
|
+
input: useReasonArgs.outputSchema ? withOutputGuardrails(useReasonArgs.input, useReasonArgs.outputSchema) : useReasonArgs.input
|
|
1706
1889
|
});
|
|
1707
|
-
const steps = [];
|
|
1708
|
-
const allToolCalls =
|
|
1709
|
-
|
|
1890
|
+
const steps = checkpoint?.steps ?? [];
|
|
1891
|
+
const allToolCalls = steps.flatMap(
|
|
1892
|
+
(step) => step.toolCalls
|
|
1893
|
+
);
|
|
1894
|
+
const allToolResults = steps.flatMap(
|
|
1895
|
+
(step) => step.toolResults
|
|
1896
|
+
);
|
|
1710
1897
|
let finalText = "";
|
|
1711
1898
|
let finalRaw;
|
|
1712
1899
|
let finalOutput;
|
|
1713
|
-
let aggregatedUsage
|
|
1900
|
+
let aggregatedUsage = steps.reduce(
|
|
1901
|
+
(usage, step) => mergeUsage(usage, step.usage),
|
|
1902
|
+
void 0
|
|
1903
|
+
);
|
|
1714
1904
|
let finalFinishReason;
|
|
1715
1905
|
let aggregatedProviderMetadata;
|
|
1716
|
-
let aggregatedWarnings
|
|
1906
|
+
let aggregatedWarnings = steps.reduce(
|
|
1907
|
+
(warnings, step) => mergeWarnings(warnings, step.warnings),
|
|
1908
|
+
args.initialWarnings
|
|
1909
|
+
);
|
|
1910
|
+
let pending = checkpoint?.pending;
|
|
1911
|
+
const approvedCalls = checkpoint?.approvedCalls ?? [];
|
|
1912
|
+
const save = () => {
|
|
1913
|
+
ctx.currentNodeState.byKey[checkpointKey] = {
|
|
1914
|
+
status: "tool_loop",
|
|
1915
|
+
opId,
|
|
1916
|
+
messages,
|
|
1917
|
+
steps,
|
|
1918
|
+
approvedCalls,
|
|
1919
|
+
...pending ? { pending } : {}
|
|
1920
|
+
};
|
|
1921
|
+
ctx.stateDirty = true;
|
|
1922
|
+
};
|
|
1717
1923
|
const emitToolEvent = (event, payload) => {
|
|
1718
1924
|
ctx.node.emit(event, {
|
|
1719
1925
|
...payload,
|
|
@@ -1723,26 +1929,43 @@ ${tryStringify2(args.response)}`
|
|
|
1723
1929
|
});
|
|
1724
1930
|
};
|
|
1725
1931
|
try {
|
|
1726
|
-
for (let stepIndex =
|
|
1932
|
+
for (let stepIndex = pending ? steps.length - 1 : steps.length; stepIndex < maxSteps; stepIndex += 1) {
|
|
1727
1933
|
(0, import_core42.throwIfExecutionAborted)(abortSignal);
|
|
1728
1934
|
traceSpan?.addEvent?.("useReason.tool-step.start", {
|
|
1729
1935
|
stepIndex,
|
|
1730
1936
|
toolCount: toolDefinitions.length
|
|
1731
1937
|
});
|
|
1732
|
-
const
|
|
1938
|
+
const reused = Boolean(pending);
|
|
1939
|
+
const stream = useReasonArgs.stream ?? useReasonArgs.model.options?.streaming ?? true;
|
|
1940
|
+
const emit = useReasonArgs.emit ?? true;
|
|
1941
|
+
let textStarted = false;
|
|
1942
|
+
const structuredChunk = useReasonArgs.outputSchema && stream && emit && shouldStreamStructured(useReasonArgs.structured) ? createStructuredOutputStreamer(useReasonArgs, id, opId) : void 0;
|
|
1943
|
+
const step = pending ?? await reasonEngine(
|
|
1733
1944
|
{
|
|
1734
1945
|
...useReasonArgs,
|
|
1735
1946
|
tools: toolDefinitions,
|
|
1736
1947
|
messages,
|
|
1737
1948
|
emit: false,
|
|
1738
|
-
stream
|
|
1949
|
+
stream,
|
|
1950
|
+
onTextChunk: (delta) => {
|
|
1951
|
+
if (structuredChunk) structuredChunk(delta);
|
|
1952
|
+
if (!emit || useReasonArgs.outputSchema || !delta) return;
|
|
1953
|
+
if (!textStarted) {
|
|
1954
|
+
emitToolEvent("text-start", {});
|
|
1955
|
+
textStarted = true;
|
|
1956
|
+
}
|
|
1957
|
+
emitToolEvent("text-delta", { delta });
|
|
1958
|
+
}
|
|
1739
1959
|
},
|
|
1740
1960
|
{ ...id ? { id } : {}, opId }
|
|
1741
1961
|
);
|
|
1962
|
+
if (textStarted) emitToolEvent("text-end", {});
|
|
1742
1963
|
finalText = step.text;
|
|
1743
1964
|
finalRaw = step.raw;
|
|
1744
|
-
|
|
1745
|
-
|
|
1965
|
+
if (!reused) {
|
|
1966
|
+
aggregatedUsage = mergeUsage(aggregatedUsage, step.usage);
|
|
1967
|
+
accumulateTokenUsage(step.usage);
|
|
1968
|
+
}
|
|
1746
1969
|
finalFinishReason = step.finishReason;
|
|
1747
1970
|
aggregatedProviderMetadata = mergeProviderMetadata(
|
|
1748
1971
|
aggregatedProviderMetadata,
|
|
@@ -1750,32 +1973,35 @@ ${tryStringify2(args.response)}`
|
|
|
1750
1973
|
);
|
|
1751
1974
|
aggregatedWarnings = mergeWarnings(aggregatedWarnings, step.warnings);
|
|
1752
1975
|
const toolCalls = step.toolCalls ?? [];
|
|
1753
|
-
const toolResults = [];
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1976
|
+
const toolResults = reused ? steps[stepIndex]?.toolResults ?? [] : [];
|
|
1977
|
+
if (!reused)
|
|
1978
|
+
steps.push({
|
|
1979
|
+
stepIndex,
|
|
1980
|
+
text: step.text,
|
|
1981
|
+
toolCalls,
|
|
1982
|
+
toolResults,
|
|
1983
|
+
...step.usage ? { usage: step.usage } : {},
|
|
1984
|
+
...step.finishReason ? { finishReason: step.finishReason } : {},
|
|
1985
|
+
...step.providerMetadata ? { providerMetadata: step.providerMetadata } : {},
|
|
1986
|
+
...step.warnings ? { warnings: step.warnings } : {}
|
|
1987
|
+
});
|
|
1764
1988
|
if (toolCalls.length === 0) {
|
|
1765
|
-
if ((useReasonArgs.emit ?? true) && step.text.length > 0) {
|
|
1766
|
-
emitToolEvent("text-start", {});
|
|
1767
|
-
emitToolEvent("text-delta", { delta: step.text });
|
|
1768
|
-
emitToolEvent("text-end", {});
|
|
1769
|
-
}
|
|
1770
1989
|
break;
|
|
1771
1990
|
}
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1991
|
+
if (!reused) {
|
|
1992
|
+
allToolCalls.push(...toolCalls);
|
|
1993
|
+
messages.push({
|
|
1994
|
+
role: "assistant",
|
|
1995
|
+
content: step.text,
|
|
1996
|
+
toolCalls,
|
|
1997
|
+
...step.continuation ? { continuation: step.continuation } : {}
|
|
1998
|
+
});
|
|
1999
|
+
}
|
|
2000
|
+
pending = step;
|
|
2001
|
+
save();
|
|
1778
2002
|
for (const toolCall of toolCalls) {
|
|
2003
|
+
if (toolResults.some((result2) => result2.toolCallId === toolCall.id))
|
|
2004
|
+
continue;
|
|
1779
2005
|
(0, import_core42.throwIfExecutionAborted)(abortSignal);
|
|
1780
2006
|
const tool = toolByName.get(toolCall.name);
|
|
1781
2007
|
if (!tool) {
|
|
@@ -1805,7 +2031,7 @@ ${tryStringify2(args.response)}`
|
|
|
1805
2031
|
input: toolCall.input
|
|
1806
2032
|
});
|
|
1807
2033
|
}
|
|
1808
|
-
if (shouldApproveTool) {
|
|
2034
|
+
if (shouldApproveTool && !approvedCalls.includes(toolCall.id)) {
|
|
1809
2035
|
const approval = await awaitInterruptInternal({
|
|
1810
2036
|
id: `tool:${toolCall.id}`,
|
|
1811
2037
|
request: {
|
|
@@ -1840,8 +2066,18 @@ ${tryStringify2(args.response)}`
|
|
|
1840
2066
|
isError: true
|
|
1841
2067
|
});
|
|
1842
2068
|
}
|
|
2069
|
+
messages.push({
|
|
2070
|
+
role: "tool",
|
|
2071
|
+
content: denied.content,
|
|
2072
|
+
toolCallId: denied.toolCallId,
|
|
2073
|
+
name: denied.name,
|
|
2074
|
+
isError: true
|
|
2075
|
+
});
|
|
2076
|
+
save();
|
|
1843
2077
|
continue;
|
|
1844
2078
|
}
|
|
2079
|
+
approvedCalls.push(toolCall.id);
|
|
2080
|
+
save();
|
|
1845
2081
|
}
|
|
1846
2082
|
try {
|
|
1847
2083
|
(0, import_core42.throwIfExecutionAborted)(abortSignal);
|
|
@@ -1895,25 +2131,19 @@ ${tryStringify2(args.response)}`
|
|
|
1895
2131
|
message: result2.content
|
|
1896
2132
|
});
|
|
1897
2133
|
}
|
|
2134
|
+
const completed = toolResults.at(-1);
|
|
2135
|
+
if (completed)
|
|
2136
|
+
messages.push({
|
|
2137
|
+
role: "tool",
|
|
2138
|
+
content: completed.content,
|
|
2139
|
+
toolCallId: completed.toolCallId,
|
|
2140
|
+
name: completed.name,
|
|
2141
|
+
...completed.isError !== void 0 ? { isError: completed.isError } : {}
|
|
2142
|
+
});
|
|
2143
|
+
save();
|
|
1898
2144
|
}
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
steps[steps.length - 1] = {
|
|
1902
|
-
...currentStep,
|
|
1903
|
-
toolResults
|
|
1904
|
-
};
|
|
1905
|
-
}
|
|
1906
|
-
for (const result2 of toolResults) {
|
|
1907
|
-
messages.push({
|
|
1908
|
-
role: "tool",
|
|
1909
|
-
content: result2.content,
|
|
1910
|
-
toolCallId: result2.toolCallId,
|
|
1911
|
-
name: result2.name,
|
|
1912
|
-
...result2.structuredContent !== void 0 ? { structuredContent: result2.structuredContent } : {},
|
|
1913
|
-
...result2.isError !== void 0 ? { isError: result2.isError } : {},
|
|
1914
|
-
...result2.raw !== void 0 ? { raw: result2.raw } : {}
|
|
1915
|
-
});
|
|
1916
|
-
}
|
|
2145
|
+
pending = void 0;
|
|
2146
|
+
save();
|
|
1917
2147
|
}
|
|
1918
2148
|
if (steps.length >= maxSteps && steps.at(-1)?.toolCalls.length) {
|
|
1919
2149
|
throw new Error(
|
|
@@ -1967,6 +2197,8 @@ ${tryStringify2(args.response)}`
|
|
|
1967
2197
|
output: useReasonArgs.telemetry?.output ?? finalOutput ?? finalText
|
|
1968
2198
|
}
|
|
1969
2199
|
});
|
|
2200
|
+
ctx.currentNodeState.byKey[checkpointKey] = { status: "completed", result };
|
|
2201
|
+
ctx.stateDirty = true;
|
|
1970
2202
|
return result;
|
|
1971
2203
|
} catch (error) {
|
|
1972
2204
|
traceSpan?.fail?.(error, {
|
|
@@ -2000,7 +2232,15 @@ ${tryStringify2(args.response)}`
|
|
|
2000
2232
|
args.model.options?.abortSignal
|
|
2001
2233
|
);
|
|
2002
2234
|
(0, import_core52.throwIfExecutionAborted)(abortSignal);
|
|
2003
|
-
|
|
2235
|
+
const inferred = inferOutputFormat(
|
|
2236
|
+
args.outputSchema,
|
|
2237
|
+
args.responseFormat ?? args.model.options?.responseFormat
|
|
2238
|
+
);
|
|
2239
|
+
args = {
|
|
2240
|
+
...args,
|
|
2241
|
+
abortSignal,
|
|
2242
|
+
...!args.interrupt && inferred.responseFormat ? { responseFormat: inferred.responseFormat } : {}
|
|
2243
|
+
};
|
|
2004
2244
|
const id = typeof args.id === "string" && args.id.length > 0 ? args.id : void 0;
|
|
2005
2245
|
const opId = createRuntimeId2();
|
|
2006
2246
|
const checkpointKey = resolveReasonCheckpointKey({
|
|
@@ -2044,6 +2284,8 @@ ${tryStringify2(args.response)}`
|
|
|
2044
2284
|
if (args.tools?.length) {
|
|
2045
2285
|
return runReasonToolLoop({
|
|
2046
2286
|
useReasonArgs: args,
|
|
2287
|
+
checkpointKey,
|
|
2288
|
+
initialWarnings: inferred.warnings,
|
|
2047
2289
|
...id ? { id } : {},
|
|
2048
2290
|
opId,
|
|
2049
2291
|
...traceSpan ? { traceSpan } : {}
|
|
@@ -2067,10 +2309,7 @@ ${tryStringify2(args.response)}`
|
|
|
2067
2309
|
let aggregatedUsage;
|
|
2068
2310
|
let finalFinishReason;
|
|
2069
2311
|
let aggregatedProviderMetadata;
|
|
2070
|
-
let aggregatedWarnings;
|
|
2071
|
-
const emittedSetValues = /* @__PURE__ */ new Map();
|
|
2072
|
-
const emittedAppendCounts = /* @__PURE__ */ new Map();
|
|
2073
|
-
const emittedTextValues = /* @__PURE__ */ new Map();
|
|
2312
|
+
let aggregatedWarnings = inferred.warnings;
|
|
2074
2313
|
if (existingCheckpoint) {
|
|
2075
2314
|
firstText = existingCheckpoint.firstText;
|
|
2076
2315
|
firstRaw = existingCheckpoint.firstRaw;
|
|
@@ -2092,78 +2331,7 @@ ${tryStringify2(args.response)}`
|
|
|
2092
2331
|
emit: suppressTextStream ? false : args.emit,
|
|
2093
2332
|
stream: useStructuredIncrementalStreaming ? true : suppressTextStream ? false : args.stream,
|
|
2094
2333
|
...useStructuredIncrementalStreaming ? {
|
|
2095
|
-
onTextChunk: (
|
|
2096
|
-
firstText += delta;
|
|
2097
|
-
finalText = firstText;
|
|
2098
|
-
for (const fieldPath of setFieldPaths) {
|
|
2099
|
-
const values = extractCompletedFieldValues({
|
|
2100
|
-
text: firstText,
|
|
2101
|
-
path: fieldPath
|
|
2102
|
-
});
|
|
2103
|
-
for (const { path, value } of values) {
|
|
2104
|
-
const nextSerialized = JSON.stringify(value);
|
|
2105
|
-
if (emittedSetValues.get(path) === nextSerialized) continue;
|
|
2106
|
-
emittedSetValues.set(path, nextSerialized);
|
|
2107
|
-
emitStructuredData({
|
|
2108
|
-
kind: "set",
|
|
2109
|
-
path,
|
|
2110
|
-
value,
|
|
2111
|
-
dataType: args.structured?.dataType ?? "reason-output",
|
|
2112
|
-
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
2113
|
-
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
2114
|
-
...id ? { id } : {},
|
|
2115
|
-
streamId: opId
|
|
2116
|
-
});
|
|
2117
|
-
}
|
|
2118
|
-
}
|
|
2119
|
-
for (const fieldPath of appendFieldPaths) {
|
|
2120
|
-
const groups = extractCompletedArrayItemGroups({
|
|
2121
|
-
text: firstText,
|
|
2122
|
-
path: fieldPath
|
|
2123
|
-
});
|
|
2124
|
-
for (const { path, items } of groups) {
|
|
2125
|
-
const emittedAppendCount = emittedAppendCounts.get(path) ?? 0;
|
|
2126
|
-
if (items.length <= emittedAppendCount) continue;
|
|
2127
|
-
const nextItems = items.slice(emittedAppendCount);
|
|
2128
|
-
emittedAppendCounts.set(path, items.length);
|
|
2129
|
-
emitStructuredData({
|
|
2130
|
-
kind: "append",
|
|
2131
|
-
path,
|
|
2132
|
-
items: nextItems,
|
|
2133
|
-
dataType: args.structured?.dataType ?? "reason-output",
|
|
2134
|
-
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
2135
|
-
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
2136
|
-
...id ? { id } : {},
|
|
2137
|
-
streamId: opId
|
|
2138
|
-
});
|
|
2139
|
-
}
|
|
2140
|
-
}
|
|
2141
|
-
for (const fieldPath of textDeltaFieldPaths) {
|
|
2142
|
-
const values = extractStreamingStringValues({
|
|
2143
|
-
text: firstText,
|
|
2144
|
-
path: fieldPath
|
|
2145
|
-
});
|
|
2146
|
-
for (const { path, value } of values) {
|
|
2147
|
-
const emittedTextValue = emittedTextValues.get(path) ?? "";
|
|
2148
|
-
if (value.length <= emittedTextValue.length || !value.startsWith(emittedTextValue)) {
|
|
2149
|
-
continue;
|
|
2150
|
-
}
|
|
2151
|
-
const nextDelta = value.slice(emittedTextValue.length);
|
|
2152
|
-
emittedTextValues.set(path, value);
|
|
2153
|
-
if (nextDelta.length === 0) continue;
|
|
2154
|
-
emitStructuredData({
|
|
2155
|
-
kind: "text-delta",
|
|
2156
|
-
path,
|
|
2157
|
-
delta: nextDelta,
|
|
2158
|
-
dataType: args.structured?.dataType ?? "reason-output",
|
|
2159
|
-
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
2160
|
-
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
2161
|
-
...id ? { id } : {},
|
|
2162
|
-
streamId: opId
|
|
2163
|
-
});
|
|
2164
|
-
}
|
|
2165
|
-
}
|
|
2166
|
-
}
|
|
2334
|
+
onTextChunk: createStructuredOutputStreamer(args, id, opId)
|
|
2167
2335
|
} : {}
|
|
2168
2336
|
},
|
|
2169
2337
|
reasonMeta,
|
|
@@ -2283,6 +2451,7 @@ ${tryStringify2(args.response)}`
|
|
|
2283
2451
|
const second = await reasonEngine(
|
|
2284
2452
|
{
|
|
2285
2453
|
...args,
|
|
2454
|
+
...inferred.responseFormat ? { responseFormat: inferred.responseFormat } : {},
|
|
2286
2455
|
emit: suppressTextStream ? false : args.emit,
|
|
2287
2456
|
stream: suppressTextStream ? false : args.stream
|
|
2288
2457
|
},
|
|
@@ -4629,7 +4798,7 @@ var responseSchema = import_zod3.z.discriminatedUnion("type", [
|
|
|
4629
4798
|
import_zod3.z.object({ type: import_zod3.z.literal("cancel") }).strict()
|
|
4630
4799
|
]);
|
|
4631
4800
|
function resultFromOutcome(outcome, runId, sessionId) {
|
|
4632
|
-
const usage = outcome.pending?.schema.meta?.__kortyxResumeStatePatch?.tokenUsage ?? outcome.state.runtime?.tokenUsage;
|
|
4801
|
+
const usage = outcome.pending?.schema.meta?.__kortyxResumeStatePatch?.tokenUsage ?? outcome.error?.__kortyxHookStatePatch?.tokenUsage ?? outcome.state.runtime?.tokenUsage;
|
|
4633
4802
|
const info = {
|
|
4634
4803
|
runId,
|
|
4635
4804
|
sessionId,
|