
    k
i&                        d Z ddlZddlmZmZmZmZ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 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  ej        e          Z eeee!ef         gee         f         Z" G d de          Z#dS )a  Semantic router middleware for intent-based routing.

This module provides a middleware that routes requests based on
semantic similarity to predefined intents using Redis and vector embeddings.
Compatible with LangChain's AgentMiddleware protocol for use with create_agent.
    N)Any	AwaitableCallableDictListOptionalUnion)ModelCallResultModelRequestModelResponse)ToolMessage)ToolCallRequest)Command)RoutingConfigSemanticRouter)Route   )AsyncRedisMiddleware)SemanticRouterConfigc            	       v    e Zd ZU dZeed<   eed<   eee	f         ed<   deddf fdZ
dd	Zd
eeeeef         ef                  defdZdedee         fdZ	 ddedee	         dee	ge	f         fdZdedeegee         f         defdZdedeegeeeef                  f         deeef         fdZ xZS )SemanticRouterMiddlewareak  Middleware that routes requests based on semantic similarity.

    Uses redisvl.extensions.router.SemanticRouter to classify user intents
    and route requests to appropriate handlers. This is useful for:
    - Directing queries to specialized agents
    - Triggering specific workflows based on intent
    - Adding routing metadata for downstream processing

    Example:
        ```python
        from langgraph.middleware.redis import (
            SemanticRouterMiddleware,
            SemanticRouterConfig,
        )

        routes = [
            {"name": "greeting", "references": ["hello", "hi", "hey"]},
            {"name": "support", "references": ["help", "issue", "problem"]},
        ]

        config = SemanticRouterConfig(
            redis_url="redis://localhost:6379",
            routes=routes,
        )

        middleware = SemanticRouterMiddleware(config)

        # Register custom handler for greeting route
        @middleware.register_route_handler("greeting")
        async def handle_greeting(request, route_match):
            return {"content": "Hello! How can I help you today?"}
        ```
    _router_config_route_handlersconfigreturnNc                 f    t                                          |           || _        i | _        dS )z}Initialize the semantic router middleware.

        Args:
            config: Configuration for the semantic router.
        N)super__init__r   r   )selfr   	__class__s     C:\Users\Dell Inspiron 16\Desktop\tws\AgrotaPowerBi\back-agrota-powerbi\mcp-client-agrota\venv\Lib\site-packages\langgraph/middleware/redis/semantic_router.pyr   z!SemanticRouterMiddleware.__init__F   s2     	   !    c                    K   g }| j         j        D ]T}|d         |d         d}|                    d          |d         |d<   t          di |}|                    |           Ut          | j         j        | j         j                  }| j         j        ||d}| j         j	        r| j         j	        |d<   n| j         j
        r| j         j
        |d	<   | j         j        | j         j        |d
<   t          di || _        dS )a  Set up the SemanticRouter instance.

        Note: SemanticRouter from redisvl uses synchronous Redis operations
        internally, so we must provide redis_url and let it manage its own
        sync connection rather than passing our async client.
        name
references)r%   r&   distance_thresholdN)max_kaggregation_method)r%   routesrouting_config	redis_urlconnection_kwargs
vectorizer )r   r*   getr   appendr   r(   r)   r%   r,   connection_argsr.   r   r   )r    r*   route_configroute_kwargsrouter+   router_kwargss          r"   _setup_asyncz%SemanticRouterMiddleware._setup_asyncP   sB       L/ 		! 		!L$V,*<8, ,L
  455A5ABV5W12))L))EMM%     ',$#|>
 
 
 L%,)
 )
 <! 	N)-)?M+&&\) 	N151MM-.<".*.,*AM,'%6666r#   messagesc                 8   |sdS t          |          D ]}t          |t                    r5|                    dd          }|dk    r|                    dd          c S Lt	          |dd          pt	          |dd          }|dv rt	          |dd          c S dS )zExtract the query to use for routing.

        Args:
            messages: List of messages from the request.

        Returns:
            The extracted query string.
         roleusercontenttypeN)r<   human)reversed
isinstancedictr0   getattr)r    r8   messager;   msg_types        r"   _extract_queryz'SemanticRouterMiddleware._extract_query|   s      	2  )) 
	; 
	;G'4(( 	;{{62..6>>";;y"55555 " #7FD99 WVT> > 000"7Ir::::: 1 rr#   queryc                 4    |sdS |                      |          S )zGet the matching route for a query.

        Args:
            query: The user query to route.

        Returns:
            The route match object, or None if no match.
        N)r   )r    rG   s     r"   
