Drop-in replacement: reemplaza tu SDK cambiando la base URL

Por: Artiko
bifrostdrop-inopenai-sdklangchainlitellm

Drop-in replacement: reemplaza tu SDK cambiando la base URL

En el capítulo anterior configuraste proveedores, claves, aliasing y hasta modelos locales dentro de Bifrost. Ya tienes un gateway que sabe a qué proveedor enrutar cada petición. Pero queda una pregunta clave: ¿tienes que reescribir tu aplicación para empezar a aprovecharlo? La respuesta corta es no.

Este capítulo trata sobre la funcionalidad que probablemente te haga adoptar Bifrost en un proyecto real sin fricción: el drop-in replacement. La idea es tan simple como poderosa: tu código sigue usando el SDK de OpenAI, de Anthropic o de Google GenAI que ya conoces, y lo único que cambias es la base_url para que apunte a Bifrost. A partir de ese momento, sin tocar ni una línea más de lógica, ganas fallbacks, caching, governance y observabilidad.

Vamos de 0 a héroe: primero entenderemos el concepto, luego veremos el endpoint exacto de cada SDK con ejemplos en Python y JavaScript, después qué es realmente una “integration” por dentro, y cerraremos con las integraciones de frameworks (LangChain, LiteLLM, Pydantic AI) y la migración completa desde LiteLLM.

¿Qué significa “drop-in replacement”?

Un drop-in replacement es un componente que puedes “soltar” en el lugar de otro sin cambiar el código que lo rodea. En el contexto de Bifrost, significa que el gateway expone endpoints HTTP que hablan exactamente el mismo protocolo que las APIs oficiales de cada proveedor.

Bifrost actúa como un protocol adapter (adaptador de protocolo): recibe una petición con el formato de, por ejemplo, OpenAI, la traduce a su formato interno unificado, la enruta al proveedor que corresponda, y devuelve la respuesta de vuelta en el formato que tu SDK espera. Todo eso ocurre de forma invisible para tu aplicación.

La consecuencia práctica es directa: solo cambias base_url, y todo lo demás se queda exactamente igual. Las claves de los proveedores reales las gestiona Bifrost (las configuraste en el capítulo anterior), así que en tu cliente puedes pasar una clave ficticia como "dummy-key".

flowchart LR
    subgraph App["Tu aplicacion (sin cambios)"]
        SDK["SDK de OpenAI / Anthropic / GenAI"]
    end
    subgraph Bifrost["Bifrost Gateway"]
        EP["Endpoint compatible<br/>/openai, /anthropic, /genai"]
        Core["Formato interno unificado<br/>+ fallbacks, caching,<br/>governance, observabilidad"]
    end
    subgraph Prov["Proveedores reales"]
        OpenAI["OpenAI"]
        Anthropic["Anthropic"]
        Google["Google Vertex / Gemini"]
    end

    SDK -- "base_url = http://localhost:8080/openai" --> EP
    EP --> Core
    Core --> OpenAI
    Core --> Anthropic
    Core --> Google
    Core -- "respuesta en formato del SDK" --> SDK

El diagrama deja clara la magia: tu código cree que sigue hablando con OpenAI, pero en realidad habla con Bifrost, que añade toda una capa de valor por debajo.

El puerto y los endpoints

Por defecto, Bifrost escucha en el puerto 8080 (lo viste al hacer tu primer request en el capítulo 2). A partir de ahí, cada SDK tiene su propio prefijo de ruta:

SDK / clienteEndpoint base (local)
OpenAIhttp://localhost:8080/openai
Anthropichttp://localhost:8080/anthropic
Google GenAIhttp://localhost:8080/genai
LangChainhttp://localhost:8080/langchain
LiteLLMhttp://localhost:8080/litellm

Cada prefijo le dice a Bifrost en qué “dialecto” viene la petición, para traducirla correctamente. No hay que recompilar nada ni instalar paquetes nuevos: si ya usas openai en Python, sigues usando openai.

OpenAI SDK: el caso más común

El SDK de OpenAI es el punto de partida de la mayoría de proyectos. Cambiar a Bifrost es cuestión de dos campos: base_url y api_key.

Python

import openai

client = openai.OpenAI(
    base_url="http://localhost:8080/openai",
    api_key="dummy-key"
)

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Hello!"}]
)

print(response.choices[0].message.content)

Compara esto con tu código original:

