@prestyj/ai 5.16.2 → 5.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +165 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +165 -24
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -572,6 +572,108 @@ function normalizeRootForAnthropic(schema) {
|
|
|
572
572
|
return out;
|
|
573
573
|
}
|
|
574
574
|
|
|
575
|
+
// src/utils/strict-tool-schema.ts
|
|
576
|
+
var UnsupportedStrictSchemaError = class extends Error {
|
|
577
|
+
};
|
|
578
|
+
var UNSUPPORTED_KEYS = [
|
|
579
|
+
"$ref",
|
|
580
|
+
"$defs",
|
|
581
|
+
"definitions",
|
|
582
|
+
"allOf",
|
|
583
|
+
"oneOf",
|
|
584
|
+
"patternProperties",
|
|
585
|
+
"dependentSchemas",
|
|
586
|
+
"dependencies",
|
|
587
|
+
"unevaluatedProperties",
|
|
588
|
+
"propertyNames",
|
|
589
|
+
"contains",
|
|
590
|
+
"prefixItems",
|
|
591
|
+
"not",
|
|
592
|
+
"if",
|
|
593
|
+
"then",
|
|
594
|
+
"else"
|
|
595
|
+
];
|
|
596
|
+
function isSchemaObject(value) {
|
|
597
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
598
|
+
}
|
|
599
|
+
function isStructured(value) {
|
|
600
|
+
if (!isSchemaObject(value)) return false;
|
|
601
|
+
const types = typeof value.type === "string" ? [value.type] : Array.isArray(value.type) ? value.type : [];
|
|
602
|
+
return types.includes("object") || types.includes("array") || value.properties !== void 0 || value.items !== void 0;
|
|
603
|
+
}
|
|
604
|
+
function allowsNull(schema) {
|
|
605
|
+
if (!isSchemaObject(schema)) return false;
|
|
606
|
+
if (schema.type === "null" || Array.isArray(schema.type) && schema.type.includes("null"))
|
|
607
|
+
return true;
|
|
608
|
+
if (schema.const === null || Array.isArray(schema.enum) && schema.enum.includes(null))
|
|
609
|
+
return true;
|
|
610
|
+
return Array.isArray(schema.anyOf) && schema.anyOf.some((variant) => allowsNull(variant));
|
|
611
|
+
}
|
|
612
|
+
function makeNodeStrict(schema) {
|
|
613
|
+
if (!isSchemaObject(schema)) {
|
|
614
|
+
throw new UnsupportedStrictSchemaError("boolean schemas are unsupported");
|
|
615
|
+
}
|
|
616
|
+
for (const key of UNSUPPORTED_KEYS) {
|
|
617
|
+
if (schema[key] !== void 0) {
|
|
618
|
+
throw new UnsupportedStrictSchemaError(`${key} schemas are unsupported`);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
if (schema.anyOf !== void 0) {
|
|
622
|
+
if (!Array.isArray(schema.anyOf) || schema.anyOf.length === 0) {
|
|
623
|
+
throw new UnsupportedStrictSchemaError("anyOf must contain at least one schema");
|
|
624
|
+
}
|
|
625
|
+
for (const variant of schema.anyOf) {
|
|
626
|
+
if (isStructured(variant)) {
|
|
627
|
+
throw new UnsupportedStrictSchemaError("object and array unions are unsupported");
|
|
628
|
+
}
|
|
629
|
+
makeNodeStrict(variant);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
if (schema.items !== void 0) {
|
|
633
|
+
if (Array.isArray(schema.items)) {
|
|
634
|
+
throw new UnsupportedStrictSchemaError("tuple schemas are unsupported");
|
|
635
|
+
}
|
|
636
|
+
makeNodeStrict(schema.items);
|
|
637
|
+
}
|
|
638
|
+
const isObjectSchema = schema.type === "object";
|
|
639
|
+
if (schema.properties !== void 0 && !isObjectSchema) {
|
|
640
|
+
throw new UnsupportedStrictSchemaError("properties require type object");
|
|
641
|
+
}
|
|
642
|
+
if (!isObjectSchema) return;
|
|
643
|
+
if (schema.additionalProperties !== void 0 && schema.additionalProperties !== false) {
|
|
644
|
+
throw new UnsupportedStrictSchemaError(
|
|
645
|
+
"schema-valued or true additionalProperties is unsupported"
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
if (schema.properties !== void 0 && !isSchemaObject(schema.properties)) {
|
|
649
|
+
throw new UnsupportedStrictSchemaError("object properties must be a schema map");
|
|
650
|
+
}
|
|
651
|
+
const properties = schema.properties ?? {};
|
|
652
|
+
const required = new Set(Array.isArray(schema.required) ? schema.required : []);
|
|
653
|
+
for (const [key, property] of Object.entries(properties)) {
|
|
654
|
+
makeNodeStrict(property);
|
|
655
|
+
if (!required.has(key) && !allowsNull(property)) {
|
|
656
|
+
properties[key] = { anyOf: [property, { type: "null" }] };
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
schema.required = Object.keys(properties);
|
|
660
|
+
schema.additionalProperties = false;
|
|
661
|
+
}
|
|
662
|
+
function supportsStrictToolSampling(provider) {
|
|
663
|
+
return provider === "openai";
|
|
664
|
+
}
|
|
665
|
+
function makeStrictToolSchema(schema) {
|
|
666
|
+
const cloned = structuredClone(schema);
|
|
667
|
+
if (!isSchemaObject(cloned)) {
|
|
668
|
+
throw new UnsupportedStrictSchemaError("root schema must be an object");
|
|
669
|
+
}
|
|
670
|
+
makeNodeStrict(cloned);
|
|
671
|
+
if (cloned.type !== "object") {
|
|
672
|
+
throw new UnsupportedStrictSchemaError("root schema must have type object");
|
|
673
|
+
}
|
|
674
|
+
return cloned;
|
|
675
|
+
}
|
|
676
|
+
|
|
575
677
|
// src/providers/reasoning-field.ts
|
|
576
678
|
var REASONING_FIELD_ALIASES = [
|
|
577
679
|
"reasoning_content",
|
|
@@ -1074,13 +1176,11 @@ function toOpenAIMessages(messages, options) {
|
|
|
1074
1176
|
const parts = typeof msg.content === "string" ? msg.content : void 0;
|
|
1075
1177
|
const toolCalls = typeof msg.content !== "string" ? msg.content.filter(
|
|
1076
1178
|
(p) => p.type === "tool_call"
|
|
1077
|
-
).map(
|
|
1078
|
-
(tc)
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
})
|
|
1083
|
-
) : void 0;
|
|
1179
|
+
).map((tc) => ({
|
|
1180
|
+
id: remapToolCallId(tc.id, idMap),
|
|
1181
|
+
type: "function",
|
|
1182
|
+
function: { name: tc.name, arguments: JSON.stringify(tc.args) }
|
|
1183
|
+
})) : void 0;
|
|
1084
1184
|
const textParts = typeof msg.content !== "string" ? msg.content.filter((p) => p.type === "text").map((p) => p.text).join("") : void 0;
|
|
1085
1185
|
const thinkingParts = typeof msg.content !== "string" ? msg.content.filter((p) => p.type === "thinking").map((p) => p.text).join("") : void 0;
|
|
1086
1186
|
const contentValue = parts || textParts || null;
|
|
@@ -1156,15 +1256,28 @@ function toOpenAIMessages(messages, options) {
|
|
|
1156
1256
|
}
|
|
1157
1257
|
return out;
|
|
1158
1258
|
}
|
|
1159
|
-
function toOpenAITools(tools) {
|
|
1160
|
-
return tools.map((tool) =>
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1259
|
+
function toOpenAITools(tools, opts) {
|
|
1260
|
+
return tools.map((tool) => {
|
|
1261
|
+
let parameters = resolveToolSchema(tool);
|
|
1262
|
+
let strict;
|
|
1263
|
+
if (opts?.strict) {
|
|
1264
|
+
try {
|
|
1265
|
+
parameters = makeStrictToolSchema(parameters);
|
|
1266
|
+
strict = true;
|
|
1267
|
+
} catch (error) {
|
|
1268
|
+
if (!(error instanceof UnsupportedStrictSchemaError)) throw error;
|
|
1269
|
+
}
|
|
1166
1270
|
}
|
|
1167
|
-
|
|
1271
|
+
return {
|
|
1272
|
+
type: "function",
|
|
1273
|
+
function: {
|
|
1274
|
+
name: tool.name,
|
|
1275
|
+
description: tool.description,
|
|
1276
|
+
parameters,
|
|
1277
|
+
...strict ? { strict } : {}
|
|
1278
|
+
}
|
|
1279
|
+
};
|
|
1280
|
+
});
|
|
1168
1281
|
}
|
|
1169
1282
|
function toOpenAIToolChoice(choice) {
|
|
1170
1283
|
if (choice === "auto") return "auto";
|
|
@@ -2022,7 +2135,11 @@ async function* runStream2(options) {
|
|
|
2022
2135
|
...options.topP != null && !hasFixedKimiSampling ? { top_p: options.topP } : {},
|
|
2023
2136
|
...options.stop ? { stop: options.stop } : {},
|
|
2024
2137
|
...options.thinking && !usesThinkingParam && !isKimiK3 && !isKimiK27 && !isLocal ? { reasoning_effort: toOpenAIReasoningEffort(options.thinking, options.model) } : {},
|
|
2025
|
-
...options.tools?.length ? {
|
|
2138
|
+
...options.tools?.length ? {
|
|
2139
|
+
tools: toOpenAITools(options.tools, {
|
|
2140
|
+
strict: supportsStrictToolSampling(options.provider)
|
|
2141
|
+
})
|
|
2142
|
+
} : {},
|
|
2026
2143
|
...options.toolChoice && options.tools?.length ? { tool_choice: toOpenAIToolChoice(options.toolChoice) } : {},
|
|
2027
2144
|
...useStreaming ? { stream_options: { include_usage: true } } : {}
|
|
2028
2145
|
};
|
|
@@ -2531,7 +2648,7 @@ function toCodexToolChoice(choice, tools) {
|
|
|
2531
2648
|
function streamOpenAICodex(options) {
|
|
2532
2649
|
return new StreamResult(runStream3(options), options.signal);
|
|
2533
2650
|
}
|
|
2534
|
-
async function* runStream3(options) {
|
|
2651
|
+
async function* runStream3(options, retriedWithoutReasoning = false) {
|
|
2535
2652
|
const baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
2536
2653
|
const url = `${baseUrl}/codex/responses`;
|
|
2537
2654
|
const downgradedImages = downgradeUnsupportedImages(options.messages, options.supportsImages);
|
|
@@ -2601,6 +2718,20 @@ async function* runStream3(options) {
|
|
|
2601
2718
|
const parsed = parseCodexErrorBody(text, response.status);
|
|
2602
2719
|
const message = parsed.message ?? `Codex API returned HTTP ${response.status}.`;
|
|
2603
2720
|
const requestId = parsed.requestId ?? readHeader(response.headers, "x-request-id", "openai-request-id", "x-oai-request-id");
|
|
2721
|
+
if (!retriedWithoutReasoning && !options.signal?.aborted && response.status === 400 && (parsed.errorObj?.code === "invalid_encrypted_content" || /encrypted content.*could not be (?:verified|decrypted|parsed)/i.test(message)) && options.messages.some(
|
|
2722
|
+
(msg) => msg.role === "assistant" && Array.isArray(msg.content) && msg.content.some((part) => part.type === "raw" && isEncryptedReasoning(part.data))
|
|
2723
|
+
)) {
|
|
2724
|
+
providerDiag("codex_retry_without_encrypted_reasoning", { status: response.status });
|
|
2725
|
+
const messages = options.messages.map(
|
|
2726
|
+
(msg) => msg.role === "assistant" && Array.isArray(msg.content) ? {
|
|
2727
|
+
...msg,
|
|
2728
|
+
content: msg.content.filter(
|
|
2729
|
+
(part) => !(part.type === "raw" && isEncryptedReasoning(part.data))
|
|
2730
|
+
)
|
|
2731
|
+
} : msg
|
|
2732
|
+
);
|
|
2733
|
+
return yield* runStream3({ ...options, messages }, true);
|
|
2734
|
+
}
|
|
2604
2735
|
const usageLimit = codexUsageLimitError(parsed.errorObj, response.status, requestId);
|
|
2605
2736
|
if (usageLimit) throw usageLimit;
|
|
2606
2737
|
let hint;
|
|
@@ -2980,13 +3111,23 @@ function toCodexInput(messages, options) {
|
|
|
2980
3111
|
return { system, input };
|
|
2981
3112
|
}
|
|
2982
3113
|
function toCodexTools(tools) {
|
|
2983
|
-
return tools.map((tool) =>
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
3114
|
+
return tools.map((tool) => {
|
|
3115
|
+
let parameters = resolveToolSchema(tool);
|
|
3116
|
+
let strict = null;
|
|
3117
|
+
try {
|
|
3118
|
+
parameters = makeStrictToolSchema(parameters);
|
|
3119
|
+
strict = true;
|
|
3120
|
+
} catch (error) {
|
|
3121
|
+
if (!(error instanceof UnsupportedStrictSchemaError)) throw error;
|
|
3122
|
+
}
|
|
3123
|
+
return {
|
|
3124
|
+
type: "function",
|
|
3125
|
+
name: tool.name,
|
|
3126
|
+
description: tool.description,
|
|
3127
|
+
parameters,
|
|
3128
|
+
strict
|
|
3129
|
+
};
|
|
3130
|
+
});
|
|
2990
3131
|
}
|
|
2991
3132
|
function parseCodexErrorBody(text, statusCode) {
|
|
2992
3133
|
if (!text) return {};
|