feat: add agents/app
This commit is contained in:
		
							
								
								
									
										35
									
								
								agents/app/langgraph_tools/tools/catalog/catalog_class.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								agents/app/langgraph_tools/tools/catalog/catalog_class.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| class CatalogTools: | ||||
|     def list_products(self): | ||||
|         """ | ||||
|         Lista todos los productos disponibles en el catálogo con su disponibilidad | ||||
|         """ | ||||
|  | ||||
|         pass | ||||
|  | ||||
|     def search_products(self): | ||||
|         """ | ||||
|         Busca productos en el catálogo que coincidan con la consulta | ||||
|         """ | ||||
|         pass | ||||
|  | ||||
|     def check_price(self): | ||||
|         """ | ||||
|         Verifica el precio de un producto específico | ||||
|         """ | ||||
|         pass | ||||
|  | ||||
|     def check_availability(self): | ||||
|         """ | ||||
|         Verifica la disponibilidad de un producto espécifico | ||||
|         """ | ||||
|         pass | ||||
|  | ||||
|     def get_product_details(self): | ||||
|         """ | ||||
|         Obtiene detalles completos de un producto | ||||
|         """ | ||||
|         pass | ||||
|  | ||||
|     def list_tools(self): | ||||
|         """Retorna los metodos de esta clase en una lista""" | ||||
|         pass | ||||
							
								
								
									
										190
									
								
								agents/app/langgraph_tools/tools/catalog/catalog_tools.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								agents/app/langgraph_tools/tools/catalog/catalog_tools.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,190 @@ | ||||
| from langchain_core.tools import tool | ||||
| from typing import List, Dict, Optional | ||||
| from app.seller.catalog_tools import CatalogTrytonTools | ||||
| import json | ||||
| import requests | ||||
|  | ||||
|  | ||||
| url = "http://192.168.0.25:8000" | ||||
| key = "9a9ffc430146447d81e6698240199a4be2b0e774cb18474999d0f60e33b5b1eb1cfff9d9141346a98844879b5a9e787489c891ddc8fb45cc903b7244cab64fb1" | ||||
| db = "tryton" | ||||
| application_name = "sale_don_confiao" | ||||
|  | ||||
|  | ||||
| catalog = CatalogTrytonTools(url, application_name, key, db) | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def list_products() -> str: | ||||
|     """ | ||||
|     Lista todos los productos disponibles en el catálogo con su disponibilidad | ||||
|     """ | ||||
|     response = catalog.list_products() | ||||
|  | ||||
|     products: str = "" | ||||
|  | ||||
|     for product in response: | ||||
|         id = product["id"] | ||||
|         name = product["name"] | ||||
|         id_unit_measurement = product["default_uom."]["id"] | ||||
|         products += f"- id: {id} - Nombre: {name} - ID Unidad de Medida: {id_unit_measurement} \n" | ||||
|  | ||||
|     return products | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def search_products(product_name: str) -> str: | ||||
|     """ | ||||
|     Busca productos en el catálogo que coincidan con el nombre del producto proporcionado. | ||||
|  | ||||
|     Parámetros: | ||||
|     - product_name (str): El nombre del producto a buscar. | ||||
|  | ||||
|     Devuelve: | ||||
|     - str: Una cadena de texto que lista los IDs, nombres y precios de los productos encontrados, | ||||
|            formateada con cada producto en una línea separada. | ||||
|     """ | ||||
|  | ||||
|     response = catalog.search_products(product_name) | ||||
|     precios: str = "" | ||||
|  | ||||
|     for product in response: | ||||
|         id = product["id"] | ||||
|         name = product["name"] | ||||
|         precio = product["template."]["list_price"]["decimal"] | ||||
|         id_unit_measurement = product["default_uom."]["id"] | ||||
|  | ||||
|         precios += f"- id: {id} - Nombre: {name}: ${precio} - ID Unidad de Medida: {id_unit_measurement}\n" | ||||
|          | ||||
|  | ||||
|     return precios | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def check_price(product_name: str) -> str: | ||||
|     """ | ||||
|     Verifica el precio de un producto específico | ||||
|     """ | ||||
|     response = catalog.search_products(product_name) | ||||
|     precios: str = "" | ||||
|  | ||||
|     for product in response: | ||||
|         id = product["id"] | ||||
|         name = product["name"] | ||||
|         precio = product["template."]["list_price"]["decimal"] | ||||
|         id_unit_measurement = product["default_uom."]["id"] | ||||
|  | ||||
|         precios += f"- id: {id} - Nombre: {name}: ${precio} - ID Unidad de Medida: {id_unit_measurement}\n" | ||||
|  | ||||
|     return precios | ||||
|  | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def check_availability(product_name: str) -> str: | ||||
|     """ | ||||
|     Verifica la disponibilidad de un producto específico | ||||
|     """ | ||||
|     response = catalog.search_products("Papa") | ||||
|     disponibilidad: str = "" | ||||
|  | ||||
|     for product in response: | ||||
|         id = product["id"] | ||||
|         name = product["name"] | ||||
|         stock = product["quantity"] | ||||
|         id_unit_measurement = product["default_uom."]["id"] | ||||
|  | ||||
|         disponibilidad += f"- id: {id} - Nombre: {name} - Stock: {stock} - ID Unidad de Medida: {id_unit_measurement}\n" | ||||
|  | ||||
|     return disponibilidad | ||||
|  | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def get_product_details(product_name: str) -> str: | ||||
|     """ | ||||
|     Obtiene los detalles completos de un producto | ||||
|     """ | ||||
|     products = CatalogManager.search_products(product_name) | ||||
|     if products: | ||||
|         product = products[0]  # Tomar el primer producto que coincida | ||||
|         stock_status = "En stock" if product["stock"] > 0 else "Agotado" | ||||
|         return f""" | ||||
| Detalles del producto: | ||||
| - Producto: {product['producto']} | ||||
| - Categoría: {product['categoria']} | ||||
| - Unidad de medida: {product['unidad']} | ||||
| - id unidad de medida: {product["default_uom."]["id"]} | ||||
| - Precio: ${product['precio']:,} | ||||
| - Estado: {stock_status} ({product['stock']} unidades) | ||||
| """ | ||||
|     return f"No se encontró el producto '{product_name}' en nuestro catálogo." | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def list_category_products(category_name: str) -> str: | ||||
|     """ | ||||
|     Lista todos los productos de una categoría específica | ||||
|     """ | ||||
|     products = CatalogManager.search_products(category_name) | ||||
|     if not products: | ||||
|         return f"No se encontraron productos en la categoría '{category_name}'." | ||||
|  | ||||
|     result = f"Productos en la categoría {category_name}:\n" | ||||
|     for product in products: | ||||
|         if product["categoria"] == category_name: | ||||
|             stock_status = " Disponible" if product["stock"] > 0 else " Agotado" | ||||
|             result += f"- {product['producto']} | {product['unidad']} | ${product['precio']:,} | {stock_status}\n" | ||||
|  | ||||
|     return result | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def get_low_stock_products(threshold: int = 10) -> str: | ||||
|     """ | ||||
|     Lista productos con stock bajo (por defecto, menos de 10 unidades) | ||||
|     """ | ||||
|     products = CatalogManager.list_all_products() | ||||
|     low_stock = [p for p in products if p["stock"] <= threshold] | ||||
|  | ||||
|     if not low_stock: | ||||
|         return f"No hay productos con stock menor a {threshold} unidades." | ||||
|  | ||||
|     result = f"Productos con stock bajo (menos de {threshold} unidades):\n" | ||||
|     for product in low_stock: | ||||
|         result += f"- {product['producto']}: {product['stock']} {product['unidad']}(s) restantes\n" | ||||
|  | ||||
|     return result | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def get_products_by_price_range(min_price: float, max_price: float) -> str: | ||||
|     """ | ||||
|     Busca productos dentro de un rango de precios específico | ||||
|     """ | ||||
|     products = CatalogManager.list_all_products() | ||||
|     filtered_products = [p for p in products if min_price <= p["precio"] <= max_price] | ||||
|  | ||||
|     if not filtered_products: | ||||
|         return f"No se encontraron productos entre ${min_price:,} y ${max_price:,}." | ||||
|  | ||||
|     result = f"Productos entre ${min_price:,} y ${max_price:,}:\n" | ||||
|     for product in filtered_products: | ||||
|         result += ( | ||||
|             f"- {product['producto']} | {product['unidad']} | ${product['precio']:,}\n" | ||||
|         ) | ||||
|  | ||||
|     return result | ||||
|  | ||||
|  | ||||
| # Lista de todas las herramientas disponibles | ||||
| tools = [ | ||||
|     list_products, | ||||
|     search_products, | ||||
|     check_price, | ||||
|     check_availability, | ||||
|     # get_product_details, | ||||
|     # list_category_products, | ||||
|     # get_low_stock_products, | ||||
|     # get_products_by_price_range, | ||||
| ] | ||||
							
								
								
									
										66
									
								
								agents/app/langgraph_tools/tools/general_info.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								agents/app/langgraph_tools/tools/general_info.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| import yaml | ||||
