
    kk i>                     2   U d dl Z d dlmZ d dlmZmZ d dlmZ d dlZd dl	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 d d	lmZ d d
lmZmZmZ d dlZd dlmZ  ee      j=                         j>                  Z  ee dz  d        e jB                  d      Z" e jB                  d      Z#da$edz  e%d<   i Z&ee'ef   e%d<   dZ( G d de      Z) edd      Z* e       Z+ e jB                  d      Z#e#s e,d        e-d       ee#dd      Z. ej^                  d ed       d! ed"       g      Z0d#e'd$efd%Z1e+je                  d&      d'e)fd(       Z3e*ji                  d)      d*        Z5d+ Z6e*jo                  e+       e8d,k(  r ejr                  e*d-d./       yy)0    N)Path)DictAny)load_dotenv)
ChatOpenAI)ChatPromptTemplateMessagesPlaceholder)AgentExecutorcreate_openai_functions_agent)ConversationBufferMemory)MultiServerMCPClient)FastAPIHTTPException	APIRouter)	BaseModelz.envT)overrideMCP_HTTP_URLOPENAI_API_KEYAGENT_EXECUTORCONVERSATION_MEMORIESz[ORDEN_CREADA_EXITOSAMENTE]c                   "    e Zd ZU eed<   eed<   y)UserMessagewhatsapp_idmensajeN)__name__
__module____qualname__str__annotations__     */opt/movilizia/ClienteMCP/mcp-movilizia.pyr   r   "   s    Lr!   r   zCLIENTE MCPz/mcp-movilizia)title	root_pathu>   ❌ ERROR: OPENAI_API_KEY no está definido en el archivo .envu3   OPENAI_API_KEY no está definido en el archivo .envzgpt-4og?)openai_api_keymodeltemperature)systemu  
    Eres el *Asistente de Transporte Rural LHIA*, un experto en la gestión de transporte de carga agropecuaria.
    Tu objetivo es ayudar al usuario a registrarse como transportista, eliminar su registro si ya no desea trabajar,
    o crear una orden de servicio para clientes.

    [INSTRUCCIÓN CLAVE DE ENTRADA]
    El mensaje de entrada, {input}, comenzará con el ID de WhatsApp del usuario envuelto en corchetes, así: *[593963709752] Mensaje del usuario*.
    *SIEMPRE* debes extraer el número de WhatsApp de estos corchetes para usarlo en las llamadas a herramientas.

    [REGLAS CLAVE DE CONVERSIÓN DE PESO Y CÁLCULO]
    *OBLIGATORIO:* Cualquier cantidad de peso o carga que obtengas del usuario debe ser convertida a su valor *decimal equivalente en toneladas (FLOAT)*.
    - Ejemplo: "2.5 toneladas" -> 2.5
    - Ejemplo: "media tonelada" -> 0.5
    - Ejemplo: "4000 libras" -> 1.814
    - Ejemplo: "500 kilogramos" -> 0.5

    *INFERENCIA DE PESO (CLAVE PARA ÓRDENES):* Si el usuario NO proporciona un peso explícito, debes estimar el peso total en toneladas según la descripción.
    - Ejemplo: "3 vacas" -> 0.5 ton/vaca * 3 = 1.5 ton.
    - *Prioridad:* Si el usuario da un peso explícito, usa el valor proporcionado.

    [REGLAS DE OPERACIÓN CONVERSACIONAL]

    0.  *Intención de Campaña (Máxima Prioridad):*
        * *Detección:* Si el usuario usa palabras como *"campaña"* o *"campañas"*, ignora otras intenciones.
        * *Acción:* Responde con:
          "*¡Claro! Las campañas de LHIA son iniciativas que lanzamos periódicamente para fomentar el transporte eficiente de productos agropecuarios. Actualmente no hay una activa, pero te avisaremos cuando lancemos la siguiente. ¿En qué otra cosa relacionada con transporte o registro de transportistas puedo ayudarte?*"
          *NO uses herramientas.*

    1.  *Intención de Registro (Transportista - Prioridad Alta):*
        * *Detección:* Si el usuario dice frases como:
          - "quiero registrarme"
          - "quiero ser transportista"
          - "soy transportista"
          - "quiero trabajar como transportista"
          Entonces asume intención de **registro**.
        * *Flujo de Lógica (con verificación previa):*
            1. **Primero**, usa la herramienta `find_transporter_by_whatsapp` con el número extraído.
            2. **Si ya existe un transportista**, responde amablemente:
               > "Ya estás registrado como transportista. No es necesario volver a hacerlo. En cuanto tengamos nuevas órdenes disponibles, te las enviaremos directamente."
               *NO continues pidiendo datos ni registres de nuevo.*
            3. **Si NO existe el transportista**, continúa el flujo normal de registro:
                - Pide su *Nombre completo*.
                - Luego la *Placa del vehículo*.
                - Luego la *Carga Máxima* (en toneladas, pero acepta conversiones).
            4. Cuando tengas todos los datos, usa `register_new_transporter`.
               - Respuesta final al usuario:  
                 > "Bienvenido a bordo, tu registro fue exitoso. En cuanto tengamos órdenes de transporte que coincidan con tus capacidades, te las enviaremos directamente."

    1.1 *Intención de Baja (Eliminación de Registro):*
        * *Detección:* Si el usuario dice frases como:
          - "ya no quiero ser transportista"
          - "ya no quiero trabajar"
          - "ya no quiero brindar mis servicios"
          - "ya no quiero recibir órdenes"
          Entonces asume intención de **eliminar su registro**.
        * *Flujo de Lógica (Eliminación):*
            1. Usa `find_transporter_by_whatsapp` para verificar si existe.
            2. **Si existe**, usa la herramienta `delete_transporter_by_whatsapp`.
               - Luego responde con:
                 > "Tu registro ha sido eliminado correctamente. Esperamos volver a trabajar contigo en el futuro."
            3. **Si NO existe**, responde:
                 > "Aún no estás registrado como transportista. Si deseas registrarte más adelante, puedo ayudarte con eso."

    2.  *Intención de Orden (Cliente - Por Defecto):*
        * *Detección:* Si el mensaje no es de campaña ni de registro, asume que es un cliente que busca transporte.
        * *Flujo Conversacional:*
            - Si es el primer mensaje, pregunta directamente:
              > "¿Qué necesitas transportar? Por favor, describe la carga con detalle para estimar el vehículo adecuado."
            - Recolecta de forma secuencial:
              1. Nombre del cliente.
              2. Descripción de la carga.
              3. Origen (con referencia precisa).
              4. Destino.
            - No repitas preguntas si el usuario ya ha dado la información.
            - Calcula internamente el `peso_aproximado` en toneladas.
            - Cuando tengas todos los datos, usa `register_new_order`.
            - Mensaje final:
              > "Tu solicitud de transporte ha sido registrada exitosamente. Pronto un transportista se pondrá en contacto contigo."

    [REGLAS DE LOGS Y NOTIFICACIÓN]
    - *Antes de llamar a cualquier herramienta*, genera un log en el `agent_scratchpad` mostrando los parámetros exactos.
    - Para registros o eliminaciones, incluye el número de WhatsApp en el log.
    - Para órdenes, asegúrate de incluir `carga` y `peso_aproximado` correctamente.
    - En la respuesta final al usuario, **nunca muestres JSON ni etiquetas internas**.

    [RESUMEN DE TOOLS DISPONIBLES]
    - `find_transporter_by_whatsapp(whatsapp: str)` → Busca transportista por WhatsApp.
    - `delete_transporter_by_whatsapp(whatsapp: str)` → Elimina transportista por WhatsApp.
    - `register_new_transporter(transportista, vehiculo)` → Registra nuevo transportista.
    - `register_new_order(orden)` → Crea nueva orden de transporte.

    history)variable_name)humanz{input}agent_scratchpadr   returnc                 h    | t         vr"t        d|         t        dd      t         | <   t         |    S )uG   Obtiene la memoria de conversación por 'whatsapp_id' o crea una nueva.u   ✨ Creando nueva memoria para r)   T)
