
    )j                       d Z ddlmZ ddlZddlZddlZddlZddlmZ ddl	m
Z
mZmZmZmZ ddlZddlmZ ddlmZ ddlmZmZmZmZ  ej        e          Zd	d
d	d	d	dZdPdZdQdZdRdZdSdZ dTdZ!dUdZ"dVd Z#dddddddd!dWd)Z$dd*dXd/Z%dYd2Z&dZd3Z'd[d5Z( G d6 d7e          Z)d8ddd8d9d\d=Z*d]dAZ+d^dFZ,dGZ- G dH dI          Z. G dJ dK          Z/ G dL dM          Z0d_dOZ1dS )`ah  OpenAI-compatible facade that talks to Google's Cloud Code Assist backend.

This adapter lets Hermes use the ``google-gemini-cli`` provider as if it were
a standard OpenAI-shaped chat completion endpoint, while the underlying HTTP
traffic goes to ``cloudcode-pa.googleapis.com/v1internal:{generateContent,
streamGenerateContent}`` with a Bearer access token obtained via OAuth PKCE.

Architecture
------------
- ``GeminiCloudCodeClient`` exposes ``.chat.completions.create(**kwargs)``
  mirroring the subset of the OpenAI SDK that ``run_agent.py`` uses.
- Incoming OpenAI ``messages[]`` / ``tools[]`` / ``tool_choice`` are translated
  to Gemini's native ``contents[]`` / ``tools[].functionDeclarations`` /
  ``toolConfig`` / ``systemInstruction`` shape.
- The request body is wrapped ``{project, model, user_prompt_id, request}``
  per Code Assist API expectations.
- Responses (``candidates[].content.parts[]``) are converted back to
  OpenAI ``choices[0].message`` shape with ``content`` + ``tool_calls``.
- Streaming uses SSE (``?alt=sse``) and yields OpenAI-shaped delta chunks.

Attribution
-----------
Translation semantics follow jenslys/opencode-gemini-auth (MIT) and the public
Gemini API docs. Request envelope shape
(``{project, model, user_prompt_id, request}``) is documented nowhere; it is
reverse-engineered from the opencode-gemini-auth and clawdbot implementations.
    )annotationsN)SimpleNamespace)AnyDictIteratorListOptional)google_oauth)sanitize_gemini_tool_parameters)CODE_ASSIST_ENDPOINTCodeAssistErrorProjectContextresolve_project_contextusermodel)r   	assistantsystemtoolfunctioncontentr   returnstrc                |   | dS t          | t                    r| S t          | t                    rg }| D ]}t          |t                    r|                    |           -t          |t                    r|                    d          dk    rDt          |                    d          t                    r|                    |d                    |                    d          dv r.t                              d|                    d                     d                    |          S t          |           S )zCOpenAI content may be str or a list of parts; reduce to plain text.N typetext>   	image_urlinput_audioz0Dropping multimodal part (not yet supported): %s
)	
isinstancer   listappenddictgetloggerdebugjoin)r   piecesps      E/home/wildlama/.hermes/hermes-agent/agent/gemini_cloudcode_adapter.py_coerce_content_to_textr+   A   s'   r'3 '4   ! 	d 	dA!S!! da    At$$ d55==F**z!%%--/M/M*MM!F),,,,UU6]]&BBBLL!SUVUZUZ[aUbUbcccyy   w<<    	tool_callDict[str, Any]c                X   |                      d          pi }|                     dd          }	 t          |t                    r|rt          j        |          ni }n# t          j        $ r d|i}Y nw xY wt          |t                    sd|i}|                     d          pd|ddd	S )
z-OpenAI tool_call -> Gemini functionCall part.r   	argumentsr   _raw_valuename)r3   args skip_thought_signature_validator)functionCallthoughtSignature)r$   r    r   jsonloadsJSONDecodeErrorr#   )r-   fnargs_rawr4   s       r*   _translate_tool_call_to_geminir=   V   s    	z	"	"	(bBvvk2&&H"'1(C'@'@UXUtz(###SU " " "!"dD!!  $ FF6NN(b
 
 ?	 	 	s   -A A32A3messagec                   t          |                     d          p|                     d          pd          }t          |                     d                    }	 |                                                    d          rt          j        |          nd}n# t
          j        $ r d}Y nw xY wt          |t                    r|nd|i}d||d	iS )
