
    yj"f                       d Z ddlmZ ddlZddlZddlZddlmZmZm	Z	 ddl
mZmZmZ ddlmZ ddlmZmZmZ ddlmZ dd	lmZ dd
lmZmZmZmZmZ ddlmZ ddl m!Z! ddl"m#Z#m$Z$m%Z% ddl&m'Z'm(Z(m)Z)m*Z*m+Z+m,Z,m-Z-  ed          Z. ed          Z/dXdZ0dYdZ1dZdZ2 ej3        e4          Z5dZ6d Z7	 	 d[d\d+Z8d]d.Z9dd/d^dDZ:e0dEdddFd_dV            Z;g dWZ<dS )`uf  Agentic/tool orchestration — the multi-iteration tool execution loop.

`execute_tool_loop` owns:
- initial tool-enabled call
- tool execution
- conversation augmentation with assistant messages + tool results
- max-iteration handling and synthesis call
- stream-final-only mode
- empty-response retry (one retry nudge when the model returns empty content)
    )annotationsN)AsyncIterator	AwaitableCallable)Any	ParamSpecTypeVar)	BaseModelretrystop_after_attemptwait_exponential)ModelTransport)ValidationException)get_last_tool_metadataiteration_scopeset_current_iterationset_current_tool_call_seqset_last_tool_metadata   )honcho_llm_call_inner)history_adapter_for_provider)AttemptPlancurrent_attempteffective_temperature)HonchoLLMCallResponseHonchoLLMCallStreamChunkIterationCallbackIterationDataLLMTelemetryContextStreamingResponseWithMetadataVerbosityType_P_RfnCallable[_P, Awaitable[_R]]returnc                H     t          j                   d fd            }|S )	a'  Wrap an async tool-loop entry point in `iteration_scope()` so the
    per-iteration ContextVars (iteration, tool_call_seq, provider id, last
    tool metadata) are reset to their pre-call values on exit. Defensive
    against subsequent loops in the same asyncio Task observing stale state.
    args_P.argskwargs	_P.kwargsr'   r$   c                 t   K   t                      5   | i | d {V cd d d            S # 1 swxY w Y   d S )N)r   )r)   r+   r%   s     9/DATA/AppData/hermes/projects/honcho/src/llm/tool_loop.pywrapperz&_with_iteration_scope.<locals>.wrapper?   s       	- 	-T,V,,,,,,,,	- 	- 	- 	- 	- 	- 	- 	- 	- 	- 	- 	- 	- 	- 	- 	- 	- 	-s   -11)r)   r*   r+   r,   r'   r$   )	functoolswraps)r%   r/   s   ` r.   _with_iteration_scoper2   6   s?     _R- - - - - - N    baseLLMTelemetryContext | None	iterationintc                    | dS t          | j        | j        | j        | j        || j        | j        | j        | j        	  	        S )a  Return a copy of `base` with `iteration` set, or None if no base.

    We always copy rather than mutate the caller-supplied context so callers
    that pass the same context into multiple `honcho_llm_call` invocations
    don't see drift across concurrent runs.
    N)	workspace_namecall_purposeparent_categoryrun_idr6   observerobserved	peer_name
agent_type)	r    r9   r:   r;   r<   r=   r>   r?   r@   )r4   r6   s     r.   _telemetry_for_iterationrA   G   sS     |t*&,{.?
 
 
 
r3   	telemetryresponseHonchoLLMCallResponse[Any]Nonec                   | | j         sdS | j        r| j        sdS | j        sdS 	 ddlm}m}  | || j         | j        | j        | j        | j        | j        | j	        |d |j
        D             |j        |j        |j        pd|j        pd                     dS # t          $ r  t                               dd           Y dS w xY w)	u  emit AgentIterationEvent after each per-iteration LLM response.

    Fired immediately after `response = await call_func()` in the per-iteration
    loop AND after the max-iteration synthesis call. Emitted regardless of
    whether the model requested tool calls — the no-tool terminating iteration
    still counts as an iteration for cost calibration.

    Skipped when telemetry context is missing or lacks the required agent
    identifiers (no agent → no agent.iteration event).
    Nr   )AgentIterationEventemitc                    g | ]
}|d          S name .0tcs     r.   
<listcomp>z)_emit_agent_iteration.<locals>.<listcomp>   s    JJJ2BvJJJJr3   )r<   r;   r@   r9   r=   r>   r?   r6   
tool_callsinput_tokensoutput_tokenscache_read_tokenscache_creation_tokensz"Failed to emit AgentIterationEventTexc_info)r<   r;   r@   r9   src.telemetry.eventsrG   rH   r=   r>   r?   tool_calls_maderR   rS   cache_read_input_tokenscache_creation_input_tokens	Exceptionloggerdebug)rB   r6   rC   rG   rH   s        r.   _emit_agent_iterationr_   _   s;    	 0$ I,@  	# J 	CBBBBBBB ' ) 9$/(7"+"+#-#JJ1IJJJ%2&4"*"B"Ga&.&J&Oa  	
 	
 	
 	
 	
"  J J J9DIIIIIIJs   A3B &CCd   providerr   contentr   rQ   list[dict[str, Any]]thinking_blockslist[dict[str, Any]] | Nonereasoning_detailsdict[str, Any]c                    ddl m} ddl m t          |           } ||fd|D             |pg |pg           }|                    |          S )zEFormat an assistant message with tool calls in provider-native shape.r   )CompletionResult)ToolCallResultc           
     x    g | ]6} |d          |d         |d         |                     d                    7S )idrK   inputthought_signature)rl   rK   rm   rn   )get)rN   	tool_callrj   s     r.   rP   z1format_assistant_tool_message.<locals>.<listcomp>   sc     
 
 
  NT?v&("+--0C"D"D	  
 
 
r3   )rb   rQ   rd   rf   )backendri   rj   r   format_assistant_tool_message)	ra   rb   rQ   rd   rf   BackendCompletionResultadapterresultrj   s	           @r.   rr   rr      s     EDDDDD''''''*844G$$
 
 
 
 (
 
 
 (-2+1r  F 00888r3   tool_resultsconversation_messagesc                t    t          |           }|                    |                    |                     dS )zHAppend tool results to `conversation_messages` in provider-native shape.N)r   extendformat_tool_results)ra   rv   rw   rt   s       r.   append_tool_resultsr{      s9     +844G  !<!<\!J!JKKKKKr3   )rB   winning_planr   promptstr
max_tokensresponse_modeltype[BaseModel] | None	json_modebooltemperaturefloat | None	stop_seqslist[str] | None	verbosityr"   enable_retryretry_attemptsbefore_retry_callbackCallable[[Any], None]'AsyncIterator[HonchoLLMCallStreamChunk]c                  
K   dd
 fd}|	rI t          t          
          t          ddd          |	          |          } |             d
{V }n |             d
{V }|2 3 d
{V }|W V  6 d
S )a?  Stream the final response after tool execution is complete.

    Uses the AttemptPlan captured at the moment streaming began (typically
    the plan whose inner LLM call just succeeded) and pins it across any
    retries of the stream setup. Re-running provider selection here would
    bleed the outer current_attempt ContextVar into streaming retries,
    potentially rolling the selection back to primary after the tool loop
    had already settled on fallback. Tenacity retries re-issue the same
    streaming call against the same pinned model for transient errors.
    r   r'   r   c                    K   dz  t          j                  } t          j        j        t          
          j        j        dj        d d j	        | 	           d {V S )Nr   )attemptr   Tstreamclient_overridetoolstool_choicemessagesselected_configplanrB   )
dataclassesreplacer   ra   modelr   reasoning_effortthinking_budget_tokensclientr   )plan_for_attemptrw   r   r   r}   r   r   r   stream_attemptrB   r   r   r|   s    r.   _setup_streamz,stream_final_response.<locals>._setup_stream   s      !
 '.")
 
 

 +!!+..)/(/*(8!'
 
 
 
 
 
 
 
 
 	
r3   r      
   
multiplierminmaxstopwaitbefore_sleepN)r'   r   r   )r|   r}   r   rw   r   r   r   r   r   r   r   r   rB   r   wrappedr   chunkr   s   ````````` ` `    @r.   stream_final_responser      s6     > N 
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
D  '
%#N33!QA2>>>.
 
 
 	 
 wyy$}&&&&&&       e vvs   ?BF)stream_finaliteration_callbackrB   r   r   r   str | dict[str, Any] | Nonetool_executor$Callable[[str, dict[str, Any]], Any]max_tool_iterationsmax_input_tokens
