| |
| 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() |
|
|
|
|