L16 Lotse-Chat für Monteure: Datenmodell, Provider mit Tool-Schleife, Vorschläge und Bestätigen über bestehende Services

Chatverlauf (LotseConversation/-Message) und Aktionskarten (LotseActionProposal) als
Mandantentabellen mit RLS; Schalter lotseChatEnabled. Tool-Use-Schleife mit Runden- und
Tokengrenze, Datenminimierung per Platzhalter, Auftragszuordnung im Sichtbarkeits-Scope,
Bestätigen/Verwerfen/Alle bestätigen mit Hash, Ablauf und Idempotenz, Transkriptions-API.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 18:57:20 +02:00
co-authored by Claude Opus 5
parent 6b8cdf543b
commit 68f4eb32dc
26 changed files with 2363 additions and 6 deletions
+20
View File
@@ -0,0 +1,20 @@
import { requireApiContext } from "@/server/api/context";
import { ApiError, json, readFormData, withApi } from "@/server/api/respond";
import { CHAT_AUDIO_MAX_BYTES, transcribeChatAudio } from "@/server/services/lotse/chat/transcribe";
/**
* POST /api/v1/lotse/transcribe — multipart `file` (audio, max. 10 MB) → `{ text }` (lane L16).
* Microphone key of the Lotse chat: the transcript is put into the input field and can be edited
* before sending. The audio is not stored.
*/
export const POST = withApi(async (req: Request) => {
const ctx = await requireApiContext("lotse", "lotse:use", "field:execute");
const declared = Number(req.headers.get("content-length") ?? "0");
if (declared > CHAT_AUDIO_MAX_BYTES + 1024 * 1024) throw new ApiError("payload_too_large", "audio too large");
const form = await readFormData(req);
const file = form.get("file");
if (!(file instanceof File)) throw new ApiError("invalid", "file missing");
if (file.size > CHAT_AUDIO_MAX_BYTES) throw new ApiError("payload_too_large", "audio too large");
const result = await transcribeChatAudio(ctx, Buffer.from(await file.arrayBuffer()));
return json(result);
});