| """ |
| LetXipu Beta SX - Gradio Interface v9.0 |
| Dark/Light Theme · Glassmorphism · Chatbot-like Interface |
| """ |
|
|
| import gradio as gr |
| import sys |
| import os |
|
|
| sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) |
|
|
| from dotenv import load_dotenv |
| load_dotenv(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".env")) |
|
|
| from modules.search_tab import create_search_tab |
| from modules.metadata_tab import create_metadata_tab |
| from modules.pdf_tab import create_pdf_tab |
| from modules.research_tab import create_research_tab |
| from modules.sources_tab import create_sources_tab |
| from modules.prompts_config_tab import create_prompts_config_tab |
| from modules.config.agents_tab import create_agents_tab |
| from modules.config.sources_config_tab import create_sources_config_tab |
| from modules.config.ai_tab import create_ai_tab |
| from modules.config.mining_tab import create_mining_tab |
| from modules.history_tab import create_history_tab |
|
|
| from backend.database.models import init_db |
| init_db() |
|
|
| VERSION = "9.0.0" |
|
|
| |
| assets_dir = os.path.join(os.path.dirname(__file__), "assets") |
|
|
| def create_app(theme, css, js): |
| with gr.Blocks(title="LetXipu Beta SX", theme=theme, css=css, js=js) as app: |
| |
| |
| gr.HTML(""" |
| <button class="theme-toggle" id="theme-toggle" onclick="toggleTheme()" title="Cambiar a modo claro"> |
| ☀️ |
| </button> |
| """) |
|
|
| |
| gr.HTML(f""" |
| <div class="header-banner"> |
| <div style="display: flex; justify-content: space-between; align-items: center; position: relative; z-index: 2;"> |
| <div> |
| <h1>🔬 LetXipu Beta SX</h1> |
| <p>Motor de Búsqueda Académica Independiente · Python Backend</p> |
| </div> |
| <div style="display: flex; gap: 8px; align-items: center;"> |
| <div class="header-badge">v{VERSION}</div> |
| <div class="header-badge" style="background: rgba(16, 185, 129, 0.2); border-color: rgba(16, 185, 129, 0.3); color: #10b981;">17 fuentes</div> |
| <div class="header-badge" style="background: rgba(59, 130, 246, 0.2); border-color: rgba(59, 130, 246, 0.3); color: #3b82f6;">87 modelos</div> |
| </div> |
| </div> |
| </div> |
| """) |
|
|
| |
| gr.HTML(""" |
| <div class="status-banner status-connected"> |
| <span class="status-dot connected"></span> |
| <span>✅ Backend Python independiente activo — Pipeline completo con 12 fases</span> |
| </div> |
| """) |
|
|
| |
| with gr.Tabs(elem_id="main-tabs") as tabs: |
| with gr.TabItem("🔍 Búsqueda y Extracción", id="search"): |
| create_search_tab() |
| with gr.TabItem("🔬 Agente de Research", id="research"): |
| create_research_tab() |
| with gr.TabItem("📄 Análisis PDF Local", id="pdf"): |
| create_pdf_tab() |
| with gr.TabItem("⚙️ Configuración (Core)", id="config_core"): |
| create_sources_tab() |
| create_prompts_config_tab() |
| with gr.TabItem("🛠️ Ajustes Avanzados", id="config_adv"): |
| create_agents_tab() |
| create_sources_config_tab() |
| create_ai_tab() |
| create_mining_tab() |
| with gr.TabItem("🕰️ Historial", id="history"): |
| create_history_tab() |
|
|
| |
| with gr.Row(): |
| with gr.Column(scale=1): |
| gr.Markdown("### 🛠️ Controles del Sistema") |
| with gr.Column(scale=2): |
| with gr.Row(): |
| restart_btn = gr.Button("🔄 Reiniciar App", variant="secondary", size="sm") |
| clear_cache_btn = gr.Button("🗑️ Limpiar Cache", variant="secondary", size="sm") |
| clear_processes_btn = gr.Button("🧹 Matar Procesos", variant="secondary", size="sm") |
| control_output = gr.Markdown("") |
|
|
| def do_restart(): |
| import subprocess |
| import sys |
| try: |
| |
| os.system("taskkill /F /IM python.exe /T 2>nul") |
| |
| subprocess.Popen([sys.executable, "app.py"], cwd=os.path.dirname(os.path.abspath(__file__))) |
| return "🔄 Reiniciando app... La página se recargará en unos segundos." |
| except Exception as e: |
| return f"❌ Error al reiniciar: {str(e)}" |
|
|
| def do_clear_cache(): |
| import shutil |
| cleared = [] |
| |
| gradio_cache = os.path.join(os.path.expanduser("~"), ".cache", "gradio") |
| if os.path.exists(gradio_cache): |
| shutil.rmtree(gradio_cache, ignore_errors=True) |
| cleared.append("Gradio cache") |
| |
| for root, dirs, files in os.walk(os.path.dirname(os.path.abspath(__file__))): |
| for d in dirs: |
| if d == "__pycache__": |
| shutil.rmtree(os.path.join(root, d), ignore_errors=True) |
| cleared.append(root) |
| |
| prompts_cache = os.path.join(os.path.dirname(os.path.abspath(__file__)), "prompts_config.json") |
| if os.path.exists(prompts_cache): |
| os.remove(prompts_cache) |
| cleared.append("prompts_config.json") |
| return f"🗑️ Cache limpiado: {', '.join(cleared) if cleared else 'nada que limpiar'}" |
|
|
| def do_clear_processes(): |
| import subprocess |
| try: |
| |
| result = subprocess.run(["taskkill", "/F", "/IM", "python.exe", "/T"], |
| capture_output=True, text=True, shell=True) |
| return f"🧹 Procesos limpiados: {result.stdout.strip() if result.stdout else 'completado'}" |
| except Exception as e: |
| return f"❌ Error: {str(e)}" |
|
|
| restart_btn.click(fn=do_restart, outputs=[control_output]) |
| clear_cache_btn.click(fn=do_clear_cache, outputs=[control_output]) |
| clear_processes_btn.click(fn=do_clear_processes, outputs=[control_output]) |
|
|
| |
| gr.HTML(f""" |
| <div class="app-footer"> |
| <span>LetXipu Beta SX v{VERSION} · Independiente · 2026</span> |
| <span>Backend: Python + httpx · Frontend: Gradio · 17 fuentes · 87 modelos · Pipeline de 12 fases</span> |
| </div> |
| """) |
|
|
| return app |
|
|
|
|
| if __name__ == "__main__": |
| from backend.database.models import SessionLocal, User |
| import hashlib |
| |
| |
| def init_admin(): |
| db = SessionLocal() |
| admin = db.query(User).filter(User.username == "admin").first() |
| if not admin: |
| hashed = hashlib.sha256("admin123".encode()).hexdigest() |
| db.add(User(username="admin", hashed_password=hashed, role="admin")) |
| db.commit() |
| db.close() |
| |
| def check_auth(username, password): |
| db = SessionLocal() |
| user = db.query(User).filter(User.username == username).first() |
| db.close() |
| if user and user.hashed_password == hashlib.sha256(password.encode()).hexdigest(): |
| return True |
| return False |
|
|
| init_admin() |
| |
| with open("assets/styles.css", "r", encoding="utf-8") as f: |
| custom_css = f.read() |
| |
| with open("assets/custom.js", "r", encoding="utf-8") as f: |
| custom_js = f.read() |
|
|
| theme = gr.themes.Base( |
| primary_hue="purple", |
| secondary_hue="indigo", |
| ).set( |
| body_background_fill="#0a0a0c", |
| body_background_fill_dark="#0a0a0c", |
| block_background_fill="#111827", |
| block_background_fill_dark="#111827", |
| block_border_color="#374151", |
| block_border_color_dark="#374151", |
| block_label_text_color="#9ca3af", |
| block_label_text_color_dark="#9ca3af", |
| block_title_text_color="#ffffff", |
| block_title_text_color_dark="#ffffff", |
| input_background_fill="#1f2937", |
| input_background_fill_dark="#1f2937", |
| input_border_color="#374151", |
| input_border_color_dark="#374151", |
| button_primary_background_fill="#8b5cf6", |
| button_primary_background_fill_dark="#8b5cf6", |
| button_primary_text_color="#ffffff", |
| button_secondary_background_fill="#1f2937", |
| button_secondary_background_fill_dark="#1f2937", |
| button_secondary_text_color="#9ca3af", |
| checkbox_background_color="#1f2937", |
| checkbox_background_color_dark="#1f2937", |
| slider_color="#8b5cf6", |
| slider_color_dark="#8b5cf6", |
| ) |
|
|
| app = create_app(theme, custom_css, custom_js) |
|
|
| app.launch( |
| server_name="0.0.0.0", |
| share=False, |
| show_error=True, |
| show_api=False, |
| allowed_paths=[assets_dir] |
| ) |
|
|