Panel Alexamigo
← Memory

nexuslex.md

# NexusLex — Asistente Legal IA (Navas & Cusi)

## Estado: OPERATIVO (desplegado 10 abr 2026)
- **URL**: `https://navas.alexamigo.com` | **Puerto**: 5040
- **Acceso**: contraseña `01091988` (localStorage, sin JWT)
- **Proposito**: Regalo para bufete Navas & Cusi. Chat RAG sobre derecho bancario espanol.

## Stack
- **Backend**: FastAPI + uvicorn (2 workers) + SQLAlchemy + PostgreSQL
- **RAG**: ChromaDB (PersistentClient) + Gemini Embedding 001 (3072 dims) + OpenAI GPT-5.4-nano (streaming SSE)
- **Frontend**: React 19 + Vite + Tailwind CSS (dark theme dorado/azul marino)
- **Deploy**: systemd `nexuslex.service` + nginx reverse proxy + certbot SSL

## Credenciales
- **DB**: PostgreSQL `nexuslex` / user `nexuslex_user` / pass `NexusLex2026pg` (SIN `!`, dio problemas)
- **Gemini API** (embeddings): `AIzaSyB84Vfxlh12JY-Ni_byuoCnFFlQeWZjoEo`
- **OpenAI API** (LLM): `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`
- **ChromaDB path**: `/home/ubuntu/proyectos-cloud/nexuslex/chromadb_data`
- **Collection**: `boe_bancario`

## Estructura VPS: `/home/ubuntu/proyectos-cloud/nexuslex/`
```
├── backend/
│   ├── main.py                    # FastAPI app (CORS, routers, SPA serving)
│   ├── config.py                  # Pydantic settings (.env)
│   ├── models.py                  # SQLAlchemy: LegalArea, Document, Chunk, Conversation, Message, IngestLog
│   ├── database.py                # Engine + SessionLocal + get_db
│   ├── routers/
│   │   ├── chat.py                # POST /api/chat (SSE streaming)
│   │   ├── documents.py           # GET /api/documents/stats, GET /api/documents
│   │   ├── ingest.py              # POST /api/ingest/boe, GET /api/ingest/status
│   │   └── areas.py               # GET /api/areas
│   ├── services/
│   │   ├── rag_engine.py          # Pipeline: embed query → retrieve 8 chunks → prompt → stream LLM
│   │   ├── chromadb_client.py     # ChromaDB wrapper (add_chunks, query_chunks)
│   │   ├── llm_client.py          # OpenAI streaming (max_completion_tokens, NO max_tokens)
│   │   ├── embedding_client.py    # Gemini embed_texts() + embed_query()
│   │   └── prompt_builder.py      # System prompt legal + RAG template
│   ├── ingestion/
│   │   ├── boe_fetcher.py         # BOE HTML fetcher (12 law IDs)
│   │   ├── document_processor.py  # HTML cleaner + article extractor
│   │   ├── chunker.py             # Chunking por articulos (500-1500 chars, overlap 100)
│   │   └── embedder.py            # Full pipeline: fetch → process → chunk → embed → ChromaDB
│   ├── requirements.txt
│   └── venv/
├── frontend/
│   ├── src/
│   │   ├── App.tsx                # Auth gate (localStorage password check)
│   │   ├── pages/AccessPage.tsx   # Input contrasena + gradiente oscuro
│   │   ├── pages/ChatPage.tsx     # Chat UI: MessageBubble, CitationBadge, WelcomeScreen, markdown renderer
│   │   ├── hooks/useChat.ts       # SSE streaming via fetch + ReadableStream
│   │   └── index.css              # Tailwind + custom properties dark theme
│   ├── vite.config.ts
│   └── package.json
├── chromadb_data/                 # Persistencia ChromaDB
├── deploy.sh                      # npm build + systemctl restart
└── .env                           # Todas las API keys y config
```

