
    yj=?                       d Z ddlm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 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 ddlmZmZ ddlmZmZ ddlmZm Z  ddl!m"Z"m#Z#m$Z$m%Z%m&Z&  ej'        e(          Z) ede          Z*d`dZ+dadZ,dbd/Z-dcd2Z.ddd5Z/ded9Z0e	 	 	 	 	 	 	 	 	 	 	 	 	 	 dfdgdU            Z1e	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dhdidW            Z1e	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 djdkd\            Z1	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 dhdld^Z1g d_Z2dS )ma  Single-call executor: the inner LLM-call path without tool-loop orchestration.

`honcho_llm_call_inner` handles one backend call (complete or stream), building
the effective ModelConfig and delegating to request_builder. Result / stream
chunk types are bridged to the public Honcho* shapes here.

Used by:
- src/llm/api.py (the public entrypoint, for both tool-less and tool-enabled paths)
- src/llm/tool_loop.py (each iteration of the tool loop calls this)
    )annotationsN)AsyncIterator)AnyLiteralTypeVaroverload)	BaseModel)ModelConfigModelTransport   )CompletionResult)StreamChunk)ToolCallResult)CLIENTSbackend_for_provider)execute_completionexecute_stream)AttemptPlaneffective_config_for_call)HonchoLLMCallResponseHonchoLLMCallStreamChunkLLMTelemetryContextProviderClientReasoningEffortTypeM)bounderrBaseException | Nonereturn(Literal['success', 'error', 'cancelled']c                F    | dS t          | t          j                  rdS dS )u   Map a finally-block error into the telemetry outcome literal.

    CancelledError is a normal control-flow event (client disconnect, server
    shutdown) — surface it distinctly so it doesn't pollute error-rate alerts.
    Nsuccess	cancellederror)
isinstanceasyncioCancelledError)r   s    8/DATA/AppData/hermes/projects/honcho/src/llm/executor.py_outcome_from_errorr)   +   s-     {y#w-.. {7    	tool_callr   dict[str, Any]c                R    | j         | j        | j        d}| j        
| j        |d<   |S )N)idnameinputthought_signature)r.   r/   r0   r1   )r+   results     r(   _tool_call_result_to_dictr3   :   s;    l F
 ".&/&A"#Mr*   planAttemptPlan | None	telemetryLLMTelemetryContext | Noneproviderr   modelstr
max_tokensintduration_msfloat	has_toolsbool
was_streamoutcomer2   BackendCompletionResult | Noner$   Nonec                    	 ddl m}m}m} d}|I|j        rB	  ||j                  }n0# t
          $ r# t                              d|j                   Y nw xY w| | j        nd}| | j	        nd}| | j
        nd} | |d"i d|r|j        ndd|d	|r|j        ndd
|dt          |||           d|d|d|	r|	j        ndd|	r|	j        ndd|	r|	j        ndd|	r|	j        ndd|	r|	j        ndd|d||k    d|
rt'          |
          j        ndd|d|d|d|d|d|	rt+          |	j                  ndd|d|r|j        ndd|r|j        nd           dS # t2          $ r  t                              dd !           Y dS w xY w)#zBuild and emit an LLMCallCompletedEvent. Best-effort; swallows errors so
    telemetry failures never bleed into the LLM call path.r   )CallPurposeLLMCallCompletedEventemitNz=Unknown LLMTelemetryContext.call_purpose=%r; emitting withoutr   Fworkspace_namecall_purposeparent_category	transportprovider_labelr9   effective_max_output_tokensprovider_input_tokensprovider_output_tokenscache_read_tokenscache_creation_tokensfinish_reasonrB   is_final_attempterror_classattemptretry_attemptswas_fallbackr=   r?   tool_call_countrA   run_id	iterationz$Failed to emit LLMCallCompletedEventT)exc_info )src.telemetry.eventsrF   rG   rH   rJ   
