Improve model loading

This commit is contained in:
Nicolas Mowen 2026-04-03 15:57:05 -06:00
parent 8e4f2cc5f8
commit 3a79f3952e
2 changed files with 56 additions and 37 deletions

View File

@ -1,15 +1,15 @@
"""GenAI client manager for Frigate. """GenAI client manager for Frigate.
Manages GenAI provider clients from Frigate config. Configuration is read only Manages GenAI provider clients from Frigate config. Clients are created lazily
in _update_config(); no other code should read config.genai. Exposes clients on first access so that providers whose roles are never used (e.g. chat when
by role: chat_client, description_client, embeddings_client. no chat feature is active) are never initialized.
""" """
import logging import logging
from typing import TYPE_CHECKING, Optional from typing import TYPE_CHECKING, Optional
from frigate.config import FrigateConfig from frigate.config import FrigateConfig
from frigate.config.camera.genai import GenAIRoleEnum from frigate.config.camera.genai import GenAIConfig, GenAIRoleEnum
if TYPE_CHECKING: if TYPE_CHECKING:
from frigate.genai import GenAIClient from frigate.genai import GenAIClient
@ -21,24 +21,22 @@ class GenAIClientManager:
"""Manages GenAI provider clients from Frigate config.""" """Manages GenAI provider clients from Frigate config."""
def __init__(self, config: FrigateConfig) -> None: def __init__(self, config: FrigateConfig) -> None:
self._chat_client: Optional[GenAIClient] = None self._configs: dict[str, GenAIConfig] = {}
self._description_client: Optional[GenAIClient] = None self._role_map: dict[GenAIRoleEnum, str] = {}
self._embeddings_client: Optional[GenAIClient] = None
self._clients: dict[str, "GenAIClient"] = {} self._clients: dict[str, "GenAIClient"] = {}
self.update_config(config) self.update_config(config)
def update_config(self, config: FrigateConfig) -> None: def update_config(self, config: FrigateConfig) -> None:
"""Build role clients from current Frigate config.genai. """Store provider configs and build the role→name mapping.
Called from __init__ and can be called again when config is reloaded. Called from __init__ and can be called again when config is reloaded.
Each role (chat, descriptions, embeddings) gets the client for the Clients are not created here; they are instantiated lazily on first
provider that has that role in its roles list. access via a role property or list_models().
""" """
from frigate.genai import PROVIDERS, load_providers from frigate.genai import PROVIDERS, load_providers
self._chat_client = None self._configs = {}
self._description_client = None self._role_map = {}
self._embeddings_client = None
self._clients = {} self._clients = {}
if not config.genai: if not config.genai:
@ -46,51 +44,72 @@ class GenAIClientManager:
load_providers() load_providers()
for _name, genai_cfg in config.genai.items(): for name, genai_cfg in config.genai.items():
if not genai_cfg.provider: if not genai_cfg.provider:
continue continue
provider_cls = PROVIDERS.get(genai_cfg.provider) if genai_cfg.provider not in PROVIDERS:
if not provider_cls:
logger.warning( logger.warning(
"Unknown GenAI provider %s in config, skipping.", "Unknown GenAI provider %s in config, skipping.",
genai_cfg.provider, genai_cfg.provider,
) )
continue continue
try:
client = provider_cls(genai_cfg)
except Exception as e:
logger.exception(
"Failed to create GenAI client for provider %s: %s",
genai_cfg.provider,
e,
)
continue
self._clients[_name] = client self._configs[name] = genai_cfg
for role in genai_cfg.roles: for role in genai_cfg.roles:
if role == GenAIRoleEnum.chat: self._role_map[role] = name
self._chat_client = client
elif role == GenAIRoleEnum.descriptions: def _get_client(self, name: str) -> "Optional[GenAIClient]":
self._description_client = client """Return the client for *name*, creating it on first access."""
elif role == GenAIRoleEnum.embeddings: if name in self._clients:
self._embeddings_client = client return self._clients[name]
from frigate.genai import PROVIDERS
genai_cfg = self._configs.get(name)
if not genai_cfg:
return None
provider_cls = PROVIDERS.get(genai_cfg.provider)
if not provider_cls:
return None
try:
client = provider_cls(genai_cfg)
except Exception as e:
logger.exception(
"Failed to create GenAI client for provider %s: %s",
genai_cfg.provider,
e,
)
return None
self._clients[name] = client
return client
@property @property
def chat_client(self) -> "Optional[GenAIClient]": def chat_client(self) -> "Optional[GenAIClient]":
"""Client configured for the chat role (e.g. chat with function calling).""" """Client configured for the chat role (e.g. chat with function calling)."""
return self._chat_client name = self._role_map.get(GenAIRoleEnum.chat)
return self._get_client(name) if name else None
@property @property
def description_client(self) -> "Optional[GenAIClient]": def description_client(self) -> "Optional[GenAIClient]":
"""Client configured for the descriptions role (e.g. review descriptions, object descriptions).""" """Client configured for the descriptions role (e.g. review descriptions, object descriptions)."""
return self._description_client name = self._role_map.get(GenAIRoleEnum.descriptions)
return self._get_client(name) if name else None
@property @property
def embeddings_client(self) -> "Optional[GenAIClient]": def embeddings_client(self) -> "Optional[GenAIClient]":
"""Client configured for the embeddings role.""" """Client configured for the embeddings role."""
return self._embeddings_client name = self._role_map.get(GenAIRoleEnum.embeddings)
return self._get_client(name) if name else None
def list_models(self) -> dict[str, list[str]]: def list_models(self) -> dict[str, list[str]]:
"""Return available models keyed by config entry name.""" """Return available models keyed by config entry name."""
return {name: client.list_models() for name, client in self._clients.items()} result: dict[str, list[str]] = {}
for name in self._configs:
client = self._get_client(name)
if client:
result[name] = client.list_models()
return result

View File

@ -4,7 +4,7 @@ import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc"; import react from "@vitejs/plugin-react-swc";
import monacoEditorPlugin from "vite-plugin-monaco-editor"; import monacoEditorPlugin from "vite-plugin-monaco-editor";
const proxyHost = process.env.PROXY_HOST || "localhost:5000"; const proxyHost = process.env.PROXY_HOST || "192.168.50.106:5002";
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({