Para las aplicaciones nuevas de LangChain, usa ChatOpenRouter. Mantén ChatOpenAI con https://openrouter.ai/api/v1 en los proyectos antiguos que no puedas actualizar y reserva https://openrouter.ai/api para la integración con Anthropic Agent SDK.
La respuesta rápida: en los proyectos nuevos, usa la integración nativa
OpenRouter aporta el endpoint del modelo y la capa de enrutamiento; LangChain se encarga de las abstracciones para modelos de chat, cadenas y agentes. La combinación nativa actual es langchain-openrouter con ChatOpenRouter en Python, o @langchain/openrouter con ChatOpenRouter en JavaScript y TypeScript.
| Tu situación | Ruta recomendada | Motivo principal |
|---|---|---|
| Aplicación nueva de LangChain en Python | langchain-openrouter + ChatOpenRouter | Expone el enrutamiento del proveedor, los metadatos, el razonamiento, las herramientas y los controles de salida estructurada |
| Aplicación nueva de LangChain en JS/TS | @langchain/openrouter + ChatOpenRouter | Mantiene las interfaces nativas de LangChain para mensajes, streaming y herramientas |
| Aplicación antigua de LangChain | ChatOpenAI + base_url="https://openrouter.ai/api/v1" | Es el cambio de compatibilidad más pequeño |
Aplicación existente que usa init_chat_model | LangChain v1 más langchain-openrouter | Activa la ruta de despacho mediante model_provider="openrouter" |
| Aplicación con Claude/Anthropic Agent SDK | ANTHROPIC_BASE_URL="https://openrouter.ai/api" | El Agent SDK utiliza la ruta compatible con Anthropic |
| Aplicación Next.js con Vercel AI SDK | @openrouter/ai-sdk-provider + createOpenRouter() | Utiliza las abstracciones de streamText() y herramientas del AI SDK |
El anuncio del paquete oficial de LangChain explica que la integración nativa conserva comportamientos específicos de OpenRouter que un wrapper genérico de ChatOpenAI puede perder, como el contenido de razonamiento, la salida estructurada, los metadatos de enrutamiento y la identidad de tracing. Si esos campos no son importantes para tu aplicación, no necesitas reescribir de inmediato una configuración de compatibilidad que ya funciona.
Empieza por el mapa de endpoints, no por el nombre del framework
Cada SDK añade rutas distintas a su URL base. La ruta compatible con OpenAI es https://openrouter.ai/api/v1; la guía del SDK de OpenAI para OpenRouter utiliza ese valor. En cambio, la guía de Anthropic Agent SDK configura ANTHROPIC_BASE_URL con https://openrouter.ai/api.
| Cliente o SDK | URL base que debes configurar | Familia habitual de solicitudes |
|---|---|---|
| SDK de OpenAI para Python/JS | https://openrouter.ai/api/v1 | Chat Completions de OpenAI |
ChatOpenAI heredado de LangChain | https://openrouter.ai/api/v1 | Llamadas de chat compatibles con OpenAI |
ChatOpenRouter de LangChain | Normalmente, ninguna URL base personalizada | Integración nativa con OpenRouter |
| Proveedor de OpenRouter para Vercel AI SDK | Normalmente, ninguna URL base personalizada | El paquete del proveedor gestiona el endpoint |
| Anthropic Agent SDK | https://openrouter.ai/api | Llamadas del Agent SDK compatibles con Anthropic |
No introduzcas /chat/completions en la URL base de un SDK salvo que ese SDK solicite explícitamente un endpoint completo. Si un error muestra una ruta duplicada, como /v1/v1, revisa cómo combina el SDK la URL base con la ruta del endpoint.
En un cliente heredado de LangChain compatible con OpenAI, la configuración mínima funcional es:
import os
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="openai/gpt-4o",
api_key=os.environ["OPENROUTER_API_KEY"],
base_url="https://openrouter.ai/api/v1",
)
response = llm.invoke("Explain provider routing in one sentence.")
print(response.content)
Utiliza una clave de API de OpenRouter, no una clave de OpenAI. La guía de inicio rápido de OpenRouter especifica autenticación mediante un token Bearer. HTTP-Referer y X-OpenRouter-Title son cabeceras opcionales para atribuir la aplicación; no sustituyen a Authorization.
LangChain + OpenRouter: configuración actual
Python: langchain-openrouter
Instala la integración y mantén la clave fuera del archivo de código:
pip install -U langchain-openrouter
export OPENROUTER_API_KEY="sk-or-..."
Después, crea una instancia del modelo de chat nativo. La integración de LangChain para Python documenta model, temperature, max_tokens y max_retries con esta estructura:
from langchain_openrouter import ChatOpenRouter
model = ChatOpenRouter(
model="anthropic/claude-sonnet-4.5",
temperature=0,
max_tokens=1024,
max_retries=2,
)
response = model.invoke([
("system", "You are a concise technical assistant."),
("human", "What does OpenRouter add to a LangChain application?"),
])
print(response.content)
Los nombres de modelo de OpenRouter suelen seguir el formato provider/model, por ejemplo anthropic/claude-sonnet-4.5, openai/gpt-4o o deepseek/deepseek-r1. Consulta la API de modelos de OpenRouter antes de desplegar, porque los identificadores, alias, variantes, disponibilidad y parámetros compatibles pueden cambiar.
JavaScript y TypeScript: @langchain/openrouter
Instala la integración y el núcleo de LangChain:
npm install @langchain/openrouter @langchain/core
El constructor basado en objetos de la integración de LangChain para JavaScript resulta explícito y facilita compararlo con el ejemplo de Python:
import { ChatOpenRouter } from "@langchain/openrouter";
const model = new ChatOpenRouter({
model: "anthropic/claude-sonnet-4.5",
temperature: 0,
maxTokens: 1024,
});
const response = await model.invoke([
{ role: "system", content: "You are a concise technical assistant." },
{ role: "user", content: "Explain the provider/model format." },
]);
console.log(response.content);
Python utiliza max_tokens; el ejemplo de JavaScript usa maxTokens. Ambas integraciones leen OPENROUTER_API_KEY de forma predeterminada. Si un modelo aparece en el catálogo pero falla en el código, comprueba el slug exacto, el sufijo de variante y los parámetros admitidos por la solicitud.
init_chat_model y la trampa de las versiones
Un error habitual es:
ValueError: Unsupported model_provider='openrouter'.
Un hilo de troubleshooting del foro de LangChain atribuye este caso a una instalación de LangChain anterior a v1 utilizada con documentación de v1. La solución recomienda actualizar el framework y el paquete del proveedor conjuntamente:
pip install -U "langchain>=1" langchain-openrouter
Después, utiliza el inicializador genérico:
import os
from langchain.chat_models import init_chat_model
os.environ["OPENROUTER_API_KEY"] = "sk-or-..."
model = init_chat_model(
"anthropic/claude-sonnet-4.5",
model_provider="openrouter",
)
print(model.invoke("Say hello in one sentence.").content)
Si el proyecto no puede migrar a LangChain v1, instala langchain-openrouter y crea directamente ChatOpenRouter. Así evitas el registro genérico de proveedores y dejas explícita la dependencia de OpenRouter.
Migrar de ChatOpenAI a ChatOpenRouter
El cambio de código es pequeño, pero el límite de integración pasa de un adaptador genérico de OpenAI a una clase específica del proveedor:
# Before: OpenAI-compatible workaround
from langchain_openai import ChatOpenAI
model = ChatOpenAI(
model="anthropic/claude-sonnet-4.5",
base_url="https://openrouter.ai/api/v1",
api_key=os.environ["OPENROUTER_API_KEY"],
)
# After: native OpenRouter integration
from langchain_openrouter import ChatOpenRouter
model = ChatOpenRouter(model="anthropic/claude-sonnet-4.5")
Conserva primero los mismos prompts y puntos de llamada de las cadenas. Añade el enrutamiento del proveedor, el razonamiento, la salida estructurada o el tratamiento de metadatos solo después de comprobar que la llamada nativa básica funciona.
Streaming, callbacks, herramientas y salida estructurada en LangChain
model.stream() devuelve fragmentos de mensaje para consumir texto de forma sencilla. stream_events(..., version="v3") expone eventos del ciclo de vida, más adecuados para registrar callbacks, actualizar el estado de una interfaz y hacer tracing; ambas opciones están documentadas en la integración de LangChain para Python.
model = ChatOpenRouter(model="openai/gpt-4o")
for event in model.stream_events(
"Explain why OpenRouter has provider fallbacks.",
version="v3",
):
if event["event"] == "on_chat_model_stream":
chunk = event["data"]["chunk"]
print(chunk.text, end="", flush=True)
Usa un callback cuando la aplicación necesite notificaciones de inicio, tokens, finalización o errores. Elige stream() cuando solo necesites los fragmentos del modelo. Lee el uso y los metadatos de respuesta de la respuesta completada o del resultado agregado del stream, como muestra la documentación enlazada de la integración, no de un fragmento inicial elegido arbitrariamente.
Llamadas a herramientas y enrutamiento del proveedor
OpenRouter documenta un flujo normalizado de llamadas a herramientas: el modelo propone una llamada, la aplicación la ejecuta y después envía el resultado de la herramienta al modelo. ChatOpenRouter.bind_tools() acepta herramientas de LangChain, funciones de Python, clases de Pydantic y esquemas en forma de diccionario.
from pydantic import BaseModel, Field
class GetWeather(BaseModel):
"""Get current weather for a city."""
location: str = Field(
description="City and state, for example San Francisco, CA"
)
model_with_tools = model.bind_tools(
[GetWeather],
strict=True,
)
result = model_with_tools.invoke("What is the weather in San Francisco?")
print(result.tool_calls)
La primera llamada solo devuelve las herramientas que el modelo propone utilizar. Para completar manualmente el ciclo, crea un ToolMessage por cada llamada antes de pedir al modelo la respuesta final:
from langchain_core.messages import HumanMessage, ToolMessage
from langchain_core.tools import tool
@tool
def get_weather(location: str) -> str:
"""Get the current weather in a location."""
return f"Sunny in {location}."
model_with_tools = model.bind_tools([get_weather])
messages = [HumanMessage("What is the weather in San Francisco?")]
ai_msg = model_with_tools.invoke(messages)
messages.append(ai_msg)
for call in ai_msg.tool_calls:
tool_output = get_weather.invoke(call["args"])
messages.append(
ToolMessage(
content=tool_output,
tool_call_id=call["id"],
)
)
final_msg = model_with_tools.invoke(messages)
print(final_msg.content)
El enrutamiento del proveedor se configura en el modelo mediante las opciones específicas de OpenRouter que describen la integración de LangChain y la guía de selección de proveedores de OpenRouter:
routed_model = ChatOpenRouter(
model="anthropic/claude-sonnet-4.5",
openrouter_provider={
"order": ["Anthropic", "Google"],
"allow_fallbacks": True,
"require_parameters": True,
"data_collection": "deny",
},
)
order expresa una preferencia. only limita los proveedores elegibles y ignore excluye proveedores. allow_fallbacks=True permite que una solicitud se envíe fuera del orden preferido. require_parameters=True evita utilizar proveedores que no admitan todos los parámetros incluidos en la carga útil. Estas opciones equilibran disponibilidad y control sobre el proveedor.
En los clientes antiguos de ChatOpenAI, los campos específicos de OpenRouter quizá deban enviarse mediante extra_body o model_kwargs, según la versión del cliente. Un usuario preguntó en un hilo de Reddit sobre LangChain y OpenRouter:
«Me pregunto, ¿OpenRouter no admite la especificación de la API de OpenAI de forma que el cliente de OpenAI funcione?» — u/tuxedo0.
El paquete específico deja claro el límite de integración con OpenRouter, en lugar de exigir que un wrapper genérico de OpenAI exponga todos los campos propios del proveedor.
Salida estructurada: comprueba el endpoint, no solo el nombre del modelo
La integración nativa ofrece salida tipada mediante with_structured_output(). La documentación de LangChain para Python muestra el método nativo con JSON Schema:
from pydantic import BaseModel, Field
class Movie(BaseModel):
title: str
year: int
director: str
rating: float = Field(description="Rating from 0 to 10")
structured_model = ChatOpenRouter(
model="openai/gpt-5.5",
).with_structured_output(
Movie,
method="json_schema",
strict=True,
)
movie = structured_model.invoke("Give details about the movie Inception.")
print(movie)
La integración también documenta function_calling; strict no es compatible con json_mode. El comportamiento de la salida estructurada de OpenRouter puede variar según el endpoint que sirva la petición, así que el nombre del modelo por sí solo no ofrece garantías. La guía de OpenRouter sobre salida estructurada explica por qué son importantes las comprobaciones de capacidades del proveedor, require_parameters y la validación en el cliente.
Vercel AI SDK + OpenRouter
Si tu aplicación de Next.js o TypeScript utiliza el AI SDK de Vercel, instala el proveedor de OpenRouter:
npm install @openrouter/ai-sdk-provider ai zod
Este ejemplo compacto cubre el streaming de texto y una herramienta local basada en Zod. La guía de OpenRouter para Vercel AI SDK utiliza el mismo patrón con createOpenRouter() y openrouter("provider/model").
import { createOpenRouter } from "@openrouter/ai-sdk-provider";
import { isStepCount, streamText, tool } from "ai";
import { z } from "zod";
const openrouter = createOpenRouter({
apiKey: process.env.OPENROUTER_API_KEY,
});
const result = streamText({
model: openrouter("openai/gpt-4o"),
tools: {
getWeather: tool({
description: "Get the current weather in a location",
inputSchema: z.object({
location: z.string().describe("City and state"),
}),
execute: async ({ location }) => ({
location,
temperature: 18,
unit: "celsius",
}),
}),
},
stopWhen: isStepCount(3),
prompt: "What is the weather in San Francisco?",
});
for await (const textPart of result.textStream) {
process.stdout.write(textPart);
}
textStream expone los deltas de texto. Usa fullStream cuando la aplicación necesite razonamiento, llamadas a herramientas, resultados de herramientas, límites entre pasos o eventos de finalización; la referencia de streamText() del AI SDK documenta esas superficies de resultado. El ejecutor del tiempo en el ejemplo es simulado, no una llamada a una API real.
Un issue público del proveedor de OpenRouter informó de un posible buffering en una ruta de llamadas a herramientas con varios chunks usando @openrouter/ai-sdk-provider 2.2.3, AI SDK 6.0.81, Node.js 24 y openai/gpt-5.2. El informe describía la ausencia del evento tool-input-end. Trátalo como un problema específico de esa versión, no como un comportamiento universal. Si puedes reproducirlo, el issue propone esta solución alternativa:
import { createOpenAI } from "@ai-sdk/openai";
const openrouter = createOpenAI({
apiKey: process.env.OPENROUTER_API_KEY,
baseURL: "https://openrouter.ai/api/v1",
});
const model = openrouter.chat("openai/gpt-5.2");
Instala @ai-sdk/openai por separado y fija las versiones que hayas probado. Esta alternativa cambia el adaptador del proveedor, así que comprueba qué opciones específicas de OpenRouter siguen disponibles.
Anthropic Agent SDK + OpenRouter
Anthropic Agent SDK utiliza un límite de integración independiente. La guía oficial de Agent SDK de OpenRouter indica que el SDK utiliza Claude Code como runtime y acepta esta configuración de entorno:
export ANTHROPIC_BASE_URL="https://openrouter.ai/api"
export ANTHROPIC_AUTH_TOKEN="$OPENROUTER_API_KEY"
export ANTHROPIC_API_KEY=""
Dejar ANTHROPIC_API_KEY vacío es intencionado. La clave de OpenRouter va en ANTHROPIC_AUTH_TOKEN, y en esta configuración del Agent SDK la ruta /api no debe sustituirse por la ruta compatible con OpenAI, /api/v1.
La estructura TypeScript de la guía es:
npm install @anthropic-ai/claude-agent-sdk
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt: "Find and explain the bug in auth.py.",
options: {
allowedTools: ["Read"],
},
})) {
if (message.type === "assistant") {
console.log(message.message.content);
}
}
OpenRouter proporciona el endpoint del modelo; Agent SDK conserva el bucle del agente y el runtime de herramientas. Prueba las herramientas, el streaming, el comportamiento del contexto y las opciones específicas del modelo en la ruta exacta que vayas a desplegar, porque la paridad automática con las funciones nativas de Anthropic no está garantizada.
Errores 401, 404 y «funciona con curl, pero no con el SDK»
Identifica primero qué capa está fallando antes de cambiar de modelo. La autenticación, la construcción de la URL, la búsqueda del modelo, los filtros de proveedores y la validación de parámetros requieren soluciones diferentes.
| Síntoma | Capa probable | Primera comprobación |
|---|---|---|
401 Missing Authentication header | Cabecera o carga de variables de entorno | Confirma Authorization: Bearer ... y prueba /api/v1/key |
401 Unauthorized con una clave presente | Clave incorrecta, revocada o vacía | Confirma que el proceso ve OPENROUTER_API_KEY y que utilizas una clave de OpenRouter |
404 con una ruta mal formada | Composición de la URL base | Los clientes compatibles con OpenAI usan /api/v1; Agent SDK usa /api; busca un /v1 duplicado |
404 Invalid model o model_not_found | Identificador del modelo | Consulta /api/v1/models y utiliza el id devuelto o un alias documentado |
404 No allowed providers are available | Filtros de proveedores o disponibilidad del endpoint | Revisa only, ignore, max_price, require_parameters y la configuración de política de datos |
400 para top_k, min_p, herramientas o JSON Schema | Incompatibilidad de capacidades | Comprueba supported_parameters y exige proveedores compatibles al enrutar |
curl funciona, pero falla el código del framework | Ruta del SDK, mayúsculas y minúsculas, serialización o cabeceras descartadas | Compara la URL final, la cabecera de autenticación, el ID del modelo y el cuerpo JSON |
OpenRouter documenta GET /api/v1/key como una comprobación autenticada de la clave de API. Ejecútala antes de depurar LangChain:
curl -sS https://openrouter.ai/api/v1/key \
-H "Authorization: Bearer $OPENROUTER_API_KEY"
Después, verifica la disponibilidad de modelos:
curl -sS https://openrouter.ai/api/v1/models \
-H "Authorization: Bearer $OPENROUTER_API_KEY"
La API de modelos permite filtrar los modelos compatibles con herramientas antes de añadirlas:
curl -sS \
"https://openrouter.ai/api/v1/models?supported_parameters=tools" \
-H "Authorization: Bearer $OPENROUTER_API_KEY"
Un 404 también puede indicar que el modelo existe, pero que ningún proveedor elegible queda disponible después de aplicar los filtros de la solicitud. Para inspeccionar las decisiones de enrutamiento, activa la cabecera opcional X-OpenRouter-Metadata: enabled; la documentación de Router Metadata describe el objeto openrouter_metadata resultante y su comportamiento ante errores.
Durante el desarrollo, compara la solicitud generada después de ocultar la clave: URL base, ruta final, presencia de Authorization, ID exacto del modelo y campos específicos del proveedor en el cuerpo. Para un seguimiento centrado en la autenticación, consulta la guía de AIReiter para resolver errores de clave de API no válida y errores 401/403.
Checklist de migración para producción
- Alinea las generaciones de los paquetes. Mantén compatibles
langchain,langchain-coreylangchain-openroutero@langchain/openrouter, en lugar de copiar versiones de un tutorial antiguo. - Resuelve los IDs canónicos de los modelos. Utiliza
/api/v1/modelso el catálogo de modelos de OpenRouter; considera los alias un cambio de comportamiento deliberado. - Define el límite del fallback. El fallback entre proveedores mejora la disponibilidad. El fallback entre modelos puede cambiar la calidad, la latencia, las capacidades y el coste. Registra el
modelfinal que devuelve OpenRouter. - Exige las capacidades importantes. Usa filtros de capacidades del proveedor para las solicitudes con herramientas y salida estructurada; no consideres que admitir herramientas demuestra compatibilidad con JSON Schema estricto.
- Valida las salidas de la aplicación. Pydantic, Zod u otro validador debe comprobar los argumentos de las herramientas y las respuestas estructuradas antes de que el código posterior actúe sobre ellas.
- Registra metadatos útiles. Conserva el ID de la solicitud, el modelo, el uso, el motivo de finalización y la información del proveedor que sirvió la petición cuando tu política de privacidad lo permita. La integración de LangChain también documenta
session_idpara agrupar solicitudes ytracepara los metadatos de cada petición. - Prueba los dos modos de streaming. Que una llamada sin streaming funcione no demuestra que los fragmentos de llamadas a herramientas o el streaming de la interfaz funcionen correctamente.
- Prueba las restricciones de proveedores. Si utilizas
only,ignore, filtros de recopilación de datos o un límite de precio, comprueba también el flujo de error cuando no haya proveedores elegibles. - Mantén las credenciales en el servidor. Guarda
OPENROUTER_API_KEYen el entorno del servidor o en un gestor de secretos, nunca en un bundle del navegador ni en un archivo.envincluido en el repositorio.
El orden más fiable es este: verifica la clave, verifica el ID del modelo, realiza una llamada sencilla de texto y después añade el enrutamiento, las herramientas, la salida estructurada y el streaming de varios pasos.