LLM_Monitor / gemini_adapter.py
potato-pzy
fix: revert default Gemini model back to gemini-3.1-flash-lite
a076686
Raw
History Blame Contribute Delete
3.18 kB
"""
gemini_adapter.py — Concrete LLMAdapter for Google Gemini.
V3 (Sidecar): adds stream_chat() for sentence-level streaming.
"""
import os
from typing import AsyncGenerator, Optional
import google.generativeai as genai
from llm_adapter import LLMAdapter
class GeminiAdapter(LLMAdapter):
"""
Wraps Google Gemini API as an LLMAdapter.
Supports both blocking `chat()` and streaming `stream_chat()`.
"""
def __init__(
self,
api_key: Optional[str] = None,
model_name: str = "gemini-3.1-flash-lite",
system_prompt: Optional[str] = None,
):
self.api_key = api_key or os.getenv("GEMINI_API_KEY")
if not self.api_key:
raise ValueError(
"Gemini API key not found. Set GEMINI_API_KEY environment variable "
"or pass api_key directly."
)
self.model_name = model_name
self.system_prompt = system_prompt or "You are a helpful AI assistant."
genai.configure(api_key=self.api_key)
self._model = genai.GenerativeModel(
model_name=self.model_name,
system_instruction=self.system_prompt,
)
# ------------------------------------------------------------------
# Blocking interface (existing — unchanged)
# ------------------------------------------------------------------
def chat(self, prompt: str, system_prompt: Optional[str] = None) -> str:
"""Send prompt, return full response text (blocking)."""
model = self._get_model(system_prompt)
response = model.generate_content(prompt)
return response.text
# ------------------------------------------------------------------
# Streaming interface (NEW)
# ------------------------------------------------------------------
async def stream_chat(
self,
prompt: str,
system_prompt: Optional[str] = None,
) -> AsyncGenerator[str, None]:
"""
Yield token chunks from Gemini as they arrive.
This is a synchronous SDK call wrapped in an async generator —
Gemini's Python SDK streams synchronously, so we iterate the
response object directly and yield each text chunk.
"""
model = self._get_model(system_prompt)
# generate_content with stream=True returns a synchronous iterator
response = model.generate_content(prompt, stream=True)
for chunk in response:
text = getattr(chunk, "text", None)
if text:
yield text
# ------------------------------------------------------------------
# Helpers
# ------------------------------------------------------------------
def _get_model(self, system_prompt: Optional[str]):
"""Return model instance, recreating if system prompt differs."""
if system_prompt and system_prompt != self.system_prompt:
return genai.GenerativeModel(
model_name=self.model_name,
system_instruction=system_prompt,
)
return self._model
def get_model_name(self) -> str:
return self.model_name