replace deprecated google-generativeai with google-genai

update gemini genai provider with new calls from SDK
provider_options specifies any http options
suppress unneeded info logging
This commit is contained in:
Josh Hawkins 2026-01-14 20:10:28 -06:00
parent 7900db3a77
commit 83e46b9e41
3 changed files with 30 additions and 22 deletions

View File

@ -47,7 +47,7 @@ onnxruntime == 1.22.*
# Embeddings # Embeddings
transformers == 4.45.* transformers == 4.45.*
# Generative AI # Generative AI
google-generativeai == 0.8.* google-genai == 1.58.*
ollama == 0.6.* ollama == 0.6.*
openai == 1.65.* openai == 1.65.*
# push notifications # push notifications

View File

@ -3,8 +3,8 @@
import logging import logging
from typing import Optional from typing import Optional
import google.generativeai as genai from google import genai
from google.api_core.exceptions import GoogleAPICallError from google.genai import errors, types
from frigate.config import GenAIProviderEnum from frigate.config import GenAIProviderEnum
from frigate.genai import GenAIClient, register_genai_provider from frigate.genai import GenAIClient, register_genai_provider
@ -16,44 +16,51 @@ logger = logging.getLogger(__name__)
class GeminiClient(GenAIClient): class GeminiClient(GenAIClient):
"""Generative AI client for Frigate using Gemini.""" """Generative AI client for Frigate using Gemini."""
provider: genai.GenerativeModel provider: genai.Client
def _init_provider(self): def _init_provider(self):
"""Initialize the client.""" """Initialize the client."""
genai.configure(api_key=self.genai_config.api_key) # Merge provider_options into HttpOptions
return genai.GenerativeModel( http_options_dict = {
self.genai_config.model, **self.genai_config.provider_options "api_version": "v1",
"timeout": int(self.timeout * 1000), # requires milliseconds
}
if isinstance(self.genai_config.provider_options, dict):
http_options_dict.update(self.genai_config.provider_options)
return genai.Client(
api_key=self.genai_config.api_key,
http_options=types.HttpOptions(**http_options_dict),
) )
def _send(self, prompt: str, images: list[bytes]) -> Optional[str]: def _send(self, prompt: str, images: list[bytes]) -> Optional[str]:
"""Submit a request to Gemini.""" """Submit a request to Gemini."""
data = [ contents = [
{ types.Part.from_bytes(data=img, mime_type="image/jpeg") for img in images
"mime_type": "image/jpeg",
"data": img,
}
for img in images
] + [prompt] ] + [prompt]
try: try:
# Merge runtime_options into generation_config if provided # Merge runtime_options into generation_config if provided
generation_config_dict = {"candidate_count": 1} generation_config_dict = {"candidate_count": 1}
generation_config_dict.update(self.genai_config.runtime_options) generation_config_dict.update(self.genai_config.runtime_options)
response = self.provider.generate_content( response = self.provider.models.generate_content(
data, model=self.genai_config.model,
generation_config=genai.types.GenerationConfig( contents=contents,
**generation_config_dict config=types.GenerateContentConfig(
), **generation_config_dict,
request_options=genai.types.RequestOptions(
timeout=self.timeout,
), ),
) )
except GoogleAPICallError as e: except errors.APIError as e:
logger.warning("Gemini returned an error: %s", str(e)) logger.warning("Gemini returned an error: %s", str(e))
return None return None
except Exception as e:
logger.warning("An unexpected error occurred with Gemini: %s", str(e))
return None
try: try:
description = response.text.strip() description = response.text.strip()
except ValueError: except (ValueError, AttributeError):
# No description was generated # No description was generated
return None return None
return description return description

View File

@ -89,6 +89,7 @@ def apply_log_levels(default: str, log_levels: dict[str, LogLevel]) -> None:
"ws4py": LogLevel.error, "ws4py": LogLevel.error,
"PIL": LogLevel.warning, "PIL": LogLevel.warning,
"numba": LogLevel.warning, "numba": LogLevel.warning,
"google_genai.models": LogLevel.warning,
**log_levels, **log_levels,
} }