ValueErrorloggerdebugrV   rW   is_fallbackrI   rK   _infer_provider_labelinput_tokensoutput_tokenscache_read_input_tokenscache_creation_input_tokensrS   type__name__len
tool_callsrZ   r[   	Exception)r4   r6   r8   r9   r;   r=   r?   rA   rB   r2   r$   rF   rG   rH   rJ   rV   rW   rX   s                     r(   _emit_llm_call_completedrm   E   s    2LQQQQQQQQQQ ,0 Y%; *{9+ABB   S*     #'"2$,,040@,,a+/+;t''!!   <E O	 8 84)\ ?H!Q!:!:T #(	
  5XudKKK e -7J ?E'Kv':':! AG(M(<(<A FL#R6#A#AQR ;AGF66a 8>Gv334    #*^";";!" 6;DT%[[11#$  %&  .~'( *\)* (K+, $)-. <B!HV%6!7!7!7q/0 &:12 -6?	((434 3<E9..5	
 	
 	
 	
 	
<  L L L;dKKKKKKLs2   E# ) E# *AE# ADE# #&FF
_transport
str | Nonec                    d|v r|                     dd          d         S |t          |j        dd          nd}|rd|                                v rdS dS )u-  Best-effort vendor inference for relay setups.

    When the model name carries a vendor prefix (OpenRouter convention:
    "anthropic/claude-..." routed through the openai transport), surface that
    as the provider label so analytics can distinguish "openai-the-vendor"
    from "openai-the-transport-pointing-at-openrouter".

    `_transport` is currently unused but kept on the signature so callers stay
    explicit about which transport produced the call — future inference rules
    (e.g. anthropic-direct vs anthropic-via-relay) may need it.
    /r   r   Nbase_url
openrouter)splitgetattrselected_configlower)rn   r9   r4   rr   s       r(   rc   rc      ss     e||{{3""1%% <@;K$j$777QU   LHNN$4$444|4r*   BackendCompletionResultHonchoLLMCallResponse[Any]c                    t          | j        | j        | j        | j        | j        | j        r| j        gng d | j        D             | j        | j	        | j
        
  
        S )Nc                ,    g | ]}t          |          S r]   )r3   ).0tcs     r(   
<listcomp>z1completion_result_to_response.<locals>.<listcomp>   s!    SSS22266SSSr*   )
contentrd   re   rg   rf   finish_reasonstool_calls_madethinking_contentthinking_blocksreasoning_details)r   r   rd   re   rg   rf   rS   rk   r   r   r   )r2   s    r(   completion_result_to_responser      st     !(*$*$F & >171EM,--2SSARSSS0. 2   r*   chunkBackendStreamChunkr   c                d    t          | j        | j        | j        r| j        gng | j                  S )N)r   is_doner   re   )r   r   r   rS   re   )r   s    r(   stream_chunk_to_response_chunkr      sA     $050CK+,,)	   r*   Fpromptresponse_modeltype[M]	json_modetemperaturefloat | None	stop_seqslist[str] | Nonereasoning_effortr   	verbosity'Literal['low', 'medium', 'high'] | Nonethinking_budget_tokens
int | NonestreamLiteral[False]client_overrideProviderClient | Nonetoolslist[dict[str, Any]] | Nonetool_choicestr | dict[str, Any] | Nonemessagesrv   ModelConfig | NoneHonchoLLMCallResponse[M]c                
   K   d S Nr]   r8   r9   r   r;   r   r   r   r   r   r   r   r   r   r   r   r   rv   r4   r6   s                      r(   honcho_llm_call_innerr      s      *  #sr*   HonchoLLMCallResponse[str]c                
   K   d S r   r]   r   s                      r(   r   r      s      * "%r*   .type[BaseModel] | NoneLiteral[True]'AsyncIterator[HonchoLLMCallStreamChunk]c                
   K   d S r   r]   r   s                      r(   r   r      s      * /2cr*   DHonchoLLMCallResponse[Any] | AsyncIterator[HonchoLLMCallStreamChunk]c                "   K   |pt          j                   }|t          d            |d|dg}t           |          }t	          | |||
|          }||	d}|rt          j                    	 t          ||||||j        |	  	         d{V n^# t          $ rQ}t           t          j                    z
  dz  t                    d	t          |          d|
            d}~ww xY wd fd} |            S t          j                    }d}d}	 t          ||||||j        |	  	         d{V }t          |          t           t          j                    |z
  dz  t                    dt          |          ||
           S # t          $ r}|} d}~ww xY w# t           t          j                    |z
  dz  t                    dt          |          ||
           w xY w)a  One backend call. No retry, no fallback, no tool loop.

    The outer src/llm/api.py `honcho_llm_call` handles retry + fallback +
    tool orchestration on top of this.

    Emits one LLMCallCompletedEvent per call. On the stream path, setup
    runs inside the awaited coroutine (so it sits inside any outer retry
    wrapper) and emits its own event on failure; the wrapping generator
    emits a second event from its finally block after drain completes or
    raises. `was_stream` is True for streamed calls. Token counts are
    zero on the stream path because provider token totals aren't surfaced
    post-stream at this layer; aggregate envelopes (DialecticCompletedEvent
    etc.) carry the accurate totals.
    NzMissing client for user)roler   )rv   r8   r9   r   r   r   r   )r   r   )r   r;   r   r   response_formatcache_policyextra_params  Tr4   r6   r8   r9   r;   r=   r?   rA   rB   r2   r$   r   r   c                  K   d } 	 2 3 d {V }t          |          W V  6 n# t          $ r}|}  d }~ww xY w	 t          	t          j                    z
  dz  t          
          dt          |           d |            d S # t          	t          j                    z
  dz  t          
          dt          |           d |            w xY w)Nr   Tr   )r   BaseExceptionrm   timeperf_counterr@   r)   )stream_errorr   excr;   r9   r4   r8   stream_iterstream_startr6   r   s      r(   _wrap_streamz+honcho_llm_call_inner.<locals>._wrap_streamj  sT     15L#. @ @ @ @ @ @ @%8??????? $/;    " $/ )'%)!%!2!4!4|!Ct K"5kk#/==&     ('%)!%!2!4!4|!Ct K"5kk#/==&    s)   % #% B 
727B ACF)r   r   )r   getr_   r   r   r   r   r   r   r   rm   r@   r)   r   r   )r8   r9   r   r;   r   r   r   r   r   r   r   r   r   r   r   r   rv   r4   r6   clientbackendeffective_configcall_extrasr   r   startbackend_resultr$   r   r   s   `` `         `   ``         @@r(   r   r     sC     F 5H 5 5F~9x99:::#778"8V44G0'5)   1:	"R"RK ? (**	 . !%' .-:(
! 
! 
! 
 
 
 
 
 
KK  	 	 	$#!%!.00<?4Gu+++C00    	 	 	 	 	 	 	 	 	 	 	 	 	 	. |~~E59N"&E
1!#*)6$
 
 
 
 
 
 

 

 

 

 

 

 -^<<
 	!!*,,u4<5kk'..!	
 	
 	
 	
 	
	     	!!*,,u4<5kk'..!	
 	
 	
 	
 	
s>   >#B" "
C=,AC88C=/1F, ,
F>6F99F>>G AH)r   r   r   )r   r   r   r    )r+   r   r   r,   )r4   r5   r6   r7   r8   r   r9   r:   r;   r<   r=   r>   r?   r@   rA   r@   rB   r    r2   rC   r$   r   r   rD   )rn   r   r9   r:   r4   r5   r   ro   )r2   rx   r   ry   )r   r   r   r   )FNNNNNFNNNNNNN)(r8   r   r9   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   rv   r   r4   r5   r6   r7   r   r   )NFNNNNNFNNNNNNN)(r8   r   r9   r:   r   r:   r;   r<   r   rD   r   r@   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   rv   r   r4   r5   r6   r7   r   r   )NFNNNNN.NNNNNNN)(r8   r   r9   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   rv   r   r4   r5   r6   r7   r   r   )(r8   r   r9   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   rv   r   r4   r5   r6   r7   r   r   )3__doc__
__future__r   r&   loggingr   collections.abcr   typingr   r   r   r   pydanticr	   
src.configr
   r   r   r   rx   r   r   r   registryr   r   request_builderr   r   runtimer   r   typesr   r   r   r   r   	getLoggerri   r`   r   r)   r3   rm   rc   r   r   r   __all__r]   r*   r(   <module>r      sg  	 	 # " " " " "    ) ) ) ) ) ) 2 2 2 2 2 2 2 2 2 2 2 2       2 2 2 2 2 2 2 2 @ @ @ @ @ @ 6 6 6 6 6 6 # # # # # # 3 3 3 3 3 3 3 3 ? ? ? ? ? ? ? ? ; ; ; ; ; ; ; ;              
	8	$	$GCy!!!      BL BL BL BLJ   4   "    
  $"&,09=)-"-1)-/3,0*.#,0'# # # # 
#. 
   $"&,09=)-"-1)-/3,0*.#,0'% % % % 
%. 
 .2 $"&,09=)--1)-/3,0*.#,0'2 2 2 2 
28 .2 $"&,09=)--1)-/3,0*.#,0'[
 [
 [
 [
 [
|  r*   