diff --git a/packages/summarizer/summarizer.ts b/packages/summarizer/summarizer.ts index 5c23882..64d0af2 100644 --- a/packages/summarizer/summarizer.ts +++ b/packages/summarizer/summarizer.ts @@ -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( 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> { + 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(content: string, context: string, validator: z.ZodType): T { const parsed = parseLlmJson(content, context); const result = validator.safeParse(parsed); @@ -510,6 +561,7 @@ function parseAndValidateLlmJson(content: string, context: string, validator: async function correctStructuredJson( 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 },