Spaces:
Sleeping
Sleeping
import os | |
import requests | |
from fastapi.responses import JSONResponse | |
import openai | |
import sqlite3 | |
import secrets | |
from fastapi import FastAPI | |
from dotenv import load_dotenv | |
from pydantic import BaseModel | |
from llama_index.llms.openai import OpenAI | |
from llama_cloud import ChatMessage, MessageRole | |
from llama_index.core.tools import FunctionTool | |
from llama_index.agent.openai import OpenAIAgent | |
from fastapi.middleware.cors import CORSMiddleware | |
from fastapi.security import HTTPBasic, HTTPBasicCredentials | |
from fastapi import HTTPException, Depends | |
from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_500_INTERNAL_SERVER_ERROR | |
load_dotenv() | |
# Configuración de la API Key de OpenAI y base_url | |
API_KEY = os.getenv("OPENAI_API_KEY") | |
MODEL = os.getenv("OPENAI_MODEL") | |
BASE_URL = os.getenv("OPENAI_BASE_URL") | |
DB_PATH = "./anhelados.db" | |
# Variables de entorno para autenticación básica | |
BASIC_AUTH_USERNAME = os.getenv("BASIC_AUTH_USERNAME") | |
BASIC_AUTH_PASSWORD = os.getenv("BASIC_AUTH_PASSWORD") | |
# Crear una instancia de cliente ajustada para usar OpenAI | |
openai.api_key = API_KEY | |
client = openai.OpenAI(api_key=API_KEY, base_url=BASE_URL) | |
class SQLExecutor: | |
def query(sql_query: str): | |
""" | |
Ejecuta una consulta SQL y devuelve los resultados. | |
Palabras clave, helados, anhelados, sabor de helados, jairo, edson, | |
Cualquier consulta SQL que generes debe resumir tu solicitud en una sola consulta, | |
ejemplo, si deseas saber la cantidad de insumos que tienes en tu almacén debes hacer | |
un conteo de los insumos, en el caso de produccion de las maquinas debes resumir de | |
igual manera la consulta todo con el fin de que la respuesta sea precisa. | |
""" | |
try: | |
with sqlite3.connect(DB_PATH) as conn: | |
cursor = conn.cursor() | |
cursor.execute(sql_query) | |
return cursor.fetchall() | |
except Exception as e: | |
return "No hay registros para mostrar" | |
class ApiPosDiegoWilly: | |
def pos_laura_delgado(sql_query: str): | |
""" | |
Ejecuta una consulta SQL para una base de datos `MYSQL` y devuelve los resultados. | |
Palabra clave ejecutar api POS, laura, delgado, diego, willy o pos_laura_delgado | |
""" | |
respuesta = requests.post( | |
url="https://innovpythia-pos-untels.hf.space/execute", | |
json={"query": sql_query}, | |
) | |
return respuesta.json() | |
class ApiPosAxel: | |
def academia_axel_victor(sql_query: str): | |
""" | |
Ejecuta una consulta SQL y devuelve los resultados (base de datos SQLITE) referente a la academia. | |
Palabra clave ejecutar api academia, axel, victor, barzola, tinocco o api_axel_victor | |
""" | |
respuesta = requests.post( | |
url="https://innovpythia-acadsh-api.hf.space/execute", | |
json={"query": sql_query}, | |
) | |
return respuesta.json() | |
# Creación del Agente utilizando la API de OpenAI | |
sql_tool = FunctionTool.from_defaults(fn=SQLExecutor.query) | |
api_laura_delgado = FunctionTool.from_defaults(fn=ApiPosDiegoWilly.pos_laura_delgado) | |
api_axel_victor = FunctionTool.from_defaults(fn=ApiPosAxel.academia_axel_victor) | |
estructura_base_de_datos = ChatMessage( | |
role=MessageRole.USER, | |
content=""" | |
En caso se quiera hacer cualquiera de las funciones de un CRUD con Anhelados esta es la estructura de la base de datos SQLITE de Anhelados y debes usar sql_tool: | |
1. almacen: ID_Almacen, ID_Insumo, Cantidad_Actual, Cantidad_Minima, Referencia: insumos (ID_Insumo) | |
2. clientes: ID_Proveedor, Nombre, Contacto, Direccion | |
3. detalle_pedidos: ID_Pedido, ID_Insumo, Cantidad, Costo_Unitario, Referencias: pedidos_proveedor (ID_Pedido), insumos (ID_Insumo) | |
4. detalle_ventas: ID_Venta, ID_Producto, Cantidad, Precio_Unitario, Referencias: ventas (ID_Venta), productos (ID_Producto) | |
5. empleados: ID_Empleado, Nombre, Apellido, Sueldo, Fecha_Inicio | |
6. gastos: ID_Gasto, Tipo, Monto, Fecha | |
7. gastos_imprevistos: ID_Gasto_Imprevisto, Descripcion, Monto, Fecha | |
8. insumos: ID_Insumo, Descripcion, Tipo, Costo, Cantidad | |
9. maquinas: ID_Maquina, Tipo, Capacidad, Consumo_Energetico | |
10. pedidos_proveedor: ID_Pedido, ID_Proveedor, Fecha, Total, Referencia: clientes (ID_Proveedor) | |
11. produccion: ID_Produccion, ID_Maquina, Fecha, Cantidad_Producida, Referencia: maquinas (ID_Maquina) | |
12. productos: ID_Producto, Nombre, Precio | |
13. registro_energetico: ID_Registro, ID_Maquina, Fecha, Consumo, Referencia: maquinas (ID_Maquina) | |
14. ventas: ID_Venta, Fecha, ID_Empleado, Total, Referencia: empleados (ID_Empleado) | |
""", | |
additional_kwargs={"tool": "sql_tool"}, | |
) | |
mensaje_academia = ChatMessage( | |
role=MessageRole.USER, | |
content="""La base de datos tiene esta estructura SQLite de la Academia: La base de datos diseñada gestiona información sobre becas, estudiantes, profesores, cursos, horarios y evaluaciones. Incluye las siguientes tablas: | |
Becas: Contiene los campos ID_Beca, Nombre_Beca, Descripcion y Monto de cada beca. | |
Estudiantes: Contiene los campos ID_Estudiante, Nombre, Apellido, Fecha_Nacimiento, Direccion y Telefono de cada estudiante. | |
Profesores: Contiene los campos ID_Profesor, Nombre, Apellido, Especialidad y Telefono de cada profesor. | |
Cursos: Relaciona los cursos con los profesores, y contiene los campos ID_Curso, Nombre_Curso, Duracion y ID_Profesor, con una clave foránea que referencia a los profesores. | |
Horarios: Relaciona los cursos con sus horarios, y contiene los campos ID_Horario, ID_Curso, Dia, Hora_Inicio y Hora_Fin, con una clave foránea que referencia a los cursos. | |
Evaluaciones: Registra las evaluaciones de los estudiantes en los cursos, y contiene los campos ID_Evaluacion, ID_Estudiante, ID_Curso, Fecha y Calificacion, con claves foráneas que referencian a estudiantes y cursos.""", | |
additional_kwargs={"tool": "api_axel_victor"}, | |
) | |
mensaje_phymed = ChatMessage( | |
role=MessageRole.USER, | |
content="""La base de datos tiene esta estructura MYSQL de la empresa Phymed, | |
se centra en vender productos de medicina física y rehabilitación a empresas o personas naturales. | |
La empresa utiliza un POS (Terminal punto de venta). | |
Cuentan con un vendedor en tienda que se encarga de recibir, asesorar y registrar la venta (proforma manual). | |
Luego, la proforma generada pasa al área de almacén donde se registra la boleta (manual) y se despacha al cliente. | |
La base de datos utilizada tiene las siguientes tablas, todas las id(PK) son autoincrementable: | |
- clientes: Contiene los campos de id, nombre, telefono y direccion. | |
- productos: Contiene los campos de id, codigo, nombre, compra, venta, existencias. | |
- productos_ventas: Relaciona 2 tablas productos y ventas para registrar las ventas, cuenta con los siguientes campos id, cantidad, precio, idProducto, idVenta. | |
- usuarios: id, usuario, nombre, telefono, direccion, password. | |
- ventas: id, fecha, total, idUsuario, idCliente. | |
""", | |
additional_kwargs={"tool": "api_laura_delgado"}, | |
) | |
llm = OpenAI(model=MODEL, api_client=client) | |
agent = OpenAIAgent.from_tools( | |
[sql_tool, api_laura_delgado, api_axel_victor], | |
llm=llm, | |
verbose=True, | |
max_function_calls=20, | |
chat_history=[estructura_base_de_datos, mensaje_academia, mensaje_phymed], | |
system_prompt="Eres un asistente super amable te llamas Chispitas y asistes a la empresa Anhelados, siempre saludas, brindas información puntual y aqui te brindo datos para solucionar consultas del usuario, Ubicaciones en Lima, Perú: San Isidro, Miraflores, Surco y La Molina con horarios de Atencion de 9 AM a 10 PM. Ofrece helados artesanales, milkshakes, postres y bebidas. Promociones regulares y servicio de delivery disponible. Locales Específicos: San Isidro: Av. Javier Prado Este 1234, Tel: (01) 123-4567. Miraflores: Calle Alcanfores 567, Tel: (01) 234-5678. Surco: Av. Caminos del Inca 890, Tel: (01) 345-6789. La Molina: Av. La Molina 345, Tel: (01) 456-7890. Productos Destacados: Helados clásicos como vainilla, chocolate y fresa. Especialidades como lúcuma, maracuyá y chirimoya. Opciones veganas y gourmet. Promociones: 2x1 en helados clásicos los martes. Descuentos en helados gourmet al comprar 3 litros. Servicios Adicionales: Delivery y pedidos personalizados para eventos. Reservas para cumpleaños y reuniones. Siempre termina con un mensaje de agradecimiento por la preferencia. Siempre responde en español, Cualquier consulta SQL que generes debe resumir tu solicitud en una sola consulta, cuando consultan la base de datos haz hasta 3 intentos de llamar a la funcion de ser necesario.", | |
) | |
def ask_agent(question): | |
response = agent.chat(question) | |
print(response) | |
return str(response) | |
app = FastAPI( | |
title="API de la heladería Anhelados", | |
description="Esta es la API de ChatBot de la microempresa Anhelados, que ofrece helados artesanales, milkshakes, postres y bebidas.", | |
version="3.2.8", | |
openapi_url="/anhelados-openapi.json", | |
docs_url="/anhelados-docs", | |
redoc_url="/anhelados-redoc", | |
contact={ | |
"name": "Anhelados", | |
"url": "https://www.anhelados.com", | |
"email": "contacto@anhelados.com", | |
}, | |
license_info={"name": "MIT", "url": "https://opensource.org/licenses/MIT"}, | |
) | |
security = HTTPBasic() | |
# CORS | |
app.add_middleware( | |
CORSMiddleware, | |
allow_origins=["*"], | |
allow_credentials=True, | |
allow_methods=["*"], | |
allow_headers=["*"], | |
) | |
def verify_credentials(credentials: HTTPBasicCredentials = Depends(security)): | |
correct_username = secrets.compare_digest(credentials.username, BASIC_AUTH_USERNAME) | |
correct_password = secrets.compare_digest(credentials.password, BASIC_AUTH_PASSWORD) | |
if not (correct_username and correct_password): | |
return JSONResponse( | |
status_code=HTTP_401_UNAUTHORIZED, | |
content={"message": "Autenticación fallida"}, | |
headers={"WWW-Authenticate": "Basic"}, | |
) | |
return credentials | |
class Item(BaseModel): | |
question: str | |
async def chat(item: Item, _: HTTPBasicCredentials = Depends(verify_credentials)): | |
try: | |
response = ask_agent(item.question) | |
return {"response": response} | |
except HTTPException as e: | |
raise e | |
except Exception as e: | |
raise HTTPException(status_code=HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e)) | |
def read_root(): | |
return {"message": "Bienvenido a la API de Anhelados-ChatBot."} | |
# uvicorn main:app --host localhost --port 7860 --reload | |