
    ic                         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/piyush/.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                   t   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	f                  dz  deee	f         fd
Zd Zde	defdZde	defdZde	deeef         dz  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++ !R+=+C+C)-! 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                   & |                      |          }|                    d          }|r|                     |||||          S |                    d|pd                                          &|rzt	          |d         t
                    r_|d                             d          dk    r@t          &fdt          D                       r t          |          }i |d         ddi|d<   ||d	}|                    d
          }|||d
<   |r#t          |          rt          |          }||d<   |                    d          }	|                    d          }
|                    d          }|                    d          }|                    dd          }|                    dd          }|                    dd          }|                    d          }|
!|	r|                     |	|
                     n*|!|	r|                     |	|                     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=d&}|r.t	          |t
                    r|                    d          du rd}d'|rdnd(i|d)<   |                    dd          r=|                    dd          s'|r|                    d*          }|||d+<   nd&dd,|d+<   |d-k    rtt!          ||          } t#          |          rMt%          |           }!|!r;|                    d.i           }"|"                    d/i           }#|!|#d0<   |#|"d/<   |"|d.<   n%| r| |d0<   n|d1k    rt!          ||          }!|!r|!|d0<   |                    d2          }$|$r|                    |$           |r||d.<   |                    d3          }%|%r|                    |%           |S )4uX  Build chat.completions.create() kwargs.

        params (all optional):
            timeout: float — API call timeout
            max_tokens: int | None — user-configured max tokens
            ephemeral_max_output_tokens: int | None — one-shot override
            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
            model_lower: str — lowercase model name for pattern matching
            # Provider profile path (all per-provider quirks live in providers/)
            provider_profile: ProviderProfile | None — when present, delegates to
                _build_kwargs_from_profile(); all flag params below are bypassed.
            # Legacy-path flags — only used when provider_profile is None
            # (i.e. custom / unregistered providers). Known providers all go
            # through provider_profile.
            is_openrouter: bool
            is_nous: bool
            is_qwen_portal: bool
            is_github_models: bool
            is_nvidia_nim: bool
            is_kimi: bool
            is_tokenhub: 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
            qwen_session_metadata: dict | None
            # 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_body_additions: dict | None
        provider_profile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   timeoutNrY   max_tokens_param_fnephemeral_max_output_tokens
max_tokensanthropic_max_outputis_nvidia_nimFis_kimiis_tokenhubr   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   r   
extra_bodygoogler,   zgoogle-gemini-cliextra_body_additionsrequest_overrides)rX   r)   _build_kwargs_from_profiler&   r#   r$   anyr
   rO   r   r	   updater6   r%   r   r*   r.   r@   r:   )'rF   r   rH   rY   paramsrW   _profile
api_kwargsrh   max_tokens_fn	ephemeralrk   anthropic_max_outrm   rn   ro   r   _kimi_thinking_off_kimi_effort_e_tokenhub_thinking_off_tokenhub_effort
_lm_effortr   rt   ru   rv   rw   r;   provider_prefs_kimi_thinking_enabledgh_reasoningraw_thinking_configr,   openai_compat_extragoogle_extra	additions	overridesr^   s'                                         @r-   build_kwargsz%ChatCompletionsTransport.build_kwargs   s-   h ))(33	 ::011 	22%E6   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!  	( !'' 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*'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 	P6::mUZ;[;[ 	P P%zz*BCC+.:J{+6:h*O*O
;'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/   c           	      6   ddl m} |                    |          }|pd                                |rzt	          |d         t
                    r_|d                             d          dk    r@t          fdt          D                       r t          |          }i |d         ddi|d<   ||d}|j
        |u rn.|j
        |j
        |d
<   n|                    d
          }|||d
<   |                    d          }	|	|	|d<   |r#t          |          rt          |          }||d<   |                    d          }
|                    d          }|                    d          }|                    d          }|!|
r|                     |
|                     nW|!|
r|                     |
|                     n4|j        r&|
r$|                     |
|j                             n|||d<   |                    d          }|                    ||                    dd          |                    d          ||                    d                    \  }}|                    |           i }|                    |                    d          |                    d          ||                    d          |          }|r|                    |           |r|                    |           |                    d          }|r|                    |           |                    d          }|rP|                                D ];\  }}|dk    r+t	          |t
                    r|                    |           6|||<   <|r||d<   |S )u   Build API kwargs using a ProviderProfile — single path, no legacy flags.

        This method replaces the entire flag-based kwargs assembly when a
        provider_profile is passed. Every quirk comes from the profile object.
        r   )OMIT_TEMPERATUREr   r_   r`   c              3       K   | ]}|v V  	d S rb   rE   )rc   rd   _model_lowers     r-   re   zFChatCompletionsTransport._build_kwargs_from_profile.<locals>.<genexpr>  s(      EE!A%EEEEEEr/   rf   rg   Ntemperaturerh   rY   ri   rj   rk   rl   r   rr   Fqwen_session_metadataollama_num_ctx)r   rr   r   r   r   
session_idrx   r;   )r   rx   r   r;   r   r   r   r   )providers.baser   prepare_messagesr&   r#   r$   r)   r   r
   rO   fixed_temperaturer   r	   r   default_max_tokensbuild_api_kwargs_extrasbuild_extra_bodyitems)rF   profiler   rW   rY   r   r   r   temprh   r   r   user_maxanthropic_maxr   extra_body_from_profiletop_level_from_profiler   profile_bodyr   r   kvr   s                          @r-   r   z3ChatCompletionsTransport._build_kwargs_from_profilez  sH    	433333 ,,Y77	 **,,	A9Q<..	A !  ((H44EEEE/DEEEEE 5 YI@il@FK@@IaL !&
 &

 $(888&2(/(AJ}%% ::m,,D,0
=) **Y''$+Jy!  	( '' 7/66"'Jw 

#899JJ<==	::l++

#9:: ] mmI667777!m!mmH556666' 	5M 	5mmG,FGGHHHH&'4J|$ "::&899++!1#)::.BE#J#J&,jj1H&I&I%zz*:;; ,   	8!7 	0111 &(
 //zz,//!',B!C!CZZ
++- 0 
 
  	,l+++ # 	75666 JJ566	 	)i((( JJ233	 	&!)) & &1$$At)<)<$%%a(((($%JqMM 	2'1J|$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!!5[-"$+"7&6&>$	      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_response9  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_statsC  s    '400=4%!8$???4/155:'#7;;@q 	IW 	I%+HHHtr/   rb   )__name__
__module____qualname____doc__propertyr*   rG   rO   r$   r   rX   r[   r   r   r   r   r6   r   r7   r   rE   r/   r-   rB   rB   f   s        
 "# " " " X"*T#s(^,*	d38n	* * * *X4S#X#7 Dc3h<P     .2	X XX tCH~&X DcN#d*	X 
c3hX X X Xtp p pdK
3 K
=O K
 K
 K
 K
Z# $    C DcNT4I      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{    * * * * * *i i i i i0 i i iZ 0 / / / / /  %'? @ @ @ @ @r/   