
    iY                         d Z ddlZddlmZmZmZmZ ddlmZ ddl	m
Z
mZ ddlmZ ddlmZ ddlmZmZmZ d	ed
edz  dedz  fdZdedz  dedz  fdZdedefdZ G d de          ZddlmZ  ede           dS )u  OpenAI Chat Completions transport.

Handles the default api_mode ('chat_completions') used by ~16 OpenAI-compatible
providers (OpenRouter, Nous, NVIDIA, Qwen, Ollama, DeepSeek, xAI, Kimi, etc.).

Messages and tools are already in OpenAI format — convert_messages and
convert_tools are near-identity.  The complexity lives in build_kwargs
which has provider-specific conditionals for max_tokens defaults,
reasoning configuration, temperature handling, and extra_body assembly.
    N)AnyDictListOptional)resolve_lmstudio_effort)is_moonshot_modelsanitize_moonshot_tools)DEVELOPER_ROLE_MODELS)ProviderTransport)NormalizedResponseToolCallUsagemodelreasoning_configreturnc                    |t          |t                    sdS | pd                                                                }|                    d          r|                    dd          d         }|                    d          sdS |                    d          du rd	diS t          |                    d
d          pd                                                                          }|dk    rd	diS d	di}|                    d          r|S |dvrd}|                    d          r-d|v r|dv rd|d<   n|dv rd|d<   nd|d<   nd|v r|dv rdnd|d<   |S )zLTranslate Hermes/OpenRouter-style reasoning config to Gemini thinkingConfig.N zgoogle//   geminienabledFincludeThoughtseffortmediumnoneTzgemini-2.5->   lowhighxhighr   minimal)zgemini-3z
gemini-3.1flash>   r   r   r   thinkingLevel>   r   r   r   pro)
isinstancedictstriplower
startswithsplitgetstr)r   r   normalized_modelr   thinking_configs        F/home/ubuntu/.hermes/hermes-agent/agent/transports/chat_completions.py_build_gemini_thinking_configr.      s   z2BD'I'It**,,2244""9-- =+11#q99!< &&x00 tI&&%// "5))!%%h99EXFFLLNNTTVVF!5))'8$&?O
 ""=11 BBB
 ""#=>> &&&+++3800,,,39003;00&&& $5555 O,     configc                    t          | t                    r| sdS i }t          |                     d          t                    r| d         |d<   t          |                     d          t                    rI| d                                         r/| d                                                                         |d<   t          |                     d          t          t          f          rt          | d                   |d<   |pdS )zEConvert Gemini thinking config keys to the OpenAI-compat field names.Nr   include_thoughtsr!   thinking_levelthinkingBudgetthinking_budget)	r#   r$   r)   boolr*   r%   r&   intfloat)r0   
translateds     r-   "_snake_case_gemini_thinking_configr:   N   s    fd## 6 t!#J&**.//66 C)/0A)B
%&&**_--s33 O8O8U8U8W8W O'-o'>'D'D'F'F'L'L'N'N
#$&**-..e== F(+F3C,D(E(E
$%r/   base_urlc                     t          | pd                                                              d                                          }|sdS d|vrdS |                    d          S )Nr   r   Fz!generativelanguage.googleapis.comz/openai)r*   r%   rstripr&   endswith)r;   
normalizeds     r-   !_is_gemini_openai_compat_base_urlr@   ]   sj    X^$$**,,33C88>>@@J u**<<uy)))r/   c                   z   e Zd ZdZedefd            Zdeeee	f                  deeee	f                  fdZ
deeee	f                  deeee	f                  fdZ	 dd	edeeee	f                  deeeee	f                           deee	f         fd
Zde	defdZde	defdZde	deeeef                  fdZdS )ChatCompletionsTransportzfTransport for api_mode='chat_completions'.

    The default path for OpenAI-compatible providers.
    r   c                     dS )Nchat_completions )selfs    r-   api_modez!ChatCompletionsTransport.api_model   s    !!r/   messagesc                    d}|D ]x}t          |t                    sd|v sd|v rd} nU|                    d          }t          |t                    r*|D ]#}t          |t                    rd|v sd|v rd} n$|r ny|s|S t	          j        |          }|D ]}t          |t                    s|                    dd           |                    dd           |                    d          }t          |t                    rF|D ]C}t          |t                    r,|                    dd           |                    dd           D|S )	u9  Messages are already in OpenAI format — sanitize Codex leaks only.

        Strips Codex Responses API fields (``codex_reasoning_items`` /
        ``codex_message_items`` on the message, ``call_id``/``response_item_id``
        on tool_calls) that strict chat-completions providers reject with 400/422.
        Fcodex_reasoning_itemscodex_message_itemsT
