diff --git a/app/lib/llm-config-bootstrap.tsx b/app/lib/llm-config-bootstrap.tsx new file mode 100644 index 0000000..07d8126 --- /dev/null +++ b/app/lib/llm-config-bootstrap.tsx @@ -0,0 +1,91 @@ +// One-time bootstrap of the active LLM settings from arcadia. +// +// On mount (and again whenever the session changes), if the operator has no +// active LLM settings in localStorage, fetch the tenant's enabled +// configurations and seed the active settings from the preferred row: +// +// 1. Any row with `metadata.default === true` (operator-marked default). +// 2. Otherwise the first enabled row. +// +// Once active settings exist, this component does nothing — the settings +// panel remains the place to switch between configs. + +import { useEffect } from "react" +import { useArcadiaClient } from "@crema/arcadia-client" +import { + loadSettings, + saveSettings, + type LLMProvidersSettings, + type ProviderId, +} from "@crema/llm-providers-ui" + +import { + listConfigurations, + saveActiveReasoning, + type LlmConfiguration, +} from "~/lib/arcadia/llm-configs" + +const ACTIVE_KEY = "crema.llm-providers.settings" + +function hasActiveSettings(): boolean { + if (typeof window === "undefined") return false + return !!localStorage.getItem(ACTIVE_KEY) +} + +function pickPreferred(configs: LlmConfiguration[]): LlmConfiguration | null { + const enabled = configs.filter((c) => c.enabled) + if (enabled.length === 0) return null + const flagged = enabled.find( + (c) => (c.metadata as { default?: boolean } | null)?.default === true, + ) + return flagged ?? enabled[0] +} + +function applyConfig(c: LlmConfiguration): void { + const current = loadSettings() + const next: LLMProvidersSettings = { + ...current, + providerId: c.provider as ProviderId, + model: c.model, + baseURL: c.base_url || undefined, + secretName: c.secret_name || undefined, + } + saveSettings(next) + saveActiveReasoning(c.reasoning_effort ?? "off") +} + +export function LlmConfigBootstrap() { + const arcadia = useArcadiaClient() + + useEffect(() => { + let cancelled = false + + const tryBootstrap = async () => { + if (hasActiveSettings()) return + const token = + typeof window !== "undefined" + ? sessionStorage.getItem("arcadia_access_token") + : null + if (!token) return + try { + const configs = await listConfigurations(arcadia, { enabled: true }) + if (cancelled) return + const pick = pickPreferred(configs) + if (pick && !hasActiveSettings()) applyConfig(pick) + } catch { + // 401 / network — silently skip; will retry on next session change. + } + } + + void tryBootstrap() + + const onSessionChange = () => void tryBootstrap() + window.addEventListener("crema:session-change", onSessionChange) + return () => { + cancelled = true + window.removeEventListener("crema:session-change", onSessionChange) + } + }, [arcadia]) + + return null +} diff --git a/app/root.tsx b/app/root.tsx index b4b1d84..0458807 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -13,6 +13,7 @@ import "./app.css" import { ToastProvider, Toaster } from "@crema/notification-ui" import { CommandBusProvider } from "@crema/action-bus" import { ArcadiaProvider } from "@crema/arcadia-client" +import { LlmConfigBootstrap } from "~/lib/llm-config-bootstrap" // CREMA:PROVIDERS-IMPORTS const ARCADIA_URL = import.meta.env.VITE_ARCADIA_URL ?? "http://localhost:4000" @@ -61,6 +62,7 @@ export default function App() { }} > +