Proveedores: claves, aliasing y modelos locales

Por: Artiko
bifrostproveedoresopenaianthropicollama

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).

IdentificadorProveedorCapacidades principales
openaiOpenAILa más completa: chat, imágenes, audio, batch, video, containers, passthrough
anthropicAnthropicChat, Responses, Files, Batch, conteo de tokens, passthrough
azureAzure OpenAIChat, texto, imágenes, TTS, STT, embeddings, video, batch
bedrockAWS BedrockChat, texto, imágenes, edición de imágenes, embeddings, rerank, conteo de tokens
geminiGoogle GeminiChat, imágenes, TTS, STT, Files, Batch, video, conteo de tokens
vertexGoogle Vertex AIChat, imágenes, embeddings, video, conteo de tokens, rerank, passthrough
cohereCohereChat, embeddings, rerank, conteo de tokens
mistralMistralChat, embeddings, STT, OCR
groqGroqChat con streaming; texto vía conversión de fallback
xaixAIChat, texto, imágenes
perplexityPerplexitySolo chat
fireworksFireworksChat, texto, embeddings
cerebrasCerebrasChat y text completions con streaming
openrouterOpenRouterChat, texto, embeddings
huggingfaceHugging FaceChat, imágenes, embeddings, TTS, STT
nebiusNebiusChat, texto, imágenes, embeddings
parasailParasailChat, Responses
replicateReplicateChat, texto, imágenes, video
runwayRunwaySolo generación de video
elevenlabsElevenLabsProveedor especializado en TTS y STT
ollamaOllamaChat, texto, embeddings (modelos locales)
vllmvLLMChat, texto, embeddings, rerank, STT (self-hosted)
sglSGLangChat, 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:

CampoPara qué sirveNotas
nameIdentificador único de la claveObligatorio, único dentro del proveedor
valueLa API keyUsa env.NOMBRE_VAR o el valor directo
modelsModelos a los que aplica esta clave["*"] aplica a todos
weightPeso para distribuir tráficoDecimal 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
      }
    }
  }
}

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"
    }
  }
}

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:

CampoPara qué sirveValores
idIdentificador únicostring UUID
nameEtiqueta legiblecualquier string
scope_kindNivel de alcance del precioglobal, provider, provider_key, virtual_key, virtual_key_provider, virtual_key_provider_key
patternMatcher del nombre de modelonombre exacto o prefijo con *
match_typeComportamiento del matchexact o wildcard
request_typesOperaciones cubiertasarreglo: chat_completion, embedding, image_generation, etc. (mínimo una)
pricing_patchCostos 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_kind que mencionan virtual_key se 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"
        }
      }
    }
  }
}

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:

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