@jaypie/llm 1.2.40 → 1.2.41
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/dist/cjs/index.cjs +164 -64
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/operate/OperateLoop.d.ts +6 -0
- package/dist/cjs/util/fillFormatArrays.d.ts +17 -0
- package/dist/cjs/util/index.d.ts +1 -0
- package/dist/esm/index.js +164 -64
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/operate/OperateLoop.d.ts +6 -0
- package/dist/esm/util/fillFormatArrays.d.ts +17 -0
- package/dist/esm/util/index.d.ts +1 -0
- package/package.json +1 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -575,6 +575,154 @@ function extractReasoning(history) {
|
|
|
575
575
|
return reasoningTexts;
|
|
576
576
|
}
|
|
577
577
|
|
|
578
|
+
function naturalZodSchema(definition) {
|
|
579
|
+
if (Array.isArray(definition)) {
|
|
580
|
+
if (definition.length === 0) {
|
|
581
|
+
// Handle empty array - accept any[]
|
|
582
|
+
return v4.z.array(v4.z.any());
|
|
583
|
+
}
|
|
584
|
+
else if (definition.length === 1) {
|
|
585
|
+
// Handle array types
|
|
586
|
+
const itemType = definition[0];
|
|
587
|
+
switch (itemType) {
|
|
588
|
+
case String:
|
|
589
|
+
return v4.z.array(v4.z.string());
|
|
590
|
+
case Number:
|
|
591
|
+
return v4.z.array(v4.z.number());
|
|
592
|
+
case Boolean:
|
|
593
|
+
return v4.z.array(v4.z.boolean());
|
|
594
|
+
default:
|
|
595
|
+
if (typeof itemType === "object") {
|
|
596
|
+
// Handle array of objects
|
|
597
|
+
return v4.z.array(naturalZodSchema(itemType));
|
|
598
|
+
}
|
|
599
|
+
// Handle enum arrays
|
|
600
|
+
return v4.z.enum(definition);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
else {
|
|
604
|
+
// Handle enum arrays
|
|
605
|
+
return v4.z.enum(definition);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
else if (definition && typeof definition === "object") {
|
|
609
|
+
if (Object.keys(definition).length === 0) {
|
|
610
|
+
// Handle empty object - accept any key-value pairs
|
|
611
|
+
return v4.z.record(v4.z.string(), v4.z.any());
|
|
612
|
+
}
|
|
613
|
+
else {
|
|
614
|
+
// Handle object with properties
|
|
615
|
+
const schemaShape = {};
|
|
616
|
+
for (const [key, value] of Object.entries(definition)) {
|
|
617
|
+
schemaShape[key] = naturalZodSchema(value);
|
|
618
|
+
}
|
|
619
|
+
return v4.z.object(schemaShape);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
else {
|
|
623
|
+
switch (definition) {
|
|
624
|
+
case String:
|
|
625
|
+
return v4.z.string();
|
|
626
|
+
case Number:
|
|
627
|
+
return v4.z.number();
|
|
628
|
+
case Boolean:
|
|
629
|
+
return v4.z.boolean();
|
|
630
|
+
case Object:
|
|
631
|
+
return v4.z.record(v4.z.string(), v4.z.any());
|
|
632
|
+
case Array:
|
|
633
|
+
return v4.z.array(v4.z.any());
|
|
634
|
+
default:
|
|
635
|
+
throw new Error(`Unsupported type: ${definition}`);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
//
|
|
641
|
+
//
|
|
642
|
+
// Helpers
|
|
643
|
+
//
|
|
644
|
+
function isPlainObject(value) {
|
|
645
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Convert a `format` declaration (Zod schema, NaturalSchema, or a JSON Schema
|
|
649
|
+
* JsonObject) into a plain JSON Schema we can walk. Mirrors the conversion the
|
|
650
|
+
* provider adapters perform in `formatOutputSchema`, but without any
|
|
651
|
+
* provider-specific sanitization.
|
|
652
|
+
*/
|
|
653
|
+
function formatToJsonSchema(format) {
|
|
654
|
+
if (format instanceof v4.z.ZodType) {
|
|
655
|
+
return v4.z.toJSONSchema(format);
|
|
656
|
+
}
|
|
657
|
+
if (isPlainObject(format) && format.type === "json_schema") {
|
|
658
|
+
const clone = structuredClone(format);
|
|
659
|
+
clone.type = "object";
|
|
660
|
+
return clone;
|
|
661
|
+
}
|
|
662
|
+
try {
|
|
663
|
+
return v4.z.toJSONSchema(naturalZodSchema(format));
|
|
664
|
+
}
|
|
665
|
+
catch {
|
|
666
|
+
return undefined;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Walk a JSON Schema alongside a parsed value, filling any declared array field
|
|
671
|
+
* that is absent (`undefined`/`null`) with `[]`. Recurses into object
|
|
672
|
+
* properties and array items so nested declared arrays are also backfilled.
|
|
673
|
+
*/
|
|
674
|
+
function fillFromSchema(schema, value) {
|
|
675
|
+
if (!isPlainObject(schema)) {
|
|
676
|
+
return value;
|
|
677
|
+
}
|
|
678
|
+
const type = schema.type;
|
|
679
|
+
const isArray = type === "array" || (type === undefined && "items" in schema);
|
|
680
|
+
if (isArray) {
|
|
681
|
+
if (value === undefined || value === null) {
|
|
682
|
+
return [];
|
|
683
|
+
}
|
|
684
|
+
const items = schema.items;
|
|
685
|
+
if (Array.isArray(value) && isPlainObject(items)) {
|
|
686
|
+
return value.map((entry) => fillFromSchema(items, entry));
|
|
687
|
+
}
|
|
688
|
+
return value;
|
|
689
|
+
}
|
|
690
|
+
const isObject = type === "object" || (type === undefined && "properties" in schema);
|
|
691
|
+
if (isObject) {
|
|
692
|
+
if (!isPlainObject(value)) {
|
|
693
|
+
return value;
|
|
694
|
+
}
|
|
695
|
+
const properties = schema.properties;
|
|
696
|
+
if (isPlainObject(properties)) {
|
|
697
|
+
for (const [key, propSchema] of Object.entries(properties)) {
|
|
698
|
+
value[key] = fillFromSchema(propSchema, value[key]);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
return value;
|
|
702
|
+
}
|
|
703
|
+
return value;
|
|
704
|
+
}
|
|
705
|
+
//
|
|
706
|
+
//
|
|
707
|
+
// Main
|
|
708
|
+
//
|
|
709
|
+
/**
|
|
710
|
+
* Ensure every array field declared in `format` is present in `content` as an
|
|
711
|
+
* array. A declared `format` is a schema contract: an empty list should surface
|
|
712
|
+
* as `[]`, not be dropped from the response. Some providers/models omit empty
|
|
713
|
+
* array fields entirely, leaving consumers to read `.length` on `undefined`.
|
|
714
|
+
*
|
|
715
|
+
* Only mutates a (cloned) structured object; strings and non-objects pass
|
|
716
|
+
* through untouched.
|
|
717
|
+
*/
|
|
718
|
+
function fillFormatArrays({ content, format, }) {
|
|
719
|
+
const schema = formatToJsonSchema(format);
|
|
720
|
+
if (!schema) {
|
|
721
|
+
return content;
|
|
722
|
+
}
|
|
723
|
+
return fillFromSchema(schema, structuredClone(content));
|
|
724
|
+
}
|
|
725
|
+
|
|
578
726
|
/**
|
|
579
727
|
* Converts a string to a standardized LlmInputMessage
|
|
580
728
|
* @param input - String to format
|
|
@@ -702,68 +850,6 @@ function maxTurnsFromOptions(options) {
|
|
|
702
850
|
return 1;
|
|
703
851
|
}
|
|
704
852
|
|
|
705
|
-
function naturalZodSchema(definition) {
|
|
706
|
-
if (Array.isArray(definition)) {
|
|
707
|
-
if (definition.length === 0) {
|
|
708
|
-
// Handle empty array - accept any[]
|
|
709
|
-
return v4.z.array(v4.z.any());
|
|
710
|
-
}
|
|
711
|
-
else if (definition.length === 1) {
|
|
712
|
-
// Handle array types
|
|
713
|
-
const itemType = definition[0];
|
|
714
|
-
switch (itemType) {
|
|
715
|
-
case String:
|
|
716
|
-
return v4.z.array(v4.z.string());
|
|
717
|
-
case Number:
|
|
718
|
-
return v4.z.array(v4.z.number());
|
|
719
|
-
case Boolean:
|
|
720
|
-
return v4.z.array(v4.z.boolean());
|
|
721
|
-
default:
|
|
722
|
-
if (typeof itemType === "object") {
|
|
723
|
-
// Handle array of objects
|
|
724
|
-
return v4.z.array(naturalZodSchema(itemType));
|
|
725
|
-
}
|
|
726
|
-
// Handle enum arrays
|
|
727
|
-
return v4.z.enum(definition);
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
else {
|
|
731
|
-
// Handle enum arrays
|
|
732
|
-
return v4.z.enum(definition);
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
else if (definition && typeof definition === "object") {
|
|
736
|
-
if (Object.keys(definition).length === 0) {
|
|
737
|
-
// Handle empty object - accept any key-value pairs
|
|
738
|
-
return v4.z.record(v4.z.string(), v4.z.any());
|
|
739
|
-
}
|
|
740
|
-
else {
|
|
741
|
-
// Handle object with properties
|
|
742
|
-
const schemaShape = {};
|
|
743
|
-
for (const [key, value] of Object.entries(definition)) {
|
|
744
|
-
schemaShape[key] = naturalZodSchema(value);
|
|
745
|
-
}
|
|
746
|
-
return v4.z.object(schemaShape);
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
else {
|
|
750
|
-
switch (definition) {
|
|
751
|
-
case String:
|
|
752
|
-
return v4.z.string();
|
|
753
|
-
case Number:
|
|
754
|
-
return v4.z.number();
|
|
755
|
-
case Boolean:
|
|
756
|
-
return v4.z.boolean();
|
|
757
|
-
case Object:
|
|
758
|
-
return v4.z.record(v4.z.string(), v4.z.any());
|
|
759
|
-
case Array:
|
|
760
|
-
return v4.z.array(v4.z.any());
|
|
761
|
-
default:
|
|
762
|
-
throw new Error(`Unsupported type: ${definition}`);
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
|
|
767
853
|
//
|
|
768
854
|
// Constants
|
|
769
855
|
//
|
|
@@ -6092,7 +6178,7 @@ class OperateLoop {
|
|
|
6092
6178
|
if (this.adapter.hasStructuredOutput(response)) {
|
|
6093
6179
|
const structuredOutput = this.adapter.extractStructuredOutput(response);
|
|
6094
6180
|
if (structuredOutput) {
|
|
6095
|
-
state.responseBuilder.setContent(structuredOutput);
|
|
6181
|
+
state.responseBuilder.setContent(this.applyFormatArrayDefaults(structuredOutput, options));
|
|
6096
6182
|
state.responseBuilder.complete();
|
|
6097
6183
|
return false; // Stop loop
|
|
6098
6184
|
}
|
|
@@ -6217,7 +6303,7 @@ class OperateLoop {
|
|
|
6217
6303
|
}
|
|
6218
6304
|
}
|
|
6219
6305
|
// No tool calls or no toolkit - we're done
|
|
6220
|
-
state.responseBuilder.setContent(parsed.content);
|
|
6306
|
+
state.responseBuilder.setContent(this.applyFormatArrayDefaults(parsed.content, options));
|
|
6221
6307
|
state.responseBuilder.complete();
|
|
6222
6308
|
// Add final history items
|
|
6223
6309
|
const historyItems = this.adapter.responseToHistoryItems(parsed.raw);
|
|
@@ -6226,6 +6312,20 @@ class OperateLoop {
|
|
|
6226
6312
|
}
|
|
6227
6313
|
return false; // Stop loop
|
|
6228
6314
|
}
|
|
6315
|
+
/**
|
|
6316
|
+
* Backfill declared array fields when a `format` is supplied. A declared
|
|
6317
|
+
* `format` is a schema contract: an empty array field should surface as `[]`
|
|
6318
|
+
* rather than be dropped by a provider/model that omits empty lists.
|
|
6319
|
+
*/
|
|
6320
|
+
applyFormatArrayDefaults(content, options) {
|
|
6321
|
+
if (!options.format) {
|
|
6322
|
+
return content;
|
|
6323
|
+
}
|
|
6324
|
+
if (typeof content !== "object" || content === null) {
|
|
6325
|
+
return content;
|
|
6326
|
+
}
|
|
6327
|
+
return fillFormatArrays({ content, format: options.format });
|
|
6328
|
+
}
|
|
6229
6329
|
/**
|
|
6230
6330
|
* Sync the current input state from the updated provider request.
|
|
6231
6331
|
* This is necessary because appendToolResult modifies the provider-specific request,
|