stratumiops/docs/es/architecture/adrs/002-stratum-llm.md

280 lines
10 KiB
Markdown
Raw Normal View History

# 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/`