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