| from langchain_core.tools import tool | ||||
| from datetime import datetime | ||||
| import pytz | ||||
| import os | ||||
|  | ||||
| # Obtener la ruta del directorio actual | ||||
| current_dir = os.path.dirname(os.path.abspath(__file__)) | ||||
| yaml_path = os.path.join(current_dir, "store_info.yaml") | ||||
|  | ||||
| with open(yaml_path, "r") as f: | ||||
|     STORE_INFO = yaml.safe_load(f) | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def get_store_hours() -> str: | ||||
|     """Obtiene el horario de la tienda""" | ||||
|     return STORE_INFO["store"]["hours"] | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def get_store_location() -> str: | ||||
|     """Obtiene la ubicación de la tienda""" | ||||
|     return STORE_INFO["store"]["location"] | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def get_contact_info() -> dict: | ||||
|     """Obtiene la información de contacto""" | ||||
|     return STORE_INFO["store"]["contact"] | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def get_about_info() -> str: | ||||
|     """Obtiene el about de la tienda""" | ||||
|     return STORE_INFO["store"]["about"] | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def get_link_page() -> str: | ||||
|     """Obtiene la pagina web de la tienda""" | ||||
|     return STORE_INFO["store"]["page"] | ||||
|  | ||||
|  | ||||
| def get_time(): | ||||
|     """ | ||||
|     Retorna la hora actual en Bogotá, Colombia. | ||||
|     """ | ||||
|     # Definir la zona horaria de Bogotá | ||||
|     bogota_tz = pytz.timezone("America/Bogota") | ||||
|  | ||||
|     # Obtener la hora actual en Bogotá | ||||
|     hora_actual = datetime.now(bogota_tz) | ||||
|  | ||||
|     # Formatear la hora en un formato legible | ||||
|     return hora_actual.strftime("%H:%M:%S") | ||||
|  | ||||
|  | ||||
| tools = [ | ||||
|     get_store_hours, | ||||
|     get_store_location, | ||||
|     get_contact_info, | ||||
|     get_about_info, | ||||
|     get_link_page, | ||||
|     get_time, | ||||
| ] | ||||
							
								
								
									
										476
									
								
								agents/app/langgraph_tools/tools/orders/db_manager.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										476
									
								
								agents/app/langgraph_tools/tools/orders/db_manager.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,476 @@ | ||||