tool_callscall_idresponse_item_idN)r#   r$   r)   listcopydeepcopypop)rF   rH   kwargsneeds_sanitizemsgrL   tc	sanitizeds           r-   convert_messagesz)ChatCompletionsTransport.convert_messagesp   s     	 	Cc4(( &#--1F#1M1M!%..J*d++ $  B!"d++ bDVZ\D\D\)-! E 	OM(++	 
	9 
	9Cc4(( GG+T222GG)4000..J*d++ 9$ 9 9B!"d++ 9y$///14888r/   toolsc                     |S )u0   Tools are already in OpenAI format — identity.rE   )rF   rY   s     r-   convert_toolsz&ChatCompletionsTransport.convert_tools   s    r/   Nr   c                 6  0 |                      |          }|                    dd          }|rW|                    d          }|                    d          }||u r| ||          }n| ||           n| ||          }|                    d|pd                                          0|rzt          |d         t                    r_|d                             d	          d
k    r@t          0fdt          D                       r t          |          }i |d         d	di|d<   ||d}	|                    d          }
|
|
|	d<   |                    d          }|                    dd          }|r|	                    dd           n|||	d<   |                    d          }|r|r||	d<   |r#t          |          rt          |          }||	d<   |                    d          }|                    d          }|                    d          }|                    d          }|                    dd          }|                    dd          }|                    dd          }|                    d          }|!|r|	                     ||                     n|!|r|	                     ||                     np|r!|r|	                     |d                     nM|r!|r|	                     |d                     n*|r!|r|	                     |d                     n|||	d<   |rt          |o+t          |t                    o|                    d           du           }|s_d!}|rVt          |t                    rA|                    d"          pd                                                                }|d#v r|}||	d$<   |rt          |o+t          |t                    o|                    d           du           }|s_d%}|rVt          |t                    rA|                    d"          pd                                                                }|d#v r|}||	d$<   |                    d&d          r@|                    d'd          r*t          ||                    d(                    }|||	d$<   i }|                    d)d          }|                    d*d          }|                    d+d          }t          |                    d,          pd                                                                          } |                    d-          }!|                    d.          }"|"r|r|"|d/<   |r=d0}#|r.t          |t                    r|                    d           du rd}#d1|#rd nd2i|d3<   |                    d'd          rn|                    d&d          sX|r|                    d4          }$|$|$|d5<   n9|/t	          |          }%|r|%                    d           du rn|%|d5<   nd0d!d6|d5<   |rd7g|d8<   |                    d9          }&|&r |                    d:i           }'|&|'d;<   |'|d:<   |                    d<d          rw|rut          |t                    r`|                    d"          pd                                                                }(|                    d d0          })|(d=k    s|)du rd|d><   |rd0|d?<   | d@k    rtt!          ||          }*t#          |!          rMt%          |*          }+|+r;|                    dAi           },|,                    dBi           }-|+|-dC<   |-|,dB<   |,|dA<   n%|*r|*|dC<   n| dDk    rt!          ||          }+|+r|+|dC<   |                    dE          }.|.r|                    |.           |r||	dA<   |                    dF          }/|/r|	                    |/           |	S )Gu  Build chat.completions.create() kwargs.

        This is the most complex transport method — it handles ~16 providers
        via params rather than subclasses.

        params:
            timeout: float — API call timeout
            max_tokens: int | None — user-configured max tokens
            ephemeral_max_output_tokens: int | None — one-shot override (error recovery)
            max_tokens_param_fn: callable — returns {max_tokens: N} or {max_completion_tokens: N}
            reasoning_config: dict | None
            request_overrides: dict | None
            session_id: str | None
            qwen_session_metadata: dict | None — {sessionId, promptId} precomputed
            model_lower: str — lowercase model name for pattern matching
            # Provider detection flags (all optional, default False)
            is_openrouter: bool
            is_nous: bool
            is_qwen_portal: bool
            is_github_models: bool
            is_nvidia_nim: bool
            is_kimi: bool
            is_lmstudio: bool
            is_custom_provider: bool
            ollama_num_ctx: int | None
            # Provider routing
            provider_preferences: dict | None
            # Qwen-specific
            qwen_prepare_fn: callable | None — runs AFTER codex sanitization
            qwen_prepare_inplace_fn: callable | None — in-place variant for deepcopied lists
            # Temperature
            fixed_temperature: Any — from _fixed_temperature_for_model()
            omit_temperature: bool
            # Reasoning
            supports_reasoning: bool
            github_reasoning_extra: dict | None
            lmstudio_reasoning_options: list[str] | None  # raw allowed_options from /api/v1/models
            # Claude on OpenRouter/Nous max output
            anthropic_max_output: int | None
            # Extra
            extra_body_additions: dict | None — pre-built extra_body entries
        is_qwen_portalFqwen_prepare_fnqwen_prepare_inplace_fnNmodel_lowerr   r   rolesystemc              3       K   | ]}|v V  	d S NrE   ).0pr`   s     r-   	<genexpr>z8ChatCompletionsTransport.build_kwargs.<locals>.<genexpr>   s(      DDA$DDDDDDr/   	developer)r   rH   timeoutfixed_temperatureomit_temperaturetemperatureqwen_session_metadatametadatarY   max_tokens_param_fnephemeral_max_output_tokens
max_tokensanthropic_max_outputis_nvidia_nimis_kimiis_tokenhubr   i @  i   i }  r   r   r   )r   r   r   reasoning_effortr   is_lmstudiosupports_reasoninglmstudio_reasoning_optionsis_openrouteris_nousis_github_modelsprovider_namer;   provider_preferencesproviderTtypedisabledthinkinggithub_reasoning_extra	reasoning)r   r   zproduct=hermes-agenttagsollama_num_ctxoptionsnum_ctxis_custom_providerr   thinkvl_high_resolution_imagesr   
extra_bodygoogler,   zgoogle-gemini-cliextra_body_additionsrequest_overrides)rX   r)   r&   r#   r$   anyr
   rO   rR   r   r	   updater6   r%   r   r*   r.   r@   r:   )1rF   r   rH   rY   paramsrW   is_qwen	qwen_prepqwen_prep_inplace
api_kwargsri   
fixed_temp	omit_temp	qwen_metamax_tokens_fn	ephemeralrq   anthropic_max_outrs   rt   ru   r   _kimi_thinking_off_kimi_effort_e_tokenhub_thinking_off_tokenhub_effort
_lm_effortr   rz   r{   r|   r}   r;   provider_prefs_kimi_thinking_enabledgh_reasoningrc
ollama_ctxr   _effort_enabledraw_thinking_configr,   openai_compat_extragoogle_extra	additions	overridesr`   s1                                                   @r-   build_kwargsz%ChatCompletionsTransport.build_kwargs   s	   d ))(33	
 **-u55 	5

#455I &

+D E EH$$( )	) 4 4I %0%%i0000* )	) 4 4I jj"0C0C0E0EFF	A9Q<..	A !  ((H44DDDD.CDDDDD 5 YI@il@FK@@IaL !&
 &


 **Y''$+Jy! ZZ 344
