chahinebrini f2e822be95 feat(sos): llmProvider toggle + sort:latency + bench scaffolding
- backend/coach: routing zu Sonnet (default) / Haiku / Groq Llama je nach
  sessionData.llmProvider. sort:latency für Anthropic-Modelle (-30..58% TTFB).
- frontend: LlmProviderToggle (Sonnet/Haiku/Groq pills), llmProvider.ts
  Storage-Helper. sosStream.ts schickt llmProvider im /sos-session-Body.
- bench: SosTtsBenchmark sammelt Marker (req->session, lyra-ttfb, lyra-done,
  tts-fired/headers/body/file, audio-loaded, first-audio); Output als console.table.
- ops: backend/scripts/llm-bench.sh + Python-Variante für realistic SOS-Prompt.
- speak-cartesia + speak-elevenlabs Endpoints (waren ungetracked, jetzt mit drin).
2026-05-06 13:58:07 +02:00

52 lines
1.3 KiB
TypeScript

/**
* In-Memory Session Store für SOS-Streaming
*
* POST /api/coach/sos-session speichert messages/locale hier,
* GET /api/coach/sos-stream lädt sie per sessionId.
*
* TTL: 5 Minuten (Auto-Cleanup)
*/
type SosSessionData = {
userId: string;
messages: Array<{ role: "user" | "assistant"; content: string }>;
locale: string;
/** A/B-Test: client wählt LLM via Toggle. Default openrouter-sonnet. */
llmProvider?: string;
createdAt: number;
};
const sessions = new Map<string, SosSessionData>();
const SESSION_TTL = 5 * 60 * 1000; // 5min
// Cleanup-Intervall: alle 2min alte Sessions löschen
setInterval(
() => {
const now = Date.now();
for (const [id, data] of sessions.entries()) {
if (now - data.createdAt > SESSION_TTL) {
sessions.delete(id);
}
}
},
2 * 60 * 1000,
);
export function setSosSession(sessionId: string, data: SosSessionData) {
sessions.set(sessionId, data);
}
export function getSosSession(sessionId: string): SosSessionData | undefined {
const data = sessions.get(sessionId);
if (!data) return undefined;
// TTL-Check
if (Date.now() - data.createdAt > SESSION_TTL) {
sessions.delete(sessionId);
return undefined;
}
return data;
}
export function deleteSosSession(sessionId: string) {
sessions.delete(sessionId);
}