| import sqlite3 | ||||
| import uuid | ||||
| import os | ||||
| import re | ||||
| from datetime import datetime | ||||
| from typing import List, Dict, Optional, Tuple | ||||
| from contextlib import contextmanager | ||||
|  | ||||
| # Construir la ruta de la base de datos de manera más robusta | ||||
| current_dir = os.path.dirname(os.path.abspath(__file__)) | ||||
| app_dir = os.path.abspath(os.path.join(current_dir, "..", "..", "..")) | ||||
| DATABASE_PATH = os.path.join(app_dir, "data", "orders.db") | ||||
|  | ||||
| # Estados válidos para órdenes | ||||
| ORDER_STATES = ['in_cart', 'confirmed', 'processing', 'ready', 'delivering', 'delivered', 'cancelled'] | ||||
| DELIVERY_STATES = ['pending', 'assigned', 'in_transit', 'delivered', 'failed'] | ||||
|  | ||||
| def validate_phone(phone: str) -> bool: | ||||
|     """Valida el formato del número de teléfono""" | ||||
|     phone_pattern = re.compile(r'^\+?1?\d{9,15}$') | ||||
|     return bool(phone_pattern.match(phone)) | ||||
|  | ||||
| def init_database(): | ||||
|     """Inicializa la base de datos de pedidos""" | ||||
|     os.makedirs(os.path.dirname(DATABASE_PATH), exist_ok=True) | ||||
|  | ||||
|     conn = sqlite3.connect(DATABASE_PATH) | ||||
|     cursor = conn.cursor() | ||||
|  | ||||
|     # Crear tabla de carritos/pedidos con nuevos campos | ||||
|     cursor.execute(""" | ||||
|     CREATE TABLE IF NOT EXISTS orders ( | ||||
|         order_id TEXT PRIMARY KEY, | ||||
|         phone TEXT NOT NULL, | ||||
|         status TEXT NOT NULL, | ||||
|         total REAL DEFAULT 0, | ||||
|         delivery_address TEXT, | ||||
|         delivery_status TEXT, | ||||
|         payment_method TEXT, | ||||
|         discount_applied REAL DEFAULT 0, | ||||
|         notes TEXT, | ||||
|         created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, | ||||
|         updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP | ||||
|     ) | ||||
|     """) | ||||
|  | ||||
|     # Crear tabla de items del pedido con unidad de medida | ||||
|     cursor.execute(""" | ||||
|     CREATE TABLE IF NOT EXISTS order_items ( | ||||
|         id INTEGER PRIMARY KEY AUTOINCREMENT, | ||||
|         order_id TEXT NOT NULL, | ||||
|         product_id TEXT NOT NULL, | ||||
|         quantity INTEGER NOT NULL, | ||||
|         price REAL NOT NULL, | ||||
|         unit TEXT NOT NULL, | ||||
|         FOREIGN KEY (order_id) REFERENCES orders (order_id), | ||||
|         UNIQUE(order_id, product_id) | ||||
|     ) | ||||
|     """) | ||||
|  | ||||
|     conn.commit() | ||||
|     conn.close() | ||||
|  | ||||
| @contextmanager | ||||
| def get_db_connection(): | ||||
|     """Contexto para manejar la conexión a la base de datos""" | ||||
|     conn = sqlite3.connect(DATABASE_PATH) | ||||
|     conn.row_factory = sqlite3.Row | ||||
|     try: | ||||
|         yield conn | ||||
|     finally: | ||||
|         conn.close() | ||||
|  | ||||
| class OrderManager: | ||||
|     @staticmethod | ||||
|     def get_active_cart(phone: str) -> Optional[str]: | ||||
|         """Obtiene el carrito activo de un cliente o crea uno nuevo""" | ||||
|         if not validate_phone(phone): | ||||
|             raise ValueError("Número de teléfono inválido") | ||||
|  | ||||
|         with get_db_connection() as conn: | ||||
|             cursor = conn.cursor() | ||||
|             cursor.execute( | ||||
|                 """ | ||||
|                 SELECT order_id FROM orders  | ||||
|                 WHERE phone = ? AND status = 'in_cart' | ||||
|                 ORDER BY created_at DESC LIMIT 1 | ||||
|             """, | ||||
|                 (phone,), | ||||
|             ) | ||||
|             result = cursor.fetchone() | ||||
|  | ||||
|             if result: | ||||
|                 return result["order_id"] | ||||
|  | ||||
|             order_id = str(uuid.uuid4()) | ||||
|             cursor.execute( | ||||
|                 """ | ||||
|                 INSERT INTO orders (order_id, phone, status) | ||||
|                 VALUES (?, ?, 'in_cart') | ||||
|             """, | ||||
|                 (order_id, phone), | ||||
|             ) | ||||
|             conn.commit() | ||||
|             return order_id | ||||
|  | ||||
|     @staticmethod | ||||
|     def add_to_cart(phone: str, product_id: str, quantity: int, price: float, unit: str) -> bool: | ||||
|         """Añade un producto al carrito del cliente""" | ||||
|         try: | ||||
|             order_id = OrderManager.get_active_cart(phone) | ||||
|             with get_db_connection() as conn: | ||||
|                 cursor = conn.cursor() | ||||
|                  | ||||
|                 # Verificar si el producto ya está en el carrito | ||||
|                 cursor.execute( | ||||
|                     """ | ||||
|                     SELECT quantity FROM order_items  | ||||
|                     WHERE order_id = ? AND product_id = ? | ||||
|                 """, | ||||
|                     (order_id, product_id), | ||||
|                 ) | ||||
|                 existing_item = cursor.fetchone() | ||||
|  | ||||
|                 if existing_item: | ||||
|                     # Actualizar cantidad si ya existe | ||||
|                     cursor.execute( | ||||
|                         """ | ||||
|                         UPDATE order_items  | ||||
|                         SET quantity = quantity + ?,  | ||||
|                             price = ? | ||||
|                         WHERE order_id = ? AND product_id = ? | ||||
|                     """, | ||||
|                         (quantity, price, order_id, product_id), | ||||
|                     ) | ||||
|                 else: | ||||
|                     # Insertar nuevo item | ||||
|                     cursor.execute( | ||||
|                         """ | ||||
|                         INSERT INTO order_items (order_id, product_id, quantity, price, unit) | ||||
|                         VALUES (?, ?, ?, ?, ?) | ||||
|                     """, | ||||
|                         (order_id, product_id, quantity, price, unit), | ||||
|                     ) | ||||
|  | ||||
|                 # Actualizar total del carrito | ||||
|                 cursor.execute( | ||||
|                     """ | ||||
|                     UPDATE orders  | ||||
|                     SET total = ( | ||||
|                         SELECT SUM(quantity * price)  | ||||
|                         FROM order_items  | ||||
|                         WHERE order_id = ? | ||||
|                     ) | ||||
|                     WHERE order_id = ? | ||||
|                 """, | ||||
|                     (order_id, order_id), | ||||
|                 ) | ||||
|  | ||||
|                 conn.commit() | ||||
|                 return True | ||||
|         except Exception as e: | ||||
|             print(f"Error adding to cart: {e}") | ||||
|             return False | ||||
|  | ||||
|     @staticmethod | ||||
|     def remove_from_cart(phone: str, product_id: str) -> bool: | ||||
|         """Elimina un producto del carrito""" | ||||
|         try: | ||||
|             order_id = OrderManager.get_active_cart(phone) | ||||
|             with get_db_connection() as conn: | ||||
|                 cursor = conn.cursor() | ||||
|                 cursor.execute( | ||||
|                     """ | ||||
|                     DELETE FROM order_items  | ||||
|                     WHERE order_id = ? AND product_id = ? | ||||
|                 """, | ||||
|                     (order_id, product_id), | ||||
|                 ) | ||||
|  | ||||
|                 # Actualizar total | ||||
|                 cursor.execute( | ||||
|                     """ | ||||
|                     UPDATE orders  | ||||
|                     SET total = ( | ||||
|                         SELECT SUM(quantity * price)  | ||||
|                         FROM order_items  | ||||
|                         WHERE order_id = ? | ||||
|                     ) | ||||
|                     WHERE order_id = ? | ||||
|                 """, | ||||
|                     (order_id, order_id), | ||||
|                 ) | ||||
|  | ||||
|                 conn.commit() | ||||
|                 return True | ||||
|         except Exception as e: | ||||
|             print(f"Error removing from cart: {e}") | ||||
|             return False | ||||
|  | ||||
|     @staticmethod | ||||
|     def get_cart_items(phone: str) -> List[Dict]: | ||||
|         """Obtiene los items en el carrito del cliente""" | ||||
|         order_id = OrderManager.get_active_cart(phone) | ||||
|         with get_db_connection() as conn: | ||||
|             cursor = conn.cursor() | ||||
|             cursor.execute( | ||||
|                 """ | ||||
|                 SELECT * FROM order_items  | ||||
|                 WHERE order_id = ? | ||||
|             """, | ||||
|                 (order_id,), | ||||
|             ) | ||||
|             return [dict(row) for row in cursor.fetchall()] | ||||
|  | ||||
|     @staticmethod | ||||
|     def confirm_order(phone: str, delivery_address: Optional[str] = None,  | ||||
|                      payment_method: Optional[str] = None, notes: Optional[str] = None) -> Tuple[bool, str]: | ||||
|         """Confirma un pedido y lo marca como confirmado""" | ||||
|         try: | ||||
|             order_id = OrderManager.get_active_cart(phone) | ||||
|             with get_db_connection() as conn: | ||||
|                 cursor = conn.cursor() | ||||
|                  | ||||
|                 # Verificar que hay items en el carrito | ||||
|                 cursor.execute("SELECT COUNT(*) FROM order_items WHERE order_id = ?", (order_id,)) | ||||
|                 if cursor.fetchone()[0] == 0: | ||||
|                     return False, "El carrito está vacío" | ||||
|  | ||||
|                 # Actualizar estado de la orden | ||||
|                 cursor.execute( | ||||
|                     """ | ||||
|                     UPDATE orders  | ||||
|                     SET status = 'confirmed', | ||||
|                         delivery_address = ?, | ||||
|                         payment_method = ?, | ||||
|                         notes = ?, | ||||
|                         delivery_status = 'pending', | ||||
|                         updated_at = CURRENT_TIMESTAMP | ||||
|                     WHERE order_id = ? | ||||
|                 """, | ||||
|                     (delivery_address, payment_method, notes, order_id), | ||||
|                 ) | ||||
|  | ||||
|                 conn.commit() | ||||
|                 return True, order_id | ||||
|         except Exception as e: | ||||
|             print(f"Error confirming order: {e}") | ||||
|             return False, str(e) | ||||
|  | ||||
|     @staticmethod | ||||
|     def get_order_history(phone: str) -> List[Dict]: | ||||
|         """Obtiene el historial de pedidos del cliente""" | ||||
|         with get_db_connection() as conn: | ||||
|             cursor = conn.cursor() | ||||
|             cursor.execute( | ||||
|                 """ | ||||
|                 SELECT o.*,  | ||||
|                        COUNT(oi.id) as item_count, | ||||
|                        GROUP_CONCAT(oi.quantity || 'x ' || oi.product_id) as items | ||||
|                 FROM orders o | ||||
|                 LEFT JOIN order_items oi ON o.order_id = oi.order_id | ||||
|                 WHERE o.phone = ? AND o.status != 'in_cart' | ||||
|                 GROUP BY o.order_id | ||||
|                 ORDER BY o.created_at DESC | ||||
|             """, | ||||
|                 (phone,), | ||||
|             ) | ||||
|             return [dict(row) for row in cursor.fetchall()] | ||||
|  | ||||
|     @staticmethod | ||||
|     def update_delivery_status(order_id: str, status: str) -> bool: | ||||
|         """Actualiza el estado de entrega de una orden""" | ||||
|         if status not in DELIVERY_STATES: | ||||
|             raise ValueError(f"Estado de entrega inválido. Debe ser uno de: {', '.join(DELIVERY_STATES)}") | ||||
|          | ||||
|         try: | ||||
|             with get_db_connection() as conn: | ||||
|                 cursor = conn.cursor() | ||||
|                 cursor.execute( | ||||
|                     """ | ||||
|                     UPDATE orders  | ||||
|                     SET delivery_status = ?, | ||||
|                         updated_at = CURRENT_TIMESTAMP | ||||
|                     WHERE order_id = ? | ||||
|                 """, | ||||
|                     (status, order_id), | ||||
|                 ) | ||||
|                 conn.commit() | ||||
|                 return True | ||||
|         except Exception as e: | ||||
|             print(f"Error updating delivery status: {e}") | ||||
|             return False | ||||
|  | ||||
|     @staticmethod | ||||
|     def apply_discount(order_id: str, discount_amount: float) -> bool: | ||||
|         """Aplica un descuento al total de la orden""" | ||||
|         try: | ||||
|             with get_db_connection() as conn: | ||||
|                 cursor = conn.cursor() | ||||
|                 cursor.execute( | ||||
|                     """ | ||||
|                     UPDATE orders  | ||||
|                     SET discount_applied = ?, | ||||
|                         total = (SELECT SUM(quantity * price) FROM order_items WHERE order_id = ?) - ?, | ||||
|                         updated_at = CURRENT_TIMESTAMP | ||||
|                     WHERE order_id = ? | ||||
|                 """, | ||||
|                     (discount_amount, order_id, discount_amount, order_id), | ||||
|                 ) | ||||
|                 conn.commit() | ||||
|                 return True | ||||
|         except Exception as e: | ||||
|             print(f"Error applying discount: {e}") | ||||
|             return False | ||||
|  | ||||
|     @staticmethod | ||||
|     def get_order_details(order_id: str) -> Optional[Dict]: | ||||
|         """Obtiene los detalles completos de una orden""" | ||||
|         with get_db_connection() as conn: | ||||
|             cursor = conn.cursor() | ||||
|             cursor.execute("SELECT * FROM orders WHERE order_id = ?", (order_id,)) | ||||
|             order = cursor.fetchone() | ||||
|              | ||||
|             if not order: | ||||
|                 return None | ||||
|                  | ||||
|             cursor.execute("SELECT * FROM order_items WHERE order_id = ?", (order_id,)) | ||||
|             items = cursor.fetchall() | ||||
|              | ||||
|             order_dict = dict(order) | ||||
|             order_dict['items'] = [dict(item) for item in items] | ||||
|             return order_dict | ||||
|  | ||||
|     @staticmethod | ||||
|     def merge_orders(phone: str, order_ids: List[str], new_address: Optional[str] = None) -> Tuple[bool, str]: | ||||
|         """Crea un nuevo pedido combinando los productos de varios pedidos""" | ||||
|         try: | ||||
|             with get_db_connection() as conn: | ||||
|                 cursor = conn.cursor() | ||||
|                  | ||||
|                 # Verificar que todos los pedidos existen y son del mismo cliente | ||||
|                 for order_id in order_ids: | ||||
|                     cursor.execute( | ||||
|                         "SELECT phone, status FROM orders WHERE order_id = ?", | ||||
|                         (order_id,) | ||||
|                     ) | ||||
|                     order = cursor.fetchone() | ||||
|                     if not order or order[0] != phone: | ||||
|                         return False, f"El pedido {order_id} no existe o no pertenece a este cliente" | ||||
|                     if order[1] not in ['in_cart', 'confirmed']: | ||||
|                         return False, f"El pedido {order_id} no se puede modificar en su estado actual" | ||||
|  | ||||
|                 # Crear nuevo pedido | ||||
|                 new_order_id = str(uuid.uuid4()) | ||||
|                 cursor.execute( | ||||
|                     """ | ||||
|                     INSERT INTO orders (order_id, phone, status, delivery_address) | ||||
|                     VALUES (?, ?, 'confirmed', ?) | ||||
|                     """, | ||||
|                     (new_order_id, phone, new_address) | ||||
|                 ) | ||||
|  | ||||
|                 # Copiar items de los pedidos originales | ||||
|                 for order_id in order_ids: | ||||
|                     cursor.execute( | ||||
|                         """ | ||||
|                         INSERT INTO order_items (order_id, product_id, quantity, price, unit) | ||||
|                         SELECT ?, product_id, quantity, price, unit | ||||
|                         FROM order_items WHERE order_id = ? | ||||
|                         """, | ||||
|                         (new_order_id, order_id) | ||||
|                     ) | ||||
|  | ||||
|                 # Actualizar total | ||||
|                 cursor.execute( | ||||
|                     """ | ||||
|                     UPDATE orders  | ||||
|                     SET total = ( | ||||
|                         SELECT SUM(quantity * price)  | ||||
|                         FROM order_items  | ||||
|                         WHERE order_id = ? | ||||
|                     ) | ||||
|                     WHERE order_id = ? | ||||
|                     """, | ||||
|                     (new_order_id, new_order_id) | ||||
|                 ) | ||||
|  | ||||
|                 conn.commit() | ||||
|                 return True, new_order_id | ||||
|         except Exception as e: | ||||
|             print(f"Error merging orders: {e}") | ||||
|             return False, "Error al combinar los pedidos" | ||||
|  | ||||
|     @staticmethod | ||||
|     def delete_order(phone: str, order_id: str) -> bool: | ||||
|         """Elimina un pedido si está en estado permitido""" | ||||
|         try: | ||||
|             with get_db_connection() as conn: | ||||
|                 cursor = conn.cursor() | ||||
|                  | ||||
|                 # Verificar propiedad y estado del pedido | ||||
|                 cursor.execute( | ||||
|                     "SELECT status FROM orders WHERE order_id = ? AND phone = ?", | ||||
|                     (order_id, phone) | ||||
|                 ) | ||||
|                 order = cursor.fetchone() | ||||
|                  | ||||
|                 if not order: | ||||
|                     return False | ||||
|                  | ||||
|                 if order[0] not in ['in_cart', 'confirmed']: | ||||
|                     return False | ||||
|                  | ||||
|                 # Eliminar items y pedido | ||||
|                 cursor.execute("DELETE FROM order_items WHERE order_id = ?", (order_id,)) | ||||
|                 cursor.execute("DELETE FROM orders WHERE order_id = ?", (order_id,)) | ||||
|                  | ||||
|                 conn.commit() | ||||
|                 return True | ||||
|         except Exception as e: | ||||
|             print(f"Error deleting order: {e}") | ||||
|             return False | ||||
|  | ||||
|     @staticmethod | ||||
|     def modify_order_items(phone: str, order_id: str, items: List[Dict]) -> bool: | ||||
|         """Modifica los items de un pedido existente""" | ||||
|         try: | ||||
|             with get_db_connection() as conn: | ||||
|                 cursor = conn.cursor() | ||||
|                  | ||||
|                 # Verificar propiedad y estado del pedido | ||||
|                 cursor.execute( | ||||
|                     "SELECT status FROM orders WHERE order_id = ? AND phone = ?", | ||||
|                     (order_id, phone) | ||||
|                 ) | ||||
|                 order = cursor.fetchone() | ||||
|                  | ||||
|                 if not order or order[0] not in ['in_cart', 'confirmed']: | ||||
|                     return False | ||||
|                  | ||||
|                 # Eliminar items actuales | ||||
|                 cursor.execute("DELETE FROM order_items WHERE order_id = ?", (order_id,)) | ||||
|                  | ||||
|                 # Insertar nuevos items | ||||
|                 for item in items: | ||||
|                     cursor.execute( | ||||
|                         """ | ||||
|                         INSERT INTO order_items (order_id, product_id, quantity, price, unit) | ||||
|                         VALUES (?, ?, ?, ?, ?) | ||||
|                         """, | ||||
|                         (order_id, item['product_id'], item['quantity'], item['price'], item['unit']) | ||||
|                     ) | ||||
|                  | ||||
|                 # Actualizar total | ||||
|                 cursor.execute( | ||||
|                     """ | ||||
|                     UPDATE orders  | ||||
|                     SET total = ( | ||||
|                         SELECT SUM(quantity * price)  | ||||
|                         FROM order_items  | ||||
|                         WHERE order_id = ? | ||||
|                     ) | ||||
|                     WHERE order_id = ? | ||||
|                     """, | ||||
|                     (order_id, order_id) | ||||
|                 ) | ||||
|                  | ||||
|                 conn.commit() | ||||
|                 return True | ||||
|         except Exception as e: | ||||
|             print(f"Error modifying order: {e}") | ||||
|             return False | ||||
|  | ||||
| # Inicializar la base de datos al importar el módulo | ||||
| init_database() | ||||
							
								
								
									
										370
									
								
								agents/app/langgraph_tools/tools/orders/order_tools.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										370
									
								
								agents/app/langgraph_tools/tools/orders/order_tools.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,370 @@ | ||||
