🧩 Custom OpenAI / Custom Anthropic
Context Code soporta dos proveedores genéricos que apuntan a endpoints OpenAI-compatible o Anthropic-compatible arbitrarios: tu propio gateway, un servidor on-premise, un proxy LLM, LiteLLM, vLLM, LM Studio, DeepInfra, Together, Fireworks, etc.
Se identifican con los IDs:
| ID | Cliente que usa | Caso típico |
|---|---|---|
custom-openai | Cliente HTTP responses OpenAI-compatible | LiteLLM, vLLM, LM Studio, DeepInfra, Together, Fireworks, gateways propios |
custom-anthropic | (Adapter Anthropic — ver "Limitaciones") | Instalaciones on-premise Anthropic-compatible |
A diferencia de openai, claude, openrouter, etc., estos dos no traen endpoint ni catálogo de modelos predefinido: tú los configuras durante /login y el CLI los persiste en tu perfil.
🪄 Wizard de configuración (/login)
Cuando seleccionas Custom OpenAI o Custom Anthropic en /login, se abre un asistente de 3 pasos:
Paso 1/3 · Endpoint
URL del servidor API (ej: http://localhost:8001/v1)
> _
Paso 2/3 · API key
Ingresa tu API key.
> ********
Paso 3/3 · Modelo
Selecciona un modelo de la lista (el CLI hace GET {endpoint}/models).
Si la lista no se puede cargar, podras escribirlo a mano.Lo que persiste el wizard al terminar:
- API key → almacenamiento seguro del sistema (
providerApiKeys[<provider>], y bajo el perfil activo si aplica). - Endpoint → SQLite del provider (
provider-state.sqlite3, tablaprovider_base_url). - Modelo elegido → como
lastModeldel perfil activo, víasetProviderProfileLastModel+setStoredLastModelForProvider. - Lista completa de modelos descubiertos → caché en
~/.context/config.jsonbajocustomProviderModelsCache[<provider>]. La consume/modelpara mostrarte tu catálogo real en vez del de OpenAI.
El fetch al endpoint /models tiene un timeout de 15 s. Si el servidor no responde, el wizard cae al modo "ingresar modelo a mano" sin colgarse.
🔌 /provider con custom
/provider list # incluye CUSTOM OPENAI y CUSTOM ANTHROPIC
/provider current # muestra el activo (ej: Custom OpenAI / main)
/provider custom-openai # cambia el provider activo
/provider custom-openai perfil <nombre> # cambia al perfil <nombre> dentro de custom-openai
/provider custom-openai <baseURL> # cambia solo el endpoint del activo
/provider custom-openai clear # restaura el endpoint por defecto (api.openai.com)El autocompletar (Tab tras /provider ) lista los dos custom junto al resto.
Estado mostrado en /provider list:
● Conectado→ hay API key guardada para ese perfil.○ Desconectado→ falta API key (corre/login).ACTUAL→ es el perfil activo.- La línea
+- → <url>muestra el endpoint configurado.
🤖 /model con custom
Cuando el provider activo es custom-openai (o cualquier OpenAI-compatible: openrouter, ollama, gemini-api, zai, nvidia, deepseek), al abrir /model el CLI hace fetch dinámico en background a <baseUrl>/models con tu API key y mergea los resultados con el catálogo base.
Cómo se construye la lista
- Default del usuario (modelo guardado como activo del perfil).
- Catálogo offline (lo que cacheó el wizard de
/loginpaso 3, si lo corriste). - Fetch dinámico del endpoint en background al abrir
/model. Esos modelos se cachean en memoria durante la sesión.
Los tres se mergean sin duplicados. Cada vez que abres /model se vuelve a disparar el fetch para mantener la lista al día, con dedupe automático.
Si el endpoint no responde
/model no se bloquea. Muestra inmediatamente el catálogo offline (paso 2) y, si el fetch eventualmente termina, agrega los modelos nuevos. Si el fetch falla, te quedas con lo offline.
TIP
Si tu endpoint NO devuelve la ruta /models (algunos gateways minimalistas no la implementan), /model solo mostrará el catálogo offline. Si tampoco corriste el wizard de /login, verás solo el default. En cualquier caso puedes escribir el nombre del modelo directamente: /model <nombre>.
custom-anthropic no hace fetch
Anthropic-compatible no expone /models estándar; custom-anthropic solo muestra el catálogo offline del último /login.
📂 Perfiles para múltiples custom
El sistema de perfiles está habilitado para custom-openai y custom-anthropic, igual que para openai o claude. La intención es que cada perfil represente un endpoint distinto:
custom-openai / main → http://localhost:8001/v1 (vLLM local)
custom-openai / deepinfra → https://api.deepinfra.com/v1 (DeepInfra)
custom-openai / together → https://api.together.xyz/v1 (Together)Comandos para gestionarlos:
/login --profile deepinfra # wizard crea/activa el perfil 'deepinfra' y guarda todo ahi
/perfil create custom-openai together # alternativa: crear primero
/perfil use custom-openai together # activar
/login # wizard cae en el perfil activo
/provider custom-openai perfil main # alternar entre perfilesQué hace /login --profile <nombre> para custom-*:
- Antes de
saveProviderApiKeyysetProviderBaseUrl, el wizard llamaensureProviderProfile(provider, <nombre>)ysetActiveProviderProfile(profile.id). Si el perfil no existe se crea; si existe se reutiliza. - La API key, el endpoint, el modelo elegido y la lista de modelos descubiertos se persisten bajo ese perfil.
- Al terminar, el perfil queda activo.
Caché de modelos por perfil: customProviderModelsCache se indexa por profileId. Cada perfil tiene su propio catálogo, así /model muestra siempre la lista del perfil activo, no la del último /login. Si por alguna razón no hay caché para el perfil activo (perfil legacy creado antes de este cambio), cae al cache "por-provider" como fallback.
🧠 ¿Cómo se enruta una request?
Cuando el provider activo es custom-openai:
getAPIProvider()retorna'custom-openai'.isOpenAICompatibleProvider('custom-openai')→true.getOpenAIBaseUrl('custom-openai')leegetConfiguredProviderBaseUrl('custom-openai')(tu endpoint del wizard), no cae al defaultapi.openai.com.getOpenAICompatibleAccessToken('custom-openai')lee la API key víagetStoredProviderApiKey('custom-openai'), noOPENAI_API_KEY.- La request va a
<tu-endpoint>/responses(o/chat/completionssegún el modo) con tu Bearer token.
Esto significa que tu endpoint debe implementar al menos las rutas OpenAI-compatible que el CLI invoca (/responses o equivalente, /models para el wizard).
⚠️ Limitaciones honestas
custom-anthropicno tiene adapter completo todavía. Está registrado como provider de primera clase (aparece en/provider list,/login,/model, etc.) y su perfil persiste igual que cualquier otro. Pero el adapter Anthropic (services/api/anthropic.tsy similares) no enrutacustom-anthropica tu endpoint custom: las requests reales seguirán yendo al endpoint Anthropic configurado globalmente. Para producción úsalo solo si tu setup ya redirigeapi.anthropic.coma tu host (proxy reverso, DNS override,ANTHROPIC_BASE_URLenv, etc.).Sin auto-refresh de modelos. La lista solo se actualiza al re-correr
/logincon el mismo perfil. Si tu endpoint añade modelos nuevos, hasta el siguiente login/modelseguirá mostrando la lista anterior. (Una mejora futura:/provider custom-openai refresh-modelsque haga refetch sin re-loguear).No persistimos el "label" del endpoint. En
/provider listsiempre verásCUSTOM OPENAI; no hay un nombre amigable como "DeepInfra" o "vLLM local" para diferenciar perfiles. El nombre del perfil (main,deepinfra, etc.) cumple esa función.
🧪 Receta rápida — un único endpoint local
context # abre la sesión interactiva
/login # selecciona "Custom OpenAI"
# Paso 1: http://localhost:8001/v1
# Paso 2: tu API key (o cualquier string si el server no la valida)
# Paso 3: elige el modelo de la lista que descubre el wizard
/provider list # confirma "Custom OpenAI / main · Conectado · ACTUAL"
/model # cambia al modelo que quieras del catálogo🧪 Receta rápida — varios endpoints
# Primero: el local (default, queda en perfil 'main')
context
/login # "Custom OpenAI", endpoint local, key, modelo
# Segundo: DeepInfra (perfil 'deepinfra')
/login --profile deepinfra # mismo wizard, endpoint DeepInfra, key, modelo
# el wizard crea el perfil 'deepinfra' y lo activa
# Tercero: Together (perfil 'together')
/login --profile together # idem
# Alternar (cada uno con su key, endpoint, modelo activo y catalogo)
/provider custom-openai perfil main
/provider custom-openai perfil deepinfra
/provider custom-openai perfil together/model mostrará en cada caso el catálogo del endpoint correspondiente al perfil activo, gracias a que la caché se indexa por profileId.
