Ë
    áß²j�0  ã                   ór   — d Z ddlZddlmZmZ ddlmZ ddl	m
Z
 dZdZdefd	„Zd
edz  fd„Z G d„ d«      Zy)aA  Provider reasoning-parameter policy for ``AIAgent``.

When ``reasoning`` extra_body is safe to send, LM Studio / Ollama / GitHub Models capability probes,
``reasoning_content`` echo families, and strict-API tool-call sanitising.
Extracted from ``run_agent.py``; every method resolves through ``AIAgent``'s MRO unchanged.
é    N)ÚforwardÚforward_static)Úmatches_reasoning_echo_family)Úbase_url_host_matches)	z	deepseek/z
anthropic/zopenai/zx-ai/zgoogle/gemini-2zgoogle/gemma-4z
qwen/qwen3z
tencent/hyzxiaomi/é<   Ú
cache_attrc           	      óš  — t        | |d«      }|€i }t        | ||«       | j                  | j                  f}|j	                  |«      }|�-|\  }}	 ||«      st        j                  «       |	z
  t        k  r|S 	  || j                  | j                  t        | dd«      «      }|t        j                  «       f||<   |S # t        $ r |}Y Œ(w xY w)z£``probe(model, base_url, api_key)`` once per (model, base_url); ``unknown`` is what a raising probe
    yields, ``definitive(value)`` decides permanent vs 60s TTL.NÚapi_keyÚ )	ÚgetattrÚsetattrÚmodelÚbase_urlÚgetÚtimeÚ	monotonicÚ_PROBE_TTL_SÚ	Exception)
Úagentr   ÚprobeÚunknownÚ
definitiveÚcacheÚkeyÚcachedÚvalueÚtss
             ú;/home/ubuntu/.hermes/hermes-agent/agent/reasoning_params.pyÚ_cached_prober      sÌ   € ô �E˜: tÓ,€EØ€}ØˆÜ��z 5Ô)Ø�;‰;˜Ÿ™Ð
'€CØ�Y‰Y�s‹^€FØÐØ‰	ˆˆrÙ�eÔ¤§¡Ó!1°BÑ!6¼,Ò FØˆLðÙ�e—k‘k 5§>¡>´7¸5À)ÈRÓ3PÓQˆð œŸ™Ó)Ð*€Eˆ#�JØ€Løô ò ØŠðús   Á8)B< Â<C
Ã	C
Úreturnc                 ó²  — t        | dd«      dk7  ryt        t        | dd«      xs d«      }t        t        | dd«      xs d«      }	 ddlm}  ||«      }|�|j	                  |«      nd}|sy	 dd	lm}  |||d
¬«      }|�|j                  d
u ryt        | dd«      r| j                  «       syt        |«      S # t
        $ r Y yw xY w# t
        $ r d}Y ŒVw xY w)uñ  Reasoning config for a main-loop request whose ``agent.reasoning_effort`` is unset.

    Asks the active provider profile (``ProviderProfile.default_reasoning_config``; the custom /
    OpenAI-compatible profile answers medium) so a route's own default never silently applies â€”
    kimi-k3 behind a relay defaults to ``max``, 3x the reasoning tokens of medium. Resolved at
    request time, so ``/model`` and fallback activation re-evaluate it. None keeps the field off
    the wire: a non chat-completions transport, a profile without a default (those decide inside
    ``build_api_kwargs_extras``), a model the catalog / ``model_overrides`` mark
    ``supports_reasoning: false``, or a local Ollama model whose ``/api/show`` lacks ``thinking``.
    Úapi_modeNÚchat_completionsÚproviderr   r   r   )Úget_provider_profile)Úget_model_capabilitiesF)Úallow_networkÚ_ollama_num_ctx)r   ÚstrÚ	providersr%   Údefault_reasoning_configr   Úagent.models_devr&   Úsupports_reasoningÚ _ollama_supports_thinking_cachedÚdict)r   r$   r   r%   ÚprofileÚdefaultr&   Úcapss           r   Úunset_reasoning_defaultr3   .   sù   € ô ˆu�j $Ó'Ð+=Ò=ØÜ”7˜5 *¨bÓ1Ò7°RÓ8€HÜ”˜˜w¨Ó+Ò1¨rÓ2€EðÝ2á& xÓ0ˆØ=DÐ=P�'×2Ñ2°5Ô9ÐVZˆñ ØðÝ;á% h°ÀUÔKˆð Ð˜D×3Ñ3°uÑ<Øô ˆuÐ'¨Ô.°u×7]Ñ7]Ô7_ØÜ�‹=Ðøô! ò Ùðûô ò ØŠðús$   Á#B9 Á.C Â9	CÃCÃCÃCc                   ó  — e Zd ZdZdefd„Zdee   fd„Zdefd„Z	de
