10 KiB
ADR-002: Stratum-LLM - Biblioteca Unificada de Providers LLM
Estado
Propuesto
Contexto
Estado Actual: Conexiones LLM Fragmentadas
El ecosistema stratumiops tiene 4 proyectos con funcionalidad IA, cada uno con su propia implementación:
| Proyecto | Implementación | Providers | Duplicación |
|---|---|---|---|
| Vapora | typedialog-ai (path dep) |
Claude, OpenAI, Ollama | Base compartida |
| TypeDialog | typedialog-ai (local) |
Claude, OpenAI, Ollama | Define la abstracción |
| Provisioning | Custom LlmClient |
Claude, OpenAI | 100% duplicado |
| Kogral | rig-core |
Solo embeddings | Diferente stack |
Problemas Identificados
1. Duplicación de Código
Provisioning reimplementa lo que TypeDialog ya tiene:
- reqwest HTTP client
- Headers: x-api-key, anthropic-version
- JSON body formatting
- Response parsing
- Error handling
Impacto: ~500 líneas duplicadas, bugs arreglados en un lugar no se propagan.
2. Solo API Keys, No CLI Detection
Ningún proyecto detecta credenciales de CLIs oficiales:
Claude CLI: ~/.config/claude/credentials.json
OpenAI CLI: ~/.config/openai/credentials.json
Impacto: Usuarios con Claude Pro/Max ($20-100/mes) pagan API tokens cuando podrían usar su suscripción.
3. Sin Fallback Automático
Cuando un provider falla (rate limit, timeout), la request falla completamente:
Actual: Request → Claude API → Rate Limit → ERROR
Deseado: Request → Claude API → Rate Limit → OpenAI → Success
4. Sin Circuit Breaker
Si Claude API está caído, cada request intenta conectar, falla, y propaga el error:
Request 1 → Claude → Timeout (30s) → Error
Request 2 → Claude → Timeout (30s) → Error
Request 3 → Claude → Timeout (30s) → Error
Impacto: Latencia acumulada, UX degradado.
5. Sin Caching
Requests idénticas van siempre a la API:
"Explain this Rust error" → Claude → $0.003
"Explain this Rust error" → Claude → $0.003 (mismo resultado)
Impacto: Costos innecesarios, especialmente en desarrollo/testing.
6. Kogral No Integrado
Kogral tiene guidelines y patterns que podrían enriquecer el contexto de LLM, pero no hay integración.
Decisión
Crear stratum-llm como crate unificado que:
- Consolide las implementaciones existentes de typedialog-ai y provisioning
- Detecte credenciales CLI y subscripciones antes de usar API keys
- Implemente fallback automático con circuit breaker
- Añada caching de requests para reducir costos
- Integre Kogral para enriquecer contexto
- Sea usado por todos los proyectos del ecosistema
Arquitectura
┌─────────────────────────────────────────────────────────┐
│ stratum-llm │
├─────────────────────────────────────────────────────────┤
│ CredentialDetector │
│ ├─ Claude CLI → ~/.config/claude/ (subscription) │
│ ├─ OpenAI CLI → ~/.config/openai/ │
│ ├─ Env vars → *_API_KEY │
│ └─ Ollama → localhost:11434 (free) │
│ │ │
│ ▼ │
│ ProviderChain (ordered by priority) │
│ [CLI/Sub] → [API] → [DeepSeek] → [Ollama] │
│ │ │ │ │ │
│ └──────────┴─────────┴───────────┘ │
│ │ │
│ CircuitBreaker per provider │
│ │ │
│ RequestCache │
│ │ │
│ KogralIntegration │
│ │ │
│ UnifiedClient │
│ │
└─────────────────────────────────────────────────────────┘
Justificación
Por Qué No Usar Otra Crate Externa
| Alternativa | Por Qué No |
|---|---|
| kaccy-ai | Orientada a blockchain/fraud detection |
| llm (crate) | Muy básica, sin circuit breaker ni caching |
| langchain-rust | Port de Python, no idiomático Rust |
| rig-core | Solo embeddings/RAG, no chat completion |
Mejor opción: Construir sobre typedialog-ai y añadir features faltantes.
Por Qué CLI Detection es Importante
Análisis de costos para usuario típico:
| Escenario | Costo Mensual |
|---|---|
| Solo API (actual) | ~$840 |
| Claude Pro + API overflow | ~$20 + ~$200 = $220 |
| Claude Max + API overflow | ~$100 + ~$50 = $150 |
Ahorro potencial: 70-80% detectando y usando subscripciones primero.
Por Qué Circuit Breaker
Sin circuit breaker, un provider caído causa:
- N requests × 30s timeout = N×30s de latencia total
- Todos los recursos ocupados esperando timeouts
Con circuit breaker:
- Primera falla abre circuito
- Siguientes requests fallan inmediatamente (fast fail)
- Fallback a otro provider sin esperar
- Circuito se resetea después de cooldown
Por Qué Caching
Para desarrollo típico:
- Mismas preguntas repetidas mientras se itera
- Testing ejecuta mismos prompts múltiples veces
Cache hit rate estimado: 15-30% en desarrollo activo.
Por Qué Kogral Integration
Kogral tiene guidelines por lenguaje, patterns por dominio, y ADRs. Sin integración el LLM genera código genérico; con integración genera código que sigue convenciones del proyecto.
Consecuencias
Positivas
- Single source of truth para lógica de LLM
- CLI detection reduce costos 70-80%
- Circuit breaker + fallback = alta disponibilidad
- 15-30% menos requests en desarrollo (caching)
- Kogral mejora calidad de generación
- Feature-gated: cada feature es opcional
Negativas
- Migration effort: Refactorizar Vapora, TypeDialog, Provisioning
- New dependency: Proyectos dependen de stratumiops
- CLI auth complexity: Diferentes formatos de credenciales por versión
- Cache invalidation: Respuestas obsoletas si no se gestiona bien
Mitigaciones
| Negativo | Mitigación |
|---|---|
| Migration effort | Re-export API compatible desde typedialog-ai |
| New dependency | Path dependency local, no crates.io |
| CLI auth complexity | Version detection, fallback a API si falla |
| Cache invalidation | TTL configurable, opción de bypass |
Métricas de Éxito
| Métrica | Actual | Objetivo |
|---|---|---|
| Líneas de código duplicadas | ~500 | 0 |
| CLI credential detection | 0% | 100% |
| Fallback success rate | 0% | >90% |
| Cache hit rate | 0% | 15-30% |
| Latency (provider down) | 30s+ | <1s (fast fail) |
Análisis de Impacto en Costos
Basado en datos reales de uso ($840/mes):
| Escenario | Ahorro |
|---|---|
| CLI detection (Claude Max) | ~$700/mes |
| Caching (15% hit rate) | ~$50/mes |
| DeepSeek fallback para código | ~$100/mes |
| Total potencial | $500-700/mes |
Estrategia de Migración
Fases de Migración
- Crear stratum-llm con API compatible con typedialog-ai
- typedialog-ai re-exporta stratum-llm (backward compatible)
- Vapora migra a stratum-llm directamente
- Provisioning migra su LlmClient a stratum-llm
- Deprecar typedialog-ai, consolidar en stratum-llm
Adopción de Features
| Feature | Adopción |
|---|---|
| Basic providers | Inmediata (reemplazo directo) |
| CLI detection | Opcional, feature flag |
| Circuit breaker | Default on |
| Caching | Default on, configurable TTL |
| Kogral | Feature flag, requiere Kogral instalado |
Alternativas Consideradas
Alternativa 1: Mejorar typedialog-ai In-Place
Pros: No requiere nuevo crate
Cons: TypeDialog es proyecto específico, no infraestructura compartida
Decisión: stratum-llm en stratumiops es mejor ubicación para infraestructura cross-project.
Alternativa 2: Usar LiteLLM (Python) como Proxy
Pros: Muy completo, 100+ providers
Cons: Dependencia Python, latencia de proxy, no Rust-native
Decisión: Mantener stack Rust puro.
Alternativa 3: Cada Proyecto Mantiene su Implementación
Pros: Independencia
Cons: Duplicación, inconsistencia, bugs no compartidos
Decisión: Consolidar es mejor a largo plazo.
Referencias
Implementaciones Existentes:
- TypeDialog:
typedialog/crates/typedialog-ai/ - Vapora:
vapora/crates/vapora-llm-router/ - Provisioning:
provisioning/platform/crates/rag/
Kogral: kogral/
Ubicación Objetivo: stratumiops/crates/stratum-llm/