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.
Manages GenAI provider clients from Frigate config. Configuration is read only
in _update_config(); no other code should read config.genai. Exposes clients
by role: chat_client, description_client, embeddings_client.
Manages GenAI provider clients from Frigate config. Clients are created lazily
on first access so that providers whose roles are never used (e.g. chat when
no chat feature is active) are never initialized.
"""
import logging
from typing import TYPE_CHECKING, Optional
from frigate.config import FrigateConfig
from frigate.config.camera.genai import GenAIRoleEnum
from frigate.config.camera.genai import GenAIConfig, GenAIRoleEnum
if TYPE_CHECKING:
from frigate.genai import GenAIClient
@ -21,24 +21,22 @@ class GenAIClientManager:
"""Manages GenAI provider clients from Frigate config."""
def __init__(self, config: FrigateConfig) -> None:
self._chat_client: Optional[GenAIClient] = None
self._description_client: Optional[GenAIClient] = None
self._embeddings_client: Optional[GenAIClient] = None
self._configs: dict[str, GenAIConfig] = {}
self._role_map: dict[GenAIRoleEnum, str] = {}
self._clients: dict[str, "GenAIClient"] = {}
self.update_config(config)
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.
Each role (chat, descriptions, embeddings) gets the client for the
provider that has that role in its roles list.
Clients are not created here; they are instantiated lazily on first
access via a role property or list_models().
"""
from frigate.genai import PROVIDERS, load_providers
self._chat_client = None
self._description_client = None
self._embeddings_client = None
self._configs = {}
self._role_map = {}
self._clients = {}
if not config.genai:
@ -46,51 +44,72 @@ class GenAIClientManager:
load_providers()
for _name, genai_cfg in config.genai.items():
for name, genai_cfg in config.genai.items():
if not genai_cfg.provider:
continue
provider_cls = PROVIDERS.get(genai_cfg.provider)
if not provider_cls:
if genai_cfg.provider not in PROVIDERS:
logger.warning(
"Unknown GenAI provider %s in config, skipping.",
genai_cfg.provider,
)
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:
if role == GenAIRoleEnum.chat:
self._chat_client = client
elif role == GenAIRoleEnum.descriptions:
self._description_client = client
elif role == GenAIRoleEnum.embeddings:
self._embeddings_client = client
self._role_map[role] = name
def _get_client(self, name: str) -> "Optional[GenAIClient]":
"""Return the client for *name*, creating it on first access."""
if name in self._clients:
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
def chat_client(self) -> "Optional[GenAIClient]":
"""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
def description_client(self) -> "Optional[GenAIClient]":
"""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
def embeddings_client(self) -> "Optional[GenAIClient]":
"""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]]:
"""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 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/
export default defineConfig({