Files
darroyo 4c9ed3c24b Initial commit: LlamaLink Go rewrite
Complete rewrite from Python/FastAPI to Go/Gin:
- Go backend: auth (API keys + bcrypt), llama.cpp subprocess manager,
  hot-swap multi-model, rate limiting, quota system, webhooks
- Vue 3 SPA admin panel (src/) with Tailwind CSS
- Deployment: Docker multi-stage, docker-compose, nginx, systemd
- GORM/SQLite models: ApiKey, Model, UsageLog, Quota, Webhook
- REST API: /api/v1/admin/* (keys, models, chat, usage, health)
- Embedded frontend via go:embed (build output at web/dist/)

Removed legacy Python artifacts (app/, tests/, pyproject.toml, etc.)
2026-07-30 10:58:55 -04:00

9.4 KiB

LlamaLink — Especificación Técnica (Living Document)

Este documento se actualiza conforme evoluciona la implementación. Última actualización: implementación inicial.

1. Descripción General

LlamaLink es un servidor middleware/gateway que permite ejecutar modelos de lenguaje (LLMs) de forma local usando llama.cpp, exponiéndolos de manera segura a través de una API REST propia protegida con API keys, permitiendo así el acceso remoto controlado a la inferencia.

Objetivos principales

  • Ejecutar modelos GGUF localmente mediante llama.cpp.
  • Exponer un endpoint HTTP compatible con el estándar OpenAI (/v1/chat/completions).
  • Autenticar y autorizar clientes remotos mediante API keys.
  • Controlar uso, límites y seguridad del acceso a los modelos.

2. Arquitectura

Cliente remoto (con API key)
        │
        ▼
┌─────────────────────┐
│   LlamaLink API      │  ← Gateway (FastAPI)
│  - Autenticación      │
│  - Rate limiting      │
│  - Enrutamiento       │
│  - Logs y métricas    │
└──────────┬───────────┘
           │ (proxy HTTP)
           ▼
┌─────────────────────┐
│    llama.cpp          │  ← Motor de inferencia
│   (llama-server)      │
└─────────────────────┘

Integración con llama.cpp

  • Opción A (implementada): LlamaLink actúa como gateway/proxy delante del servidor HTTP nativo de llama.cpp (llama-server).
  • Opción B: Uso de llama-cpp-python embebido (futuro).

Multi-modelo

  • Se soporta la configuración de múltiples modelos en la base de datos.
  • Solo un modelo está activo/cargado en llama-server en un momento dado.
  • Si llega una request para un modelo diferente al activo:
    1. Se rechaza con HTTP 503 + Retry-After mientras dura el swap
    2. El admin puede disparar el swap manualmente vía POST /v1/models/{name}/load
    3. Ocurre automáticamente en lazy mode si MANAGE_LLAMA_SERVER=true
  • El swap es secuencial (un asyncio.Lock evita swaps concurrentes).

3. Stack Tecnológico

Componente Tecnología Justificación
Motor de inferencia llama.cpp (C++) Ya compilado y optimizado, no requiere reescritura
API Gateway Python 3.11+ / FastAPI Desarrollo rápido, ecosistema maduro, buena documentación automática (OpenAPI/Swagger)
Servidor ASGI Uvicorn Estándar para FastAPI, soporta async y alto rendimiento
Base de datos SQLite (MVP) → PostgreSQL (producción) Escalable según necesidad
ORM SQLAlchemy 2.0 (async) Manejo de modelos y migraciones
Autenticación API Keys con hash SHA-256 Nunca se almacenan en texto plano
Rate limiting slowapi (memoria o Redis) Control de abuso por IP
Reverse proxy / TLS Nginx o Caddy HTTPS obligatorio en producción
Contenedores Docker + docker-compose Despliegue reproducible

4. Estructura de Carpetas

llamalink/
├── app/
│   ├── main.py              # Punto de entrada FastAPI + lifespan
│   ├── config.py            # Configuración pydantic-settings
│   ├── __init__.py
│   ├── core/
│   │   ├── __init__.py
│   │   ├── security.py     # Hash/verify API keys (SHA-256)
│   │   ├── errors.py       # JSON error handlers estandarizados
│   │   └── logging.py      # Structured logging
│   ├── db/
│   │   ├── __init__.py
│   │   ├── base.py         # SQLAlchemy declarative base
│   │   ├── session.py      # Engine + session factory
│   │   └── models.py       # ORM models
│   ├── schemas/
│   │   ├── __init__.py
│   │   ├── common.py       # ErrorResponse, HealthResponse
│   │   ├── chat.py         # OpenAI-compat request/response
│   │   └── keys.py         # Key management schemas
│   ├── auth/
│   │   ├── __init__.py
│   │   ├── service.py      # CRUD keys, hashing
│   │   └── dependencies.py # Bearer auth FastAPI deps
│   ├── routers/
│   │   ├── __init__.py
│   │   ├── chat.py         # /v1/chat/completions
│   │   ├── keys.py         # /v1/keys CRUD
│   │   └── health.py       # /health, /ready
│   └── services/
│       ├── __init__.py
│       ├── llama_proxy.py  # httpx async client → llama-server
│       ├── rate_limiter.py # slowapi setup
│       ├── usage_tracker.py # Token counting + DB logs
│       ├── quota.py        # Chequeo de cuota mensual
│       ├── webhook.py      # Notificaciones al superar cuota
│       ├── model_manager.py # Subprocess lifecycle + state machine
│       └── models_registry.py # Mapa de modelos configurados
├── migrations/              # Alembic
├── deploy/                 # Nginx, Caddy, systemd units
├── docker/                 # Dockerfiles
├── scripts/                # CLI tools
├── tests/
├── pyproject.toml
├── .env.example
└── README.md

5. Endpoints Implementados

Método Endpoint Descripción Auth
POST /v1/chat/completions Enviar prompt y recibir respuesta del modelo
POST /v1/keys Crear nueva API key Sí (admin)
GET /v1/keys Listar API keys existentes Sí (admin)
DELETE /v1/keys/{key_id} Revocar una API key Sí (admin)
GET /health Verificar estado del servicio No
GET /ready Verificar listo para servir No

Pendiente de implementar (Fase 4+)

Método Endpoint Descripción
GET /v1/models Listar modelos configurados
GET /v1/models/active Mostrar cuál está cargado
POST /v1/models/{name}/load Cargar/swap a un modelo
GET /v1/usage/{key_id} Métricas de uso de una key
GET/DELETE /admin/* Panel web de administración

6. Variables de Entorno

LLAMALINK_ENV=development
LLAMALINK_HOST=0.0.0.0
LLAMALINK_PORT=8000
LLAMALINK_SECRET_KEY=changeme

DATABASE_URL=sqlite+aiosqlite:///./llamalink.db

MANAGE_LLAMA_SERVER=true
LLAMA_SERVER_BIN=llama-server
LLAMA_SERVER_HOST=127.0.0.1
LLAMA_SERVER_PORT=8080
LLAMA_SERVER_STARTUP_TIMEOUT=120
MODEL_SWAP_COOLDOWN=2
LLAMA_SERVER_STOP_TIMEOUT=10

RATE_LIMIT_PER_MINUTE=60
RATE_LIMIT_STORAGE=memory

LOG_LEVEL=INFO
LOG_FORMAT=json

ADMIN_API_KEY=changeme

7. Modelo de Datos

ApiKey

  • id, name, hashed_key, key_prefix
  • scopes: CSV de permisos (chat,models,usage,admin)
  • is_active, is_admin, created_at, last_used_at, owner_label

Model

  • id, name, model_path, alias
  • ctx_size, n_gpu_layers, extra_args (JSON)
  • is_default, is_enabled, is_active, loaded_at

UsageLog

  • api_key_id, model_name, endpoint
  • prompt_tokens, completion_tokens, total_tokens
  • latency_ms, status, ip_address, user_agent
  • error_message, created_at

Quota

  • api_key_id, model_scope (None = global)
  • period_start, period_end
  • tokens_limit, tokens_used

Webhook

  • api_key_id, url, event, secret (HMAC)
  • is_active, created_at

8. Estados del ModelManager

STOPPED ──load()──> LOADING ──success──> READY
  ↑                          │
  │                       fail
  │                          ↓
  └──<─────── FAILED      FAILED
                │
                └──swap()──> SWAPPING ──success──> READY

9. Formato de Errores

Todos los errores siguen este formato:

{
  "error": {
    "code": "error_code_string",
    "message": "Descripción legible",
    "retry_after_seconds": 30
  }
}

Códigos: invalid_api_key, api_key_revoked, rate_limit_exceeded, model_not_found, model_loading, quota_exceeded, upstream_error, internal_error, validation_error, unauthorized


10. Roadmap de Implementación

Fase 0 — Esqueleto

  • pyproject.toml, config, main, db, .env.example, .gitignore

Fase 1 — MVP

  • Auth (hash, service, deps)
  • llama_proxy (httpx async, streaming)
  • /v1/chat/completions (non-stream + stream)
  • /v1/keys CRUD
  • /health, /ready
  • Error handlers estandarizados
  • Quota service (stub)
  • Rate limiter (stub)

🔄 Fase 2 — Endurecimiento

  • Rate limiting (slowapi)
  • Usage tracker middleware
  • Logging estructurado JSON

📋 Fase 3 — Docker + Producción

  • docker-compose.yml
  • Dockerfile.api, Dockerfile.llama
  • Nginx.conf, Caddyfile
  • systemd units

📋 Fase 4 — Multi-modelo + Streaming

  • model_manager (subprocess lifecycle, state machine)
  • streaming en /v1/chat/completions
  • /v1/models CRUD
  • Lazy swap + admin trigger
  • 503 + Retry-After durante swap

📋 Fase 5 — Cuotas + Webhooks

  • Quota enforcement middleware
  • Webhook delivery con HMAC
  • Background task para notificaciones

📋 Fase 6 — Panel Admin Web

  • Jinja2 templates + htmx
  • /admin/* routes
  • Crear/revocar keys desde UI
  • Ver métricas y logs

📋 Fase 7 — Polish

  • Validación estricta de inputs
  • Auto-revoke en actividad sospechosa
  • Cobertura de tests ≥ 80%
  • ruff + mypy limpios