_get_routez#SemanticRouterMiddleware._get_route   s#      	4||E"""r#   
route_namehandlerc                 N     dt           dt           f fd}| ||          S |S )a  Register a handler for a specific route.

        Can be used as a decorator or called directly.

        Args:
            route_name: The name of the route to handle.
            handler: Optional handler function. If not provided,
                returns a decorator.

        Returns:
            The handler function, or a decorator if handler not provided.

        Example:
            ```python
            # As decorator
            @middleware.register_route_handler("greeting")
            async def handle_greeting(request, route_match):
                return {"content": "Hello!"}

            # Direct registration
            middleware.register_route_handler("greeting", handle_greeting)
            ```
        fnr   c                     | j         <   | S N)r   )rM   rJ   r    s    r"   	decoratorzBSemanticRouterMiddleware.register_route_handler.<locals>.decorator   s    /1D ,Ir#   )RouteHandler)r    rJ   rK   rP   s   ``  r"   register_route_handlerz/SemanticRouterMiddleware.register_route_handler   sS    6	, 	< 	 	 	 	 	 	 	 9W%%%r#   requestc                   K   |                                   d{V  t          |t                    r|                    dg           }nt	          |dg           }|                     |          }|s ||           d{V S d}	 |                     |          }n<# t          $ r/}| j        s t          
                    d|            Y d}~nd}~ww xY w||j        }|| j        v r1|t	          |dd          d} | j        |         ||           d{V S t          |t                    r.d|vri |d<   ||d         d<   t	          |dd          |d         d<    ||           d{V S )	aT  Wrap a model call with semantic routing.

        This method is part of the LangChain AgentMiddleware protocol.
        Determines the route based on the user's message and either:
        - Calls a registered route handler if one exists
        - Adds routing info to the request and calls the default handler

        Args:
            request: The model request containing messages.
            handler: The async function to call the model.

        Returns:
            The model response.

        Raises:
            Exception: If graceful_degradation is False and routing fails.
        Nr8   z Router failed, passing through: distance)r%   rU   runtimer5   route_distance)_ensure_initialized_asyncrA   rB   r0   rC   rF   rI   	Exception_graceful_degradationloggerwarningr%   r   )	r    rS   rK   r8   rG   route_matcherJ   
route_infos	            r"   awrap_model_callz)SemanticRouterMiddleware.awrap_model_call   s     , ,,......... gt$$ 	8{{:r22HHw
B77H##H-- 	* ))))))))) 	C//%00KK 	C 	C 	C- NNAaAABBBBBBBB	C
 "$)J T111& 'Z F F 
 >T1*=gzRRRRRRRRR '4(( G++)+GI&.8	"7+7>T8 8	"#34 WW%%%%%%%%%s   B 
C%%CCc                 (   K    ||           d{V S )av  Pass through tool calls without routing.

        This method is part of the LangChain AgentMiddleware protocol.
        Semantic router only applies to model calls, not tool calls.

        Args:
            request: The tool call request.
            handler: The async function to execute the tool.

        Returns:
            The tool result from the handler.
        Nr/   )r    rS   rK   s      r"   awrap_tool_callz(SemanticRouterMiddleware.awrap_tool_call	  s*      & WW%%%%%%%%%r#   )r   NrO   )__name__
__module____qualname____doc__r   __annotations__r   r   strrQ   r   r7   r   r	   rB   r   rF   r   rI   r   rR   r   r   r   r
   r`   r   LangChainToolMessager   rb   __classcell__)r!   s   @r"   r   r      s           D !!!!#|+,,,,"3 " " " " " " "*7 *7 *7 *7XtE$sCx.#2E,F'G C    6# # # # # # BF! !!(0(>!	<.,.	/! ! ! !F@&@& <.)M*BBC@& 
	@& @& @& @&D& & y/CW/L)MNN
& 
#W,	-& & & & & & & &r#   r   )$rf   loggingtypingr   r   r   r   r   r   r	   !langchain.agents.middleware.typesr
   r   r   langchain_core.messagesr   ri   langgraph.prebuilt.tool_noder   langgraph.typesr   redisvl.extensions.routerr   r    redisvl.extensions.router.schemar   aior   typesr   	getLoggerrc   r[   rh   rQ   r   r/   r#   r"   <module>rv      s}     H H H H H H H H H H H H H H H H H H         
 H G G G G G 8 8 8 8 8 8 # # # # # # C C C C C C C C 2 2 2 2 2 2 % % % % % % ' ' ' ' ' '		8	$	$ tCH~6	/8RRS}& }& }& }& }&3 }& }& }& }& }&r#   