JJ1599	 	3NN=$////#(2J}% JJ677	 	/ 	/%.Jz"  	( !'' 7/66"'Jw 

#899JJ<==	ZZ--
"JJ'=>>

?E::**Y..jj66!::&899 ] mmI667777##mmJ778888 	9} 	9mmE223333 	9 	9mmE223333 	9 	9mmE223333*'8J|$  	>!%  =/66=$((33u<" "
 & >'# *
3CT(J(J **..x88>BEEGGMMOOB666')1=
-.  	B%)  =/66=$((33u<& &"
 * B#) # .
3CT(J(J .*..x88>BEEGGMMOOB666+-(1A
-. ::mU++ 	<

;OQV0W0W 	<0 

788 J %1;
-. &(


?E::**Y..!::&8%@@FJJ77=2>>DDFFLLNN::j))$:;; 	4m 	4%3Jz"  	%)" 3J/?$F$F 3#''	22e;;-2*%;K		&Jz" ::*E22 	T6::mUZ;[;[ 	T T%zz*BCC+.:J{+#/.//B 5266)#4#4#=#=24
;//:>(.S.SJ{+ 	:"8!9Jv ZZ 011
 	, nnY33G!+GI$+Jy! ::*E22 	0 0J/?$F$F 0+//99?RFFHHNNPP+//	4@@f$$E(9(9*/Jw' 	;6:J23H$$"?GW"X"X0:: 	D"DEX"Y"Y" C*4..r*J*J'#6#:#:8R#H#HL6EL!234@'1/BJ|,$ D0C
,-111;ECSTTO @0?
,- JJ566	 	)i((( 	2'1J|$ JJ233	 	)i(((r/   responsec           	         |j         d         }|j        }|j        pd}d}|j        rg }|j        D ]}i }t	          |dd          }	|	,t          |d          r|j        pi                     d          }	|	;t          |	d          r&	 |	                                }	n# t          $ r Y nw xY w|	|d<   |
                    t          |j        |j        j        |j        j        |pd                     d}
t          |d          rS|j        rL|j        }t#          t	          |d	d          pdt	          |d
d          pdt	          |dd          pd          }
t	          |dd          }t	          |dd          }|Dt          |d          r4t	          |dd          pi }t%          |t&                    rd|v r|d         }i }|||d<   t	          |dd          }|r||d<   t)          |j        ||||
|pd          S )u  Normalize OpenAI ChatCompletion to NormalizedResponse.

        For chat_completions, this is near-identity — the response is already
        in OpenAI format.  extra_content on tool_calls (Gemini thought_signature)
        is preserved via ToolCall.provider_data.  reasoning_details (OpenRouter
        unified format) and reasoning_content (DeepSeek/Moonshot) are also
        preserved for downstream replay.
        r   stopNextra_contentmodel_extra