int | Noneget_attempt_planCallable[[], AttemptPlan]r   r   IterationCallback | None:HonchoLLMCallResponse[Any] | StreamingResponseWithMetadatac                   	
45K   ddl m}m} t          |cxk    rt          k    s+n t          ddt           dt           dz   d| z             |r|                                nd d	g4d
}g }d
}d
}d
}d
}d
}d}|}||k     rdt          j        d           t          
                    d|dz    d|            | |4          |k    rd} |4|          4|4|dz   fd7 
	f
d}|r9 t          t          |          t          ddd          |          |          } n|}  |              d{V }!||!j        z  }||!j        z  }||!j        z  }||!j        z  }t%          |dz   |!           |!j        st          
                    d           t)          |!j        t,                    rL|!j                                        s3|dk     r-||dz
  k     r$|dz  }4                    ddd	           |dz  }|rW             }"t3          |" 4	
|||t5          |dz                       }#t7          |#||||||!j        |dz   | 	  	        S ||!_        ||!_        ||!_        ||!_        ||!_        |dz   |!_        |!j        p||!_        |!S              j        }$tA          |$|!j        |!j        |!j!        |!j"                  }%4                    |%           tG          |dz              g }&tI          |!j                  D ]\  }'}(|(d!         })|(d"         }*|(%                    d#d$          }+t          
                    d%|)            tM          |'|+pd           tO          i            	  ||)|*           d{V },tQ                      }-|&                    |+|)|,d&           |                    |)|*|,|-d'           # tR          $ rU}.t          *                    d(|) d)|.            |&                    |+|)d*t-          |.           dd+           Y d}.~.d}.~.ww xY wtW          |$|&4           |x	 tY          |dz   d, |!j        D             |!j        |!j        |!j        pd