## Base legal indexada (9 leyes, 3857 chunks)
1. Ley 5/2019 de Credito Inmobiliario (LCCI)
2. TRLGDCU (defensa consumidores)
3. LEC (procedimientos bancarios)
4. Codigo Civil (obligaciones y contratos)
5. Ley Condiciones Generales de Contratacion
6. Ley Credito al Consumo
7. Ley Servicios de Pago
8. Ley del Mercado de Valores
9. Orden EHA/2899/2011 (transparencia bancaria)

### Leyes NO indexadas (solo PDF en BOE, sin HTML):
- Ley Regulacion Mercado Hipotecario (1981)
- Ley Disciplina e Intervencion Entidades de Credito (1988)
- Ley de Usura / Ley Azcarate (1908)

## DB PostgreSQL — Tablas
- `legal_areas`: 16 areas (solo bancario activo en MVP)
- `documents`: source, source_id, title, document_type, publication_date, legal_area_id, raw_content, content_hash, chunk_count
- `chunks`: document_id, chunk_index, content, section_title (TEXT, no VARCHAR), chromadb_id
- `conversations`: id UUID, title, created_at
- `messages`: conversation_id, role, content, citations JSONB, chunks_used JSONB, tokens_used
- `ingest_logs`: source, status, documents_fetched/new/updated, chunks_created

## Endpoints API
| Metodo | Ruta | Que hace |
|--------|------|----------|
| GET | `/api/health` | Health check |
| POST | `/api/chat` | Chat con SSE streaming + RAG |
| GET | `/api/areas` | Lista areas legales |
| GET | `/api/documents/stats` | Stats: total docs + chunks |
| GET | `/api/documents` | Lista documentos indexados |
| POST | `/api/ingest/boe` | Trigger ingesta BOE (background) |
| GET | `/api/ingest/status` | Ultimo log de ingesta |

## Paleta colores frontend
- Fondos: `#0A0E1A` (principal), `#111827` (cards), `#1F2937` (hover)
- Dorado: `#C9A84C` (acento), `#E5C76B` (hover)
- Azul: `#1E3A5F` (institucional)
- Texto: `#F3F4F6` (principal), `#9CA3AF` (secundario)

## Comandos utiles
```bash
# Restart servicio
sudo systemctl restart nexuslex
sudo systemctl status nexuslex

# Ver logs
sudo journalctl -u nexuslex -f --no-pager

# Re-deploy frontend
cd /home/ubuntu/proyectos-cloud/nexuslex && bash deploy.sh

# Re-ingestar leyes
cd /home/ubuntu/proyectos-cloud/nexuslex && source backend/venv/bin/activate && python -c "
from backend.ingestion.embedder import run_full_ingestion
import asyncio
asyncio.run(run_full_ingestion())
"

# Consultar stats
curl https://navas.alexamigo.com/api/documents/stats
curl https://navas.alexamigo.com/api/health
```

## Errores conocidos y fixes aplicados
1. **Password `!` en PostgreSQL**: el `!` en connection string causa auth failure. Se quito: `NexusLex2026pg`
2. **SQLAlchemy `metadata` reservado**: usar `extra_metadata = Column("metadata", JSON)`
3. **Gemini embedding model**: usar `gemini-embedding-001` (estable), NO `gemini-embedding-exp-03-07`
4. **OpenAI GPT-5.4+**: usar `max_completion_tokens` en vez de `max_tokens`
5. **FastAPI catch-all `/{path:path}`**: debe ir DESPUES de los API routers, y guardia `if full_path.startswith("api/")`
6. **`section_title`**: cambiado de VARCHAR(500) a TEXT (articulos del CC tienen titulos muy largos)
7. **BOE leyes pre-1980**: solo PDF, `clean_boe_html()` devuelve "Texto no disponible" → skip automatico

## Escalabilidad LLM
- Cambiar modelo en `.env`: `LLM_MODEL=gpt-5.4-mini` (mas potente, ~$5/mes)
- Sin cambiar codigo, solo reiniciar servicio

## Posibles mejoras futuras
- Indexar mas leyes (derecho civil, mercantil, fiscal)
- Añadir las 3 leyes que solo tienen PDF (OCR o descarga manual)
- JWT auth real si se comparte con mas gente
- Historial de conversaciones en sidebar
- Exportar conversacion a PDF