@x12i/openrouter-runtime 1.1.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +160 -145
- package/dist/index.cjs +153 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +60 -5
- package/dist/index.d.ts +60 -5
- package/dist/index.js +149 -23
- package/dist/index.js.map +1 -1
- package/package.json +41 -41
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 x12i
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 x12i
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,145 +1,160 @@
|
|
|
1
|
-
# @x12i/openrouter-runtime
|
|
2
|
-
|
|
3
|
-
TypeScript runtime for executing OpenRouter calls with normalized request and response objects.
|
|
4
|
-
|
|
5
|
-
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.
|
|
6
|
-
|
|
7
|
-
## Install
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
npm install @x12i/openrouter-runtime
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## Usage
|
|
14
|
-
|
|
15
|
-
```ts
|
|
16
|
-
import { createOpenRouterRuntime } from "@x12i/openrouter-runtime";
|
|
17
|
-
|
|
18
|
-
const runtime = createOpenRouterRuntime({
|
|
19
|
-
apiKey: process.env.OPENROUTER_API_KEY!,
|
|
20
|
-
defaults: {
|
|
21
|
-
serverTools: {
|
|
22
|
-
datetime: { mode: "allowed", timezone: "Asia/Jerusalem" }
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
const response = await runtime.run({
|
|
28
|
-
model: "openai/gpt-5.2",
|
|
29
|
-
messages: [{ role: "user", content: "What time is it?" }]
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
console.log(response.text);
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## Console logging
|
|
36
|
-
|
|
37
|
-
Turn on runtime console logs with:
|
|
38
|
-
|
|
39
|
-
```bash
|
|
40
|
-
OPENROUTER_RUNTIME_LOGS=true
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
When that env var is `true` / `1` / `yes` / `on`, and you do not pass a custom `logger`, the runtime logs to the console:
|
|
44
|
-
|
|
45
|
-
- `runtime.request.started` — includes `streaming: false` and `entrypoint: "run"`
|
|
46
|
-
- `runtime.request.compiled` — includes `bodyStream: false` and any streaming-related warning codes
|
|
47
|
-
- `runtime.response.normalized`
|
|
48
|
-
- `runtime.executeStreamingChat.called` — if someone hits the reserved streaming method
|
|
49
|
-
- provider retry / function-tool events
|
|
50
|
-
|
|
51
|
-
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`.
|
|
52
|
-
|
|
53
|
-
Example:
|
|
54
|
-
|
|
55
|
-
```bash
|
|
56
|
-
OPEN_ROUTER_KEY=sk-or-...
|
|
57
|
-
OPENROUTER_RUNTIME_LOGS=true
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
You can still pass an explicit `logger` to override the console logger.
|
|
61
|
-
|
|
62
|
-
##
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
});
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
### `
|
|
121
|
-
|
|
122
|
-
```ts
|
|
123
|
-
|
|
124
|
-
model: "openai/gpt-5.2",
|
|
125
|
-
prompt: "
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
1
|
+
# @x12i/openrouter-runtime
|
|
2
|
+
|
|
3
|
+
TypeScript runtime for executing OpenRouter calls with normalized request and response objects.
|
|
4
|
+
|
|
5
|
+
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.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @x12i/openrouter-runtime
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { createOpenRouterRuntime } from "@x12i/openrouter-runtime";
|
|
17
|
+
|
|
18
|
+
const runtime = createOpenRouterRuntime({
|
|
19
|
+
apiKey: process.env.OPENROUTER_API_KEY!,
|
|
20
|
+
defaults: {
|
|
21
|
+
serverTools: {
|
|
22
|
+
datetime: { mode: "allowed", timezone: "Asia/Jerusalem" }
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const response = await runtime.run({
|
|
28
|
+
model: "openai/gpt-5.2",
|
|
29
|
+
messages: [{ role: "user", content: "What time is it?" }]
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
console.log(response.text);
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Console logging
|
|
36
|
+
|
|
37
|
+
Turn on runtime console logs with:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
OPENROUTER_RUNTIME_LOGS=true
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
When that env var is `true` / `1` / `yes` / `on`, and you do not pass a custom `logger`, the runtime logs to the console:
|
|
44
|
+
|
|
45
|
+
- `runtime.request.started` — includes `streaming: false` and `entrypoint: "run"`
|
|
46
|
+
- `runtime.request.compiled` — includes `bodyStream: false` and any streaming-related warning codes
|
|
47
|
+
- `runtime.response.normalized`
|
|
48
|
+
- `runtime.executeStreamingChat.called` — if someone hits the reserved streaming method
|
|
49
|
+
- provider retry / function-tool events
|
|
50
|
+
|
|
51
|
+
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`.
|
|
52
|
+
|
|
53
|
+
Example:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
OPEN_ROUTER_KEY=sk-or-...
|
|
57
|
+
OPENROUTER_RUNTIME_LOGS=true
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
You can still pass an explicit `logger` to override the console logger.
|
|
61
|
+
|
|
62
|
+
## App name and metadata
|
|
63
|
+
|
|
64
|
+
`agentId` is optional. When it is set, OpenRouter's activity log shows it in the **App** column. The runtime sends that value as `X-Title` and `X-OpenRouter-Title`, after `defaultHeaders`, so it wins over `appAttribution.appName` for that call. `appAttribution.siteUrl` is still sent as `HTTP-Referer` when set. Omit `agentId` when you do not have one.
|
|
65
|
+
|
|
66
|
+
`metadata` is optional. String, number, and boolean values, plus `agentId`, are stored as one record on the request `metadata` object: `m.0`, `m.1`, and so on. Read it back with `decodeProviderMetadata`. The original `metadata` object is still copied onto the normalized response. Objects and arrays are not stored in the record. The record must fit in 16 pieces of 256 characters.
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
await runtime.run({
|
|
70
|
+
model: "openai/gpt-5.2",
|
|
71
|
+
prompt: "Explain the tradeoff in one paragraph.",
|
|
72
|
+
agentId: "agent-42",
|
|
73
|
+
metadata: { sessionId: "s-1" }
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Server Tools
|
|
78
|
+
|
|
79
|
+
Enable OpenRouter server tools through `serverTools`:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
await runtime.run({
|
|
83
|
+
model: "anthropic/claude-sonnet-4",
|
|
84
|
+
messages: [{ role: "user", content: "Research recent OpenRouter web search changes." }],
|
|
85
|
+
serverTools: {
|
|
86
|
+
webSearch: { mode: "required", maxResults: 5 },
|
|
87
|
+
webFetch: { mode: "allowed", maxContentTokens: 50000 }
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Citation Policy
|
|
93
|
+
|
|
94
|
+
`defaults.requireCitationsWhenSearchUsed` controls the package-wide default for web-search citation enforcement. A request-level `serverTools.webSearch.requireCitations` value overrides that default:
|
|
95
|
+
|
|
96
|
+
- `requireCitations: true`: if web search is used and no citations are extracted, the runtime emits `CITATIONS_REQUIRED_BUT_MISSING`.
|
|
97
|
+
- `requireCitations: false`: disables citation enforcement for that request, even if the global default is `true`.
|
|
98
|
+
|
|
99
|
+
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[]`.
|
|
100
|
+
|
|
101
|
+
`applyPatch` automatically selects the Responses API. The runtime returns patch proposals and never mutates files unless an explicit `patchApplier` is supplied.
|
|
102
|
+
|
|
103
|
+
## Function Tools
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
const runtime = createOpenRouterRuntime({
|
|
107
|
+
apiKey: process.env.OPENROUTER_API_KEY!,
|
|
108
|
+
tools: {
|
|
109
|
+
getCustomerRisk: async (args) => ({ score: 82, args })
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Function calls are executed locally and looped back to OpenRouter until a final response is produced or `maxToolIterations` is reached.
|
|
115
|
+
|
|
116
|
+
## Streaming
|
|
117
|
+
|
|
118
|
+
Streaming is a **separate API**. It is never an argument on `run()`, never the default, and never available through the removed `stream()` name.
|
|
119
|
+
|
|
120
|
+
### `run()` — non-streaming only
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
const response = await runtime.run({
|
|
124
|
+
model: "openai/gpt-5.2",
|
|
125
|
+
prompt: "Summarize this document."
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
- Always sends `stream: false`
|
|
130
|
+
- Returns a completed `RuntimeResponse`
|
|
131
|
+
- Use for tools, research, extraction, patches, automation
|
|
132
|
+
|
|
133
|
+
Any truthy `rawOpenRouterOverrides.stream` is overwritten (`STREAMING_OVERRIDE_IGNORED_FOR_RUN`). Nested `advisor.stream` is forced off (`ADVISOR_STREAMING_IGNORED_FOR_RUN`).
|
|
134
|
+
|
|
135
|
+
### `executeStreamingChat()` — streaming only
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
for await (const event of runtime.executeStreamingChat({
|
|
139
|
+
model: "openai/gpt-5.2",
|
|
140
|
+
prompt: "Say hello"
|
|
141
|
+
})) {
|
|
142
|
+
if (event.type === "stream.text.delta") {
|
|
143
|
+
process.stdout.write(event.data.text);
|
|
144
|
+
}
|
|
145
|
+
if (event.type === "stream.done") {
|
|
146
|
+
console.log("\nfinal:", event.data.text);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
- Always sends `stream: true` (Chat Completions SSE)
|
|
152
|
+
- Yields typed events: `stream.start`, `stream.text.delta`, `stream.tool_call.delta`, `stream.usage`, `stream.warning`, `stream.error`, `stream.done`
|
|
153
|
+
- Chat Completions only — Responses / `applyPatch` must use `run()`
|
|
154
|
+
- Local function-tool loops are not run here; use `run()` for tool iteration
|
|
155
|
+
|
|
156
|
+
| Method | Streaming? | Notes |
|
|
157
|
+
| --- | --- | --- |
|
|
158
|
+
| `runtime.run(request)` | No | Default execution path |
|
|
159
|
+
| `runtime.executeStreamingChat(request)` | Yes | Explicit streaming API |
|
|
160
|
+
| `runtime.stream(request)` | — | **Removed** — breaks by design |
|