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

53 lines
1.9 KiB
TypeScript

// SOS-LLM-Provider mit AsyncStorage-Persist + Listener-Pattern.
// Live-Switch im SOS-Screen — analog zu lib/ttsProvider.ts.
//
// Backend (sos-session.post.ts) nimmt das Feld entgegen, sos-stream.get.ts
// routet dann je nach Wert zu OpenRouter (Sonnet/Haiku) oder Groq (Llama).
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useEffect, useState } from 'react';
export type LlmProvider = 'openrouter-sonnet' | 'openrouter-haiku' | 'groq-llama';
const STORAGE_KEY = 'rebreak-sos-llm-provider';
const DEFAULT_PROVIDER: LlmProvider = 'openrouter-sonnet';
export const LLM_PROVIDER_LABEL: Record<LlmProvider, string> = {
'openrouter-sonnet': 'Sonnet',
'openrouter-haiku': 'Haiku',
'groq-llama': 'Groq',
};
const listeners = new Set<(p: LlmProvider) => void>();
let cached: LlmProvider | null = null;
export async function loadLlmProvider(): Promise<LlmProvider> {
if (cached) return cached;
const raw = await AsyncStorage.getItem(STORAGE_KEY).catch(() => null);
cached =
raw === 'openrouter-haiku' || raw === 'groq-llama' ? raw : DEFAULT_PROVIDER;
return cached;
}
export async function setLlmProvider(p: LlmProvider): Promise<void> {
cached = p;
await AsyncStorage.setItem(STORAGE_KEY, p).catch(() => {});
for (const cb of listeners) cb(p);
}
/** Always-fresh read — analog zu currentProvider() in ttsProvider.ts. */
export function currentLlmProvider(): LlmProvider {
return cached ?? DEFAULT_PROVIDER;
}
export function useLlmProvider(): [LlmProvider, (p: LlmProvider) => Promise<void>] {
const [p, setP] = useState<LlmProvider>(cached ?? DEFAULT_PROVIDER);
useEffect(() => {
let mounted = true;
loadLlmProvider().then((v) => { if (mounted) setP(v); });
const cb = (v: LlmProvider) => { if (mounted) setP(v); };
listeners.add(cb);
return () => { mounted = false; listeners.delete(cb); };
}, []);
return [p, setLlmProvider];
}