@x12i/openrouter-runtime
v1.1.0
Published
TypeScript runtime for OpenRouter models, server tools, user tools, citations, patches, and normalized responses.
Readme
@x12i/openrouter-runtime
TypeScript runtime for executing OpenRouter calls with normalized request and response objects.
It supports Chat Completions, Responses, OpenRouter server tools, local function tools, citation extraction, usage normalization, generated image extraction, patch proposal extraction, retries, and policy validation.
Install
npm install @x12i/openrouter-runtimeUsage
import { createOpenRouterRuntime } from "@x12i/openrouter-runtime";
const runtime = createOpenRouterRuntime({
apiKey: process.env.OPENROUTER_API_KEY!,
defaults: {
serverTools: {
datetime: { mode: "allowed", timezone: "Asia/Jerusalem" }
}
}
});
const response = await runtime.run({
model: "openai/gpt-5.2",
messages: [{ role: "user", content: "What time is it?" }]
});
console.log(response.text);Console logging
Turn on runtime console logs with:
OPENROUTER_RUNTIME_LOGS=trueWhen that env var is true / 1 / yes / on, and you do not pass a custom logger, the runtime logs to the console:
runtime.request.started— includesstreaming: falseandentrypoint: "run"runtime.request.compiled— includesbodyStream: falseand any streaming-related warning codesruntime.response.normalizedruntime.executeStreamingChat.called— if someone hits the reserved streaming method- provider retry / function-tool events
This package does not auto-load .env. Put the vars in your environment (shell export, process manager, or host dotenv) before calling createOpenRouterRuntime(). See .env.example.
Example:
OPEN_ROUTER_KEY=sk-or-...
OPENROUTER_RUNTIME_LOGS=trueYou can still pass an explicit logger to override the console logger.
Server Tools
Enable OpenRouter server tools through serverTools:
await runtime.run({
model: "anthropic/claude-sonnet-4",
messages: [{ role: "user", content: "Research recent OpenRouter web search changes." }],
serverTools: {
webSearch: { mode: "required", maxResults: 5 },
webFetch: { mode: "allowed", maxContentTokens: 50000 }
}
});Citation Policy
defaults.requireCitationsWhenSearchUsed controls the package-wide default for web-search citation enforcement. A request-level serverTools.webSearch.requireCitations value overrides that default:
requireCitations: true: if web search is used and no citations are extracted, the runtime emitsCITATIONS_REQUIRED_BUT_MISSING.requireCitations: false: disables citation enforcement for that request, even if the global default istrue.
When defaults.onPolicyViolation is "throw", citation policy failures are returned as errors[] with source: "policy" and status: "policy_violation". When it is "return_error", they remain in warnings[].
applyPatch automatically selects the Responses API. The runtime returns patch proposals and never mutates files unless an explicit patchApplier is supplied.
Function Tools
const runtime = createOpenRouterRuntime({
apiKey: process.env.OPENROUTER_API_KEY!,
tools: {
getCustomerRisk: async (args) => ({ score: 82, args })
}
});Function calls are executed locally and looped back to OpenRouter until a final response is produced or maxToolIterations is reached.
Streaming
Streaming is a separate API. It is never an argument on run(), never the default, and never available through the removed stream() name.
run() — non-streaming only
const response = await runtime.run({
model: "openai/gpt-5.2",
prompt: "Summarize this document."
});- Always sends
stream: false - Returns a completed
RuntimeResponse - Use for tools, research, extraction, patches, automation
Any truthy rawOpenRouterOverrides.stream is overwritten (STREAMING_OVERRIDE_IGNORED_FOR_RUN). Nested advisor.stream is forced off (ADVISOR_STREAMING_IGNORED_FOR_RUN).
executeStreamingChat() — streaming only
for await (const event of runtime.executeStreamingChat({
model: "openai/gpt-5.2",
prompt: "Say hello"
})) {
if (event.type === "stream.text.delta") {
process.stdout.write(event.data.text);
}
if (event.type === "stream.done") {
console.log("\nfinal:", event.data.text);
}
}- Always sends
stream: true(Chat Completions SSE) - Yields typed events:
stream.start,stream.text.delta,stream.tool_call.delta,stream.usage,stream.warning,stream.error,stream.done - Chat Completions only — Responses /
applyPatchmust userun() - Local function-tool loops are not run here; use
run()for tool iteration
| Method | Streaming? | Notes |
| --- | --- | --- |
| runtime.run(request) | No | Default execution path |
| runtime.executeStreamingChat(request) | Yes | Explicit streaming API |
| runtime.stream(request) | — | Removed — breaks by design |