|!j        pd
-          }/ ||/           n,# tR          $ r t          -                    d.d/           Y nw xY w|d
k    r |d0v rd1}t          
                    d2           |dz  }||k     dt          -                    d3| d4           |dz   5d5}04                    d|0d	           | |4          |k    rd} |4|          4|rO             }"t3          |" 4	
|||t5          5                    }#t7          |#|||||d|dz   | 	  	        S t          j        d           d84 
5	fd6}1|r9 t          t          |          t          ddd          |          |1          }2n|1}2 |2             d{V }3t%          t5          5          5|3           ||3_        |dz   |3_        ||3j        z   |3_        ||3j        z   |3_        ||3j        z   |3_        ||3j        z   |3_        |3j        p||3_        |3S )9a  Run the iterative tool calling loop for agentic LLM interactions.

    Loop per iteration:
      1. Make an LLM call with tools available
      2. Execute any tool calls the LLM requests
      3. Append tool results to the conversation
      4. Repeat until the LLM stops calling tools or max iterations reached

    Returns:
        Final HonchoLLMCallResponse with accumulated token counts and tool call
        history, or a StreamingResponseWithMetadata if stream_final=True.
    r   )count_message_tokenstruncate_messages_to_fitzmax_tool_iterations must be in [z, z]; zgot user)rolerb   r   FzTool execution iteration /NTeffective_tool_choicer   rw   rc   iteration_for_callr7   r'   rD   c                   
K                }t          |j        |j        t                    	|j        |j        d|j        | ||j        |t          
|                     d {V S NFr   	r   ra   r   r   r   r   r   r   rA   )r   rw   r   r   r   r   r   r}   r   r   rB   r   r   r   s       r.   _call_with_messagesz.execute_tool_loop.<locals>._call_with_messages_  s      
 $#%%D.
%k22%+ $1. $ 429>PQQ'         r3   r   r   r   r   z$No tool calls in response, finishingziYour last response was empty. Provide a concise answer to the original query using the available context.)r|   r}   r   rw   r   r   r   r   r   r   r   r   rB   )	r   rY   rR   rS   r[   rZ   thinking_content
iterationshit_input_token_caprK   rm   rl    zExecuting tool: )tool_id	tool_nameru   )r   
tool_inputtool_resulttool_result_metadatazTool execution failed for z: zError: )r   r   ru   is_errorc                    g | ]
}|d          S rJ   rL   rM   s     r.   rP   z%execute_tool_loop.<locals>.<listcomp>  s    NNNr6
NNNr3   )r6   rQ   rR   rS   rT   rU   ziteration_callback failedrV   )requiredanyautozJSwitched tool_choice from 'required'/'any' to 'auto' after first iterationz,Tool execution loop reached max iterations ()zYou have reached the maximum number of tool calls. Based on all the information you have gathered, provide your final response now. Do not attempt to call any more tools.c                    K                } t          | j        | j        t          
          | j        | j        d| j        d d | j        | t          	                     d {V S r   r   )r   rw   r   r   r   r}   r   r   synthesis_iterationrB   r   r   s    r.   _final_callz&execute_tool_loop.<locals>._final_calla  s      !!*MJ!+..!' K* 0.y:MNN'
 
 
 
 
 
 
 
 
 	
