@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.js
CHANGED
|
@@ -505,6 +505,108 @@ function normalizeRootForAnthropic(schema) {
|
|
|
505
505
|
return out;
|
|
506
506
|
}
|
|
507
507
|
|
|
508
|
+
// src/utils/strict-tool-schema.ts
|
|
509
|
+
var UnsupportedStrictSchemaError = class extends Error {
|
|
510
|
+
};
|
|
511
|
+
var UNSUPPORTED_KEYS = [
|
|
512
|
+
"$ref",
|
|
513
|
+
"$defs",
|
|
514
|
+
"definitions",
|
|
515
|
+
"allOf",
|
|
516
|
+
"oneOf",
|
|
517
|
+
"patternProperties",
|
|
518
|
+
"dependentSchemas",
|
|
519
|
+
"dependencies",
|
|
520
|
+
"unevaluatedProperties",
|
|
521
|
+
"propertyNames",
|
|
522
|
+
"contains",
|
|
523
|
+
"prefixItems",
|
|
524
|
+
"not",
|
|
525
|
+
"if",
|
|
526
|
+
"then",
|
|
527
|
+
"else"
|
|
528
|
+
];
|
|
529
|
+
function isSchemaObject(value) {
|
|
530
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
531
|
+
}
|
|
532
|
+
function isStructured(value) {
|
|
533
|
+
if (!isSchemaObject(value)) return false;
|
|
534
|
+
const types = typeof value.type === "string" ? [value.type] : Array.isArray(value.type) ? value.type : [];
|
|
535
|
+
return types.includes("object") || types.includes("array") || value.properties !== void 0 || value.items !== void 0;
|
|
536
|
+
}
|
|
537
|
+
function allowsNull(schema) {
|
|
538
|
+
if (!isSchemaObject(schema)) return false;
|
|
539
|
+
if (schema.type === "null" || Array.isArray(schema.type) && schema.type.includes("null"))
|
|
540
|
+
return true;
|
|
541
|
+
if (schema.const === null || Array.isArray(schema.enum) && schema.enum.includes(null))
|
|
542
|
+
return true;
|
|
543
|
+
return Array.isArray(schema.anyOf) && schema.anyOf.some((variant) => allowsNull(variant));
|
|
544
|
+
}
|
|
545
|
+
function makeNodeStrict(schema) {
|
|
546
|
+
if (!isSchemaObject(schema)) {
|
|
547
|
+
throw new UnsupportedStrictSchemaError("boolean schemas are unsupported");
|
|
548
|
+
}
|
|
549
|
+
for (const key of UNSUPPORTED_KEYS) {
|
|
550
|
+
if (schema[key] !== void 0) {
|
|
551
|
+
throw new UnsupportedStrictSchemaError(`${key} schemas are unsupported`);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
if (schema.anyOf !== void 0) {
|
|
555
|
+
if (!Array.isArray(schema.anyOf) || schema.anyOf.length === 0) {
|
|
556
|
+
throw new UnsupportedStrictSchemaError("anyOf must contain at least one schema");
|
|
557
|
+
}
|
|
558
|
+
for (const variant of schema.anyOf) {
|
|
559
|
+
if (isStructured(variant)) {
|
|
560
|
+
throw new UnsupportedStrictSchemaError("object and array unions are unsupported");
|
|
561
|
+
}
|
|
562
|
+
makeNodeStrict(variant);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
if (schema.items !== void 0) {
|
|
566
|
+
if (Array.isArray(schema.items)) {
|
|
567
|
+
throw new UnsupportedStrictSchemaError("tuple schemas are unsupported");
|
|
568
|
+
}
|
|
569
|
+
makeNodeStrict(schema.items);
|
|
570
|
+
}
|
|
571
|
+
const isObjectSchema = schema.type === "object";
|
|
572
|
+
if (schema.properties !== void 0 && !isObjectSchema) {
|
|
573
|
+
throw new UnsupportedStrictSchemaError("properties require type object");
|
|
574
|
+
}
|
|
575
|
+
if (!isObjectSchema) return;
|
|
576
|
+
if (schema.additionalProperties !== void 0 && schema.additionalProperties !== false) {
|
|
577
|
+
throw new UnsupportedStrictSchemaError(
|
|
578
|
+
"schema-valued or true additionalProperties is unsupported"
|
|
579
|
+
);
|
|
580
|
+
}
|
|
581
|
+
if (schema.properties !== void 0 && !isSchemaObject(schema.properties)) {
|
|
582
|
+
throw new UnsupportedStrictSchemaError("object properties must be a schema map");
|
|
583
|
+
}
|
|
584
|
+
const properties = schema.properties ?? {};
|
|
585
|
+
const required = new Set(Array.isArray(schema.required) ? schema.required : []);
|
|
586
|
+
for (const [key, property] of Object.entries(properties)) {
|
|
587
|
+
makeNodeStrict(property);
|
|
588
|
+
if (!required.has(key) && !allowsNull(property)) {
|
|
589
|
+
properties[key] = { anyOf: [property, { type: "null" }] };
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
schema.required = Object.keys(properties);
|
|
593
|
+
schema.additionalProperties = false;
|
|
594
|
+
}
|
|
595
|
+
function supportsStrictToolSampling(provider) {
|
|
596
|
+
return provider === "openai";
|
|
597
|
+
}
|
|
598
|
+
function makeStrictToolSchema(schema) {
|
|
599
|
+
const cloned = structuredClone(schema);
|
|
600
|
+
if (!isSchemaObject(cloned)) {
|
|
601
|
+
throw new UnsupportedStrictSchemaError("root schema must be an object");
|
|
602
|
+
}
|
|
603
|
+
makeNodeStrict(cloned);
|
|
604
|
+
if (cloned.type !== "object") {
|
|
605
|
+
throw new UnsupportedStrictSchemaError("root schema must have type object");
|
|
606
|
+
}
|
|
607
|
+
return cloned;
|
|
608
|
+
}
|
|
609
|
+
|
|
508
610
|
// src/providers/reasoning-field.ts
|
|
509
611
|
var REASONING_FIELD_ALIASES = [
|
|
510
612
|
"reasoning_content",
|
|
@@ -1007,13 +1109,11 @@ function toOpenAIMessages(messages, options) {
|
|
|
1007
1109
|
const parts = typeof msg.content === "string" ? msg.content : void 0;
|
|
1008
1110
|
const toolCalls = typeof msg.content !== "string" ? msg.content.filter(
|
|
1009
1111
|
(p) => p.type === "tool_call"
|
|
1010
|
-
).map(
|
|
1011
|
-
(tc)
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
})
|
|
1016
|
-
) : void 0;
|
|
1112
|
+
).map((tc) => ({
|
|
1113
|
+
id: remapToolCallId(tc.id, idMap),
|
|
1114
|
+
type: "function",
|
|
1115
|
+
function: { name: tc.name, arguments: JSON.stringify(tc.args) }
|
|
1116
|
+
})) : void 0;
|
|
1017
1117
|
const textParts = typeof msg.content !== "string" ? msg.content.filter((p) => p.type === "text").map((p) => p.text).join("") : void 0;
|
|
1018
1118
|
const thinkingParts = typeof msg.content !== "string" ? msg.content.filter((p) => p.type === "thinking").map((p) => p.text).join("") : void 0;
|
|
1019
1119
|
const contentValue = parts || textParts || null;
|
|
@@ -1089,15 +1189,28 @@ function toOpenAIMessages(messages, options) {
|
|
|
1089
1189
|
}
|
|
1090
1190
|
return out;
|
|
1091
1191
|
}
|
|
1092
|
-
function toOpenAITools(tools) {
|
|
1093
|
-
return tools.map((tool) =>
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1192
|
+
function toOpenAITools(tools, opts) {
|
|
1193
|
+
return tools.map((tool) => {
|
|
1194
|
+
let parameters = resolveToolSchema(tool);
|
|
1195
|
+
let strict;
|
|
1196
|
+
if (opts?.strict) {
|
|
1197
|
+
try {
|
|
1198
|
+
parameters = makeStrictToolSchema(parameters);
|
|
1199
|
+
strict = true;
|
|
1200
|
+
} catch (error) {
|
|
1201
|
+
if (!(error instanceof UnsupportedStrictSchemaError)) throw error;
|
|
1202
|
+
}
|
|
1099
1203
|
}
|
|
1100
|
-
|
|
1204
|
+
return {
|
|
1205
|
+
type: "function",
|
|
1206
|
+
function: {
|
|
1207
|
+
name: tool.name,
|
|
1208
|
+
description: tool.description,
|
|
1209
|
+
parameters,
|
|
1210
|
+
...strict ? { strict } : {}
|
|
1211
|
+
}
|
|
1212
|
+
};
|
|
1213
|
+
});
|
|
1101
1214
|
}
|
|
1102
1215
|
function toOpenAIToolChoice(choice) {
|
|
1103
1216
|
if (choice === "auto") return "auto";
|
|
@@ -1955,7 +2068,11 @@ async function* runStream2(options) {
|
|
|
1955
2068
|
...options.topP != null && !hasFixedKimiSampling ? { top_p: options.topP } : {},
|
|
1956
2069
|
...options.stop ? { stop: options.stop } : {},
|
|
1957
2070
|
...options.thinking && !usesThinkingParam && !isKimiK3 && !isKimiK27 && !isLocal ? { reasoning_effort: toOpenAIReasoningEffort(options.thinking, options.model) } : {},
|
|
1958
|
-
...options.tools?.length ? {
|
|
2071
|
+
...options.tools?.length ? {
|
|
2072
|
+
tools: toOpenAITools(options.tools, {
|
|
2073
|
+
strict: supportsStrictToolSampling(options.provider)
|
|
2074
|
+
})
|
|
2075
|
+
} : {},
|
|
1959
2076
|
...options.toolChoice && options.tools?.length ? { tool_choice: toOpenAIToolChoice(options.toolChoice) } : {},
|
|
1960
2077
|
...useStreaming ? { stream_options: { include_usage: true } } : {}
|
|
1961
2078
|
};
|
|
@@ -2464,7 +2581,7 @@ function toCodexToolChoice(choice, tools) {
|
|
|
2464
2581
|
function streamOpenAICodex(options) {
|
|
2465
2582
|
return new StreamResult(runStream3(options), options.signal);
|
|
2466
2583
|
}
|
|
2467
|
-
async function* runStream3(options) {
|
|
2584
|
+
async function* runStream3(options, retriedWithoutReasoning = false) {
|
|
2468
2585
|
const baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
2469
2586
|
const url = `${baseUrl}/codex/responses`;
|
|
2470
2587
|
const downgradedImages = downgradeUnsupportedImages(options.messages, options.supportsImages);
|
|
@@ -2534,6 +2651,20 @@ async function* runStream3(options) {
|
|
|
2534
2651
|
const parsed = parseCodexErrorBody(text, response.status);
|
|
2535
2652
|
const message = parsed.message ?? `Codex API returned HTTP ${response.status}.`;
|
|
2536
2653
|
const requestId = parsed.requestId ?? readHeader(response.headers, "x-request-id", "openai-request-id", "x-oai-request-id");
|
|
2654
|
+
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(
|
|
2655
|
+
(msg) => msg.role === "assistant" && Array.isArray(msg.content) && msg.content.some((part) => part.type === "raw" && isEncryptedReasoning(part.data))
|
|
2656
|
+
)) {
|
|
2657
|
+
providerDiag("codex_retry_without_encrypted_reasoning", { status: response.status });
|
|
2658
|
+
const messages = options.messages.map(
|
|
2659
|
+
(msg) => msg.role === "assistant" && Array.isArray(msg.content) ? {
|
|
2660
|
+
...msg,
|
|
2661
|
+
content: msg.content.filter(
|
|
2662
|
+
(part) => !(part.type === "raw" && isEncryptedReasoning(part.data))
|
|
2663
|
+
)
|
|
2664
|
+
} : msg
|
|
2665
|
+
);
|
|
2666
|
+
return yield* runStream3({ ...options, messages }, true);
|
|
2667
|
+
}
|
|
2537
2668
|
const usageLimit = codexUsageLimitError(parsed.errorObj, response.status, requestId);
|
|
2538
2669
|
if (usageLimit) throw usageLimit;
|
|
2539
2670
|
let hint;
|
|
@@ -2913,13 +3044,23 @@ function toCodexInput(messages, options) {
|
|
|
2913
3044
|
return { system, input };
|
|
2914
3045
|
}
|
|
2915
3046
|
function toCodexTools(tools) {
|
|
2916
|
-
return tools.map((tool) =>
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
3047
|
+
return tools.map((tool) => {
|
|
3048
|
+
let parameters = resolveToolSchema(tool);
|
|
3049
|
+
let strict = null;
|
|
3050
|
+
try {
|
|
3051
|
+
parameters = makeStrictToolSchema(parameters);
|
|
3052
|
+
strict = true;
|
|
3053
|
+
} catch (error) {
|
|
3054
|
+
if (!(error instanceof UnsupportedStrictSchemaError)) throw error;
|
|
3055
|
+
}
|
|
3056
|
+
return {
|
|
3057
|
+
type: "function",
|
|
3058
|
+
name: tool.name,
|
|
3059
|
+
description: tool.description,
|
|
3060
|
+
parameters,
|
|
3061
|
+
strict
|
|
3062
|
+
};
|
|
3063
|
+
});
|
|
2923
3064
|
}
|
|
2924
3065
|
function parseCodexErrorBody(text, statusCode) {
|
|
2925
3066
|
if (!text) return {};
|