// L9 Lotse — optional live test of the Claude report draft and voice summary against a synthetic, // already minimised input. Runs ONLY when ANTHROPIC_API_KEY is set (costs tokens); otherwise it skips // with exit 0 so the default gate stays offline. // // Lauf: ANTHROPIC_API_KEY=… npx tsx scripts/test-lotse-live.ts import "dotenv/config"; import { getLotseProvider } from "../src/server/ai/lotse/anthropic"; import type { ReportDraftInput } from "../src/server/ai/providers"; async function main() { if (!process.env.ANTHROPIC_API_KEY?.trim()) { console.log("↷ übersprungen: kein ANTHROPIC_API_KEY gesetzt (Live-Berichtsentwurf gegen Claude)."); process.exit(0); } const provider = getLotseProvider(); if (!provider) { console.log("↷ übersprungen: Lotse nicht konfiguriert."); process.exit(0); } let failures = 0; const ok = (cond: boolean, msg: string) => { console.log(`${cond ? "✓" : "✗ FEHLER"} ${msg}`); if (!cond) failures++; }; const input: ReportDraftInput = { locale: "de", addressForm: "neutral", workOrder: { title: "Heizkörper tauschen", description: "Kundin erreichbar unter [Telefon].", scope: "Heizkörper im Bad ersetzen", orderType: "Reparatur" }, notes: [ { kind: "work_done", text: "M. M. hat den alten Heizkörper demontiert und den neuen montiert.", at: "2026-09-14T08:30:00+02:00" }, { kind: "deviation (Sprachnotiz)", text: "Nur ein Heizkörper verbaut, zweiter war beschädigt.", at: "2026-09-14T10:00:00+02:00" }, ], checklist: [{ label: "Anlage entlüftet", checked: false, comment: null }], materials: [{ name: "Heizkörper 600x1000", planned: "2", actual: "1", unit: "Stk", status: "partially_used", reason: null }], photos: [{ phase: "after", comment: "Neuer Heizkörper montiert", requirement: null }], time: [{ type: "work", minutes: 95, user: "M. M." }], }; const started = Date.now(); const out = await provider.draftReport(input); console.log(` Modell ${out.meta.model}, Tokens ${out.meta.inputTokens}/${out.meta.outputTokens}, ${Date.now() - started} ms`); ok(out.workPerformed.trim().length > 0, "workPerformed nicht leer"); ok(Array.isArray(out.missingInformation), "missingInformation ist eine Liste"); ok(!/\[Telefon\]/.test(`${out.workPerformed}${out.hints}`), "Platzhalter nicht in den Bericht übernommen"); ok(!/\b(Sie|du)\b/.test(out.missingInformation.join(" ")), "Neutrale Anrede in missingInformation"); const summary = await provider.summarizeTranscript({ locale: "de", addressForm: "neutral", transcript: "Also, der alte Heizkörper ist raus, der neue hängt, entlüften mach ich morgen." }); ok(summary.summary.trim().length > 0, "Zusammenfassung nicht leer"); process.exit(failures ? 1 : 0); } main().catch((err) => { console.error(err); process.exit(1); });