280 lines
10 KiB
Markdown
280 lines
10 KiB
Markdown
# 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/`
|