- OpenAI-kompatible Transkription + Processor transcription (done/failed/disabled, AiGeneration, Notiz aus Sprachnotiz) - Claude-Lotse (strukturierte Ausgabe, Refusal/Fallback), Datenminimierung, Vorschläge in content.lotse - Vollständigkeitsprüfung (Regeln + KI-Hinweise mit Deep-Link), Einstellungen, KI-Protokoll - Freigabeprinzip: Submit eines Lotse-Entwurfs nur mit Prüfbestätigung (serverseitig) - Migration lotse_address_form (TenantSettings) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
51 lines
2.2 KiB
TypeScript
51 lines
2.2 KiB
TypeScript
import { z } from "zod";
|
|
|
|
/**
|
|
* Lotse block of the report snapshot (client-safe, lane L9). Stored as optional `content.lotse` of
|
|
* `ReportContent` (src/lib/reports/content.ts), so it is frozen together with the report on approval.
|
|
*
|
|
* Suggestions are kept SEPARATE from `content.texts`: nothing the model wrote reaches the report text
|
|
* until a person accepts it (Spec §15.3/§15.4, Brandbook §12.4). `reviewedAt` is set on submit when
|
|
* the submitter confirmed "Ich habe den Vorschlag geprüft" (checked server-side in submitReport).
|
|
*/
|
|
|
|
/** Report text fields the Lotse drafts (ReportDraftOutput); `problems` stays technician-only. */
|
|
export const LOTSE_TEXT_FIELDS = ["workPerformed", "deviations", "additionalWork", "openItems", "nextSteps", "hints"] as const;
|
|
export type LotseTextField = (typeof LOTSE_TEXT_FIELDS)[number];
|
|
|
|
export const LOTSE_SUGGESTION_STATES = ["pending", "accepted", "discarded"] as const;
|
|
export type LotseSuggestionState = (typeof LOTSE_SUGGESTION_STATES)[number];
|
|
|
|
export const LOTSE_TEXT_MAX = 10_000;
|
|
export const LOTSE_MISSING_MAX = 20;
|
|
|
|
const isoDateTime = z.string().datetime({ offset: true });
|
|
|
|
export const lotseSuggestionSchema = z.object({
|
|
field: z.enum(LOTSE_TEXT_FIELDS),
|
|
text: z.string().max(LOTSE_TEXT_MAX),
|
|
state: z.enum(LOTSE_SUGGESTION_STATES),
|
|
decidedAt: isoDateTime.nullable(),
|
|
});
|
|
export type LotseSuggestion = z.infer<typeof lotseSuggestionSchema>;
|
|
|
|
export const lotseBlockSchema = z.object({
|
|
generationId: z.string(),
|
|
model: z.string(),
|
|
draftedAt: isoDateTime,
|
|
draftedById: z.string().nullable(),
|
|
suggestions: z.array(lotseSuggestionSchema),
|
|
/** plain-language hints of what the model could not find in the data */
|
|
missingInformation: z.array(z.string().max(500)).max(LOTSE_MISSING_MAX),
|
|
reviewedAt: isoDateTime.nullable(),
|
|
reviewedById: z.string().nullable(),
|
|
});
|
|
export type LotseBlock = z.infer<typeof lotseBlockSchema>;
|
|
|
|
export const LOTSE_ADDRESS_FORMS = ["sie", "du"] as const;
|
|
export type LotseAddressForm = (typeof LOTSE_ADDRESS_FORMS)[number];
|
|
|
|
export function pendingSuggestions(block: LotseBlock | null | undefined): LotseSuggestion[] {
|
|
return block ? block.suggestions.filter((s) => s.state === "pending") : [];
|
|
}
|