Reject empty repaired LLM JSON
This commit is contained in:
@@ -146,7 +146,31 @@ const scoringResponseSchema = z.object({
|
|||||||
id: z.string(),
|
id: z.string(),
|
||||||
subcriteria: z.array(scoringSubcriterionSchema),
|
subcriteria: z.array(scoringSubcriterionSchema),
|
||||||
}).strict()),
|
}).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({
|
const swotResponseSchema = z.object({
|
||||||
staerken: z.array(z.string()),
|
staerken: z.array(z.string()),
|
||||||
@@ -392,6 +416,7 @@ async function createParsedStructuredCompletion<T>(
|
|||||||
return await correctStructuredJson(
|
return await correctStructuredJson(
|
||||||
client,
|
client,
|
||||||
model,
|
model,
|
||||||
|
messages,
|
||||||
content,
|
content,
|
||||||
error,
|
error,
|
||||||
schemaName,
|
schemaName,
|
||||||
@@ -498,6 +523,32 @@ function validationMessage(error: unknown): string {
|
|||||||
return errorMessage(error);
|
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 {
|
function parseAndValidateLlmJson<T>(content: string, context: string, validator: z.ZodType<T>): T {
|
||||||
const parsed = parseLlmJson<unknown>(content, context);
|
const parsed = parseLlmJson<unknown>(content, context);
|
||||||
const result = validator.safeParse(parsed);
|
const result = validator.safeParse(parsed);
|
||||||
@@ -510,6 +561,7 @@ function parseAndValidateLlmJson<T>(content: string, context: string, validator:
|
|||||||
async function correctStructuredJson<T>(
|
async function correctStructuredJson<T>(
|
||||||
client: OpenAI,
|
client: OpenAI,
|
||||||
model: string,
|
model: string,
|
||||||
|
originalMessages: Array<{ role: "system" | "user"; content: string }>,
|
||||||
invalidContent: string,
|
invalidContent: string,
|
||||||
validationError: unknown,
|
validationError: unknown,
|
||||||
schemaName: string,
|
schemaName: string,
|
||||||
@@ -543,10 +595,13 @@ ${validationMessage(validationError)}
|
|||||||
Erwartetes JSON Schema:
|
Erwartetes JSON Schema:
|
||||||
${JSON.stringify(schema, null, 2)}
|
${JSON.stringify(schema, null, 2)}
|
||||||
|
|
||||||
|
Urspruengliche Aufgabe:
|
||||||
|
${JSON.stringify(originalMessages, null, 2)}
|
||||||
|
|
||||||
Fehlerhafte Ausgabe:
|
Fehlerhafte Ausgabe:
|
||||||
${invalidContent}
|
${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`,
|
`${schemaName}_correction`,
|
||||||
@@ -1257,7 +1312,7 @@ Vorgaben:
|
|||||||
},
|
},
|
||||||
required: ["fragen"],
|
required: ["fragen"],
|
||||||
},
|
},
|
||||||
segmentierungResponseSchema,
|
segmentierungValidatorFor(fragen),
|
||||||
`Could not parse answer segmentation JSON for ${fallbackLabel}`,
|
`Could not parse answer segmentation JSON for ${fallbackLabel}`,
|
||||||
sessionId,
|
sessionId,
|
||||||
{ operation: "segmentierung", label: fallbackLabel, onLlmCall },
|
{ operation: "segmentierung", label: fallbackLabel, onLlmCall },
|
||||||
|
|||||||
Reference in New Issue
Block a user