Reject empty repaired LLM JSON
This commit is contained in:
@@ -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 },
|
||||
|
||||
Reference in New Issue
Block a user