Source code for aeat.adapters.outbound.llm._providers.gemini
"""Google Gemini provider adapter for the LLM outbound subpackage.Speaks the Gemini ``v1beta/models/{model}:generateContent`` HTTP API andadapts its response shape to the:class:`~adapters.outbound.llm._providers.base.ProviderCompletion`contract. Internal pydantic models mirror the upstream JSON schema and are keptprivate."""from__future__importannotationsfromtypingimportoverrideimporthttpxfrompydanticimportBaseModel,ConfigDict,Fieldfrom.....core.configimportload_settingsfrom.....core.loggingimportget_loggerfrom.._errorsimportLLMConfigError,LLMProviderErrorfrom.._modelsimportLLMProviderfrom.baseimportProviderCompletion,ProviderRequest,_ProviderAdapter,check_http_error_logger=get_logger(__name__)class_GeminiPart(BaseModel):"""Single text part of a Gemini ``content`` block."""model_config=ConfigDict(strict=True,frozen=True)text:str|None=Noneclass_GeminiContent(BaseModel):"""Multi-part content payload returned for a single Gemini candidate."""model_config=ConfigDict(strict=True,frozen=True)parts:tuple[_GeminiPart,...]class_GeminiCandidate(BaseModel):"""One candidate completion within a Gemini response."""model_config=ConfigDict(strict=True,frozen=True)content:_GeminiContentclass_GeminiUsage(BaseModel):"""Token accounting reported by the Gemini API. Attributes: prompt_token_count: Tokens charged for the prompt. candidates_token_count: Tokens charged for the generated candidates. """model_config=ConfigDict(strict=True,frozen=True,populate_by_name=True)prompt_token_count:int=Field(default=0,ge=0,alias="promptTokenCount")candidates_token_count:int=Field(default=0,ge=0,alias="candidatesTokenCount")class_GeminiResponse(BaseModel):"""Top-level Gemini ``generateContent`` response envelope. Attributes: candidates: Candidate completions returned by the model. usage_metadata: Token accounting metadata. """model_config=ConfigDict(strict=True,frozen=True,populate_by_name=True)candidates:tuple[_GeminiCandidate,...]usage_metadata:_GeminiUsage=Field(default_factory=_GeminiUsage,alias="usageMetadata")
[docs]classGeminiAdapter(_ProviderAdapter):"""Provider adapter that invokes the Gemini ``generateContent`` HTTP API. A non-empty ``api_key`` is mandatory; the adapter refuses to construct without one because the underlying API rejects unauthenticated calls. """provider=LLMProvider.GEMINIdef__init__(self,api_key:str,timeout_s:int)->None:"""Initialize the adapter. Args: api_key: Google AI Studio API key. timeout_s: Per-request HTTP timeout in seconds. Raises: LLMConfigError: When ``api_key`` is empty. """ifnotapi_key:msg="AEAT_LLM_GEMINI_API_KEY must be set for the Gemini provider."raiseLLMConfigError(msg)self._api_key=api_keyself._timeout_s=timeout_s
[docs]@overrideasyncdefcomplete(self,request:ProviderRequest)->ProviderCompletion:"""Execute a Gemini completion request. Args: request: Normalized provider request. Returns: A :class:`ProviderCompletion` containing the concatenated text of the first candidate and reported token counts. Raises: LLMProviderError: When the API returns a 4xx or 5xx HTTP error status. """parts:list[dict[str,str]]=[]ifrequest.systemisnotNone:parts.append({"text":f"System instruction:\n{request.system}"})parts.append({"text":request.prompt})endpoint=load_settings().aeat_llm_gemini_generate_content_template.format(model=request.model)try:asyncwithhttpx.AsyncClient(timeout=self._timeout_s)asclient:response=awaitclient.post(endpoint,headers={"x-goog-api-key":self._api_key},json={"contents":[{"role":"user","parts":parts}],"generationConfig":{"temperature":request.temperature,"maxOutputTokens":request.max_tokens,},},)excepthttpx.RequestErrorasexc:_logger.debug("Gemini connection failure model=%s",request.model,exc_info=True)raiseLLMProviderError("Gemini connection failure.")fromexccheck_http_error(response,provider_name="Gemini",model=request.model,logger=_logger)parsed=_GeminiResponse.model_validate_json(response.text)text="".join(part.textor""forpartinparsed.candidates[0].content.parts).strip()returnProviderCompletion(text=text,model=request.model,input_tokens=parsed.usage_metadata.prompt_token_count,output_tokens=parsed.usage_metadata.candidates_token_count,provider_request_id=None,)