OpenRouter
OpenRouterLLMService streams chat completions from OpenRouter through its OpenAI-compatible API.
Overview
OpenRouter puts one OpenAI-compatible API in front of many model providers, and routes each request to one of them.
OpenRouterLLMService: anOpenAILLMServicesubclass with OpenRouter's base URL and default model.
Upstream page
Pipecat's documentation for the same service.
Source
packages/openrouter/src/llm.ts.
OpenRouter docs
The chat completion API reference.
Provider routing
The provider preferences and their semantics.
Installation
bun add @pipedog/openrouterIt depends on @pipedog/openai, which is installed with it.
import { OpenRouterLLMService } from "@pipedog/openrouter/llm";Prerequisites
- An OpenRouter account, with credits, and an API key from OpenRouter.
- Pass the key as
apiKey. It is required, and the service never falls back toOPENAI_API_KEY. The examples read it fromOPENROUTER_API_KEY.
OpenRouterLLMService
Configuration
Prop
Type
The other options (organization, project, defaultHeaders, serviceTier,
retryOnTimeout, retryTimeoutSecs, function calling) are OpenAILLMService's. See
OpenAI.
| Base URL | https://openrouter.ai/api/v1 |
| Default model | openai/gpt-4.1 |
| Models to try | openai/gpt-4.1, anthropic/claude-sonnet-4. See the provider's catalog for the rest. |
Settings
Runtime-updatable settings, passed as settings: new OpenRouterLLMService.Settings({...})
(OpenRouterLLMSettings). They are OpenAI's settings, with these
defaults:
| Setting | Type | Default | Description |
|---|---|---|---|
model | string | "openai/gpt-4.1" | Model name. |
provider | OpenRouterProviderPreferences | null | null | Which upstream providers may serve the request, sent as provider in the request body. A plain object is converted. |
Every other setting keeps OpenAI's default. Unset sampling settings are left out of the request, so
the provider's own defaults apply. extra carries provider-specific request fields.
Usage
import { LLMUpdateSettingsFrame } from "@pipedog/core/frames/frames";
import { OpenRouterLLMService } from "@pipedog/openrouter/llm";
const llm = new OpenRouterLLMService({
apiKey: process.env.OPENROUTER_API_KEY ?? "",
settings: new OpenRouterLLMService.Settings({
model: "openai/gpt-4.1",
systemInstruction: "You are a helpful assistant in a voice conversation.",
temperature: 0.7,
}),
});Constrain which providers serve the request with OpenRouterLLMService.ProviderPreferences. Its options are camelCase (allowFallbacks, preferredMaxLatency, ...); options it doesn't know are sent as given:
const routed = new OpenRouterLLMService({
apiKey: process.env.OPENROUTER_API_KEY ?? "",
settings: new OpenRouterLLMService.Settings({
model: "anthropic/claude-sonnet-4",
provider: new OpenRouterLLMService.ProviderPreferences({
sort: "latency",
preferredMaxLatency: { p90: 1.5 },
allowFallbacks: true,
}),
}),
});Change settings mid-call with an LLMUpdateSettingsFrame. The next completion uses them:
// Queue it on the worker, or push it from a processor upstream of the service.
const update = new LLMUpdateSettingsFrame({
delta: new OpenRouterLLMService.Settings({ temperature: 0.2, maxTokens: 300 }),
});Notes
- Runs a completion for each
LLMContextFrame, between anLLMFullResponseStartFrameand anLLMFullResponseEndFrame, and streams the reply asLLMTextFrames, asOpenAILLMServicedoes. Function calls, token usage metrics andrunInferencework the same way. - "developer" messages are sent as "user" messages, since the API doesn't support that role (
supportsDeveloperRoleisfalse). - For Gemini models, every system message after the first is sent as a user message.
- A
providerinextra.extra_bodywins over theprovidersetting.
Runtime notes
| Runtime | Works | What it needs |
|---|---|---|
| Bun | ✅ yes | Nothing. |
| Node.js | ✅ yes | Nothing. |
| Deno | ✅ yes | Nothing. |
| Cloudflare Workers | ✅ yes | Nothing. |
Generated from pipecat.runtimes and pipecat.compat in packages/openrouter/package.json: ✅ means the compat smoke test passes on that runtime.
- The client is the official
openaiJS SDK (through@pipedog/openai), which talks throughfetch. It works on Bun, Node, Deno and Cloudflare Workers. - The deprecated
modelconstructor option is not ported. Usesettings. - Tested against a local OpenAI-compatible mock server, not against OpenRouter itself.
pipedog is an unofficial community TypeScript port of Pipecat. It is not affiliated with or endorsed by Daily or the Pipecat project.
Portions adapted from the Pipecat documentation, Copyright (c) 2024–2025 Daily, BSD-2-Clause (see apps/docs/LICENSE-pipecat-docs).