| from langchain_core.tools import tool | ||||
| from typing import Optional, List | ||||
| from .db_manager import OrderManager | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def add_to_cart(phone: str, product_id: str, quantity: int) -> str: | ||||
|     """ | ||||
|     Añade un producto al carrito del cliente. | ||||
|     Args: | ||||
|         phone: Número de teléfono del cliente | ||||
|         product_id: ID o nombre del producto | ||||
|         quantity: Cantidad a añadir | ||||
|     """ | ||||
|     # Primero intentar obtener el producto por ID o nombre | ||||
|     product = CatalogManager.get_product(product_id) | ||||
|     if not product: | ||||
|         # Si no se encuentra, intentar buscar por nombre exacto | ||||
|         product = CatalogManager.get_product_by_name(product_id) | ||||
|         if not product: | ||||
|             return f"❌ No se encontró el producto '{product_id}' en el catálogo. Por favor, verifica el nombre o ID del producto." | ||||
|  | ||||
|     if product["stock"] < quantity: | ||||
|         return f"❌ Stock insuficiente. Solo hay {product['stock']} {product['unidad']} disponibles de {product['producto']}." | ||||
|  | ||||
|     # Añadir al carrito | ||||
|     success = OrderManager.add_to_cart( | ||||
|         phone,  | ||||
|         str(product["id"]),  | ||||
|         quantity,  | ||||
|         product["precio"], | ||||
|         product["unidad"] | ||||
|     ) | ||||
|      | ||||
|     if success: | ||||
|         remaining_stock = product["stock"] - quantity | ||||
|         response = f"✅ Se añadieron {quantity} {product['unidad']} de {product['producto']} al carrito." | ||||
|          | ||||
|         # Advertencia de stock bajo | ||||
|         if remaining_stock < 10: | ||||
|             response += f"\n⚠️ Aviso: Solo quedan {remaining_stock} {product['unidad']} en stock." | ||||
|              | ||||
|         # Sugerir productos relacionados | ||||
|         related_products = CatalogManager.search_products(product["categoria"]) | ||||
|         if len(related_products) > 1: | ||||
|             response += "\n\n📦 También podría interesarte:" | ||||
|             for related in related_products[:3]: | ||||
|                 if related["id"] != product["id"] and related["stock"] > 0: | ||||
|                     response += f"\n- {related['producto']} ({related['unidad']} a ${related['precio']:,})" | ||||
|          | ||||
|         return response | ||||
|     else: | ||||
|         return "❌ Hubo un error al añadir el producto al carrito. Por favor, intenta de nuevo." | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def remove_from_cart(phone: str, product_id: str) -> str: | ||||
|     """ | ||||
|     Elimina un producto del carrito del cliente. | ||||
|     Args: | ||||
|         phone: Número de teléfono del cliente | ||||
|         product_id: ID o nombre del producto a eliminar | ||||
|     """ | ||||
|     product = CatalogManager.get_product(product_id) | ||||
|     if not product: | ||||
|         product = CatalogManager.get_product_by_name(product_id) | ||||
|         if not product: | ||||
|             return f"❌ No se encontró el producto '{product_id}' en el catálogo." | ||||
|  | ||||
|     success = OrderManager.remove_from_cart(phone, str(product["id"])) | ||||
|     if success: | ||||
|         return f"✅ Se eliminó {product['producto']} del carrito." | ||||
|     else: | ||||
|         return "❌ No se pudo eliminar el producto del carrito. ¿Está seguro que el producto está en su carrito?" | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def view_cart(phone: str) -> str: | ||||
|     """ | ||||
|     Muestra los productos en el carrito del cliente. | ||||
|     Args: | ||||
|         phone: Número de teléfono del cliente | ||||
|     """ | ||||
|     items = OrderManager.get_cart_items(phone) | ||||
|     if not items: | ||||
|         return "El carrito está vacío." | ||||
|  | ||||
|     result = "🛒 Productos en el carrito:\n\n" | ||||
|     total = 0 | ||||
|     for item in items: | ||||
|         product = CatalogManager.get_product(item["product_id"]) | ||||
|         if product: | ||||
|             subtotal = item["quantity"] * item["price"] | ||||
|             total += subtotal | ||||
|             result += f"- {product['producto']}\n" | ||||
|             result += f"  Cantidad: {item['quantity']} {item['unit']}\n" | ||||
|             result += f"  Precio unitario: ${item['price']:,}\n" | ||||
|             result += f"  Subtotal: ${subtotal:,}\n\n" | ||||
|  | ||||
|     result += f"Total del carrito: ${total:,}" | ||||
|     return result | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def confirm_order(phone: str, delivery_address: Optional[str] = None,  | ||||
|                  payment_method: Optional[str] = None, notes: Optional[str] = None) -> str: | ||||
|     """ | ||||
|     Confirma el pedido actual del cliente. | ||||
|     Args: | ||||
|         phone: Número de teléfono del cliente | ||||
|         delivery_address: Dirección de entrega (opcional) | ||||
|         payment_method: Método de pago (opcional) | ||||
|         notes: Notas adicionales para el pedido (opcional) | ||||
|     """ | ||||
|     success, result = OrderManager.confirm_order( | ||||
|         phone, delivery_address, payment_method, notes | ||||
|     ) | ||||
|      | ||||
|     if success: | ||||
|         order_details = OrderManager.get_order_details(result) | ||||
|         response = "✅ ¡Pedido confirmado exitosamente!\n\n" | ||||
|         response += "📋 Resumen del pedido:\n" | ||||
|          | ||||
|         # Detalles de entrega | ||||
|         if delivery_address: | ||||
|             response += f"📍 Dirección de entrega: {delivery_address}\n" | ||||
|         if payment_method: | ||||
|             response += f"💳 Método de pago: {payment_method}\n" | ||||
|         if notes: | ||||
|             response += f"📝 Notas: {notes}\n" | ||||
|          | ||||
|         response += "\nProductos:\n" | ||||
|         for item in order_details['items']: | ||||
|             product = CatalogManager.get_product(item['product_id']) | ||||
|             response += f"- {item['quantity']} {item['unit']} de {product['producto']} a ${item['price']:,} c/u\n" | ||||
|          | ||||
|         response += f"\n💰 Total: ${order_details['total']:,}" | ||||
|          | ||||
|         if delivery_address: | ||||
|             response += "\n\n🚚 El pedido será preparado y enviado a la dirección proporcionada." | ||||
|             response += "\nRecibirás actualizaciones sobre el estado de tu pedido." | ||||
|         else: | ||||
|             response += "\n\n🏪 El pedido estará listo para retirar en tienda." | ||||
|          | ||||
|         return response | ||||
|     else: | ||||
|         return f"❌ Error al confirmar el pedido: {result}" | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def view_order_history(phone: str) -> str: | ||||
|     """ | ||||
|     Muestra el historial de órdenes del cliente. | ||||
|     Args: | ||||
|         phone: Número de teléfono del cliente | ||||
|     """ | ||||
|     orders = OrderManager.get_order_history(phone) | ||||
|     if not orders: | ||||
|         return "No hay pedidos registrados para este número." | ||||
|  | ||||
|     result = "📚 Historial de pedidos:\n\n" | ||||
|     for order in orders: | ||||
|         result += f"🔸 Pedido #{order['order_id'][:8]}\n" | ||||
|         result += f"  Fecha: {order['created_at']}\n" | ||||
|         result += f"  Estado: {order['status']}\n" | ||||
|         if order['delivery_status']: | ||||
|             result += f"  Estado de entrega: {order['delivery_status']}\n" | ||||
|         result += f"  Total: ${order['total']:,}\n" | ||||
|         if order['delivery_address']: | ||||
|             result += f"  Dirección: {order['delivery_address']}\n" | ||||
|         if order['notes']: | ||||
|             result += f"  Notas: {order['notes']}\n" | ||||
|         result += "\n" | ||||
|  | ||||
|     return result | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def update_delivery_status(phone: str, order_id: str, status: str) -> str: | ||||
|     """ | ||||
|     Actualiza el estado de entrega de una orden. | ||||
|     Args: | ||||
|         phone: Número de teléfono del cliente | ||||
|         order_id: ID de la orden | ||||
|         status: Nuevo estado de entrega | ||||
|     """ | ||||
|     success = OrderManager.update_delivery_status(order_id, status) | ||||
|     if success: | ||||
|         return f"✅ Estado de entrega actualizado a: {status}" | ||||
|     return "❌ Error al actualizar el estado de entrega" | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def apply_discount(phone: str, discount_amount: float) -> str: | ||||
|     """ | ||||
|     Aplica un descuento al carrito actual. | ||||
|     Args: | ||||
|         phone: Número de teléfono del cliente | ||||
|         discount_amount: Cantidad del descuento | ||||
|     """ | ||||
|     order_id = OrderManager.get_active_cart(phone) | ||||
|     success = OrderManager.apply_discount(order_id, discount_amount) | ||||
|     if success: | ||||
|         return f"✅ Descuento de ${discount_amount:,} aplicado al carrito" | ||||
|     return "❌ Error al aplicar el descuento" | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def get_order_status(phone: str, order_id: str) -> str: | ||||
|     """ | ||||
|     Obtiene el estado actual de una orden específica. | ||||
|     Args: | ||||
|         phone: Número de teléfono del cliente | ||||
|         order_id: ID de la orden | ||||
|     """ | ||||
|     order = OrderManager.get_order_details(order_id) | ||||
|     if not order: | ||||
|         return f"No se encontró la orden con ID {order_id}" | ||||
|      | ||||
|     result = f"📦 Estado del pedido #{order_id[:8]}:\n" | ||||
|     result += f"Estado: {order['status']}\n" | ||||
|     if order['delivery_status']: | ||||
|         result += f"Estado de entrega: {order['delivery_status']}\n" | ||||
|     result += f"Total: ${order['total']:,}\n" | ||||
|     if order['delivery_address']: | ||||
|         result += f"Dirección de entrega: {order['delivery_address']}\n" | ||||
|      | ||||
|     return result | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def merge_orders(phone: str, order_ids: List[str], delivery_address: Optional[str] = None) -> str: | ||||
|     """ | ||||
|     Combina varios pedidos en uno nuevo. | ||||
|     Args: | ||||
|         phone: Número de teléfono del cliente | ||||
|         order_ids: Lista de IDs de pedidos a combinar | ||||
|         delivery_address: Nueva dirección de entrega (opcional) | ||||
|     """ | ||||
|     success, result = OrderManager.merge_orders(phone, order_ids, delivery_address) | ||||
|      | ||||
|     if success: | ||||
|         order_details = OrderManager.get_order_details(result) | ||||
|         response = "✅ ¡Pedidos combinados exitosamente!\n\n" | ||||
|         response += "📋 Resumen del nuevo pedido:\n" | ||||
|          | ||||
|         if delivery_address: | ||||
|             response += f"📍 Dirección de entrega: {delivery_address}\n" | ||||
|          | ||||
|         response += "\nProductos:\n" | ||||
|         for item in order_details['items']: | ||||
|             product = CatalogManager.get_product(item['product_id']) | ||||
|             response += f"- {item['quantity']} {item['unit']} de {product['producto']} a ${item['price']:,} c/u\n" | ||||
|          | ||||
|         response += f"\n💰 Total: ${order_details['total']:,}" | ||||
|         return response | ||||
|     else: | ||||
|         return f"❌ Error: {result}" | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def delete_order(phone: str, order_id: str) -> str: | ||||
|     """ | ||||
|     Elimina un pedido si está en estado permitido. | ||||
|     Args: | ||||
|         phone: Número de teléfono del cliente | ||||
|         order_id: ID del pedido a eliminar | ||||
|     """ | ||||
|     success = OrderManager.delete_order(phone, order_id) | ||||
|      | ||||
|     if success: | ||||
|         return f"✅ El pedido #{order_id[:8]} ha sido eliminado exitosamente." | ||||
|     else: | ||||
|         return "❌ No se pudo eliminar el pedido. Solo se pueden eliminar pedidos en estado 'en carrito' o 'confirmado'." | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def modify_order(phone: str, order_id: str, modifications: str) -> str: | ||||
|     """ | ||||
|     Modifica los productos de un pedido existente. | ||||
|     Args: | ||||
|         phone: Número de teléfono del cliente | ||||
|         order_id: ID del pedido a modificar | ||||
|         modifications: Descripción de las modificaciones (ej: "agregar 2 kg de arroz, quitar café") | ||||
|     """ | ||||
|     # Obtener detalles actuales del pedido | ||||
|     current_order = OrderManager.get_order_details(order_id) | ||||
|     if not current_order: | ||||
|         return f"❌ No se encontró el pedido #{order_id[:8]}" | ||||
|      | ||||
|     # Procesar las modificaciones (esto es un ejemplo simplificado) | ||||
|     try: | ||||
|         # Aquí iría la lógica para interpretar las modificaciones | ||||
|         # Por ahora solo mostraremos un mensaje informativo | ||||
|         return ( | ||||
|             "Para modificar el pedido, por favor especifica exactamente qué cambios deseas hacer:\n" | ||||
|             "- Para agregar productos: 'agregar X unidades de [producto]'\n" | ||||
|             "- Para quitar productos: 'quitar [producto]'\n" | ||||
|             "- Para cambiar cantidades: 'cambiar [producto] a X unidades'\n" | ||||
|             "\nPedido actual:\n" + | ||||
|             "\n".join(f"- {item['quantity']} {item['unit']} de {CatalogManager.get_product(item['product_id'])['producto']}" | ||||
|                      for item in current_order['items']) | ||||
|         ) | ||||
|     except Exception as e: | ||||
|         return f"❌ Error al modificar el pedido: {str(e)}" | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def get_order_products(phone: str, order_id: str) -> str: | ||||
|     """ | ||||
|     Obtiene la lista detallada de productos en un pedido. | ||||
|     Args: | ||||
|         phone: Número de teléfono del cliente | ||||
|         order_id: ID del pedido | ||||
|     """ | ||||
|     # Limpiar el ID del pedido (remover # si existe) | ||||
|     clean_order_id = order_id.strip().replace('#', '') | ||||
|      | ||||
|     order = OrderManager.get_order_details(clean_order_id) | ||||
|     if not order: | ||||
|         return f"❌ No se encontró el pedido #{clean_order_id[:8]}" | ||||
|      | ||||
|     if order['phone'] != phone: | ||||
|         return "❌ Este pedido no pertenece a este cliente" | ||||
|      | ||||
|     if not order.get('items'): | ||||
|         return f"ℹ️ El pedido #{clean_order_id[:8]} no tiene productos registrados" | ||||
|      | ||||
|     result = f"📦 Productos en el pedido #{clean_order_id[:8]}:\n\n" | ||||
|      | ||||
|     total = 0 | ||||
|     for item in order['items']: | ||||
|         product = CatalogManager.get_product(item['product_id']) | ||||
|         if product: | ||||
|             subtotal = item['quantity'] * item['price'] | ||||
|             total += subtotal | ||||
|             result += f"- {item['quantity']} {item['unit']} de {product['producto']}\n" | ||||
|             result += f"  💵 Precio unitario: ${item['price']:,}\n" | ||||
|             result += f"  💰 Subtotal: ${subtotal:,}\n\n" | ||||
|         else: | ||||
|             result += f"- {item['quantity']} {item['unit']} de producto no encontrado (ID: {item['product_id']})\n" | ||||
|      | ||||
|     result += f"💰 Total del pedido: ${order['total']:,}\n" | ||||
|      | ||||
|     if order.get('discount_applied', 0) > 0: | ||||
|         result += f"🏷️ Descuento aplicado: ${order['discount_applied']:,}\n" | ||||
|      | ||||
|     if order.get('delivery_address'): | ||||
|         result += f"📍 Dirección de entrega: {order['delivery_address']}\n" | ||||
|     if order.get('delivery_status'): | ||||
|         result += f"🚚 Estado de entrega: {order['delivery_status']}\n" | ||||
|      | ||||
|     return result | ||||
|  | ||||
|  | ||||
| # Lista de todas las herramientas disponibles | ||||
| tools = [ | ||||
|     add_to_cart, | ||||
|     remove_from_cart, | ||||
|     view_cart, | ||||
|     confirm_order, | ||||
|     view_order_history, | ||||
|     update_delivery_status, | ||||
|     apply_discount, | ||||
|     get_order_status, | ||||
|     merge_orders, | ||||
|     delete_order, | ||||
|     modify_order, | ||||
|     get_order_products, | ||||
| ] | ||||
							
								
								
									
										243
									
								
								agents/app/langgraph_tools/tools/orders/order_tools_2.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										243
									
								
								agents/app/langgraph_tools/tools/orders/order_tools_2.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,243 @@ | ||||
