mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-04-11 17:47:37 +03:00
GenAI tweak (#22773)
Some checks are pending
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
Some checks are pending
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
* refresh model dropdown after changing provider or base url * decouple list_models from provider init switching providers in the UI left an invalid model in the config, then _init_provider would fail and list_models would return an empty list, making it impossible to select a valid model
This commit is contained in:
parent
67a1531da0
commit
e95e9b52f3
@ -238,10 +238,15 @@ class LlamaCppClient(GenAIClient):
|
|||||||
|
|
||||||
def list_models(self) -> list[str]:
|
def list_models(self) -> list[str]:
|
||||||
"""Return available model IDs from the llama.cpp server."""
|
"""Return available model IDs from the llama.cpp server."""
|
||||||
if self.provider is None:
|
base_url = self.provider or (
|
||||||
|
self.genai_config.base_url.rstrip("/")
|
||||||
|
if self.genai_config.base_url
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
if base_url is None:
|
||||||
return []
|
return []
|
||||||
try:
|
try:
|
||||||
response = requests.get(f"{self.provider}/v1/models", timeout=10)
|
response = requests.get(f"{base_url}/v1/models", timeout=10)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
models = []
|
models = []
|
||||||
for m in response.json().get("data", []):
|
for m in response.json().get("data", []):
|
||||||
|
|||||||
@ -134,10 +134,20 @@ class OllamaClient(GenAIClient):
|
|||||||
|
|
||||||
def list_models(self) -> list[str]:
|
def list_models(self) -> list[str]:
|
||||||
"""Return available model names from the Ollama server."""
|
"""Return available model names from the Ollama server."""
|
||||||
if self.provider is None:
|
client = self.provider
|
||||||
return []
|
if client is None:
|
||||||
|
# Provider init may have failed due to invalid model, but we can
|
||||||
|
# still list available models with a fresh client.
|
||||||
|
if not self.genai_config.base_url:
|
||||||
|
return []
|
||||||
|
try:
|
||||||
|
client = ApiClient(
|
||||||
|
host=self.genai_config.base_url, timeout=self.timeout
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
return []
|
||||||
try:
|
try:
|
||||||
response = self.provider.list()
|
response = client.list()
|
||||||
return sorted(
|
return sorted(
|
||||||
m.get("name", m.get("model", "")) for m in response.get("models", [])
|
m.get("name", m.get("model", "")) for m in response.get("models", [])
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// Combobox widget for genai *.model fields.
|
// Combobox widget for genai *.model fields.
|
||||||
// Fetches available models from the provider's backend and shows them in a dropdown.
|
// Fetches available models from the provider's backend and shows them in a dropdown.
|
||||||
import { useState, useMemo } from "react";
|
import { useState, useMemo, useEffect, useRef } from "react";
|
||||||
import type { WidgetProps } from "@rjsf/utils";
|
import type { WidgetProps } from "@rjsf/utils";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
@ -19,6 +19,7 @@ import {
|
|||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from "@/components/ui/popover";
|
} from "@/components/ui/popover";
|
||||||
|
import type { ConfigFormContext } from "@/types/configForm";
|
||||||
import { getSizedFieldClassName } from "../utils";
|
import { getSizedFieldClassName } from "../utils";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -37,17 +38,45 @@ function getProviderKey(widgetId: string): string | undefined {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function GenAIModelWidget(props: WidgetProps) {
|
export function GenAIModelWidget(props: WidgetProps) {
|
||||||
const { id, value, disabled, readonly, onChange, options } = props;
|
const { id, value, disabled, readonly, onChange, options, registry } = props;
|
||||||
const { t } = useTranslation(["views/settings"]);
|
const { t } = useTranslation(["views/settings"]);
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
const fieldClassName = getSizedFieldClassName(options, "sm");
|
const fieldClassName = getSizedFieldClassName(options, "sm");
|
||||||
const providerKey = useMemo(() => getProviderKey(id), [id]);
|
const providerKey = useMemo(() => getProviderKey(id), [id]);
|
||||||
|
|
||||||
const { data: allModels } = useSWR<Record<string, string[]>>("genai/models", {
|
const formContext = registry?.formContext as ConfigFormContext | undefined;
|
||||||
|
|
||||||
|
// Build a fingerprint from the saved config's provider + base_url so the
|
||||||
|
// SWR key changes (and models are refetched) whenever those fields are saved.
|
||||||
|
const configFingerprint = useMemo(() => {
|
||||||
|
if (!providerKey) return "";
|
||||||
|
const genai = (
|
||||||
|
formContext?.fullConfig as Record<string, unknown> | undefined
|
||||||
|
)?.genai;
|
||||||
|
if (!genai || typeof genai !== "object" || Array.isArray(genai)) return "";
|
||||||
|
const entry = (genai as Record<string, unknown>)[providerKey];
|
||||||
|
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return "";
|
||||||
|
const e = entry as Record<string, unknown>;
|
||||||
|
return `${e.provider ?? ""}|${e.base_url ?? ""}`;
|
||||||
|
}, [providerKey, formContext?.fullConfig]);
|
||||||
|
|
||||||
|
const { data: allModels, mutate: mutateModels } = useSWR<
|
||||||
|
Record<string, string[]>
|
||||||
|
>("genai/models", {
|
||||||
revalidateOnFocus: false,
|
revalidateOnFocus: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Revalidate models when the saved config fingerprint changes (e.g. after
|
||||||
|
// switching provider or base_url and saving).
|
||||||
|
const prevFingerprint = useRef(configFingerprint);
|
||||||
|
useEffect(() => {
|
||||||
|
if (configFingerprint !== prevFingerprint.current) {
|
||||||
|
prevFingerprint.current = configFingerprint;
|
||||||
|
mutateModels();
|
||||||
|
}
|
||||||
|
}, [configFingerprint, mutateModels]);
|
||||||
|
|
||||||
const models = useMemo(() => {
|
const models = useMemo(() => {
|
||||||
if (!allModels || !providerKey) return [];
|
if (!allModels || !providerKey) return [];
|
||||||
return allModels[providerKey] ?? [];
|
return allModels[providerKey] ?? [];
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user