client = openai.OpenAI(api_key="your-openai-key")

La única diferencia es que añades base_url apuntando a Bifrost y reemplazas tu clave real por "dummy-key". La llamada a client.chat.completions.create(...) es idéntica.

JavaScript / TypeScript

import OpenAI from "openai";

const openai = new OpenAI({
  baseURL: "http://localhost:8080/openai",
  apiKey: "dummy-key",
});

const response = await openai.chat.completions.create({
  model: "gpt-4o-mini",
  messages: [{ role: "user", content: "Hello!" }],
});

console.log(response.choices[0].message.content);

Observa que en JS el campo se llama baseURL (camelCase) en lugar de base_url. Es la convención del propio SDK de OpenAI, no de Bifrost.

Pasar cabeceras de governance

Aquí es donde empieza a brillar el valor añadido. Bifrost lee cabeceras HTTP con el prefijo x-bf- para activar funcionalidades como las virtual keys (que verás en el capítulo de governance). El SDK de OpenAI permite inyectar cabeceras por defecto:

client = openai.OpenAI(
    base_url="http://localhost:8080/openai",
    api_key="dummy-key",
    default_headers={"x-bf-vk": "vk_12345"}
)
const openai = new OpenAI({
  baseURL: "http://localhost:8080/openai",
  apiKey: "dummy-key",
  defaultHeaders: { "x-bf-vk": "vk_12345" },
});

La cabecera x-bf-vk identifica la virtual key con la que se contabiliza el uso. En Python el parámetro es default_headers; en JS es defaultHeaders.

Acceder a varios proveedores desde el mismo cliente

Una ventaja sutil pero potente: aunque estés usando el SDK de OpenAI, puedes invocar modelos de otros proveedores prefijando el nombre del modelo. Por ejemplo:

response = client.chat.completions.create(
    model="anthropic/claude-3-sonnet-20240229",
    messages=[{"role": "user", "content": "Hello!"}]
)

Otros prefijos válidos siguen el mismo patrón, por ejemplo vertex/gemini-pro, azure/gpt-4o u ollama/llama3.1:8b. Así, con un único cliente OpenAI, accedes a todo el catálogo configurado en Bifrost.

Anthropic SDK

Si tu código usa el SDK de Anthropic con client.messages.create(...), el cambio es igual de pequeño: apunta base_url al endpoint /anthropic.

Python

import anthropic

client = anthropic.Anthropic(
    base_url="http://localhost:8080/anthropic",
    api_key="dummy-key"
)

response = client.messages.create(
    model="claude-3-sonnet-20240229",
    max_tokens=1000,
    messages=[{"role": "user", "content": "Hello!"}]
)

print(response.content[0].text)

JavaScript / TypeScript

import Anthropic from "@anthropic-ai/sdk";

const anthropic = new Anthropic({
  baseURL: "http://localhost:8080/anthropic",
  apiKey: "dummy-key",
});

const response = await anthropic.messages.create({
  model: "claude-3-sonnet-20240229",
  max_tokens: 1000,
  messages: [{ role: "user", content: "Hello!" }],
});

console.log(response.content[0].text);

Las cabeceras de governance funcionan igual que con OpenAI: default_headers={"x-bf-vk": "vk_12345"} en Python y defaultHeaders en JS.

Google GenAI SDK

El SDK de Google GenAI no expone un parámetro base_url directo en el constructor; en su lugar, se configura a través de http_options. Bifrost lo expone en el endpoint /genai.

Python

from google import genai
from google.genai.types import HttpOptions

client = genai.Client(
    api_key="dummy-key",
    http_options=HttpOptions(base_url="http://localhost:8080/genai")
)

response = client.models.generate_content(
    model="gemini-1.5-flash",
    contents="Hello!"
)

print(response.text)

JavaScript

import { GoogleGenerativeAI } from "@google/generative-ai";

const genAI = new GoogleGenerativeAI("dummy-key", {
  baseUrl: "http://localhost:8080/genai",
});

const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash" });
const response = await model.generateContent("Hello!");

console.log(response.response.text());

Igual que con los demás, al prefijar el modelo (por ejemplo "openai/gpt-4o-mini" o "anthropic/claude-3-sonnet-20240229") puedes alcanzar otros proveedores desde el cliente de GenAI.

¿Qué es realmente una “integration” en Bifrost?