aI  OpenAI tool-role message -> Gemini functionResponse part.

    The function name isn't in the OpenAI tool message directly; it must be
    passed via the assistant message that issued the call. For simplicity we
    look up ``name`` on the message (OpenAI SDK copies it there) or on the
    ``tool_call_id`` cross-reference.
    r3   tool_call_idr   r   ){[NoutputfunctionResponse)r3   response)
r   r$   r+   strip
startswithr8   r9   r:   r    r#   )r>   r3   r   parsedrE   s        r*    _translate_tool_result_to_geminirI   l   s     w{{6""Kgkk.&A&AKVLLD%gkk)&<&<==G(/(B(B:(N(NXG$$$TX   #FD11Jvv'7JH 
 
 s   =B B/.B/messagesList[Dict[str, Any]]5tuple[List[Dict[str, Any]], Optional[Dict[str, Any]]]c                   g }g }| D ]}t          |t                    st          |                    d          pd          }|dk    r6|                    t          |                    d                               y|dk    s|dk    r'|                    dt          |          gd           t                              |d          }g }t          |                    d                    }|r|                    d|i           |                    d	          pg }t          |t                    r<|D ]9}	t          |	t                    r"|                    t          |	                     :|sp|                    ||d           d
}
d
                    d |D                                                       }|rdd|igd}
