diff --git a/frigate/data_processing/post/object_descriptions.py b/frigate/data_processing/post/object_descriptions.py index ccb7cc023e..d4c25ac9c7 100644 --- a/frigate/data_processing/post/object_descriptions.py +++ b/frigate/data_processing/post/object_descriptions.py @@ -16,7 +16,7 @@ from frigate.config import CameraConfig, FrigateConfig from frigate.const import CLIPS_DIR, UPDATE_EVENT_DESCRIPTION from frigate.data_processing.post.semantic_trigger import SemanticTriggerProcessor from frigate.data_processing.types import PostProcessDataEnum -from frigate.genai import GenAIClient +from frigate.genai.manager import GenAIClientManager from frigate.models import Event from frigate.types import TrackedObjectUpdateTypesEnum from frigate.util.builtin import EventsPerSecond, InferenceSpeed @@ -41,7 +41,7 @@ class ObjectDescriptionProcessor(PostProcessorApi): embeddings: "Embeddings", requestor: InterProcessRequestor, metrics: DataProcessorMetrics, - client: GenAIClient, + genai_manager: GenAIClientManager, semantic_trigger_processor: SemanticTriggerProcessor | None, ): super().__init__(config, metrics, None) @@ -49,7 +49,7 @@ class ObjectDescriptionProcessor(PostProcessorApi): self.embeddings = embeddings self.requestor = requestor self.metrics = metrics - self.genai_client = client + self.genai_manager = genai_manager self.semantic_trigger_processor = semantic_trigger_processor self.tracked_events: dict[str, list[Any]] = {} self.early_request_sent: dict[str, bool] = {} @@ -198,6 +198,9 @@ class ObjectDescriptionProcessor(PostProcessorApi): if data_type != PostProcessDataEnum.tracked_object: return + if self.genai_manager.description_client is None: + return + state: str | None = frame_data.get("state", None) if state is not None: @@ -329,7 +332,7 @@ class ObjectDescriptionProcessor(PostProcessorApi): """Embed the description for an event.""" start = datetime.datetime.now().timestamp() camera_config = self.config.cameras[str(event.camera)] - description = self.genai_client.generate_object_description( + description = self.genai_manager.description_client.generate_object_description( camera_config, thumbnails, event ) diff --git a/frigate/data_processing/post/review_descriptions.py b/frigate/data_processing/post/review_descriptions.py index 9cfdd42c5b..3354991e1f 100644 --- a/frigate/data_processing/post/review_descriptions.py +++ b/frigate/data_processing/post/review_descriptions.py @@ -22,6 +22,7 @@ from frigate.config.camera.review import GenAIReviewConfig, ImageSourceEnum from frigate.const import CACHE_DIR, CLIPS_DIR, UPDATE_REVIEW_DESCRIPTION from frigate.data_processing.types import PostProcessDataEnum from frigate.genai import GenAIClient +from frigate.genai.manager import GenAIClientManager from frigate.models import Recordings, ReviewSegment from frigate.util.builtin import EventsPerSecond, InferenceSpeed from frigate.util.image import get_image_from_recording @@ -41,12 +42,12 @@ class ReviewDescriptionProcessor(PostProcessorApi): config: FrigateConfig, requestor: InterProcessRequestor, metrics: DataProcessorMetrics, - client: GenAIClient, + genai_manager: GenAIClientManager, ): super().__init__(config, metrics, None) self.requestor = requestor self.metrics = metrics - self.genai_client = client + self.genai_manager = genai_manager self.review_desc_speed = InferenceSpeed(self.metrics.review_desc_speed) self.review_desc_dps = EventsPerSecond() self.review_desc_dps.start() @@ -63,7 +64,7 @@ class ReviewDescriptionProcessor(PostProcessorApi): Estimates ~1 token per 1250 pixels. Targets 98% context utilization with safety margin. Capped at 20 frames. """ - context_size = self.genai_client.get_context_size() + context_size = self.genai_manager.description_client.get_context_size() camera_config = self.config.cameras[camera] detect_width = camera_config.detect.width @@ -111,6 +112,9 @@ class ReviewDescriptionProcessor(PostProcessorApi): if data_type != PostProcessDataEnum.review: return + if self.genai_manager.description_client is None: + return + camera = data["after"]["camera"] camera_config = self.config.cameras[camera] @@ -200,7 +204,7 @@ class ReviewDescriptionProcessor(PostProcessorApi): target=run_analysis, args=( self.requestor, - self.genai_client, + self.genai_manager.description_client, self.review_desc_speed, camera_config, final_data, @@ -316,7 +320,7 @@ class ReviewDescriptionProcessor(PostProcessorApi): os.path.join(CLIPS_DIR, "genai-requests", f"{start_ts}-{end_ts}") ).mkdir(parents=True, exist_ok=True) - return self.genai_client.generate_review_summary( + return self.genai_manager.description_client.generate_review_summary( start_ts, end_ts, events_with_context, diff --git a/frigate/embeddings/maintainer.py b/frigate/embeddings/maintainer.py index f924a8dbbf..4d4368d688 100644 --- a/frigate/embeddings/maintainer.py +++ b/frigate/embeddings/maintainer.py @@ -202,7 +202,7 @@ class EmbeddingMaintainer(threading.Thread): # post processors self.post_processors: list[PostProcessorApi] = [] - if self.genai_manager.description_client is not None and any( + if any( c.review.genai.enabled_in_config for c in self.config.cameras.values() ): self.post_processors.append( @@ -210,7 +210,7 @@ class EmbeddingMaintainer(threading.Thread): self.config, self.requestor, self.metrics, - self.genai_manager.description_client, + self.genai_manager, ) ) @@ -248,7 +248,7 @@ class EmbeddingMaintainer(threading.Thread): ) self.post_processors.append(semantic_trigger_processor) - if self.genai_manager.description_client is not None and any( + if any( c.objects.genai.enabled_in_config for c in self.config.cameras.values() ): self.post_processors.append( @@ -257,7 +257,7 @@ class EmbeddingMaintainer(threading.Thread): self.embeddings, self.requestor, self.metrics, - self.genai_manager.description_client, + self.genai_manager, semantic_trigger_processor, ) ) diff --git a/web/src/components/config-form/section-configs/genai.ts b/web/src/components/config-form/section-configs/genai.ts index 06a4551d0e..e5a2984602 100644 --- a/web/src/components/config-form/section-configs/genai.ts +++ b/web/src/components/config-form/section-configs/genai.ts @@ -3,14 +3,6 @@ import type { SectionConfigOverrides } from "./types"; const genai: SectionConfigOverrides = { base: { sectionDocs: "/configuration/genai/config", - restartRequired: [ - "*.provider", - "*.api_key", - "*.base_url", - "*.model", - "*.provider_options", - "*.runtime_options", - ], advancedFields: ["*.base_url", "*.provider_options", "*.runtime_options"], hiddenFields: ["genai.enabled_in_config"], uiSchema: { diff --git a/web/src/components/config-form/theme/widgets/GenAIModelWidget.tsx b/web/src/components/config-form/theme/widgets/GenAIModelWidget.tsx index 2f35f113e5..bb4cb9fed8 100644 --- a/web/src/components/config-form/theme/widgets/GenAIModelWidget.tsx +++ b/web/src/components/config-form/theme/widgets/GenAIModelWidget.tsx @@ -44,10 +44,9 @@ export function GenAIModelWidget(props: WidgetProps) { const fieldClassName = getSizedFieldClassName(options, "sm"); const providerKey = useMemo(() => getProviderKey(id), [id]); - const { data: allModels } = useSWR>( - "genai/models", - { revalidateOnFocus: false }, - ); + const { data: allModels } = useSWR>("genai/models", { + revalidateOnFocus: false, + }); const models = useMemo(() => { if (!allModels || !providerKey) return [];