Hasta ahora hemos hablado de “cambiar la base URL”, pero conviene entender qué ocurre por debajo. En Bifrost, una integration es un protocol adapter que traduce entre la API unificada interna de Bifrost y el formato específico de cada proveedor (OpenAI, Anthropic, Google GenAI, etc.).

Es importante el matiz: una integration no es un simple passthrough. No reenvía tu petición tal cual al proveedor. En su lugar, hace tres cosas activamente:

  1. Convierte la petición entrante desde el formato del proveedor al formato interno de Bifrost.
  2. Transforma la respuesta de Bifrost de vuelta al formato que ese proveedor espera.
  3. Mapea los errores apropiadamente entre ambos sistemas.
sequenceDiagram
    participant App as Tu app (SDK OpenAI)
    participant Int as Integration /openai
    participant Core as Nucleo unificado Bifrost
    participant Prov as Proveedor real

    App->>Int: POST /openai/v1/chat/completions
    Int->>Core: Traduce a formato interno
    Core->>Prov: Enruta segun config y aliasing
    Prov-->>Core: Respuesta del proveedor
    Core-->>Int: Respuesta unificada
    Int-->>App: Traduce a formato OpenAI

Por eso cambias solo el endpoint de conexión: Bifrost se encarga de todas las conversiones estructurales de forma invisible. Las integraciones soportadas son: OpenAI SDK, Anthropic SDK, Google GenAI, LiteLLM, LangChain y AWS Bedrock.

Los endpoints compatibles por proveedor

Cada integration ofrece endpoints HTTP compatibles con la API del proveedor correspondiente:

Todos soportan además el listado de modelos a través de su respectivo endpoint /models, con filtrado opcional por proveedor mediante la cabecera x-bf-list-models-provider.

Como las rutas son idénticas a las oficiales, hasta puedes probarlas con curl sin SDK alguno:

curl http://localhost:8080/openai/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer dummy-key" \
  -d '{
    "model": "gpt-4o-mini",
    "messages": [{"role": "user", "content": "Hello!"}]
  }'

Integración con LangChain

LangChain es uno de los frameworks de orquestación más usados. Bifrost lo expone en el endpoint /langchain, reutilizando el cliente ChatOpenAI (que habla protocolo OpenAI por debajo).

Python

from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage

llm = ChatOpenAI(
    model="gpt-4o-mini",
    openai_api_base="http://localhost:8080/langchain",
    openai_api_key="dummy-key"
)

response = llm.invoke([HumanMessage(content="Hello!")])
print(response.content)

JavaScript

import { ChatOpenAI } from "@langchain/openai";

const llm = new ChatOpenAI({
  model: "gpt-4o-mini",
  configuration: {
    baseURL: "http://localhost:8080/langchain",
  },
  openAIApiKey: "dummy-key",
});

const response = await llm.invoke("Hello!");
console.log(response.content);

Para activar governance, pasa la virtual key como cabecera por defecto:

llm = ChatOpenAI(
    model="gpt-4o-mini",
    openai_api_base="http://localhost:8080/langchain",
    default_headers={"x-bf-vk": "your-virtual-key"}
)
const llm = new ChatOpenAI({
  model: "gpt-4o-mini",
  configuration: {
    baseURL: "http://localhost:8080/langchain",
    defaultHeaders: { "x-bf-vk": "your-virtual-key" },
  },
  openAIApiKey: "dummy-key",
});

Limitación importante: esta integración solo funciona con proveedores de IA que tanto LangChain como Bifrost soporten. Si uno de los dos no conoce al proveedor, la petición fallará.

Integración con el SDK de LiteLLM

LiteLLM tiene su propio SDK de Python (la función completion), independiente de su proxy. Bifrost lo expone en el endpoint /litellm.

from litellm import completion

response = completion(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Hello!"}],
    base_url="http://localhost:8080/litellm"
)

print(response.choices[0].message.content)

Los mismos patrones de LiteLLM se aplican a través de la misma base_url, simplemente cambiando el nombre del modelo:

Para governance, LiteLLM usa el parámetro extra_headers:

response = completion(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Hello!"}],
    base_url="http://localhost:8080/litellm",
    extra_headers={"x-bf-vk": "your-virtual-key"}
)

Limitación: al igual que con LangChain, esta integración solo funciona con proveedores que tanto LiteLLM como Bifrost soporten.

