""" Google Gemini Provider 구현 """ import asyncio from typing import Optional, List, Dict, Any from ..base import BaseAIProvider, AIProviderType, AIMessage, AIResponse class GeminiProvider(BaseAIProvider): """Google Gemini API 프로바이더""" AVAILABLE_MODELS = [ "gemini-2.0-flash-exp", "gemini-1.5-pro", "gemini-1.5-flash", "gemini-1.5-flash-8b", "gemini-1.0-pro", ] def __init__(self, api_key: str, model: Optional[str] = None): super().__init__(api_key, model) self._client = None @property def provider_type(self) -> AIProviderType: return AIProviderType.GEMINI @property def provider_name(self) -> str: return "Google Gemini" @property def default_model(self) -> str: return "gemini-1.5-flash" @property def available_models(self) -> List[str]: return self.AVAILABLE_MODELS.copy() def initialize(self) -> bool: """Gemini 클라이언트 초기화""" if not self.validate_api_key(): return False try: import google.generativeai as genai genai.configure(api_key=self._api_key) self._client = genai self._is_initialized = True return True except ImportError: print("Google Generative AI 라이브러리가 설치되지 않았습니다. pip install google-generativeai 를 실행하세요.") return False except Exception as e: print(f"Gemini 초기화 실패: {e}") return False def _convert_messages_to_gemini(self, messages: List[AIMessage]) -> tuple: """ AIMessage를 Gemini 형식으로 변환 Returns: (system_instruction, history, last_user_message) """ system_instruction = None history = [] last_user_message = None for msg in messages: if msg.role == "system": system_instruction = msg.content elif msg.role == "user": last_user_message = msg.content # 이전 user 메시지는 history에 추가 if len([m for m in messages if m.role == "user"]) > 1: history.append({"role": "user", "parts": [msg.content]}) elif msg.role == "assistant": history.append({"role": "model", "parts": [msg.content]}) # 마지막 user 메시지를 history에서 제거 (send_message로 별도 전송) if history and history[-1].get("role") == "user": history.pop() return system_instruction, history, last_user_message async def chat( self, messages: List[AIMessage], temperature: float = 0.7, max_tokens: Optional[int] = None, **kwargs ) -> AIResponse: """비동기 채팅 요청 (Gemini는 sync를 async로 래핑)""" # Gemini SDK는 기본적으로 동기식이므로 executor에서 실행 loop = asyncio.get_event_loop() return await loop.run_in_executor( None, lambda: self.chat_sync(messages, temperature, max_tokens, **kwargs) ) def chat_sync( self, messages: List[AIMessage], temperature: float = 0.7, max_tokens: Optional[int] = None, **kwargs ) -> AIResponse: """동기 채팅 요청""" if not self._is_initialized: if not self.initialize(): raise RuntimeError("Gemini 초기화 실패") system_instruction, history, last_user_message = self._convert_messages_to_gemini(messages) # 모델 생성 설정 generation_config = { "temperature": temperature, } if max_tokens: generation_config["max_output_tokens"] = max_tokens # 모델 인스턴스 생성 model = self._client.GenerativeModel( model_name=self._model, generation_config=generation_config, system_instruction=system_instruction ) # 대화 시작 if history: chat = model.start_chat(history=history) else: chat = model.start_chat() # 메시지 전송 response = chat.send_message(last_user_message or "") # 토큰 사용량 추출 시도 usage = None if hasattr(response, 'usage_metadata'): usage = { "prompt_tokens": getattr(response.usage_metadata, 'prompt_token_count', 0), "completion_tokens": getattr(response.usage_metadata, 'candidates_token_count', 0), "total_tokens": getattr(response.usage_metadata, 'total_token_count', 0), } return AIResponse( content=response.text, model=self._model, provider=self.provider_name, usage=usage, raw_response=response )