|
|
import os |
|
|
import gradio as gr |
|
|
import shutil |
|
|
from typing import List |
|
|
|
|
|
from src.file_processor import chunk_pdfs, chunk_all_documents |
|
|
from src.chroma_db import save_to_chroma_db, get_chroma_client |
|
|
from langchain_core.prompts import ChatPromptTemplate |
|
|
from langchain_ollama import OllamaEmbeddings |
|
|
from langchain_ollama import ChatOllama |
|
|
|
|
|
|
|
|
def initialize_system(process_documents=True): |
|
|
""" |
|
|
Inicializa el sistema RAG con opción de procesar documentos |
|
|
""" |
|
|
if process_documents: |
|
|
print("Procesando documentos...") |
|
|
processed_documents = chunk_pdfs() |
|
|
|
|
|
print("Inicializando modelo de embeddings...") |
|
|
embedding_model = OllamaEmbeddings( |
|
|
model="nomic-embed-text" |
|
|
) |
|
|
|
|
|
print("Guardando documentos en la base de datos...") |
|
|
db = save_to_chroma_db(processed_documents, embedding_model) |
|
|
return db, embedding_model |
|
|
else: |
|
|
print("Saltando procesamiento de documentos...") |
|
|
print("Inicializando modelo de embeddings...") |
|
|
embedding_model = OllamaEmbeddings( |
|
|
model="nomic-embed-text" |
|
|
) |
|
|
|
|
|
|
|
|
try: |
|
|
db = get_chroma_client() |
|
|
print("Conectado a base de datos existente") |
|
|
return db, embedding_model |
|
|
except Exception as e: |
|
|
print(f"Error conectando a base de datos existente: {e}") |
|
|
return None, embedding_model |
|
|
|
|
|
|
|
|
documents_processed = False |
|
|
db = None |
|
|
embedding_model = None |
|
|
|
|
|
|
|
|
PROMPT_TEMPLATE = """ |
|
|
Tienes que responder la siguiente pregunta basada en el contexto proporcionado: |
|
|
{context} |
|
|
|
|
|
Responde la siguiente pregunta: {question} |
|
|
|
|
|
Proporciona una respuesta con un enfoque de análisis histórico, considerando las causas, consecuencias y evolución de los hechos descritos. |
|
|
Sitúa los eventos en su marco temporal y geopolítico, y explica los factores sociales, económicos y políticos relevantes. |
|
|
Evita opiniones o juicios de valor y no incluyas información que no esté sustentada en el contexto. |
|
|
""" |
|
|
|
|
|
|
|
|
prompt_template = ChatPromptTemplate.from_template(PROMPT_TEMPLATE) |
|
|
|
|
|
|
|
|
model = ChatOllama(model="hf.co/unsloth/granite-4.0-h-small-GGUF:Q2_K_L") |
|
|
|
|
|
|
|
|
|
|
|
def answer_question(question): |
|
|
""" |
|
|
Función que responde preguntas basadas en el contexto de los documentos usando ChromaDB Docker |
|
|
""" |
|
|
global documents_processed, db |
|
|
|
|
|
if not question.strip(): |
|
|
return "Por favor ingresa una pregunta válida." |
|
|
|
|
|
if not documents_processed or db is None: |
|
|
return "❌ No hay documentos procesados disponibles. Por favor, procesa algunos documentos primero usando la opción de arriba." |
|
|
|
|
|
try: |
|
|
|
|
|
docs = db.similarity_search_with_score(question, k=3) |
|
|
|
|
|
if not docs: |
|
|
return "No se encontraron documentos relevantes para responder tu pregunta." |
|
|
|
|
|
context = "\n\n---\n\n".join([doc.page_content for doc, _score in docs]) |
|
|
|
|
|
|
|
|
prompt = prompt_template.format(context=context, question=question) |
|
|
|
|
|
|
|
|
response = model.invoke(prompt) |
|
|
|
|
|
return response.content if hasattr(response, 'content') else str(response) |
|
|
|
|
|
except Exception as e: |
|
|
return f"Error al procesar la pregunta: {str(e)}. Verifica que ChromaDB Docker esté funcionando en el puerto 8000." |
|
|
|
|
|
|
|
|
APORTACIONES_PATH = 'aportaciones' |
|
|
|
|
|
def handle_file_upload(files) -> str: |
|
|
""" |
|
|
Función que maneja la subida de archivos de los usuarios |
|
|
""" |
|
|
if not files: |
|
|
return "😅 ¡Ups! No has seleccionado ningún archivo. ¡Inténtalo de nuevo!" |
|
|
|
|
|
success_count = 0 |
|
|
error_count = 0 |
|
|
error_messages = [] |
|
|
|
|
|
|
|
|
os.makedirs(APORTACIONES_PATH, exist_ok=True) |
|
|
|
|
|
for file_obj in files: |
|
|
try: |
|
|
|
|
|
filename = os.path.basename(file_obj.name) |
|
|
|
|
|
|
|
|
destination_path = os.path.join(APORTACIONES_PATH, filename) |
|
|
|
|
|
|
|
|
shutil.copy2(file_obj.name, destination_path) |
|
|
|
|
|
print(f"✅ Archivo {filename} subido exitosamente a {APORTACIONES_PATH}") |
|
|
success_count += 1 |
|
|
|
|
|
except Exception as e: |
|
|
error_message = f"❌ Error al subir {filename}: {str(e)}" |
|
|
print(error_message) |
|
|
error_messages.append(error_message) |
|
|
error_count += 1 |
|
|
|
|
|
|
|
|
if success_count > 0 and error_count == 0: |
|
|
return f"🎉 ¡Genial! Has subido {success_count} archivo(s) exitosamente a la carpeta 'aportaciones'. ¡Tu conocimiento ahora forma parte del sistema! 🚀" |
|
|
elif success_count > 0 and error_count > 0: |
|
|
return f"⚠️ {success_count} archivo(s) subido(s) correctamente, pero {error_count} archivo(s) tuvieron problemas:\n" + "\n".join(error_messages) |
|
|
else: |
|
|
return f"😞 ¡Vaya! Hubo problemas al subir los archivos:\n" + "\n".join(error_messages) |
|
|
|
|
|
def process_user_documents(): |
|
|
""" |
|
|
Función que procesa los documentos subidos por usuarios |
|
|
""" |
|
|
global documents_processed, db, embedding_model |
|
|
|
|
|
try: |
|
|
print("🔄 Procesando documentos de usuarios...") |
|
|
|
|
|
|
|
|
processed_documents = chunk_all_documents() |
|
|
|
|
|
if not processed_documents: |
|
|
return "😅 No se encontraron documentos para procesar. ¡Sube algunos archivos primero!" |
|
|
|
|
|
print("🔗 Inicializando modelo de embeddings...") |
|
|
embedding_model = OllamaEmbeddings( |
|
|
model="nomic-embed-text" |
|
|
) |
|
|
|
|
|
print("💾 Guardando documentos en la base de datos...") |
|
|
db = save_to_chroma_db(processed_documents, embedding_model) |
|
|
|
|
|
documents_processed = True |
|
|
|
|
|
return f"🎊 ¡Perfecto! Se procesaron {len(processed_documents)} documentos exitosamente. ¡Ya puedes hacer preguntas sobre tu nuevo contenido! 📚✨" |
|
|
|
|
|
except Exception as e: |
|
|
return f"❌ Error al procesar documentos: {str(e)}. Asegúrate de que todos los servicios estén funcionando correctamente." |
|
|
|
|
|
|
|
|
with gr.Blocks( |
|
|
title="Sistema RAG - Consulta de Documentos", |
|
|
theme=gr.themes.Soft(), |
|
|
css=""" |
|
|
.gradio-container { |
|
|
max-width: 800px; |
|
|
margin: auto; |
|
|
} |
|
|
.title { |
|
|
text-align: center; |
|
|
color: #2563eb; |
|
|
font-size: 2.5em; |
|
|
margin-bottom: 1em; |
|
|
} |
|
|
.subtitle { |
|
|
text-align: center; |
|
|
color: #64748b; |
|
|
font-size: 1.1em; |
|
|
margin-bottom: 2em; |
|
|
} |
|
|
""" |
|
|
) as demo: |
|
|
gr.HTML("<h1 class='title'>🤖 Sistema RAG - Consulta de Documentos</h1>") |
|
|
gr.HTML("<p class='subtitle'>Haz preguntas sobre el contenido de tus documentos usando IA con ChromaDB Docker</p>") |
|
|
|
|
|
gr.HTML(""" |
|
|
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
|
padding: 20px; |
|
|
border-radius: 15px; |
|
|
margin: 20px 0; |
|
|
text-align: center; |
|
|
color: white;"> |
|
|
<h3 style="margin: 0 0 10px 0;">🚀 ¡Comparte tu conocimiento!</h3> |
|
|
<p style="margin: 0; font-size: 1.1em;"> |
|
|
¿Tienes documentos interesantes que quieres añadir al sistema? |
|
|
¡Súbelos aquí y forma parte de esta aventura del conocimiento! 📚✨ |
|
|
</p> |
|
|
</div> |
|
|
""") |
|
|
|
|
|
with gr.Row(): |
|
|
with gr.Column(scale=2): |
|
|
file_upload = gr.File( |
|
|
label="📎 Subir documentos", |
|
|
file_count="multiple", |
|
|
file_types=[".pdf", ".txt", ".md"], |
|
|
elem_id="file_upload" |
|
|
) |
|
|
|
|
|
with gr.Column(scale=1): |
|
|
upload_btn = gr.Button( |
|
|
"⬆️ Subir archivos", |
|
|
variant="secondary", |
|
|
size="lg" |
|
|
) |
|
|
|
|
|
upload_output = gr.Markdown( |
|
|
label="Estado de subida", |
|
|
elem_id="upload_status" |
|
|
) |
|
|
|
|
|
with gr.Row(): |
|
|
process_btn = gr.Button( |
|
|
"🔄 Procesar documentos", |
|
|
variant="primary", |
|
|
size="lg" |
|
|
) |
|
|
|
|
|
process_output = gr.Markdown( |
|
|
label="Estado de procesamiento", |
|
|
elem_id="process_status" |
|
|
) |
|
|
|
|
|
question_input = gr.Textbox( |
|
|
label="Tu pregunta", |
|
|
placeholder="Ej: ¿Cuáles son los pasos recomendados para fertilizar un jardín de vegetales?", |
|
|
lines=3, |
|
|
max_lines=10 |
|
|
) |
|
|
|
|
|
submit_btn = gr.Button( |
|
|
"🔍 Consultar", |
|
|
variant="primary", |
|
|
size="lg" |
|
|
) |
|
|
|
|
|
answer_output = gr.Markdown( |
|
|
label="Respuesta", |
|
|
show_copy_button=True |
|
|
) |
|
|
|
|
|
|
|
|
gr.Examples( |
|
|
examples=[ |
|
|
"¿Cuál es el orgigen étnico de los habitantes de Gaza?", |
|
|
"¿Qué documentos históricos están disponibles?", |
|
|
"¿Qué ocurrió el 7 de octubre de 2023?", |
|
|
], |
|
|
inputs=question_input, |
|
|
label="Ejemplos de preguntas" |
|
|
) |
|
|
|
|
|
|
|
|
submit_btn.click( |
|
|
fn=answer_question, |
|
|
inputs=[question_input], |
|
|
outputs=[answer_output] |
|
|
) |
|
|
|
|
|
question_input.submit( |
|
|
fn=answer_question, |
|
|
inputs=[question_input], |
|
|
outputs=[answer_output] |
|
|
) |
|
|
|
|
|
|
|
|
upload_btn.click( |
|
|
fn=handle_file_upload, |
|
|
inputs=[file_upload], |
|
|
outputs=[upload_output] |
|
|
) |
|
|
|
|
|
process_btn.click( |
|
|
fn=process_user_documents, |
|
|
inputs=[], |
|
|
outputs=[process_output] |
|
|
) |
|
|
|
|
|
gr.HTML(""" |
|
|
<div style="text-align: center; margin-top: 2em; color: #64748b; font-size: 0.9em;"> |
|
|
<p>Sistema RAG con LangChain, Ollama y ChromaDB Docker</p> |
|
|
<p style="font-size: 0.8em; margin-top: 0.5em;">🌐 ChromaDB corriendo en contenedor Docker (puerto 8000)</p> |
|
|
</div> |
|
|
""") |
|
|
|
|
|
if __name__ == "__main__": |
|
|
print("🚀 Sistema RAG - Consulta de Documentos") |
|
|
print("=" * 50) |
|
|
|
|
|
|
|
|
print("💡 ¡Novedad! Los usuarios ahora pueden subir documentos a la carpeta 'aportaciones' desde la interfaz web") |
|
|
print(" ¡Comparte tu conocimiento y enriquecer el sistema! 📚✨") |
|
|
|
|
|
print("\n🚀 Usando base de datos existente directamente...") |
|
|
process_documents = False |
|
|
|
|
|
|
|
|
print("\nInicializando sistema...") |
|
|
db, embedding_model = initialize_system(process_documents) |
|
|
|
|
|
|
|
|
documents_processed = (db is not None) |
|
|
if documents_processed: |
|
|
print("✅ Sistema inicializado con documentos existentes") |
|
|
else: |
|
|
print("⚠️ No se pudo conectar a documentos existentes") |
|
|
print("💡 Asegúrate de que la base de datos ChromaDB esté disponible en 'chroma/'") |
|
|
|
|
|
print("\n🚀 Iniciando interfaz web...") |
|
|
demo.launch( |
|
|
server_name="0.0.0.0", |
|
|
server_port=7862, |
|
|
share=True, |
|
|
debug=False |
|
|
) |