||
fS )zCConvert OpenAI messages[] to Gemini contents[] + systemInstruction.roler   r   r   r   r   )rN   partsr   
tool_callsNr   c              3     K   | ]}||V  	d S N ).0r)   s     r*   	<genexpr>z)_build_gemini_contents.<locals>.<genexpr>   s'      @@Aa@a@@@@@@r,   )r    r#   r   r$   r"   r+   rI   _ROLE_MAP_OPENAI_TO_GEMINIr!   r=   r'   rF   )rJ   system_text_partscontentsmsgrN   gemini_rolerO   r   rP   tcsystem_instructionjoined_systems               r*   _build_gemini_contentsr^      s    $&%'H #? #?#t$$ 	3776??,f--8$$%<SWWY=O=O%P%PQQQ 6>>TZ//OO:3??@     044T6BB&(&swwy'9'9:: 	)LL&$((( WW\**0b
j$'' 	E  E Eb$'' ELL!?!C!CDDD 	u==>>>>37II@@):@@@@@FFHHM 
}-.
 

 '''r,   toolsc                (   t          | t                    r| sg S g }| D ]}t          |t                    s|                    d          pi }t          |t                    sE|                    d          }|s]dt	          |          i}|                    d          rt	          |d                   |d<   |                    d          }t          |t                    rt          |          |d<   |                    |           |sg S d|igS )z8OpenAI tools[] -> Gemini tools[].functionDeclarations[].r   r3   description
parametersfunctionDeclarations)r    r!   r#   r$   r   r   r"   )r_   declarationstr;   r3   declparamss          r*   _translate_tools_to_geminirh      s1   eT"" % 	)+L " "!T"" 	UU:$""d## 	vvf~~ 	D		"66-   	9"%b&7"8"8D%%fd## 	I!@!H!HDD!!!! 	#\233r,   tool_choiceOptional[Dict[str, Any]]c                0   | dS t          | t                    r$| dk    rdddiiS | dk    rdddiiS | dk    rddd	iiS t          | t                    rC|                     d
          pi }|                    d          }|rddt          |          gdiS dS )z>OpenAI tool_choice -> Gemini toolConfig.functionCallingConfig.NautofunctionCallingConfigmodeAUTOrequiredANYnoneNONEr   r3   )rn   allowedFunctionNames)r    r   r#   r$   )ri   r;   r3   s      r*    _translate_tool_choice_to_geminiru      s    t+s## ?&  +ff-=>>*$$+fe_==&  +ff-=>>+t$$ 	__Z((.Bvvf~~ 	'!-0YYK* *  4r,   configc                j   t          | t                    r| sdS |                     d|                     d                    }|                     d|                     d                    }|                     d|                     d                    }i }t          |t          t          f          rt          |          |d<   t          |t
                    r=|                                r)|                                                                |d<   t          |t                    r||d<   |pdS )zGAccept thinkingBudget / thinkingLevel / includeThoughts (+ snake_case).NthinkingBudgetthinking_budgetthinkingLevelthinking_levelincludeThoughtsinclude_thoughts)	r    r#   r$   intfloatr   rF   lowerbool)rv   budgetlevelinclude
normalizeds        r*   _normalize_thinking_configr      s   fd## 6 tZZ(&**5F*G*GHHFJJ

3C(D(DEEEjj*FJJ7I,J,JKKG!#J&3,'' 3'*6{{
#$% <%++-- <&+kkmm&9&9&;&;
?#'4   0(/
$%r,   )r_   ri   temperature
max_tokenstop_pstopthinking_configr   Optional[float]r   Optional[int]r   r   r   c                V   t          |           \  }}	d|i}
|	|	|
d<   t          |          }|r||
d<   t          |          }|||
d<   i }t          |t          t
          f          rt          |          |d<   t          |t                    r|dk    r||d<   t          |t          t
          f          rt          |          |d	<   t          |t                    r	|r|g|d
<   n&t          |t                    r|rd |D             |d
<   t          |          }|r||d<   |r||
d<   |
S )zFBuild the inner Gemini request body (goes inside ``request`` wrapper).rX   NsystemInstructionr_   
toolConfigr   r   maxOutputTokenstopPstopSequencesc                0    g | ]}|t          |          S rS   )r   )rT   ss     r*   
<listcomp>z(build_gemini_request.<locals>.<listcomp>"  s#    -H-H-Ha-Hc!ff-H-H-Hr,   thinkingConfiggenerationConfig)	r^   rh   ru   r    r~   r   r   r!   r   )rJ   r_   ri   r   r   r   r   r   rX   r\   bodygemini_toolstool_cfggeneration_confignormalized_thinkings                  r*   build_gemini_requestr      s    $:(#C#C H &1D%$6 !-e44L %$W/<<H%\(*+U|,, >+0+=+=-(*c"" :zA~~/9+,%#u&& 1$)%LL&!$ I I.2V/**	D$		 ID I-H-Hd-H-H-H/*4_EE B.A*+ 5#4 Kr,   )user_prompt_id
project_idinner_requestr   Optional[str]c                P    | ||pt          t          j                              |dS )z:Wrap the inner Gemini request in the Code Assist envelope.)projectr   r   request)r   uuiduuid4)r   r   r   r   s       r*   wrap_code_assist_requestr   ,  s2     (=C
,=,= 	  r,   respr   c                   t          |                     d          t                    r|                     d          n| }|                    d          pg }t          |t                    r|st	          |          S |d         }t          |t                    r|                    d          ni }t          |t                    r|                    d          ng }g }g }g }	t          |pg           D ]\  }
}t          |t                    s|                    d          du rDt          |                    d          t                    r|                    |d                    wt          |                    d          t                    r|                    |d                    |                    d	          }t          |t                    r|                    d
          r	 t          j	        |                    d          pi d          }n# t          t          f$ r d}Y nw xY w|	                    t          dt          j                    j        dd          d|
t          t          |d
                   |                               |	rdn0t!          t          |                    d          pd                    }|                    d          pi }t          t#          |                    d          pd          t#          |                    d          pd          t#          |                    d          pd          t          t#          |                    d          pd                              }t          d|rd                    |          nd|	pdd                    |          pdd                    |          pdd           }t          d||!          }t          d"t          j                    j        dd          d#t#          t'          j                              ||g|$          S )%zNon-streaming Gemini response -> OpenAI-shaped SimpleNamespace.

    Code Assist wraps the actual Gemini response inside ``response``, so we
    unwrap it first if present.
    rE   
candidatesr   r   rO   thoughtTr   r6   r3   r4   Fensure_ascii{}call_N   r   r3   r0   )idr   indexr   rP   finishReasonr   usageMetadatapromptTokenCountcandidatesTokenCounttotalTokenCountcachedContentTokenCountcached_tokensprompt_tokenscompletion_tokenstotal_tokensprompt_tokens_detailsr   rN   r   rP   	reasoningreasoning_contentreasoning_detailsr   r>   finish_reason	chatcmpl-chat.completionr   objectcreatedr   choicesusage)r    r$   r#   r!   _empty_response	enumerater   r"   r8   dumps	TypeError
ValueErrorr   r   r   hex_map_gemini_finish_reasonr~   r'   time)r   r   innerr   candcontent_objrO   text_piecesreasoning_piecesrP   ipartfcargs_strr   
usage_metar   r>   choices                      r*   _translate_gemini_responser   @  sI    %/txx
/C/CT$J$JTDHHZ   PTE<((.BJj$'' &z &u%%%a=D)3D$)?)?G$((9%%%RK(2;(E(EMKOOG$$$2EK"$(*JU[b))  4$%% 	 88I$&&$((6**C00 6 ''V555dhhv&&,, 	tF|,,,XXn%%b$ 
	BFF6NN 
	 :bffVnn&:OOOz*       o24:<<+CRC022(c"V*ooRRR	      %/ LL4MDHH^$$*++5 5M ?++1rJ*..);<<ABBjnn-CDDIJJ(9::?a@@-jnn-FGGL1MM
 
 
	  E (3=$$$%''*++3t''"233;t  G #  F
 .tz||',.. DIKK     s   /+HH10H1c                <   t          ddd d d d           }t          d|d          }t          dddt          d                    }t          d	t          j                    j        d d
          dt	          t          j                              | |g|          S )Nr   r   r   r   r   r   r   r   r   r   r   r   )r   r   r   r   r~   r   )r   r>   r   r   s       r*   r   r     s    "$$  G 1gVLLLF11-A>>>  E .tz||',.. DIKK     r,   reasonc                b    dddddd}|                     |                                 d          S )Nr   lengthcontent_filter)STOP
MAX_TOKENSSAFETY
RECITATIONOTHER)r$   upper)r   mappings     r*   r   r     s;    "& G ;;v||~~v...r,   c                      e Zd ZdZdS )_GeminiStreamChunkz<Mimics an OpenAI ChatCompletionChunk with .choices[0].delta.N)__name__
__module____qualname____doc__rS   r,   r*   r   r     s        FFDr,   r   r   )r   tool_call_deltar   r   r   r   r   c                V   dd d d d d}|r||d<   |t          |                    dd          |                    d          p"dt          j                    j        d d          d	t          |                    d
          pd|                    d          pd                    g|d<   |r
||d<   ||d<   t          di |}t          d||          }t          dt          j                    j        d d          dt          t          j                              | |gd           S )Nr   )rN   r   rP   r   r   r   r   r   r   r   r   r   r3   r   r0   r   )r   r   r   r   rP   r   r   )r   deltar   r   zchat.completion.chunkr   rS   )r   r$   r   r   r   r   r~   r   )r   r   r   r   r   delta_kwargsr   r   s           r*   _make_stream_chunkr     su    !$ $L  *")Y"&5!%%gq11""4((K,KDJLL4DSbS4I,K,K$$((006B)--k::@b  	'
 '
 '
 &\"  6$-[!,5()++l++E1EOOOF.tz||',..&DIKK     r,   rE   httpx.ResponseIterator[Dict[str, Any]]c              #    K   d}|                                  D ]}|s||z  }d|v r|                    dd          \  }}|                    d          }|s5|                    d          rc|dd         }|dk    r dS 	 t	          j        |          V  n8# t          j        $ r& t                              d	|dd
                    Y nw xY wd|v dS )z:Parse Server-Sent Events from an httpx streaming response.r   r      zdata:    Nz[DONE]zNon-JSON SSE line: %s   )		iter_textsplitrstriprG   r8   r9   r:   r%   r&   )rE   bufferchunklinedatas        r*   _iter_sse_eventsr    s(     F##%% F F 	%fnn!<<a00LD&;;t$$D x(( FABBx8##FFF*T******+ F F FLL!8$tt*EEEEEF fnn	F Fs   B2CCeventtool_call_counter	List[int]List[_GeminiStreamChunk]c           
        t          |                     d          t                    r|                     d          n| }|                    d          pg }|sg S |d         }t          |t                    sg S g }|                    d          pi }t          |t                    r|                    d          ng }|pg D ]}	t          |	t                    s|	                    d          du rSt          |	                    d          t                    r+|                    t          ||	d         	                     t          |	                    d          t                    r2|	d         r*|                    t          ||	d         
                     |	                    d          }
t          |
t                    r|
                    d          rt          |
d                   }|d         }|dxx         dz  cc<   	 t          j        |
                    d          pi d          }n# t          t          f$ r d}Y nw xY w|                    t          ||||d                     t          |                    d          pd          }|rAt          |          }|d         dk    rd}|                    t          ||                     |S )u  Unwrap Code Assist envelope and emit OpenAI-shaped chunk(s).

    ``tool_call_counter`` is a single-element list used as a mutable counter
    across events in the same stream. Each ``functionCall`` part gets a
    fresh, unique OpenAI ``index`` — keying by function name would collide
    whenever the model issues parallel calls to the same tool (e.g. reading
    three files in one turn).
    rE   r   r   r   rO   r   Tr   )r   r   )r   r   r6   r3   r   r4   Fr   r   )r   r3   r0   )r   r   r   r   rP   )r   r   )r    r$   r#   r   r"   r   r8   r   r   r   r   )r	  r   r
  r   r   r   chunksr   rO   r   r   r3   idxr   finish_reason_rawmappeds                   r*   _translate_stream_eventr    s    &0		*0E0Et%L%LWEIIj!!!RWE<((.BJ 	a=DdD!! 	')Fhhy!!'RG$.w$=$=EGKK   2E  $%% 	88I$&&:dhhv6F6F+L+L&MM,tF|      dhhv&&,, 	Qf 	QMM,5$v,OOOPPPXXn%%b$ 	BFF6NN 	r&z??D#A&Ca   A%    :bffVnn&:OOOz*       MM,  !)! !      DHH^44:;; M*+<==Q!##!F(uFKKKLLLMs   +H99IIzcloudcode-pa://googlec                      e Zd Zd	dZd
dZdS )_GeminiChatCompletionsclient'GeminiCloudCodeClient'c                    || _         d S rR   )_clientselfr  s     r*   __init__z_GeminiChatCompletions.__init__C  s    r,   kwargsr   r   c                &     | j         j        di |S )NrS   )r  _create_chat_completion)r  r  s     r*   createz_GeminiChatCompletions.createF  s    3t|3==f===r,   Nr  r  )r  r   r   r   )r   r   r   r  r  rS   r,   r*   r  r  B  s<           > > > > > >r,   r  c                      e Zd ZddZdS )_GeminiChatNamespacer  r  c                .    t          |          | _        d S rR   )r  completionsr  s     r*   r  z_GeminiChatNamespace.__init__K  s    1&99r,   Nr   )r   r   r   r  rS   r,   r*   r"  r"  J  s(        : : : : : :r,   r"  c                  l    e Zd ZdZdddddd1dZd2dZd Zd Zd3dZddddddddddddd4d*Z	d5d0Z
dS )6GeminiCloudCodeClientzAMinimal OpenAI-SDK-compatible facade over Code Assist v1internal.Nr   )api_keybase_urldefault_headersr   r'  r   r(  r)  Optional[Dict[str, str]]r   r   _r   c                   |pd| _         |pt          | _        t          |pi           | _        || _        d | _        d| _        t          |           | _	        d| _
        t          j        t          j        dddd                    | _        d S )Nzgoogle-oauthFg      .@g     @g      >@)connectreadwritepool)timeout)r'  MARKER_BASE_URLr(  r#   _default_headers_configured_project_id_project_context_project_context_lockr"  chat	is_closedhttpxClientTimeout_http)r  r'  r(  r)  r   r+  s         r*   r  zGeminiCloudCodeClient.__init__R  s     0. 3O $_%: ; ;&0#:>%*"(..	\%-5X\cg*h*h*hiii


r,   r   Nonec                j    d| _         	 | j                                         d S # t          $ r Y d S w xY w)NT)r8  r<  close	Exceptionr  s    r*   r?  zGeminiCloudCodeClient.closeh  sJ    	J 	 	 	DD	s   $ 
22c                    | S rR   rS   rA  s    r*   	__enter__zGeminiCloudCodeClient.__enter__p  s    r,   c                .    |                                   d S rR   )r?  )r  exc_typeexc_valexc_tbs       r*   __exit__zGeminiCloudCodeClient.__exit__s  s    

r,   access_tokenr   r   c                r   | j         | j         S t          j                    }t          j                    }|r|j        nd}|r(t          ||r|j        nddd          | _         | j         S t          || j        ||          }|j        s|j        r t          j	        |j        |j                   || _         |S )z=Lazily resolve and cache the project context for this client.Nr   stored)r   managed_project_idtier_idsource)configured_project_idenv_project_iduser_agent_model)r   rL  )
r5  r
   resolve_project_id_from_envload_credentialsr   r   rL  r   r4  update_project_ids)r  rI  r   env_projectcredsstored_projectctxs          r*   _ensure_project_contextz-GeminiCloudCodeClient._ensure_project_contextv  s     ,((">@@-//-2:))  	)$2)?D#L5#;#;"	% % %D! ((%"&"=&"	
 
 
 > 	S3 	+>#&#9    !$
r,   zgemini-2.5-flashF)r   rJ   streamr_   ri   r   r   r   r   
extra_bodyr1  rJ   Optional[List[Dict[str, Any]]]rZ  r   r_   ri   r   r   r   r   r   r   r[  rj   r1  c          
        t          j                    }|                     ||          }d }t          |
t                    r*|
                    d          p|
                    d          }t          |pg ||||||	|          }t          |j        ||          }ddd| ddt          t          j                              d	}|                    | j                   |r|                     |||
          S t           d}| j                            |||          }|j        dk    rt'          |          	 |                                }n'# t*          $ r}t-          d| d          |d }~ww xY wt/          ||          S )Nr   r   )rJ   r_   ri   r   r   r   r   r   )r   r   r   zapplication/jsonzBearer z hermes-agent (gemini-cli-compat)zgl-python/hermes)zContent-TypeAcceptAuthorizationz
User-AgentzX-Goog-Api-Clientzx-activity-request-id)r   wrappedheadersz/v1internal:generateContentr8   ra  r   zInvalid JSON from Code Assist: code_assist_invalid_jsoncode)r   )r
   get_valid_access_tokenrY  r    r#   r$   r   r   r   r   r   r   updater3  _stream_completionr   r<  poststatus_code_gemini_http_errorr8   r   r   r   )r  r   rJ   rZ  r_   ri   r   r   r   r   r[  r1  r+  rI  rX  r   r   r`  ra  urlrE   payloadexcs                          r*   r  z-GeminiCloudCodeClient._create_chat_completion  s     $:<<**<??j$'' 	d(nn->??c:>>RbCcCcO$^##!+	
 	
 	
 +~
 
 
 /(5|55<!3%(%6%6
 
 	t,--- 	Z**QX*YYY%BBB:??3Wg?FF3&&$X...	mmooGG 	 	 	!7#77/   	
 *'????s   ;E 
E4E//E4r`  r.   ra  Dict[str, str]Iterator[_GeminiStreamChunk]c               p     t            dt          |          dd<   d fd} |            S )z5Generator that yields OpenAI-shaped streaming chunks.z)/v1internal:streamGenerateContent?alt=sseztext/event-streamr^  r   rp  c               3    K   	 j                             d	          5 } | j        dk    r#|                                  t	          |           dg}t          |           D ]}t          ||          D ]}|V  	 d d d            d S # 1 swxY w Y   d S # t          j        $ r}t          d| d          |d }~ww xY w)NPOSTrb  r   r   zStreaming request failed: code_assist_stream_errorrd  )
r<  rZ  rj  r.  rk  r  r  r9  	HTTPErrorr   )
rE   r
  r	  r  rn  r   r  stream_headersrl  r`  s
        r*   
_generatorz<GeminiCloudCodeClient._stream_completion.<locals>._generator  sS     Z&&vs.&YY (]e+s22 0:::453%!1(!;!; ( (%<UEK\%]%] ( (E"'KKKK((( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ( ?   %6663   s;   B AB B BB BB C)B>>C)r   rp  )r   r#   )r  r   r`  ra  rw  rv  rl  s   ```  @@r*   rh  z(GeminiCloudCodeClient._stream_completion  sl     &PPPg#6x 	 	 	 	 	 	 	 	 	 	" z||r,   )
r'  r   r(  r   r)  r*  r   r   r+  r   )r   r=  )rI  r   r   r   r   r   )r   r   rJ   r\  rZ  r   r_   r   ri   r   r   r   r   r   r   r   r   r   r[  rj   r1  r   r+  r   r   r   )r   r   r`  r.   ra  ro  r   rp  )r   r   r   r   r  r?  rC  rH  rY  r  rh  rS   r,   r*   r&  r&  O  s        KK
 "&"&48j j j j j j,       ! ! ! !L (37'+$(!%/3?@ ?@ ?@ ?@ ?@ ?@B     r,   r&  r   c                p   | j         }d}i }	 | j        }n# t          $ r d}Y nw xY w|rF	 t          j        |          }t          |t                    r|}n# t          t          f$ r i }Y nw xY wt          |t                    r|	                    d          nd}t          |t                    si }t          |	                    d          pd                                          }t          |	                    d          pd                                          }|	                    d          }t          |t                    r|ng }	d}
i }d}|	D ][}t          |t                    st          |	                    d          pd          }|
sp|                    d          r[|	                    d	          }t          |t                    r|r|}
|	                    d
          }t          |t                    r|}||                    d          r|	                    d          }t          |t                    r@|                    d          r+	 t          |dd                   }# t          $ r Y ,w xY wt          |t          t          f          rt          |          }]|t	 | j        	                    d          p| j        	                    d          }n# t          $ r d}Y nw xY w|r*	 t          |          }n# t          t          f$ r d}Y nw xY wd| }|dk    rd}n|dk    r
d}|
dk    rd}d}t          |t                    rKt          |	                    d          p|	                    d          pd                                          }|dk    r|
dk    r|pd}d| d}|
|d|ddz  }nT|dk    r|d k    rd!|pd  d"}|
|d#|ddz  }n3|d$k    r|p|pd}d%| d&}n |rd'| d(|pd d)| }nd*| d+|dd,          }t#          |||| |||
||d-.          S )/u<  Translate an httpx response into a CodeAssistError with rich metadata.

    Parses Google's error envelope (``{"error": {"code", "message", "status",
    "details": [...]}}``) so the agent's error classifier can reason about
    the failure — ``status_code`` enables the rate_limit / auth classification
    paths, and ``response`` lets the main loop honor ``Retry-After`` just
    like it does for OpenAI SDK exceptions.

    Also lifts a few recognizable Google conditions into human-readable
    messages so the user sees something better than a 500-char JSON dump:

        MODEL_CAPACITY_EXHAUSTED → "Gemini model capacity exhausted for
            <model>. This is a Google-side throttle..."
        RESOURCE_EXHAUSTED w/o reason → quota-style message
        404 → "Model <name> not found at cloudcode-pa..."
    r   errorNstatusr>   detailsz@typez/google.rpc.ErrorInfor   metadataz/google.rpc.RetryInfo
retryDelayr   zRetry-Afterzretry-aftercode_assist_http_i  code_assist_unauthorizedi  code_assist_rate_limitedMODEL_CAPACITY_EXHAUSTEDcode_assist_capacity_exhaustedr   modelIdzthis Gemini modelzGemini capacity exhausted for z (Google-side throttle, not a Hermes issue). Try a different Gemini model or set a fallback_providers entry to a non-Gemini provider.z Google suggests retrying in gzs.RESOURCE_EXHAUSTEDzGemini quota exhausted (z.). Check /gquota for remaining daily requests.z Retry suggested in i  zCode Assist 404: z is not available at cloudcode-pa.googleapis.com. It may have been renamed or retired. Check hermes_cli/models.py for the current list.zCode Assist HTTP z (z): zCode Assist returned HTTP z: i  )rz  r   r|  r>   )re  rj  rE   retry_afterr{  )rj  r   r@  r8   r9   r    r#   r   r   r$   r   rF   r!   endswithr   r~   ra  r   )rE   rz  	body_text	body_jsonrH   err_obj
err_statuserr_message_raw_detailserr_details_listerror_reasonerror_metadataretry_delay_secondsdetailtype_urlr   md	delay_raw
header_valre  
model_hinttargetr>   s                          r*   rk  rk    s   " !F I "IM		   			 	Z	**F&$'' #"	I& 	 	 	III	 )39d(C(CMimmG$$$Ggt$$ W[[**0b117799Jgkk),,23399;;K;;y))L'1,'E'EM||2 L%'N+/" 7 7&$'' 	vzz'**0b11 	7 1 12I J J 	7ZZ))F&#&& &6 &%J''B"d## $!# (X->->?V-W-W(

<00I)S)) 7i.@.@.E.E 7*/	#2#*?*?''!   DIU|44 7&+I&6&6# "	!)--m<<c@P@T@TUb@c@cJJ 	 	 	JJJ	 	++&+J&7&7##z* + + +&*###+ (v''D}})	3)5553D
 J.$'' e++G44[8J8J98U8U[Y[\\bbdd
}})CCC22BV B B B 	
 *P7JPPPPPG	3:)===;{'J6J ; ; ; 	 *G.AGGGGGG	37 6wI I I I 	
 
 KWfWW
0EgWW+WW KvJJ4C4JJ' "&"	
 
   sS    $$+A A,+A,I33
J J44K) )K87K8>L L$#L$)r   r   r   r   )r-   r.   r   r.   )r>   r.   r   r.   )rJ   rK   r   rL   )r_   r   r   rK   )ri   r   r   rj   )rv   r   r   rj   )rJ   rK   r_   r   ri   r   r   r   r   r   r   r   r   r   r   r   r   r.   )
r   r   r   r   r   r.   r   r   r   r.   )r   r.   r   r   r   r   )r   r   r   r   )r   r   r   r   )r   r   r   r   r   rj   r   r   r   r   r   r   )rE   r   r   r   )r	  r.   r   r   r
  r  r   r  )rE   r   r   r   )2r   
__future__r   r8   loggingr   r   typesr   typingr   r   r   r   r	   r9  agentr
   agent.gemini_schemar   agent.google_code_assistr   r   r   r   	getLoggerr   r%   rV   r+   r=   rI   r^   rh   ru   r   r   r   r   r   r   r   r   r  r  r2  r  r"  r&  rk  rS   r,   r*   <module>r     su   8 # " " " " "     ! ! ! ! ! ! 6 6 6 6 6 6 6 6 6 6 6 6 6 6        ? ? ? ? ? ?            
	8	$	$      *   ,   24( 4( 4( 4(n4 4 4 44   0   ( #' $!* * * * * *d %)     (R R R Rj   (/ / / /	 	 	 	 	 	 	 	 04#'' ' ' ' ' 'TF F F F,; ; ; ;D *> > > > > > > >: : : : : : : :
h h h h h h h hVS S S S S Sr,   