@namzu/openrouter 2.4.1 → 3.0.1
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/CHANGELOG.md +57 -0
- package/README.md +30 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +267 -28
- package/dist/client.js.map +1 -1
- package/package.json +2 -2
- package/src/client.ts +303 -27
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,62 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 3641102: Prompt caching now places explicit breakpoints instead of sending a top-level
|
|
8
|
+
`cache_control: { type: 'auto' }`, a value OpenRouter does not define (its
|
|
9
|
+
top-level field takes `{ type: 'ephemeral' }` and puts the breakpoint on the
|
|
10
|
+
last cacheable block — with request-only context at the tail, the part the next
|
|
11
|
+
request replaces).
|
|
12
|
+
|
|
13
|
+
For `anthropic/`, `google/gemini` and `qwen/` models, the models OpenRouter
|
|
14
|
+
documents as needing explicit breakpoints, a cache request marks the last
|
|
15
|
+
static system message and the last non-system message before request-only
|
|
16
|
+
context, each inside a content part (a string becomes one text part). A system
|
|
17
|
+
message directly before the context is the step's guidance, which changes from
|
|
18
|
+
step to step, so it is passed over rather than marked. Other models, which
|
|
19
|
+
OpenRouter documents as caching automatically, are sent no marker and keep
|
|
20
|
+
their plain string content.
|
|
21
|
+
|
|
22
|
+
## 3.0.0
|
|
23
|
+
|
|
24
|
+
### Major Changes
|
|
25
|
+
|
|
26
|
+
- e83dfe5: `listModels()` omits `inputPrice` and `outputPrice` for a model this driver has no rate for, instead of reporting the rate as `0`.
|
|
27
|
+
|
|
28
|
+
The returned `ModelInfo[]` is the published signature that moved: both fields are optional on it now, so read them as `number | undefined`. Their runtime values changed too, and that half is not caught by a type — a key that used to be present is now absent, and code that defaulted it with `?? 0` will keep doing exactly what this release stopped doing for you.
|
|
29
|
+
|
|
30
|
+
What each driver does now:
|
|
31
|
+
|
|
32
|
+
- **`@namzu/anthropic`** — the live listing from `models.list` carries no rates, so it omits both. The bundled offline catalogue still carries its real published prices, unchanged.
|
|
33
|
+
- **`@namzu/openai`** — `client.models.list` publishes no rates, so both are omitted. `codex` likewise.
|
|
34
|
+
- **`@namzu/deepseek`** — the account listing and the bundled known-model list are unrated here, so both are omitted.
|
|
35
|
+
- **`@namzu/google`** — the two-row price table still prices `gemini-2.5-flash` and `gemini-2.5-pro`; every other model the API returns is now unpriced rather than free.
|
|
36
|
+
- **`@namzu/openrouter`** — a model whose listing carries no `pricing` block is unpriced. A model the vendor prices at `"0"` is still free and still reports `0`, which is the distinction this release is about.
|
|
37
|
+
|
|
38
|
+
An absent rate means unknown, and a consumer that shows one should say so rather than `$0.00`. `@namzu/sdk`'s `ModelInfo.inputPrice` carries the reasoning.
|
|
39
|
+
|
|
40
|
+
### Minor Changes
|
|
41
|
+
|
|
42
|
+
- e5411cc: Send and surface reasoning.
|
|
43
|
+
|
|
44
|
+
The driver now carries the SDK's thinking controls onto OpenRouter's unified `reasoning`
|
|
45
|
+
object — `thinking: { type: 'enabled', budgetTokens }` becomes `{ enabled: true, max_tokens }`,
|
|
46
|
+
`adaptive` becomes `{ enabled: true }`, `disabled` becomes `{ enabled: false }`, and `effort`
|
|
47
|
+
becomes `{ effort }` — and streams the model's thinking back on the reasoning channel
|
|
48
|
+
instead of dropping it. `effort: 'max'` and `effort: 'ultra'` are refused rather than sent:
|
|
49
|
+
this wire cannot carry those levels, and an effort that arrives as a different depth is
|
|
50
|
+
indistinguishable from one that was honoured.
|
|
51
|
+
|
|
52
|
+
What a host observes: a caller that set `thinking` or `effort` used to get
|
|
53
|
+
`OpenRouterProvider does not implement thinking`; the request is now made. Reasoning blocks
|
|
54
|
+
arrive on the assistant message for every model that returns them (verified against
|
|
55
|
+
`nvidia/nemotron-3.5-lightning:free`, which returns both `reasoning` and `reasoning_details`),
|
|
56
|
+
and `usage.reasoningTokens` reports the thinking share of the completion tokens the vendor
|
|
57
|
+
already billed. The text is emitted once, not twice: OpenRouter sends it in both fields, and
|
|
58
|
+
the indexed `reasoning_details` form is the one used.
|
|
59
|
+
|
|
3
60
|
## 2.4.1
|
|
4
61
|
|
|
5
62
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -62,9 +62,38 @@ for await (const chunk of provider.chatStream({
|
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
`chatStream` is the only model entry point; a non-streaming call is that
|
|
65
|
-
stream collected. In practice the kernel's
|
|
65
|
+
stream collected. In practice the kernel's turn loop calls it and hands you
|
|
66
66
|
events.
|
|
67
67
|
|
|
68
|
+
## Reasoning
|
|
69
|
+
|
|
70
|
+
The driver carries the SDK's thinking controls and returns what the model thinks:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { registerOpenRouter, OpenRouterProvider } from '@namzu/openrouter'
|
|
74
|
+
|
|
75
|
+
registerOpenRouter()
|
|
76
|
+
const provider = new OpenRouterProvider({ apiKey: 'sk-example' })
|
|
77
|
+
|
|
78
|
+
for await (const chunk of provider.chatStream({
|
|
79
|
+
model: 'nvidia/nemotron-3.5-lightning:free',
|
|
80
|
+
messages: [{ role: 'user', content: 'What is 17 times 3?' }],
|
|
81
|
+
thinking: { type: 'enabled', budgetTokens: 4_096 },
|
|
82
|
+
})) {
|
|
83
|
+
if (chunk.delta.reasoning?.text) process.stderr.write(chunk.delta.reasoning.text)
|
|
84
|
+
if (chunk.delta.content) process.stdout.write(chunk.delta.content)
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`thinking: { type: 'enabled', budgetTokens }` is sent as `reasoning: { enabled: true,
|
|
89
|
+
max_tokens }`, `adaptive` as `{ enabled: true }`, `disabled` as `{ enabled: false }`, and
|
|
90
|
+
`effort` as `{ effort }`. `effort: 'max'` and `'ultra'` are refused rather than sent — this
|
|
91
|
+
wire cannot carry those levels. Reasoning arrives on the kernel's reasoning channel with an
|
|
92
|
+
index per block and a `done` marker when output starts, and `usage.reasoningTokens` reports
|
|
93
|
+
the thinking share of the completion tokens OpenRouter already bills as output. OpenRouter
|
|
94
|
+
sends each fragment twice (a flat `reasoning` string and the indexed `reasoning_details`);
|
|
95
|
+
the driver emits it once.
|
|
96
|
+
|
|
68
97
|
## Documentation
|
|
69
98
|
|
|
70
99
|
- [Namzu docs](https://github.com/cogitave/namzu/tree/main/docs)
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,oBAAoB,EACpB,WAAW,EACX,SAAS,EACT,oBAAoB,EACpB,WAAW,EAGX,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,oBAAoB,EACpB,WAAW,EACX,SAAS,EACT,oBAAoB,EACpB,WAAW,EAGX,MAAM,YAAY,CAAA;AASnB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAyQlD;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,oBAUrC,CAAA;AAED,qBAAa,kBAAmB,YAAW,WAAW;IACrD,QAAQ,CAAC,EAAE,gBAAe;IAC1B,QAAQ,CAAC,IAAI,gBAAe;IAC5B,QAAQ,CAAC,YAAY,uBAA0B;IAE/C,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,OAAO,CAAQ;gBAEX,MAAM,EAAE,gBAAgB;IAQpC,OAAO,CAAC,UAAU;IAkBlB,OAAO,CAAC,cAAc;IAwBtB,OAAO,CAAC,gBAAgB;IA4CjB,UAAU,CAAC,MAAM,EAAE,oBAAoB,GAAG,aAAa,CAAC,WAAW,CAAC;IA4K3E;;;;;;;;;;;;;OAaG;IACG,eAAe,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB1D;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACG,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAyB5F,uEAAuE;IACvE,OAAO,CAAC,cAAc,CAAC,CAAqB;IAEtC,UAAU,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;IAsCtD,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;CAWrC"}
|
package/dist/client.js
CHANGED
|
@@ -1,6 +1,126 @@
|
|
|
1
|
-
import { ProviderRequestError,
|
|
1
|
+
import { ProviderRequestError, isCallerAbortError, isProviderRequestError, providerHttpError, providerVendorError, } from '@namzu/sdk';
|
|
2
2
|
import { attributionHeaders } from '@namzu/sdk';
|
|
3
|
+
/**
|
|
4
|
+
* Models whose upstream caches only where the request places an explicit
|
|
5
|
+
* `cache_control` breakpoint. OpenRouter's prompt-caching page
|
|
6
|
+
* (openrouter.ai/docs/features/prompt-caching) lists Anthropic Claude, Google
|
|
7
|
+
* Gemini ("only the last breakpoint" is used) and Alibaba Qwen ("requires
|
|
8
|
+
* explicit `cache_control`"); OpenAI, DeepSeek, Grok, Moonshot, Groq and Z.AI
|
|
9
|
+
* cache automatically and are sent no marker, so their messages keep the
|
|
10
|
+
* plain string shape they have always had.
|
|
11
|
+
*/
|
|
12
|
+
function takesExplicitBreakpoints(model) {
|
|
13
|
+
const id = model.replace(/^~/, '').toLowerCase();
|
|
14
|
+
return id.startsWith('anthropic/') || id.startsWith('google/gemini') || id.startsWith('qwen/');
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Request-only context the runtime appends after the conversation: a
|
|
18
|
+
* runtime-context user message of kind `step-context`. It changes from one
|
|
19
|
+
* request to the next, so a breakpoint after it caches a prefix no later
|
|
20
|
+
* request repeats. The same test as the Anthropic driver's.
|
|
21
|
+
*/
|
|
22
|
+
function isRequestOnlyContext(msg) {
|
|
23
|
+
return (msg.role === 'user' &&
|
|
24
|
+
msg.source?.type === 'runtime-context' &&
|
|
25
|
+
msg.source.kind === 'step-context');
|
|
26
|
+
}
|
|
27
|
+
const EPHEMERAL = { type: 'ephemeral' };
|
|
28
|
+
/**
|
|
29
|
+
* Put a breakpoint on a formatted message's last content part, turning a
|
|
30
|
+
* string into a one-part array to carry it. Answers false when the message
|
|
31
|
+
* has no content to carry one — an assistant turn holding only tool calls.
|
|
32
|
+
*
|
|
33
|
+
* Always INSIDE a content part: a top-level `cache_control` on a `tool`
|
|
34
|
+
* message is not accepted by OpenRouter, while one inside its content part
|
|
35
|
+
* is (NousResearch/hermes-agent#57845, verified against
|
|
36
|
+
* `anthropic/claude-haiku-4.5`).
|
|
37
|
+
*/
|
|
38
|
+
function markLastPart(message) {
|
|
39
|
+
const content = message.content;
|
|
40
|
+
if (typeof content === 'string') {
|
|
41
|
+
if (content.length === 0)
|
|
42
|
+
return false;
|
|
43
|
+
message.content = [{ type: 'text', text: content, cache_control: EPHEMERAL }];
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
if (Array.isArray(content) && content.length > 0) {
|
|
47
|
+
const last = content[content.length - 1];
|
|
48
|
+
if (last === null || typeof last !== 'object')
|
|
49
|
+
return false;
|
|
50
|
+
// A copy: the array is the caller's own message content.
|
|
51
|
+
message.content = [...content.slice(0, -1), { ...last, cache_control: EPHEMERAL }];
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Explicit breakpoints for the runtime's cache request: one after the last
|
|
58
|
+
* static system message, one on the last non-system message before
|
|
59
|
+
* request-only context. Two of the four Anthropic allows.
|
|
60
|
+
*
|
|
61
|
+
* This replaces a top-level `cache_control: { type: 'auto' }`. OpenRouter
|
|
62
|
+
* defines the top-level field with `{ type: 'ephemeral' }` only, and applies
|
|
63
|
+
* it "to the last cacheable block" — which, with request-only context at the
|
|
64
|
+
* tail, is the context itself, so the cached prefix would never be read
|
|
65
|
+
* again.
|
|
66
|
+
*
|
|
67
|
+
* System messages are passed over when walking back: the runtime places a
|
|
68
|
+
* step's preamble (host `step.system`, step skills, the policy notice,
|
|
69
|
+
* `turn` contributions) as a system message directly before the context,
|
|
70
|
+
* and its text changes from step to step, so a marker there would end the
|
|
71
|
+
* prefix on bytes the next request does not repeat.
|
|
72
|
+
*/
|
|
73
|
+
function applyCacheBreakpoints(source, formatted) {
|
|
74
|
+
let lastStatic = -1;
|
|
75
|
+
let historyEnd = source.length;
|
|
76
|
+
for (const [i, msg] of source.entries()) {
|
|
77
|
+
if (msg.role === 'system' && msg.cacheHint === 'cache')
|
|
78
|
+
lastStatic = i;
|
|
79
|
+
if (historyEnd === source.length && isRequestOnlyContext(msg))
|
|
80
|
+
historyEnd = i;
|
|
81
|
+
}
|
|
82
|
+
const staticMessage = lastStatic >= 0 ? formatted[lastStatic] : undefined;
|
|
83
|
+
if (staticMessage)
|
|
84
|
+
markLastPart(staticMessage);
|
|
85
|
+
for (let i = historyEnd - 1; i > lastStatic; i--) {
|
|
86
|
+
if (source[i]?.role === 'system')
|
|
87
|
+
continue;
|
|
88
|
+
const message = formatted[i];
|
|
89
|
+
if (message && markLastPart(message))
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
3
93
|
const OPENROUTER_BASE_URL = process.env.OPENROUTER_BASE_URL ?? 'https://openrouter.ai/api/v1';
|
|
94
|
+
/**
|
|
95
|
+
* A rate OpenRouter published for a model, or nothing.
|
|
96
|
+
*
|
|
97
|
+
* This listing is the one place both answers arrive on the same field and
|
|
98
|
+
* only one of them used to survive. OpenRouter states each rate as a decimal
|
|
99
|
+
* string of USD per token and states `"0"` for the models it serves at no
|
|
100
|
+
* charge — so `?? '0'` did not default a missing price, it asserted one: every
|
|
101
|
+
* model whose pricing block was absent was reported as free, which is a claim
|
|
102
|
+
* about a bill. Absence now stays absent, a published `"0"` is still a real
|
|
103
|
+
* zero, and a value that does not parse is treated as absence rather than
|
|
104
|
+
* `NaN` — which would reach a consumer as a total nobody can read.
|
|
105
|
+
*
|
|
106
|
+
* See `ModelInfo.inputPrice`.
|
|
107
|
+
*/
|
|
108
|
+
function pricePerMillion(raw) {
|
|
109
|
+
if (raw === undefined)
|
|
110
|
+
return undefined;
|
|
111
|
+
const parsed = Number.parseFloat(raw);
|
|
112
|
+
return Number.isFinite(parsed) ? parsed * 1_000_000 : undefined;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* The reasoning breakdown, when the vendor reports one.
|
|
116
|
+
*
|
|
117
|
+
* `completion_tokens` already includes these — OpenRouter bills thinking as
|
|
118
|
+
* output — so this is a SUBSET, never an addition. It is reported rather than
|
|
119
|
+
* dropped because it is the only measure of how much of a turn was spent
|
|
120
|
+
* thinking, which is what a reader of `/cost` is looking at when a reasoning
|
|
121
|
+
* model looks expensive. Absent stays absent: a vendor that does not report
|
|
122
|
+
* the split has not reported zero.
|
|
123
|
+
*/
|
|
4
124
|
function parseUsage(raw) {
|
|
5
125
|
if (!raw) {
|
|
6
126
|
return {
|
|
@@ -11,12 +131,87 @@ function parseUsage(raw) {
|
|
|
11
131
|
cacheWriteTokens: 0,
|
|
12
132
|
};
|
|
13
133
|
}
|
|
134
|
+
const reasoningTokens = raw.completion_tokens_details?.reasoning_tokens;
|
|
14
135
|
return {
|
|
15
136
|
promptTokens: raw.prompt_tokens,
|
|
16
137
|
completionTokens: raw.completion_tokens,
|
|
17
138
|
totalTokens: raw.total_tokens,
|
|
18
139
|
cachedTokens: raw.prompt_tokens_details?.cached_tokens ?? raw.cache_read_input_tokens ?? 0,
|
|
19
140
|
cacheWriteTokens: raw.cache_creation_input_tokens ?? 0,
|
|
141
|
+
...(reasoningTokens !== undefined ? { reasoningTokens } : {}),
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Map one frame's reasoning onto the kernel's channel.
|
|
146
|
+
*
|
|
147
|
+
* The wire sends the same text twice: a flat `delta.reasoning` string AND a
|
|
148
|
+
* `delta.reasoning_details` array. Emitting both would double every block, so
|
|
149
|
+
* the array wins whenever it is present — it is the one that carries the index
|
|
150
|
+
* and the signature — and the flat string is the fallback for a vendor that
|
|
151
|
+
* sends only that.
|
|
152
|
+
*
|
|
153
|
+
* An empty array means the frame carried no reasoning at all; it is not a
|
|
154
|
+
* statement that the flat field should be trusted.
|
|
155
|
+
*/
|
|
156
|
+
function parseReasoning(details, flat) {
|
|
157
|
+
if (details && details.length > 0) {
|
|
158
|
+
const mapped = [];
|
|
159
|
+
for (const [position, detail] of details.entries()) {
|
|
160
|
+
const index = typeof detail.index === 'number' ? detail.index : position;
|
|
161
|
+
if (detail.type === 'reasoning.encrypted') {
|
|
162
|
+
if (detail.data)
|
|
163
|
+
mapped.push({ index, type: 'redacted_thinking', encrypted: detail.data });
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (typeof detail.text !== 'string' || detail.text.length === 0)
|
|
167
|
+
continue;
|
|
168
|
+
mapped.push({
|
|
169
|
+
index,
|
|
170
|
+
type: 'thinking',
|
|
171
|
+
text: detail.text,
|
|
172
|
+
...(detail.signature ? { signature: detail.signature } : {}),
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
return mapped;
|
|
176
|
+
}
|
|
177
|
+
return typeof flat === 'string' && flat.length > 0
|
|
178
|
+
? [{ index: 0, type: 'thinking', text: flat }]
|
|
179
|
+
: [];
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* The effort levels this wire can carry.
|
|
183
|
+
*
|
|
184
|
+
* OpenRouter's unified `reasoning.effort` publishes exactly these. The SDK's
|
|
185
|
+
* `max` and `ultra` are OpenAI-model-specific names with no equivalent here,
|
|
186
|
+
* and a driver must not send one and hope: an effort that arrives as a
|
|
187
|
+
* different depth is indistinguishable from one that was honoured.
|
|
188
|
+
*/
|
|
189
|
+
const OPENROUTER_REASONING_EFFORT = ['none', 'minimal', 'low', 'medium', 'high', 'xhigh'];
|
|
190
|
+
/**
|
|
191
|
+
* Map the SDK's thinking controls onto OpenRouter's unified `reasoning` object.
|
|
192
|
+
*
|
|
193
|
+
* `undefined` means "say nothing", which leaves the vendor's own default in
|
|
194
|
+
* place — the only correct answer for a driver that was asked for nothing.
|
|
195
|
+
*/
|
|
196
|
+
function formatReasoning(thinking, effort) {
|
|
197
|
+
if (effort !== undefined) {
|
|
198
|
+
const carried = OPENROUTER_REASONING_EFFORT.includes(effort);
|
|
199
|
+
if (!carried) {
|
|
200
|
+
throw new Error(`OpenRouterProvider cannot carry effort "${effort}". Its reasoning.effort accepts ${OPENROUTER_REASONING_EFFORT.join(', ')}. Sending a different level would return a perfectly ordinary completion that no caller could tell apart from the one that was asked for.`);
|
|
201
|
+
}
|
|
202
|
+
return { effort };
|
|
203
|
+
}
|
|
204
|
+
if (!thinking || thinking.type === undefined)
|
|
205
|
+
return undefined;
|
|
206
|
+
if (thinking.type === 'disabled')
|
|
207
|
+
return { enabled: false };
|
|
208
|
+
// 'adaptive' has no separate spelling on this wire: the model decides its
|
|
209
|
+
// own depth, which is what `enabled: true` without a budget expresses.
|
|
210
|
+
// 'enabled' fixes the depth with budgetTokens, so it carries the cap.
|
|
211
|
+
const budgetTokens = thinking.type === 'enabled' ? thinking.budgetTokens : undefined;
|
|
212
|
+
return {
|
|
213
|
+
enabled: true,
|
|
214
|
+
...(budgetTokens !== undefined ? { max_tokens: budgetTokens } : {}),
|
|
20
215
|
};
|
|
21
216
|
}
|
|
22
217
|
function formatToolChoice(tc) {
|
|
@@ -125,8 +320,14 @@ export class OpenRouterProvider {
|
|
|
125
320
|
body.repetition_penalty = params.repetitionPenalty;
|
|
126
321
|
if (params.stop)
|
|
127
322
|
body.stop = params.stop;
|
|
128
|
-
if (params.cacheControl) {
|
|
129
|
-
|
|
323
|
+
if (params.cacheControl && takesExplicitBreakpoints(params.model)) {
|
|
324
|
+
applyCacheBreakpoints(params.messages, body.messages);
|
|
325
|
+
}
|
|
326
|
+
// Refused, not dropped, when this wire cannot carry the level asked
|
|
327
|
+
// for — see `formatReasoning`.
|
|
328
|
+
const reasoning = formatReasoning(params.thinking, params.effort);
|
|
329
|
+
if (reasoning !== undefined) {
|
|
330
|
+
body.reasoning = reasoning;
|
|
130
331
|
}
|
|
131
332
|
if (params.responseFormat) {
|
|
132
333
|
body.response_format = params.responseFormat;
|
|
@@ -134,7 +335,6 @@ export class OpenRouterProvider {
|
|
|
134
335
|
return body;
|
|
135
336
|
}
|
|
136
337
|
async *chatStream(params) {
|
|
137
|
-
assertThinkingUnsupported('OpenRouterProvider', params);
|
|
138
338
|
const body = this.buildRequestBody(params, true);
|
|
139
339
|
const timeout = AbortSignal.timeout(this.config.timeout ?? 120_000);
|
|
140
340
|
// Compose the caller abort with the request timeout so a Stop cancels the
|
|
@@ -180,6 +380,9 @@ export class OpenRouterProvider {
|
|
|
180
380
|
const reader = response.body.getReader();
|
|
181
381
|
const decoder = new TextDecoder();
|
|
182
382
|
let buffer = '';
|
|
383
|
+
// Reasoning blocks this stream has opened and not yet closed, in the
|
|
384
|
+
// order the wire reported them.
|
|
385
|
+
const openReasoning = new Set();
|
|
183
386
|
try {
|
|
184
387
|
while (true) {
|
|
185
388
|
params.signal?.throwIfAborted();
|
|
@@ -207,20 +410,52 @@ export class OpenRouterProvider {
|
|
|
207
410
|
const choice = parsed.choices[0];
|
|
208
411
|
if (!choice)
|
|
209
412
|
continue;
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
413
|
+
const fragments = parseReasoning(choice.delta.reasoning_details, choice.delta.reasoning);
|
|
414
|
+
const content = choice.delta.content;
|
|
415
|
+
const toolCalls = choice.delta.tool_calls?.map((tc) => ({
|
|
416
|
+
index: tc.index,
|
|
417
|
+
id: tc.id,
|
|
418
|
+
type: tc.type,
|
|
419
|
+
function: tc.function,
|
|
420
|
+
}));
|
|
421
|
+
// A block closes when output starts, which is the only boundary
|
|
422
|
+
// this wire gives: without it a consumer's reasoning pane never
|
|
423
|
+
// receives `done` and stays open for the life of the run. An
|
|
424
|
+
// empty `content` is NOT output — every reasoning frame on this
|
|
425
|
+
// wire carries `"content":""`, so treating it as the boundary
|
|
426
|
+
// would close each block on the frame that opened it.
|
|
427
|
+
if ((typeof content === 'string' && content.length > 0) || toolCalls?.length) {
|
|
428
|
+
for (const index of openReasoning) {
|
|
429
|
+
yield { id: parsed.id, delta: { reasoning: { index, done: true } } };
|
|
430
|
+
}
|
|
431
|
+
openReasoning.clear();
|
|
432
|
+
}
|
|
433
|
+
const [firstFragment, ...restFragments] = fragments;
|
|
434
|
+
if (firstFragment)
|
|
435
|
+
openReasoning.add(firstFragment.index);
|
|
436
|
+
if (firstFragment ||
|
|
437
|
+
content !== undefined ||
|
|
438
|
+
toolCalls !== undefined ||
|
|
439
|
+
choice.finish_reason !== undefined ||
|
|
440
|
+
parsed.usage !== undefined) {
|
|
441
|
+
yield {
|
|
442
|
+
id: parsed.id,
|
|
443
|
+
delta: {
|
|
444
|
+
...(firstFragment ? { reasoning: firstFragment } : {}),
|
|
445
|
+
content,
|
|
446
|
+
toolCalls,
|
|
447
|
+
},
|
|
448
|
+
finishReason: choice.finish_reason,
|
|
449
|
+
usage: parsed.usage ? parseUsage(parsed.usage) : undefined,
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
// The delta shape carries one reasoning object per chunk, so a
|
|
453
|
+
// frame reporting several blocks rides in sequence rather than
|
|
454
|
+
// being collapsed into one — order is what replay echoes back.
|
|
455
|
+
for (const fragment of restFragments) {
|
|
456
|
+
openReasoning.add(fragment.index);
|
|
457
|
+
yield { id: parsed.id, delta: { reasoning: fragment } };
|
|
458
|
+
}
|
|
224
459
|
}
|
|
225
460
|
catch (parseErr) {
|
|
226
461
|
if (isProviderRequestError(parseErr))
|
|
@@ -332,16 +567,20 @@ export class OpenRouterProvider {
|
|
|
332
567
|
}
|
|
333
568
|
const data = (await response.json());
|
|
334
569
|
signal?.throwIfAborted();
|
|
335
|
-
return data.data.map((m) =>
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
570
|
+
return data.data.map((m) => {
|
|
571
|
+
const inputPrice = pricePerMillion(m.pricing?.prompt);
|
|
572
|
+
const outputPrice = pricePerMillion(m.pricing?.completion);
|
|
573
|
+
return {
|
|
574
|
+
id: m.id,
|
|
575
|
+
name: m.name,
|
|
576
|
+
contextWindow: m.context_length,
|
|
577
|
+
maxOutputTokens: m.top_provider?.max_completion_tokens ?? 4096,
|
|
578
|
+
...(inputPrice !== undefined ? { inputPrice } : {}),
|
|
579
|
+
...(outputPrice !== undefined ? { outputPrice } : {}),
|
|
580
|
+
supportsToolUse: true,
|
|
581
|
+
supportsStreaming: true,
|
|
582
|
+
};
|
|
583
|
+
});
|
|
345
584
|
}
|
|
346
585
|
async healthCheck() {
|
|
347
586
|
try {
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@namzu/openrouter",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"description": "OpenRouter LLM provider for @namzu/sdk. Access to 200+ models (Claude, GPT-4, Llama, Mixtral) via OpenRouter's unified API.",
|
|
5
5
|
"license": "FSL-1.1-MIT",
|
|
6
6
|
"engines": {
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"@types/node": "^22.19.17",
|
|
56
56
|
"typescript": "^5.5.0",
|
|
57
57
|
"vitest": "^4.1.11",
|
|
58
|
-
"@namzu/sdk": "^
|
|
58
|
+
"@namzu/sdk": "^44.0.0"
|
|
59
59
|
},
|
|
60
60
|
"scripts": {
|
|
61
61
|
"build": "tsc --build",
|
package/src/client.ts
CHANGED
|
@@ -9,7 +9,6 @@ import type {
|
|
|
9
9
|
} from '@namzu/sdk'
|
|
10
10
|
import {
|
|
11
11
|
ProviderRequestError,
|
|
12
|
-
assertThinkingUnsupported,
|
|
13
12
|
isCallerAbortError,
|
|
14
13
|
isProviderRequestError,
|
|
15
14
|
providerHttpError,
|
|
@@ -18,8 +17,121 @@ import {
|
|
|
18
17
|
import { attributionHeaders } from '@namzu/sdk'
|
|
19
18
|
import type { OpenRouterConfig } from './types.js'
|
|
20
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Models whose upstream caches only where the request places an explicit
|
|
22
|
+
* `cache_control` breakpoint. OpenRouter's prompt-caching page
|
|
23
|
+
* (openrouter.ai/docs/features/prompt-caching) lists Anthropic Claude, Google
|
|
24
|
+
* Gemini ("only the last breakpoint" is used) and Alibaba Qwen ("requires
|
|
25
|
+
* explicit `cache_control`"); OpenAI, DeepSeek, Grok, Moonshot, Groq and Z.AI
|
|
26
|
+
* cache automatically and are sent no marker, so their messages keep the
|
|
27
|
+
* plain string shape they have always had.
|
|
28
|
+
*/
|
|
29
|
+
function takesExplicitBreakpoints(model: string): boolean {
|
|
30
|
+
const id = model.replace(/^~/, '').toLowerCase()
|
|
31
|
+
return id.startsWith('anthropic/') || id.startsWith('google/gemini') || id.startsWith('qwen/')
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Request-only context the runtime appends after the conversation: a
|
|
36
|
+
* runtime-context user message of kind `step-context`. It changes from one
|
|
37
|
+
* request to the next, so a breakpoint after it caches a prefix no later
|
|
38
|
+
* request repeats. The same test as the Anthropic driver's.
|
|
39
|
+
*/
|
|
40
|
+
function isRequestOnlyContext(msg: ChatCompletionParams['messages'][number]): boolean {
|
|
41
|
+
return (
|
|
42
|
+
msg.role === 'user' &&
|
|
43
|
+
msg.source?.type === 'runtime-context' &&
|
|
44
|
+
msg.source.kind === 'step-context'
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const EPHEMERAL = { type: 'ephemeral' } as const
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Put a breakpoint on a formatted message's last content part, turning a
|
|
52
|
+
* string into a one-part array to carry it. Answers false when the message
|
|
53
|
+
* has no content to carry one — an assistant turn holding only tool calls.
|
|
54
|
+
*
|
|
55
|
+
* Always INSIDE a content part: a top-level `cache_control` on a `tool`
|
|
56
|
+
* message is not accepted by OpenRouter, while one inside its content part
|
|
57
|
+
* is (NousResearch/hermes-agent#57845, verified against
|
|
58
|
+
* `anthropic/claude-haiku-4.5`).
|
|
59
|
+
*/
|
|
60
|
+
function markLastPart(message: { content?: unknown }): boolean {
|
|
61
|
+
const content = message.content
|
|
62
|
+
if (typeof content === 'string') {
|
|
63
|
+
if (content.length === 0) return false
|
|
64
|
+
message.content = [{ type: 'text', text: content, cache_control: EPHEMERAL }]
|
|
65
|
+
return true
|
|
66
|
+
}
|
|
67
|
+
if (Array.isArray(content) && content.length > 0) {
|
|
68
|
+
const last = content[content.length - 1]
|
|
69
|
+
if (last === null || typeof last !== 'object') return false
|
|
70
|
+
// A copy: the array is the caller's own message content.
|
|
71
|
+
message.content = [...content.slice(0, -1), { ...last, cache_control: EPHEMERAL }]
|
|
72
|
+
return true
|
|
73
|
+
}
|
|
74
|
+
return false
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Explicit breakpoints for the runtime's cache request: one after the last
|
|
79
|
+
* static system message, one on the last non-system message before
|
|
80
|
+
* request-only context. Two of the four Anthropic allows.
|
|
81
|
+
*
|
|
82
|
+
* This replaces a top-level `cache_control: { type: 'auto' }`. OpenRouter
|
|
83
|
+
* defines the top-level field with `{ type: 'ephemeral' }` only, and applies
|
|
84
|
+
* it "to the last cacheable block" — which, with request-only context at the
|
|
85
|
+
* tail, is the context itself, so the cached prefix would never be read
|
|
86
|
+
* again.
|
|
87
|
+
*
|
|
88
|
+
* System messages are passed over when walking back: the runtime places a
|
|
89
|
+
* step's preamble (host `step.system`, step skills, the policy notice,
|
|
90
|
+
* `turn` contributions) as a system message directly before the context,
|
|
91
|
+
* and its text changes from step to step, so a marker there would end the
|
|
92
|
+
* prefix on bytes the next request does not repeat.
|
|
93
|
+
*/
|
|
94
|
+
function applyCacheBreakpoints(
|
|
95
|
+
source: ChatCompletionParams['messages'],
|
|
96
|
+
formatted: { content?: unknown }[],
|
|
97
|
+
): void {
|
|
98
|
+
let lastStatic = -1
|
|
99
|
+
let historyEnd = source.length
|
|
100
|
+
for (const [i, msg] of source.entries()) {
|
|
101
|
+
if (msg.role === 'system' && msg.cacheHint === 'cache') lastStatic = i
|
|
102
|
+
if (historyEnd === source.length && isRequestOnlyContext(msg)) historyEnd = i
|
|
103
|
+
}
|
|
104
|
+
const staticMessage = lastStatic >= 0 ? formatted[lastStatic] : undefined
|
|
105
|
+
if (staticMessage) markLastPart(staticMessage)
|
|
106
|
+
for (let i = historyEnd - 1; i > lastStatic; i--) {
|
|
107
|
+
if (source[i]?.role === 'system') continue
|
|
108
|
+
const message = formatted[i]
|
|
109
|
+
if (message && markLastPart(message)) return
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
21
113
|
const OPENROUTER_BASE_URL = process.env.OPENROUTER_BASE_URL ?? 'https://openrouter.ai/api/v1'
|
|
22
114
|
|
|
115
|
+
/**
|
|
116
|
+
* A rate OpenRouter published for a model, or nothing.
|
|
117
|
+
*
|
|
118
|
+
* This listing is the one place both answers arrive on the same field and
|
|
119
|
+
* only one of them used to survive. OpenRouter states each rate as a decimal
|
|
120
|
+
* string of USD per token and states `"0"` for the models it serves at no
|
|
121
|
+
* charge — so `?? '0'` did not default a missing price, it asserted one: every
|
|
122
|
+
* model whose pricing block was absent was reported as free, which is a claim
|
|
123
|
+
* about a bill. Absence now stays absent, a published `"0"` is still a real
|
|
124
|
+
* zero, and a value that does not parse is treated as absence rather than
|
|
125
|
+
* `NaN` — which would reach a consumer as a total nobody can read.
|
|
126
|
+
*
|
|
127
|
+
* See `ModelInfo.inputPrice`.
|
|
128
|
+
*/
|
|
129
|
+
function pricePerMillion(raw: string | undefined): number | undefined {
|
|
130
|
+
if (raw === undefined) return undefined
|
|
131
|
+
const parsed = Number.parseFloat(raw)
|
|
132
|
+
return Number.isFinite(parsed) ? parsed * 1_000_000 : undefined
|
|
133
|
+
}
|
|
134
|
+
|
|
23
135
|
interface RawUsage {
|
|
24
136
|
prompt_tokens: number
|
|
25
137
|
completion_tokens: number
|
|
@@ -27,11 +139,24 @@ interface RawUsage {
|
|
|
27
139
|
prompt_tokens_details?: {
|
|
28
140
|
cached_tokens?: number
|
|
29
141
|
}
|
|
142
|
+
completion_tokens_details?: {
|
|
143
|
+
reasoning_tokens?: number
|
|
144
|
+
}
|
|
30
145
|
cache_discount?: number
|
|
31
146
|
cache_read_input_tokens?: number
|
|
32
147
|
cache_creation_input_tokens?: number
|
|
33
148
|
}
|
|
34
149
|
|
|
150
|
+
/**
|
|
151
|
+
* The reasoning breakdown, when the vendor reports one.
|
|
152
|
+
*
|
|
153
|
+
* `completion_tokens` already includes these — OpenRouter bills thinking as
|
|
154
|
+
* output — so this is a SUBSET, never an addition. It is reported rather than
|
|
155
|
+
* dropped because it is the only measure of how much of a turn was spent
|
|
156
|
+
* thinking, which is what a reader of `/cost` is looking at when a reasoning
|
|
157
|
+
* model looks expensive. Absent stays absent: a vendor that does not report
|
|
158
|
+
* the split has not reported zero.
|
|
159
|
+
*/
|
|
35
160
|
function parseUsage(raw?: RawUsage): TokenUsage {
|
|
36
161
|
if (!raw) {
|
|
37
162
|
return {
|
|
@@ -42,12 +167,111 @@ function parseUsage(raw?: RawUsage): TokenUsage {
|
|
|
42
167
|
cacheWriteTokens: 0,
|
|
43
168
|
}
|
|
44
169
|
}
|
|
170
|
+
const reasoningTokens = raw.completion_tokens_details?.reasoning_tokens
|
|
45
171
|
return {
|
|
46
172
|
promptTokens: raw.prompt_tokens,
|
|
47
173
|
completionTokens: raw.completion_tokens,
|
|
48
174
|
totalTokens: raw.total_tokens,
|
|
49
175
|
cachedTokens: raw.prompt_tokens_details?.cached_tokens ?? raw.cache_read_input_tokens ?? 0,
|
|
50
176
|
cacheWriteTokens: raw.cache_creation_input_tokens ?? 0,
|
|
177
|
+
...(reasoningTokens !== undefined ? { reasoningTokens } : {}),
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* One entry of OpenRouter's unified `reasoning_details`.
|
|
183
|
+
*
|
|
184
|
+
* `reasoning.text` and `reasoning.summary` carry visible text; the encrypted
|
|
185
|
+
* form carries an opaque payload this driver must pass through unchanged, or
|
|
186
|
+
* a replaying caller loses the block it paid for.
|
|
187
|
+
*/
|
|
188
|
+
interface RawReasoningDetail {
|
|
189
|
+
type?: string
|
|
190
|
+
text?: string
|
|
191
|
+
signature?: string
|
|
192
|
+
data?: string
|
|
193
|
+
index?: number
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
type ReasoningDelta = NonNullable<StreamChunk['delta']['reasoning']>
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Map one frame's reasoning onto the kernel's channel.
|
|
200
|
+
*
|
|
201
|
+
* The wire sends the same text twice: a flat `delta.reasoning` string AND a
|
|
202
|
+
* `delta.reasoning_details` array. Emitting both would double every block, so
|
|
203
|
+
* the array wins whenever it is present — it is the one that carries the index
|
|
204
|
+
* and the signature — and the flat string is the fallback for a vendor that
|
|
205
|
+
* sends only that.
|
|
206
|
+
*
|
|
207
|
+
* An empty array means the frame carried no reasoning at all; it is not a
|
|
208
|
+
* statement that the flat field should be trusted.
|
|
209
|
+
*/
|
|
210
|
+
function parseReasoning(
|
|
211
|
+
details: readonly RawReasoningDetail[] | undefined,
|
|
212
|
+
flat: string | null | undefined,
|
|
213
|
+
): ReasoningDelta[] {
|
|
214
|
+
if (details && details.length > 0) {
|
|
215
|
+
const mapped: ReasoningDelta[] = []
|
|
216
|
+
for (const [position, detail] of details.entries()) {
|
|
217
|
+
const index = typeof detail.index === 'number' ? detail.index : position
|
|
218
|
+
if (detail.type === 'reasoning.encrypted') {
|
|
219
|
+
if (detail.data) mapped.push({ index, type: 'redacted_thinking', encrypted: detail.data })
|
|
220
|
+
continue
|
|
221
|
+
}
|
|
222
|
+
if (typeof detail.text !== 'string' || detail.text.length === 0) continue
|
|
223
|
+
mapped.push({
|
|
224
|
+
index,
|
|
225
|
+
type: 'thinking',
|
|
226
|
+
text: detail.text,
|
|
227
|
+
...(detail.signature ? { signature: detail.signature } : {}),
|
|
228
|
+
})
|
|
229
|
+
}
|
|
230
|
+
return mapped
|
|
231
|
+
}
|
|
232
|
+
return typeof flat === 'string' && flat.length > 0
|
|
233
|
+
? [{ index: 0, type: 'thinking', text: flat }]
|
|
234
|
+
: []
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* The effort levels this wire can carry.
|
|
239
|
+
*
|
|
240
|
+
* OpenRouter's unified `reasoning.effort` publishes exactly these. The SDK's
|
|
241
|
+
* `max` and `ultra` are OpenAI-model-specific names with no equivalent here,
|
|
242
|
+
* and a driver must not send one and hope: an effort that arrives as a
|
|
243
|
+
* different depth is indistinguishable from one that was honoured.
|
|
244
|
+
*/
|
|
245
|
+
const OPENROUTER_REASONING_EFFORT = ['none', 'minimal', 'low', 'medium', 'high', 'xhigh'] as const
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Map the SDK's thinking controls onto OpenRouter's unified `reasoning` object.
|
|
249
|
+
*
|
|
250
|
+
* `undefined` means "say nothing", which leaves the vendor's own default in
|
|
251
|
+
* place — the only correct answer for a driver that was asked for nothing.
|
|
252
|
+
*/
|
|
253
|
+
function formatReasoning(
|
|
254
|
+
thinking: ChatCompletionParams['thinking'],
|
|
255
|
+
effort: ChatCompletionParams['effort'],
|
|
256
|
+
): Record<string, unknown> | undefined {
|
|
257
|
+
if (effort !== undefined) {
|
|
258
|
+
const carried = (OPENROUTER_REASONING_EFFORT as readonly string[]).includes(effort)
|
|
259
|
+
if (!carried) {
|
|
260
|
+
throw new Error(
|
|
261
|
+
`OpenRouterProvider cannot carry effort "${effort}". Its reasoning.effort accepts ${OPENROUTER_REASONING_EFFORT.join(', ')}. Sending a different level would return a perfectly ordinary completion that no caller could tell apart from the one that was asked for.`,
|
|
262
|
+
)
|
|
263
|
+
}
|
|
264
|
+
return { effort }
|
|
265
|
+
}
|
|
266
|
+
if (!thinking || thinking.type === undefined) return undefined
|
|
267
|
+
if (thinking.type === 'disabled') return { enabled: false }
|
|
268
|
+
// 'adaptive' has no separate spelling on this wire: the model decides its
|
|
269
|
+
// own depth, which is what `enabled: true` without a budget expresses.
|
|
270
|
+
// 'enabled' fixes the depth with budgetTokens, so it carries the cap.
|
|
271
|
+
const budgetTokens = thinking.type === 'enabled' ? thinking.budgetTokens : undefined
|
|
272
|
+
return {
|
|
273
|
+
enabled: true,
|
|
274
|
+
...(budgetTokens !== undefined ? { max_tokens: budgetTokens } : {}),
|
|
51
275
|
}
|
|
52
276
|
}
|
|
53
277
|
|
|
@@ -158,8 +382,15 @@ export class OpenRouterProvider implements LLMProvider {
|
|
|
158
382
|
if (params.repetitionPenalty !== undefined) body.repetition_penalty = params.repetitionPenalty
|
|
159
383
|
if (params.stop) body.stop = params.stop
|
|
160
384
|
|
|
161
|
-
if (params.cacheControl) {
|
|
162
|
-
body.
|
|
385
|
+
if (params.cacheControl && takesExplicitBreakpoints(params.model)) {
|
|
386
|
+
applyCacheBreakpoints(params.messages, body.messages as { content?: unknown }[])
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Refused, not dropped, when this wire cannot carry the level asked
|
|
390
|
+
// for — see `formatReasoning`.
|
|
391
|
+
const reasoning = formatReasoning(params.thinking, params.effort)
|
|
392
|
+
if (reasoning !== undefined) {
|
|
393
|
+
body.reasoning = reasoning
|
|
163
394
|
}
|
|
164
395
|
|
|
165
396
|
if (params.responseFormat) {
|
|
@@ -170,7 +401,6 @@ export class OpenRouterProvider implements LLMProvider {
|
|
|
170
401
|
}
|
|
171
402
|
|
|
172
403
|
async *chatStream(params: ChatCompletionParams): AsyncIterable<StreamChunk> {
|
|
173
|
-
assertThinkingUnsupported('OpenRouterProvider', params)
|
|
174
404
|
const body = this.buildRequestBody(params, true)
|
|
175
405
|
|
|
176
406
|
const timeout = AbortSignal.timeout(this.config.timeout ?? 120_000)
|
|
@@ -219,6 +449,9 @@ export class OpenRouterProvider implements LLMProvider {
|
|
|
219
449
|
const reader = response.body.getReader()
|
|
220
450
|
const decoder = new TextDecoder()
|
|
221
451
|
let buffer = ''
|
|
452
|
+
// Reasoning blocks this stream has opened and not yet closed, in the
|
|
453
|
+
// order the wire reported them.
|
|
454
|
+
const openReasoning = new Set<number>()
|
|
222
455
|
|
|
223
456
|
try {
|
|
224
457
|
while (true) {
|
|
@@ -243,6 +476,8 @@ export class OpenRouterProvider implements LLMProvider {
|
|
|
243
476
|
choices: Array<{
|
|
244
477
|
delta: {
|
|
245
478
|
content?: string
|
|
479
|
+
reasoning?: string | null
|
|
480
|
+
reasoning_details?: RawReasoningDetail[]
|
|
246
481
|
tool_calls?: Array<{
|
|
247
482
|
index: number
|
|
248
483
|
id?: string
|
|
@@ -265,19 +500,56 @@ export class OpenRouterProvider implements LLMProvider {
|
|
|
265
500
|
const choice = parsed.choices[0]
|
|
266
501
|
if (!choice) continue
|
|
267
502
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
503
|
+
const fragments = parseReasoning(choice.delta.reasoning_details, choice.delta.reasoning)
|
|
504
|
+
const content = choice.delta.content
|
|
505
|
+
const toolCalls = choice.delta.tool_calls?.map((tc) => ({
|
|
506
|
+
index: tc.index,
|
|
507
|
+
id: tc.id,
|
|
508
|
+
type: tc.type as 'function' | undefined,
|
|
509
|
+
function: tc.function,
|
|
510
|
+
}))
|
|
511
|
+
|
|
512
|
+
// A block closes when output starts, which is the only boundary
|
|
513
|
+
// this wire gives: without it a consumer's reasoning pane never
|
|
514
|
+
// receives `done` and stays open for the life of the run. An
|
|
515
|
+
// empty `content` is NOT output — every reasoning frame on this
|
|
516
|
+
// wire carries `"content":""`, so treating it as the boundary
|
|
517
|
+
// would close each block on the frame that opened it.
|
|
518
|
+
if ((typeof content === 'string' && content.length > 0) || toolCalls?.length) {
|
|
519
|
+
for (const index of openReasoning) {
|
|
520
|
+
yield { id: parsed.id, delta: { reasoning: { index, done: true } } }
|
|
521
|
+
}
|
|
522
|
+
openReasoning.clear()
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
const [firstFragment, ...restFragments] = fragments
|
|
526
|
+
if (firstFragment) openReasoning.add(firstFragment.index)
|
|
527
|
+
|
|
528
|
+
if (
|
|
529
|
+
firstFragment ||
|
|
530
|
+
content !== undefined ||
|
|
531
|
+
toolCalls !== undefined ||
|
|
532
|
+
choice.finish_reason !== undefined ||
|
|
533
|
+
parsed.usage !== undefined
|
|
534
|
+
) {
|
|
535
|
+
yield {
|
|
536
|
+
id: parsed.id,
|
|
537
|
+
delta: {
|
|
538
|
+
...(firstFragment ? { reasoning: firstFragment } : {}),
|
|
539
|
+
content,
|
|
540
|
+
toolCalls,
|
|
541
|
+
},
|
|
542
|
+
finishReason: choice.finish_reason as StreamChunk['finishReason'],
|
|
543
|
+
usage: parsed.usage ? parseUsage(parsed.usage) : undefined,
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// The delta shape carries one reasoning object per chunk, so a
|
|
548
|
+
// frame reporting several blocks rides in sequence rather than
|
|
549
|
+
// being collapsed into one — order is what replay echoes back.
|
|
550
|
+
for (const fragment of restFragments) {
|
|
551
|
+
openReasoning.add(fragment.index)
|
|
552
|
+
yield { id: parsed.id, delta: { reasoning: fragment } }
|
|
281
553
|
}
|
|
282
554
|
} catch (parseErr) {
|
|
283
555
|
if (isProviderRequestError(parseErr)) throw parseErr
|
|
@@ -403,16 +675,20 @@ export class OpenRouterProvider implements LLMProvider {
|
|
|
403
675
|
}
|
|
404
676
|
signal?.throwIfAborted()
|
|
405
677
|
|
|
406
|
-
return data.data.map((m) =>
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
678
|
+
return data.data.map((m) => {
|
|
679
|
+
const inputPrice = pricePerMillion(m.pricing?.prompt)
|
|
680
|
+
const outputPrice = pricePerMillion(m.pricing?.completion)
|
|
681
|
+
return {
|
|
682
|
+
id: m.id,
|
|
683
|
+
name: m.name,
|
|
684
|
+
contextWindow: m.context_length,
|
|
685
|
+
maxOutputTokens: m.top_provider?.max_completion_tokens ?? 4096,
|
|
686
|
+
...(inputPrice !== undefined ? { inputPrice } : {}),
|
|
687
|
+
...(outputPrice !== undefined ? { outputPrice } : {}),
|
|
688
|
+
supportsToolUse: true,
|
|
689
|
+
supportsStreaming: true,
|
|
690
|
+
}
|
|
691
|
+
})
|
|
416
692
|
}
|
|
417
693
|
|
|
418
694
|
async healthCheck(): Promise<boolean> {
|