feat(agent): refresh assistant model catalog

This commit is contained in:
ginnoir
2026-07-08 19:37:17 -05:00
parent 4dc04b21d6
commit b279f16ba0
6 changed files with 111 additions and 41 deletions
@@ -1,7 +1,7 @@
"use client";
import { useEffect, useRef, useState, useTransition } from "react";
import { ChevronDown, ImagePlus, Loader2, Mic, Send, Square } from "lucide-react";
import { useCallback, useEffect, useRef, useState, useTransition } from "react";
import { ChevronDown, ImagePlus, Loader2, Mic, RefreshCw, Send, Square } from "lucide-react";
import { setAssistantModel } from "@/app/settings/assistant-actions";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
@@ -87,34 +87,44 @@ export function AssistantPanel({ configured, userId, assistantName, assistantMod
};
}, []);
useEffect(() => {
let cancelled = false;
const loadModels = useCallback(async (options?: { refresh?: boolean; signal?: AbortSignal }) => {
if (options?.signal?.aborted) return;
async function loadModels() {
setModelsLoading(true);
try {
const response = await fetch("/api/agent/models");
if (!response.ok) throw new Error("Model discovery unavailable");
const payload = (await response.json()) as ModelsResponse;
if (cancelled) return;
setModelOptions(payload.models);
setSelectedModel(payload.selectedModel);
setFallbackModel(payload.fallbackModel);
setModelsDegraded(payload.degraded);
} catch {
if (cancelled) return;
setModelsDegraded(true);
} finally {
if (!cancelled) setModelsLoading(false);
}
setModelsLoading(true);
try {
const response = await fetch(`/api/agent/models${options?.refresh ? "?refresh=1" : ""}`, {
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);
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);
}
void loadModels();
return () => {
cancelled = true;
};
}, []);
useEffect(() => {
const controller = new AbortController();
queueMicrotask(() => {
void loadModels({ signal: controller.signal });
});
return () => {
controller.abort();
};
}, [loadModels]);
function scrollToBottom() {
requestAnimationFrame(() => {
const node = listRef.current;
@@ -271,6 +281,20 @@ export function AssistantPanel({ configured, userId, assistantName, assistantMod
/>
</div>
) : null}
<Button
type="button"
size="sm"
variant="outline"
aria-label="Refresh assistant models"
disabled={modelsLoading || savingModel || isPending}
onClick={() => void loadModels({ refresh: true })}
>
{modelsLoading ? (
<Loader2 className="size-4 animate-spin" />
) : (
<RefreshCw className="size-4" />
)}
</Button>
{messages.length > 0 ? (
<button
type="button"