import { useCallback, useRef, useState } from "react"; import type { RuntimeModelFile } from "../features/runtime/types"; import { resolveAlias } from "../lib/modelAlias"; import { useToast } from "../components/Toast"; interface Options { preferredModel: string; onSwitchToCooper: (cooperPath: string) => void; localModels: RuntimeModelFile[]; } interface SessionModelController { effectiveModel: string; overrideSource: 'auto' | 'user'; setUserOverride: (modelPath: string) => void; clearOverride: () => void; onDegradationDetected: () => void; degradationPromptShown: boolean; } export function useSessionModelController({ preferredModel, onSwitchToCooper, localModels, }: Options): SessionModelController { const { showToast } = useToast(); const [effectiveModel, setEffectiveModel] = useState(preferredModel); const [overrideSource, setOverrideSource] = useState<'auto' | 'user'>('auto'); const [degradationPromptShown, setDegradationPromptShown] = useState(false); // Keep effectiveModel in sync when preferredModel changes externally (e.g. startup auto-select) // but only if the user hasn't manually overridden const prevPreferredRef = useRef(preferredModel); if (preferredModel !== prevPreferredRef.current) { prevPreferredRef.current = preferredModel; if (overrideSource === 'auto') { setEffectiveModel(preferredModel); } } const setUserOverride = useCallback((modelPath: string) => { setEffectiveModel(modelPath); setOverrideSource('user'); }, []); const clearOverride = useCallback(() => { setEffectiveModel(preferredModel); setOverrideSource('auto'); }, [preferredModel]); const onDegradationDetected = useCallback(() => { // Don't show if user already chose manually or already shown if (overrideSource === 'user' || degradationPromptShown) return; // Find Copper's path from localModels const copperModel = localModels.find((m) => resolveAlias(m.path) === 'Copper'); if (!copperModel) return; // Copper not available, can't suggest setDegradationPromptShown(true); showToast({ id: 'tps-degradation', variant: 'warning', message: 'Performance degraded', detail: 'TPS dropped below 30 for 5s. Switch to Copper (0.8B) for faster responses?', action: { label: 'Switch to Copper', onClick: () => { setUserOverride(copperModel.path); onSwitchToCooper(copperModel.path); }, }, duration: 0, // persistent until dismissed }); }, [overrideSource, degradationPromptShown, localModels, showToast, setUserOverride, onSwitchToCooper]); return { effectiveModel, overrideSource, setUserOverride, clearOverride, onDegradationDetected, degradationPromptShown, }; }