Cosio's picture
Update app.py
d5d43c4 verified
# app.py
import gradio as gr
import pandas as pd
import PyPDF2
import requests
import io
import json
import os # Importar el m贸dulo os para acceder a variables de entorno
# Variable global para almacenar el texto extra铆do de la base de conocimientos
# Se vac铆a cada vez que se carga un nuevo archivo.
knowledge_base_text = ""
# Longitud m谩xima del contexto que se pasar谩 al modelo de lenguaje.
# Los modelos de lenguaje tienen l铆mites en la cantidad de texto que pueden procesar.
# Si la base de conocimientos es muy grande, se truncar谩.
MAX_CONTEXT_LENGTH = 8000 # Caracteres
def load_knowledge_base(file):
"""
Carga un archivo (CSV, XLSX, PDF) y extrae su contenido de texto para usarlo
como base de conocimientos.
Args:
file: Un objeto de archivo de Gradio (gr.File), que contiene la ruta temporal del archivo subido.
Returns:
str: Un mensaje de estado indicando si la carga fue exitosa o si hubo un error.
"""
global knowledge_base_text
knowledge_base_text = "" # Limpiar la base de conocimientos anterior al cargar una nueva
if file is None:
return "Por favor, sube un archivo para la base de conocimientos."
file_path = file.name # La ruta temporal del archivo subido por Gradio
file_extension = file_path.split('.')[-1].lower()
try:
if file_extension == 'csv':
# Leer archivos CSV usando pandas
df = pd.read_csv(file_path)
knowledge_base_text = df.to_string(index=False) # Convertir DataFrame a cadena de texto
elif file_extension == 'xlsx':
# Leer archivos XLSX usando pandas (requiere openpyxl)
df = pd.read_excel(file_path)
knowledge_base_text = df.to_string(index=False) # Convertir DataFrame a cadena de texto
elif file_extension == 'pdf':
# Leer archivos PDF usando PyPDF2
with open(file_path, 'rb') as f:
reader = PyPDF2.PdfReader(f)
for page_num in range(len(reader.pages)):
page = reader.pages[page_num]
# Extraer texto de cada p谩gina y a帽adirlo a la base de conocimientos
knowledge_base_text += page.extract_text() + "\n"
else:
return "Formato de archivo no soportado. Por favor, sube un archivo .csv, .xlsx o .pdf."
# Truncar la base de conocimientos si excede la longitud m谩xima del contexto
if len(knowledge_base_text) > MAX_CONTEXT_LENGTH:
knowledge_base_text = knowledge_base_text[:MAX_CONTEXT_LENGTH] + "\n... [Contenido truncado debido a la longitud m谩xima del contexto]"
return f"Base de conocimientos cargada exitosamente (truncada a {MAX_CONTEXT_LENGTH} caracteres). 隆Ahora puedes chatear!"
else:
return "Base de conocimientos cargada exitosamente. 隆Ahora puedes chatear!"
except Exception as e:
knowledge_base_text = "" # Limpiar la base de conocimientos en caso de error
return f"Error al cargar la base de conocimientos: {e}. Aseg煤rate de que el archivo no est茅 corrupto o vac铆o."
def get_llm_response(prompt_text, context_text, personality_setting):
"""
Genera una respuesta utilizando la API de Gemini, incorporando el contexto
de la base de conocimientos y la personalidad seleccionada.
Args:
prompt_text (str): La pregunta del usuario.
context_text (str): El texto de la base de conocimientos que se usar谩 como contexto.
personality_setting (str): La personalidad deseada para el bot (e.g., "amigable", "formal").
Returns:
str: La respuesta generada por el modelo de lenguaje, o un mensaje de error.
"""
# Construir la instrucci贸n del sistema para guiar el comportamiento del LLM
system_instruction = (
f"Eres un asistente de IA con una personalidad {personality_setting}. "
"Responde a las preguntas de manera 煤til y concisa, utilizando la informaci贸n "
"proporcionada en el contexto si es relevante. Si la respuesta no est谩 en el contexto, "
"usa tu conocimiento general. Responde siempre en espa帽ol."
)
# Combinar la instrucci贸n del sistema, el contexto y la pregunta del usuario en un solo prompt
full_prompt = f"{system_instruction}\n\nContexto:\n{context_text}\n\nPregunta: {prompt_text}"
# *** CAMBIO IMPORTANTE AQU脥: Leer la clave API de las variables de entorno ***
api_key = os.getenv("GEMINI_API_KEY")
if not api_key:
return "Error: La clave API de Gemini no est谩 configurada. Por favor, a帽谩dela como un secreto en Hugging Face Spaces (GEMINI_API_KEY)."
api_url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key={api_key}"
# Preparar el historial de chat para la API de Gemini
chat_history = []
chat_history.append({"role": "user", "parts": [{"text": full_prompt}]})
# Carga 煤til para la solicitud a la API de Gemini
payload = {
"contents": chat_history,
"generationConfig": {
"responseMimeType": "text/plain" # Solicitar una respuesta en texto plano
}
}
try:
# Realizar la solicitud POST a la API de Gemini
response = requests.post(api_url, headers={'Content-Type': 'application/json'}, data=json.dumps(payload))
response.raise_for_status() # Lanzar una excepci贸n para c贸digos de estado HTTP de error (4xx o 5xx)
result = response.json() # Parsear la respuesta JSON
# Extraer el texto de la respuesta del modelo
if result.get("candidates") and len(result["candidates"]) > 0 and \
result["candidates"][0].get("content") and result["candidates"][0]["content"].get("parts") and \
len(result["candidates"][0]["content"]["parts"]) > 0:
return result["candidates"][0]["content"]["parts"][0]["text"]
else:
print("Error: Estructura de respuesta inesperada de la API del LLM.", result)
return "Lo siento, no pude generar una respuesta. Hubo un problema con la API del modelo."
except requests.exceptions.RequestException as e:
# Manejar errores de conexi贸n o HTTP
print(f"Error al llamar a la API de Gemini: {e}")
return f"Lo siento, hubo un error de conexi贸n al intentar obtener una respuesta: {e}"
except json.JSONDecodeError as e:
# Manejar errores al decodificar la respuesta JSON
print(f"Error al decodificar la respuesta JSON de la API: {e}")
return "Lo siento, hubo un problema al procesar la respuesta del modelo."
except Exception as e:
# Manejar cualquier otra excepci贸n inesperada
print(f"Ocurri贸 un error inesperado al obtener la respuesta del LLM: {e}")
return "Lo siento, ocurri贸 un error inesperado al intentar obtener una respuesta."
def chat(user_message, personality_setting):
"""
Funci贸n principal del chatbot que procesa el mensaje del usuario y genera una respuesta.
Args:
user_message (str): El mensaje o pregunta del usuario.
personality_setting (str): La personalidad seleccionada para el bot.
Returns:
str: La respuesta generada por el bot.
"""
if not user_message:
return "Por favor, escribe un mensaje para que el bot pueda responder."
# Usar la base de conocimientos global como contexto para el LLM
# Si no hay base de conocimientos cargada, se indica al LLM que no hay contexto.
context_for_llm = knowledge_base_text if knowledge_base_text else "No hay una base de conocimientos cargada."
# Obtener la respuesta del modelo de lenguaje
bot_response = get_llm_response(user_message, context_for_llm, personality_setting)
return bot_response
# Configuraci贸n de la interfaz de Gradio
with gr.Blocks(title="Chatbot de Base de Conocimientos") as demo:
gr.Markdown(
"""
# 馃 Chatbot de Base de Conocimientos Configurable
Sube un archivo (CSV, XLSX, PDF) para que el bot lo use como base de conocimientos.
Luego, selecciona una personalidad y haz tus preguntas.
"""
)
with gr.Row():
# Componente para subir archivos
file_input = gr.File(label="Sube tu base de conocimientos (.csv, .xlsx, .pdf)", type="filepath")
# Bot贸n para cargar la base de conocimientos
load_button = gr.Button("Cargar Base de Conocimientos")
# Cuadro de texto para mostrar el estado de la carga
status_output = gr.Textbox(label="Estado de la Carga", interactive=False)
# Configurar la acci贸n del bot贸n de carga
load_button.click(
fn=load_knowledge_base, # Funci贸n a llamar
inputs=file_input, # Entrada de la funci贸n
outputs=status_output # Salida de la funci贸n
)
gr.Markdown("---") # Separador visual
with gr.Row():
# Desplegable para seleccionar la personalidad del bot
personality_dropdown = gr.Dropdown(
label="Personalidad del Bot",
choices=["amigable", "formal", "creativo", "anal铆tico"], # Opciones de personalidad
value="amigable", # Valor predeterminado
interactive=True
)
# Cuadro de texto para que el usuario escriba su pregunta
user_query_input = gr.Textbox(label="Tu Pregunta", placeholder="Escribe tu pregunta aqu铆...")
# Bot贸n para enviar la pregunta
chat_button = gr.Button("Enviar Pregunta")
# Cuadro de texto para mostrar la respuesta del bot
bot_response_output = gr.Textbox(label="Respuesta del Bot", interactive=False)
# Configurar la acci贸n del bot贸n de chat
chat_button.click(
fn=chat, # Funci贸n a llamar
inputs=[user_query_input, personality_dropdown], # Entradas de la funci贸n
outputs=bot_response_output # Salida de la funci贸n
)
# Ejemplos para facilitar las pruebas r谩pidas del bot
gr.Examples(
examples=[
["驴Qu茅 es un chatbot?", "amigable"],
["Dame un resumen de la informaci贸n cargada.", "anal铆tico"],
["驴C贸mo puedo usar este bot?", "formal"]
],
inputs=[user_query_input, personality_dropdown],
outputs=bot_response_output,
fn=chat, # La funci贸n que se ejecuta al seleccionar un ejemplo
cache_examples=False # No almacenar en cach茅 los resultados de los ejemplos
)
# Para ejecutar la aplicaci贸n Gradio (esto lo har谩 Hugging Face Spaces autom谩ticamente)
demo.launch()