fix(confidence-engine): handle propertyless openai object schemas

This commit is contained in:
2026-09-07 10:59:28 +01:00
parent 0eadef6e3b
commit e289f0be1f
3 changed files with 64 additions and 7 deletions
+19 -6
View File
@@ -112,7 +112,7 @@ const openAIReconstructionJsonSchema = createOpenAIStrictSchema(
/** @internal OpenAI Structured Outputs requires every object property. */
export function createOpenAIStrictSchema(schema, zodSchema = reconstructionV2Schema) {
const projected = structuredClone(schema);
projectOpenAIStrictSchema(projected, zodSchema, projected);
projectOpenAIStrictSchema(projected, zodSchema, projected, new WeakSet());
return projected;
}
@@ -157,10 +157,12 @@ function zodAcceptsNull(schema) {
return schema?.isNullable?.() === true;
}
function projectOpenAIStrictSchema(schema, zodSchema, rootSchema) {
function projectOpenAIStrictSchema(schema, zodSchema, rootSchema, visited) {
const resolved = resolveSchema(schema, rootSchema);
if (!resolved || typeof resolved !== "object" || visited.has(resolved)) return;
visited.add(resolved);
const shape = zodObjectShape(zodSchema);
if (resolved?.properties) {
if (resolved?.type === "object" || resolved?.properties) {
if (shape) {
for (const [key, property] of Object.entries(resolved.properties)) {
const propertyZodSchema = shape[key];
@@ -169,17 +171,28 @@ function projectOpenAIStrictSchema(schema, zodSchema, rootSchema) {
}
}
}
resolved.required = Object.keys(resolved.properties);
if (resolved.properties) resolved.required = Object.keys(resolved.properties);
resolved.additionalProperties = false;
}
if (resolved?.items) {
projectOpenAIStrictSchema(resolved.items, zodArrayItem(zodSchema), rootSchema);
projectOpenAIStrictSchema(resolved.items, zodArrayItem(zodSchema), rootSchema, visited);
}
if (resolved?.properties) {
for (const [key, property] of Object.entries(resolved.properties)) {
projectOpenAIStrictSchema(property, shape?.[key], rootSchema);
projectOpenAIStrictSchema(property, shape?.[key], rootSchema, visited);
}
}
for (const branch of [
...(resolved.anyOf ?? []),
...(resolved.oneOf ?? []),
...(resolved.allOf ?? []),
]) {
projectOpenAIStrictSchema(branch, null, rootSchema, visited);
}
for (const definition of Object.values(resolved.$defs ?? resolved.definitions ?? {})) {
projectOpenAIStrictSchema(definition, null, rootSchema, visited);
}
}
function schemaAllowsNull(schema, rootSchema) {