memory_keyreturn_messages)r   printr   )r   s    r"   get_or_create_memoryr2      s>    ///}=>-E  .
k* !--r!   z/messageuser_messagec                 H  K   t         t        dd      | j                  }| j                  }t	        d       t	        d| d|        t        |      }d| d	| }	 |t         _        t	        d
|        t         j                  d|i       d{   }|d   }t	        d|j                         dd  d       t        |v r	 |j                  t        d      d   j                         }t        j                  d|t        j                        }|rz|j                  d      }	|j                  |	d      j                         }
t!        j"                  |	      }|j%                  d|j%                  dd            }t	        d| d|        n/t	        d       |j                  t        d      j                         }
n|}
t	        d| d|
dd  d       d||
d S 7 H# t         j&                  $ r<}t	        d|        |j                  t        d      j                         }
Y d}~hd}~wt(        $ r<}t	        d|        |j                  t        d      j                         }
Y d}~d}~ww xY w# t(        $ r%}d!| }t	        |       t        d"d#|       d}~ww xY ww)$ul   
    Endpoint para recibir mensajes entrantes de un usuario de WhatsApp.
    (Implementación con Logs)
    Ni  u#   El Asistente no está inicializado.)status_codedetailu   
🟡 LOG | Mensaje entranteu   👤 Usuario (z) dijo: [z] u$   🤖 Invocando al agente LHIA para: inputoutputu$   🤖 LOG | Respuesta cruda del LLM: d   ...   z(\{.*\})r    id_ordenidzN/Au3   🔔 NOTIFICACIÓN: Orden creada exitosamente para z. ID: uu   ⚠️ Advertencia: Etiqueta de orden encontrada, pero JSON no pudo ser extraído. Enviando solo la respuesta limpia.u6   ❌ ERROR de JSON al parsear la respuesta del agente: u<   ❌ ERROR inesperado al procesar la notificación de orden: u%   ✅ LOG | Enviando respuesta final a z. Contenido: 2   success)statusr   mensaje_respuestau=   ❌ ERROR INTERNO: Ocurrió un error al procesar el mensaje: i  z6Error interno del asistente al procesar la solicitud: )r   r   r   r   r1   r2   memoryainvokestripNOTIFICATION_TAGsplitresearchDOTALLgroupreplacejsonloadsgetJSONDecodeError	Exception)r3   
user_inputr   rD   	llm_inputresultassistant_responsejson_and_response
json_match	json_textclean_response
order_dataorder_ide	error_msgs                  r"   receive_messager_      s     4YZZ%%J**K 
)+	N;-x
|
<= "+.FK=:,/IA
 &4YK@A%--w	.BCC#H- 	45G5M5M5OPTQT5U4VVYZ[ 11Z$6$<$<=Mq$QRS$T$Z$Z$\!  YY{4EryyQ
 * 0 0 3I%6%>%>y"%M%S%S%UN "&I!6J)~~j*..u:UVHOP[}\bckblmn   R  S%7%?%?@PRT%U%[%[%]N 0N 	5k]-P^_b`bPcOddghi&!/
 	
_ D@ '' ZNqcRS!3!;!;<Lb!Q!W!W!Y ZTUVTWXY!3!;!;<Lb!Q!W!W!YZ"  
STUSVW	iKA3O
 	
	
sy   AJ"3I1 G1I1 C7G ;I1 J"I1 I./2H&!I1 &I.22I)$I1 )I..I1 1	J: JJJ"startupc                  4   K   t                d{    y7 w)z3Ejecuta setup_agent al inicio del servidor FastAPI.N)setup_agentr    r!   r"   startup_eventrc     s      -s   c                    K   t        dt         d       	 t        dt        ddi      } | j                          d{   }t        d|D cg c]  }|j                   c}        t        t        |t        
      }t        ||dd      a
t        d       t        d       y7 fc c}w # t
        $ r}t        d|        t        d	        d}~ww xY ww)zEInicializa el agente y las herramientas antes de iniciar el servidor.u)   
🔌 Conectando al MCP de Transporte en r;   
transportestreamable_http)url	transportNu   ✅ Herramientas detectadas: u)   ❌ ERROR: Error al conectar con el MCP: uH   💡 Verifica que el servidor Spring AI MCP esté corriendo y accesible.)llmtoolspromptT)agentrj   rD   verboseuI   
🤖 Asistente de Transporte Rural LHIA listo para recibir mensajes 🚚u@   📢 Servidor escuchando en http://localhost:8003/mcp-movilizia/)r1   MCP_URLr   	get_toolsnamerR   r   ri   rk   r
   r   )transporte_mcp_clientrj   toolr]   rl   s        r"   rb   rb     s      
6wis
CD 4.6
 ! ,5577-U.KTtyy.K-LMN *E #	N 

VW	
LM+ 8.K 9!=>XYsJ   C'B, B%B, B'
 	B, )<C%B, 'B, ,	C5CCC__main__z0.0.0.0iC  )hostport):ospathlibr   typingr   r   dotenvr   rN   rI   langchain_openair   langchain_core.promptsr   r	   langchain.agentsr
   r   langchain.memoryr   langchain_mcp_adapters.clientr   fastapir   r   r   uvicornpydanticr   __file__resolveparentBASE_DIRgetenvrn   
openai_keyr   r   r   r   rG   r   approuterr1   
ValueErrorri   from_messagesrk   r2   postr_   on_eventrc   rb   include_routerr   runr    r!   r"   <module>r      s   	     	 ' J I 5 > 5 5  
 >!!#** Hv - "))N
#RYY'(
 (,$ +=? tC!99: ?0 ) 
 M-=>	
 RYY'(
	
JK
J
KK
 
*		)	)[	x i0&89`+ `
N.c ..F . ZW
 W
 W
| i 
"NH   6  zGKK. r!   