| from langchain_core.tools import tool | ||||
| from typing import Optional, List | ||||
| import requests | ||||
| import json | ||||
|  | ||||
| url = "http://192.168.0.25:8000" | ||||
| key = "9a9ffc430146447d81e6698240199a4be2b0e774cb18474999d0f60e33b5b1eb1cfff9d9141346a98844879b5a9e787489c891ddc8fb45cc903b7244cab64fb1" | ||||
| db = "tryton" | ||||
| application_name = "sale_don_confiao" | ||||
| url_don_confiao = "{}/{}/{}".format(url, db, application_name) | ||||
| url_order = "{}/{}/{}".format(url, db, "sale_order") | ||||
|  | ||||
|  | ||||
| #@tool | ||||
| def create_party( | ||||
|     party_full_name: str, | ||||
|     contact_method_type: str, | ||||
|     contact_method_value: str, | ||||
| ): | ||||
|     """ | ||||
|     Crea un nuevo cliente (party) en el sistema. | ||||
|      | ||||
|     Args: | ||||
|         party_full_name (str): Nombre completo del cliente. | ||||
|         contact_method_type (str): Tipo de método de contacto (ej. 'email', 'phone'). | ||||
|         contact_method_value (str): Valor del método de contacto (ej. dirección de email o número de teléfono). | ||||
|          | ||||
|     Returns: | ||||
|         requests.Response: La respuesta del servidor que contiene la información del cliente creado. | ||||
|  | ||||
|     """ | ||||
|      | ||||
|     url = "http://192.168.0.25:8000" | ||||
|     key = "9a9ffc430146447d81e6698240199a4be2b0e774cb18474999d0f60e33b5b1eb1cfff9d9141346a98844879b5a9e787489c891ddc8fb45cc903b7244cab64fb1" | ||||
|     db = "tryton" | ||||
|     application_name = "sale_don_confiao" | ||||
|     url_don_confiao = "{}/{}/{}".format(url, db, application_name) | ||||
|     url_order = "{}/{}/{}".format(url, db, "sale_order") | ||||
|  | ||||
|     url_order = url_order.replace("sale_order", "sale_don_confiao") | ||||
|  | ||||
|     data = { | ||||
|         "name": party_full_name, | ||||
|         "contact_mechanisms": [ | ||||
|             ["create", [{"type": contact_method_type, "value": contact_method_value}]] | ||||
|         ], | ||||
|     } | ||||
|  | ||||
|     response = requests.post( | ||||
|         url_order + "/parties", | ||||
|         headers={"Authorization": f"bearer {key}"}, | ||||
|         data=json.dumps(data), | ||||
|     ) | ||||
|  | ||||
|     return json.loads(response.text) | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def create_sale_order( | ||||
|     party: int, pickup_location: str = "on_site", lines: Optional[List] = None | ||||
| ) -> int: | ||||
|     """ | ||||
|     Crea una nueva orden de venta en el sistema. | ||||
|  | ||||
|     Args: | ||||
|         party (int): El ID del cliente. | ||||
|         pickup_location (str, optional): Ubicación de recogida. Valores posibles: | ||||
|             - "on_site": Recoger en el local | ||||
|             - "at_home": Entrega a domicilio | ||||
|             Por defecto es "on_site". | ||||
|         lines (List, optional): Lista de líneas de la orden. Por defecto es None. | ||||
|             Cada línea debe ser una lista con el formato: ["create", [{"product": str, "unit": str, "quantity": str, "unitprice": str}]] | ||||
|             Donde: | ||||
|                 - product (str): ID del producto | ||||
|                 - unit (str): ID de la unidad de medida | ||||
|                 - quantity (str): Cantidad del producto | ||||
|                 - unitprice (str): Precio unitario del producto | ||||
|             Ejemplo: | ||||
|                 lines=[["create", [{ | ||||
|                     "product": "1", | ||||
|                     "unit": "1", | ||||
|                     "quantity": "5", | ||||
|                     "unitprice": "10" | ||||
|                 }]]] | ||||
|  | ||||
|     Returns: | ||||
|         int: El ID de la orden creada | ||||
|     """ | ||||
|     data = { | ||||
|         "party": party, | ||||
|         "pickup_location": pickup_location, | ||||
|     } | ||||
|  | ||||
|     if lines: | ||||
|         data["lines"] = lines | ||||
|  | ||||
|     response = requests.post( | ||||
|         url_order + "/order", | ||||
|         headers={ | ||||
|             "Authorization": f"bearer {key}", | ||||
|         }, | ||||
|         data=json.dumps(data), | ||||
|     ) | ||||
|  | ||||
|     # Extraer y retornar directamente el ID de la orden | ||||
|     return json.loads(json.loads(response.text)[0]).get("id") | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def search_sale_order(order_id: int): | ||||
|     """ | ||||
|     Busca una orden de venta específica en el sistema. | ||||
|  | ||||
|     Args: | ||||
|         order_id (int): El ID de la orden de venta a buscar. | ||||
|  | ||||
|     Returns: | ||||
|         requests.Response: La respuesta del servidor que contiene: | ||||
|             - party (int): ID del cliente | ||||
|             - id (int): ID de la orden | ||||
|             - lines (List): Lista de líneas de la orden | ||||
|             Ejemplo de respuesta: | ||||
|             [{"party": 2573, "id": 22, "lines": []}] | ||||
|     """ | ||||
|     response_sale = requests.get( | ||||
|         url_order + f"/order/{order_id}", | ||||
|         headers={ | ||||
|             "Authorization": f"bearer {key}", | ||||
|         }, | ||||
|     ) | ||||
|  | ||||
|     return response_sale | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def add_lines_to_order( | ||||
|     order_id: int, product: str, unit: str, quantity: str, unitprice: str | ||||
| ): | ||||
|     """ | ||||
|     Agrega una línea de producto a una orden existente. | ||||
|  | ||||
|     Args: | ||||
|         order_id (int): ID de la orden a la que se agregará la línea. | ||||
|         product (str): ID del producto a agregar. | ||||
|         unit (str): ID de la unidad de medida del producto. | ||||
|         quantity (str): Cantidad del producto a agregar. | ||||
|         unitprice (str): Precio unitario del producto. | ||||
|  | ||||
|     Returns: | ||||
|         requests.Response: La respuesta del servidor que contiene: | ||||
|             - order_lines (List[int]): Lista de IDs de las líneas creadas | ||||
|             - status (str): Estado de la operación ('success' si fue exitosa) | ||||
|             - message (str): Mensaje descriptivo del resultado | ||||
|             Ejemplo de respuesta: | ||||
|             {"order_lines": [1], "status": "success", "message": "Order lines created successfully"} | ||||
|     """ | ||||
|     data = { | ||||
|         "order": order_id, | ||||
|         "product": product, | ||||
|         "unit": unit, | ||||
|         "quantity": quantity, | ||||
|         "unitprice": unitprice, | ||||
|     } | ||||
|  | ||||
|     response = requests.post( | ||||
|         url_order + f"/{order_id}/order_line", | ||||
|         headers={ | ||||
|             "Authorization": f"bearer {key}", | ||||
|         }, | ||||
|         data=json.dumps(data), | ||||
|     ) | ||||
|  | ||||
|     return response | ||||
|  | ||||
|  | ||||
| @tool | ||||
| def search_associate_party_to_contact_mechanism(contact_mechanism: str): | ||||
|     """ | ||||
|     Busca un cliente en el sistema por un metodo de contacto que pueda ser un numero de celular o un correo electronico. | ||||
|  | ||||
|     Args: | ||||
|         contact_mechanism (str): El número de contacto del cliente. | ||||
|  | ||||
|     Returns: | ||||
|         requests.Response: La respuesta del servidor que contiene: | ||||
|             - id (int): ID de la asociación en la base de datos | ||||
|             - associate_party (int): ID del cliente asociado (Party) | ||||
|             - status (str): Estado de la operación ('success' si fue exitosa) | ||||
|             - type (str): Tipo de contacto asociado | ||||
|             - message (str): Mensaje descriptivo del resultado | ||||
|             Ejemplo de respuesta: | ||||
|             {"id": 1, "associate_party": 1, "status": "success", "type": "phone", "message": "Associate Party found"} | ||||
|     """ | ||||
|     response = requests.get( | ||||
|         url_order + f"/associate_party/{contact_mechanism}", | ||||
|         headers={ | ||||
|             "Authorization": f"bearer {key}", | ||||
|         }, | ||||
|     ) | ||||
|  | ||||
|     return response | ||||
|  | ||||
|  | ||||
| tools = [ | ||||
|     create_party, | ||||
|     create_sale_order, | ||||
|     search_sale_order, | ||||
|     add_lines_to_order, | ||||
|     search_associate_party_to_contact_mechanism, | ||||
| ] | ||||
|  | ||||
| # if __name__ == "__main__": | ||||
| #     # Crear una orden de venta | ||||
| #     party = 2573 | ||||
| #     pickup_location = "at_home" | ||||
| #     order_id = create_sale_order(party=party, pickup_location=pickup_location) | ||||
| #     print(f"\nOrden creada con ID: {order_id}") | ||||
|  | ||||
| #     # Agregar líneas a la orden | ||||
| #     product = "1" | ||||
| #     unit = "1" | ||||
| #     quantity = "3" | ||||
| #     unitprice = "15" | ||||
|  | ||||
| #     add_line_response = add_lines_to_order(order_id, product, unit, quantity, unitprice) | ||||
| #     print(f"\nRespuesta al agregar línea: {add_line_response.text}") | ||||
|  | ||||
| #     # Verificar la orden actualizada | ||||
| #     updated_order = search_sale_order(order_id) | ||||
| #     print(f"\nOrden actualizada: {updated_order.text}") | ||||
|  | ||||
| #     # Agregar otra línea a la orden | ||||
| #     product = "2" | ||||
| #     unit = "1" | ||||
| #     quantity = "3" | ||||
| #     unitprice = "15" | ||||
|  | ||||
| #     add_line_response = add_lines_to_order(order_id, product, unit, quantity, unitprice) | ||||
| #     print(f"\nRespuesta al agregar línea: {add_line_response.text}") | ||||
|  | ||||
| #     # Verificar la orden actualizada | ||||
| #     updated_order = search_sale_order(order_id) | ||||
| #     print(f"\nOrden actualizada: {updated_order.text}") | ||||
							
								
								
									
										20
									
								
								agents/app/langgraph_tools/tools/store_info.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								agents/app/langgraph_tools/tools/store_info.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| store: | ||||
