Skip to main content

Entorno local

Requisitos previos​

  • Node.js 20+ y pnpm (no usar npm/yarn en ningún repo de Aura).
  • Docker (para PostgreSQL + pgvector).
  • Ajolote corriendo (servicio de auth) — o acceso al ajolote de dev.
  • Token de GitHub con scope read:packages para instalar los paquetes privados @cuatro-quinas/*.

GitHub Packages (una sola vez)​

Los paquetes @cuatro-quinas/ajolote-* se publican en GitHub Packages. Configurá tu ~/.npmrc de usuario (no el del repo):

//npm.pkg.github.com/:_authToken=ghp_TU_TOKEN

Alternativa por sesión: export GITHUB_TOKEN=ghp_... antes de pnpm install.

Backend (Aura-reloaded)​

pnpm install
cp env.example .env # completar DATABASE_URL, OPENAI_API_KEY, etc.
docker compose up -d postgres # PostgreSQL + pgvector
docker compose up -d rabbitmq # (opcional) cola de /anfibios/transcriptions — UI en :15672
pnpm db:push # aplica el schema (solo dev)
pnpm ingest:manuals # (opcional) carga manuales en knowledge_base
pnpm start:dev # → API en http://localhost:3000

El módulo Transcripciones (/anfibios/transcriptions) es solo local: necesita RabbitMQ (el docker compose up -d rabbitmq de arriba) + RABBITMQ_URL. Sin eso, subir audios responde 503; el resto de Aura anda igual.

Variables de entorno (backend)​

VariableDefaultReq.Descripción
DATABASE_URL—✅Connection string de PostgreSQL
OPENAI_API_KEY—✅Embeddings (siempre OpenAI)
AUTH_SERVICE_URL—✅URL del ajolote (mismo que usa el front)
AUTH_APP_IDaura—App id en ajolote
LLM_PROVIDERopenai—openai | groq | cerebras | mistral | mock
LLM_MODELgpt-4o-mini—Modelo de chat
LLM_API_KEY= OPENAI_API_KEY—API key del provider de chat
LLM_BASE_URL——Base URL para providers no-OpenAI
EMBEDDING_MODELtext-embedding-3-small—Modelo de embeddings (runtime)
SIMILARITY_THRESHOLD0.78—Umbral de hit en memory_embeddings. Solo semilla del primer boot (ver abajo)
APPROVED_THRESHOLD0.85—Umbral para ejemplos aprobados. Solo semilla del primer boot
KNOWLEDGE_THRESHOLD0.60—Umbral de hit en knowledge_base. Solo semilla del primer boot
QUERY_LATENCY_BUDGET_MS500—Timeout de la query RAG (ms)
CUSTOM_LLM_TOKEN_VOICE——Bearer del agente de voz de ElevenLabs. Vacío = POST /chat/completions rechaza todo
CUSTOM_LLM_TOKEN_WHATSAPP——Bearer del agente de WhatsApp. Distinto del de voz: el token es el que elige el canal
PORT3000—Puerto HTTP/WS
RETELL_API_KEY / RETELL_SECRET——Retell (transferencias / auth webhook)
PATIENT_LOOKUP_URL——Base URL del sistema de pacientes para el lookup (AURA_SHARED_SECRET es el secreto compartido de ese endpoint). Vacío = el agente funciona, pero sin filtro de equipo en el RAG, y SecurityBootLogger lo avisa al boot. Antes se llamaba EXISTING_BACKEND_URL, que se sigue leyendo como fallback
PATIENT_LOOKUP_PROVIDERoxitesa—Qué adaptador contesta: oxitesa | mock | disabled. Un valor desconocido rompe el arranque en vez de degradar en silencio. Sin setear queda oxitesa, que contesta "sin conexión" hasta que se cargue la URL y funciona apenas se carga. mock resuelve desde un roster en memoria; disabled apaga la consulta y es la salida de emergencia
PATIENT_LOOKUP_MOCK——JSON [{ dni, phones, equipment }] que pisa el roster por defecto del mock. Los teléfonos se normalizan al cargar, así que se escriben como venga
DEEPGRAM_API_KEY——STT (transcripción en vivo y de audios subidos en /anfibios)
RABBITMQ_URL——Cola de transcripción de audios (solo local, ver docker-compose). Sin ella la subida da 503
TRANSCRIPTION_CONCURRENCY1—Jobs de transcripción en paralelo (prefetch)
TRANSCRIPTION_MAX_ATTEMPTS3—Reintentos antes de mandar el job a la DLQ

Cambiar de provider LLM es solo .env (sin recompilar), ej. LLM_PROVIDER=mock para dev sin API keys.

Los tres umbrales de RAG ya no se leen de la ENV

Ahora son ajustes runtime con scope por canal. La ENV se usa una sola vez: en el primer boot, para sembrar la fila scope='global' de cada uno. Después de eso, cambiar KNOWLEDGE_THRESHOLD en el .env no hace nada — hay que tocar el setting (panel de /anfibios/settings, o PATCH con ?scope= para un canal). Siguen declaradas para que el primer arranque de cada entorno conserve el valor que ya tenía.

Frontend (aura-front)​

pnpm install
cp .env.example .env.local
pnpm dev # → http://localhost:5173

Variables de entorno (frontend)​

VITE_API_BASE_URL=http://localhost:3000 # backend de Aura
VITE_AUTH_URL=http://localhost:5173 # dev server (proxy a ajolote)
VITE_AUTH_PROXY_TARGET=http://100.64.188.115:3010 # ajolote real (dev)
VITE_COPILOT_WS_URL=ws://localhost:3000/copilot/TU_AGENT_ID
VITE_USE_MOCK=false # true = levanta sin backend
VITE_SHOW_SOURCE_FILTER=true # muestra filtro de origen en paneles
Cookie de ajolote en dev

En dev el front le pega a su propio dev server y Vite proxya /api/auth al ajolote real (VITE_AUTH_PROXY_TARGET). Esto es porque la cookie de sesión sale SameSite=Lax y no viaja cross-site. Sin el proxy, get-session devuelve null y nunca quedás logueado. Detalle en Autenticación.

Probar el Custom LLM por curl (y los acentos)​

POST /chat/completions es el endpoint que llama ElevenLabs, y en dev se prueba con curl imitando ese request. El body tiene que ser UTF-8. Escribir el JSON con acentos directo en la línea de comandos en Windows manda los bytes en cp1252 (á = 0xE1), que no es UTF-8 válido: el parser los reemplaza por <?> y el turno llega dañado — pierde el match de equipos en el RAG y le entra ruido al modelo. El backend loguea un [TURN] ... received invalid UTF-8 cuando eso pasa, así que si lo ves en el log, el problema está en el cliente, no en Aura.

La forma segura es un archivo y --data-binary:

# el archivo queda en UTF-8 real, sin depender del codepage de la consola
cat > /tmp/turno.json <<'JSON'
{"messages":[{"role":"user","content":"el M50 tira H08 y la cánula está doblada"}]}
JSON

curl -X POST localhost:3000/chat/completions \
-H "Authorization: Bearer $CUSTOM_LLM_TOKEN_WHATSAPP" \
-H 'Content-Type: application/json' -H 'x-conversation-id: demo-1' \
--data-binary @/tmp/turno.json

Y para que la salida se lea bien (los á que se ven como á o ? son la consola, no los datos): chcp 65001 en la terminal de Windows, y PYTHONIOENCODING=utf-8 si inspeccionás las respuestas con Python. Los datos ya están bien: el endpoint responde application/json; charset=utf-8, el SSE declara text/event-stream; charset=utf-8 y en Postgres á está guardado como \303\241.

Ajolote (auth)​

El backend y el front tienen que apuntar al mismo ajolote (AUTH_SERVICE_URL == VITE_AUTH_PROXY_TARGET), o los roles no matchean. La administración de usuarios y roles (crear tenant, usuarios, asignar roles) se hace contra ajolote, no desde Aura — ver Autenticación → Roles.