# 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: ```text 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: ```text 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: ```text 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: ```text "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: 1. **Consolide** las implementaciones existentes de typedialog-ai y provisioning 2. **Detecte** credenciales CLI y subscripciones antes de usar API keys 3. **Implemente** fallback automático con circuit breaker 4. **Añada** caching de requests para reducir costos 5. **Integre** Kogral para enriquecer contexto 6. **Sea usado** por todos los proyectos del ecosistema ### Arquitectura ```text ┌─────────────────────────────────────────────────────────┐ │ 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 1. Single source of truth para lógica de LLM 2. CLI detection reduce costos 70-80% 3. Circuit breaker + fallback = alta disponibilidad 4. 15-30% menos requests en desarrollo (caching) 5. Kogral mejora calidad de generación 6. Feature-gated: cada feature es opcional ### Negativas 1. **Migration effort**: Refactorizar Vapora, TypeDialog, Provisioning 2. **New dependency**: Proyectos dependen de stratumiops 3. **CLI auth complexity**: Diferentes formatos de credenciales por versión 4. **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 1. Crear stratum-llm con API compatible con typedialog-ai 2. typedialog-ai re-exporta stratum-llm (backward compatible) 3. Vapora migra a stratum-llm directamente 4. Provisioning migra su LlmClient a stratum-llm 5. 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/`