@kortyx/hooks 0.22.0 → 0.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/dist/index.js +916 -677
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +2 -1
- package/dist/internal.js +28 -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
|
}
|
|
@@ -426,8 +426,18 @@ var emitNodeEvent = (emitEvent, nodeId, event, payload) => {
|
|
|
426
426
|
...typeof nodeId === "string" && nodeId.length > 0 ? { node: nodeId } : {}
|
|
427
427
|
});
|
|
428
428
|
};
|
|
429
|
+
var resolveProviderOptions = (defaults, overrides) => {
|
|
430
|
+
const providerOptions = defaults || overrides ? { ...defaults, ...overrides } : void 0;
|
|
431
|
+
for (const [key, value] of Object.entries(overrides ?? {})) {
|
|
432
|
+
const previous = defaults?.[key];
|
|
433
|
+
if (previous && value && typeof previous === "object" && typeof value === "object" && !Array.isArray(previous) && !Array.isArray(value)) {
|
|
434
|
+
if (providerOptions) providerOptions[key] = { ...previous, ...value };
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
return providerOptions;
|
|
438
|
+
};
|
|
429
439
|
async function runReasonEngine(args) {
|
|
430
|
-
|
|
440
|
+
let stream = args.stream ?? args.model.options?.streaming ?? true;
|
|
431
441
|
const emit = args.emit ?? true;
|
|
432
442
|
const temperature = args.temperature ?? args.model.options?.temperature ?? args.defaultTemperature;
|
|
433
443
|
const maxOutputTokens = args.maxOutputTokens ?? args.model.options?.maxOutputTokens;
|
|
@@ -435,7 +445,10 @@ async function runReasonEngine(args) {
|
|
|
435
445
|
const abortSignal = args.abortSignal ?? args.model.options?.abortSignal;
|
|
436
446
|
const reasoning = args.reasoning ?? args.model.options?.reasoning;
|
|
437
447
|
const responseFormat = args.responseFormat ?? args.model.options?.responseFormat;
|
|
438
|
-
const providerOptions =
|
|
448
|
+
const providerOptions = resolveProviderOptions(
|
|
449
|
+
args.model.options?.providerOptions,
|
|
450
|
+
args.providerOptions
|
|
451
|
+
);
|
|
439
452
|
const tools = args.tools ?? args.model.options?.tools;
|
|
440
453
|
(0, import_core2.throwIfExecutionAborted)(abortSignal);
|
|
441
454
|
const model = args.model.provider.getModel(args.model.modelId, {
|
|
@@ -449,6 +462,7 @@ async function runReasonEngine(args) {
|
|
|
449
462
|
...tools !== void 0 ? { tools } : {},
|
|
450
463
|
...providerOptions !== void 0 ? { providerOptions } : {}
|
|
451
464
|
});
|
|
465
|
+
if (tools?.length && !model.supportsToolStreaming) stream = false;
|
|
452
466
|
const messages = args.messages !== void 0 ? args.messages : [
|
|
453
467
|
...typeof args.system === "string" && args.system.length > 0 ? [{ role: "system", content: args.system }] : [],
|
|
454
468
|
{ role: "user", content: String(args.input ?? "") }
|
|
@@ -476,7 +490,9 @@ async function runReasonEngine(args) {
|
|
|
476
490
|
},
|
|
477
491
|
telemetry: {
|
|
478
492
|
...args.telemetry ?? {},
|
|
479
|
-
input: args.telemetry?.input ?? messages
|
|
493
|
+
input: args.telemetry?.input ?? messages.map(
|
|
494
|
+
({ continuation: _continuation, raw: _raw, ...message }) => message
|
|
495
|
+
)
|
|
480
496
|
}
|
|
481
497
|
});
|
|
482
498
|
const modelRequestStartedAt = Date.now();
|
|
@@ -507,7 +523,11 @@ async function runReasonEngine(args) {
|
|
|
507
523
|
});
|
|
508
524
|
};
|
|
509
525
|
const failTrace = (span, error) => {
|
|
526
|
+
const detail = error;
|
|
510
527
|
span?.fail?.(error, {
|
|
528
|
+
...detail?.usage ? { usage: detail.usage } : {},
|
|
529
|
+
...detail?.finishReason ? { finishReason: detail.finishReason } : {},
|
|
530
|
+
...detail?.providerMetadata ? { providerMetadata: detail.providerMetadata } : {},
|
|
511
531
|
attributes: {
|
|
512
532
|
providerId: args.model.provider.id,
|
|
513
533
|
modelId: args.model.modelId
|
|
@@ -516,6 +536,7 @@ async function runReasonEngine(args) {
|
|
|
516
536
|
};
|
|
517
537
|
try {
|
|
518
538
|
if (stream) {
|
|
539
|
+
let continuation;
|
|
519
540
|
let final = "";
|
|
520
541
|
let raw;
|
|
521
542
|
let usage;
|
|
@@ -554,6 +575,7 @@ async function runReasonEngine(args) {
|
|
|
554
575
|
break;
|
|
555
576
|
}
|
|
556
577
|
case "finish": {
|
|
578
|
+
continuation = chunk.continuation;
|
|
557
579
|
if (chunk.raw !== void 0) {
|
|
558
580
|
raw = chunk.raw;
|
|
559
581
|
}
|
|
@@ -602,6 +624,7 @@ async function runReasonEngine(args) {
|
|
|
602
624
|
}
|
|
603
625
|
const result2 = {
|
|
604
626
|
text: final,
|
|
627
|
+
...continuation ? { continuation } : {},
|
|
605
628
|
...raw !== void 0 ? { raw } : {},
|
|
606
629
|
...usage !== void 0 ? { usage } : {},
|
|
607
630
|
...finishReason !== void 0 ? { finishReason } : {},
|
|
@@ -615,6 +638,7 @@ async function runReasonEngine(args) {
|
|
|
615
638
|
}
|
|
616
639
|
const response = await model.invoke(messages);
|
|
617
640
|
(0, import_core2.throwIfExecutionAborted)(abortSignal);
|
|
641
|
+
if (response.content) args.onTextChunk?.(response.content);
|
|
618
642
|
if (emit) {
|
|
619
643
|
emitNodeEvent(args.emitEvent, args.nodeId, "text-start", {
|
|
620
644
|
...commonMeta
|
|
@@ -631,6 +655,7 @@ async function runReasonEngine(args) {
|
|
|
631
655
|
}
|
|
632
656
|
const result = {
|
|
633
657
|
text: response.content,
|
|
658
|
+
...response.continuation ? { continuation: response.continuation } : {},
|
|
634
659
|
...response.raw !== void 0 ? { raw: response.raw } : {},
|
|
635
660
|
...response.usage !== void 0 ? { usage: response.usage } : {},
|
|
636
661
|
...response.finishReason !== void 0 ? { finishReason: response.finishReason } : {},
|
|
@@ -664,480 +689,188 @@ async function reasonEngine(args, meta, inputOverride) {
|
|
|
664
689
|
);
|
|
665
690
|
(0, import_core3.throwIfExecutionAborted)(abortSignal);
|
|
666
691
|
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
|
-
|
|
692
|
+
try {
|
|
693
|
+
return await runReasonEngine({
|
|
694
|
+
model: args.model,
|
|
695
|
+
input: inputOverride ?? args.input,
|
|
696
|
+
system: args.system,
|
|
697
|
+
temperature: args.temperature,
|
|
698
|
+
maxOutputTokens: args.maxOutputTokens,
|
|
699
|
+
stopSequences: args.stopSequences,
|
|
700
|
+
abortSignal,
|
|
701
|
+
reasoning: args.reasoning,
|
|
702
|
+
responseFormat: args.responseFormat,
|
|
703
|
+
providerOptions: args.providerOptions,
|
|
704
|
+
tools: args.tools,
|
|
705
|
+
messages: args.messages,
|
|
706
|
+
defaultTemperature: ctx.node.config?.model?.temperature,
|
|
707
|
+
stream: args.stream,
|
|
708
|
+
emit: args.emit,
|
|
709
|
+
onTextChunk: args.onTextChunk,
|
|
710
|
+
telemetry: args.telemetry,
|
|
711
|
+
nodeId: ctx.node.graph.node,
|
|
712
|
+
emitEvent: ctx.node.emit,
|
|
713
|
+
reasonTrace: ctx.reasonTrace,
|
|
714
|
+
id: meta.id,
|
|
715
|
+
opId: meta.opId
|
|
716
|
+
});
|
|
717
|
+
} catch (error) {
|
|
718
|
+
const usage = error?.usage;
|
|
719
|
+
if (usage) accumulateTokenUsage(usage);
|
|
720
|
+
throw error;
|
|
721
|
+
}
|
|
691
722
|
}
|
|
692
723
|
|
|
693
|
-
// src/reason/
|
|
694
|
-
var
|
|
724
|
+
// src/reason/output-format.ts
|
|
725
|
+
var import_zod2 = require("zod");
|
|
726
|
+
function inferOutputFormat(schema, explicit) {
|
|
727
|
+
if (explicit) return { responseFormat: explicit };
|
|
728
|
+
if (!schema) return {};
|
|
695
729
|
try {
|
|
696
|
-
|
|
697
|
-
|
|
730
|
+
const json = (0, import_zod2.toJSONSchema)(schema);
|
|
731
|
+
const clean = (value) => {
|
|
732
|
+
if (Array.isArray(value)) return value.map(clean);
|
|
733
|
+
if (!value || typeof value !== "object") return value;
|
|
734
|
+
const object = value;
|
|
735
|
+
if ([
|
|
736
|
+
"allOf",
|
|
737
|
+
"not",
|
|
738
|
+
"dependentRequired",
|
|
739
|
+
"dependentSchemas",
|
|
740
|
+
"if",
|
|
741
|
+
"then",
|
|
742
|
+
"else",
|
|
743
|
+
"prefixItems"
|
|
744
|
+
].some((key) => key in object))
|
|
745
|
+
throw new Error(
|
|
746
|
+
"Schema composition requires an explicit provider schema"
|
|
747
|
+
);
|
|
748
|
+
if (object.type === "object") {
|
|
749
|
+
if (object.additionalProperties !== false)
|
|
750
|
+
throw new Error(
|
|
751
|
+
"Dynamic properties require an explicit provider schema"
|
|
752
|
+
);
|
|
753
|
+
const properties = object.properties;
|
|
754
|
+
const required = object.required;
|
|
755
|
+
if (properties && Object.keys(properties).some((key) => !required?.includes(key))) {
|
|
756
|
+
throw new Error(
|
|
757
|
+
"Optional properties require an explicit provider schema"
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
return Object.fromEntries(
|
|
762
|
+
Object.entries(object).filter(([key]) => key !== "$schema" && key !== "default").map(([key, child]) => {
|
|
763
|
+
if ((key === "properties" || key === "$defs" || key === "definitions") && child && typeof child === "object")
|
|
764
|
+
return [
|
|
765
|
+
key,
|
|
766
|
+
Object.fromEntries(
|
|
767
|
+
Object.entries(child).map(([name, definition]) => [
|
|
768
|
+
name,
|
|
769
|
+
clean(definition)
|
|
770
|
+
])
|
|
771
|
+
)
|
|
772
|
+
];
|
|
773
|
+
return [
|
|
774
|
+
key,
|
|
775
|
+
key === "enum" || key === "const" ? child : clean(child)
|
|
776
|
+
];
|
|
777
|
+
})
|
|
778
|
+
);
|
|
779
|
+
};
|
|
780
|
+
if (json.type !== "object")
|
|
781
|
+
throw new Error("The provider schema must have an object root");
|
|
782
|
+
return { responseFormat: { type: "json", schema: clean(json) } };
|
|
783
|
+
} catch {
|
|
698
784
|
return {
|
|
699
|
-
|
|
700
|
-
|
|
785
|
+
responseFormat: { type: "json" },
|
|
786
|
+
warnings: [
|
|
787
|
+
{
|
|
788
|
+
type: "compatibility",
|
|
789
|
+
feature: "outputSchema",
|
|
790
|
+
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."
|
|
791
|
+
}
|
|
792
|
+
]
|
|
701
793
|
};
|
|
702
794
|
}
|
|
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);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// src/reason/structured-stream.ts
|
|
798
|
+
var resolveAppendFieldPaths = (cfg) => {
|
|
799
|
+
const fields = cfg?.fields;
|
|
800
|
+
if (!fields) return [];
|
|
801
|
+
const appendPaths = Object.entries(fields).filter(([, mode]) => mode === "append").map(([path]) => path);
|
|
802
|
+
for (const path of appendPaths) {
|
|
803
|
+
assertStructuredPath(path, "useReason structured append");
|
|
716
804
|
}
|
|
805
|
+
return appendPaths;
|
|
717
806
|
};
|
|
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;
|
|
807
|
+
var resolveSetFieldPaths = (cfg) => {
|
|
808
|
+
const fields = cfg?.fields;
|
|
809
|
+
if (!fields) return [];
|
|
810
|
+
const setPaths = Object.entries(fields).filter(([, mode]) => mode === "set").map(([path]) => path);
|
|
811
|
+
for (const path of setPaths) {
|
|
812
|
+
assertStructuredPath(path, "useReason structured set");
|
|
728
813
|
}
|
|
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);
|
|
814
|
+
return setPaths;
|
|
745
815
|
};
|
|
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;
|
|
816
|
+
var resolveTextDeltaFieldPaths = (cfg) => {
|
|
817
|
+
const fields = cfg?.fields;
|
|
818
|
+
if (!fields) return [];
|
|
819
|
+
const textPaths = Object.entries(fields).filter(([, mode]) => mode === "text-delta").map(([path]) => path);
|
|
820
|
+
for (const path of textPaths) {
|
|
821
|
+
assertStructuredPath(path, "useReason structured text-delta");
|
|
764
822
|
}
|
|
823
|
+
return textPaths;
|
|
765
824
|
};
|
|
766
|
-
var
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
825
|
+
var extractCompletedStringValueAt = (args) => {
|
|
826
|
+
let value = "";
|
|
827
|
+
for (let i = args.start; i < args.text.length; i += 1) {
|
|
828
|
+
const ch = args.text[i];
|
|
829
|
+
if (ch === void 0) break;
|
|
830
|
+
if (ch === '"') {
|
|
831
|
+
return {
|
|
832
|
+
complete: true,
|
|
833
|
+
value
|
|
834
|
+
};
|
|
771
835
|
}
|
|
772
|
-
if (
|
|
773
|
-
|
|
836
|
+
if (ch !== "\\") {
|
|
837
|
+
value += ch;
|
|
838
|
+
continue;
|
|
774
839
|
}
|
|
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
|
-
);
|
|
840
|
+
const next = args.text[i + 1];
|
|
841
|
+
if (!next) {
|
|
842
|
+
return {
|
|
843
|
+
complete: false
|
|
844
|
+
};
|
|
786
845
|
}
|
|
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
|
-
}
|
|
846
|
+
if (next === '"' || next === "\\" || next === "/" || next === "b" || next === "f" || next === "n" || next === "r" || next === "t") {
|
|
847
|
+
value += next === "b" ? "\b" : next === "f" ? "\f" : next === "n" ? "\n" : next === "r" ? "\r" : next === "t" ? " " : next;
|
|
848
|
+
i += 1;
|
|
849
|
+
continue;
|
|
799
850
|
}
|
|
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");
|
|
851
|
+
if (next === "u") {
|
|
852
|
+
const hex = args.text.slice(i + 2, i + 6);
|
|
853
|
+
if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
|
|
854
|
+
return {
|
|
855
|
+
complete: false
|
|
856
|
+
};
|
|
833
857
|
}
|
|
834
|
-
|
|
858
|
+
value += String.fromCharCode(Number.parseInt(hex, 16));
|
|
859
|
+
i += 5;
|
|
860
|
+
continue;
|
|
835
861
|
}
|
|
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
862
|
return {
|
|
846
|
-
|
|
847
|
-
interruptRequired: false,
|
|
848
|
-
...output !== void 0 ? { output } : {}
|
|
863
|
+
complete: false
|
|
849
864
|
};
|
|
850
865
|
}
|
|
851
866
|
return {
|
|
852
|
-
|
|
853
|
-
interruptRequired: true,
|
|
854
|
-
request,
|
|
855
|
-
...output !== void 0 ? { output } : {}
|
|
867
|
+
complete: false
|
|
856
868
|
};
|
|
857
869
|
};
|
|
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;
|
|
870
|
+
var skipWhitespace = (text, start) => {
|
|
871
|
+
let i = start;
|
|
872
|
+
while (i < text.length && /\s/.test(text[i] ?? "")) i += 1;
|
|
873
|
+
return i;
|
|
1141
874
|
};
|
|
1142
875
|
var pathSegmentMatches = (patternPart, actualPart) => patternPart === "*" || patternPart === actualPart;
|
|
1143
876
|
var pathMatchesPattern = (actualParts, patternParts) => actualParts.length === patternParts.length && actualParts.every(
|
|
@@ -1521,103 +1254,560 @@ var extractCompletedArrayItems = (args) => {
|
|
|
1521
1254
|
}
|
|
1522
1255
|
return items;
|
|
1523
1256
|
};
|
|
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
|
-
});
|
|
1257
|
+
var extractCompletedArrayItemGroups = (args) => {
|
|
1258
|
+
return locateValueStarts(args).flatMap((match) => {
|
|
1259
|
+
const items = extractCompletedArrayItems({
|
|
1260
|
+
text: args.text,
|
|
1261
|
+
path: match.path
|
|
1262
|
+
});
|
|
1263
|
+
return items.length > 0 ? [
|
|
1264
|
+
{
|
|
1265
|
+
path: match.path,
|
|
1266
|
+
items
|
|
1267
|
+
}
|
|
1268
|
+
] : [];
|
|
1269
|
+
});
|
|
1270
|
+
};
|
|
1271
|
+
var extractStreamingStringValue = (args) => {
|
|
1272
|
+
const located = locateValueStart(args);
|
|
1273
|
+
if (!located.found || located.start === void 0) {
|
|
1274
|
+
return {
|
|
1275
|
+
found: located.found,
|
|
1276
|
+
complete: false,
|
|
1277
|
+
value: ""
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
const stringStart = skipWhitespace(args.text, located.start);
|
|
1281
|
+
if (args.text[stringStart] !== '"') {
|
|
1282
|
+
return {
|
|
1283
|
+
found: true,
|
|
1284
|
+
complete: false,
|
|
1285
|
+
value: ""
|
|
1286
|
+
};
|
|
1287
|
+
}
|
|
1288
|
+
let value = "";
|
|
1289
|
+
for (let i = stringStart + 1; i < args.text.length; i += 1) {
|
|
1290
|
+
const ch = args.text[i];
|
|
1291
|
+
if (ch === void 0) break;
|
|
1292
|
+
if (ch === '"') {
|
|
1293
|
+
return {
|
|
1294
|
+
found: true,
|
|
1295
|
+
complete: true,
|
|
1296
|
+
value
|
|
1297
|
+
};
|
|
1298
|
+
}
|
|
1299
|
+
if (ch !== "\\") {
|
|
1300
|
+
value += ch;
|
|
1301
|
+
continue;
|
|
1302
|
+
}
|
|
1303
|
+
const next = args.text[i + 1];
|
|
1304
|
+
if (!next) {
|
|
1305
|
+
return {
|
|
1306
|
+
found: true,
|
|
1307
|
+
complete: false,
|
|
1308
|
+
value
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
if (next === '"' || next === "\\" || next === "/" || next === "b" || next === "f" || next === "n" || next === "r" || next === "t") {
|
|
1312
|
+
value += next === "b" ? "\b" : next === "f" ? "\f" : next === "n" ? "\n" : next === "r" ? "\r" : next === "t" ? " " : next;
|
|
1313
|
+
i += 1;
|
|
1314
|
+
continue;
|
|
1315
|
+
}
|
|
1316
|
+
if (next === "u") {
|
|
1317
|
+
const hex = args.text.slice(i + 2, i + 6);
|
|
1318
|
+
if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
|
|
1319
|
+
return {
|
|
1320
|
+
found: true,
|
|
1321
|
+
complete: false,
|
|
1322
|
+
value
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1325
|
+
value += String.fromCharCode(Number.parseInt(hex, 16));
|
|
1326
|
+
i += 5;
|
|
1327
|
+
continue;
|
|
1328
|
+
}
|
|
1329
|
+
return {
|
|
1330
|
+
found: true,
|
|
1331
|
+
complete: false,
|
|
1332
|
+
value
|
|
1333
|
+
};
|
|
1334
|
+
}
|
|
1335
|
+
return {
|
|
1336
|
+
found: true,
|
|
1337
|
+
complete: false,
|
|
1338
|
+
value
|
|
1339
|
+
};
|
|
1340
|
+
};
|
|
1341
|
+
var extractStreamingStringValues = (args) => {
|
|
1342
|
+
return locateValueStarts(args).flatMap((match) => {
|
|
1343
|
+
const current = extractStreamingStringValue({
|
|
1344
|
+
text: args.text,
|
|
1345
|
+
path: match.path
|
|
1346
|
+
});
|
|
1347
|
+
return current.found ? [
|
|
1348
|
+
{
|
|
1349
|
+
path: match.path,
|
|
1350
|
+
complete: current.complete,
|
|
1351
|
+
value: current.value
|
|
1352
|
+
}
|
|
1353
|
+
] : [];
|
|
1354
|
+
});
|
|
1355
|
+
};
|
|
1356
|
+
|
|
1357
|
+
// src/reason/output-stream.ts
|
|
1358
|
+
function createStructuredOutputStreamer(args, id, opId) {
|
|
1359
|
+
let firstText = "";
|
|
1360
|
+
const setFieldPaths = resolveSetFieldPaths(args.structured);
|
|
1361
|
+
const appendFieldPaths = resolveAppendFieldPaths(args.structured);
|
|
1362
|
+
const textDeltaFieldPaths = resolveTextDeltaFieldPaths(args.structured);
|
|
1363
|
+
const emittedSetValues = /* @__PURE__ */ new Map();
|
|
1364
|
+
const emittedAppendCounts = /* @__PURE__ */ new Map();
|
|
1365
|
+
const emittedTextValues = /* @__PURE__ */ new Map();
|
|
1366
|
+
return (delta) => {
|
|
1367
|
+
firstText += delta;
|
|
1368
|
+
for (const fieldPath of setFieldPaths) {
|
|
1369
|
+
const values = extractCompletedFieldValues({
|
|
1370
|
+
text: firstText,
|
|
1371
|
+
path: fieldPath
|
|
1372
|
+
});
|
|
1373
|
+
for (const { path, value } of values) {
|
|
1374
|
+
const nextSerialized = JSON.stringify(value);
|
|
1375
|
+
if (emittedSetValues.get(path) === nextSerialized) continue;
|
|
1376
|
+
emittedSetValues.set(path, nextSerialized);
|
|
1377
|
+
emitStructuredData({
|
|
1378
|
+
kind: "set",
|
|
1379
|
+
path,
|
|
1380
|
+
value,
|
|
1381
|
+
dataType: args.structured?.dataType ?? "reason-output",
|
|
1382
|
+
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
1383
|
+
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
1384
|
+
...id ? { id } : {},
|
|
1385
|
+
streamId: opId
|
|
1386
|
+
});
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
for (const fieldPath of appendFieldPaths) {
|
|
1390
|
+
const groups = extractCompletedArrayItemGroups({
|
|
1391
|
+
text: firstText,
|
|
1392
|
+
path: fieldPath
|
|
1393
|
+
});
|
|
1394
|
+
for (const { path, items } of groups) {
|
|
1395
|
+
const emittedAppendCount = emittedAppendCounts.get(path) ?? 0;
|
|
1396
|
+
if (items.length <= emittedAppendCount) continue;
|
|
1397
|
+
const nextItems = items.slice(emittedAppendCount);
|
|
1398
|
+
emittedAppendCounts.set(path, items.length);
|
|
1399
|
+
emitStructuredData({
|
|
1400
|
+
kind: "append",
|
|
1401
|
+
path,
|
|
1402
|
+
items: nextItems,
|
|
1403
|
+
dataType: args.structured?.dataType ?? "reason-output",
|
|
1404
|
+
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
1405
|
+
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
1406
|
+
...id ? { id } : {},
|
|
1407
|
+
streamId: opId
|
|
1408
|
+
});
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
for (const fieldPath of textDeltaFieldPaths) {
|
|
1412
|
+
const values = extractStreamingStringValues({
|
|
1413
|
+
text: firstText,
|
|
1414
|
+
path: fieldPath
|
|
1415
|
+
});
|
|
1416
|
+
for (const { path, value } of values) {
|
|
1417
|
+
const emittedTextValue = emittedTextValues.get(path) ?? "";
|
|
1418
|
+
if (value.length <= emittedTextValue.length || !value.startsWith(emittedTextValue)) {
|
|
1419
|
+
continue;
|
|
1420
|
+
}
|
|
1421
|
+
const nextDelta = value.slice(emittedTextValue.length);
|
|
1422
|
+
emittedTextValues.set(path, value);
|
|
1423
|
+
if (nextDelta.length === 0) continue;
|
|
1424
|
+
emitStructuredData({
|
|
1425
|
+
kind: "text-delta",
|
|
1426
|
+
path,
|
|
1427
|
+
delta: nextDelta,
|
|
1428
|
+
dataType: args.structured?.dataType ?? "reason-output",
|
|
1429
|
+
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
1430
|
+
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
1431
|
+
...id ? { id } : {},
|
|
1432
|
+
streamId: opId
|
|
1433
|
+
});
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
};
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
// src/reason/parsing.ts
|
|
1440
|
+
var tryJsonParse = (value) => {
|
|
1441
|
+
try {
|
|
1442
|
+
return { ok: true, value: JSON.parse(value) };
|
|
1443
|
+
} catch (error) {
|
|
1444
|
+
return {
|
|
1445
|
+
ok: false,
|
|
1446
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
};
|
|
1450
|
+
var extractJsonCodeBlock = (value) => {
|
|
1451
|
+
const match = value.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
1452
|
+
if (!match || typeof match[1] !== "string") return null;
|
|
1453
|
+
const inner = match[1].trim();
|
|
1454
|
+
return inner.length > 0 ? inner : null;
|
|
1455
|
+
};
|
|
1456
|
+
var tryStringify = (value) => {
|
|
1457
|
+
if (typeof value === "string") return value;
|
|
1458
|
+
try {
|
|
1459
|
+
return JSON.stringify(value, null, 2);
|
|
1460
|
+
} catch {
|
|
1461
|
+
return String(value);
|
|
1462
|
+
}
|
|
1463
|
+
};
|
|
1464
|
+
var isRecord2 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1465
|
+
var resolveOutputCandidate = (text) => {
|
|
1466
|
+
const trimmed = text.trim();
|
|
1467
|
+
if (!trimmed) return text;
|
|
1468
|
+
const direct = tryJsonParse(trimmed);
|
|
1469
|
+
if (direct.ok) return direct.value;
|
|
1470
|
+
const block = extractJsonCodeBlock(trimmed);
|
|
1471
|
+
if (block) {
|
|
1472
|
+
const parsedBlock = tryJsonParse(block);
|
|
1473
|
+
if (parsedBlock.ok) return parsedBlock.value;
|
|
1474
|
+
}
|
|
1475
|
+
return text;
|
|
1476
|
+
};
|
|
1477
|
+
var looksLikeStructuredJsonAttempt = (value) => {
|
|
1478
|
+
const trimmed = value.trim();
|
|
1479
|
+
if (!trimmed) return false;
|
|
1480
|
+
return trimmed.startsWith("{") || trimmed.startsWith("[") || /^```(?:json)?/i.test(trimmed);
|
|
1481
|
+
};
|
|
1482
|
+
var isLikelyTruncatedStructuredOutput = (text) => {
|
|
1483
|
+
const trimmed = text.trim();
|
|
1484
|
+
if (!looksLikeStructuredJsonAttempt(trimmed)) return false;
|
|
1485
|
+
const direct = tryJsonParse(trimmed);
|
|
1486
|
+
if (!direct.ok && direct.error) return true;
|
|
1487
|
+
const block = extractJsonCodeBlock(trimmed);
|
|
1488
|
+
if (!block) return false;
|
|
1489
|
+
const parsedBlock = tryJsonParse(block);
|
|
1490
|
+
return !parsedBlock.ok && Boolean(parsedBlock.error);
|
|
1491
|
+
};
|
|
1492
|
+
var createTruncatedStructuredOutputError = (label) => new Error(
|
|
1493
|
+
`${label} was truncated before producing valid structured output. The model stopped due to output length. Increase maxOutputTokens or simplify the requested output.`
|
|
1494
|
+
);
|
|
1495
|
+
var createInvalidStructuredOutputError = (label) => new Error(
|
|
1496
|
+
`${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.`
|
|
1497
|
+
);
|
|
1498
|
+
var parseReasonOutputWithSchema = (args) => {
|
|
1499
|
+
const candidate = resolveOutputCandidate(args.text);
|
|
1500
|
+
try {
|
|
1501
|
+
return parseWithSchema(args.schema, candidate, args.label);
|
|
1502
|
+
} catch (error) {
|
|
1503
|
+
if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
|
|
1504
|
+
throw createTruncatedStructuredOutputError(args.label);
|
|
1505
|
+
}
|
|
1506
|
+
if (typeof candidate === "string" && candidate.trim().length > 0) {
|
|
1507
|
+
throw createInvalidStructuredOutputError(args.label);
|
|
1508
|
+
}
|
|
1509
|
+
throw error;
|
|
1510
|
+
}
|
|
1511
|
+
};
|
|
1512
|
+
var parseInterruptFirstPassResult = (args) => {
|
|
1513
|
+
const candidate = resolveOutputCandidate(args.text);
|
|
1514
|
+
if (!isRecord2(candidate)) {
|
|
1515
|
+
if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
|
|
1516
|
+
throw createTruncatedStructuredOutputError("useReason output");
|
|
1517
|
+
}
|
|
1518
|
+
if (typeof candidate === "string" && candidate.trim().length > 0) {
|
|
1519
|
+
throw createInvalidStructuredOutputError("useReason output");
|
|
1520
|
+
}
|
|
1521
|
+
throw new Error(
|
|
1522
|
+
"useReason first pass with interrupt must return a JSON object."
|
|
1523
|
+
);
|
|
1524
|
+
}
|
|
1525
|
+
const mode = args.mode ?? "required";
|
|
1526
|
+
let interruptRequired = true;
|
|
1527
|
+
if (mode === "optional") {
|
|
1528
|
+
if (candidate.decision !== "continue" && candidate.decision !== "interrupt") {
|
|
1529
|
+
throw new Error(
|
|
1530
|
+
'useReason optional interrupt first pass must include decision "continue" or "interrupt".'
|
|
1531
|
+
);
|
|
1532
|
+
}
|
|
1533
|
+
interruptRequired = candidate.decision === "interrupt";
|
|
1534
|
+
if (!interruptRequired) {
|
|
1535
|
+
const hasUnexpectedRequest = [
|
|
1536
|
+
candidate.interruptRequest,
|
|
1537
|
+
candidate.request,
|
|
1538
|
+
candidate.interrupt
|
|
1539
|
+
].some((value) => value !== null && value !== void 0);
|
|
1540
|
+
if (hasUnexpectedRequest) {
|
|
1541
|
+
throw new Error(
|
|
1542
|
+
'useReason optional interrupt first pass with decision "continue" must not include an interrupt request.'
|
|
1543
|
+
);
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
let request;
|
|
1548
|
+
if (interruptRequired) {
|
|
1549
|
+
const requestCandidates = [
|
|
1550
|
+
candidate.interruptRequest,
|
|
1551
|
+
candidate.request,
|
|
1552
|
+
candidate.interrupt,
|
|
1553
|
+
mode === "required" ? candidate : void 0
|
|
1554
|
+
];
|
|
1555
|
+
const requestPayload = requestCandidates.find(
|
|
1556
|
+
(value) => value !== null && value !== void 0
|
|
1557
|
+
);
|
|
1558
|
+
request = parseWithSchema(
|
|
1559
|
+
args.requestSchema,
|
|
1560
|
+
requestPayload,
|
|
1561
|
+
"useReason interrupt.request"
|
|
1562
|
+
);
|
|
1563
|
+
}
|
|
1564
|
+
let output;
|
|
1565
|
+
if (args.outputSchema) {
|
|
1566
|
+
const outputPayload = Object.hasOwn(candidate, "output") ? candidate.output : candidate;
|
|
1567
|
+
try {
|
|
1568
|
+
output = parseWithSchema(
|
|
1569
|
+
args.outputSchema,
|
|
1570
|
+
outputPayload,
|
|
1571
|
+
"useReason output"
|
|
1572
|
+
);
|
|
1573
|
+
} catch (error) {
|
|
1574
|
+
if (args.finishReason?.unified === "length" || isLikelyTruncatedStructuredOutput(args.text)) {
|
|
1575
|
+
throw createTruncatedStructuredOutputError("useReason output");
|
|
1576
|
+
}
|
|
1577
|
+
if (typeof outputPayload === "string" && outputPayload.trim().length > 0) {
|
|
1578
|
+
throw createInvalidStructuredOutputError("useReason output");
|
|
1579
|
+
}
|
|
1580
|
+
throw error;
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
const explicitDraft = typeof candidate.draftText === "string" && candidate.draftText.length > 0 ? candidate.draftText : typeof candidate.text === "string" && candidate.text.length > 0 ? candidate.text : void 0;
|
|
1584
|
+
if (!args.outputSchema && !explicitDraft) {
|
|
1585
|
+
throw new Error(
|
|
1586
|
+
"useReason first pass with interrupt requires `draftText` when outputSchema is not provided."
|
|
1587
|
+
);
|
|
1588
|
+
}
|
|
1589
|
+
const draftText = explicitDraft ?? (output !== void 0 ? tryStringify(output) : String(args.text ?? ""));
|
|
1590
|
+
if (!interruptRequired) {
|
|
1591
|
+
return {
|
|
1592
|
+
draftText,
|
|
1593
|
+
interruptRequired: false,
|
|
1594
|
+
...output !== void 0 ? { output } : {}
|
|
1595
|
+
};
|
|
1596
|
+
}
|
|
1597
|
+
return {
|
|
1598
|
+
draftText,
|
|
1599
|
+
interruptRequired: true,
|
|
1600
|
+
request,
|
|
1601
|
+
...output !== void 0 ? { output } : {}
|
|
1602
|
+
};
|
|
1603
|
+
};
|
|
1604
|
+
|
|
1605
|
+
// src/reason/prompting.ts
|
|
1606
|
+
var import_zod3 = require("zod");
|
|
1607
|
+
var tryStringify2 = (value) => {
|
|
1608
|
+
if (typeof value === "string") return value;
|
|
1609
|
+
try {
|
|
1610
|
+
return JSON.stringify(value, null, 2);
|
|
1611
|
+
} catch {
|
|
1612
|
+
return String(value);
|
|
1613
|
+
}
|
|
1614
|
+
};
|
|
1615
|
+
var tryGetSchemaHint = (schema) => {
|
|
1616
|
+
try {
|
|
1617
|
+
const hint = (0, import_zod3.toJSONSchema)(schema);
|
|
1618
|
+
return JSON.stringify(hint, null, 2);
|
|
1619
|
+
} catch {
|
|
1620
|
+
return void 0;
|
|
1621
|
+
}
|
|
1622
|
+
};
|
|
1623
|
+
var withSchemaHint = (input, label, schema) => {
|
|
1624
|
+
const hint = tryGetSchemaHint(schema);
|
|
1625
|
+
if (!hint) return input;
|
|
1626
|
+
return `${input}
|
|
1627
|
+
|
|
1628
|
+
${label}:
|
|
1629
|
+
${hint}`;
|
|
1630
|
+
};
|
|
1631
|
+
var defaultInterruptFirstPassInput = (args) => {
|
|
1632
|
+
const mode = args.mode ?? "required";
|
|
1633
|
+
const base = mode === "optional" ? args.outputSchema ? [
|
|
1634
|
+
args.input,
|
|
1635
|
+
"Output rules:",
|
|
1636
|
+
"- Return JSON only. No markdown fences.",
|
|
1637
|
+
"- Return an object with keys: decision, output, interruptRequest, draftText.",
|
|
1638
|
+
'- decision must be either "continue" or "interrupt".',
|
|
1639
|
+
'- Use decision "continue" when you can produce the final output without asking the user.',
|
|
1640
|
+
'- Use decision "interrupt" only when user input is needed before finalizing.',
|
|
1641
|
+
"- output must match the expected output JSON schema.",
|
|
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
|
+
"- draftText should be a concise plain-language summary of output."
|
|
1645
|
+
].join("\n\n") : [
|
|
1646
|
+
args.input,
|
|
1647
|
+
"Output rules:",
|
|
1648
|
+
"- Return JSON only. No markdown fences.",
|
|
1649
|
+
"- Return an object with keys: decision, draftText, interruptRequest.",
|
|
1650
|
+
'- decision must be either "continue" or "interrupt".',
|
|
1651
|
+
'- Use decision "continue" when you can produce the final answer without asking the user.',
|
|
1652
|
+
'- Use decision "interrupt" only when user input is needed before finalizing.',
|
|
1653
|
+
"- draftText should be the current answer text.",
|
|
1654
|
+
'- When decision is "interrupt", interruptRequest must match the expected interrupt request JSON schema and must be a non-null object.',
|
|
1655
|
+
'- When decision is "continue", interruptRequest must be null.'
|
|
1656
|
+
].join("\n\n") : args.outputSchema ? [
|
|
1657
|
+
args.input,
|
|
1658
|
+
"Output rules:",
|
|
1659
|
+
"- Return JSON only. No markdown fences.",
|
|
1660
|
+
"- Return an object with keys: output, interruptRequest, draftText.",
|
|
1661
|
+
"- output must match the expected output JSON schema.",
|
|
1662
|
+
"- interruptRequest must match the expected interrupt request JSON schema.",
|
|
1663
|
+
"- interruptRequest must be a non-null object.",
|
|
1664
|
+
"- draftText should be a concise plain-language summary of output.",
|
|
1665
|
+
"- If uncertain, still return a valid interruptRequest with safe default options."
|
|
1666
|
+
].join("\n\n") : [
|
|
1667
|
+
args.input,
|
|
1668
|
+
"Output rules:",
|
|
1669
|
+
"- Return JSON only. No markdown fences.",
|
|
1670
|
+
"- Return an object with keys: draftText, interruptRequest.",
|
|
1671
|
+
"- draftText should be the current draft answer text.",
|
|
1672
|
+
"- interruptRequest must match the expected interrupt request JSON schema.",
|
|
1673
|
+
"- interruptRequest must be a non-null object.",
|
|
1674
|
+
"- If uncertain, still return a valid interruptRequest with safe default options."
|
|
1675
|
+
].join("\n\n");
|
|
1676
|
+
const withInterruptSchema = withSchemaHint(
|
|
1677
|
+
base,
|
|
1678
|
+
"Expected interrupt request JSON schema (for `interruptRequest`)",
|
|
1679
|
+
args.requestSchema
|
|
1680
|
+
);
|
|
1681
|
+
if (!args.outputSchema) return withInterruptSchema;
|
|
1682
|
+
const withOutputSchema = withSchemaHint(
|
|
1683
|
+
withInterruptSchema,
|
|
1684
|
+
"Expected output JSON schema (for `output`)",
|
|
1685
|
+
args.outputSchema
|
|
1686
|
+
);
|
|
1687
|
+
return [
|
|
1688
|
+
withOutputSchema,
|
|
1689
|
+
"JSON template:",
|
|
1690
|
+
mode === "optional" ? '{ "decision": "continue", "output": { ... }, "interruptRequest": null, "draftText": "..." }' : '{ "output": { ... }, "interruptRequest": { ... }, "draftText": "..." }'
|
|
1691
|
+
].join("\n\n");
|
|
1692
|
+
};
|
|
1693
|
+
var defaultContinuationInput = (args) => [
|
|
1694
|
+
"You are continuing a paused reasoning task after user input.",
|
|
1695
|
+
"Update the previous result using the user response and return the final answer only.",
|
|
1696
|
+
"If the previous output was JSON, return JSON in the same schema.",
|
|
1697
|
+
"",
|
|
1698
|
+
`Original request:
|
|
1699
|
+
${args.input}`,
|
|
1700
|
+
`Draft answer text:
|
|
1701
|
+
${args.draftText}`,
|
|
1702
|
+
`Draft output object:
|
|
1703
|
+
${tryStringify2(args.draftOutput ?? args.draftText)}`,
|
|
1704
|
+
`Interrupt request shown to user:
|
|
1705
|
+
${tryStringify2(args.request)}`,
|
|
1706
|
+
`User response:
|
|
1707
|
+
${tryStringify2(args.response)}`
|
|
1708
|
+
].join("\n\n");
|
|
1709
|
+
var withOutputGuardrails = (input, schema) => {
|
|
1710
|
+
const base = [
|
|
1711
|
+
input,
|
|
1712
|
+
"Output rules:",
|
|
1713
|
+
"- Return only JSON, no markdown fences.",
|
|
1714
|
+
"- Ensure the response conforms to the expected output schema.",
|
|
1715
|
+
"- Do not include explanatory text outside the JSON object."
|
|
1716
|
+
].join("\n\n");
|
|
1717
|
+
return withSchemaHint(base, "Expected output JSON schema", schema);
|
|
1537
1718
|
};
|
|
1538
|
-
var
|
|
1539
|
-
const
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
value: ""
|
|
1545
|
-
};
|
|
1719
|
+
var withStructuredStreamHints = (input, args) => {
|
|
1720
|
+
const rules = [];
|
|
1721
|
+
for (const path of args.setFieldPaths ?? []) {
|
|
1722
|
+
rules.push(
|
|
1723
|
+
`- For the field path pattern \`${path}\`, write one stable final value and do not rewrite it after it is complete.`
|
|
1724
|
+
);
|
|
1546
1725
|
}
|
|
1547
|
-
const
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1726
|
+
for (const path of args.appendFieldPaths ?? []) {
|
|
1727
|
+
rules.push(
|
|
1728
|
+
`- For the array field path pattern \`${path}\`, write items in final order and finish each item before starting the next.`
|
|
1729
|
+
);
|
|
1730
|
+
rules.push(
|
|
1731
|
+
`- Once an item in \`${path}\` is complete, do not rewrite or reorder earlier items.`
|
|
1732
|
+
);
|
|
1554
1733
|
}
|
|
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
|
-
|
|
1734
|
+
for (const path of args.textDeltaFieldPaths ?? []) {
|
|
1735
|
+
rules.push(
|
|
1736
|
+
`- For the string field path pattern \`${path}\`, write the value progressively by appending text to the end.`
|
|
1737
|
+
);
|
|
1738
|
+
rules.push(
|
|
1739
|
+
`- Do not rewrite or replace the earlier prefix of \`${path}\` once emitted.`
|
|
1740
|
+
);
|
|
1741
|
+
}
|
|
1742
|
+
if (rules.length === 0) return input;
|
|
1743
|
+
return [
|
|
1744
|
+
input,
|
|
1745
|
+
"Streaming rules:",
|
|
1746
|
+
...rules,
|
|
1747
|
+
"- Keep the overall response as valid JSON."
|
|
1748
|
+
].join("\n\n");
|
|
1749
|
+
};
|
|
1750
|
+
|
|
1751
|
+
// src/reason/result.ts
|
|
1752
|
+
var mergeUsage = (left, right) => {
|
|
1753
|
+
if (!left) return right;
|
|
1754
|
+
if (!right) return left;
|
|
1755
|
+
const sum = (a, b) => a === void 0 && b === void 0 ? void 0 : (a ?? 0) + (b ?? 0);
|
|
1756
|
+
const raw = left.raw || right.raw ? {
|
|
1757
|
+
...left.raw ?? {},
|
|
1758
|
+
...right.raw ?? {}
|
|
1759
|
+
} : void 0;
|
|
1760
|
+
const input = sum(left.input, right.input);
|
|
1761
|
+
const output = sum(left.output, right.output);
|
|
1762
|
+
const total = sum(left.total, right.total);
|
|
1763
|
+
const reasoning = sum(left.reasoning, right.reasoning);
|
|
1764
|
+
const cacheRead = sum(left.cacheRead, right.cacheRead);
|
|
1765
|
+
const cacheWrite = sum(left.cacheWrite, right.cacheWrite);
|
|
1766
|
+
const merged = {
|
|
1767
|
+
...right.outputIncludesReasoning !== void 0 ? { outputIncludesReasoning: right.outputIncludesReasoning } : {},
|
|
1768
|
+
...right.inputIncludesCacheWrite !== void 0 ? { inputIncludesCacheWrite: right.inputIncludesCacheWrite } : {},
|
|
1769
|
+
...right.inputIncludesCacheRead !== void 0 ? { inputIncludesCacheRead: right.inputIncludesCacheRead } : {}
|
|
1770
|
+
};
|
|
1771
|
+
if (input !== void 0) merged.input = input;
|
|
1772
|
+
if (output !== void 0) merged.output = output;
|
|
1773
|
+
if (total !== void 0) merged.total = total;
|
|
1774
|
+
if (reasoning !== void 0) merged.reasoning = reasoning;
|
|
1775
|
+
if (cacheRead !== void 0) merged.cacheRead = cacheRead;
|
|
1776
|
+
if (cacheWrite !== void 0) merged.cacheWrite = cacheWrite;
|
|
1777
|
+
if (raw) merged.raw = raw;
|
|
1778
|
+
return merged;
|
|
1779
|
+
};
|
|
1780
|
+
var mergeWarnings = (left, right) => {
|
|
1781
|
+
if (!left?.length) return right;
|
|
1782
|
+
if (!right?.length) return left;
|
|
1783
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1784
|
+
const merged = [];
|
|
1785
|
+
for (const warning of [...left, ...right]) {
|
|
1786
|
+
const key = JSON.stringify(warning);
|
|
1787
|
+
if (seen.has(key)) continue;
|
|
1788
|
+
seen.add(key);
|
|
1789
|
+
merged.push(warning);
|
|
1601
1790
|
}
|
|
1791
|
+
return merged;
|
|
1792
|
+
};
|
|
1793
|
+
var mergeProviderMetadata = (left, right) => {
|
|
1794
|
+
if (!left) return right;
|
|
1795
|
+
if (!right) return left;
|
|
1602
1796
|
return {
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
value
|
|
1797
|
+
...left,
|
|
1798
|
+
...right
|
|
1606
1799
|
};
|
|
1607
1800
|
};
|
|
1608
|
-
var
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
value: current.value
|
|
1619
|
-
}
|
|
1620
|
-
] : [];
|
|
1801
|
+
var emitReasonStructuredOutput = (args) => {
|
|
1802
|
+
if (!args.emit || !shouldEmitStructured(args.structured)) return;
|
|
1803
|
+
emitStructuredData({
|
|
1804
|
+
dataType: args.structured?.dataType ?? "reason-output",
|
|
1805
|
+
data: args.output,
|
|
1806
|
+
kind: "final",
|
|
1807
|
+
...args.structured?.schemaId ? { schemaId: args.structured.schemaId } : {},
|
|
1808
|
+
...args.structured?.schemaVersion ? { schemaVersion: args.structured.schemaVersion } : {},
|
|
1809
|
+
...args.id ? { id: args.id } : {},
|
|
1810
|
+
streamId: args.opId
|
|
1621
1811
|
});
|
|
1622
1812
|
};
|
|
1623
1813
|
|
|
@@ -1674,16 +1864,20 @@ var createInitialMessages = (args) => [
|
|
|
1674
1864
|
{ role: "user", content: String(args.input ?? "") }
|
|
1675
1865
|
];
|
|
1676
1866
|
var runReasonToolLoop = async (args) => {
|
|
1677
|
-
const { useReasonArgs, id,
|
|
1867
|
+
const { useReasonArgs, id, traceSpan, checkpointKey } = args;
|
|
1678
1868
|
const ctx = getHookContext();
|
|
1679
1869
|
const abortSignal = (0, import_core4.combineAbortSignals)(
|
|
1680
1870
|
ctx.node.abortSignal,
|
|
1681
1871
|
useReasonArgs.abortSignal,
|
|
1682
1872
|
useReasonArgs.model.options?.abortSignal
|
|
1683
1873
|
);
|
|
1874
|
+
const saved = ctx.currentNodeState.byKey[checkpointKey];
|
|
1875
|
+
const checkpoint = saved?.status === "tool_loop" ? saved : void 0;
|
|
1876
|
+
const opId = checkpoint?.opId ?? args.opId;
|
|
1684
1877
|
const tools = useReasonArgs.tools ?? [];
|
|
1685
1878
|
const toolByName = /* @__PURE__ */ new Map();
|
|
1686
1879
|
let validationCompleted = false;
|
|
1880
|
+
let separateOutput = false;
|
|
1687
1881
|
try {
|
|
1688
1882
|
for (const tool of tools) {
|
|
1689
1883
|
if (toolByName.has(tool.name)) {
|
|
@@ -1698,6 +1892,17 @@ var runReasonToolLoop = async (args) => {
|
|
|
1698
1892
|
"useReason tools cannot be combined with useReason interrupt mode yet. Use toolExecution.approval for tool approval."
|
|
1699
1893
|
);
|
|
1700
1894
|
}
|
|
1895
|
+
separateOutput = Boolean(
|
|
1896
|
+
useReasonArgs.model.provider.getModel(useReasonArgs.model.modelId, {
|
|
1897
|
+
...useReasonArgs.model.options,
|
|
1898
|
+
...useReasonArgs.reasoning !== void 0 ? { reasoning: useReasonArgs.reasoning } : {},
|
|
1899
|
+
...useReasonArgs.responseFormat ? { responseFormat: useReasonArgs.responseFormat } : {},
|
|
1900
|
+
providerOptions: resolveProviderOptions(
|
|
1901
|
+
useReasonArgs.model.options?.providerOptions,
|
|
1902
|
+
useReasonArgs.providerOptions
|
|
1903
|
+
) ?? {}
|
|
1904
|
+
}).requiresSeparateStructuredOutput
|
|
1905
|
+
);
|
|
1701
1906
|
validationCompleted = true;
|
|
1702
1907
|
} finally {
|
|
1703
1908
|
if (!validationCompleted) {
|
|
@@ -1705,21 +1910,47 @@ var runReasonToolLoop = async (args) => {
|
|
|
1705
1910
|
}
|
|
1706
1911
|
}
|
|
1707
1912
|
const toolDefinitions = toToolDefinitions(tools);
|
|
1913
|
+
let finalizing = checkpoint?.finalizing ?? false;
|
|
1914
|
+
let completed = false;
|
|
1708
1915
|
const maxSteps = Math.max(1, useReasonArgs.toolExecution?.maxSteps ?? 3);
|
|
1709
|
-
const messages = createInitialMessages({
|
|
1916
|
+
const messages = checkpoint?.messages ?? createInitialMessages({
|
|
1710
1917
|
system: useReasonArgs.system,
|
|
1711
|
-
input: useReasonArgs.input
|
|
1918
|
+
input: useReasonArgs.outputSchema ? withOutputGuardrails(useReasonArgs.input, useReasonArgs.outputSchema) : useReasonArgs.input
|
|
1712
1919
|
});
|
|
1713
|
-
const steps = [];
|
|
1714
|
-
const allToolCalls =
|
|
1715
|
-
|
|
1920
|
+
const steps = checkpoint?.steps ?? [];
|
|
1921
|
+
const allToolCalls = steps.flatMap(
|
|
1922
|
+
(step) => step.toolCalls
|
|
1923
|
+
);
|
|
1924
|
+
const allToolResults = steps.flatMap(
|
|
1925
|
+
(step) => step.toolResults
|
|
1926
|
+
);
|
|
1716
1927
|
let finalText = "";
|
|
1717
1928
|
let finalRaw;
|
|
1718
1929
|
let finalOutput;
|
|
1719
|
-
let aggregatedUsage
|
|
1930
|
+
let aggregatedUsage = steps.reduce(
|
|
1931
|
+
(usage, step) => mergeUsage(usage, step.usage),
|
|
1932
|
+
void 0
|
|
1933
|
+
);
|
|
1720
1934
|
let finalFinishReason;
|
|
1721
1935
|
let aggregatedProviderMetadata;
|
|
1722
|
-
let aggregatedWarnings
|
|
1936
|
+
let aggregatedWarnings = steps.reduce(
|
|
1937
|
+
(warnings, step) => mergeWarnings(warnings, step.warnings),
|
|
1938
|
+
args.initialWarnings
|
|
1939
|
+
);
|
|
1940
|
+
let pending = checkpoint?.pending;
|
|
1941
|
+
const approvedCalls = checkpoint?.approvedCalls ?? [];
|
|
1942
|
+
const save = () => {
|
|
1943
|
+
ctx.currentNodeState.byKey[checkpointKey] = {
|
|
1944
|
+
status: "tool_loop",
|
|
1945
|
+
opId,
|
|
1946
|
+
messages,
|
|
1947
|
+
steps,
|
|
1948
|
+
approvedCalls,
|
|
1949
|
+
finalizing,
|
|
1950
|
+
...pending ? { pending } : {}
|
|
1951
|
+
};
|
|
1952
|
+
ctx.stateDirty = true;
|
|
1953
|
+
};
|
|
1723
1954
|
const emitToolEvent = (event, payload) => {
|
|
1724
1955
|
ctx.node.emit(event, {
|
|
1725
1956
|
...payload,
|
|
@@ -1729,26 +1960,44 @@ var runReasonToolLoop = async (args) => {
|
|
|
1729
1960
|
});
|
|
1730
1961
|
};
|
|
1731
1962
|
try {
|
|
1732
|
-
for (let stepIndex =
|
|
1963
|
+
for (let stepIndex = pending ? steps.length - 1 : steps.length; stepIndex < maxSteps; stepIndex += 1) {
|
|
1733
1964
|
(0, import_core4.throwIfExecutionAborted)(abortSignal);
|
|
1734
1965
|
traceSpan?.addEvent?.("useReason.tool-step.start", {
|
|
1735
1966
|
stepIndex,
|
|
1736
1967
|
toolCount: toolDefinitions.length
|
|
1737
1968
|
});
|
|
1738
|
-
const
|
|
1969
|
+
const reused = Boolean(pending);
|
|
1970
|
+
const stream = useReasonArgs.stream ?? useReasonArgs.model.options?.streaming ?? true;
|
|
1971
|
+
const emit = useReasonArgs.emit ?? true;
|
|
1972
|
+
let textStarted = false;
|
|
1973
|
+
const structuredChunk = useReasonArgs.outputSchema && (!separateOutput || finalizing) && stream && emit && shouldStreamStructured(useReasonArgs.structured) ? createStructuredOutputStreamer(useReasonArgs, id, opId) : void 0;
|
|
1974
|
+
const step = pending ?? await reasonEngine(
|
|
1739
1975
|
{
|
|
1740
1976
|
...useReasonArgs,
|
|
1741
|
-
tools: toolDefinitions,
|
|
1977
|
+
tools: finalizing ? [] : toolDefinitions,
|
|
1978
|
+
...separateOutput && !finalizing ? { responseFormat: { type: "text" } } : {},
|
|
1742
1979
|
messages,
|
|
1743
1980
|
emit: false,
|
|
1744
|
-
stream
|
|
1981
|
+
stream,
|
|
1982
|
+
onTextChunk: (delta) => {
|
|
1983
|
+
if (structuredChunk) structuredChunk(delta);
|
|
1984
|
+
if (!emit || useReasonArgs.outputSchema || !delta) return;
|
|
1985
|
+
if (!textStarted) {
|
|
1986
|
+
emitToolEvent("text-start", {});
|
|
1987
|
+
textStarted = true;
|
|
1988
|
+
}
|
|
1989
|
+
emitToolEvent("text-delta", { delta });
|
|
1990
|
+
}
|
|
1745
1991
|
},
|
|
1746
1992
|
{ ...id ? { id } : {}, opId }
|
|
1747
1993
|
);
|
|
1994
|
+
if (textStarted) emitToolEvent("text-end", {});
|
|
1748
1995
|
finalText = step.text;
|
|
1749
1996
|
finalRaw = step.raw;
|
|
1750
|
-
|
|
1751
|
-
|
|
1997
|
+
if (!reused) {
|
|
1998
|
+
aggregatedUsage = mergeUsage(aggregatedUsage, step.usage);
|
|
1999
|
+
accumulateTokenUsage(step.usage);
|
|
2000
|
+
}
|
|
1752
2001
|
finalFinishReason = step.finishReason;
|
|
1753
2002
|
aggregatedProviderMetadata = mergeProviderMetadata(
|
|
1754
2003
|
aggregatedProviderMetadata,
|
|
@@ -1756,32 +2005,75 @@ var runReasonToolLoop = async (args) => {
|
|
|
1756
2005
|
);
|
|
1757
2006
|
aggregatedWarnings = mergeWarnings(aggregatedWarnings, step.warnings);
|
|
1758
2007
|
const toolCalls = step.toolCalls ?? [];
|
|
1759
|
-
const toolResults = [];
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
2008
|
+
const toolResults = reused ? steps[stepIndex]?.toolResults ?? [] : [];
|
|
2009
|
+
if (!reused)
|
|
2010
|
+
steps.push({
|
|
2011
|
+
stepIndex,
|
|
2012
|
+
text: step.text,
|
|
2013
|
+
toolCalls,
|
|
2014
|
+
toolResults,
|
|
2015
|
+
...step.usage ? { usage: step.usage } : {},
|
|
2016
|
+
...step.finishReason ? { finishReason: step.finishReason } : {},
|
|
2017
|
+
...step.providerMetadata ? { providerMetadata: step.providerMetadata } : {},
|
|
2018
|
+
...step.warnings ? { warnings: step.warnings } : {}
|
|
2019
|
+
});
|
|
1770
2020
|
if (toolCalls.length === 0) {
|
|
1771
|
-
if (
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
2021
|
+
if (separateOutput && !finalizing) {
|
|
2022
|
+
if (args.allowValidatedToolOutput && useReasonArgs.outputSchema) {
|
|
2023
|
+
try {
|
|
2024
|
+
finalOutput = parseReasonOutputWithSchema({
|
|
2025
|
+
text: step.text,
|
|
2026
|
+
schema: useReasonArgs.outputSchema,
|
|
2027
|
+
...step.finishReason ? { finishReason: step.finishReason } : {},
|
|
2028
|
+
label: "useReason output"
|
|
2029
|
+
});
|
|
2030
|
+
aggregatedWarnings = mergeWarnings(aggregatedWarnings, [
|
|
2031
|
+
{
|
|
2032
|
+
type: "compatibility",
|
|
2033
|
+
feature: "responseFormat",
|
|
2034
|
+
details: "Reused locally validated tool-phase output without an extra native-schema request. Set responseFormat.schema explicitly to require provider schema enforcement."
|
|
2035
|
+
}
|
|
2036
|
+
]);
|
|
2037
|
+
completed = true;
|
|
2038
|
+
break;
|
|
2039
|
+
} catch {
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
messages.push({
|
|
2043
|
+
role: "assistant",
|
|
2044
|
+
content: step.text,
|
|
2045
|
+
...step.continuation ? { continuation: step.continuation } : {}
|
|
2046
|
+
});
|
|
2047
|
+
messages.push({
|
|
2048
|
+
role: "user",
|
|
2049
|
+
content: "Return the final answer using the requested output schema and the tool results above."
|
|
2050
|
+
});
|
|
2051
|
+
finalizing = true;
|
|
2052
|
+
pending = void 0;
|
|
2053
|
+
save();
|
|
2054
|
+
continue;
|
|
1775
2055
|
}
|
|
2056
|
+
completed = true;
|
|
1776
2057
|
break;
|
|
1777
2058
|
}
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
2059
|
+
if (finalizing)
|
|
2060
|
+
throw new Error(
|
|
2061
|
+
"Provider requested a tool during schema-only finalization."
|
|
2062
|
+
);
|
|
2063
|
+
if (!reused) {
|
|
2064
|
+
allToolCalls.push(...toolCalls);
|
|
2065
|
+
messages.push({
|
|
2066
|
+
role: "assistant",
|
|
2067
|
+
content: step.text,
|
|
2068
|
+
toolCalls,
|
|
2069
|
+
...step.continuation ? { continuation: step.continuation } : {}
|
|
2070
|
+
});
|
|
2071
|
+
}
|
|
2072
|
+
pending = step;
|
|
2073
|
+
save();
|
|
1784
2074
|
for (const toolCall of toolCalls) {
|
|
2075
|
+
if (toolResults.some((result2) => result2.toolCallId === toolCall.id))
|
|
2076
|
+
continue;
|
|
1785
2077
|
(0, import_core4.throwIfExecutionAborted)(abortSignal);
|
|
1786
2078
|
const tool = toolByName.get(toolCall.name);
|
|
1787
2079
|
if (!tool) {
|
|
@@ -1811,7 +2103,7 @@ var runReasonToolLoop = async (args) => {
|
|
|
1811
2103
|
input: toolCall.input
|
|
1812
2104
|
});
|
|
1813
2105
|
}
|
|
1814
|
-
if (shouldApproveTool) {
|
|
2106
|
+
if (shouldApproveTool && !approvedCalls.includes(toolCall.id)) {
|
|
1815
2107
|
const approval = await awaitInterruptInternal({
|
|
1816
2108
|
id: `tool:${toolCall.id}`,
|
|
1817
2109
|
request: {
|
|
@@ -1846,8 +2138,18 @@ var runReasonToolLoop = async (args) => {
|
|
|
1846
2138
|
isError: true
|
|
1847
2139
|
});
|
|
1848
2140
|
}
|
|
2141
|
+
messages.push({
|
|
2142
|
+
role: "tool",
|
|
2143
|
+
content: denied.content,
|
|
2144
|
+
toolCallId: denied.toolCallId,
|
|
2145
|
+
name: denied.name,
|
|
2146
|
+
isError: true
|
|
2147
|
+
});
|
|
2148
|
+
save();
|
|
1849
2149
|
continue;
|
|
1850
2150
|
}
|
|
2151
|
+
approvedCalls.push(toolCall.id);
|
|
2152
|
+
save();
|
|
1851
2153
|
}
|
|
1852
2154
|
try {
|
|
1853
2155
|
(0, import_core4.throwIfExecutionAborted)(abortSignal);
|
|
@@ -1901,32 +2203,26 @@ var runReasonToolLoop = async (args) => {
|
|
|
1901
2203
|
message: result2.content
|
|
1902
2204
|
});
|
|
1903
2205
|
}
|
|
2206
|
+
const completed2 = toolResults.at(-1);
|
|
2207
|
+
if (completed2)
|
|
2208
|
+
messages.push({
|
|
2209
|
+
role: "tool",
|
|
2210
|
+
content: completed2.content,
|
|
2211
|
+
toolCallId: completed2.toolCallId,
|
|
2212
|
+
name: completed2.name,
|
|
2213
|
+
...completed2.isError !== void 0 ? { isError: completed2.isError } : {}
|
|
2214
|
+
});
|
|
2215
|
+
save();
|
|
1904
2216
|
}
|
|
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
|
-
}
|
|
2217
|
+
pending = void 0;
|
|
2218
|
+
save();
|
|
1923
2219
|
}
|
|
1924
|
-
if (
|
|
2220
|
+
if (!completed) {
|
|
1925
2221
|
throw new Error(
|
|
1926
2222
|
`useReason tool loop reached maxSteps (${maxSteps}) before producing a final response.`
|
|
1927
2223
|
);
|
|
1928
2224
|
}
|
|
1929
|
-
if (useReasonArgs.outputSchema) {
|
|
2225
|
+
if (useReasonArgs.outputSchema && finalOutput === void 0) {
|
|
1930
2226
|
finalOutput = parseReasonOutputWithSchema({
|
|
1931
2227
|
text: finalText,
|
|
1932
2228
|
schema: useReasonArgs.outputSchema,
|
|
@@ -1973,6 +2269,8 @@ var runReasonToolLoop = async (args) => {
|
|
|
1973
2269
|
output: useReasonArgs.telemetry?.output ?? finalOutput ?? finalText
|
|
1974
2270
|
}
|
|
1975
2271
|
});
|
|
2272
|
+
ctx.currentNodeState.byKey[checkpointKey] = { status: "completed", result };
|
|
2273
|
+
ctx.stateDirty = true;
|
|
1976
2274
|
return result;
|
|
1977
2275
|
} catch (error) {
|
|
1978
2276
|
traceSpan?.fail?.(error, {
|
|
@@ -2008,7 +2306,18 @@ async function useReason(args) {
|
|
|
2008
2306
|
args.model.options?.abortSignal
|
|
2009
2307
|
);
|
|
2010
2308
|
(0, import_core5.throwIfExecutionAborted)(abortSignal);
|
|
2011
|
-
|
|
2309
|
+
const allowValidatedToolOutput = Boolean(
|
|
2310
|
+
args.outputSchema && !(args.responseFormat ?? args.model.options?.responseFormat)
|
|
2311
|
+
);
|
|
2312
|
+
const inferred = inferOutputFormat(
|
|
2313
|
+
args.outputSchema,
|
|
2314
|
+
args.responseFormat ?? args.model.options?.responseFormat
|
|
2315
|
+
);
|
|
2316
|
+
args = {
|
|
2317
|
+
...args,
|
|
2318
|
+
abortSignal,
|
|
2319
|
+
...!args.interrupt && inferred.responseFormat ? { responseFormat: inferred.responseFormat } : {}
|
|
2320
|
+
};
|
|
2012
2321
|
const id = typeof args.id === "string" && args.id.length > 0 ? args.id : void 0;
|
|
2013
2322
|
const opId = createRuntimeId2();
|
|
2014
2323
|
const checkpointKey = resolveReasonCheckpointKey({
|
|
@@ -2052,6 +2361,9 @@ async function useReason(args) {
|
|
|
2052
2361
|
if (args.tools?.length) {
|
|
2053
2362
|
return runReasonToolLoop({
|
|
2054
2363
|
useReasonArgs: args,
|
|
2364
|
+
allowValidatedToolOutput,
|
|
2365
|
+
checkpointKey,
|
|
2366
|
+
initialWarnings: inferred.warnings,
|
|
2055
2367
|
...id ? { id } : {},
|
|
2056
2368
|
opId,
|
|
2057
2369
|
...traceSpan ? { traceSpan } : {}
|
|
@@ -2075,10 +2387,7 @@ async function useReason(args) {
|
|
|
2075
2387
|
let aggregatedUsage;
|
|
2076
2388
|
let finalFinishReason;
|
|
2077
2389
|
let aggregatedProviderMetadata;
|
|
2078
|
-
let aggregatedWarnings;
|
|
2079
|
-
const emittedSetValues = /* @__PURE__ */ new Map();
|
|
2080
|
-
const emittedAppendCounts = /* @__PURE__ */ new Map();
|
|
2081
|
-
const emittedTextValues = /* @__PURE__ */ new Map();
|
|
2390
|
+
let aggregatedWarnings = inferred.warnings;
|
|
2082
2391
|
if (existingCheckpoint) {
|
|
2083
2392
|
firstText = existingCheckpoint.firstText;
|
|
2084
2393
|
firstRaw = existingCheckpoint.firstRaw;
|
|
@@ -2100,78 +2409,7 @@ async function useReason(args) {
|
|
|
2100
2409
|
emit: suppressTextStream ? false : args.emit,
|
|
2101
2410
|
stream: useStructuredIncrementalStreaming ? true : suppressTextStream ? false : args.stream,
|
|
2102
2411
|
...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
|
-
}
|
|
2412
|
+
onTextChunk: createStructuredOutputStreamer(args, id, opId)
|
|
2175
2413
|
} : {}
|
|
2176
2414
|
},
|
|
2177
2415
|
reasonMeta,
|
|
@@ -2291,6 +2529,7 @@ async function useReason(args) {
|
|
|
2291
2529
|
const second = await reasonEngine(
|
|
2292
2530
|
{
|
|
2293
2531
|
...args,
|
|
2532
|
+
...inferred.responseFormat ? { responseFormat: inferred.responseFormat } : {},
|
|
2294
2533
|
emit: suppressTextStream ? false : args.emit,
|
|
2295
2534
|
stream: suppressTextStream ? false : args.stream
|
|
2296
2535
|
},
|