import { Button } from "@/components/ui/button"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Input } from "@/components/ui/input"; import { Switch } from "@/components/ui/switch"; import { cn } from "@/lib/utils"; import { MODELS, PROVIDERS, getAutocompleteEligibleModels, getModel, getProvider, providerNeedsKey, providerSupportsKey, type ModelId, type ProviderId, } from "@/modules/ai/config"; import { clearKey, getAllKeys, setKey } from "@/modules/ai/lib/keyring"; import { usePreferencesStore } from "@/modules/settings/preferences"; import { emitKeysChanged, setAutocompleteEnabled, setAutocompleteModelId, setAutocompleteProvider, setDefaultModel, setLmstudioBaseURL, setLmstudioModelId, setOpenaiCompatibleBaseURL, setOpenaiCompatibleModelId, } from "@/modules/settings/store"; import { invoke } from "@tauri-apps/api/core"; import { ArrowDown01Icon, CheckmarkCircle02Icon, Cancel01Icon, } from "@hugeicons/core-free-icons"; import { HugeiconsIcon } from "@hugeicons/react"; import { useEffect, useMemo, useState } from "react"; import { ProviderIcon } from "../components/ProviderIcon"; import { ProviderKeyCard } from "../components/ProviderKeyCard"; import { SectionHeader } from "../components/SectionHeader"; type KeysMap = Record; export function ModelsSection() { const [keys, setKeys] = useState(null); const defaultModel = usePreferencesStore((s) => s.defaultModelId); const lmstudioModelId = usePreferencesStore((s) => s.lmstudioModelId); const openaiCompatModelId = usePreferencesStore( (s) => s.openaiCompatibleModelId, ); useEffect(() => { void getAllKeys().then(setKeys); }, []); const onSave = async (provider: ProviderId, value: string) => { await setKey(provider, value); setKeys((prev) => (prev ? { ...prev, [provider]: value } : prev)); await emitKeysChanged(); }; const onClear = async (provider: ProviderId) => { await clearKey(provider); setKeys((prev) => (prev ? { ...prev, [provider]: null } : prev)); await emitKeysChanged(); }; if (!keys) { return
Loading…
; } const cloudProviders = PROVIDERS.filter( (p) => providerNeedsKey(p.id) && p.id !== "lmstudio" && p.id !== "openai-compatible", ); const configuredCount = cloudProviders.filter((p) => !!keys[p.id]).length; return (
{configuredCount} of {cloudProviders.length} configured
{cloudProviders.map((p) => ( onSave(p.id, v)} onClear={() => onClear(p.id)} /> ))}
onSave("openai-compatible", v)} onClearKey={() => onClear("openai-compatible")} />
); } function DefaultModelBlock({ defaultModel, keys, lmstudioModelId, openaiCompatModelId, }: { defaultModel: ModelId; keys: KeysMap; lmstudioModelId: string; openaiCompatModelId: string; }) { const m = getModel(defaultModel); const isAvailable = (modelId: string, providerId: ProviderId): boolean => { if (modelId === "lmstudio-local") return !!lmstudioModelId.trim(); if (modelId === "openai-compatible-custom") return !!openaiCompatModelId.trim(); return providerNeedsKey(providerId) ? !!keys[providerId] : true; }; return (
{PROVIDERS.map((p) => { const models = MODELS.filter((x) => x.provider === p.id); if (models.length === 0) return null; const hasKey = providerNeedsKey(p.id) ? !!keys[p.id] : true; return (
{p.label} {!hasKey ? ( no key ) : null}
{models.map((mod) => { const available = isAvailable(mod.id, p.id); return ( available && void setDefaultModel(mod.id as ModelId) } className={cn( "flex items-start gap-2 text-[12px]", mod.id === defaultModel && "bg-accent/50", )} > {mod.label} {mod.description} ); })}
); })}
); } function LocalModelsBlock() { const baseURL = usePreferencesStore((s) => s.lmstudioBaseURL); const modelId = usePreferencesStore((s) => s.lmstudioModelId); const [urlDraft, setUrlDraft] = useState(baseURL); const [modelDraft, setModelDraft] = useState(modelId); const [testStatus, setTestStatus] = useState< "idle" | "testing" | "ok" | "fail" >("idle"); useEffect(() => setUrlDraft(baseURL), [baseURL]); useEffect(() => setModelDraft(modelId), [modelId]); const test = async () => { setTestStatus("testing"); try { const status = await invoke("lm_ping", { baseUrl: urlDraft, }); setTestStatus(status > 0 ? "ok" : "fail"); } catch { setTestStatus("fail"); } }; return (
Run any GGUF model on your machine via LM Studio's HTTP server. Enable the server in LM Studio → Developer tab.
setUrlDraft(e.target.value)} onBlur={() => { const v = urlDraft.trim(); if (v && v !== baseURL) void setLmstudioBaseURL(v); }} placeholder="http://localhost:1234/v1" spellCheck={false} className="h-8 flex-1 font-mono text-[11.5px]" />
setModelDraft(e.target.value)} onBlur={() => { const v = modelDraft.trim(); if (v !== modelId) void setLmstudioModelId(v); }} placeholder="qwen2.5-coder-7b-instruct" spellCheck={false} className="h-8 font-mono text-[11.5px]" /> {!modelId.trim() ? (

Enter the model id that's loaded in LM Studio — e.g. the one shown on the server's /v1/models page.

) : null}
); } function OpenAICompatibleBlock({ compatKey, onSaveKey, onClearKey, }: { compatKey: string | null; onSaveKey: (v: string) => Promise; onClearKey: () => Promise; }) { const baseURL = usePreferencesStore((s) => s.openaiCompatibleBaseURL); const modelId = usePreferencesStore((s) => s.openaiCompatibleModelId); const [urlDraft, setUrlDraft] = useState(baseURL); const [modelDraft, setModelDraft] = useState(modelId); const [keyDraft, setKeyDraft] = useState(""); const [testStatus, setTestStatus] = useState< "idle" | "testing" | "ok" | "fail" >("idle"); useEffect(() => setUrlDraft(baseURL), [baseURL]); useEffect(() => setModelDraft(modelId), [modelId]); const test = async () => { setTestStatus("testing"); try { const status = await invoke("lm_ping", { baseUrl: urlDraft, }); setTestStatus(status > 0 ? "ok" : "fail"); } catch { setTestStatus("fail"); } }; return (
Any OpenAI-compatible HTTPS endpoint — vLLM, Z.AI, Fireworks, hosted Ollama, etc.
setUrlDraft(e.target.value)} onBlur={() => { const v = urlDraft.trim(); if (v !== baseURL) void setOpenaiCompatibleBaseURL(v); }} placeholder="https://api.example.com/v1" spellCheck={false} className="h-8 flex-1 font-mono text-[11.5px]" />
setModelDraft(e.target.value)} onBlur={() => { const v = modelDraft.trim(); if (v !== modelId) void setOpenaiCompatibleModelId(v); }} placeholder="gpt-4o, qwen3-max, glm-4.6, …" spellCheck={false} className="h-8 font-mono text-[11.5px]" /> {compatKey ? (
{`${compatKey.slice(0, 4)}${"•".repeat(8)}${compatKey.slice(-4)}`}
) : (
setKeyDraft(e.target.value)} placeholder="Optional — leave empty for unauthenticated endpoints" spellCheck={false} className="h-8 flex-1 font-mono text-[11.5px]" />
)}
); } function AutocompleteBlock({ keys }: { keys: KeysMap }) { const enabled = usePreferencesStore((s) => s.autocompleteEnabled); const provider = usePreferencesStore((s) => s.autocompleteProvider); const modelId = usePreferencesStore((s) => s.autocompleteModelId); const eligible = useMemo(() => getAutocompleteEligibleModels(), []); const currentModel = useMemo( () => MODELS.find((m) => m.provider === provider && m.id === modelId) ?? MODELS.find((m) => m.id === modelId) ?? eligible[0], [eligible, provider, modelId], ); const setModel = (id: string, providerId: ProviderId) => { void setAutocompleteProvider(providerId); void setAutocompleteModelId(id); }; const hasKey = providerSupportsKey(provider) ? providerNeedsKey(provider) ? !!keys[provider] : true : true; // Group eligible models by provider for the dropdown. const grouped = useMemo(() => { const map = new Map(); for (const m of eligible) { const arr = map.get(m.provider) ?? []; arr.push(m); map.set(m.provider, arr); } return map; }, [eligible]); return (
Inline ghost-text suggestions in the code editor. Pick a fast model (LPU/wafer-scale, local, or a small cloud tier).
void setAutocompleteEnabled(v)} />
{PROVIDERS.map((p) => { const list = grouped.get(p.id); if (!list || list.length === 0) return null; const pHasKey = providerNeedsKey(p.id) ? !!keys[p.id] : true; return (
{p.label} {!pHasKey ? ( no key ) : null}
{list.map((m) => ( pHasKey && setModel(m.id, p.id)} className={cn( "text-[11.5px]", m.id === modelId && "bg-accent/50", )} > {m.label} {m.description} ))}
); })}
{!hasKey ? ( No API key configured for {getProvider(provider).label}. Add one above. ) : null}
); } function FieldRow({ label, children, }: { label: string; children: React.ReactNode; }) { return (
{label}
{children}
); } function StatusLine({ status, }: { status: "idle" | "testing" | "ok" | "fail"; }) { if (status === "idle") return null; if (status === "testing") { return ( Testing… ); } if (status === "ok") { return ( Reachable — server responded. ); } return ( Could not reach the server. ); } function Label({ children }: { children: React.ReactNode }) { return ( {children} ); }