dz  fd„Z edd	«      Zdefd
„Zdefd„Zedefd„«       Zdefd„Zdefd„Zdefd„Z edd«      Z edd«      Zedde
ddde
fd„«       Z edd«      Zdefd„Zy)ÚReasoningParamsMixinzBReasoning-parameter gating and echo policy (see module docstring).r    c                 óþ  ‡— | j                   }t        |d«      st        |d«      ryt        |d«      st        |d«      r"	 ddlm} t	         || j
                  «      «      S | j                  xs d	j                  «       j                  «       d
k(  r t        d„ | j                  «       D «       «      S t        |d«      r| j                  «       S | j                  «       rt        |d«      ry	 ddlm}m}  || j
                  «      }|€ |«        |�t	        |j#                  d«      «      S | j
                  xs d	j                  «       Št        ˆfd„t$        D «       «      S # t        $ r Y yw xY w# t        $ r d}Y Œnw xY w)zÑTrue when reasoning extra_body is safe to send: OpenRouter forwards unknown extra_body upstream and
        some routes 400 on ``reasoning``, so gate to known reasoning-capable families and direct Nous Portal.znousresearch.comzai-gateway.vercel.shTzmodels.github.aizgithubcopilot.comr   ©Úgithub_model_reasoning_effortsFr   Úlmstudioc              3   ó.   K  — | ]  }|xr |d k7  –— Œ y­w)ÚoffN© )Ú.0Úopts     r   ú	<genexpr>zFReasoningParamsMixin._supports_reasoning_extra_body.<locals>.<genexpr>g   s   è ø€ Òa°�sÒ+˜s e™|Ó+Ñaùs   ‚z
