o
    lQi#                     @  s   d Z ddlmZ ddlZddlZddlmZ ddlmZm	Z	 ddl
Z
ddlmZmZ ddlmZ dd	lmZmZmZmZ d
Zd!ddZeG dd dZG dd dZG dd deZG dd deZG dd dZd"ddZd#dd ZdS )$z
Gemini client for transcript variant generation.

This is the production counterpart to the prompt test harness. It reuses the
same cacheable prompt and schema, but exposes a worker-friendly API.
    )annotationsN)	dataclass)AnyOptional   )GEMINI_MODELTHINKING_LEVEL)
TokenUsage)TranscriptVariantBatchResult$build_transcript_variant_user_prompt'get_cacheable_transcript_variant_prompt"get_transcript_variant_json_schemaz0https://generativelanguage.googleapis.com/v1betareturnstrc                  C  s(   t t d d d } d|  S )Nzutf-8   ztranscript-variant-)hashlibsha1r   encode	hexdigest)prompt_hash r   src/variant_provider.pyget_variant_cache_display_name   s   


r   c                   @  s&   e Zd ZU ded< ded< ded< dS )VariantBatchResultlist[dict[str, Any]]itemsr	   token_usagedict[str, Any]raw_responseN)__name__
__module____qualname____annotations__r   r   r   r   r   $   s   
 r   c                   @  s>   e Zd ZdddZdd	d
ZdddZdddZdddZdS )TranscriptVariantCacheManagerapi_keyr   c                 C  s
   || _ d S N)r$   )selfr$   r   r   r   __init__,   s   
z&TranscriptVariantCacheManager.__init__ttl_sintr   r   c                   s(   |   I d H }|r|S | |I d H S r%   )_find_existing_cache_create_cache)r&   r(   existingr   r   r   ensure_cache/   s
   z*TranscriptVariantCacheManager.ensure_cachedict[str, Any] | Nonec              	     s   t  d| j }t }tjdd4 I d H ]}||I d H }|jdkr0	 W d   I d H  d S dt }| dg D ]*}|d|krg|d|krg| 	|d	 I d H }|pZ|  W  d   I d H  S q=W d   I d H  d S 1 I d H syw   Y  d S )
N/cachedContents?key=      >@timeout   models/cachedContentsmodeldisplayNamename)
AISTUDIO_BASEr$   r   httpxAsyncClientgetstatus_coder   jsonget_cache_info)r&   urldisplay_nameclientresp
model_namecachedetailedr   r   r   r*   5   s(   


		z2TranscriptVariantCacheManager._find_existing_cachec              	     s   t  d| j }dt t ddt igi| dd}tjdd4 I d H .}|j||d	I d H }|jd
krGt	d|j d|j
d d  | W  d   I d H  S 1 I d H s[w   Y  d S )Nr/   r4   partstexts)r6   r7   systemInstructionttl      N@r1   r>   r3   zCache creation failed:    )r9   r$   r   r   r   r:   r;   postr=   RuntimeErrorrH   r>   )r&   r(   r@   bodyrB   rC   r   r   r   r+   C   s   
 0z+TranscriptVariantCacheManager._create_cache
cache_namec              	     s   t  d| d| j }tjdd4 I d H '}||I d H }|jdkr2| W  d   I d H  S W d   I d H  d S 1 I d H sCw   Y  d S )N/z?key=r0   r1   r3   )r9   r$   r:   r;   r<   r=   r>   )r&   rS   r@   rB   rC   r   r   r   r?   S   s   
z,TranscriptVariantCacheManager.get_cache_infoN)r$   r   )r(   r)   r   r   )r   r.   )rS   r   r   r.   )r   r    r!   r'   r-   r*   r+   r?   r   r   r   r   r#   +   s    



r#   c                   @     e Zd ZdZdS )GeminiRetryableErrorzMRaised for errors that should be retried (429, 500, timeout, empty response).Nr   r    r!   __doc__r   r   r   r   rV   \       rV   c                   @  rU   )GeminiPermanentErrorzNRaised for errors that should NOT be retried (400 bad request, schema issues).NrW   r   r   r   r   rZ   a   rY   rZ   c                   @  sN   e Zd ZdZdZdddZd	d
 ZdddZd ddZd!ddZ	d"ddZ
