Per le nuove applicazioni LangChain, scegli ChatOpenRouter. Nei progetti più vecchi che non puoi aggiornare, mantieni ChatOpenAI con https://openrouter.ai/api/v1; usa invece https://openrouter.ai/api solo per il percorso Anthropic Agent SDK.
La risposta in un minuto: per i nuovi progetti LangChain usa l’integrazione nativa
OpenRouter fornisce l’endpoint dei modelli e il livello di instradamento; LangChain aggiunge le astrazioni per chat model, chain e agenti. Oggi l’abbinamento nativo è langchain-openrouter con ChatOpenRouter in Python, oppure @langchain/openrouter con ChatOpenRouter in JavaScript e TypeScript.
| La tua situazione | Percorso consigliato | Motivo principale |
|---|---|---|
| Nuova app LangChain in Python | langchain-openrouter + ChatOpenRouter | L’integrazione espone routing del provider, metadati, reasoning, tool e controlli sull’output strutturato |
| Nuova app LangChain in JS/TS | @langchain/openrouter + ChatOpenRouter | Mantiene le interfacce native di LangChain per messaggi, streaming e tool |
| App LangChain esistente e datata | ChatOpenAI + base_url="https://openrouter.ai/api/v1" | È la modifica di compatibilità più contenuta |
App esistente che usa init_chat_model | LangChain v1 più langchain-openrouter | Attiva il percorso di dispatch model_provider="openrouter" |
| App Claude/Anthropic Agent SDK | ANTHROPIC_BASE_URL="https://openrouter.ai/api" | Agent SDK usa il percorso compatibile con Anthropic |
| App Next.js che usa Vercel AI SDK | @openrouter/ai-sdk-provider + createOpenRouter() | Utilizza streamText() e le astrazioni per i tool dell’AI SDK |
L’annuncio del pacchetto ufficiale di LangChain spiega che l’integrazione nativa conserva comportamenti specifici di OpenRouter che un wrapper generico ChatOpenAI potrebbe perdere, tra cui contenuto di reasoning, output strutturato, metadati di routing e identità per il tracing. Se questi dati non ti servono, non c’è motivo di riscrivere subito una configurazione di compatibilità già funzionante.
Prima chiarisci la mappa degli endpoint, poi scegli il framework
Gli SDK aggiungono percorsi diversi alla propria base URL. Il percorso compatibile con OpenAI è https://openrouter.ai/api/v1, come mostra la guida di OpenRouter all’OpenAI SDK. La guida all’Anthropic Agent SDK imposta invece ANTHROPIC_BASE_URL su https://openrouter.ai/api.
| Client o SDK | Base URL da configurare | Famiglia di richieste tipica |
|---|---|---|
| OpenAI Python/JS SDK | https://openrouter.ai/api/v1 | OpenAI Chat Completions |
LangChain legacy ChatOpenAI | https://openrouter.ai/api/v1 | Chiamate chat compatibili con OpenAI |
LangChain ChatOpenRouter | Di norma nessuna base URL personalizzata | Integrazione nativa OpenRouter |
| Provider OpenRouter per Vercel AI SDK | Di norma nessuna base URL personalizzata | Il pacchetto del provider gestisce l’endpoint |
| Anthropic Agent SDK | https://openrouter.ai/api | Chiamate Agent SDK compatibili con Anthropic |
Non inserire /chat/completions nella base URL di uno SDK, a meno che non sia lo SDK stesso a richiedere esplicitamente un endpoint completo. Se compare un percorso duplicato, per esempio /v1/v1, controlla il modo in cui lo SDK concatena la base URL al percorso dell’endpoint.
Per un client LangChain legacy compatibile con OpenAI, la configurazione minima funzionante è questa:
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)
Usa una chiave API OpenRouter, non una chiave OpenAI. Il Quickstart di OpenRouter specifica l’autenticazione tramite Bearer token. HTTP-Referer e X-OpenRouter-Title sono intestazioni opzionali per attribuire le richieste all’applicazione, non sostituti di Authorization.
LangChain + OpenRouter: configurazione attuale
Python: langchain-openrouter
Installa l’integrazione e lascia la chiave fuori dal codice sorgente:
pip install -U langchain-openrouter
export OPENROUTER_API_KEY="sk-or-..."
Poi crea il chat model nativo. La documentazione dell’integrazione LangChain per Python mostra questa configurazione per model, temperature, max_tokens e max_retries:
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)
I nomi dei modelli OpenRouter seguono generalmente il formato provider/model, per esempio anthropic/claude-sonnet-4.5, openai/gpt-4o o deepseek/deepseek-r1. Prima del deployment consulta la Models API di OpenRouter: ID, alias, varianti, disponibilità e parametri supportati possono cambiare.
JavaScript e TypeScript: @langchain/openrouter
Installa l’integrazione e il core di LangChain:
npm install @langchain/openrouter @langchain/core
Il costruttore a oggetto della documentazione dell’integrazione LangChain per JavaScript è esplicito e si confronta facilmente con l’esempio 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);
In Python si usa max_tokens; nell’esempio JavaScript trovi invece maxTokens. Entrambe le integrazioni leggono automaticamente OPENROUTER_API_KEY. Se un modello compare nel catalogo ma fallisce nel codice, verifica lo slug esatto, il suffisso della variante e i parametri supportati dalla richiesta.
init_chat_model e il problema delle versioni
Un errore frequente è:
ValueError: Unsupported model_provider='openrouter'.
Una discussione di troubleshooting sul LangChain Forum riconduce il problema a un’installazione di LangChain precedente alla v1 usata insieme alla documentazione per la v1. La soluzione suggerita è aggiornare contemporaneamente framework e pacchetto partner:
pip install -U "langchain>=1" langchain-openrouter
Poi usa l’inizializzatore generico:
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)
Se il progetto non può passare a LangChain v1, installa langchain-openrouter e crea direttamente ChatOpenRouter. In questo modo aggiri il registro dei provider generico e rendi esplicita la dipendenza da OpenRouter.
Da ChatOpenAI a ChatOpenRouter
La modifica al codice è contenuta, ma il confine d’integrazione passa da un adapter OpenAI generico a una classe specifica per il provider:
# 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")
All’inizio mantieni invariati prompt e punti in cui richiami chain e modello. Aggiungi routing del provider, reasoning, output strutturato o gestione dei metadati solo dopo aver verificato che la chiamata nativa di base funzioni.
Streaming, callback, tool e output strutturato in LangChain
model.stream() restituisce chunk di messaggi, sufficienti per una semplice gestione del testo. stream_events(..., version="v3") espone invece eventi del ciclo di vita, più adatti a log tramite callback, stato dell’interfaccia e tracing; entrambe le modalità sono descritte nella documentazione dell’integrazione LangChain per 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 una callback quando l’app deve ricevere notifiche di avvio, token, completamento o errore. Preferisci stream() quando ti servono soltanto i chunk del modello. Leggi usage e metadati della risposta dal risultato completato o dallo stream aggregato, come indicato nella documentazione collegata, non da un chunk iniziale scelto arbitrariamente.
Tool calling e routing del provider
OpenRouter descrive un flusso normalizzato per il tool calling: il modello propone una chiamata a un tool, l’applicazione la esegue e poi rimanda il risultato al modello. ChatOpenRouter.bind_tools() accetta tool LangChain, funzioni Python, classi Pydantic e schemi sotto forma di dizionario.
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 prima chiamata restituisce soltanto i tool che il modello propone di invocare. Per completare manualmente il round trip, crea un ToolMessage per ogni chiamata e invialo al modello prima di richiedere la risposta finale:
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)
Il routing del provider si configura nei parametri specifici di OpenRouter descritti dall’integrazione LangChain e dalla guida di OpenRouter al provider routing:
routed_model = ChatOpenRouter(
model="anthropic/claude-sonnet-4.5",
openrouter_provider={
"order": ["Anthropic", "Google"],
"allow_fallbacks": True,
"require_parameters": True,
"data_collection": "deny",
},
)
order esprime una preferenza. only limita i provider utilizzabili, mentre ignore li esclude. Con allow_fallbacks=True la richiesta può essere inoltrata anche oltre l’ordine preferito. require_parameters=True impedisce di inviare la richiesta a provider che non supportano tutti i parametri presenti nel payload. Sono impostazioni che bilanciano disponibilità e controllo sul provider.
Con i client ChatOpenAI più vecchi, i campi specifici di OpenRouter potrebbero dover passare da extra_body o model_kwargs, a seconda della versione del client. In una discussione Reddit su LangChain/OpenRouter, un utente chiedeva:
“Mi chiedo: OpenRouter non supporta le specifiche dell’API OpenAI, così da permettere il funzionamento del client OpenAI?” — u/tuxedo0.
Il pacchetto dedicato rende esplicito il confine dell’integrazione OpenRouter, senza obbligare un wrapper OpenAI generico a esporre ogni campo specifico del provider.
Output strutturato: verifica l’endpoint, non solo il nome del modello
L’integrazione nativa offre output tipizzati tramite with_structured_output(). La documentazione LangChain per Python mostra il metodo nativo basato su 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)
L’integrazione documenta anche function_calling; strict non è supportato con json_mode. Il comportamento dell’output strutturato di OpenRouter può variare in base all’endpoint che serve la richiesta, quindi il solo nome del modello non offre garanzie. La guida di OpenRouter all’output strutturato spiega perché siano importanti i controlli sulle capacità del provider, require_parameters e la validazione lato client.
Vercel AI SDK + OpenRouter
Per un’applicazione Next.js o TypeScript che usa l’AI SDK di Vercel, installa il provider OpenRouter:
npm install @openrouter/ai-sdk-provider ai zod
Questo esempio compatto combina lo streaming del testo con un tool Zod locale. La guida di OpenRouter al Vercel AI SDK usa lo stesso schema basato su createOpenRouter() e 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 espone i delta del testo. Usa fullStream quando l’applicazione deve gestire reasoning, chiamate ai tool, risultati dei tool, confini tra gli step o eventi di completamento; la documentazione di riferimento di streamText() per l’AI SDK descrive questi risultati. L’esecutore del meteo nell’esempio è simulato e non chiama un’API reale.
Una issue pubblica del provider OpenRouter ha segnalato un buffering percepito in un percorso di tool call suddiviso in più chunk con @openrouter/ai-sdk-provider 2.2.3, AI SDK 6.0.81, Node.js 24 e openai/gpt-5.2. La segnalazione descriveva l’assenza dell’evento tool-input-end. Consideralo un problema legato a una specifica combinazione di versioni, non una caratteristica generale. Se il problema si riproduce, la issue riporta questo workaround:
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");
Installa separatamente @ai-sdk/openai e blocca le versioni che hai testato. Questo workaround cambia l’adapter del provider: verifica quindi quali opzioni specifiche di OpenRouter restano disponibili.
Anthropic Agent SDK + OpenRouter
Anthropic Agent SDK rappresenta un confine d’integrazione separato. La guida ufficiale di OpenRouter ad Agent SDK spiega che lo SDK usa Claude Code come runtime e accetta questa configurazione ambientale:
export ANTHROPIC_BASE_URL="https://openrouter.ai/api"
export ANTHROPIC_AUTH_TOKEN="$OPENROUTER_API_KEY"
export ANTHROPIC_API_KEY=""
Lasciare vuoto ANTHROPIC_API_KEY è intenzionale. La chiave OpenRouter va inserita in ANTHROPIC_AUTH_TOKEN e, in questa configurazione Agent SDK, il percorso /api non deve essere sostituito da quello compatibile con OpenAI, /api/v1.
La struttura TypeScript mostrata dalla guida è:
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 fornisce l’endpoint del modello; Agent SDK mantiene il ciclo dell’agente e il runtime dei tool. Testa tool, streaming, comportamento del contesto e opzioni specifiche del modello sull’esatto percorso che userai in produzione: la compatibilità con le funzionalità native Anthropic non è automatica.
401, 404 e il caso “con curl funziona, ma nello SDK no”
Prima di cambiare modello, individua il livello in cui si verifica l’errore. Autenticazione, costruzione dell’URL, ricerca del modello, filtri sui provider e validazione dei parametri richiedono correzioni diverse.
| Sintomo | Livello probabile | Prima verifica |
|---|---|---|
401 Missing Authentication header | Header o caricamento delle variabili d’ambiente | Conferma Authorization: Bearer ...; prova /api/v1/key |
401 Unauthorized con una chiave presente | Chiave errata, revocata o vuota | Verifica che il processo veda OPENROUTER_API_KEY; usa una chiave OpenRouter |
404 con un percorso malformato | Composizione della base URL | I client compatibili con OpenAI usano /api/v1; Agent SDK usa /api; controlla eventuali duplicazioni di /v1 |
404 Invalid model o model_not_found | Identificativo del modello | Interroga /api/v1/models e usa l’id restituito o un alias documentato |
404 No allowed providers are available | Filtri sui provider o disponibilità dell’endpoint | Controlla only, ignore, max_price, require_parameters e le impostazioni sulla policy dei dati |
400 per top_k, min_p, tool o JSON Schema | Incompatibilità nelle capacità | Controlla supported_parameters; quando fai routing, richiedi provider compatibili |
curl funziona ma il framework no | Percorso dello SDK, maiuscole/minuscole, serializzazione o header persi | Confronta URL finale, header di autenticazione, ID del modello e corpo JSON |
OpenRouter documenta GET /api/v1/key come controllo autenticato della chiave API. Eseguilo prima di fare debugging su LangChain:
curl -sS https://openrouter.ai/api/v1/key \
-H "Authorization: Bearer $OPENROUTER_API_KEY"
Poi verifica che il modello sia disponibile:
curl -sS https://openrouter.ai/api/v1/models \
-H "Authorization: Bearer $OPENROUTER_API_KEY"
La Models API può filtrare i modelli che supportano i tool prima che tu li aggiunga:
curl -sS \
"https://openrouter.ai/api/v1/models?supported_parameters=tools" \
-H "Authorization: Bearer $OPENROUTER_API_KEY"
Un errore 404 può anche indicare che il modello esiste, ma che dopo l’applicazione dei filtri non è rimasto alcun provider idoneo. Per analizzare le decisioni di routing, abilita l’header opt-in X-OpenRouter-Metadata: enabled; la documentazione sui Router Metadata descrive l’oggetto openrouter_metadata risultante e la gestione degli errori.
In sviluppo, confronta la richiesta generata dopo aver oscurato la chiave: base URL, percorso finale, presenza dell’header Authorization, ID esatto del modello e campi del body specifici del provider. Per un approfondimento mirato sull’autenticazione, consulta la guida di AIReiter alla risoluzione degli errori di chiave API non valida e 401/403.
Checklist di migrazione per la produzione
- Allinea le generazioni dei pacchetti. Mantieni compatibili
langchain,langchain-coreelangchain-openrouteroppure@langchain/openrouter, invece di copiare versioni da un vecchio tutorial. - Risolvi gli ID canonici dei modelli. Usa
/api/v1/modelso il catalogo dei modelli OpenRouter; considera gli alias una modifica comportamentale deliberata. - Stabilisci il confine dei fallback. Il fallback tra provider migliora la disponibilità. Il fallback tra modelli può cambiare qualità, latenza, capacità e costi. Registra il valore finale di
modelrestituito da OpenRouter. - Richiedi le capacità necessarie. Usa i filtri sulle capacità dei provider per richieste con tool e output strutturato; il supporto ai tool non dimostra che sia supportato anche lo strict JSON Schema.
- Valida gli output dell’applicazione. Pydantic, Zod o un altro validatore devono controllare gli argomenti dei tool e le risposte strutturate prima che il codice a valle possa agire su di essi.
- Registra i metadati utili. Conserva ID della richiesta, modello, usage, motivo di completamento e informazioni sul provider che ha servito la richiesta, nei limiti consentiti dalla tua privacy policy. L’integrazione LangChain documenta anche
session_idper raggruppare le richieste etraceper i metadati per singola richiesta. - Testa entrambe le modalità di streaming. Una chiamata non-streaming riuscita non dimostra che i chunk delle tool call o lo streaming nell’interfaccia funzionino correttamente.
- Verifica le restrizioni sui provider. Se usi
only,ignore, filtri sulla raccolta dati o un limite di prezzo, prova anche il percorso di errore in cui non esiste alcun provider idoneo. - Mantieni le credenziali sul server. Conserva
OPENROUTER_API_KEYnell’ambiente server o in un secret manager, mai in un bundle per il browser o in un file.envcommittato.
L’ordine più affidabile è questo: verifica la chiave, verifica l’ID del modello, esegui una semplice chiamata testuale e solo dopo aggiungi routing, tool, output strutturato e streaming multi-step.