Proveedores: claves, aliasing y modelos locales
Proveedores: claves, aliasing y modelos locales
En el capítulo 3 aprendiste a manejar la configuración de Bifrost desde tres frentes: la Web UI, el config.json y las variables de entorno. Ahora vamos a usar esa base para resolver el problema central de cualquier AI gateway: ¿cómo conecto a Bifrost con todos los modelos que necesito y cómo le doy a cada uno la identidad, las credenciales y el precio que me convienen?
Un proveedor (provider) en Bifrost es cada servicio que expone modelos de IA: OpenAI, Anthropic, AWS Bedrock, Google Vertex, tu propio Ollama corriendo en localhost, o un endpoint interno compatible con OpenAI. Bifrost normaliza a todos bajo una interfaz compatible con OpenAI, de modo que tu aplicación habla un solo idioma sin importar quién esté detrás. En este capítulo vas a pasar de “tengo una API key suelta” a “tengo un catálogo de proveedores con varias claves, alias propios, precios personalizados y modelos locales”, que es exactamente lo que distingue una instalación de juguete de una de producción.
Panorama de proveedores soportados
Bifrost soporta más de 20 proveedores de IA, todos con respuestas normalizadas al formato OpenAI. Cada uno se identifica en el config.json con un string corto (el provider identifier) que usarás tanto en la configuración como al construir el nombre del modelo en tus requests (proveedor/modelo).
| Identificador | Proveedor | Capacidades principales |
|---|---|---|
openai | OpenAI | La más completa: chat, imágenes, audio, batch, video, containers, passthrough |
anthropic | Anthropic | Chat, Responses, Files, Batch, conteo de tokens, passthrough |
azure | Azure OpenAI | Chat, texto, imágenes, TTS, STT, embeddings, video, batch |
bedrock | AWS Bedrock | Chat, texto, imágenes, edición de imágenes, embeddings, rerank, conteo de tokens |
gemini | Google Gemini | Chat, imágenes, TTS, STT, Files, Batch, video, conteo de tokens |
vertex | Google Vertex AI | Chat, imágenes, embeddings, video, conteo de tokens, rerank, passthrough |
cohere | Cohere | Chat, embeddings, rerank, conteo de tokens |
mistral | Mistral | Chat, embeddings, STT, OCR |
groq | Groq | Chat con streaming; texto vía conversión de fallback |
xai | xAI | Chat, texto, imágenes |
perplexity | Perplexity | Solo chat |
fireworks | Fireworks | Chat, texto, embeddings |
cerebras | Cerebras | Chat y text completions con streaming |
openrouter | OpenRouter | Chat, texto, embeddings |
huggingface | Hugging Face | Chat, imágenes, embeddings, TTS, STT |
nebius | Nebius | Chat, texto, imágenes, embeddings |
parasail | Parasail | Chat, Responses |
replicate | Replicate | Chat, texto, imágenes, video |
runway | Runway | Solo generación de video |
elevenlabs | ElevenLabs | Proveedor especializado en TTS y STT |
ollama | Ollama | Chat, texto, embeddings (modelos locales) |
vllm | vLLM | Chat, texto, embeddings, rerank, STT (self-hosted) |
sgl | SGLang | Chat, texto, embeddings (self-hosted) |
Nota: las capacidades dependen del proveedor y de la operación. El streaming, por ejemplo, no está disponible para todos los tipos de request. Cuando un proveedor no soporta nativamente una operación, Bifrost intenta una conversión de fallback si está implementada.
Visto en conjunto, todos los proveedores entran a Bifrost y salen como una única superficie OpenAI hacia tu aplicación:
flowchart LR
App["Tu aplicación<br/>(SDK OpenAI / curl)"] -->|"modelo: proveedor/modelo"| BF["Bifrost Gateway"]
BF --> P1["openai"]
BF --> P2["anthropic"]
BF --> P3["bedrock / vertex"]
BF --> P4["ollama (local)"]
BF --> P5["vllm / sgl (self-hosted)"]
BF --> P6["custom (compatible-OpenAI)"]
P1 -->|respuesta normalizada| BF
P2 -->|respuesta normalizada| BF
P3 -->|respuesta normalizada| BF
P4 -->|respuesta normalizada| BF
P5 -->|respuesta normalizada| BF
P6 -->|respuesta normalizada| BF
BF -->|formato OpenAI| App
Configurar un proveedor: claves, base_url y parámetros
La estructura mínima de un proveedor vive bajo el objeto providers del config.json. La clave del objeto es el identificador del proveedor (openai, anthropic, etc.) y dentro va, como mínimo, un arreglo keys con tus credenciales.
{
"providers": {
"openai": {
"keys": [
{
"name": "openai-key-1",
"value": "env.OPENAI_API_KEY",
"models": ["*"],
"weight": 1.0
}
],
"concurrency_and_buffer_size": {
"concurrency": 100,
"buffer_size": 500
}
},
"anthropic": {
"keys": [
{
"name": "anthropic-key-1",
"value": "env.ANTHROPIC_API_KEY",
"models": ["claude-3-opus", "claude-3-sonnet"],
"weight": 1.0
}
],
"concurrency_and_buffer_size": {
"concurrency": 25,
"buffer_size": 100
}
}
}
}
Repasemos los campos de cada entrada de keys, porque son los que vas a tocar todos los días:
| Campo | Para qué sirve | Notas |
|---|---|---|
name | Identificador único de la clave | Obligatorio, único dentro del proveedor |
value | La API key | Usa env.NOMBRE_VAR o el valor directo |
models | Modelos a los que aplica esta clave | ["*"] aplica a todos |
weight | Peso para distribuir tráfico | Decimal entre 0.0 y 1.0 |
El valor de value admite dos formas: una referencia a variable de entorno con el prefijo env. ("value": "env.OPENAI_API_KEY") o el valor literal ("value": "sk-proj-xxxxxxxxx"). Usa siempre env. para no commitear secretos: definirías la variable con export OPENAI_API_KEY="tu-api-key" antes de arrancar Bifrost. El detalle de variables de entorno lo viste en el capítulo 3.
El bloque network_config
Cuando necesitas apuntar a un endpoint distinto del default, agregar timeouts, headers o controlar el certificado TLS, todo eso va en network_config:
{
"providers": {
"openai": {
"keys": [
{ "name": "openai-key-1", "value": "env.OPENAI_API_KEY", "models": ["*"], "weight": 1.0 }
],
"network_config": {
"base_url": "http://localhost:8000/v1",
"default_request_timeout_in_seconds": 60,
"max_retries": 5,
"retry_backoff_initial": 1,
"retry_backoff_max": 10000,
"extra_headers": {
"x-custom-org": "my-organization"
},
"insecure_skip_verify": false,
"allow_private_network": false
}
}
}
}
base_url: sobreescribe el endpoint por defecto del proveedor. Imprescindible para gateways internos o proxies corporativos.default_request_timeout_in_seconds: timeout por request.max_retries,retry_backoff_initial,retry_backoff_max: control fino de reintentos a nivel de red (el tema completo de resiliencia se cubre en el capítulo 7).extra_headers: headers adicionales que Bifrost añade a cada llamada al proveedor.allow_private_network: ponlo entruecuando elbase_urlapunta a una IP/host privado (por ejemplo, un servicio interno o unlocalhostde otro contenedor).
El bloque concurrency_and_buffer_size controla el paralelismo (concurrency, default 1000) y el tamaño de la cola interna (buffer_size, default 5000) para ese proveedor. Bájalos para proveedores con límites de rate estrictos y súbelos para los que aguantan mucha carga.
Hacerlo por API en vez de por archivo
Todo lo anterior también se puede crear en caliente vía la API HTTP, sin editar el config.json a mano. El endpoint es POST /api/providers:
curl --location 'http://localhost:8080/api/providers' \
--header 'Content-Type: application/json' \
--data '{
"provider": "openai",
"keys": [
{
"name": "openai-key-1",
"value": "env.OPENAI_API_KEY",
"models": ["*"],
"weight": 1.0
}
],
"network_config": {
"base_url": "http://localhost:8000/v1",
"max_retries": 5,
"retry_backoff_initial": 1,
"retry_backoff_max": 10000
}
}'
Múltiples keys por proveedor y selección ponderada
Un solo proveedor puede tener varias claves, y cada una lleva un weight. Bifrost reparte el tráfico entre ellas según ese peso, lo que te permite balancear carga, repartir cuota entre cuentas o probar una clave nueva de forma gradual.
{
"providers": {
"openai": {
"keys": [
{
"name": "openai-key-1",
"value": "env.OPENAI_API_KEY_1",
"models": ["*"],
"weight": 0.7
},
{
"name": "openai-key-2",
"value": "env.OPENAI_API_KEY_2",
"models": ["*"],
"weight": 0.3
}
]
}
}
}
Con esa configuración, alrededor del 70% de los requests usan la primera clave y el 30% la segunda. El campo models además te deja enrutar modelos distintos a claves distintas, algo útil cuando tienes una cuenta estándar y otra premium:
{
"providers": {
"openai": {
"keys": [
{
"name": "openai-standard",
"value": "env.OPENAI_API_KEY",
"models": ["gpt-4o", "gpt-4o-mini"],
"weight": 1.0
},
{
"name": "openai-premium",
"value": "env.OPENAI_API_KEY_PREMIUM",
"models": ["o1-preview", "o1-mini"],
"weight": 1.0
}
]
}
}
}
Aquí los requests a gpt-4o y gpt-4o-mini salen por la clave estándar, mientras que o1-preview y o1-mini van por la premium. Esta selección ponderada es la base del load balancing, que profundizamos junto a retries y fallbacks en el capítulo 7.
Aliasing de modelos: nombres propios para tus modelos
A medida que crece tu organización, hardcodear gpt-4o-2024-11-20 por todo el código se vuelve frágil: el día que quieras migrar a otro modelo tienes que tocar cada servicio. El aliasing resuelve esto: defines un nombre estable (best-model, fast-model, embedder) y lo apuntas al modelo real. Tu código usa el alias; tú cambias el destino en un solo lugar.
Los alias se declaran con el campo aliases dentro de una entrada de keys:
{
"providers": {
"openai": {
"keys": [
{
"value": "env.OPENAI_API_KEY",
"models": ["*"],
"aliases": {
"best-model": "gpt-4o-2024-11-20",
"fast-model": "gpt-4o-mini",
"embedder": "text-embedding-3-large"
}
}
]
}
}
}
Cuando tu aplicación envía un request con model: "best-model", Bifrost resuelve el alias a gpt-4o-2024-11-20 antes de llamar al proveedor; el proveedor recibe el identificador real, nunca tu alias. La resolución es case-insensitive, así que "GPT-4O" mapea correctamente a "gpt-4o".
Forma de objeto enriquecido
Para casos donde el alias no es un simple nombre de modelo (por ejemplo, un deployment de Azure con un ID interno), puedes usar la forma de objeto en vez del string:
{
"aliases": {
"best-model": {
"model_id": "12345-azure-deployment",
"model_name": "claude-sonnet-4-5",
"model_family": "anthropic",
"description": "Claude Sonnet 4.5 on Azure",
"api_version": "2024-10-21"
}
}
}
model_id(obligatorio): el identificador real que se le envía al proveedor.model_name: nombre canónico que se usa para pricing y logs.model_family: fuerza el comportamiento de enrutamiento (anthropic,openai,mistral, etc.), clave cuando elmodel_idno permite inferir la familia.
También puedes registrar alias por API al crear una clave:
curl -X POST http://localhost:8080/api/providers/openai/keys \
-H "Content-Type: application/json" \
-d '{
"value": "env.OPENAI_API_KEY",
"models": ["*"],
"aliases": {
"best-model": "gpt-4o-2024-11-20",
"fast-model": "gpt-4o-mini"
}
}'
Custom pricing: define tus propios costos
Bifrost trae precios conocidos para muchos modelos, pero a veces necesitas sobreescribirlos: tienes un descuento negociado, usas un modelo self-hosted sin precio público, o quieres modelar el costo de un alias propio. Para eso existen los pricing overrides, que viven bajo governance.pricing_overrides y alimentan el tracking de gasto.
{
"governance": {
"pricing_overrides": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Global GPT-4o rate",
"scope_kind": "global",
"match_type": "exact",
"pattern": "gpt-4o",
"request_types": ["chat_completion"],
"pricing_patch": "{\"input_cost_per_token\":0.0000025,\"output_cost_per_token\":0.00001}"
}
]
}
}
Los campos clave:
| Campo | Para qué sirve | Valores |
|---|---|---|
id | Identificador único | string UUID |
name | Etiqueta legible | cualquier string |
scope_kind | Nivel de alcance del precio | global, provider, provider_key, virtual_key, virtual_key_provider, virtual_key_provider_key |
pattern | Matcher del nombre de modelo | nombre exacto o prefijo con * |
match_type | Comportamiento del match | exact o wildcard |
request_types | Operaciones cubiertas | arreglo: chat_completion, embedding, image_generation, etc. (mínimo una) |
pricing_patch | Costos a sobreescribir (string JSON) | costos por token y tiers |
Los valores de pricing_patch se expresan como costo por unidad en USD: input_cost_per_token y output_cost_per_token para tokens, input_cost_per_image para imágenes de entrada, output_cost_per_image_high_quality para imágenes generadas, etc. Para modelos facturados por contexto existen variantes como input_cost_per_token_above_128k_tokens.
Cuando varios overrides aplican al mismo modelo, gana el de scope más específico:
flowchart TD
A["virtual_key_provider_key"] --> B["virtual_key_provider"]
B --> C["virtual_key"]
C --> D["provider_key"]
D --> E["provider"]
E --> F["global"]
A:::most
F:::least
classDef most fill:#1f6feb,color:#fff
classDef least fill:#555,color:#fff
También puedes crear overrides por API:
curl -X POST http://localhost:8080/api/governance/pricing-overrides \
-H "Content-Type: application/json" \
-d '{
"name": "GPT-4o reduced input cost",
"scope_kind": "global",
"match_type": "exact",
"pattern": "gpt-4o",
"request_types": ["chat_completion"],
"patch": {
"input_cost_per_token": 0.0000025,
"output_cost_per_token": 0.000010
}
}'
Los conceptos de
scope_kindque mencionanvirtual_keyse conectan con el sistema de gobierno (virtual keys, presupuestos y límites) que veremos a fondo en el capítulo 9.
Custom providers: añadir un proveedor compatible con OpenAI
¿Y si el servicio que quieres usar no está en la lista de proveedores soportados, pero expone una API compatible con OpenAI? Bifrost lo soporta mediante un custom provider: le das un nombre propio, declaras custom_provider_config con base_provider_type apuntando a la familia base (típicamente "openai") y especificas qué operaciones permites.
{
"providers": {
"openai-custom": {
"keys": [
{
"name": "openai-custom-key-1",
"value": "env.OPENAI_API_KEY",
"models": ["*"],
"weight": 1.0
}
],
"network_config": {
"base_url": "https://your-openai-compatible-endpoint.com"
},
"custom_provider_config": {
"base_provider_type": "openai",
"allowed_requests": {
"chat_completion": true,
"chat_completion_stream": true,
"embedding": false,
"text_completion": false,
"responses": false,
"speech": false,
"transcription": false
},
"request_path_overrides": {
"chat_completion": "/api/v2/chat",
"chat_completion_stream": "/api/v2/chat"
}
}
}
}
}
base_provider_type: la familia base que Bifrost usa para traducir requests y respuestas ("openai"en el ejemplo).allowed_requests: lista blanca de operaciones; pon entruesolo lo que tu endpoint realmente soporta.request_path_overrides(opcional): si tu endpoint expone rutas distintas a las estándar de OpenAI, las mapeas aquí.
Una vez creado, lo invocas usando el nombre del proveedor como prefijo del modelo (openai-custom/gpt-4o-mini):
curl --location 'http://localhost:8080/v1/chat/completions' \
--header 'Content-Type: application/json' \
--data '{
"model": "openai-custom/gpt-4o-mini",
"messages": [
{"role": "user", "content": "Hello!"}
]
}'
Si tu endpoint usa un certificado autofirmado, ajusta el TLS en network_config con "insecure_skip_verify": true (rápido pero inseguro) o, mejor, proporciona la CA con "ca_cert_pem": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----". Esta misma técnica te permite tener varias instancias del mismo proveedor base con configuraciones distintas (por ejemplo, dos endpoints OpenAI-compatibles separados).
Modelos locales: Ollama, vLLM y SGLang
Una de las grandes ventajas de un AI gateway es poder mezclar modelos en la nube con modelos locales sin cambiar tu código. Bifrost trata a Ollama, vLLM y SGLang como proveedores de primera clase apuntando a un endpoint local.
Ollama
Ollama es la vía más sencilla para correr modelos en tu máquina. Su identificador es ollama y tiene una particularidad importante: debes configurar la URL explícitamente en ollama_key_config.url, porque no tiene valor por defecto y los requests fallarán sin ella. Como es un servidor local, el value de la clave queda vacío (no requiere API key).
{
"providers": {
"ollama": {
"keys": [
{
"name": "ollama-local",
"value": "",
"models": ["*"],
"weight": 1.0,
"ollama_key_config": {
"url": "http://localhost:11434"
}
}
]
}
}
}
Antes de configurarlo en Bifrost, verifica que tu Ollama esté arriba y respondiendo:
curl http://localhost:11434/api/tags
Ese endpoint lista los modelos que tienes descargados localmente. Si responde, ya puedes enrutar requests a través de Bifrost usando el prefijo ollama/:
curl --location 'http://localhost:8080/v1/chat/completions' \
--header 'Content-Type: application/json' \
--data '{
"model": "ollama/llama3",
"messages": [
{"role": "user", "content": "Hola, ¿quién eres?"}
]
}'
Para una instancia remota protegida, puedes poner un bearer token en el campo value; para la local, déjalo como string vacío.
vLLM y SGLang
Para inferencia self-hosted de mayor escala, vLLM (vllm) y SGLang (sgl) suelen exponer un servidor compatible con OpenAI. La forma de conectarlos es muy parecida a la de un custom provider: defines el base_url hacia tu endpoint, usas un valor de clave de relleno ("dummy") cuando el servidor no exige autenticación, y habilitas allow_private_network si apuntas a un host privado.
{
"providers": {
"vllm-local": {
"keys": [
{
"name": "vllm-key-1",
"value": "dummy",
"models": ["*"],
"weight": 1.0
}
],
"network_config": {
"base_url": "http://vllm-endpoint:8000",
"default_request_timeout_in_seconds": 60,
"allow_private_network": true
},
"custom_provider_config": {
"base_provider_type": "openai",
"allowed_requests": {
"chat_completion": true,
"chat_completion_stream": true
}
}
}
}
}
Con esto, una mezcla típica de equipo queda así: best-model (alias) cae en OpenAI para producción, fast-model en Groq para baja latencia, y un ollama/llama3 o tu vllm-local/... para experimentar o procesar datos sensibles sin que salgan de tu red. Tu aplicación sigue hablando un único dialecto OpenAI.
Resumen
En este capítulo construiste el catálogo de proveedores de Bifrost de punta a punta:
- Conociste los proveedores soportados y sus identificadores (
openai,anthropic,bedrock,vertex,ollama,vllm,sgl, y más), todos normalizados a formato OpenAI. - Configuraste un proveedor con
keys(name,value,models,weight),network_config(base_url, timeouts, headers, TLS) yconcurrency_and_buffer_size, tanto porconfig.jsoncomo por el endpointPOST /api/providers. - Usaste varias claves por proveedor con
weightpara selección ponderada ymodelspara enrutar modelos a claves distintas, la base del load balancing del capítulo 7. - Creaste alias con el campo
aliases(forma string y forma de objeto conmodel_id,model_name,model_family) para desacoplar tu código de los nombres reales. - Definiste custom pricing con
governance.pricing_overrides, sus niveles descope_kindy la jerarquía de resolución para alimentar el tracking de gasto. - Añadiste un custom provider compatible con OpenAI vía
custom_provider_configybase_provider_type, invocándolo con el prefijo del proveedor en el modelo. - Conectaste modelos locales: Ollama con
ollama_key_config.urly clave vacía, y vLLM/SGLang como endpoints self-hosted compatibles con OpenAI.
Con un catálogo de proveedores bien armado, el siguiente paso natural es dejar de usar curl y conectar tus aplicaciones existentes sin reescribirlas.
Siguiente: Drop-in replacement: reemplaza tu SDK cambiando la base URL