letxinet / app.py
C2MV's picture
fix: change server_name to 0.0.0.0 and hide API to bypass schema bug
7936949 verified
Raw
History Blame Contribute Delete
9.62 kB
"""
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"
# Note: Gradio loads external CSS and JS
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:
# ─── Theme Toggle Button ───
gr.HTML("""
<button class="theme-toggle" id="theme-toggle" onclick="toggleTheme()" title="Cambiar a modo claro">
☀️
</button>
""")
# ─── Header ───
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>
""")
# ─── Status ───
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>
""")
# ─── Tabs ───
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()
# ─── Control Buttons ───
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:
# Kill all python processes except current
os.system("taskkill /F /IM python.exe /T 2>nul")
# Restart the app
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 = []
# Clear Gradio cache
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")
# Clear Python __pycache__
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)
# Clear prompts config cache
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:
# Kill orphaned python processes
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])
# ─── Footer ───
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
# Asegurar que existe al menos un usuario administrador
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]
)