feat(proxy): auto-discovery de modelos + backend Kimchi
- Nueva interfaz Discoverer con Refresh() por backend - Kilo: full auto (isFree + metadata completa del upstream) - OpenCode: heurística -free para descubrir nuevos free - Kimchi: agrega todos los IDs no-curados del upstream - discoveryLoop cada 10 min en el Proxy - Backends stateful (mutex + catálogo dinámico) - CREDENCIALES.md y KIMCHI_ANALISIS.md documentan el setup
This commit is contained in:
@@ -0,0 +1,275 @@
|
||||
# Kimchi Dev — Análisis Completo para Integración en Free IDE Proxy
|
||||
|
||||
## 1. Instalación
|
||||
|
||||
```bash
|
||||
which kimchi
|
||||
# /home/ren/.local/bin/kimchi
|
||||
```
|
||||
|
||||
## 2. Configuración Local
|
||||
|
||||
### Archivos relevantes
|
||||
|
||||
| Archivo | Propósito |
|
||||
|---------|-----------|
|
||||
| `~/.config/kimchi/config.json` | Config principal: apiKey, skillPaths, deviceId, onboarding |
|
||||
| `~/.config/kimchi/harness/models.json` | Definiciones de proveedores y modelos |
|
||||
| `~/.config/kimchi/harness/settings.json` | Settings: defaultProvider, defaultModel, multiModel, etc. |
|
||||
| `~/.config/kimchi/harness/auth.json` | Auth por proveedor (oauth, access token) |
|
||||
|
||||
### `~/.config/kimchi/config.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"skillPaths": [
|
||||
".config/kimchi/harness/skills",
|
||||
".config/opencode/skills"
|
||||
],
|
||||
"migrationState": "done",
|
||||
"apiKey": "castai_v1_befd8c666ce652d47cc08accf8c5accc0a5aaca9e1f006392df69ac123198f9b_4f8f2d40",
|
||||
"onboarding": {
|
||||
"sessionModeWizardSeenAt": "2026-06-29T13:46:41.351Z"
|
||||
},
|
||||
"deviceId": "9fb28c0e-dd65-4bac-8c90-c98ac1c26382"
|
||||
}
|
||||
```
|
||||
|
||||
### `~/.config/kimchi/harness/auth.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"kimchi-dev": {
|
||||
"type": "oauth",
|
||||
"access": "castai_v1_befd8c666ce652d47cc08accf8c5accc0a5aaca9e1f006392df69ac123198f9b_4f8f2d40",
|
||||
"refresh": "",
|
||||
"expires": 9007199254740991
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `~/.config/kimchi/harness/settings.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"quietStartup": true,
|
||||
"theme": "kimchi-minimal",
|
||||
"retry": { "maxRetries": 10 },
|
||||
"lastChangelogVersion": "0.1.50",
|
||||
"defaultProvider": "kimchi-dev",
|
||||
"defaultModel": "kimi-k2.7",
|
||||
"defaultThinkingLevel": "medium",
|
||||
"multiModel": true,
|
||||
"rtkAutoInstallCheckedAt": 1782740800268
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Endpoint del API
|
||||
|
||||
### Configuración del proveedor (desde `models.json`)
|
||||
|
||||
```json
|
||||
"kimchi-dev": {
|
||||
"baseUrl": "https://llm.kimchi.dev/openai/v1",
|
||||
"apiKey": "$KIMCHI_API_KEY",
|
||||
"api": "openai-completions",
|
||||
"authHeader": true,
|
||||
"headers": {
|
||||
"User-Agent": "kimchi/0.1.50"
|
||||
},
|
||||
"models": [
|
||||
...
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Resumen del endpoint
|
||||
|
||||
| Campo | Valor |
|
||||
|-------|-------|
|
||||
| **Base URL** | `https://llm.kimchi.dev/openai/v1` |
|
||||
| **Chat Completions** | `POST https://llm.kimchi.dev/openai/v1/chat/completions` |
|
||||
| **Models listing** | `GET https://llm.kimchi.dev/openai/v1/models` |
|
||||
| **API format** | OpenAI Chat Completions estándar |
|
||||
| **Auth** | `Authorization: Bearer <access_token>` (`authHeader: true`) |
|
||||
| **User-Agent** | `kimchi/0.1.50` |
|
||||
| **Provider upstream** | castai (ai-enabler) |
|
||||
|
||||
### Auth token
|
||||
|
||||
- **Access token (hardcodeado):** `castai_v1_befd8c666ce652d47cc08accf8c5accc0a5aaca9e1f006392df69ac123198f9b_4f8f2d40`
|
||||
- **Tipo:** oauth
|
||||
- **Expira:** `9007199254740991` (nunca, es un token estático)
|
||||
- **Env var equivalente:** `KIMCHI_API_KEY` (el provider usa `${KIMCHI_API_KEY}` en `apiKey`)
|
||||
|
||||
### Verificación con curl
|
||||
|
||||
```bash
|
||||
# Listar modelos
|
||||
curl -s https://llm.kimchi.dev/openai/v1/models \
|
||||
-H "Authorization: Bearer castai_v1_befd8c666ce652d47cc08accf8c5accc0a5aaca9e1f006392df69ac123198f9b_4f8f2d40" \
|
||||
-H "User-Agent: kimchi/0.1.50"
|
||||
|
||||
# Chat completion
|
||||
curl -s https://llm.kimchi.dev/openai/v1/chat/completions \
|
||||
-H "Authorization: Bearer castai_v1_befd8c666ce652d47cc08accf8c5accc0a5aaca9e1f006392df69ac123198f9b_4f8f2d40" \
|
||||
-H "User-Agent: kimchi/0.1.50" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"model": "deepseek-v4-flash",
|
||||
"messages": [{"role": "user", "content": "Decime solo SI"}],
|
||||
"max_tokens": 500
|
||||
}'
|
||||
```
|
||||
|
||||
## 4. Modelos
|
||||
|
||||
### Categorización
|
||||
|
||||
Hay **13 modelos** que responde el upstream, pero **5 están curados** (con metadata completa en `models.json`). Los otros 8 aparecen en `/models` pero no tienen definición local.
|
||||
|
||||
### Los 5 curados (metadata de `models.json`)
|
||||
|
||||
| Modelo ID | Contexto | Max Tokens | Razonamiento | Vision | Tags |
|
||||
|-----------|----------|-----------|:---:|:---:|------|
|
||||
| `deepseek-v4-flash` | 1,048,576 (1M) | 1,048,576 (1M) | ✅ Sí | ❌ | DeepSeek V4, 284B params, propósito general |
|
||||
| `glm-5.2-fp8` | 1,048,576 (1M) | 1,048,576 (1M) | ✅ Sí | ❌ | GLM, FP8 cuantizado, rendimiento |
|
||||
| `kimi-k2.7` | 262,144 (262K) | 262,144 (262K) | ✅ Sí | ✅ Sí | Moonshot AI, multimodal, visión |
|
||||
| `minimax-m3` | 1,048,576 (1M) | 1,048,576 (1M) | ✅ Sí | ✅ Sí | MiniMax, multimodal, texto+imagen |
|
||||
| `nemotron-3-ultra-fp4` | 1,048,576 (1M) | 1,048,576 (1M) | ✅ Sí | ❌ | NVIDIA, 550B params, FP4 |
|
||||
|
||||
Costos: todos gratuitos (0 input, 0 output, 0 cache).
|
||||
|
||||
### Los 8 extras del upstream (sin metadata)
|
||||
|
||||
| Modelo ID | Notas |
|
||||
|-----------|-------|
|
||||
| `kimi-k2.5` | Versión anterior de kimi |
|
||||
| `kimi-k2.6` | Versión anterior de kimi |
|
||||
| `minimax-m2.5` | Versión anterior de minimax |
|
||||
| `minimax-m2.7` | Versión anterior de minimax |
|
||||
| `nemotron-3-super-fp4` | Variante "super" de nemotron, posiblemente más grande |
|
||||
| `qwen3-coder-next-fp8` | 🔥 **Interesante**: modelo de código Qwen, FP8 |
|
||||
| `smollm2-135m` | Toy — 135M params |
|
||||
| `smollm2-360m` | Toy — 360M params |
|
||||
|
||||
### IDs en el upstream
|
||||
|
||||
El upstream responde con estos IDs exactos. No tienen sufijo `-free` (a diferencia de opencode). Esto evita colisiones con los modelos existentes del proxy.
|
||||
|
||||
### Comparativa con modelos existentes del proxy
|
||||
|
||||
| Modelo | En opencode backend | En kimchi backend |
|
||||
|--------|:-:|:-:|
|
||||
| deepseek-v4 | `deepseek-v4-flash-free` | `deepseek-v4-flash` (sin `-free`, distinto) |
|
||||
| nemotron-3 | `nemotron-3-ultra-free` | `nemotron-3-ultra-fp4` (distinto, cuantizado FP4) |
|
||||
|
||||
Son IDs diferentes, no hay colisión.
|
||||
|
||||
## 5. Estructura del Proxy Relevante para Integración
|
||||
|
||||
### Interfaz Backend (`internal/proxy/backend.go`)
|
||||
|
||||
```go
|
||||
type Backend interface {
|
||||
Name() string
|
||||
Models() []ModelInfo
|
||||
Resolve(model string) (resolved string, ok bool)
|
||||
ChatURL() string
|
||||
Headers(requestID, sessionID string) map[string]string
|
||||
}
|
||||
```
|
||||
|
||||
### Registro en `NewProxy` (`internal/proxy/models.go:61`)
|
||||
|
||||
```go
|
||||
func NewProxy(apiKey string) *Proxy {
|
||||
oc := OpenCodeBackend{}
|
||||
kl := KiloBackend{}
|
||||
// KimchiBackend{} acá
|
||||
p := &Proxy{
|
||||
backends: []Backend{kl, oc}, // ← agregar KimchiBackend
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Orden de backends
|
||||
|
||||
Más específicos primero, para que rutas exactas ganen sobre alias:
|
||||
|
||||
```
|
||||
KimchiBackend → KiloBackend → OpenCodeBackend (default)
|
||||
```
|
||||
|
||||
### Patrón de implementación: `kilo.go` (plantilla exacta)
|
||||
|
||||
`kilo.go` es el template perfecto:
|
||||
|
||||
1. Const con la base URL
|
||||
2. Struct vacío `KiloBackend struct{}`
|
||||
3. Slice `kiloFreeModels []ModelInfo` con los modelos
|
||||
4. Map `kiloAliases map[string]string`
|
||||
5. Implementar `Name()`, `Models()`, `Resolve()`, `ChatURL()`, `Headers()`
|
||||
|
||||
### Manejo del API key
|
||||
|
||||
3 opciones identificadas:
|
||||
|
||||
| Opción | Cómo | Pros | Contras |
|
||||
|--------|------|------|---------|
|
||||
| **A) Env var `KIMCHI_API_KEY`** | `os.Getenv("KIMCHI_API_KEY")` en `Headers()` | Sin secretos en git, clave en systemd `Environment=` | Requiere configurar env var |
|
||||
| **B) Hardcodear** | Const string igual que `"public"` en opencode | Simple | **La key queda en el repo** |
|
||||
| **C) Leer config.json** | Parsear `~/.config/kimchi/config.json` en startup | Auto, no requiere config extra | Acopla proxy a ruta fija del disco |
|
||||
|
||||
**Recomendación: Opción A (env var)**. La key se configura solo en `~/.config/systemd/user/free-ide-proxy.service`, fuera del repo.
|
||||
|
||||
### `ModelByID` y routing de visión
|
||||
|
||||
El proxy tiene `ModelByID()` en `opencode.go` que solo revisa `zenModels`. Si se integran modelos con visión de kimchi (`kimi-k2.7`, `minimax-m3`), `ModelByID` no los encontrará. Habría que extender `ModelByID()` para que revise todos los backends, o crear un map global por ID.
|
||||
|
||||
## 6. Posibles Alias para el Backend Kimchi
|
||||
|
||||
Cuidando de no pisar alias existentes de opencode/kilo:
|
||||
|
||||
| Alias propuesto | Resuelve a |
|
||||
|----------------|------------|
|
||||
| `kimchi/deepseek` | `deepseek-v4-flash` |
|
||||
| `kimchi/glm` | `glm-5.2-fp8` |
|
||||
| `kimchi/kimi` | `kimi-k2.7` |
|
||||
| `kimchi/minimax` | `minimax-m3` |
|
||||
| `kimchi/nemotron` | `nemotron-3-ultra-fp4` |
|
||||
| `kimchi/qwen` | `qwen3-coder-next-fp8` |
|
||||
| `kimchi` | `deepseek-v4-flash` (alias default) |
|
||||
|
||||
Sin alias cortos sueltos (`deepseek`, `nemotron`) para no pisar los de opencode.
|
||||
|
||||
## 7. Archivos a Modificar
|
||||
|
||||
| Archivo | Acción |
|
||||
|---------|--------|
|
||||
| `internal/proxy/kimchi.go` | **CREAR** — backend completo |
|
||||
| `internal/proxy/models.go` | **EDITAR** — registrar `KimchiBackend{}` en `NewProxy()` |
|
||||
| `internal/proxy/opencode.go` | **EDITAR** — extender `ModelByID()` global o crear `AllModelsByID()` |
|
||||
| `~/.config/systemd/user/free-ide-proxy.service` | **EDITAR** — agregar `Environment=KIMCHI_API_KEY=...` |
|
||||
| `CREDENCIALES.md` | **EDITAR** — agregar sección de kimchi |
|
||||
|
||||
## 8. Comandos de Build y Deploy
|
||||
|
||||
```bash
|
||||
# Build
|
||||
cd /home/ren/ide_proxy
|
||||
go build -o free-ide-proxy .
|
||||
|
||||
# Restart service
|
||||
systemctl --user daemon-reload
|
||||
systemctl --user restart free-ide-proxy
|
||||
|
||||
# Check logs
|
||||
journalctl --user -u free-ide-proxy -f
|
||||
|
||||
# Test
|
||||
curl -s http://127.0.0.1:6446/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"model":"deepseek-v4-flash","messages":[{"role":"user","content":"Hola"}],"max_tokens":500}'
|
||||
```
|
||||
Reference in New Issue
Block a user