
    g3fi-.                    $   d Z ddlmZ ddlZddlmZ ddlmZmZm	Z	m
Z
 er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 ddlmZmZmZmZ ddlmZ  ej@                  e!      Z"dZ#e G d d             Z$ddZ%ddZ& G d de      Z'y)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                  D    e Zd ZU dZded<   ded<   ded<   ded	<   d
ed<   y)_SelectionRequestz#Prepared inputs for tool selection.list[BaseTool]available_toolsstrsystem_messager   last_user_messager   model	list[str]valid_tool_namesN__name__
__module____qualname____doc____annotations__     h/var/www/auto_recruiter/arenv/lib/python3.12/site-packages/langchain/agents/middleware/tool_selection.pyr   r   "   s#    -####r'   r   c                    | sd}t        |      | D cg c]2  }t        t        |j                     t	        |j
                        f   4 }}t        t        |         }d} G d dt              }t        |      S c c}w )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                      e Zd ZU dZded<   y)>_create_tool_selection_response.<locals>.ToolSelectionResponsezUse to select relevant tools.zCAnnotated[list[selected_tool_type], Field(description=description)]toolsNr    r&   r'   r(   ToolSelectionResponser,   D   s    +RRr'   r.   )
AssertionErrorr   r   namer   r*   r   tupler   r   )r-   msgtoolliteralsselected_tool_typer*   r.   s          r(   _create_tool_selection_responser6   -   s     6S!!
 X]OS	'$))$e8H8H&IIJH  uX/FKS	 S
 ,--s   7A;c                2    dj                  d | D              S )zFormat tools as markdown list.

    Args:
        tools: Tools to format.

    Returns:
        Markdown string with each tool on a new line.
    
c              3  T   K   | ]   }d |j                    d|j                    " yw)z- z: N)r0   r*   ).0r3   s     r(   	<genexpr>z$_render_tool_list.<locals>.<genexpr>U   s(     Ldr$))Bt'7'7&89Ls   &()joinr-   s    r(   _render_tool_listr>   L   s     99LeLLLr'   c                       e Zd ZdZdeddd	 	 	 	 	 	 	 	 	 d	 fdZd
dZ	 	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 	 ddZ	 	 	 	 	 	 ddZ	 xZ
S )LLMToolSelectorMiddlewarea  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:
        !!! example "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],
            )
            ```

        !!! example "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         |           || _        || _        |xs g | _        t        |t        t        d      f      r|| _        yt        |      | _        y)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.

                If not specified, there is no limit.
            always_include: Tool names to always include regardless of selection.

                These do not count against the `max_tools` limit.
        N)
super__init__rA   rB   rC   
isinstancer   typer   r   )selfr   rA   rB   rC   	__class__s        r(   rF   z"LLMToolSelectorMiddleware.__init__u   sR    4 	*",2emT$Z89/4DJ(/DJr'   c                <   |j                   rt        |j                         dk(  ry|j                   D cg c]  }t        |t              r| }}| j                  rU|D ch c]  }|j
                   }}| j                  D cg c]	  }||vs| }}|rd| dt        |       }t        |      |D cg c]  }|j
                  | j                  vs| }}|sy| j                  }	| j                  |	d| j                   dz  }	t        |j                        D ]  }
t        |
t              s|
} n d}t        |      | j                  xs |j                  }|D cg c]  }|j
                   }}t        ||	|||      S c c}w c c}w c c}w c c}w c c}w )	zPrepare 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   )r-   lenrG   dictrC   r0   sorted
ValueErrorrA   rB   reversedmessagesr   r/   r   r   )rI   requestr3   
base_toolsavailable_tool_namesr0   missing_toolsr2   r   r   messager   r   r   s                 r(   _prepare_selection_requestz4LLMToolSelectorMiddleware._prepare_selection_request   s    }}GMM 2a 7 (/}}StJtT<RdS
S :D#E$DII#E #E!%!4!4DX8XM  D]O T((./C(D'EG  !o% -7_D$))4K^K^:^4__ ++>>%" #'..!1AN   0 01 	&G'<0$+!	&
 >C %%

+gmm2AB$DIIBB +)/-
 	
[ T $F `6 Cs/   F
F F
	FF5FF Fc                b   g }g }|d   D ]S  }||vr|j                  |       ||vs| j                  t        |      | j                  k  sC|j                  |       U |rd| }t        |      |D 	cg c]  }	|	j                  |v s|	 }
}	|j
                  D 	cg c],  }	t        |	t              s|	j                  | j                  v r|	. }}	|
j                  |       |j
                  D 	cg c]  }	t        |	t              s|	 }}	|j                  g |
|      S c c}	w c c}	w c c}	w )zBProcess the selection response and return filtered `ModelRequest`.r-   zModel selected invalid tools: r=   )appendrB   rL   rO   r0   r-   rG   rM   rC   extendoverride)rI   responser   r   rR   selected_tool_namesinvalid_tool_selections	tool_namer2   r3   selected_toolsalways_included_toolsprovider_toolss                r(   _process_selection_responsez5LLMToolSelectorMiddleware._process_selection_response   sX    *,"$!'* 		6I 00'..y9  33&#.A*BT^^*S#**95		6 #23J2KLCS/! -*
		=P0PD*
 *

  1
dD)dii4;N;N.N 1
 1

 	34 ,3==S4JtT<R$SS&H&H&HII*
1
 Ts   6D"
D"1D'0D,D,c                   | j                  |      }| ||      S t        |j                        }|j                         }|j                  j                  |      }|j                  d|j                  d|j                  g      }t        |t              sdt        |       }t        |      | j                  ||j                  |j                  |      }	 ||	      S )JFilter tools based on LLM selection before invoking the model via handler.systemrolecontentExpected dict response, got )rW   r6   r   json_schemar   with_structured_outputinvoker   r   rG   rM   rH   r/   rc   r   
rI   rR   handlerselection_requesttype_adapterschemastructured_modelr\   r2   modified_requests
             r(   wrap_model_callz)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                  K   | j                  |      }| ||       d{   S t        |j                        }|j                         }|j                  j                  |      }|j                  d|j                  d|j                  g       d{   }t        |t              sdt        |       }t        |      | j                  ||j                  |j                  |      }	 ||	       d{   S 7 7 g7 w)re   Nrf   rg   rj   )rW   r6   r   rk   r   rl   ainvoker   r   rG   rM   rH   r/   rc   r   rn   s
             r(   awrap_model_callz*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   C?C9A1C?C;A C?4C=5C?;C?=C?)
r   zstr | BaseChatModel | NonerA   r   rB   z
int | NonerC   zlist[str] | NonereturnNone)rR   r   ry   z_SelectionRequest | None)
r\   rM   r   r   r   r   rR   r   ry   r   )rR   r   ro   z'Callable[[ModelRequest], ModelResponse]ry   r   )rR   r   ro   z2Callable[[ModelRequest], Awaitable[ModelResponse]]ry   r   )r!   r"   r#   r$   DEFAULT_SYSTEM_PROMPTrF   rW   rc   ru   rx   __classcell__)rJ   s   @r(   r@   r@   X   s    > -12 $+/"0 *"0 	"0
 "0 )"0 
"0H?
B(J(J ((J $	(J
 (J 
(JT)) 9) 
	)>// D/ 
	/r'   r@   )r-   r   ry   r   )r-   r   ry   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   r6   r>   r@   r&   r'   r(   <module>r      s    ) "  ! ; ;3( D 0 ' '  7			8	$ U 
      .>	Mh/ h/r'   