model_dump)idname	argumentsprovider_datausageprompt_tokenscompletion_tokenstotal_tokens)r   r   r   r   reasoning_contentreasoning_details)contentrL   finish_reasonr   r   r   )choicesmessager   rL   getattrhasattrr   r)   r   	Exceptionappendr   r   functionr   r   r   r   r#   r$   r   r   )rF   r   rS   choicerU   r   rL   rV   tc_provider_dataextrar   ur   r   r   r   rds                    r-   normalize_responsez+ChatCompletionsTransport.normalize_response  s    !!$n,6
> 	Jn  
 46 OT::=WR%?%?=^1r66GGE$ul33 !!$)$4$4$6$6EE( ! ! ! D!8=$_5!!(u) k3"2":d	# # #     8W%% 	(. 	A%a!<<A")!-@!"D"D"I$Q::?a  E Cd33	#C)<dCC$m)D)D$!#}d;;ArK+t,, E1D1S1S$/0C$D!(*(1BM-.S-t44 	413M-.!K!''/4
 
 
 	
s   B
B('B(c                 R    |dS t          |d          r|j        dS |j        sdS dS )z&Check that response has valid choices.NFr   T)r   r   )rF   r   s     r-   validate_responsez*ChatCompletionsTransport.validate_response  sA    5x++ 	x/?/G5 	5tr/   c                     t          |dd          }|dS t          |dd          }|dS t          |dd          pd}t          |dd          pd}|s|r||dS dS )zAExtract OpenRouter/OpenAI cache stats from prompt_tokens_details.r   Nprompt_tokens_detailscached_tokensr   cache_write_tokens)r   creation_tokens)r   )rF   r   r   detailscachedwrittens         r-   extract_cache_statsz,ChatCompletionsTransport.extract_cache_stats  s    '400=4%!8$???4/155:'#7;;@q 	IW 	I%+HHHtr/   rd   )__name__
__module____qualname____doc__propertyr*   rG   r   r   r   rX   r[   r   r   r   r   r6   r   r7   r   rE   r/   r-   rB   rB   f   s        
 "# " " " X"&d38n)= &DQUVY[^V^Q_L` & & & &P4S#X#7 Dc3h<P     15	L LL tCH~&L T#s(^,-	L 
c3hL L L L\I
3 I
=O I
 I
 I
 I
V# $    C HT#s(^4L      r/   rB   )register_transportrD   )r   rP   typingr   r   r   r   agent.lmstudio_reasoningr   agent.moonshot_schemar   r	   agent.prompt_builderr
   agent.transports.baser   agent.transports.typesr   r   r   r*   r$   r.   r:   r6   r@   rB   agent.transportsr   rE   r/   r-   <module>r      s  	 	  , , , , , , , , , , , , < < < < < < L L L L L L L L 6 6 6 6 6 6 3 3 3 3 3 3 F F F F F F F F F F5 5t 5PTW[P[ 5 5 5 5ptd{ td{    * * * * * *e e e e e0 e e eR 0 / / / / /  %'? @ @ @ @ @r/   