
    iv-                    2   S r SSKJr  SSKrSSKJr  SSKJrJrJ	r	J
r
  \(       a  SSKJrJr  SSKJr  SSKJr  SS	KJr  SS
KJrJr  SSKJr  SSKJrJrJrJr  SSKJr  \R@                  " \!5      r"Sr#\ " S S5      5       r$SS jr%SS jr& " S S\5      r'g)z#LLM-based tool selector middleware.    )annotationsN)	dataclass)TYPE_CHECKING	AnnotatedLiteralUnion)	AwaitableCallable)BaseTool)BaseChatModel)HumanMessage)FieldTypeAdapter)	TypedDict)AgentMiddlewareModelCallResultModelRequestModelResponse)init_chat_modelzNYour goal is to select the most relevant tools for answering the user's query.c                  L    \ rS rSr% SrS\S'   S\S'   S\S'   S	\S
'   S\S'   Srg)_SelectionRequest"   z#Prepared inputs for tool selection.list[BaseTool]available_toolsstrsystem_messager   last_user_messager   model	list[str]valid_tool_names N__name__
__module____qualname____firstlineno____doc____annotations____static_attributes__r!       d/home/james-whalen/.local/lib/python3.13/site-packages/langchain/agents/middleware/tool_selection.pyr   r   "   s#    -####r*   r   c           	     
   U (       d  Sn[        U5      eU  Vs/ s H0  n[        [        UR                     [	        UR
                  S94   PM2     nn[        [        U5         nSn " S S[        5      n[        U5      $ s  snf )zCreate a structured output schema for tool selection.

Args:
    tools: Available tools to include in the schema.

Returns:
    TypeAdapter for a schema where each tool name is a Literal with its description.
z&Invalid usage: tools must be non-empty)descriptionz2Tools to use. Place the most relevant tools first.c                  $    \ rS rSr% SrS\S'   Srg)>_create_tool_selection_response.<locals>.ToolSelectionResponseC   zUse to select relevant tools.zCAnnotated[list[selected_tool_type], Field(description=description)]toolsr!   Nr"   r!   r*   r+   ToolSelectionResponser/   C   s    +RRr*   r2   )
AssertionErrorr   r   namer   r-   r   tupler   r   )r1   msgtoolliteralsselected_tool_typer-   r2   s          r+   _create_tool_selection_responser:   -   s     6S!!
 X]W\t	'$))$e8H8H&IIJW\   uX/FKS	 S
 ,--s   7B c                2    SR                  S U  5       5      $ )z~Format tools as markdown list.

Args:
    tools: Tools to format.

Returns:
    Markdown string with each tool on a new line.

c              3  Z   #    U  H!  nS UR                    SUR                   3v   M#     g7f)z- z: N)r4   r-   ).0r7   s     r+   	<genexpr>$_render_tool_list.<locals>.<genexpr>T   s(     Ledr$))Bt'7'7&89es   )+)join)r1   s    r+   _render_tool_listrB   K   s     99LeLLLr*   c                     ^  \ rS rSrSrS\SSS.         SU 4S jjjrSS jr          SS jr      SS jr	      SS	 jr
S
rU =r$ )LLMToolSelectorMiddlewareW   a  Uses an LLM to select relevant tools before calling the main model.

When an agent has many tools available, this middleware filters them down
to only the most relevant ones for the user's query. This reduces token usage
and helps the main model focus on the right tools.

Examples:
    Limit to 3 tools:
    ```python
    from langchain.agents.middleware import LLMToolSelectorMiddleware

    middleware = LLMToolSelectorMiddleware(max_tools=3)

    agent = create_agent(
        model="openai:gpt-4o",
        tools=[tool1, tool2, tool3, tool4, tool5],
        middleware=[middleware],
    )
    ```

    Use a smaller model for selection:
    ```python
    middleware = LLMToolSelectorMiddleware(model="openai:gpt-4o-mini", max_tools=2)
    ```
N)r   system_prompt	max_toolsalways_includec                  > [         TU ]  5         X l        X0l        U=(       d    / U l        [        U[        [        S5      45      (       a  Xl        g[        U5      U l        g)a  Initialize the tool selector.

Args:
    model: Model to use for selection. If not provided, uses the agent's main model.
        Can be a model identifier string or BaseChatModel instance.
    system_prompt: Instructions for the selection model.
    max_tools: Maximum number of tools to select. If the model selects more,
        only the first max_tools will be used. No limit if not specified.
    always_include: Tool names to always include regardless of selection.
        These do not count against the max_tools limit.
N)
super__init__rF   rG   rH   
isinstancer   typer   r   )selfr   rF   rG   rH   	__class__s        r+   rK   "LLMToolSelectorMiddleware.__init__r   sO    & 	*",2emT$Z899/4J(/DJr*   c                   UR                   (       a  [        UR                   5      S:X  a  gUR                    Vs/ s H  n[        U[        5      (       a  M  UPM     nnU R                  (       aa  U Vs1 s H  o"R
                  iM     nnU R                   Vs/ s H  oUU;  d  M
  UPM     nnU(       a  SU S[        U5       3n[        U5      eU Vs/ s H   o"R
                  U R                  ;  d  M  UPM"     nnU(       d  gU R                  n	U R                  b  U	SU R                   S3-  n	[        UR                  5       H  n
[        U
[        5      (       d  M  U
n  O   Sn[        U5      eU R                  =(       d    UR                  nU Vs/ s H  o"R
                  PM     nn[        UU	UUUS9$ s  snf s  snf s  snf s  snf s  snf )	z{Prepare inputs for tool selection.

Returns:
    SelectionRequest with prepared inputs, or None if no selection is needed.
r   Nz.Tools in always_include not found in request: z. Available tools: z
IMPORTANT: List the tool names in order of relevance, with the most relevant first. If you exceed the maximum number of tools, only the first z will be used.z)No user message found in request messages)r   r   r   r   r    )r1   lenrL   dictrH   r4   sorted
ValueErrorrF   rG   reversedmessagesr   r3   r   r   )rN   requestr7   
base_toolsavailable_tool_namesr4   missing_toolsr6   r   r   messager   r   r    s                 r+   _prepare_selection_request4LLMToolSelectorMiddleware._prepare_selection_request   s    }}GMM 2a 7 (/}}S}tJtT<Rd}
S :D#E*$II* #E!%!4!4!4DX8X!4   D]O T((./C(D'EG  !o% -7_JD))4K^K^:^4J_ ++>>%" #'..!1AN   0 01G'<00$+! 2
 >C %%

+gmm2AB/$II/B +)/-
 	
[ T $F `6 Cs/   F;F;4G 	G&GG
6G
Gc                   / n/ nUS    H[  nXs;  a  UR                  U5        M  Xu;  d  M"  U R                  b  [        U5      U R                  :  d  MJ  UR                  U5        M]     U(       a  SU 3n[        U5      eU V	s/ s H  oR                  U;   d  M  U	PM     n
n	UR
                   V	s/ s H8  n	[        U	[        5      (       a  M  U	R                  U R                  ;   d  M6  U	PM:     nn	U
R                  U5        UR
                   V	s/ s H  n	[        U	[        5      (       d  M  U	PM     nn	/ U
QUQUl        U$ s  sn	f s  sn	f s  sn	f )z@Process the selection response and return filtered ModelRequest.r1   zModel selected invalid tools: )
appendrG   rR   rU   r4   r1   rL   rS   rH   extend)rN   responser   r    rX   selected_tool_namesinvalid_tool_selections	tool_namer6   r7   selected_toolsalways_included_toolsprovider_toolss                r+   _process_selection_response5LLMToolSelectorMiddleware._process_selection_response   sM    *,"$!'*I0'..y9 3&#.A*BT^^*S#**95 + #23J2KLCS/! -*
,T		=P0PD_ 	 *

  1
%dD) .2ii4;N;N.N % 	 1

 	34 ,3==S=4JtT<R$=S:.:>:*
1
 Ts*   EE1EE*EE.Ec                   U R                  U5      nUc  U" U5      $ [        UR                  5      nUR                  5       nUR                  R                  U5      nUR                  SUR                  S.UR                  /5      n[        U[        5      (       d  S[        U5       3n[        U5      eU R                  XsR                  UR                  U5      n	U" U	5      $ )JFilter tools based on LLM selection before invoking the model via handler.systemrolecontentExpected dict response, got )r]   r:   r   json_schemar   with_structured_outputinvoker   r   rL   rS   rM   r3   ri   r    
rN   rX   handlerselection_requesttype_adapterschemastructured_modelrb   r6   modified_requests
             r+   wrap_model_call)LLMToolSelectorMiddleware.wrap_model_call   s     !;;GD$7## 77H7X7XY))+,22II&Q#**!.?.N.NO!33
 (D))0h0@AC %%;;779J9[9[]d
 '((r*   c                  #    U R                  U5      nUc  U" U5      I Sh  vN $ [        UR                  5      nUR                  5       nUR                  R                  U5      nUR                  SUR                  S.UR                  /5      I Sh  vN n[        U[        5      (       d  S[        U5       3n[        U5      eU R                  XsR                  UR                  U5      n	U" U	5      I Sh  vN $  N Nk N7f)rl   Nrm   rn   rq   )r]   r:   r   rr   r   rs   ainvoker   r   rL   rS   rM   r3   ri   r    ru   s
             r+   awrap_model_call*LLMToolSelectorMiddleware.awrap_model_call  s     !;;GD$ ))) 77H7X7XY))+,22II&Q)11!.?.N.NO!33
 
 (D))0h0@AC %%;;779J9[9[]d
 -...+ *
 /s5    DC>A1DD A$D9D:D DD)rH   rG   r   rF   )
r   zstr | BaseChatModel | NonerF   r   rG   z
int | NonerH   zlist[str] | NonereturnNone)rX   r   r   z_SelectionRequest | None)
rb   rS   r   r   r    r   rX   r   r   r   )rX   r   rv   z'Callable[[ModelRequest], ModelResponse]r   r   )rX   r   rv   z2Callable[[ModelRequest], Awaitable[ModelResponse]]r   r   )r#   r$   r%   r&   r'   DEFAULT_SYSTEM_PROMPTrK   r]   ri   r|   r   r)   __classcell__)rO   s   @r+   rD   rD   W   s    : -12 $+/0 *0 	0
 0 )0 
0 0:>
@)) () $	)
 ) 
)V)) 9) 
	)>// D/ 
	/ /r*   rD   )r1   r   r   r   )r1   r   r   r   )(r'   
__future__r   loggingdataclassesr   typingr   r   r   r   collections.abcr	   r
   langchain.toolsr   *langchain_core.language_models.chat_modelsr   langchain_core.messagesr   pydanticr   r   typing_extensionsr   !langchain.agents.middleware.typesr   r   r   r   langchain.chat_models.baser   	getLoggerr#   loggerr   r   r:   rB   rD   r!   r*   r+   <module>r      s    ) "  ! ; ;3( D 0 ' '  7			8	$ U 
      .<	M_/ _/r*   