|
|
|
import gradio as gr |
|
import pandas as pd |
|
import PyPDF2 |
|
import requests |
|
import io |
|
import json |
|
import os |
|
|
|
|
|
|
|
knowledge_base_text = "" |
|
|
|
|
|
|
|
|
|
MAX_CONTEXT_LENGTH = 8000 |
|
|
|
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 = "" |
|
|
|
if file is None: |
|
return "Por favor, sube un archivo para la base de conocimientos." |
|
|
|
file_path = file.name |
|
file_extension = file_path.split('.')[-1].lower() |
|
|
|
try: |
|
if file_extension == 'csv': |
|
|
|
df = pd.read_csv(file_path) |
|
knowledge_base_text = df.to_string(index=False) |
|
elif file_extension == 'xlsx': |
|
|
|
df = pd.read_excel(file_path) |
|
knowledge_base_text = df.to_string(index=False) |
|
elif file_extension == 'pdf': |
|
|
|
with open(file_path, 'rb') as f: |
|
reader = PyPDF2.PdfReader(f) |
|
for page_num in range(len(reader.pages)): |
|
page = reader.pages[page_num] |
|
|
|
knowledge_base_text += page.extract_text() + "\n" |
|
else: |
|
return "Formato de archivo no soportado. Por favor, sube un archivo .csv, .xlsx o .pdf." |
|
|
|
|
|
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 = "" |
|
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. |
|
""" |
|
|
|
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." |
|
) |
|
|
|
|
|
full_prompt = f"{system_instruction}\n\nContexto:\n{context_text}\n\nPregunta: {prompt_text}" |
|
|
|
|
|
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}" |
|
|
|
|
|
chat_history = [] |
|
chat_history.append({"role": "user", "parts": [{"text": full_prompt}]}) |
|
|
|
|
|
payload = { |
|
"contents": chat_history, |
|
"generationConfig": { |
|
"responseMimeType": "text/plain" |
|
} |
|
} |
|
|
|
try: |
|
|
|
response = requests.post(api_url, headers={'Content-Type': 'application/json'}, data=json.dumps(payload)) |
|
response.raise_for_status() |
|
result = response.json() |
|
|
|
|
|
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: |
|
|
|
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: |
|
|
|
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: |
|
|
|
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." |
|
|
|
|
|
|
|
context_for_llm = knowledge_base_text if knowledge_base_text else "No hay una base de conocimientos cargada." |
|
|
|
|
|
bot_response = get_llm_response(user_message, context_for_llm, personality_setting) |
|
return bot_response |
|
|
|
|
|
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(): |
|
|
|
file_input = gr.File(label="Sube tu base de conocimientos (.csv, .xlsx, .pdf)", type="filepath") |
|
|
|
load_button = gr.Button("Cargar Base de Conocimientos") |
|
|
|
status_output = gr.Textbox(label="Estado de la Carga", interactive=False) |
|
|
|
|
|
load_button.click( |
|
fn=load_knowledge_base, |
|
inputs=file_input, |
|
outputs=status_output |
|
) |
|
|
|
gr.Markdown("---") |
|
|
|
with gr.Row(): |
|
|
|
personality_dropdown = gr.Dropdown( |
|
label="Personalidad del Bot", |
|
choices=["amigable", "formal", "creativo", "anal铆tico"], |
|
value="amigable", |
|
interactive=True |
|
) |
|
|
|
user_query_input = gr.Textbox(label="Tu Pregunta", placeholder="Escribe tu pregunta aqu铆...") |
|
|
|
chat_button = gr.Button("Enviar Pregunta") |
|
|
|
|
|
bot_response_output = gr.Textbox(label="Respuesta del Bot", interactive=False) |
|
|
|
|
|
chat_button.click( |
|
fn=chat, |
|
inputs=[user_query_input, personality_dropdown], |
|
outputs=bot_response_output |
|
) |
|
|
|
|
|
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, |
|
cache_examples=False |
|
) |
|
|
|
|
|
demo.launch() |
|
|
|
|