Reject empty repaired LLM JSON

This commit is contained in:
syntaxbullet
2026-06-17 15:48:48 +02:00
parent 1957d0dffa
commit 5da401b8af

View File

@@ -146,7 +146,31 @@ const scoringResponseSchema = z.object({
id: z.string(),
subcriteria: z.array(scoringSubcriterionSchema),
}).strict()),
}).strict();
}).strict().superRefine((value, ctx) => {
const dimensionsById = new Map(value.dimensionen.map((dimension) => [dimension.id, dimension]));
for (const dimension of SCORING_MODEL) {
const parsedDimension = dimensionsById.get(dimension.id);
if (!parsedDimension) {
ctx.addIssue({
code: "custom",
path: ["dimensionen"],
message: `Missing scoring dimension "${dimension.id}".`,
});
continue;
}
const subcriteriaIds = new Set(parsedDimension.subcriteria.map((subcriterion) => subcriterion.id));
for (const subcriterion of dimension.subcriteria) {
if (!subcriteriaIds.has(subcriterion.id)) {
ctx.addIssue({
code: "custom",
path: ["dimensionen", dimension.id, "subcriteria"],
message: `Missing scoring subcriterion "${subcriterion.id}".`,
});
}
}
}
});
const swotResponseSchema = z.object({
staerken: z.array(z.string()),
@@ -392,6 +416,7 @@ async function createParsedStructuredCompletion<T>(
return await correctStructuredJson(
client,
model,
messages,
content,
error,
schemaName,
@@ -498,6 +523,32 @@ function validationMessage(error: unknown): string {
return errorMessage(error);
}
function segmentierungValidatorFor(fragen: FrageMitAntwort[]): z.ZodType<z.infer<typeof segmentierungResponseSchema>> {
const expectedIds = fragen.map((frage, index) => frage.id ?? `frage_${index + 1}`);
return segmentierungResponseSchema.superRefine((value, ctx) => {
const returnedIds = new Set(value.fragen.map((frage) => frage.id));
for (const id of expectedIds) {
if (!returnedIds.has(id)) {
ctx.addIssue({
code: "custom",
path: ["fragen"],
message: `Missing segmented answer for "${id}".`,
});
}
}
for (const frage of value.fragen) {
if (!expectedIds.includes(frage.id)) {
ctx.addIssue({
code: "custom",
path: ["fragen", frage.id],
message: `Unexpected segmented answer id "${frage.id}".`,
});
}
}
});
}
function parseAndValidateLlmJson<T>(content: string, context: string, validator: z.ZodType<T>): T {
const parsed = parseLlmJson<unknown>(content, context);
const result = validator.safeParse(parsed);
@@ -510,6 +561,7 @@ function parseAndValidateLlmJson<T>(content: string, context: string, validator:
async function correctStructuredJson<T>(
client: OpenAI,
model: string,
originalMessages: Array<{ role: "system" | "user"; content: string }>,
invalidContent: string,
validationError: unknown,
schemaName: string,
@@ -543,10 +595,13 @@ ${validationMessage(validationError)}
Erwartetes JSON Schema:
${JSON.stringify(schema, null, 2)}
Urspruengliche Aufgabe:
${JSON.stringify(originalMessages, null, 2)}
Fehlerhafte Ausgabe:
${invalidContent}
Korrigiere nur Syntax, Typen, fehlende Pflichtfelder und enum-Werte. Antworte ausschliesslich mit dem korrigierten JSON.`,
Korrigiere Syntax, Typen, fehlende Pflichtfelder, enum-Werte und fehlende Array-Eintraege. Nutze die urspruengliche Aufgabe, wenn die fehlerhafte Ausgabe abgeschnitten ist. Antworte ausschliesslich mit dem korrigierten JSON.`,
},
],
`${schemaName}_correction`,
@@ -1257,7 +1312,7 @@ Vorgaben:
},
required: ["fragen"],
},
segmentierungResponseSchema,
segmentierungValidatorFor(fragen),
`Could not parse answer segmentation JSON for ${fallbackLabel}`,
sessionId,
{ operation: "segmentierung", label: fallbackLabel, onLlmCall },