diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 9c88140..7da8be5 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -104,7 +104,6 @@ export default async function RootLayout({ children }: { children: React.ReactNo let signedIn = false; let assistantEnabled = false; let assistantName = DEFAULT_ASSISTANT_NAME; - let assistantModelRoute: string | null = null; let assistantModel: string | null = null; const session = await auth(); @@ -119,7 +118,6 @@ export default async function RootLayout({ children }: { children: React.ReactNo themeNavStyle: users.themeNavStyle, assistantEnabled: users.assistantEnabled, assistantName: users.assistantName, - assistantModelRoute: users.assistantModelRoute, assistantModel: users.assistantModel, }) .from(users) @@ -133,7 +131,6 @@ export default async function RootLayout({ children }: { children: React.ReactNo navStyle = row.themeNavStyle as NavStyle; assistantEnabled = row.assistantEnabled; assistantName = row.assistantName?.trim() || DEFAULT_ASSISTANT_NAME; - assistantModelRoute = row.assistantModelRoute?.trim() || null; assistantModel = row.assistantModel?.trim() || null; } userDashboards = await db @@ -192,7 +189,6 @@ export default async function RootLayout({ children }: { children: React.ReactNo configured={isLlmConfigured()} userId={session.user.id} assistantName={assistantName} - assistantModelRoute={assistantModelRoute} assistantModel={assistantModel} /> ) : null} diff --git a/src/app/settings/page.tsx b/src/app/settings/page.tsx index a495932..b9c9736 100644 --- a/src/app/settings/page.tsx +++ b/src/app/settings/page.tsx @@ -324,6 +324,7 @@ function AppearanceSection({ themeNavStyle: string; assistantEnabled: boolean; assistantName: string; + assistantModelRoute: string | null; assistantSystemPrompt: string | null; }; }) { @@ -361,6 +362,7 @@ function AppearanceSection({ diff --git a/src/components/assistant-settings.tsx b/src/components/assistant-settings.tsx index ec0ccb0..fa8725f 100644 --- a/src/components/assistant-settings.tsx +++ b/src/components/assistant-settings.tsx @@ -7,9 +7,11 @@ import { resetAssistantSystemPrompt, setAssistantEnabled, setAssistantName, + setAssistantModelRoute, setAssistantSystemPrompt, } from "@/app/settings/assistant-actions"; import { DEFAULT_ASSISTANT_NAME, MAX_ASSISTANT_NAME_LENGTH } from "@/lib/assistant-config"; +import type { AssistantModelRoute } from "@/lib/llm/models"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Switch } from "@/components/ui/switch"; @@ -17,15 +19,29 @@ import { Switch } from "@/components/ui/switch"; type Props = { enabled: boolean; name: string; + modelRoute: string | null; systemPrompt: string | null; defaultSystemPrompt: string; }; -export function AssistantSettings({ enabled, name, systemPrompt, defaultSystemPrompt }: Props) { +function normalizeAssistantModelRoute(value: string | null): AssistantModelRoute { + return value === "uncensored" ? "uncensored" : "auto"; +} + +export function AssistantSettings({ + enabled, + name, + modelRoute, + systemPrompt, + defaultSystemPrompt, +}: Props) { const [isPending, startTransition] = useTransition(); const router = useRouter(); const effectivePrompt = systemPrompt ?? defaultSystemPrompt; + const [selectedRoute, setSelectedRoute] = useState(() => + normalizeAssistantModelRoute(modelRoute), + ); const [savedName, setSavedName] = useState(name); const [draftName, setDraftName] = useState(name); const [savedPrompt, setSavedPrompt] = useState(systemPrompt); @@ -45,6 +61,16 @@ export function AssistantSettings({ enabled, name, systemPrompt, defaultSystemPr }); } + function changeModelRoute(nextRoute: string) { + const route = normalizeAssistantModelRoute(nextRoute); + setSelectedRoute(route); + + startTransition(async () => { + await setAssistantModelRoute(route); + router.refresh(); + }); + } + function saveName() { const next = draftName.trim(); if (!next) return; @@ -105,6 +131,29 @@ export function AssistantSettings({ enabled, name, systemPrompt, defaultSystemPr /> +
+
+ +

+ Choose the default router family for your assistant. Individual models can still be + picked from the chat panel. +

+
+ +
+
diff --git a/src/modules/agent/components/assistant-bubble.tsx b/src/modules/agent/components/assistant-bubble.tsx index f9cc4ec..904722b 100644 --- a/src/modules/agent/components/assistant-bubble.tsx +++ b/src/modules/agent/components/assistant-bubble.tsx @@ -8,17 +8,10 @@ type Props = { configured: boolean; userId: string; assistantName: string; - assistantModelRoute: string | null; assistantModel: string | null; }; -export function AssistantBubble({ - configured, - userId, - assistantName, - assistantModelRoute, - assistantModel, -}: Props) { +export function AssistantBubble({ configured, userId, assistantName, assistantModel }: Props) { const [open, setOpen] = useState(false); return ( @@ -49,7 +42,6 @@ export function AssistantBubble({ configured={configured} userId={userId} assistantName={assistantName} - assistantModelRoute={assistantModelRoute} assistantModel={assistantModel} />
diff --git a/src/modules/agent/components/assistant-panel.tsx b/src/modules/agent/components/assistant-panel.tsx index 9cc59d2..9b4914b 100644 --- a/src/modules/agent/components/assistant-panel.tsx +++ b/src/modules/agent/components/assistant-panel.tsx @@ -2,10 +2,9 @@ import { useCallback, useEffect, useRef, useState, useTransition } from "react"; import { ChevronDown, ImagePlus, Loader2, Mic, RefreshCw, Send, Square } from "lucide-react"; -import { setAssistantModel, setAssistantModelRoute } from "@/app/settings/assistant-actions"; +import { setAssistantModel } from "@/app/settings/assistant-actions"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; -import type { AssistantModelRoute } from "@/lib/llm/models"; import { consumeAgentChatStream } from "../assistant-chat-stream"; import { clearAssistantChat, @@ -20,7 +19,6 @@ type Props = { configured: boolean; userId: string; assistantName: string; - assistantModelRoute: string | null; assistantModel: string | null; }; @@ -37,7 +35,6 @@ type ModelsResponse = { models: LlmModelOption[]; selectedModel: string; fallbackModel: string; - route: AssistantModelRoute; degraded: boolean; }; @@ -53,17 +50,7 @@ async function uploadAssistantImage(file: File): Promise { return payload.url; } -function normalizeAssistantModelRoute(value: string | null): AssistantModelRoute { - return value === "uncensored" ? "uncensored" : "auto"; -} - -export function AssistantPanel({ - configured, - userId, - assistantName, - assistantModelRoute, - assistantModel, -}: Props) { +export function AssistantPanel({ configured, userId, assistantName, assistantModel }: Props) { const [messages, setMessages] = useState(() => loadAssistantChat(userId)); const [input, setInput] = useState(""); const [pendingImage, setPendingImage] = useState(null); @@ -72,9 +59,6 @@ export function AssistantPanel({ const [isPending, setIsPending] = useState(false); const [activityLabel, setActivityLabel] = useState(null); const [modelOptions, setModelOptions] = useState([]); - const [selectedRoute, setSelectedRoute] = useState(() => - normalizeAssistantModelRoute(assistantModelRoute), - ); const [selectedModel, setSelectedModel] = useState(assistantModel ?? ""); const [fallbackModel, setFallbackModel] = useState(""); const [modelsDegraded, setModelsDegraded] = useState(false); @@ -103,40 +87,35 @@ export function AssistantPanel({ }; }, []); - const loadModels = useCallback( - async (options?: { refresh?: boolean; route?: AssistantModelRoute; signal?: AbortSignal }) => { + const loadModels = useCallback(async (options?: { refresh?: boolean; signal?: AbortSignal }) => { + if (options?.signal?.aborted) return; + + setModelsLoading(true); + try { + const params = new URLSearchParams(); + if (options?.refresh) params.set("refresh", "1"); + const query = params.size > 0 ? `?${params.toString()}` : ""; + + const response = await fetch(`/api/agent/models${query}`, { + cache: "no-store", + signal: options?.signal, + }); + if (!response.ok) throw new Error("Model discovery unavailable"); + const payload = (await response.json()) as ModelsResponse; if (options?.signal?.aborted) return; - - setModelsLoading(true); - try { - const params = new URLSearchParams(); - if (options?.refresh) params.set("refresh", "1"); - if (options?.route) params.set("route", options.route); - const query = params.size > 0 ? `?${params.toString()}` : ""; - - const response = await fetch(`/api/agent/models${query}`, { - cache: "no-store", - signal: options?.signal, - }); - if (!response.ok) throw new Error("Model discovery unavailable"); - const payload = (await response.json()) as ModelsResponse; - if (options?.signal?.aborted) return; - setModelOptions(payload.models); - setSelectedRoute(payload.route); - setSelectedModel(payload.selectedModel); - setFallbackModel(payload.fallbackModel); - setModelsDegraded(payload.degraded); - setError(null); - } catch (err) { - if (err instanceof Error && err.name === "AbortError") return; - setModelsDegraded(true); - setError("Model discovery unavailable"); - } finally { - if (!options?.signal?.aborted) setModelsLoading(false); - } - }, - [], - ); + setModelOptions(payload.models); + setSelectedModel(payload.selectedModel); + setFallbackModel(payload.fallbackModel); + setModelsDegraded(payload.degraded); + setError(null); + } catch (err) { + if (err instanceof Error && err.name === "AbortError") return; + setModelsDegraded(true); + setError("Model discovery unavailable"); + } finally { + if (!options?.signal?.aborted) setModelsLoading(false); + } + }, []); useEffect(() => { const controller = new AbortController(); @@ -200,26 +179,6 @@ export function AssistantPanel({ }); } - function changeModelRoute(nextRoute: string) { - const route = normalizeAssistantModelRoute(nextRoute); - - setSelectedRoute(route); - setSelectedModel(route); - setFallbackModel(route); - setModelOptions([]); - setModelsDegraded(false); - setError(null); - - startTransition(async () => { - try { - await setAssistantModelRoute(route); - await loadModels({ route, refresh: true }); - } catch (err) { - setError(err instanceof Error ? err.message : "Could not save assistant route"); - } - }); - } - async function sendMessage() { const text = input.trim(); const hasImage = pendingImage !== null; @@ -306,22 +265,6 @@ export function AssistantPanel({ ) : null}
-
- -
{modelOptions.length > 0 ? (