ollama.comzapi.mistral.ai)Ú'openrouter_model_reasoning_capabilitiesÚ$warm_openrouter_reasoning_caps_asyncNr-   c              3   ó@   •K  — | ]  }‰j                  |«      –— Œ y ­w©N)Ú
startswith)r=   Úprefixr   s     €r   r?   zFReasoningParamsMixin._supports_reasoning_extra_body.<locals>.<genexpr>y   s   øè ø€ ÒY°�5×#Ñ# F×+ÑYùs   ƒ)Ú_base_url_lowerr   Úhermes_cli.modelsr8   Úboolr   r   r$   ÚstripÚlowerÚanyÚ"_lmstudio_reasoning_options_cachedr.   Ú_is_openrouter_urlÚ hermes_cli.models_reasoning_capsr@   rA   r   Ú_OPENROUTER_REASONING_PREFIXES)ÚselfÚurlr8   r@   rA   r2   r   s         @r   Ú_supports_reasoning_extra_bodyz3ReasoningParamsMixin._supports_reasoning_extra_bodyX   sc  ø€ ð ×"Ñ"ˆÜ  Ð&8Ô9Ô=RÐSVÐXnÔ=oØÜ  Ð&8Ô9Ô=RÐSVÐXkÔ=lðÝLäÑ:¸4¿:¹:ÓFÓGÐGð �M‰MÒ˜R×&Ñ&Ó(×.Ñ.Ó0°JÒ>äÑa°t×7^Ñ7^Ó7`ÔaÓaÐaÜ   lÔ3à×8Ñ8Ó:Ð:Ø×&Ñ&Ô(Ô,AÀ#ÐGWÔ,XØð	÷ GÙ:¸4¿:¹:ÓFˆDØˆ|Ù4Ô6ð ÐÜ˜Ÿ™Ð!5Ó6Ó7Ð7Ø—‘Ò!˜r×(Ñ(Ó*ˆÜÓYÔ:XÔYÓYÐYøô- ò Ùðûô" ò 	ØŠDð	ús$   Á  E Ã*#E. Å	E+Å*E+Å.E<Å;E<c                 óX   — 	 ddl m} t        | d|g t        «      S # t        $ r g cY S w xY w)zjLM Studio's published reasoning ``allowed_options`` (gate + clamp so toggle models don't 400 on ``high``).r   )Ú lmstudio_model_reasoning_optionsÚ_lm_reasoning_opts_cache)Úhermes_cli.models_localrT   r   r   rH   )rP   rT   s     r   rL   z7ReasoningParamsMixin._lmstudio_reasoning_options_cached{   s9   € ð	ÝPô ˜TÐ#=Ð?_ÐacÔeiÓjÐjøô ò 	ØŠIð	ús   ‚ ›)¨)c           	      ó`   — 	 ddl m} t        t	        | d|dd„ «      «      S # t        $ r Y yw xY w)zITrue only if Ollama's ``/api/show`` declares the ``thinking`` capability.r   )Úollama_model_supports_thinkingFÚ_ollama_thinking_cacheNc                 ó
   — | d uS rC   r<   )Úvs    r   ú<lambda>zGReasoningParamsMixin._ollama_supports_thinking_cached.<locals>.<lambda>‰   s   € ÐrsÐ{Ðr€ ó    )rV   rX   r   rH   r   )rP   rX   s     r   r.   z5ReasoningParamsMixin._ollama_supports_thinking_cachedƒ   sE   € ð	ÝNô ”M $Ð(@ÐB`ÐbfÑhó  Aó  Bð  	Bøô ò 	Ùð	ús   ‚! ¡	-¬-Nc                 ó˜  — 	 ddl m}  || j                  «      }|syt	        | j
                  t        «      r| j
                  ni }|j                  d«      du ryt        |j                  dd«      «      j                  «       j                  «       }||vr%dd	d
œj                  |«      }||v r|n
d|v rdn|d   }d|iS # t        $ r Y yw xY w)zDFormat reasoning payload for GitHub Models/OpenAI-compatible routes.r   r7   NÚenabledFÚeffortÚmediumÚhighÚlow)ÚxhighÚminimal)rG   r8   r   r   Ú
isinstanceÚreasoning_configr/   r   r)   rI   rJ   )rP   r8   Ú	supportedÚcfgr`   Únearests         r   Ú#_github_models_reasoning_extra_bodyz8ReasoningParamsMixin._github_models_reasoning_extra_body‹   sÓ   € ð	ÝHñ 3°4·:±:Ó>ˆ	ÙØä'1°$×2GÑ2GÌÔ'Nˆd×#Ò#ÐTVˆØ�7‰7�9Ó Ñ&ØÜ�S—W‘W˜X xÓ0Ó1×7Ñ7Ó9×?Ñ?ÓAˆà˜Ñ"à &°5Ñ9×=Ñ=¸fÓEˆGØ '¨9Ñ 4‘WÀhÐR[ÑF[¹(ÐajÐklÑamˆFØ˜&Ð!Ð!øô! ò 	Ùð	ús   ‚B= Â=	C	ÃC	zagent.chat_completion_helpersÚbuild_assistant_messagec                 ó8  — | j                   | j                  t        | d| j                  «      f}t        | dd«      }|�|d   |k(  r|d   S | j	                  «       xs4 | j                  «       xs" | j                  «       xs | j                  «       }||f| _        |S )u  True when the provider enforces ``reasoning_content`` echo-back on tool-call replays (DeepSeek, Kimi,
        MiMo thinking all 400 without it). Cached per (provider, model, base_url), invalidated by
        ``switch_model()`` / ``_try_activate_fallback()`` â€” called ~16Ã— per turn.

        DeepSeek v4 thinking and Kimi / Moonshot thinking both reject replays of assistant tool-call
        messages that omit ``reasoning_content`` (refs 15250, #17400). Xiaomi MiMo thinking mode has the
        same requirement.
        rF   Ú_thinking_pad_cacheNr   é   )	r$   r   r   r   Ú_needs_deepseek_tool_reasoningÚ_needs_kimi_tool_reasoningÚ_needs_mimo_tool_reasoningÚ_reasoning_echo_opt_inrn   )rP   r   r   Úresults       r   Ú_needs_thinking_reasoning_padz2ReasoningParamsMixin._needs_thinking_reasoning_pad£   s§   € ð �}‰}˜dŸj™j¬'°$Ð8IÈ4Ï=É=Ó*YÐZˆÜ˜Ð4°dÓ;ˆØÐ &¨¡)¨sÒ"2Ø˜!‘9ÐØ×5Ñ5Ó7ò X¸4×;ZÑ;ZÓ;\ò XØ×4Ñ4Ó6òXØ:>×:UÑ:UÓ:Wð 	à$'¨ =ˆÔ Øˆr]   c                 ó.   — t        t        | dd«      «      S )z»``model.reasoning_echo`` opt-in for the *current* provider (covers gateways the host rules miss);
        fallback activation swaps the flag and ``restore_primary_runtime()`` restores it.Ú_reasoning_echo_flagF)rH   r   ©rP   s    r   rs   z+ReasoningParamsMixin._reasoning_echo_opt_inµ   s   € ô ”G˜DÐ"8¸%Ó@ÓAÐAr]   c                  ó’   — 	 ddl m}  t         | «       j                  d«      xs i j                  d«      «      S # t        $ r Y yw xY w)z>Read ``model.reasoning_echo`` from config; False on any error.r   ©Úload_config_readonlyr   Úreasoning_echoF)Úhermes_cli.configr{   rH   r   r   rz   s    r   Ú _read_reasoning_echo_from_configz5ReasoningParamsMixin._read_reasoning_echo_from_configº   sG   € ð	Ý>ÜÑ-Ó/×3Ñ3°GÓ<ÒBÀ×GÑGÐHXÓYÓZÐZøÜò 	Ùð	ús   ‚7: º	AÁAc                 óF   — t        d| j                  d| j                  «      S )z@True when the current provider is Kimi / Moonshot thinking mode.ÚkimiN)r   r$   r   rx   s    r   rq   z/ReasoningParamsMixin._needs_kimi_tool_reasoningÆ   s   € ä,¨V°T·]±]ÀDÈ$Ï-É-ÓXÐXr]   c                 ó~   — t        d| j                  xs dj                  «       | j                  | j                  «      S )a1  True when the current provider is DeepSeek thinking mode (omitting the echo is an HTTP 400).

        DeepSeek V4 thinking mode requires ``reasoning_content`` on every assistant tool-call turn; omitting
        it causes HTTP 400 when the message is replayed in a subsequent API request (#15250).
        Údeepseekr   ©r   r$   rJ   r   r   rx   s    r   rp   z3ReasoningParamsMixin._needs_deepseek_tool_reasoningÊ   s5   € ô -¨Z¸$¿-¹-Ò:MÈ2×9TÑ9TÓ9VÐX\×XbÑXbÐdh×dqÑdqÓrÐrr]   c                 ó~   — t        d| j                  xs dj                  «       | j                  | j                  «      S )z<True when the current provider is Xiaomi MiMo thinking mode.Úmimor   rƒ   rx   s    r   rr   z/ReasoningParamsMixin._needs_mimo_tool_reasoningÒ   s3   € ä,¨V°d·m±mÒ6IÀr×5PÑ5PÓ5RÐTX×T^ÑT^Ð`d×`mÑ`mÓnÐnr]   zagent.agent_runtime_helpersÚcopy_reasoning_content_for_apiÚ#reapply_reasoning_echo_for_providerÚapi_msgr   z
str | Nonec                 óF  — | j                  d«      }t        |t        «      s| S ddlm} ddh ||«      r
t        «       ndhz  }|D ���cg c]=  }t        |t        «      r)|j                  «       D ��ci c]  \  }}||vsŒ||“Œ c}}n|‘Œ? c}}}| d<   | S c c}}w c c}}}w )a7  Strip Codex Responses fields from tool_calls for strict Chat Completions APIs (Mistral, Fireworks
        400/422 on unknown fields). ``extra_content`` (Gemini thought_signature) is kept only for Gemini-family
        models. Builds new dicts so the internal history keeps the Codex fields for a later fallback.Ú
tool_callsr   )Ú!_model_consumes_thought_signatureÚcall_idÚresponse_item_idÚextra_content)r   rf   ÚlistÚ!agent.transports.chat_completionsr‹   Úsetr/   Úitems)rˆ   r   rŠ   r‹   rI   ÚtcÚkr[   s           r   Ú#_sanitize_tool_calls_for_strict_apiz8ReasoningParamsMixin._sanitize_tool_calls_for_strict_apiÚ   s©   € ð
 —[‘[ Ó.ˆ
Ü˜*¤dÔ+ØˆNÝWØÐ.Ð/Ñ<]Ð^cÔ<d´3´5ÐkzÐj{Ñ|ˆà+5÷!7ð !7Ø%'ô U_Ð_aÔcgÔTh°2·8±8³:×!P©4¨1¨aÀÈ%Â ! Q¡$Ô!PÐnpÑ!pô !7ˆ�Ñàˆùó "Qùô !7s   Á
(BÁ2BÁ?BÂBÂBÚsanitize_tool_call_argumentsc                 ó    — | j                   dk7  S )zgTrue for every non-Codex API: Codex Responses fields are not Chat Completions schema and 400 elsewhere.Úcodex_responses)r"   rx   s    r   Ú_should_sanitize_tool_callsz0ReasoningParamsMixin._should_sanitize_tool_callsê   s   € à�}‰}Ð 1Ñ1Ð1r]   rC   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__rH   rR   r�   r)   rL   r.   r/   rk   Ú_forwardÚ_build_assistant_messageru   rs   Ústaticmethodr~   rq   rp   rr   Ú_copy_reasoning_content_for_apiÚ$_reapply_reasoning_echo_for_providerr•   Ú_forward_staticÚ_sanitize_tool_call_argumentsr™   r<   r]   r   r5   r5   U   s  „ ÙLð!Z°ó !ZðFk°D¸±Ió kðB°$ó Bð"°T¸D±[ó "ñ,  (Ð(GÐIbÓcÐð¨tó ð$B¨ó Bð
 ð¨dò ó ððY¨Dó Yðs°ó sðo¨Dó oñ '/Ð/LÐNnÓ&oÐ#á+3Ð4QÐSxÓ+yÐ(àñ°Tð À,ð ÐZ^ò ó ðñ %4Ð4QÐSqÓ$rÐ!ð2¨Tô 2r]   r5   )r�   r   Úagent.lazy_forwardr   rž   r   r£   Úagent.message_sanitizationr   Úutilsr   rO   r   r)   r   r/   r3   r5   r<   r]   r   ú<module>r¨      sP   ðñó ç UÝ DÝ 'ð"Ð ð €ð Só ð*$ d¨T¡kó $÷NW2ò W2r]   