Integración con Pydantic AI

Pydantic AI se apoya en clientes compatibles con OpenAI, así que la integración sigue el mismo principio de drop-in: configuras el cliente subyacente para que apunte al endpoint de Bifrost y pasas "dummy-key" como clave, ya que la autenticación real la gestiona el gateway.

El patrón es el de siempre: el modelo de Pydantic AI envuelve un cliente OpenAI cuyo base_url apunta a http://localhost:8080/openai, de modo que toda tu lógica de agentes y validación tipada con Pydantic queda intacta mientras el tráfico fluye por Bifrost.

Consulta la página de integraciones de la documentación oficial para el snippet exacto de tu versión de Pydantic AI, ya que la forma de inyectar el cliente HTTP puede variar entre releases.

Migrar desde LiteLLM Proxy

Si vienes de un proxy de LiteLLM en producción, Bifrost ofrece una herramienta de migración oficial que transfiere tu configuración desde el proxy en ejecución hacia la API de gestión de Bifrost. Se distribuye como paquete de npx.

¿Por qué migrar?

El argumento principal es el rendimiento: Bifrost está escrito en Go y diseñado como un gateway de alto throughput y baja latencia, frente al overhead que añade un proxy en Python. Al migrar, conservas tu modelo mental (proveedores, equipos, virtual keys) pero ganas la capa de resiliencia, caching y observabilidad nativa de Bifrost sin reescribir tus aplicaciones, que siguen apuntando a una base_url.

Paso 1: variables de entorno

export LITELLM_URL="http://localhost:4000"
export LITELLM_MASTER_KEY="sk-1234"
export LITELLM_CONFIG="/path/to/litellm/config.yaml"
export BIFROST_URL="http://localhost:8080"
export BIFROST_API_KEY="your-bifrost-api-key"

# Opcional (para modelos almacenados en base de datos):
export LITELLM_DB_URL="postgresql://user:pass@localhost:5432/litellm"
export LITELLM_SALT_KEY="your-litellm-salt-key"

Paso 2: dry run

Antes de escribir nada, ejecuta una simulación para ver qué se migraría:

DRY_RUN=1 npx @maximhq/bifrost-migration-cli

Paso 3: migración real

npx @maximhq/bifrost-migration-cli

Qué se migra y a qué se traduce

La herramienta procesa cinco tipos de entidad en orden de dependencias:

Entidad en LiteLLMDestino en Bifrost
Model deploymentsProviders y provider keys
OrganizationsCustomers
TeamsTeams (vinculados a customers)
Internal usersUsers + membresías de equipo
Virtual keysVirtual keys

Limitaciones que debes conocer

Garantías de la herramienta

El beneficio real: valor sin tocar tu código

Aquí está la moraleja del capítulo. Al cambiar una sola línea (la base_url), tu aplicación existente empieza a recibir, sin reescritura alguna:

Todo esto se gobierna desde la configuración de Bifrost (que viste en el capítulo 3) y desde cabeceras x-bf-, no desde tu código de negocio. Esa separación es justamente lo que hace que un gateway como Bifrost sea sostenible a largo plazo: tu lógica de aplicación no se acopla a políticas operativas.

Resumen

En este capítulo aprendiste que Bifrost es un drop-in replacement: cambias la base_url de tu SDK y tu código sigue funcionando igual. Repasamos los endpoints por SDK (/openai, /anthropic, /genai, /langchain, /litellm) con ejemplos verbatim en Python y JavaScript para OpenAI, Anthropic y Google GenAI, usando "dummy-key" porque las claves reales las gestiona el gateway.

Entendiste que una integration es un adaptador que traduce (no un passthrough), con endpoints compatibles como /openai/v1/chat/completions, /anthropic/v1/messages y /genai/v1beta/models/{model}/generateContent. Vimos las integraciones con LangChain, el SDK de LiteLLM y Pydantic AI, junto con el patrón de cabeceras de governance (x-bf-vk). Finalmente, recorriste la migración desde el proxy de LiteLLM con npx @maximhq/bifrost-migration-cli, sus variables de entorno, el DRY_RUN, las entidades que se traducen y sus limitaciones, motivada por el mayor rendimiento de Bifrost.

La conclusión: ganas fallbacks, caching, governance y observabilidad sin cambiar tu código de aplicación.

Siguiente: Inferencia: streaming, tool calling, multimodal y reranking