import { useEffect, useState } from "react" import { Check, X, Loader2 } from "lucide-react" import { listModels } from "@crema/llm-ui" import { AppShell } from "~/components/layout/app-shell" import { Button } from "~/components/ui/button" import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "~/components/ui/card" import { Input } from "~/components/ui/input" import { DEFAULT_SETTINGS, saveLLMSettings, useLLMSettings, type LLMSettings, } from "~/lib/llm-settings" import { pageTitle } from "~/lib/page-meta" export const meta = () => pageTitle("Settings") type TestState = | { kind: "idle" } | { kind: "running" } | { kind: "ok"; count: number } | { kind: "fail"; reason: string } export default function SettingsRoute() { const settings = useLLMSettings() const [draft, setDraft] = useState(settings) const [savedAt, setSavedAt] = useState(null) const [test, setTest] = useState({ kind: "idle" }) useEffect(() => { setDraft(settings) }, [settings]) const runTest = async () => { setTest({ kind: "running" }) const ac = new AbortController() const timeout = setTimeout(() => ac.abort(), 4000) try { const rows = await listModels({ baseURL: draft.baseURL, signal: ac.signal }) setTest({ kind: "ok", count: rows.length }) } catch (e) { setTest({ kind: "fail", reason: e instanceof Error ? e.message : String(e), }) } finally { clearTimeout(timeout) } } const dirty = draft.baseURL !== settings.baseURL || draft.contextTokens !== settings.contextTokens || draft.responseBudget !== settings.responseBudget const save = () => { saveLLMSettings(draft) setSavedAt(Date.now()) } const reset = () => { setDraft(DEFAULT_SETTINGS) } return ( LLM Configure the local model endpoint and context budgets used by the Assistant. setDraft((d) => ({ ...d, baseURL: e.target.value })) } placeholder="http://localhost:1234/v1" spellCheck={false} autoComplete="off" /> setDraft((d) => ({ ...d, contextTokens: Number(e.target.value) || d.contextTokens, })) } /> setDraft((d) => ({ ...d, responseBudget: Number(e.target.value) || d.responseBudget, })) } />
{savedAt && !dirty && ( Saved. )} {test.kind === "ok" && ( {test.count} model{test.count === 1 ? "" : "s"} available. )} {test.kind === "fail" && ( Failed: {test.reason.slice(0, 60)} )}
) } function Field({ label, hint, children, }: { label: string hint?: string children: React.ReactNode }) { return ( ) }