r3   )r   r   rw   rc   r   r7   r'   rD   )r'   rD   ).conversationr   r   MIN_TOOL_ITERATIONSMAX_TOOL_ITERATIONSr   copyr   setr]   r^   r   r   r   rR   rS   r[   rZ   r_   rY   
isinstancerb   r~   stripappendr   rA   r!   r   r   r   ra   rr   rd   rf   r   	enumeratero   r   r   r   r\   errorr{   r   warning)6r}   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   rB   r   r   r6   all_tool_callstotal_input_tokenstotal_output_tokenstotal_cache_creation_tokenstotal_cache_read_tokensempty_response_retriesr   r   r   	call_funcrC   r|   r   current_providerassistant_messagerv   seqrp   r   r   r   r   r   eiteration_datasynthesis_promptr   final_call_funcfinal_responserw   r   s6   `` `   `````   `   `                                @@r.   execute_tool_loopr     s 
     H MLLLLLLL"5LLLL9LLLLL!-A%AA)<AAAB*(**+
 
 	
 $N6f*M*M)N  I+-N"#  '
)
)
)AVQVVATVVWWW'##$9::=MMM&*#$<$<%'7% %!
 BW:O&/!m	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	8  	,'77%rBBB2   "	# #II ,I"$$$$$$h33x55#x'KK#8#CC
 	iQAAA' ?	LL?@@@ 8+S11 (..00 +Q.. 3a 777&!+&%,, &Q    Q	   0/11.!-!)*?#1' +''!-#1*?6y)a-PP   5!$2!3"50K,C%-%>(1}(;
 
 
 
 (6H$$6H!%8H"3NH0/FH,"+a-H,C0C ( O++--69$$&
 
 	$$%6777 	i!m,,,-/'(@AA -	 -	NC!&)I"7+JmmD"--GLL7I77888 &c7?d;;;"2&&&$1M)Z$H$HHHHHHH (>'?'?$###*%."-    %%%.&0'20D	      	 	 	J)JJqJJKKK###*%."4CFF"4"4$(	        	 	,l<QRRR)K!.'!mNNX5MNNN!)!6"*"8&.&F&K!*2*N*SRS" " " #">2222 K K K:TJJJJJK >>37JJJ$*!LL\   	Q	g )
)
)j NNM7JMMM   $a-	1 
   &=M!N!NOOO # 5669III"& 8 8!#3!
 !
  
 ('))&%!"7)#%)"7.y:MNN
 
 
 -*+-(C$;! 1} 3

 

 

 
	
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0  &
%#N33!QA2>>>.
 
 
 	  &*?,,,,,,,,N
  ,?@@   &4N" )AN"4~7R"RN#69U#UN #n&PP . 	 ."HH * 	*A.A & s-   ?AO
P2A
P--P2	A
R &R=<R=)r   r   r{   r   rr   r   )r%   r&   r'   r&   )r4   r5   r6   r7   r'   r5   )rB   r5   r6   r7   rC   rD   r'   rE   )NN)ra   r   rb   r   rQ   rc   rd   re   rf   re   r'   rg   )ra   r   rv   rc   rw   rc   r'   rE   )r|   r   r}   r~   r   r7   rw   rc   r   r   r   r   r   r   r   r   r   r"   r   r   r   r7   r   r   rB   r5   r'   r   )*r}   r~   r   r7   r   re   r   rc   r   r   r   r   r   r7   r   r   r   r   r   r   r   r   r   r"   r   r   r   r7   r   r   r   r   r   r   r   r   r   r   rB   r5   r'   r   )=__doc__
__future__r   r   r0   loggingcollections.abcr   r   r   typingr   r   r	   pydanticr
   tenacityr   r   r   
src.configr   src.exceptionsr   src.utils.typesr   r   r   r   r   executorr   registryr   runtimer   r   r   typesr   r   r   r   r    r!   r"   r#   r$   r2   rA   r_   	getLogger__name__r]   r   r   rr   r{   r   r   __all__rL   r3   r.   <module>r     s   	 	 # " " " " "          > > > > > > > > > > * * * * * * * * * *       @ @ @ @ @ @ @ @ @ @ % % % % % % . . . . . .              , + + + + + 2 2 2 2 2 2         
                  Yt__WT]]   "   0/J /J /J /Jd 
	8	$	$    48599 9 9 9 9:L L L L0 -1N N N N N Nb ( 37,0+I I I I I IX  r3   