// L16 Lotse-Chat für Monteure – optionaler Live-Test gegen Claude. // // Läuft nur mit gesetztem ANTHROPIC_API_KEY (sonst Skip, Exit 0). Prüft, dass eine echte Tool-Use-Schleife eine // Antwort liefert, als AiGeneration protokolliert wird und dabei nichts an Fachdaten ändert (nur Vorschläge). // // Lauf: npx tsx scripts/test-lotse-chat-live.ts import "dotenv/config"; import { prisma } from "../src/server/db"; import { getLotseChatProvider } from "../src/server/ai/lotse/chat-anthropic"; import { sendLotseMessage } from "../src/server/services/lotse/chat/engine"; import { cleanupChatTenants, createChatFixture, failures, ok } from "./lib/lotse-chat-fixture"; const PREFIX = "lotsechat-live"; async function main() { if (!process.env.ANTHROPIC_API_KEY?.trim()) { console.log("übersprungen: ANTHROPIC_API_KEY nicht gesetzt"); await prisma.$disconnect(); return; } const f = await createChatFixture(PREFIX); const provider = getLotseChatProvider(); ok(Boolean(provider), "Anbieter eingerichtet"); const notesBefore = await prisma.activityNote.count({ where: { tenantId: f.tA.id } }); const view = await sendLotseMessage(f.ctx.tech2, { text: "Was steht heute an? Und bei A-LC-1 habe ich den Filter getauscht." }, { provider }); const last = view.messages[view.messages.length - 1]; ok(last.role === "assistant" && (last.text.length > 0 || Boolean(last.content.noticeKey)), `Antwort erhalten: ${last.text.slice(0, 120)}`); ok(!/\{\{(customer|contact|phone|email|address|site):/.test(last.text), "keine unaufgelösten Platzhalter in der Antwort"); ok((await prisma.aiGeneration.count({ where: { tenantId: f.tA.id, kind: "lotse_chat" } })) === 1, "AiGeneration protokolliert"); ok((await prisma.activityNote.count({ where: { tenantId: f.tA.id } })) === notesBefore, "keine Notiz ohne Bestätigung"); await cleanupChatTenants(PREFIX); await prisma.$disconnect(); process.exit(failures ? 1 : 0); } main().catch(async (err) => { console.error(err); await cleanupChatTenants(PREFIX).catch(() => undefined); await prisma.$disconnect(); process.exit(1); });