Backend (sos-stream.get.ts): wenn sessionData.llmProvider === 'auto' oder undefined, resolved zu plan-based default via profile.plan: - legend → openrouter-haiku (Anthropic warm + sort:latency) - pro/free → groq-llama (sachlich + schnell, ~157ms TTFB) Frontend (llmProvider.ts): DEFAULT_PROVIDER = 'auto', neue Pill 'Auto' in LlmProviderToggle. Explicit-Toggles (Sonnet/Haiku/Groq) sind debug-overrides die plan-logic bypassen.
58 lines
2.1 KiB
TypeScript
58 lines
2.1 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 = 'auto' | 'openrouter-sonnet' | 'openrouter-haiku' | 'groq-llama';
|
|
|
|
const STORAGE_KEY = 'rebreak-sos-llm-provider';
|
|
// Default 'auto' → backend wählt nach plan: Pro=Groq, Legend=Haiku, Free=Groq.
|
|
// Explicit-Toggles (Sonnet/Haiku/Groq) sind debug-overrides und bypassen plan-logic.
|
|
const DEFAULT_PROVIDER: LlmProvider = 'auto';
|
|
|
|
export const LLM_PROVIDER_LABEL: Record<LlmProvider, string> = {
|
|
auto: 'Auto',
|
|
'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 === 'auto' || raw === 'openrouter-sonnet' || 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];
|
|
}
|