dS )#TranscriptVariantClient   )   
      (   r$   r   rS   Optional[str]c                 C  s@   || _ || _t | _tjtjdddddtjdddd| _d S )	Ng      $@rL   g      4@)connectreadwritepoold   2   )max_connectionsmax_keepalive_connections)r2   limits)	r$   rS   r   schemar:   r;   TimeoutLimits_http)r&   r$   rS   r   r   r   r'   j   s   z TranscriptVariantClient.__init__c                   s   | j  I d H  d S r%   )rn   aclose)r&   r   r   r   closes   s   zTranscriptVariantClient.closer   r   r   r   c                   s  t  dt d| j }| |}d }t| jD ]}z| jj||dI d H }| ||}| 	|W   S  t
yy } z:|}| jt|t| jd  }	dd l}
|
td|d | jt|d d |	 dd l}||	I d H  W Y d }~qd }~w ty     tjy } z?t
d| }| jt|t| jd  }	dd l}
dd l}|
td	|d | jt|d d
 |	 ||	I d H  W Y d }~qd }~ww t
d| j d| )Nz/models/z:generateContent?key=rM   r   r   z8Gemini retryable error (attempt %s/%s): %s, retry in %ssr3   z	Timeout: z0Gemini timeout (attempt %s/%s): %s, retry in %ssrf   z
Exhausted z
 retries: )r9   r   r$   _build_request_bodyrangeMAX_RETRIESrn   rP   _handle_http_response_parse_responserV   RETRY_BACKOFFminlenlogging	getLoggerr   warningr   asynciosleeprZ   r:   TimeoutException)r&   r   r@   rR   
last_errorattemptrC   r   excwaitry   r|   r   r   r   generate_batchv   sD   



z&TranscriptVariantClient.generate_batchr   c                 C  s\   ddt |igdgdd| jdt idd}| jr"| j|d	< |S d
dt igi|d< |S )NuserrH   )rolerG   r   zapplication/jsonthinkingLevel)temperatureresponseMimeTyperesponseJsonSchemathinkingConfig)contentsgenerationConfigcachedContentrG   rJ   )r   rk   r   upperrS   r   )r&   r   rR   r   r   r   rq      s"   
z+TranscriptVariantClient._build_request_bodyrC   httpx.Responser   r)   c                 C  s   |j dkrtd|jd d  |j dkr&td|j  d|jd d  |j dkrD|jd d }d|v r=td	| td
| |j dkrYtd|j  d|jd d  | S )Ni  z429 rate limited: r3   rO   zHTTP z: i  RESOURCE_EXHAUSTEDzRESOURCE_EXHAUSTED: z
HTTP 400: )r=   rV   rH   rZ   r>   )r&   rC   r   	body_textr   r   r   rt      s   

 

 z-TranscriptVariantClient._handle_http_responser   c              
   C  s   | dg }|stdt| di d d  |d  dd}|dkr3td	|d  d
g  d}|d  di  dg D ]}d|v rM|d } nqA|sTtdzt|}W n tyn } ztd| d }~ww tdd |j	D t
||dS )N
candidateszNo candidates: promptFeedbackr3   r   finishReason SAFETYzSafety block: safetyRatingscontentrG   rH   zEmpty text in response partszSchema parse failed: c                 S  s   g | ]}|  qS r   )
model_dump).0itemr   r   r   
<listcomp>   s    z;TranscriptVariantClient._parse_response.<locals>.<listcomp>)r   r   r   )r<   rV   r>   dumpsrZ   r
   model_validate_json	Exceptionr   results_extract_token_usage)r&   r   r   finish_reasonresponse_textpartparsedr   r   r   r   ru      s6   z'TranscriptVariantClient._parse_responseN)r$   r   rS   ra   )r   r   r   r   )r   r   r   r   )rC   r   r   r)   r   r   )r   r   r   r   )r   r    r!   rs   rv   r'   rp   r   rq   rt   ru   r   r   r   r   r[   f   s    
	

"
r[   response_jsonr   r	   c                 C  sJ   |  di }| dd}| dd}| dd}t||||| |dkdS )NusageMetadatapromptTokenCountr   candidatesTokenCountcachedContentTokenCount)input_tokensoutput_tokenscached_tokenstotal_tokens	cache_hit)r<   r	   )r   usager   r   r   r   r   r   r      s   r   rowc                 C  s   t j| dddS )NFT)ensure_ascii	sort_keys)r>   r   )r   r   r   r   serialize_result_row   s   r   )r   r   )r   r   r   r	   )r   r   r   r   )rX   
__future__r   r   r>   dataclassesr   typingr   r   r:   configr   r   providers.baser	   transcript_variant_promptr
   r   r   r   r9   r   r   r#   r   rV   rZ   r[   r   r   r   r   r   r   <module>   s(    
1
w