@jaypie/llm 1.3.1 → 1.3.3
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 +136 -13
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +661 -0
- package/dist/cjs/operate/OperateLoop.d.ts +6 -3
- package/dist/cjs/providers/anthropic/utils.d.ts +2 -1
- package/dist/cjs/providers/bedrock/utils.d.ts +2 -1
- package/dist/cjs/providers/google/utils.d.ts +2 -1
- package/dist/cjs/providers/openai/utils.d.ts +2 -1
- package/dist/cjs/providers/openrouter/utils.d.ts +2 -1
- package/dist/cjs/providers/xai/utils.d.ts +2 -1
- package/dist/cjs/util/index.d.ts +1 -0
- package/dist/cjs/util/logger.d.ts +2 -1
- package/dist/cjs/util/repairFormatKeys.d.ts +19 -0
- package/dist/esm/index.d.ts +661 -16
- package/dist/esm/index.js +136 -13
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/operate/OperateLoop.d.ts +6 -3
- package/dist/esm/providers/anthropic/utils.d.ts +2 -1
- package/dist/esm/providers/bedrock/utils.d.ts +2 -1
- package/dist/esm/providers/google/utils.d.ts +2 -1
- package/dist/esm/providers/openai/utils.d.ts +2 -1
- package/dist/esm/providers/openrouter/utils.d.ts +2 -1
- package/dist/esm/providers/xai/utils.d.ts +2 -1
- package/dist/esm/util/index.d.ts +1 -0
- package/dist/esm/util/logger.d.ts +2 -1
- package/dist/esm/util/repairFormatKeys.d.ts +19 -0
- package/package.json +11 -5
package/dist/esm/index.js
CHANGED
|
@@ -638,7 +638,7 @@ function naturalZodSchema(definition) {
|
|
|
638
638
|
//
|
|
639
639
|
// Helpers
|
|
640
640
|
//
|
|
641
|
-
function isPlainObject(value) {
|
|
641
|
+
function isPlainObject$1(value) {
|
|
642
642
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
643
643
|
}
|
|
644
644
|
/**
|
|
@@ -647,11 +647,11 @@ function isPlainObject(value) {
|
|
|
647
647
|
* provider adapters perform in `formatOutputSchema`, but without any
|
|
648
648
|
* provider-specific sanitization.
|
|
649
649
|
*/
|
|
650
|
-
function formatToJsonSchema(format) {
|
|
650
|
+
function formatToJsonSchema$1(format) {
|
|
651
651
|
if (format instanceof z.ZodType) {
|
|
652
652
|
return z.toJSONSchema(format);
|
|
653
653
|
}
|
|
654
|
-
if (isPlainObject(format) && format.type === "json_schema") {
|
|
654
|
+
if (isPlainObject$1(format) && format.type === "json_schema") {
|
|
655
655
|
const clone = structuredClone(format);
|
|
656
656
|
clone.type = "object";
|
|
657
657
|
return clone;
|
|
@@ -669,7 +669,7 @@ function formatToJsonSchema(format) {
|
|
|
669
669
|
* properties and array items so nested declared arrays are also backfilled.
|
|
670
670
|
*/
|
|
671
671
|
function fillFromSchema(schema, value) {
|
|
672
|
-
if (!isPlainObject(schema)) {
|
|
672
|
+
if (!isPlainObject$1(schema)) {
|
|
673
673
|
return value;
|
|
674
674
|
}
|
|
675
675
|
const type = schema.type;
|
|
@@ -679,18 +679,18 @@ function fillFromSchema(schema, value) {
|
|
|
679
679
|
return [];
|
|
680
680
|
}
|
|
681
681
|
const items = schema.items;
|
|
682
|
-
if (Array.isArray(value) && isPlainObject(items)) {
|
|
682
|
+
if (Array.isArray(value) && isPlainObject$1(items)) {
|
|
683
683
|
return value.map((entry) => fillFromSchema(items, entry));
|
|
684
684
|
}
|
|
685
685
|
return value;
|
|
686
686
|
}
|
|
687
687
|
const isObject = type === "object" || (type === undefined && "properties" in schema);
|
|
688
688
|
if (isObject) {
|
|
689
|
-
if (!isPlainObject(value)) {
|
|
689
|
+
if (!isPlainObject$1(value)) {
|
|
690
690
|
return value;
|
|
691
691
|
}
|
|
692
692
|
const properties = schema.properties;
|
|
693
|
-
if (isPlainObject(properties)) {
|
|
693
|
+
if (isPlainObject$1(properties)) {
|
|
694
694
|
for (const [key, propSchema] of Object.entries(properties)) {
|
|
695
695
|
value[key] = fillFromSchema(propSchema, value[key]);
|
|
696
696
|
}
|
|
@@ -713,7 +713,7 @@ function fillFromSchema(schema, value) {
|
|
|
713
713
|
* through untouched.
|
|
714
714
|
*/
|
|
715
715
|
function fillFormatArrays({ content, format, }) {
|
|
716
|
-
const schema = formatToJsonSchema(format);
|
|
716
|
+
const schema = formatToJsonSchema$1(format);
|
|
717
717
|
if (!schema) {
|
|
718
718
|
return content;
|
|
719
719
|
}
|
|
@@ -975,6 +975,116 @@ function random$1(defaultSeed) {
|
|
|
975
975
|
return rngFn;
|
|
976
976
|
}
|
|
977
977
|
|
|
978
|
+
//
|
|
979
|
+
//
|
|
980
|
+
// Helpers
|
|
981
|
+
//
|
|
982
|
+
function isPlainObject(value) {
|
|
983
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* Convert a `format` declaration (Zod schema, NaturalSchema, or a JSON Schema
|
|
987
|
+
* JsonObject) into a plain JSON Schema we can walk. Mirrors the conversion the
|
|
988
|
+
* provider adapters perform in `formatOutputSchema`, but without any
|
|
989
|
+
* provider-specific sanitization.
|
|
990
|
+
*/
|
|
991
|
+
function formatToJsonSchema(format) {
|
|
992
|
+
if (format instanceof z.ZodType) {
|
|
993
|
+
return z.toJSONSchema(format);
|
|
994
|
+
}
|
|
995
|
+
if (isPlainObject(format) && format.type === "json_schema") {
|
|
996
|
+
const clone = structuredClone(format);
|
|
997
|
+
clone.type = "object";
|
|
998
|
+
return clone;
|
|
999
|
+
}
|
|
1000
|
+
try {
|
|
1001
|
+
return z.toJSONSchema(naturalZodSchema(format));
|
|
1002
|
+
}
|
|
1003
|
+
catch {
|
|
1004
|
+
return undefined;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
/**
|
|
1008
|
+
* Strip a single pair of surrounding double quotes from a key, if present.
|
|
1009
|
+
* `"Merchant Request"` -> `Merchant Request`; `Confidence` -> `Confidence`.
|
|
1010
|
+
*/
|
|
1011
|
+
function dequoteKey(key) {
|
|
1012
|
+
if (key.length >= 2 && key.startsWith('"') && key.endsWith('"')) {
|
|
1013
|
+
return key.slice(1, -1);
|
|
1014
|
+
}
|
|
1015
|
+
return key;
|
|
1016
|
+
}
|
|
1017
|
+
/**
|
|
1018
|
+
* Walk a JSON Schema alongside a parsed value, renaming any object key whose
|
|
1019
|
+
* de-quoted form matches a declared property name. Recurses into declared
|
|
1020
|
+
* object properties and array items so nested corrupted keys are also repaired.
|
|
1021
|
+
*/
|
|
1022
|
+
function repairFromSchema(schema, value) {
|
|
1023
|
+
if (!isPlainObject(schema)) {
|
|
1024
|
+
return value;
|
|
1025
|
+
}
|
|
1026
|
+
const type = schema.type;
|
|
1027
|
+
const isArray = type === "array" || (type === undefined && "items" in schema);
|
|
1028
|
+
if (isArray) {
|
|
1029
|
+
const items = schema.items;
|
|
1030
|
+
if (Array.isArray(value) && isPlainObject(items)) {
|
|
1031
|
+
return value.map((entry) => repairFromSchema(items, entry));
|
|
1032
|
+
}
|
|
1033
|
+
return value;
|
|
1034
|
+
}
|
|
1035
|
+
const isObject = type === "object" || (type === undefined && "properties" in schema);
|
|
1036
|
+
if (isObject && isPlainObject(value)) {
|
|
1037
|
+
const properties = schema.properties;
|
|
1038
|
+
if (isPlainObject(properties)) {
|
|
1039
|
+
const declared = new Set(Object.keys(properties));
|
|
1040
|
+
// Repair keys: rename a quote-wrapped key to its declared form, unless
|
|
1041
|
+
// the correct key is already present (then drop the corrupted duplicate).
|
|
1042
|
+
for (const key of Object.keys(value)) {
|
|
1043
|
+
if (declared.has(key)) {
|
|
1044
|
+
continue;
|
|
1045
|
+
}
|
|
1046
|
+
const repaired = dequoteKey(key);
|
|
1047
|
+
if (repaired !== key && declared.has(repaired)) {
|
|
1048
|
+
if (!(repaired in value)) {
|
|
1049
|
+
value[repaired] = value[key];
|
|
1050
|
+
}
|
|
1051
|
+
delete value[key];
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
// Recurse into declared properties now that keys are aligned.
|
|
1055
|
+
for (const [key, propSchema] of Object.entries(properties)) {
|
|
1056
|
+
if (key in value) {
|
|
1057
|
+
value[key] = repairFromSchema(propSchema, value[key]);
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
return value;
|
|
1062
|
+
}
|
|
1063
|
+
return value;
|
|
1064
|
+
}
|
|
1065
|
+
//
|
|
1066
|
+
//
|
|
1067
|
+
// Main
|
|
1068
|
+
//
|
|
1069
|
+
/**
|
|
1070
|
+
* Repair structured-output keys that a provider/model corrupted by wrapping
|
|
1071
|
+
* them in literal double quotes (observed on OpenAI for multi-word `format`
|
|
1072
|
+
* keys: `Merchant Request` returned as `"Merchant Request"`). A declared
|
|
1073
|
+
* `format` is a schema contract: returned keys should match the declared names
|
|
1074
|
+
* exactly. Any returned key whose de-quoted form matches a declared key is
|
|
1075
|
+
* renamed back to the declared key.
|
|
1076
|
+
*
|
|
1077
|
+
* Only mutates a (cloned) structured object; strings and non-objects pass
|
|
1078
|
+
* through untouched.
|
|
1079
|
+
*/
|
|
1080
|
+
function repairFormatKeys({ content, format, }) {
|
|
1081
|
+
const schema = formatToJsonSchema(format);
|
|
1082
|
+
if (!schema) {
|
|
1083
|
+
return content;
|
|
1084
|
+
}
|
|
1085
|
+
return repairFromSchema(schema, structuredClone(content));
|
|
1086
|
+
}
|
|
1087
|
+
|
|
978
1088
|
/**
|
|
979
1089
|
* Helper function to safely call a function that might throw
|
|
980
1090
|
* @param fn Function to call safely
|
|
@@ -3544,7 +3654,16 @@ class OpenAiAdapter extends BaseProviderAdapter {
|
|
|
3544
3654
|
? schema
|
|
3545
3655
|
: naturalZodSchema(schema);
|
|
3546
3656
|
const responseFormat = zodResponseFormat(zodSchema, "response");
|
|
3547
|
-
|
|
3657
|
+
// Re-spread to drop zod v4's non-enumerable `~standard` Standard-Schema
|
|
3658
|
+
// interop marker, then strip `$schema`: OpenAI's strict json_schema subset
|
|
3659
|
+
// does not recognize the draft-2020-12 `$schema` keyword, and shipping it
|
|
3660
|
+
// can leave strict structured-output enforcement silently disabled (the
|
|
3661
|
+
// model then free-forms, omitting required fields and corrupting keys).
|
|
3662
|
+
// Mirrors the OpenRouter adapter's sanitization.
|
|
3663
|
+
const jsonSchema = {
|
|
3664
|
+
...z.toJSONSchema(zodSchema),
|
|
3665
|
+
};
|
|
3666
|
+
delete jsonSchema.$schema;
|
|
3548
3667
|
// OpenAI requires additionalProperties to be false on all objects
|
|
3549
3668
|
const checks = [jsonSchema];
|
|
3550
3669
|
while (checks.length > 0) {
|
|
@@ -6310,9 +6429,12 @@ class OperateLoop {
|
|
|
6310
6429
|
return false; // Stop loop
|
|
6311
6430
|
}
|
|
6312
6431
|
/**
|
|
6313
|
-
*
|
|
6314
|
-
* `format` is a schema contract:
|
|
6315
|
-
*
|
|
6432
|
+
* Reconcile structured output against the declared `format` contract. A
|
|
6433
|
+
* declared `format` is a schema contract: returned keys should match the
|
|
6434
|
+
* declared names exactly and every declared array field should be present.
|
|
6435
|
+
* First repairs keys a provider/model corrupted by wrapping them in literal
|
|
6436
|
+
* double quotes (observed on OpenAI for multi-word keys), then backfills any
|
|
6437
|
+
* declared array field a provider/model omitted, surfacing it as `[]`.
|
|
6316
6438
|
*/
|
|
6317
6439
|
applyFormatArrayDefaults(content, options) {
|
|
6318
6440
|
if (!options.format) {
|
|
@@ -6321,7 +6443,8 @@ class OperateLoop {
|
|
|
6321
6443
|
if (typeof content !== "object" || content === null) {
|
|
6322
6444
|
return content;
|
|
6323
6445
|
}
|
|
6324
|
-
|
|
6446
|
+
const repaired = repairFormatKeys({ content, format: options.format });
|
|
6447
|
+
return fillFormatArrays({ content: repaired, format: options.format });
|
|
6325
6448
|
}
|
|
6326
6449
|
/**
|
|
6327
6450
|
* Sync the current input state from the updated provider request.
|