|   hours: "Lunes a Viernes: 8:00 AM - 8:00 PM | Sábados: 9:00 AM - 6:00 PM | Domingos y Festivos: 10:00 AM - 4:00 PM" | ||||
|   location: "Carrera 53 #42-81, 2do piso, Diagonal al Parque del Periodista, Medellín, (Col)" | ||||
|   contact: | ||||
|     phone: "+57 302 356 77 97" | ||||
|     whatsapp: "+57 302 356 77 97" | ||||
|     email: "info@recreo.red" | ||||
|  | ||||
|   about: | | ||||
|     Acerca de Nosotros  | ||||
|     La Corporación Centro Taller Recreo le apuesta a la Economía Solidaria como una herramienta  | ||||
|     para establecer relaciones fundadas en el compartir, la colectividad y el consumo responsable | ||||
|     con la colectividad y el medio ambiente; para trascender el competir, el individualismo y el | ||||
|     consumismo. De esta manera, buscamos promover valores solidarios como la responsabilidad, el | ||||
|     respeto, la ayuda mutua, la confianza y la equidad. | ||||
|  | ||||
|     Una de nuestras acciones más importantes es la consolidación del Circuito Cooperativo Tienda | ||||
|     La Ilusión, CIRCOOTIL. Por medio del Circuito se tejen puentes campo-ciudad aunando esfuerzos | ||||
|     de productores campesinos, tenderos y consumidores concientes. | ||||
|   page: "https://recreo.red/" | ||||
		Reference in New Issue
	
	Block a user