// Saved LLM configurations: server-side CRUD + one-shot import of any // localStorage settings the user had before this surface existed. // // Lists configurations (own + platform-default), exposes Add/Delete, and // surfaces published costs from the curated catalog so the operator can // see at a glance what each config costs per 1M tokens. import { useCallback, useEffect, useMemo, useState } from "react" import { Plus, Trash2, Upload } from "lucide-react" import { useArcadiaClient } from "@crema/arcadia-client" import { Button } from "~/components/ui/button" import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "~/components/ui/card" import { Input } from "~/components/ui/input" import { Label } from "~/components/ui/label" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "~/components/ui/select" import { createConfiguration, deleteConfiguration, formatCost, getCatalog, getUsageSummary, listConfigurations, type CatalogEntry, type LlmConfiguration, type LlmConfigurationInput, type LlmProvider, type LlmUsageSummary, } from "~/lib/arcadia/llm-configs" const PROVIDERS: LlmProvider[] = ["openai", "anthropic", "deepseek", "qwen", "lmstudio"] const LOCAL_SETTINGS_KEY = "crema.llm-providers.settings" export function LlmConfigurationsPanel() { const arcadia = useArcadiaClient() const [configs, setConfigs] = useState([]) const [catalog, setCatalog] = useState([]) const [usage, setUsage] = useState(null) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [creating, setCreating] = useState(false) const [draft, setDraft] = useState(emptyDraft()) const refresh = useCallback(async () => { setError(null) try { const [list, cat, sum] = await Promise.all([ listConfigurations(arcadia), getCatalog(arcadia).catch(() => [] as CatalogEntry[]), getUsageSummary(arcadia, { days: 30 }).catch(() => null), ]) setConfigs(list) setCatalog(cat) setUsage(sum) } catch (e) { setError(e instanceof Error ? e.message : "Failed to load configurations.") } finally { setLoading(false) } }, [arcadia]) useEffect(() => { void refresh() }, [refresh]) const modelsForProvider = useMemo( () => catalog.filter((c) => c.provider === draft.provider && c.model !== "*"), [catalog, draft.provider], ) const onCreate = async () => { setError(null) try { await createConfiguration(arcadia, draft) setDraft(emptyDraft()) setCreating(false) await refresh() } catch (e) { setError(e instanceof Error ? e.message : "Create failed.") } } const onDelete = async (id: string) => { setError(null) try { await deleteConfiguration(arcadia, id) await refresh() } catch (e) { setError(e instanceof Error ? e.message : "Delete failed.") } } const onImportFromLocal = async () => { setError(null) const raw = typeof window !== "undefined" ? localStorage.getItem(LOCAL_SETTINGS_KEY) : null if (!raw) { setError("No local settings found to import.") return } try { const local = JSON.parse(raw) as { providerId?: string model?: string baseURL?: string secretName?: string } if (!local.providerId || !local.model) { setError("Local settings are incomplete (missing provider or model).") return } await createConfiguration(arcadia, { name: `Imported (${local.providerId})`, provider: local.providerId as LlmProvider, model: local.model, base_url: local.baseURL || null, secret_name: local.secretName || null, }) // Keep the local store as a fallback in case import fails on a later // browser; clearing only happens after a successful refresh confirms // the row exists server-side. localStorage.removeItem(LOCAL_SETTINGS_KEY) await refresh() } catch (e) { setError(e instanceof Error ? e.message : "Import failed.") } } const hasLocalSettings = typeof window !== "undefined" && !!localStorage.getItem(LOCAL_SETTINGS_KEY) return (
Saved configurations Persisted server-side. Each config bundles a provider, model, vault secret, and per-1M-token costs (auto-filled from the catalog). Used by the proxy to attribute cost on every completion.
{usage ? (
Spend (30d) {formatCost(usage.total_cost_cents ?? 0)} {(usage.total_requests ?? 0).toLocaleString()} req ·{" "} {(usage.total_tokens ?? 0).toLocaleString()} tok
) : null}
{hasLocalSettings ? ( ) : null}
{error ? (

{error}

) : null} {creating ? (
setDraft({ ...draft, name: e.target.value })} placeholder="Production GPT-4o-mini" /> setDraft({ ...draft, model })} /> setDraft({ ...draft, secret_name: e.target.value || null })} placeholder="llm-openai-key" /> setDraft({ ...draft, base_url: e.target.value || null })} placeholder="leave blank for provider default" />

Auto-filled from the curated catalog when you save. Override later if you have a negotiated rate.

) : null} {loading ? (

Loading…

) : configs.length === 0 ? (

No configurations yet. Add one to start tracking cost.

) : (
    {configs.map((c) => (
  • {c.name} {c.tenant_id == null ? ( platform ) : null} {!c.enabled ? ( disabled ) : null} {c.provider} · {c.model} {c.secret_name ? ( <> {" "} · secret {c.secret_name} ) : null} {formatRate(c.input_cost_per_million)}/1M in ·{" "} {formatRate(c.output_cost_per_million)}/1M out
    {c.tenant_id != null ? ( ) : null}
  • ))}
)}
) } /** * Model picker: dropdown of catalog entries for the selected provider, plus * a "Custom…" escape hatch that switches to free-text for models the * catalog doesn't know about. The custom branch is sticky — once the user * types a custom name, switching providers resets to the dropdown. */ function ModelPicker({ value, models, onChange, }: { value: string models: CatalogEntry[] onChange: (model: string) => void }) { const known = useMemo(() => new Set(models.map((m) => m.model)), [models]) const isCustom = value !== "" && !known.has(value) const [customMode, setCustomMode] = useState(isCustom) // Reset to dropdown if the available models change (e.g. provider switched) // and we're not actively in custom mode. useEffect(() => { if (!isCustom) setCustomMode(false) }, [models, isCustom]) if (customMode) { return (
onChange(e.target.value)} placeholder="custom-model-id" autoFocus />
) } return ( ) } function Field({ label, children }: { label: string; children: React.ReactNode }) { return (
{children}
) } function emptyDraft(): LlmConfigurationInput { return { name: "", provider: "openai", model: "", base_url: null, secret_name: null, } } function formatRate(rate: number | null): string { if (rate == null) return "—" if (rate === 0) return "free" return `$${rate.toFixed(2)}` }