@polyweave/openrouter (1.0.9)
Installation
@polyweave:registry=https://hub.kl1.tenere.ai/api/packages/Polyweave/npm/npm install @polyweave/openrouter@1.0.9"@polyweave/openrouter": "1.0.9"About this package
@polyweave/openrouter
OpenRouter adapter for Polyweave — unified access to 300+ AI models from multiple providers via OpenAI-compatible API. Supports text generation, vision/multimodal, image generation, SSE streaming, tool calling, automatic model routing, and lazy cost estimation with dynamic API pricing lookup.
Synopsis
import { createOpenRouterAdapter } from '@polyweave/openrouter';
const adapter = createOpenRouterAdapter({ apiKey: 'YOUR_API_KEY' });
adapter.source.pipe(someSink);
adapter.sink.write({ type: 'PARAM', content: { key: 'model', value: 'openai/gpt-4o' } });
adapter.sink.write({ type: 'TEXT', content: { text: 'Hello!' } });
Install
npm install @polyweave/openrouter
Why
OpenRouter provides a single API key gateway to 300+ models from Anthropic, OpenAI, Google, Meta, Mistral, and others. This adapter converts the Polyweave frame protocol into OpenRouter API calls, handling model routing, automatic fallbacks, cost estimation with live API pricing lookup, streaming via SSE, tool calling, and vision support — all through a single frame duplex. No per-provider adapter switching; just send frames and receive frames.
API
createOpenRouterAdapter(config)
Returns an IFrameDuplex stream adapter (object with source and sink).
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
config.apiKey |
string |
No | process.env.OPENROUTER_API_KEY |
OpenRouter API key |
config.baseURL |
string |
No | 'https://openrouter.ai/api' |
API base URL path (scheme not required) |
config.siteUrl |
string |
No | undefined |
Your site URL for openrouter.ai rankings |
config.siteName |
string |
No | undefined |
Your site name for openrouter.ai rankings |
config.timeout |
number |
No | 120000 |
Request timeout in ms |
config.name |
string |
No | null |
Adapter name used in topology reports |
Adaptive authentication: The API key can be provided at construction time, via the OPENROUTER_API_KEY environment variable, or at runtime via a PARAM frame with key: 'apiKey'. If no key is provided at construction, the adapter buffers frames until a key arrives.
Return value: IFrameDuplex — an object with:
sink— receives Polyweave frames, translates to OpenRouter HTTP callssource— emits Polyweave frames from OpenRouter responsessetReady(key)— inject API key at runtime (flushes buffered frames)resetConversation()— clear accumulated message historyready(getter) — whether the adapter has an API keyapiKey(getter) — current API key value
Frame Protocol (Input)
| Frame Type | Description |
|---|---|
PARAM |
Sets a configuration parameter. frame.content.key / frame.content.value. See PARAM keys table. |
TEXT |
User text prompt. Appended to conversation as role: 'user' if metadata.role is absent. |
IMAGE |
Image for vision or generation. Routed based on model capabilities. |
TOOL_RESULT |
Result of a tool call. Appended to conversation as role: 'tool'. |
COST_CHECK |
Enter dry-run estimation mode. Buffers subsequent frames, runs cost estimation, emits a COST frame with estimate. |
ESTIMATED_OUTPUT |
Metadata frame in cost-check mode — buffered but does not trigger estimate calculation. |
START |
Pass-through (ignored). |
END |
Pass-through (ignored). |
PARAM keys:
| Key | Type | Default | Description |
|---|---|---|---|
apiKey |
string |
env | Sets API key (triggers ready state) |
model |
string |
'openai/gpt-4o-mini' |
OpenRouter model ID (e.g. 'openai/gpt-4o') |
max_tokens |
number |
1024 |
Max output tokens |
temperature |
number |
undefined |
Sampling temperature |
top_p |
number |
undefined |
Nucleus sampling |
top_k |
number |
undefined |
Top-K sampling |
frequency_penalty |
number |
undefined |
Frequency penalty |
presence_penalty |
number |
undefined |
Presence penalty |
repetition_penalty |
number |
undefined |
Repetition penalty |
seed |
number |
undefined |
Random seed |
stop |
string[] |
undefined |
Stop sequences |
messages |
Array |
undefined |
Pre-seeded conversation messages |
system |
string |
undefined |
System prompt |
stream |
boolean |
true for chat, false for image |
Enable SSE streaming |
tools |
Array |
undefined |
Tool definitions for function calling |
tool_choice |
string |
undefined |
Tool selection mode |
response_format |
Object |
undefined |
Response format (e.g. JSON mode) |
logprobs |
boolean |
undefined |
Return log probabilities |
top_logprobs |
number |
undefined |
Top logprobs to return |
transforms |
string[] |
undefined |
OpenRouter transforms (e.g. ['middle-out']) |
models |
string[] |
undefined |
Fallback model list for routing |
route |
string |
undefined |
Routing strategy ('fallback') |
provider |
Object |
undefined |
Provider preferences (e.g. { order: ['Anthropic'] }) |
task |
string |
undefined |
Task hint: 'chat' | 'image' | 'vision' | 'embedding' |
input |
string|string[] |
undefined |
Embedding input text(s) |
dimensions |
number |
undefined |
Embedding output dimensions |
encoding_format |
string |
undefined |
Embedding encoding ('float' | 'base64') |
prompt |
string |
undefined |
Image generation prompt |
num_images |
number |
1 |
Number of images to generate |
receipt_mode |
string |
'final' |
Receipt emission: 'none' | 'final' | 'progressive' |
receipt_interval |
number |
50 |
Tokens between progressive receipts |
Frame Protocol (Output)
| Frame Type | Description |
|---|---|
TEXT |
Streamed or complete text response (chunks in stream mode, full response otherwise) |
TOOL_CALL |
Tool call requested by the model |
IMAGE |
Generated image data (base64) |
RECEIPT |
Final receipt with usage stats (tokens, cost) |
COST |
Cost estimate (emitted after COST_CHECK input) |
ERROR |
Error frame with mapped error code |
END |
End-of-response marker |
META |
Passthrough for unknown frame types |
Error Conditions
| Condition | ErrorFrame.code |
When |
|---|---|---|
| API error (HTTP error response) | Mapped by status code (PROVIDER_ERROR, RATE_LIMITED, QUOTA_EXCEEDED, etc.) |
OpenRouter returns non-2xx |
Network error (ECONNREFUSED, ENOTFOUND) |
SERVICE_UNAVAILABLE |
Cannot reach OpenRouter |
Network error (ETIMEDOUT, ESOCKETTIMEDOUT) |
QUEUE_TIMEOUT |
Request timeout |
Network error (ECONNRESET) |
PROVIDER_ERROR |
Connection reset |
Budget exceeded in COST_CHECK |
BUDGET_EXCEEDED |
Estimated cost > maxBudget in COST_CHECK options |
| Estimation error | ESTIMATION_ERROR |
Cost estimation fails (e.g. price fetch error on unknown model) |
Error frames include streamId for correlation, and END frames are emitted after errors with metadata: { error: true, errorCode }.
fetchPricing(modelId, callback [, options])
Fetches current pricing for a specific model from the OpenRouter models API. Uses request coalescing (multiple simultaneous calls share one HTTP request) and a 1-hour in-memory cache.
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
modelId |
string |
Yes | — | OpenRouter model ID (e.g. 'openai/gpt-4o') |
callback |
function |
Yes | — | Callback signature: (error, pricing) => void |
options.skipCache |
boolean |
No | false |
Force fresh API fetch |
options.timeout |
number |
No | 10000 |
Request timeout in ms |
pricing object (on success): { mtoksInput, mtoksOutput, perImageInput, perImageOutput, perAudioMinute, perRequest, mtoksReasoning, _source, _inputModalities, _outputModalities, _modelName, _contextLength, _fetchedAt }
Error paths: API error → fallback pricing if model in fallback table, otherwise passes error to callback. Model not found → fallback if in table, otherwise Error with code 'MODEL_NOT_FOUND'.
getCachedPricing(modelId)
Synchronous lookup of cached pricing for a model. Returns pricing object or null if not cached or TTL expired (1 hour).
clearPricingCache()
Clears all cached pricing and the full models cache. For testing and forced refresh.
getModelCapabilities(modelId, callback)
Returns { inputModalities, outputModalities, modelName, contextLength } for a model. Checks cache first, falls back to API fetch.
supportsModality(modelId, modality, direction, callback)
| Name | Type | Required | Description |
|---|---|---|---|
modelId |
string |
Yes | Model ID |
modality |
string |
Yes | 'text' | 'image' | 'audio' | 'video' |
direction |
string |
Yes | 'input' | 'output' |
callback |
function |
Yes | (error, supports: boolean) => void |
On lookup error, assumes text-only support (returns true for 'text').
calculateCostFromPricing(pricing, params)
Calculates total cost from pricing config. Returns { totalCost, currency: 'USD', inputTokens, inputCost, outputTokens, outputCost, imageInputCount, imageInputCost, imageOutputCount, imageOutputCost, audioMinutes, audioCost, requestCost, _pricingSource }.
fetchCredits(apiKey, callback)
Fetches current credits balance from OpenRouter.
| Name | Type | Required | Description |
|---|---|---|---|
apiKey |
string |
Yes | OpenRouter API key |
callback |
function |
Yes | (error, credits) => void |
credits object (on success): { totalCredits: number, totalUsage: number, remaining: number, raw: object }
Throws/errors if apiKey is falsy (emitted via callback as Error('API key required')), on HTTP error (≥400), on JSON parse failure, or on network error.
checkSufficientCredits(apiKey, estimatedCost, callback)
| Name | Type | Required | Description |
|---|---|---|---|
apiKey |
string |
Yes | OpenRouter API key |
estimatedCost |
number |
Yes | Estimated cost in USD |
callback |
function |
Yes | (error, result) => void |
result object: { sufficient: boolean, remaining: number, estimated: number, shortfall: number, totalCredits, totalUsage, raw }
Wraps fetchCredits — propagates its error conditions.
Tests
npm test
Tests cover: adapter creation and frame routing, cost estimation with pricing fetch, PARAM accumulation, COST_CHECK dry-run mode (including budget exceeded detection), error mapping across HTTP status codes and network errors, rate limit header extraction, credits fetching and balance checking, SSE streaming, and js-rigor property tests for pricing estimatation monotonicity.
Requirements
- Node.js >= 22.0.0
- ESM (
"type": "module") @polyweave/core(peer)@polyweave/errors(peer)@polyweave/costs(peer)
Dependencies
Development Dependencies
| ID | Version |
|---|---|
| @rigor/core | * |
Peer Dependencies
| ID | Version |
|---|---|
| @polyweave/core | * |
| @polyweave/costs | * |
| @polyweave/errors | * |