@openrouter/ai-sdk-provider 1.5.4 → 6.0.0-alpha.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/dist/index.mjs CHANGED
@@ -1,2919 +1,1204 @@
1
- var __defProp = Object.defineProperty;
2
- var __defProps = Object.defineProperties;
3
- var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
- var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
- var __spreadValues = (a, b) => {
9
- for (var prop in b || (b = {}))
10
- if (__hasOwnProp.call(b, prop))
11
- __defNormalProp(a, prop, b[prop]);
12
- if (__getOwnPropSymbols)
13
- for (var prop of __getOwnPropSymbols(b)) {
14
- if (__propIsEnum.call(b, prop))
15
- __defNormalProp(a, prop, b[prop]);
16
- }
17
- return a;
18
- };
19
- var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
1
+ // src/openrouter-provider.ts
2
+ import { loadApiKey, withoutTrailingSlash } from "@ai-sdk/provider-utils";
3
+ import { SDK_METADATA } from "@openrouter/sdk";
20
4
 
21
- // node_modules/.pnpm/@ai-sdk+provider@2.0.0/node_modules/@ai-sdk/provider/dist/index.mjs
22
- var marker = "vercel.ai.error";
23
- var symbol = Symbol.for(marker);
24
- var _a;
25
- var _AISDKError = class _AISDKError2 extends Error {
26
- /**
27
- * Creates an AI SDK Error.
28
- *
29
- * @param {Object} params - The parameters for creating the error.
30
- * @param {string} params.name - The name of the error.
31
- * @param {string} params.message - The error message.
32
- * @param {unknown} [params.cause] - The underlying cause of the error.
33
- */
34
- constructor({
35
- name: name14,
36
- message,
37
- cause
38
- }) {
39
- super(message);
40
- this[_a] = true;
41
- this.name = name14;
42
- this.cause = cause;
43
- }
44
- /**
45
- * Checks if the given error is an AI SDK Error.
46
- * @param {unknown} error - The error to check.
47
- * @returns {boolean} True if the error is an AI SDK Error, false otherwise.
48
- */
49
- static isInstance(error) {
50
- return _AISDKError2.hasMarker(error, marker);
51
- }
52
- static hasMarker(error, marker15) {
53
- const markerSymbol = Symbol.for(marker15);
54
- return error != null && typeof error === "object" && markerSymbol in error && typeof error[markerSymbol] === "boolean" && error[markerSymbol] === true;
55
- }
56
- };
57
- _a = symbol;
58
- var AISDKError = _AISDKError;
59
- var name = "AI_APICallError";
60
- var marker2 = `vercel.ai.error.${name}`;
61
- var symbol2 = Symbol.for(marker2);
62
- var _a2;
63
- var APICallError = class extends AISDKError {
64
- constructor({
65
- message,
66
- url,
67
- requestBodyValues,
68
- statusCode,
69
- responseHeaders,
70
- responseBody,
71
- cause,
72
- isRetryable = statusCode != null && (statusCode === 408 || // request timeout
73
- statusCode === 409 || // conflict
74
- statusCode === 429 || // too many requests
75
- statusCode >= 500),
76
- // server error
77
- data
78
- }) {
79
- super({ name, message, cause });
80
- this[_a2] = true;
81
- this.url = url;
82
- this.requestBodyValues = requestBodyValues;
83
- this.statusCode = statusCode;
84
- this.responseHeaders = responseHeaders;
85
- this.responseBody = responseBody;
86
- this.isRetryable = isRetryable;
87
- this.data = data;
88
- }
89
- static isInstance(error) {
90
- return AISDKError.hasMarker(error, marker2);
91
- }
92
- };
93
- _a2 = symbol2;
94
- var name2 = "AI_EmptyResponseBodyError";
95
- var marker3 = `vercel.ai.error.${name2}`;
96
- var symbol3 = Symbol.for(marker3);
97
- var _a3;
98
- var EmptyResponseBodyError = class extends AISDKError {
99
- // used in isInstance
100
- constructor({ message = "Empty response body" } = {}) {
101
- super({ name: name2, message });
102
- this[_a3] = true;
103
- }
104
- static isInstance(error) {
105
- return AISDKError.hasMarker(error, marker3);
106
- }
107
- };
108
- _a3 = symbol3;
109
- function getErrorMessage(error) {
110
- if (error == null) {
111
- return "unknown error";
112
- }
113
- if (typeof error === "string") {
114
- return error;
115
- }
116
- if (error instanceof Error) {
117
- return error.message;
118
- }
119
- return JSON.stringify(error);
120
- }
121
- var name3 = "AI_InvalidArgumentError";
122
- var marker4 = `vercel.ai.error.${name3}`;
123
- var symbol4 = Symbol.for(marker4);
124
- var _a4;
125
- var InvalidArgumentError = class extends AISDKError {
126
- constructor({
127
- message,
128
- cause,
129
- argument
130
- }) {
131
- super({ name: name3, message, cause });
132
- this[_a4] = true;
133
- this.argument = argument;
134
- }
135
- static isInstance(error) {
136
- return AISDKError.hasMarker(error, marker4);
137
- }
138
- };
139
- _a4 = symbol4;
140
- var name4 = "AI_InvalidPromptError";
141
- var marker5 = `vercel.ai.error.${name4}`;
142
- var symbol5 = Symbol.for(marker5);
143
- var _a5;
144
- var InvalidPromptError = class extends AISDKError {
145
- constructor({
146
- prompt,
147
- message,
148
- cause
149
- }) {
150
- super({ name: name4, message: `Invalid prompt: ${message}`, cause });
151
- this[_a5] = true;
152
- this.prompt = prompt;
153
- }
154
- static isInstance(error) {
155
- return AISDKError.hasMarker(error, marker5);
156
- }
157
- };
158
- _a5 = symbol5;
159
- var name5 = "AI_InvalidResponseDataError";
160
- var marker6 = `vercel.ai.error.${name5}`;
161
- var symbol6 = Symbol.for(marker6);
162
- var _a6;
163
- var InvalidResponseDataError = class extends AISDKError {
164
- constructor({
165
- data,
166
- message = `Invalid response data: ${JSON.stringify(data)}.`
167
- }) {
168
- super({ name: name5, message });
169
- this[_a6] = true;
170
- this.data = data;
171
- }
172
- static isInstance(error) {
173
- return AISDKError.hasMarker(error, marker6);
174
- }
175
- };
176
- _a6 = symbol6;
177
- var name6 = "AI_JSONParseError";
178
- var marker7 = `vercel.ai.error.${name6}`;
179
- var symbol7 = Symbol.for(marker7);
180
- var _a7;
181
- var JSONParseError = class extends AISDKError {
182
- constructor({ text, cause }) {
183
- super({
184
- name: name6,
185
- message: `JSON parsing failed: Text: ${text}.
186
- Error message: ${getErrorMessage(cause)}`,
187
- cause
188
- });
189
- this[_a7] = true;
190
- this.text = text;
191
- }
192
- static isInstance(error) {
193
- return AISDKError.hasMarker(error, marker7);
194
- }
195
- };
196
- _a7 = symbol7;
197
- var name7 = "AI_LoadAPIKeyError";
198
- var marker8 = `vercel.ai.error.${name7}`;
199
- var symbol8 = Symbol.for(marker8);
200
- var _a8;
201
- var LoadAPIKeyError = class extends AISDKError {
202
- // used in isInstance
203
- constructor({ message }) {
204
- super({ name: name7, message });
205
- this[_a8] = true;
206
- }
207
- static isInstance(error) {
208
- return AISDKError.hasMarker(error, marker8);
209
- }
210
- };
211
- _a8 = symbol8;
212
- var name8 = "AI_LoadSettingError";
213
- var marker9 = `vercel.ai.error.${name8}`;
214
- var symbol9 = Symbol.for(marker9);
215
- var _a9;
216
- _a9 = symbol9;
217
- var name9 = "AI_NoContentGeneratedError";
218
- var marker10 = `vercel.ai.error.${name9}`;
219
- var symbol10 = Symbol.for(marker10);
220
- var _a10;
221
- var NoContentGeneratedError = class extends AISDKError {
222
- // used in isInstance
223
- constructor({
224
- message = "No content generated."
225
- } = {}) {
226
- super({ name: name9, message });
227
- this[_a10] = true;
228
- }
229
- static isInstance(error) {
230
- return AISDKError.hasMarker(error, marker10);
231
- }
232
- };
233
- _a10 = symbol10;
234
- var name10 = "AI_NoSuchModelError";
235
- var marker11 = `vercel.ai.error.${name10}`;
236
- var symbol11 = Symbol.for(marker11);
237
- var _a11;
238
- _a11 = symbol11;
239
- var name11 = "AI_TooManyEmbeddingValuesForCallError";
240
- var marker12 = `vercel.ai.error.${name11}`;
241
- var symbol12 = Symbol.for(marker12);
242
- var _a12;
243
- _a12 = symbol12;
244
- var name12 = "AI_TypeValidationError";
245
- var marker13 = `vercel.ai.error.${name12}`;
246
- var symbol13 = Symbol.for(marker13);
247
- var _a13;
248
- var _TypeValidationError = class _TypeValidationError2 extends AISDKError {
249
- constructor({ value, cause }) {
250
- super({
251
- name: name12,
252
- message: `Type validation failed: Value: ${JSON.stringify(value)}.
253
- Error message: ${getErrorMessage(cause)}`,
254
- cause
255
- });
256
- this[_a13] = true;
257
- this.value = value;
258
- }
259
- static isInstance(error) {
260
- return AISDKError.hasMarker(error, marker13);
261
- }
262
- /**
263
- * Wraps an error into a TypeValidationError.
264
- * If the cause is already a TypeValidationError with the same value, it returns the cause.
265
- * Otherwise, it creates a new TypeValidationError.
266
- *
267
- * @param {Object} params - The parameters for wrapping the error.
268
- * @param {unknown} params.value - The value that failed validation.
269
- * @param {unknown} params.cause - The original error or cause of the validation failure.
270
- * @returns {TypeValidationError} A TypeValidationError instance.
271
- */
272
- static wrap({
273
- value,
274
- cause
275
- }) {
276
- return _TypeValidationError2.isInstance(cause) && cause.value === value ? cause : new _TypeValidationError2({ value, cause });
277
- }
278
- };
279
- _a13 = symbol13;
280
- var TypeValidationError = _TypeValidationError;
281
- var name13 = "AI_UnsupportedFunctionalityError";
282
- var marker14 = `vercel.ai.error.${name13}`;
283
- var symbol14 = Symbol.for(marker14);
284
- var _a14;
285
- var UnsupportedFunctionalityError = class extends AISDKError {
286
- constructor({
287
- functionality,
288
- message = `'${functionality}' functionality not supported.`
289
- }) {
290
- super({ name: name13, message });
291
- this[_a14] = true;
292
- this.functionality = functionality;
293
- }
294
- static isInstance(error) {
295
- return AISDKError.hasMarker(error, marker14);
296
- }
297
- };
298
- _a14 = symbol14;
5
+ // src/chat/openrouter-chat-language-model.ts
6
+ import { combineHeaders, normalizeHeaders } from "@ai-sdk/provider-utils";
7
+ import { OpenRouter } from "@openrouter/sdk";
299
8
 
300
- // node_modules/.pnpm/eventsource-parser@3.0.3/node_modules/eventsource-parser/dist/index.js
301
- var ParseError = class extends Error {
302
- constructor(message, options) {
303
- super(message), this.name = "ParseError", this.type = options.type, this.field = options.field, this.value = options.value, this.line = options.line;
304
- }
305
- };
306
- function noop(_arg) {
307
- }
308
- function createParser(callbacks) {
309
- if (typeof callbacks == "function")
310
- throw new TypeError(
311
- "`callbacks` must be an object, got a function instead. Did you mean `{onEvent: fn}`?"
312
- );
313
- const { onEvent = noop, onError = noop, onRetry = noop, onComment } = callbacks;
314
- let incompleteLine = "", isFirstChunk = true, id, data = "", eventType = "";
315
- function feed(newChunk) {
316
- const chunk = isFirstChunk ? newChunk.replace(/^\xEF\xBB\xBF/, "") : newChunk, [complete, incomplete] = splitLines(`${incompleteLine}${chunk}`);
317
- for (const line of complete)
318
- parseLine(line);
319
- incompleteLine = incomplete, isFirstChunk = false;
320
- }
321
- function parseLine(line) {
322
- if (line === "") {
323
- dispatchEvent();
324
- return;
325
- }
326
- if (line.startsWith(":")) {
327
- onComment && onComment(line.slice(line.startsWith(": ") ? 2 : 1));
328
- return;
329
- }
330
- const fieldSeparatorIndex = line.indexOf(":");
331
- if (fieldSeparatorIndex !== -1) {
332
- const field = line.slice(0, fieldSeparatorIndex), offset = line[fieldSeparatorIndex + 1] === " " ? 2 : 1, value = line.slice(fieldSeparatorIndex + offset);
333
- processField(field, value, line);
334
- return;
335
- }
336
- processField(line, "", line);
337
- }
338
- function processField(field, value, line) {
339
- switch (field) {
340
- case "event":
341
- eventType = value;
342
- break;
343
- case "data":
344
- data = `${data}${value}
345
- `;
346
- break;
347
- case "id":
348
- id = value.includes("\0") ? void 0 : value;
349
- break;
350
- case "retry":
351
- /^\d+$/.test(value) ? onRetry(parseInt(value, 10)) : onError(
352
- new ParseError(`Invalid \`retry\` value: "${value}"`, {
353
- type: "invalid-retry",
354
- value,
355
- line
356
- })
357
- );
358
- break;
359
- default:
360
- onError(
361
- new ParseError(
362
- `Unknown field "${field.length > 20 ? `${field.slice(0, 20)}\u2026` : field}"`,
363
- { type: "unknown-field", field, value, line }
364
- )
365
- );
366
- break;
367
- }
368
- }
369
- function dispatchEvent() {
370
- data.length > 0 && onEvent({
371
- id,
372
- event: eventType || void 0,
373
- // If the data buffer's last character is a U+000A LINE FEED (LF) character,
374
- // then remove the last character from the data buffer.
375
- data: data.endsWith(`
376
- `) ? data.slice(0, -1) : data
377
- }), id = void 0, data = "", eventType = "";
378
- }
379
- function reset(options = {}) {
380
- incompleteLine && options.consume && parseLine(incompleteLine), isFirstChunk = true, id = void 0, data = "", eventType = "", incompleteLine = "";
381
- }
382
- return { feed, reset };
9
+ // src/utils/build-provider-metadata.ts
10
+ function filterUndefined(obj) {
11
+ return Object.fromEntries(
12
+ Object.entries(obj).filter(([_, v]) => v !== void 0)
13
+ );
383
14
  }
384
- function splitLines(chunk) {
385
- const lines = [];
386
- let incompleteLine = "", searchIndex = 0;
387
- for (; searchIndex < chunk.length; ) {
388
- const crIndex = chunk.indexOf("\r", searchIndex), lfIndex = chunk.indexOf(`
389
- `, searchIndex);
390
- let lineEnd = -1;
391
- if (crIndex !== -1 && lfIndex !== -1 ? lineEnd = Math.min(crIndex, lfIndex) : crIndex !== -1 ? lineEnd = crIndex : lfIndex !== -1 && (lineEnd = lfIndex), lineEnd === -1) {
392
- incompleteLine = chunk.slice(searchIndex);
393
- break;
394
- } else {
395
- const line = chunk.slice(searchIndex, lineEnd);
396
- lines.push(line), searchIndex = lineEnd + 1, chunk[searchIndex - 1] === "\r" && chunk[searchIndex] === `
397
- ` && searchIndex++;
398
- }
399
- }
400
- return [lines, incompleteLine];
15
+ function buildProviderMetadata(response) {
16
+ if (!response) {
17
+ return void 0;
18
+ }
19
+ const usage = response.usage;
20
+ const usageMetadata = usage ? filterUndefined({
21
+ promptTokens: usage.promptTokens,
22
+ completionTokens: usage.completionTokens,
23
+ totalTokens: usage.totalTokens,
24
+ ...usage.promptTokensDetails && {
25
+ promptTokensDetails: filterUndefined({
26
+ cachedTokens: usage.promptTokensDetails.cachedTokens ?? void 0,
27
+ cacheWriteTokens: usage.promptTokensDetails.cacheWriteTokens ?? void 0,
28
+ audioTokens: usage.promptTokensDetails.audioTokens ?? void 0,
29
+ videoTokens: usage.promptTokensDetails.videoTokens ?? void 0
30
+ })
31
+ },
32
+ ...usage.completionTokensDetails && {
33
+ completionTokensDetails: filterUndefined({
34
+ reasoningTokens: usage.completionTokensDetails.reasoningTokens ?? void 0,
35
+ imageTokens: usage.completionTokensDetails.imageTokens ?? void 0
36
+ })
37
+ },
38
+ cost: usage.cost,
39
+ isByok: usage.isByok,
40
+ costDetails: usage.costDetails
41
+ }) : void 0;
42
+ const metadata = filterUndefined({
43
+ responseId: response.id,
44
+ provider: response.provider,
45
+ usage: usageMetadata
46
+ });
47
+ return {
48
+ openrouter: metadata
49
+ };
401
50
  }
402
51
 
403
- // node_modules/.pnpm/eventsource-parser@3.0.3/node_modules/eventsource-parser/dist/stream.js
404
- var EventSourceParserStream = class extends TransformStream {
405
- constructor({ onError, onRetry, onComment } = {}) {
406
- let parser;
407
- super({
408
- start(controller) {
409
- parser = createParser({
410
- onEvent: (event) => {
411
- controller.enqueue(event);
412
- },
413
- onError(error) {
414
- onError === "terminate" ? controller.error(error) : typeof onError == "function" && onError(error);
415
- },
416
- onRetry,
417
- onComment
418
- });
52
+ // src/utils/build-usage.ts
53
+ function buildUsage(usage) {
54
+ if (!usage) {
55
+ return {
56
+ inputTokens: {
57
+ total: 0,
58
+ noCache: void 0,
59
+ cacheRead: void 0,
60
+ cacheWrite: void 0
419
61
  },
420
- transform(chunk) {
421
- parser.feed(chunk);
422
- }
423
- });
62
+ outputTokens: {
63
+ total: 0,
64
+ text: void 0,
65
+ reasoning: void 0
66
+ },
67
+ raw: void 0
68
+ };
424
69
  }
425
- };
426
-
427
- // node_modules/.pnpm/@ai-sdk+provider-utils@3.0.1_zod@3.25.76/node_modules/@ai-sdk/provider-utils/dist/index.mjs
428
- import * as z4 from "zod/v4";
429
-
430
- // node_modules/.pnpm/zod-to-json-schema@3.24.6_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/Options.js
431
- var ignoreOverride = Symbol("Let zodToJsonSchema decide on which parser to use");
432
-
433
- // node_modules/.pnpm/zod-to-json-schema@3.24.6_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/selectParser.js
434
- import { ZodFirstPartyTypeKind as ZodFirstPartyTypeKind3 } from "zod";
435
-
436
- // node_modules/.pnpm/zod-to-json-schema@3.24.6_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/array.js
437
- import { ZodFirstPartyTypeKind } from "zod";
438
-
439
- // node_modules/.pnpm/zod-to-json-schema@3.24.6_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/record.js
440
- import { ZodFirstPartyTypeKind as ZodFirstPartyTypeKind2 } from "zod";
441
-
442
- // node_modules/.pnpm/zod-to-json-schema@3.24.6_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/string.js
443
- var ALPHA_NUMERIC = new Set("ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvxyz0123456789");
444
-
445
- // node_modules/.pnpm/@ai-sdk+provider-utils@3.0.1_zod@3.25.76/node_modules/@ai-sdk/provider-utils/dist/index.mjs
446
- function combineHeaders(...headers) {
447
- return headers.reduce(
448
- (combinedHeaders, currentHeaders) => __spreadValues(__spreadValues({}, combinedHeaders), currentHeaders != null ? currentHeaders : {}),
449
- {}
450
- );
70
+ const rawUsage = {
71
+ inputTokens: usage.inputTokens ?? null,
72
+ outputTokens: usage.outputTokens ?? null,
73
+ ...usage.inputTokensDetails && {
74
+ inputTokensDetails: {
75
+ cachedTokens: usage.inputTokensDetails.cachedTokens ?? null
76
+ }
77
+ },
78
+ ...usage.outputTokensDetails && {
79
+ outputTokensDetails: {
80
+ reasoningTokens: usage.outputTokensDetails.reasoningTokens ?? null
81
+ }
82
+ }
83
+ };
84
+ return {
85
+ inputTokens: {
86
+ total: usage.inputTokens ?? 0,
87
+ noCache: void 0,
88
+ cacheRead: usage.inputTokensDetails?.cachedTokens,
89
+ cacheWrite: void 0
90
+ },
91
+ outputTokens: {
92
+ total: usage.outputTokens ?? 0,
93
+ text: void 0,
94
+ reasoning: usage.outputTokensDetails?.reasoningTokens
95
+ },
96
+ raw: rawUsage
97
+ };
451
98
  }
452
- function extractResponseHeaders(response) {
453
- return Object.fromEntries([...response.headers]);
99
+
100
+ // src/chat/convert-to-openrouter-messages.ts
101
+ function convertToOpenRouterMessages(prompt) {
102
+ const result = [];
103
+ for (const message of prompt) {
104
+ const converted = convertMessage(message);
105
+ result.push(...converted);
106
+ }
107
+ return result;
454
108
  }
455
- var createIdGenerator = ({
456
- prefix,
457
- size = 16,
458
- alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
459
- separator = "-"
460
- } = {}) => {
461
- const generator = () => {
462
- const alphabetLength = alphabet.length;
463
- const chars = new Array(size);
464
- for (let i = 0; i < size; i++) {
465
- chars[i] = alphabet[Math.random() * alphabetLength | 0];
109
+ function convertMessage(message) {
110
+ const messageWithOptions = message;
111
+ const providerOptions = messageWithOptions.providerOptions;
112
+ const providerMetadata = messageWithOptions.providerMetadata;
113
+ switch (message.role) {
114
+ case "system":
115
+ return [{ role: "system", content: message.content }];
116
+ case "user":
117
+ return [convertUserMessage(message.content)];
118
+ case "assistant":
119
+ return convertAssistantMessage(message.content, providerMetadata, providerOptions);
120
+ case "tool":
121
+ return convertToolMessage(message.content);
122
+ default: {
123
+ const _exhaustive = message;
124
+ throw new Error(`Unknown message role: ${_exhaustive.role}`);
466
125
  }
467
- return chars.join("");
468
- };
469
- if (prefix == null) {
470
- return generator;
471
126
  }
472
- if (alphabet.includes(separator)) {
473
- throw new InvalidArgumentError({
474
- argument: "separator",
475
- message: `The separator "${separator}" must not be part of the alphabet "${alphabet}".`
476
- });
477
- }
478
- return () => `${prefix}${separator}${generator()}`;
479
- };
480
- var generateId = createIdGenerator();
481
- function isAbortError(error) {
482
- return (error instanceof Error || error instanceof DOMException) && (error.name === "AbortError" || error.name === "ResponseAborted" || // Next.js
483
- error.name === "TimeoutError");
484
127
  }
485
- var FETCH_FAILED_ERROR_MESSAGES = ["fetch failed", "failed to fetch"];
486
- function handleFetchError({
487
- error,
488
- url,
489
- requestBodyValues
490
- }) {
491
- if (isAbortError(error)) {
492
- return error;
493
- }
494
- if (error instanceof TypeError && FETCH_FAILED_ERROR_MESSAGES.includes(error.message.toLowerCase())) {
495
- const cause = error.cause;
496
- if (cause != null) {
497
- return new APICallError({
498
- message: `Cannot connect to API: ${cause.message}`,
499
- cause,
500
- url,
501
- requestBodyValues,
502
- isRetryable: true
503
- // retry when network error
504
- });
128
+ function convertUserMessage(content) {
129
+ const convertedContent = [];
130
+ for (const part of content) {
131
+ switch (part.type) {
132
+ case "text": {
133
+ convertedContent.push({ type: "input_text", text: part.text });
134
+ break;
135
+ }
136
+ case "file":
137
+ convertedContent.push(convertFilePart(part));
138
+ break;
139
+ default: {
140
+ const _exhaustive = part;
141
+ throw new Error(`Unknown user content type: ${_exhaustive.type}`);
142
+ }
505
143
  }
506
144
  }
507
- return error;
145
+ return { role: "user", content: convertedContent };
508
146
  }
509
- function removeUndefinedEntries(record) {
510
- return Object.fromEntries(
511
- Object.entries(record).filter(([_key, value]) => value != null)
512
- );
513
- }
514
- function loadApiKey({
515
- apiKey,
516
- environmentVariableName,
517
- apiKeyParameterName = "apiKey",
518
- description
519
- }) {
520
- if (typeof apiKey === "string") {
521
- return apiKey;
522
- }
523
- if (apiKey != null) {
524
- throw new LoadAPIKeyError({
525
- message: `${description} API key must be a string.`
526
- });
147
+ function convertFilePart(part) {
148
+ const url = convertDataContent(part.data, part.mediaType);
149
+ if (part.mediaType.startsWith("image/")) {
150
+ return {
151
+ type: "input_image",
152
+ imageUrl: url,
153
+ detail: "auto"
154
+ };
527
155
  }
528
- if (typeof process === "undefined") {
529
- throw new LoadAPIKeyError({
530
- message: `${description} API key is missing. Pass it using the '${apiKeyParameterName}' parameter. Environment variables is not supported in this environment.`
531
- });
156
+ return {
157
+ type: "input_file",
158
+ fileUrl: url
159
+ };
160
+ }
161
+ function convertDataContent(data, mediaType) {
162
+ if (data instanceof URL) {
163
+ return data.toString();
532
164
  }
533
- apiKey = process.env[environmentVariableName];
534
- if (apiKey == null) {
535
- throw new LoadAPIKeyError({
536
- message: `${description} API key is missing. Pass it using the '${apiKeyParameterName}' parameter or the ${environmentVariableName} environment variable.`
537
- });
165
+ if (data instanceof Uint8Array) {
166
+ const base64 = uint8ArrayToBase64(data);
167
+ return `data:${mediaType};base64,${base64}`;
538
168
  }
539
- if (typeof apiKey !== "string") {
540
- throw new LoadAPIKeyError({
541
- message: `${description} API key must be a string. The value of the ${environmentVariableName} environment variable is not a string.`
542
- });
169
+ if (data.startsWith("http://") || data.startsWith("https://") || data.startsWith("data:")) {
170
+ return data;
543
171
  }
544
- return apiKey;
172
+ return `data:${mediaType};base64,${data}`;
545
173
  }
546
- var suspectProtoRx = /"__proto__"\s*:/;
547
- var suspectConstructorRx = /"constructor"\s*:/;
548
- function _parse(text) {
549
- const obj = JSON.parse(text);
550
- if (obj === null || typeof obj !== "object") {
551
- return obj;
174
+ function uint8ArrayToBase64(bytes) {
175
+ let binary = "";
176
+ for (const byte of bytes) {
177
+ binary += String.fromCharCode(byte);
552
178
  }
553
- if (suspectProtoRx.test(text) === false && suspectConstructorRx.test(text) === false) {
554
- return obj;
555
- }
556
- return filter(obj);
179
+ return btoa(binary);
557
180
  }
558
- function filter(obj) {
559
- let next = [obj];
560
- while (next.length) {
561
- const nodes = next;
562
- next = [];
563
- for (const node of nodes) {
564
- if (Object.prototype.hasOwnProperty.call(node, "__proto__")) {
565
- throw new SyntaxError("Object contains forbidden prototype property");
566
- }
567
- if (Object.prototype.hasOwnProperty.call(node, "constructor") && Object.prototype.hasOwnProperty.call(node.constructor, "prototype")) {
568
- throw new SyntaxError("Object contains forbidden prototype property");
569
- }
570
- for (const key in node) {
571
- const value = node[key];
572
- if (value && typeof value === "object") {
573
- next.push(value);
574
- }
181
+ function extractReasoningDetails(content, providerMetadata, providerOptions) {
182
+ const messageLevel = providerOptions?.openrouter?.reasoning_details ?? providerMetadata?.openrouter?.reasoning_details;
183
+ if (messageLevel && Array.isArray(messageLevel) && messageLevel.length > 0) {
184
+ return messageLevel;
185
+ }
186
+ for (const part of content) {
187
+ if (part.type === "reasoning") {
188
+ const partWithMeta = part;
189
+ const partLevel = partWithMeta.providerOptions?.openrouter?.reasoning_details ?? partWithMeta.providerMetadata?.openrouter?.reasoning_details;
190
+ if (partLevel && Array.isArray(partLevel) && partLevel.length > 0) {
191
+ return partLevel;
575
192
  }
576
193
  }
577
194
  }
578
- return obj;
579
- }
580
- function secureJsonParse(text) {
581
- const { stackTraceLimit } = Error;
582
- Error.stackTraceLimit = 0;
583
- try {
584
- return _parse(text);
585
- } finally {
586
- Error.stackTraceLimit = stackTraceLimit;
587
- }
588
- }
589
- var validatorSymbol = Symbol.for("vercel.ai.validator");
590
- function validator(validate) {
591
- return { [validatorSymbol]: true, validate };
592
- }
593
- function isValidator(value) {
594
- return typeof value === "object" && value !== null && validatorSymbol in value && value[validatorSymbol] === true && "validate" in value;
195
+ return void 0;
595
196
  }
596
- function asValidator(value) {
597
- return isValidator(value) ? value : standardSchemaValidator(value);
598
- }
599
- function standardSchemaValidator(standardSchema) {
600
- return validator(async (value) => {
601
- const result = await standardSchema["~standard"].validate(value);
602
- return result.issues == null ? { success: true, value: result.value } : {
603
- success: false,
604
- error: new TypeValidationError({
605
- value,
606
- cause: result.issues
607
- })
608
- };
609
- });
610
- }
611
- async function validateTypes({
612
- value,
613
- schema
614
- }) {
615
- const result = await safeValidateTypes({ value, schema });
616
- if (!result.success) {
617
- throw TypeValidationError.wrap({ value, cause: result.error });
618
- }
619
- return result.value;
620
- }
621
- async function safeValidateTypes({
622
- value,
623
- schema
624
- }) {
625
- const validator2 = asValidator(schema);
626
- try {
627
- if (validator2.validate == null) {
628
- return { success: true, value, rawValue: value };
629
- }
630
- const result = await validator2.validate(value);
631
- if (result.success) {
632
- return { success: true, value: result.value, rawValue: value };
197
+ function transformReasoningToApiFormat(sdkItems) {
198
+ const apiItems = [];
199
+ for (const rawItem of sdkItems) {
200
+ if (typeof rawItem !== "object" || rawItem === null) {
201
+ continue;
633
202
  }
634
- return {
635
- success: false,
636
- error: TypeValidationError.wrap({ value, cause: result.error }),
637
- rawValue: value
203
+ const item = rawItem;
204
+ const baseProps = {
205
+ id: item.id,
206
+ format: item.format ?? null
638
207
  };
639
- } catch (error) {
640
- return {
641
- success: false,
642
- error: TypeValidationError.wrap({ value, cause: error }),
643
- rawValue: value
644
- };
645
- }
646
- }
647
- async function parseJSON({
648
- text,
649
- schema
650
- }) {
651
- try {
652
- const value = secureJsonParse(text);
653
- if (schema == null) {
654
- return value;
208
+ let index = item.index ?? 0;
209
+ if (item.type === "reasoning.text" && item.text !== void 0) {
210
+ apiItems.push({
211
+ type: "reasoning.text",
212
+ text: item.text,
213
+ signature: item.signature ?? null,
214
+ index,
215
+ ...baseProps
216
+ });
217
+ continue;
655
218
  }
656
- return validateTypes({ value, schema });
657
- } catch (error) {
658
- if (JSONParseError.isInstance(error) || TypeValidationError.isInstance(error)) {
659
- throw error;
219
+ if (item.type === "reasoning.summary" && item.summary !== void 0) {
220
+ apiItems.push({
221
+ type: "reasoning.summary",
222
+ summary: typeof item.summary === "string" ? item.summary : "",
223
+ index,
224
+ ...baseProps
225
+ });
226
+ continue;
660
227
  }
661
- throw new JSONParseError({ text, cause: error });
662
- }
663
- }
664
- async function safeParseJSON({
665
- text,
666
- schema
667
- }) {
668
- try {
669
- const value = secureJsonParse(text);
670
- if (schema == null) {
671
- return { success: true, value, rawValue: value };
228
+ if (item.type === "reasoning.encrypted" && item.data !== void 0) {
229
+ apiItems.push({
230
+ type: "reasoning.encrypted",
231
+ data: item.data,
232
+ index,
233
+ ...baseProps
234
+ });
235
+ continue;
672
236
  }
673
- return await safeValidateTypes({ value, schema });
674
- } catch (error) {
675
- return {
676
- success: false,
677
- error: JSONParseError.isInstance(error) ? error : new JSONParseError({ text, cause: error }),
678
- rawValue: void 0
679
- };
680
- }
681
- }
682
- function isParsableJson(input) {
683
- try {
684
- secureJsonParse(input);
685
- return true;
686
- } catch (e) {
687
- return false;
688
- }
689
- }
690
- function parseJsonEventStream({
691
- stream,
692
- schema
693
- }) {
694
- return stream.pipeThrough(new TextDecoderStream()).pipeThrough(new EventSourceParserStream()).pipeThrough(
695
- new TransformStream({
696
- async transform({ data }, controller) {
697
- if (data === "[DONE]") {
698
- return;
237
+ if (item.type === "reasoning" || item.content || item.summary || item.encryptedContent) {
238
+ if (item.content && Array.isArray(item.content)) {
239
+ for (const contentItem of item.content) {
240
+ if (contentItem.type === "reasoning_text" && contentItem.text) {
241
+ apiItems.push({
242
+ type: "reasoning.text",
243
+ text: contentItem.text,
244
+ signature: item.signature ?? null,
245
+ index: index++,
246
+ ...baseProps
247
+ });
248
+ }
699
249
  }
700
- controller.enqueue(await safeParseJSON({ text: data, schema }));
701
250
  }
702
- })
703
- );
704
- }
705
- var getOriginalFetch2 = () => globalThis.fetch;
706
- var postJsonToApi = async ({
707
- url,
708
- headers,
709
- body,
710
- failedResponseHandler,
711
- successfulResponseHandler,
712
- abortSignal,
713
- fetch
714
- }) => postToApi({
715
- url,
716
- headers: __spreadValues({
717
- "Content-Type": "application/json"
718
- }, headers),
719
- body: {
720
- content: JSON.stringify(body),
721
- values: body
722
- },
723
- failedResponseHandler,
724
- successfulResponseHandler,
725
- abortSignal,
726
- fetch
727
- });
728
- var postToApi = async ({
729
- url,
730
- headers = {},
731
- body,
732
- successfulResponseHandler,
733
- failedResponseHandler,
734
- abortSignal,
735
- fetch = getOriginalFetch2()
736
- }) => {
737
- try {
738
- const response = await fetch(url, {
739
- method: "POST",
740
- headers: removeUndefinedEntries(headers),
741
- body: body.content,
742
- signal: abortSignal
743
- });
744
- const responseHeaders = extractResponseHeaders(response);
745
- if (!response.ok) {
746
- let errorInformation;
747
- try {
748
- errorInformation = await failedResponseHandler({
749
- response,
750
- url,
751
- requestBodyValues: body.values
752
- });
753
- } catch (error) {
754
- if (isAbortError(error) || APICallError.isInstance(error)) {
755
- throw error;
251
+ if (item.summary && Array.isArray(item.summary)) {
252
+ for (const summaryItem of item.summary) {
253
+ if (summaryItem.type === "summary_text" && summaryItem.text) {
254
+ apiItems.push({
255
+ type: "reasoning.summary",
256
+ summary: summaryItem.text,
257
+ index: index++,
258
+ ...baseProps
259
+ });
260
+ }
756
261
  }
757
- throw new APICallError({
758
- message: "Failed to process error response",
759
- cause: error,
760
- statusCode: response.status,
761
- url,
762
- responseHeaders,
763
- requestBodyValues: body.values
764
- });
765
262
  }
766
- throw errorInformation.value;
767
- }
768
- try {
769
- return await successfulResponseHandler({
770
- response,
771
- url,
772
- requestBodyValues: body.values
773
- });
774
- } catch (error) {
775
- if (error instanceof Error) {
776
- if (isAbortError(error) || APICallError.isInstance(error)) {
777
- throw error;
778
- }
263
+ if (item.encryptedContent) {
264
+ apiItems.push({
265
+ type: "reasoning.encrypted",
266
+ data: item.encryptedContent,
267
+ index: index++,
268
+ ...baseProps
269
+ });
779
270
  }
780
- throw new APICallError({
781
- message: "Failed to process successful response",
782
- cause: error,
783
- statusCode: response.status,
784
- url,
785
- responseHeaders,
786
- requestBodyValues: body.values
787
- });
788
271
  }
789
- } catch (error) {
790
- throw handleFetchError({ error, url, requestBodyValues: body.values });
791
- }
792
- };
793
- var createJsonErrorResponseHandler = ({
794
- errorSchema,
795
- errorToMessage,
796
- isRetryable
797
- }) => async ({ response, url, requestBodyValues }) => {
798
- const responseBody = await response.text();
799
- const responseHeaders = extractResponseHeaders(response);
800
- if (responseBody.trim() === "") {
801
- return {
802
- responseHeaders,
803
- value: new APICallError({
804
- message: response.statusText,
805
- url,
806
- requestBodyValues,
807
- statusCode: response.status,
808
- responseHeaders,
809
- responseBody,
810
- isRetryable: isRetryable == null ? void 0 : isRetryable(response)
811
- })
812
- };
813
- }
814
- try {
815
- const parsedError = await parseJSON({
816
- text: responseBody,
817
- schema: errorSchema
818
- });
819
- return {
820
- responseHeaders,
821
- value: new APICallError({
822
- message: errorToMessage(parsedError),
823
- url,
824
- requestBodyValues,
825
- statusCode: response.status,
826
- responseHeaders,
827
- responseBody,
828
- data: parsedError,
829
- isRetryable: isRetryable == null ? void 0 : isRetryable(response, parsedError)
830
- })
831
- };
832
- } catch (parseError) {
833
- return {
834
- responseHeaders,
835
- value: new APICallError({
836
- message: response.statusText,
837
- url,
838
- requestBodyValues,
839
- statusCode: response.status,
840
- responseHeaders,
841
- responseBody,
842
- isRetryable: isRetryable == null ? void 0 : isRetryable(response)
843
- })
844
- };
845
- }
846
- };
847
- var createEventSourceResponseHandler = (chunkSchema) => async ({ response }) => {
848
- const responseHeaders = extractResponseHeaders(response);
849
- if (response.body == null) {
850
- throw new EmptyResponseBodyError({});
851
- }
852
- return {
853
- responseHeaders,
854
- value: parseJsonEventStream({
855
- stream: response.body,
856
- schema: chunkSchema
857
- })
858
- };
859
- };
860
- var createJsonResponseHandler = (responseSchema) => async ({ response, url, requestBodyValues }) => {
861
- const responseBody = await response.text();
862
- const parsedResult = await safeParseJSON({
863
- text: responseBody,
864
- schema: responseSchema
865
- });
866
- const responseHeaders = extractResponseHeaders(response);
867
- if (!parsedResult.success) {
868
- throw new APICallError({
869
- message: "Invalid JSON response",
870
- cause: parsedResult.error,
871
- statusCode: response.status,
872
- responseHeaders,
873
- responseBody,
874
- url,
875
- requestBodyValues
876
- });
877
- }
878
- return {
879
- responseHeaders,
880
- value: parsedResult.value,
881
- rawValue: parsedResult.rawValue
882
- };
883
- };
884
- var schemaSymbol = Symbol.for("vercel.ai.schema");
885
- var { btoa, atob } = globalThis;
886
- function convertUint8ArrayToBase64(array) {
887
- let latin1string = "";
888
- for (let i = 0; i < array.length; i++) {
889
- latin1string += String.fromCodePoint(array[i]);
890
- }
891
- return btoa(latin1string);
892
- }
893
- function withoutTrailingSlash(url) {
894
- return url == null ? void 0 : url.replace(/\/$/, "");
895
- }
896
-
897
- // src/schemas/reasoning-details.ts
898
- import { z } from "zod/v4";
899
-
900
- // src/utils/type-guards.ts
901
- function isDefinedOrNotNull(value) {
902
- return value !== null && value !== void 0;
903
- }
904
-
905
- // src/schemas/format.ts
906
- var ReasoningFormat = /* @__PURE__ */ ((ReasoningFormat2) => {
907
- ReasoningFormat2["Unknown"] = "unknown";
908
- ReasoningFormat2["OpenAIResponsesV1"] = "openai-responses-v1";
909
- ReasoningFormat2["XAIResponsesV1"] = "xai-responses-v1";
910
- ReasoningFormat2["AnthropicClaudeV1"] = "anthropic-claude-v1";
911
- ReasoningFormat2["GoogleGeminiV1"] = "google-gemini-v1";
912
- return ReasoningFormat2;
913
- })(ReasoningFormat || {});
914
-
915
- // src/schemas/reasoning-details.ts
916
- var CommonReasoningDetailSchema = z.object({
917
- id: z.string().nullish(),
918
- format: z.enum(ReasoningFormat).nullish(),
919
- index: z.number().optional()
920
- }).loose();
921
- var ReasoningDetailSummarySchema = z.object({
922
- type: z.literal("reasoning.summary" /* Summary */),
923
- summary: z.string()
924
- }).extend(CommonReasoningDetailSchema.shape);
925
- var ReasoningDetailEncryptedSchema = z.object({
926
- type: z.literal("reasoning.encrypted" /* Encrypted */),
927
- data: z.string()
928
- }).extend(CommonReasoningDetailSchema.shape);
929
- var ReasoningDetailTextSchema = z.object({
930
- type: z.literal("reasoning.text" /* Text */),
931
- text: z.string().nullish(),
932
- signature: z.string().nullish()
933
- }).extend(CommonReasoningDetailSchema.shape);
934
- var ReasoningDetailUnionSchema = z.union([
935
- ReasoningDetailSummarySchema,
936
- ReasoningDetailEncryptedSchema,
937
- ReasoningDetailTextSchema
938
- ]);
939
- var ReasoningDetailsWithUnknownSchema = z.union([
940
- ReasoningDetailUnionSchema,
941
- z.unknown().transform(() => null)
942
- ]);
943
- var ReasoningDetailArraySchema = z.array(ReasoningDetailsWithUnknownSchema).transform((d) => d.filter((d2) => !!d2));
944
- var OutputUnionToReasoningDetailsSchema = z.union([
945
- z.object({
946
- delta: z.object({
947
- reasoning_details: z.array(ReasoningDetailsWithUnknownSchema)
948
- })
949
- }).transform(
950
- (data) => data.delta.reasoning_details.filter(isDefinedOrNotNull)
951
- ),
952
- z.object({
953
- message: z.object({
954
- reasoning_details: z.array(ReasoningDetailsWithUnknownSchema)
955
- })
956
- }).transform(
957
- (data) => data.message.reasoning_details.filter(isDefinedOrNotNull)
958
- ),
959
- z.object({
960
- text: z.string(),
961
- reasoning_details: z.array(ReasoningDetailsWithUnknownSchema)
962
- }).transform((data) => data.reasoning_details.filter(isDefinedOrNotNull))
963
- ]);
964
-
965
- // src/schemas/error-response.ts
966
- import { z as z2 } from "zod/v4";
967
- var OpenRouterErrorResponseSchema = z2.object({
968
- error: z2.object({
969
- code: z2.union([z2.string(), z2.number()]).nullable().optional().default(null),
970
- message: z2.string(),
971
- type: z2.string().nullable().optional().default(null),
972
- param: z2.any().nullable().optional().default(null)
973
- }).passthrough()
974
- }).passthrough();
975
- var openrouterFailedResponseHandler = createJsonErrorResponseHandler({
976
- errorSchema: OpenRouterErrorResponseSchema,
977
- errorToMessage: (data) => data.error.message
978
- });
979
-
980
- // src/schemas/provider-metadata.ts
981
- import { z as z3 } from "zod/v4";
982
- var FileAnnotationSchema = z3.object({
983
- type: z3.literal("file"),
984
- file: z3.object({
985
- hash: z3.string(),
986
- name: z3.string(),
987
- content: z3.array(
988
- z3.object({
989
- type: z3.string(),
990
- text: z3.string().optional()
991
- }).catchall(z3.any())
992
- ).optional()
993
- }).catchall(z3.any())
994
- }).catchall(z3.any());
995
- var OpenRouterProviderMetadataSchema = z3.object({
996
- provider: z3.string(),
997
- reasoning_details: z3.array(ReasoningDetailUnionSchema).optional(),
998
- annotations: z3.array(FileAnnotationSchema).optional(),
999
- usage: z3.object({
1000
- promptTokens: z3.number(),
1001
- promptTokensDetails: z3.object({
1002
- cachedTokens: z3.number()
1003
- }).catchall(z3.any()).optional(),
1004
- completionTokens: z3.number(),
1005
- completionTokensDetails: z3.object({
1006
- reasoningTokens: z3.number()
1007
- }).catchall(z3.any()).optional(),
1008
- totalTokens: z3.number(),
1009
- cost: z3.number().optional(),
1010
- costDetails: z3.object({
1011
- upstreamInferenceCost: z3.number()
1012
- }).catchall(z3.any()).optional()
1013
- }).catchall(z3.any())
1014
- }).catchall(z3.any());
1015
- var OpenRouterProviderOptionsSchema = z3.object({
1016
- openrouter: z3.object({
1017
- reasoning_details: z3.array(ReasoningDetailUnionSchema).optional(),
1018
- annotations: z3.array(FileAnnotationSchema).optional()
1019
- }).optional()
1020
- }).optional();
1021
-
1022
- // src/utils/map-finish-reason.ts
1023
- function mapOpenRouterFinishReason(finishReason) {
1024
- switch (finishReason) {
1025
- case "stop":
1026
- return "stop";
1027
- case "length":
1028
- return "length";
1029
- case "content_filter":
1030
- return "content-filter";
1031
- case "function_call":
1032
- case "tool_calls":
1033
- return "tool-calls";
1034
- default:
1035
- return "unknown";
1036
272
  }
273
+ return apiItems;
1037
274
  }
1038
-
1039
- // src/types/openrouter-chat-completions-input.ts
1040
- var OPENROUTER_AUDIO_FORMATS = [
1041
- "wav",
1042
- "mp3",
1043
- "aiff",
1044
- "aac",
1045
- "ogg",
1046
- "flac",
1047
- "m4a",
1048
- "pcm16",
1049
- "pcm24"
1050
- ];
1051
-
1052
- // src/chat/is-url.ts
1053
- function isUrl({
1054
- url,
1055
- protocols
1056
- }) {
1057
- try {
1058
- const urlObj = new URL(url);
1059
- return protocols.has(urlObj.protocol);
1060
- } catch (_) {
1061
- return false;
275
+ function buildReasoningFromDetails(items) {
276
+ if (items.length === 0) {
277
+ return void 0;
1062
278
  }
1063
- }
1064
-
1065
- // src/chat/file-url-utils.ts
1066
- function getFileUrl({
1067
- part,
1068
- defaultMediaType
1069
- }) {
1070
- var _a15, _b;
1071
- if (part.data instanceof Uint8Array) {
1072
- const base64 = convertUint8ArrayToBase64(part.data);
1073
- return `data:${(_a15 = part.mediaType) != null ? _a15 : defaultMediaType};base64,${base64}`;
279
+ const reasoning = {};
280
+ const textItems = items.filter((i) => i.type === "reasoning.text" && i.text);
281
+ if (textItems.length > 0) {
282
+ reasoning.text = textItems.map((i) => i.text).join("");
1074
283
  }
1075
- const stringUrl = part.data.toString();
1076
- if (isUrl({
1077
- url: stringUrl,
1078
- protocols: /* @__PURE__ */ new Set(["http:", "https:"])
1079
- })) {
1080
- return stringUrl;
284
+ const summaryItems = items.filter(
285
+ (i) => i.type === "reasoning.summary" && i.summary
286
+ );
287
+ if (summaryItems.length > 0) {
288
+ reasoning.summary = summaryItems.map((i) => i.summary).join("");
1081
289
  }
1082
- return stringUrl.startsWith("data:") ? stringUrl : `data:${(_b = part.mediaType) != null ? _b : defaultMediaType};base64,${stringUrl}`;
1083
- }
1084
- function getMediaType(dataUrl, defaultMediaType) {
1085
- var _a15;
1086
- const match = dataUrl.match(/^data:([^;]+)/);
1087
- return match ? (_a15 = match[1]) != null ? _a15 : defaultMediaType : defaultMediaType;
1088
- }
1089
- function getBase64FromDataUrl(dataUrl) {
1090
- const match = dataUrl.match(/^data:[^;]*;base64,(.+)$/);
1091
- return match ? match[1] : dataUrl;
1092
- }
1093
- var MIME_TO_FORMAT = {
1094
- // MP3 variants
1095
- mpeg: "mp3",
1096
- mp3: "mp3",
1097
- // WAV variants
1098
- "x-wav": "wav",
1099
- wave: "wav",
1100
- wav: "wav",
1101
- // OGG variants
1102
- ogg: "ogg",
1103
- vorbis: "ogg",
1104
- // AAC variants
1105
- aac: "aac",
1106
- "x-aac": "aac",
1107
- // M4A variants
1108
- m4a: "m4a",
1109
- "x-m4a": "m4a",
1110
- mp4: "m4a",
1111
- // AIFF variants
1112
- aiff: "aiff",
1113
- "x-aiff": "aiff",
1114
- // FLAC
1115
- flac: "flac",
1116
- "x-flac": "flac",
1117
- // PCM variants
1118
- pcm16: "pcm16",
1119
- pcm24: "pcm24"
1120
- };
1121
- function getInputAudioData(part) {
1122
- const fileData = getFileUrl({
1123
- part,
1124
- defaultMediaType: "audio/mpeg"
1125
- });
1126
- if (isUrl({
1127
- url: fileData,
1128
- protocols: /* @__PURE__ */ new Set(["http:", "https:"])
1129
- })) {
1130
- throw new Error(
1131
- `Audio files cannot be provided as URLs.
1132
-
1133
- OpenRouter requires audio to be base64-encoded. Please:
1134
- 1. Download the audio file locally
1135
- 2. Read it as a Buffer or Uint8Array
1136
- 3. Pass it as the data parameter
1137
-
1138
- The AI SDK will automatically handle base64 encoding.
1139
-
1140
- Learn more: https://openrouter.ai/docs/features/multimodal/audio`
1141
- );
290
+ const encryptedItem = items.find(
291
+ (i) => i.type === "reasoning.encrypted" && i.data
292
+ );
293
+ if (encryptedItem?.data) {
294
+ reasoning.encrypted = encryptedItem.data;
1142
295
  }
1143
- const data = getBase64FromDataUrl(fileData);
1144
- const mediaType = part.mediaType || "audio/mpeg";
1145
- const rawFormat = mediaType.replace("audio/", "");
1146
- const format = MIME_TO_FORMAT[rawFormat];
1147
- if (format === void 0) {
1148
- const supportedList = OPENROUTER_AUDIO_FORMATS.join(", ");
1149
- throw new Error(
1150
- `Unsupported audio format: "${mediaType}"
1151
-
1152
- OpenRouter supports the following audio formats: ${supportedList}
1153
-
1154
- Learn more: https://openrouter.ai/docs/features/multimodal/audio`
1155
- );
296
+ if (!reasoning.text && !reasoning.summary && !reasoning.encrypted) {
297
+ return void 0;
1156
298
  }
1157
- return { data, format };
1158
- }
1159
-
1160
- // src/chat/convert-to-openrouter-chat-messages.ts
1161
- function getCacheControl(providerMetadata) {
1162
- var _a15, _b, _c;
1163
- const anthropic = providerMetadata == null ? void 0 : providerMetadata.anthropic;
1164
- const openrouter2 = providerMetadata == null ? void 0 : providerMetadata.openrouter;
1165
- return (_c = (_b = (_a15 = openrouter2 == null ? void 0 : openrouter2.cacheControl) != null ? _a15 : openrouter2 == null ? void 0 : openrouter2.cache_control) != null ? _b : anthropic == null ? void 0 : anthropic.cacheControl) != null ? _c : anthropic == null ? void 0 : anthropic.cache_control;
299
+ return reasoning;
1166
300
  }
1167
- function convertToOpenRouterChatMessages(prompt) {
1168
- var _a15, _b, _c, _d, _e, _f, _g, _h, _i, _j;
1169
- const messages = [];
1170
- for (const { role, content, providerOptions } of prompt) {
1171
- switch (role) {
1172
- case "system": {
1173
- messages.push({
1174
- role: "system",
1175
- content,
1176
- cache_control: getCacheControl(providerOptions)
1177
- });
301
+ function convertAssistantMessage(content, providerMetadata, providerOptions) {
302
+ const result = [];
303
+ let textContent = "";
304
+ const sdkReasoningDetails = extractReasoningDetails(
305
+ content,
306
+ providerMetadata,
307
+ providerOptions
308
+ );
309
+ const reasoningItems = sdkReasoningDetails ? transformReasoningToApiFormat(sdkReasoningDetails) : [];
310
+ const reasoning = buildReasoningFromDetails(reasoningItems);
311
+ for (const part of content) {
312
+ switch (part.type) {
313
+ case "text":
314
+ textContent += part.text;
1178
315
  break;
1179
- }
1180
- case "user": {
1181
- if (content.length === 1 && ((_a15 = content[0]) == null ? void 0 : _a15.type) === "text") {
1182
- const cacheControl = (_b = getCacheControl(providerOptions)) != null ? _b : getCacheControl(content[0].providerOptions);
1183
- const contentWithCacheControl = cacheControl ? [
1184
- {
1185
- type: "text",
1186
- text: content[0].text,
1187
- cache_control: cacheControl
1188
- }
1189
- ] : content[0].text;
1190
- messages.push({
1191
- role: "user",
1192
- content: contentWithCacheControl
1193
- });
1194
- break;
1195
- }
1196
- const messageCacheControl = getCacheControl(providerOptions);
1197
- const contentParts = content.map(
1198
- (part) => {
1199
- var _a16, _b2, _c2, _d2, _e2, _f2, _g2;
1200
- const cacheControl = (_a16 = getCacheControl(part.providerOptions)) != null ? _a16 : messageCacheControl;
1201
- switch (part.type) {
1202
- case "text":
1203
- return {
1204
- type: "text",
1205
- text: part.text,
1206
- // For text parts, only use part-specific cache control
1207
- cache_control: cacheControl
1208
- };
1209
- case "file": {
1210
- if ((_b2 = part.mediaType) == null ? void 0 : _b2.startsWith("image/")) {
1211
- const url = getFileUrl({
1212
- part,
1213
- defaultMediaType: "image/jpeg"
1214
- });
1215
- return {
1216
- type: "image_url",
1217
- image_url: {
1218
- url
1219
- },
1220
- // For image parts, use part-specific or message-level cache control
1221
- cache_control: cacheControl
1222
- };
1223
- }
1224
- if ((_c2 = part.mediaType) == null ? void 0 : _c2.startsWith("audio/")) {
1225
- return {
1226
- type: "input_audio",
1227
- input_audio: getInputAudioData(part),
1228
- cache_control: cacheControl
1229
- };
1230
- }
1231
- const fileName = String(
1232
- (_g2 = (_f2 = (_e2 = (_d2 = part.providerOptions) == null ? void 0 : _d2.openrouter) == null ? void 0 : _e2.filename) != null ? _f2 : part.filename) != null ? _g2 : ""
1233
- );
1234
- const fileData = getFileUrl({
1235
- part,
1236
- defaultMediaType: "application/pdf"
1237
- });
1238
- if (isUrl({
1239
- url: fileData,
1240
- protocols: /* @__PURE__ */ new Set(["http:", "https:"])
1241
- })) {
1242
- return {
1243
- type: "file",
1244
- file: {
1245
- filename: fileName,
1246
- file_data: fileData
1247
- }
1248
- };
1249
- }
1250
- return {
1251
- type: "file",
1252
- file: {
1253
- filename: fileName,
1254
- file_data: fileData
1255
- },
1256
- cache_control: cacheControl
1257
- };
1258
- }
1259
- default: {
1260
- return {
1261
- type: "text",
1262
- text: "",
1263
- cache_control: cacheControl
1264
- };
1265
- }
1266
- }
1267
- }
1268
- );
1269
- messages.push({
1270
- role: "user",
1271
- content: contentParts
1272
- });
316
+ case "reasoning":
317
+ textContent += part.text;
1273
318
  break;
1274
- }
1275
- case "assistant": {
1276
- let text = "";
1277
- let reasoning = "";
1278
- const toolCalls = [];
1279
- const accumulatedReasoningDetails = [];
1280
- for (const part of content) {
1281
- switch (part.type) {
1282
- case "text": {
1283
- text += part.text;
1284
- break;
1285
- }
1286
- case "tool-call": {
1287
- const partReasoningDetails = (_c = part.providerOptions) == null ? void 0 : _c.openrouter;
1288
- if ((partReasoningDetails == null ? void 0 : partReasoningDetails.reasoning_details) && Array.isArray(partReasoningDetails.reasoning_details)) {
1289
- accumulatedReasoningDetails.push(
1290
- ...partReasoningDetails.reasoning_details
1291
- );
1292
- }
1293
- toolCalls.push({
1294
- id: part.toolCallId,
1295
- type: "function",
1296
- function: {
1297
- name: part.toolName,
1298
- arguments: JSON.stringify(part.input)
1299
- }
1300
- });
1301
- break;
1302
- }
1303
- case "reasoning": {
1304
- reasoning += part.text;
1305
- const parsedPartProviderOptions = OpenRouterProviderOptionsSchema.safeParse(part.providerOptions);
1306
- if (parsedPartProviderOptions.success && ((_e = (_d = parsedPartProviderOptions.data) == null ? void 0 : _d.openrouter) == null ? void 0 : _e.reasoning_details)) {
1307
- accumulatedReasoningDetails.push(
1308
- ...parsedPartProviderOptions.data.openrouter.reasoning_details
1309
- );
1310
- }
1311
- break;
1312
- }
1313
- case "file":
1314
- break;
1315
- default: {
1316
- break;
1317
- }
1318
- }
1319
- }
1320
- const parsedProviderOptions = OpenRouterProviderOptionsSchema.safeParse(providerOptions);
1321
- const messageReasoningDetails = parsedProviderOptions.success ? (_g = (_f = parsedProviderOptions.data) == null ? void 0 : _f.openrouter) == null ? void 0 : _g.reasoning_details : void 0;
1322
- const messageAnnotations = parsedProviderOptions.success ? (_i = (_h = parsedProviderOptions.data) == null ? void 0 : _h.openrouter) == null ? void 0 : _i.annotations : void 0;
1323
- const finalReasoningDetails = messageReasoningDetails && Array.isArray(messageReasoningDetails) && messageReasoningDetails.length > 0 ? messageReasoningDetails : accumulatedReasoningDetails.length > 0 ? accumulatedReasoningDetails : void 0;
1324
- messages.push({
1325
- role: "assistant",
1326
- content: text,
1327
- tool_calls: toolCalls.length > 0 ? toolCalls : void 0,
1328
- reasoning: reasoning || void 0,
1329
- reasoning_details: finalReasoningDetails,
1330
- annotations: messageAnnotations,
1331
- cache_control: getCacheControl(providerOptions)
319
+ case "tool-call":
320
+ result.push({
321
+ type: "function_call",
322
+ callId: part.toolCallId,
323
+ name: part.toolName,
324
+ arguments: typeof part.input === "string" ? part.input : JSON.stringify(part.input)
1332
325
  });
1333
326
  break;
1334
- }
1335
- case "tool": {
1336
- for (const toolResponse of content) {
1337
- const content2 = getToolResultContent(toolResponse);
1338
- messages.push({
1339
- role: "tool",
1340
- tool_call_id: toolResponse.toolCallId,
1341
- content: content2,
1342
- cache_control: (_j = getCacheControl(providerOptions)) != null ? _j : getCacheControl(toolResponse.providerOptions)
1343
- });
1344
- }
327
+ case "file":
1345
328
  break;
1346
- }
1347
- default: {
329
+ case "tool-result":
330
+ result.push(convertToolResult(part));
1348
331
  break;
332
+ default: {
333
+ const _exhaustive = part;
334
+ throw new Error(
335
+ `Unknown assistant content type: ${_exhaustive.type}`
336
+ );
1349
337
  }
1350
338
  }
1351
339
  }
1352
- return messages;
340
+ if (textContent) {
341
+ const assistantMessage = {
342
+ role: "assistant",
343
+ content: textContent
344
+ };
345
+ if (reasoning) {
346
+ assistantMessage.reasoning = reasoning;
347
+ }
348
+ result.unshift(assistantMessage);
349
+ } else if (reasoning) {
350
+ const assistantMessage = {
351
+ role: "assistant",
352
+ content: ""
353
+ };
354
+ assistantMessage.reasoning = reasoning;
355
+ result.unshift(assistantMessage);
356
+ }
357
+ return result;
1353
358
  }
1354
- function getToolResultContent(input) {
1355
- return input.output.type === "text" ? input.output.value : JSON.stringify(input.output.value);
359
+ function convertToolMessage(content) {
360
+ const result = [];
361
+ for (const part of content) {
362
+ if (part.type === "tool-result") {
363
+ result.push(convertToolResult(part));
364
+ }
365
+ }
366
+ return result;
1356
367
  }
1357
-
1358
- // src/chat/get-tool-choice.ts
1359
- import { z as z5 } from "zod/v4";
1360
- var ChatCompletionToolChoiceSchema = z5.union([
1361
- z5.literal("auto"),
1362
- z5.literal("none"),
1363
- z5.literal("required"),
1364
- z5.object({
1365
- type: z5.literal("function"),
1366
- function: z5.object({
1367
- name: z5.string()
1368
- })
1369
- })
1370
- ]);
1371
- function getChatCompletionToolChoice(toolChoice) {
1372
- switch (toolChoice.type) {
1373
- case "auto":
1374
- case "none":
1375
- case "required":
1376
- return toolChoice.type;
1377
- case "tool": {
368
+ function convertToolResult(part) {
369
+ const output = convertToolResultOutput(part.output);
370
+ return {
371
+ type: "function_call_output",
372
+ callId: part.toolCallId,
373
+ output: output.value,
374
+ status: output.isError ? "incomplete" : "completed"
375
+ };
376
+ }
377
+ function convertToolResultOutput(output) {
378
+ switch (output.type) {
379
+ case "text":
380
+ return { value: output.value, isError: false };
381
+ case "json":
382
+ return { value: JSON.stringify(output.value), isError: false };
383
+ case "execution-denied":
1378
384
  return {
1379
- type: "function",
1380
- function: { name: toolChoice.toolName }
385
+ value: `Execution denied: ${output.reason ?? "No reason provided"}`,
386
+ isError: true
1381
387
  };
388
+ case "error-text":
389
+ return { value: output.value, isError: true };
390
+ case "error-json":
391
+ return { value: JSON.stringify(output.value), isError: true };
392
+ case "content": {
393
+ const textParts = output.value.filter((item) => item.type === "text").map((item) => item.text);
394
+ return { value: textParts.join("\n"), isError: false };
1382
395
  }
1383
396
  default: {
1384
- toolChoice;
1385
- throw new InvalidArgumentError({
1386
- argument: "toolChoice",
1387
- message: `Invalid tool choice type: ${JSON.stringify(toolChoice)}`
1388
- });
397
+ const _exhaustive = output;
398
+ throw new Error(`Unknown tool result output type: ${_exhaustive.type}`);
1389
399
  }
1390
400
  }
1391
401
  }
1392
402
 
1393
- // src/chat/schemas.ts
1394
- import { z as z7 } from "zod/v4";
1395
-
1396
- // src/schemas/image.ts
1397
- import { z as z6 } from "zod/v4";
1398
- var ImageResponseSchema = z6.object({
1399
- type: z6.literal("image_url"),
1400
- image_url: z6.object({
1401
- url: z6.string()
1402
- }).passthrough()
1403
- }).passthrough();
1404
- var ImageResponseWithUnknownSchema = z6.union([
1405
- ImageResponseSchema,
1406
- z6.unknown().transform(() => null)
1407
- ]);
1408
- var ImageResponseArraySchema = z6.array(ImageResponseWithUnknownSchema).transform((d) => d.filter((d2) => !!d2));
403
+ // src/chat/extract-reasoning-details.ts
404
+ function extractReasoningDetails2(response) {
405
+ const extractedDetails = [];
406
+ for (const outputItem of response.output) {
407
+ if ("type" in outputItem && outputItem.type === "reasoning") {
408
+ const reasoningItem = outputItem;
409
+ extractedDetails.push({
410
+ type: "reasoning",
411
+ id: reasoningItem.id,
412
+ content: reasoningItem.content,
413
+ summary: reasoningItem.summary,
414
+ encryptedContent: reasoningItem.encryptedContent,
415
+ signature: reasoningItem.signature,
416
+ format: reasoningItem.format
417
+ });
418
+ }
419
+ }
420
+ return extractedDetails.length > 0 ? extractedDetails : void 0;
421
+ }
422
+ function hasEncryptedReasoning(reasoningDetails) {
423
+ if (!reasoningDetails) {
424
+ return false;
425
+ }
426
+ return reasoningDetails.some((d) => {
427
+ if (typeof d === "object" && d !== null) {
428
+ const obj = d;
429
+ return obj.encryptedContent != null || obj.type === "reasoning.encrypted";
430
+ }
431
+ return false;
432
+ });
433
+ }
434
+ function buildReasoningProviderMetadata(reasoningDetails) {
435
+ if (!reasoningDetails || reasoningDetails.length === 0) {
436
+ return void 0;
437
+ }
438
+ return {
439
+ openrouter: {
440
+ reasoning_details: reasoningDetails
441
+ }
442
+ };
443
+ }
1409
444
 
1410
- // src/chat/schemas.ts
1411
- var OpenRouterChatCompletionBaseResponseSchema = z7.object({
1412
- id: z7.string().optional(),
1413
- model: z7.string().optional(),
1414
- provider: z7.string().optional(),
1415
- usage: z7.object({
1416
- prompt_tokens: z7.number(),
1417
- prompt_tokens_details: z7.object({
1418
- cached_tokens: z7.number()
1419
- }).passthrough().nullish(),
1420
- completion_tokens: z7.number(),
1421
- completion_tokens_details: z7.object({
1422
- reasoning_tokens: z7.number()
1423
- }).passthrough().nullish(),
1424
- total_tokens: z7.number(),
1425
- cost: z7.number().optional(),
1426
- cost_details: z7.object({
1427
- upstream_inference_cost: z7.number().nullish()
1428
- }).passthrough().nullish()
1429
- }).passthrough().nullish()
1430
- }).passthrough();
1431
- var OpenRouterNonStreamChatCompletionResponseSchema = z7.union([
1432
- // Success response with choices
1433
- OpenRouterChatCompletionBaseResponseSchema.extend({
1434
- choices: z7.array(
1435
- z7.object({
1436
- message: z7.object({
1437
- role: z7.literal("assistant"),
1438
- content: z7.string().nullable().optional(),
1439
- reasoning: z7.string().nullable().optional(),
1440
- reasoning_details: ReasoningDetailArraySchema.nullish(),
1441
- images: ImageResponseArraySchema.nullish(),
1442
- tool_calls: z7.array(
1443
- z7.object({
1444
- id: z7.string().optional().nullable(),
1445
- type: z7.literal("function"),
1446
- function: z7.object({
1447
- name: z7.string(),
1448
- arguments: z7.string()
1449
- }).passthrough()
1450
- }).passthrough()
1451
- ).optional(),
1452
- annotations: z7.array(
1453
- z7.union([
1454
- // URL citation from web search
1455
- z7.object({
1456
- type: z7.literal("url_citation"),
1457
- url_citation: z7.object({
1458
- end_index: z7.number(),
1459
- start_index: z7.number(),
1460
- title: z7.string(),
1461
- url: z7.string(),
1462
- content: z7.string().optional()
1463
- }).passthrough()
1464
- }).passthrough(),
1465
- // File annotation from FileParserPlugin (old format)
1466
- z7.object({
1467
- type: z7.literal("file_annotation"),
1468
- file_annotation: z7.object({
1469
- file_id: z7.string(),
1470
- quote: z7.string().optional()
1471
- }).passthrough()
1472
- }).passthrough(),
1473
- // File annotation from FileParserPlugin (new format)
1474
- z7.object({
1475
- type: z7.literal("file"),
1476
- file: z7.object({
1477
- hash: z7.string(),
1478
- name: z7.string(),
1479
- content: z7.array(
1480
- z7.object({
1481
- type: z7.string(),
1482
- text: z7.string().optional()
1483
- }).passthrough()
1484
- ).optional()
1485
- }).passthrough()
1486
- }).passthrough()
1487
- ])
1488
- ).nullish()
1489
- }).passthrough(),
1490
- index: z7.number().nullish(),
1491
- logprobs: z7.object({
1492
- content: z7.array(
1493
- z7.object({
1494
- token: z7.string(),
1495
- logprob: z7.number(),
1496
- top_logprobs: z7.array(
1497
- z7.object({
1498
- token: z7.string(),
1499
- logprob: z7.number()
1500
- }).passthrough()
1501
- )
1502
- }).passthrough()
1503
- ).nullable()
1504
- }).passthrough().nullable().optional(),
1505
- finish_reason: z7.string().optional().nullable()
1506
- }).passthrough()
1507
- )
1508
- }),
1509
- // Error response (HTTP 200 with error payload)
1510
- OpenRouterErrorResponseSchema.extend({
1511
- user_id: z7.string().optional()
1512
- })
1513
- ]);
1514
- var OpenRouterStreamChatCompletionChunkSchema = z7.union([
1515
- OpenRouterChatCompletionBaseResponseSchema.extend({
1516
- choices: z7.array(
1517
- z7.object({
1518
- delta: z7.object({
1519
- role: z7.enum(["assistant"]).optional(),
1520
- content: z7.string().nullish(),
1521
- reasoning: z7.string().nullish().optional(),
1522
- reasoning_details: ReasoningDetailArraySchema.nullish(),
1523
- images: ImageResponseArraySchema.nullish(),
1524
- tool_calls: z7.array(
1525
- z7.object({
1526
- index: z7.number().nullish(),
1527
- id: z7.string().nullish(),
1528
- type: z7.literal("function").optional(),
1529
- function: z7.object({
1530
- name: z7.string().nullish(),
1531
- arguments: z7.string().nullish()
1532
- }).passthrough()
1533
- }).passthrough()
1534
- ).nullish(),
1535
- annotations: z7.array(
1536
- z7.union([
1537
- // URL citation from web search
1538
- z7.object({
1539
- type: z7.literal("url_citation"),
1540
- url_citation: z7.object({
1541
- end_index: z7.number(),
1542
- start_index: z7.number(),
1543
- title: z7.string(),
1544
- url: z7.string(),
1545
- content: z7.string().optional()
1546
- }).passthrough()
1547
- }).passthrough(),
1548
- // File annotation from FileParserPlugin (old format)
1549
- z7.object({
1550
- type: z7.literal("file_annotation"),
1551
- file_annotation: z7.object({
1552
- file_id: z7.string(),
1553
- quote: z7.string().optional()
1554
- }).passthrough()
1555
- }).passthrough(),
1556
- // File annotation from FileParserPlugin (new format)
1557
- z7.object({
1558
- type: z7.literal("file"),
1559
- file: z7.object({
1560
- hash: z7.string(),
1561
- name: z7.string(),
1562
- content: z7.array(
1563
- z7.object({
1564
- type: z7.string(),
1565
- text: z7.string().optional()
1566
- }).passthrough()
1567
- ).optional()
1568
- }).passthrough()
1569
- }).passthrough()
1570
- ])
1571
- ).nullish()
1572
- }).passthrough().nullish(),
1573
- logprobs: z7.object({
1574
- content: z7.array(
1575
- z7.object({
1576
- token: z7.string(),
1577
- logprob: z7.number(),
1578
- top_logprobs: z7.array(
1579
- z7.object({
1580
- token: z7.string(),
1581
- logprob: z7.number()
1582
- }).passthrough()
1583
- )
1584
- }).passthrough()
1585
- ).nullable()
1586
- }).passthrough().nullish(),
1587
- finish_reason: z7.string().nullable().optional(),
1588
- index: z7.number().nullish()
1589
- }).passthrough()
1590
- )
1591
- }),
1592
- OpenRouterErrorResponseSchema
1593
- ]);
445
+ // src/chat/map-openrouter-finish-reason.ts
446
+ function mapOpenRouterFinishReason(finishReason) {
447
+ if (finishReason == null) {
448
+ return { unified: "other", raw: void 0 };
449
+ }
450
+ switch (finishReason) {
451
+ case "end_turn":
452
+ case "stop":
453
+ case "stop_sequence":
454
+ return { unified: "stop", raw: finishReason };
455
+ case "max_tokens":
456
+ case "length":
457
+ return { unified: "length", raw: finishReason };
458
+ case "tool_use":
459
+ case "tool_calls":
460
+ return { unified: "tool-calls", raw: finishReason };
461
+ case "content_filter":
462
+ return { unified: "content-filter", raw: finishReason };
463
+ case "error":
464
+ return { unified: "error", raw: finishReason };
465
+ default:
466
+ return { unified: "other", raw: finishReason };
467
+ }
468
+ }
1594
469
 
1595
- // src/chat/index.ts
470
+ // src/chat/openrouter-chat-language-model.ts
1596
471
  var OpenRouterChatLanguageModel = class {
1597
- constructor(modelId, settings, config) {
1598
- this.specificationVersion = "v2";
1599
- this.provider = "openrouter";
1600
- this.defaultObjectGenerationMode = "tool";
1601
- this.supportsImageUrls = true;
1602
- this.supportedUrls = {
1603
- "image/*": [
1604
- /^data:image\/[a-zA-Z]+;base64,/,
1605
- /^https?:\/\/.+\.(jpg|jpeg|png|gif|webp)$/i
1606
- ],
1607
- // 'text/*': [/^data:text\//, /^https?:\/\/.+$/],
1608
- "application/*": [/^data:application\//, /^https?:\/\/.+$/]
1609
- };
472
+ specificationVersion = "v3";
473
+ provider = "openrouter";
474
+ modelId;
475
+ settings;
476
+ /**
477
+ * Supported URL patterns by media type.
478
+ * OpenRouter Chat API only supports image URLs natively.
479
+ * PDF URLs are not supported - use PDF data URIs or the Responses API instead.
480
+ */
481
+ supportedUrls = {
482
+ "image/*": [/^https?:\/\/.*$/]
483
+ };
484
+ constructor(modelId, settings) {
1610
485
  this.modelId = modelId;
1611
486
  this.settings = settings;
1612
- this.config = config;
1613
- }
1614
- getArgs({
1615
- prompt,
1616
- maxOutputTokens,
1617
- temperature,
1618
- topP,
1619
- frequencyPenalty,
1620
- presencePenalty,
1621
- seed,
1622
- stopSequences,
1623
- responseFormat,
1624
- topK,
1625
- tools,
1626
- toolChoice
1627
- }) {
1628
- var _a15;
1629
- const baseArgs = __spreadValues(__spreadValues({
1630
- // model id:
1631
- model: this.modelId,
1632
- models: this.settings.models,
1633
- // model specific settings:
1634
- logit_bias: this.settings.logitBias,
1635
- logprobs: this.settings.logprobs === true || typeof this.settings.logprobs === "number" ? true : void 0,
1636
- top_logprobs: typeof this.settings.logprobs === "number" ? this.settings.logprobs : typeof this.settings.logprobs === "boolean" ? this.settings.logprobs ? 0 : void 0 : void 0,
1637
- user: this.settings.user,
1638
- parallel_tool_calls: this.settings.parallelToolCalls,
1639
- // standardized settings:
1640
- max_tokens: maxOutputTokens,
1641
- temperature,
1642
- top_p: topP,
1643
- frequency_penalty: frequencyPenalty,
1644
- presence_penalty: presencePenalty,
1645
- seed,
1646
- stop: stopSequences,
1647
- response_format: (responseFormat == null ? void 0 : responseFormat.type) === "json" ? responseFormat.schema != null ? {
1648
- type: "json_schema",
1649
- json_schema: __spreadValues({
1650
- schema: responseFormat.schema,
1651
- strict: true,
1652
- name: (_a15 = responseFormat.name) != null ? _a15 : "response"
1653
- }, responseFormat.description && {
1654
- description: responseFormat.description
1655
- })
1656
- } : { type: "json_object" } : void 0,
1657
- top_k: topK,
1658
- // messages:
1659
- messages: convertToOpenRouterChatMessages(prompt),
1660
- // OpenRouter specific settings:
1661
- include_reasoning: this.settings.includeReasoning,
1662
- reasoning: this.settings.reasoning,
1663
- usage: this.settings.usage,
1664
- // Web search settings:
1665
- plugins: this.settings.plugins,
1666
- web_search_options: this.settings.web_search_options,
1667
- // Provider routing settings:
1668
- provider: this.settings.provider,
1669
- // Debug settings:
1670
- debug: this.settings.debug
1671
- }, this.config.extraBody), this.settings.extraBody);
1672
- if (tools && tools.length > 0) {
1673
- const mappedTools = tools.filter(
1674
- (tool) => tool.type === "function"
1675
- ).map((tool) => ({
1676
- type: "function",
1677
- function: {
1678
- name: tool.name,
1679
- description: tool.description,
1680
- parameters: tool.inputSchema
1681
- }
1682
- }));
1683
- return __spreadProps(__spreadValues({}, baseArgs), {
1684
- tools: mappedTools,
1685
- tool_choice: toolChoice ? getChatCompletionToolChoice(toolChoice) : void 0
1686
- });
1687
- }
1688
- return baseArgs;
1689
487
  }
1690
488
  async doGenerate(options) {
1691
- var _a15, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w;
1692
- const providerOptions = options.providerOptions || {};
1693
- const openrouterOptions = providerOptions.openrouter || {};
1694
- const args = __spreadValues(__spreadValues({}, this.getArgs(options)), openrouterOptions);
1695
- const { value: responseValue, responseHeaders } = await postJsonToApi({
1696
- url: this.config.url({
1697
- path: "/chat/completions",
1698
- modelId: this.modelId
1699
- }),
1700
- headers: combineHeaders(this.config.headers(), options.headers),
1701
- body: args,
1702
- failedResponseHandler: openrouterFailedResponseHandler,
1703
- successfulResponseHandler: createJsonResponseHandler(
1704
- OpenRouterNonStreamChatCompletionResponseSchema
1705
- ),
1706
- abortSignal: options.abortSignal,
1707
- fetch: this.config.fetch
489
+ const warnings = [];
490
+ const client = new OpenRouter({
491
+ apiKey: this.settings.apiKey,
492
+ serverURL: this.settings.baseURL,
493
+ userAgent: this.settings.userAgent
1708
494
  });
1709
- if ("error" in responseValue) {
1710
- const errorData = responseValue.error;
1711
- throw new APICallError({
1712
- message: errorData.message,
1713
- url: this.config.url({
1714
- path: "/chat/completions",
1715
- modelId: this.modelId
1716
- }),
1717
- requestBodyValues: args,
1718
- statusCode: 200,
1719
- responseHeaders,
1720
- data: errorData
1721
- });
1722
- }
1723
- const response = responseValue;
1724
- const choice = response.choices[0];
1725
- if (!choice) {
1726
- throw new NoContentGeneratedError({
1727
- message: "No choice in response"
1728
- });
1729
- }
1730
- const usageInfo = response.usage ? {
1731
- inputTokens: (_a15 = response.usage.prompt_tokens) != null ? _a15 : 0,
1732
- outputTokens: (_b = response.usage.completion_tokens) != null ? _b : 0,
1733
- totalTokens: ((_c = response.usage.prompt_tokens) != null ? _c : 0) + ((_d = response.usage.completion_tokens) != null ? _d : 0),
1734
- reasoningTokens: (_f = (_e = response.usage.completion_tokens_details) == null ? void 0 : _e.reasoning_tokens) != null ? _f : 0,
1735
- cachedInputTokens: (_h = (_g = response.usage.prompt_tokens_details) == null ? void 0 : _g.cached_tokens) != null ? _h : 0
1736
- } : {
1737
- inputTokens: 0,
1738
- outputTokens: 0,
1739
- totalTokens: 0,
1740
- reasoningTokens: 0,
1741
- cachedInputTokens: 0
1742
- };
1743
- const reasoningDetails = (_i = choice.message.reasoning_details) != null ? _i : [];
1744
- const reasoning = reasoningDetails.length > 0 ? reasoningDetails.map((detail) => {
1745
- switch (detail.type) {
1746
- case "reasoning.text" /* Text */: {
1747
- if (detail.text) {
1748
- return {
1749
- type: "reasoning",
1750
- text: detail.text,
1751
- providerMetadata: {
1752
- openrouter: {
1753
- reasoning_details: [detail]
1754
- }
1755
- }
1756
- };
1757
- }
1758
- break;
1759
- }
1760
- case "reasoning.summary" /* Summary */: {
1761
- if (detail.summary) {
1762
- return {
1763
- type: "reasoning",
1764
- text: detail.summary,
1765
- providerMetadata: {
1766
- openrouter: {
1767
- reasoning_details: [detail]
1768
- }
1769
- }
1770
- };
1771
- }
1772
- break;
495
+ const openRouterInput = convertToOpenRouterMessages(options.prompt);
496
+ const tools = convertToolsToResponsesFormat(options.tools, warnings);
497
+ const toolChoice = convertToolChoiceToResponsesFormat(options.toolChoice);
498
+ const text = convertResponseFormatToText(options.responseFormat);
499
+ const requestParams = {
500
+ model: this.modelId,
501
+ input: openRouterInput,
502
+ stream: false,
503
+ ...options.maxOutputTokens !== void 0 && {
504
+ maxOutputTokens: options.maxOutputTokens
505
+ },
506
+ ...options.temperature !== void 0 && {
507
+ temperature: options.temperature
508
+ },
509
+ ...options.topP !== void 0 && { topP: options.topP },
510
+ ...tools.length > 0 && { tools },
511
+ ...toolChoice !== void 0 && { toolChoice },
512
+ ...text !== void 0 && { text }
513
+ };
514
+ const combinedHeaders = normalizeHeaders(
515
+ combineHeaders(this.settings.headers, options.headers)
516
+ );
517
+ const response = await client.beta.responses.send(requestParams, {
518
+ fetchOptions: {
519
+ signal: options.abortSignal,
520
+ headers: combinedHeaders
521
+ }
522
+ });
523
+ const content = [];
524
+ const reasoningDetails = extractReasoningDetails2(response);
525
+ const reasoningMetadata = buildReasoningProviderMetadata(reasoningDetails);
526
+ for (const outputItem of response.output) {
527
+ if (outputItem.type === "reasoning") {
528
+ const reasoningItem = outputItem;
529
+ const reasoningText = reasoningItem.content?.filter((c) => c.type === "reasoning_text").map((c) => c.text).join("") || reasoningItem.summary?.filter((c) => c.type === "summary_text").map((c) => c.text).join("") || "";
530
+ if (reasoningText) {
531
+ content.push({
532
+ type: "reasoning",
533
+ text: reasoningText,
534
+ ...reasoningMetadata && { providerMetadata: reasoningMetadata }
535
+ });
1773
536
  }
1774
- case "reasoning.encrypted" /* Encrypted */: {
1775
- if (detail.data) {
1776
- return {
1777
- type: "reasoning",
1778
- text: "[REDACTED]",
1779
- providerMetadata: {
1780
- openrouter: {
1781
- reasoning_details: [detail]
1782
- }
1783
- }
1784
- };
537
+ } else if (outputItem.type === "message") {
538
+ const messageItem = outputItem;
539
+ for (const contentItem of messageItem.content) {
540
+ if (contentItem.type === "output_text" && contentItem.text) {
541
+ content.push({
542
+ type: "text",
543
+ text: contentItem.text
544
+ });
1785
545
  }
1786
- break;
1787
546
  }
1788
- default: {
1789
- detail;
1790
- }
1791
- }
1792
- return null;
1793
- }).filter((p) => p !== null) : choice.message.reasoning ? [
1794
- {
1795
- type: "reasoning",
1796
- text: choice.message.reasoning
1797
- }
1798
- ] : [];
1799
- const content = [];
1800
- content.push(...reasoning);
1801
- if (choice.message.content) {
1802
- content.push({
1803
- type: "text",
1804
- text: choice.message.content
1805
- });
1806
- }
1807
- if (choice.message.tool_calls) {
1808
- for (const toolCall of choice.message.tool_calls) {
547
+ } else if (outputItem.type === "function_call") {
548
+ const functionCallItem = outputItem;
1809
549
  content.push({
1810
550
  type: "tool-call",
1811
- toolCallId: (_j = toolCall.id) != null ? _j : generateId(),
1812
- toolName: toolCall.function.name,
1813
- input: toolCall.function.arguments,
1814
- providerMetadata: {
1815
- openrouter: {
1816
- reasoning_details: reasoningDetails
1817
- }
1818
- }
1819
- });
1820
- }
1821
- }
1822
- if (choice.message.images) {
1823
- for (const image of choice.message.images) {
1824
- content.push({
1825
- type: "file",
1826
- mediaType: getMediaType(image.image_url.url, "image/jpeg"),
1827
- data: getBase64FromDataUrl(image.image_url.url)
551
+ toolCallId: functionCallItem.callId,
552
+ toolName: functionCallItem.name,
553
+ // Default to empty object when arguments is undefined/empty
554
+ // (some providers omit arguments for tools with no parameters)
555
+ input: functionCallItem.arguments || "{}",
556
+ ...reasoningMetadata && { providerMetadata: reasoningMetadata }
1828
557
  });
1829
558
  }
1830
559
  }
1831
- if (choice.message.annotations) {
1832
- for (const annotation of choice.message.annotations) {
1833
- if (annotation.type === "url_citation") {
1834
- content.push({
1835
- type: "source",
1836
- sourceType: "url",
1837
- id: annotation.url_citation.url,
1838
- url: annotation.url_citation.url,
1839
- title: annotation.url_citation.title,
1840
- providerMetadata: {
1841
- openrouter: {
1842
- content: annotation.url_citation.content || ""
1843
- }
1844
- }
1845
- });
1846
- }
1847
- }
560
+ if (response.outputText && !content.some((c) => c.type === "text")) {
561
+ content.push({
562
+ type: "text",
563
+ text: response.outputText
564
+ });
1848
565
  }
1849
- const fileAnnotations = (_k = choice.message.annotations) == null ? void 0 : _k.filter(
1850
- (a) => a.type === "file"
566
+ let finishReason = mapOpenRouterFinishReason(
567
+ response.status === "completed" ? "stop" : response.status ?? "stop"
1851
568
  );
1852
- const hasToolCalls = choice.message.tool_calls && choice.message.tool_calls.length > 0;
1853
- const hasEncryptedReasoning = reasoningDetails.some(
1854
- (d) => d.type === "reasoning.encrypted" /* Encrypted */ && d.data
569
+ const hasToolCalls = content.some((c) => c.type === "tool-call");
570
+ if (hasToolCalls && hasEncryptedReasoning(reasoningDetails) && finishReason.unified === "stop") {
571
+ finishReason = { unified: "tool-calls", raw: finishReason.raw };
572
+ }
573
+ const usage = buildUsage(
574
+ response.usage ? {
575
+ inputTokens: response.usage.inputTokens,
576
+ outputTokens: response.usage.outputTokens
577
+ } : void 0
1855
578
  );
1856
- const shouldOverrideFinishReason = hasToolCalls && hasEncryptedReasoning && choice.finish_reason === "stop";
1857
- const effectiveFinishReason = shouldOverrideFinishReason ? "tool-calls" : mapOpenRouterFinishReason(choice.finish_reason);
579
+ const providerMetadata = buildProviderMetadata({
580
+ id: response.id,
581
+ provider: void 0,
582
+ // Responses API doesn't expose provider in response
583
+ usage: response.usage ? {
584
+ promptTokens: response.usage.inputTokens,
585
+ completionTokens: response.usage.outputTokens,
586
+ totalTokens: response.usage.totalTokens,
587
+ cost: response.usage.cost ?? void 0,
588
+ // Map inputTokensDetails -> promptTokensDetails
589
+ promptTokensDetails: response.usage.inputTokensDetails ? {
590
+ cachedTokens: response.usage.inputTokensDetails.cachedTokens
591
+ } : void 0,
592
+ // Map outputTokensDetails -> completionTokensDetails
593
+ completionTokensDetails: response.usage.outputTokensDetails ? {
594
+ reasoningTokens: response.usage.outputTokensDetails.reasoningTokens
595
+ } : void 0
596
+ } : void 0
597
+ });
1858
598
  return {
1859
599
  content,
1860
- finishReason: effectiveFinishReason,
1861
- usage: usageInfo,
1862
- warnings: [],
1863
- providerMetadata: {
1864
- openrouter: OpenRouterProviderMetadataSchema.parse({
1865
- provider: (_l = response.provider) != null ? _l : "",
1866
- reasoning_details: (_m = choice.message.reasoning_details) != null ? _m : [],
1867
- annotations: fileAnnotations && fileAnnotations.length > 0 ? fileAnnotations : void 0,
1868
- usage: __spreadValues(__spreadValues(__spreadValues({
1869
- promptTokens: (_n = usageInfo.inputTokens) != null ? _n : 0,
1870
- completionTokens: (_o = usageInfo.outputTokens) != null ? _o : 0,
1871
- totalTokens: (_p = usageInfo.totalTokens) != null ? _p : 0,
1872
- cost: (_q = response.usage) == null ? void 0 : _q.cost
1873
- }, ((_s = (_r = response.usage) == null ? void 0 : _r.prompt_tokens_details) == null ? void 0 : _s.cached_tokens) != null ? {
1874
- promptTokensDetails: {
1875
- cachedTokens: response.usage.prompt_tokens_details.cached_tokens
1876
- }
1877
- } : {}), ((_u = (_t = response.usage) == null ? void 0 : _t.completion_tokens_details) == null ? void 0 : _u.reasoning_tokens) != null ? {
1878
- completionTokensDetails: {
1879
- reasoningTokens: response.usage.completion_tokens_details.reasoning_tokens
1880
- }
1881
- } : {}), ((_w = (_v = response.usage) == null ? void 0 : _v.cost_details) == null ? void 0 : _w.upstream_inference_cost) != null ? {
1882
- costDetails: {
1883
- upstreamInferenceCost: response.usage.cost_details.upstream_inference_cost
1884
- }
1885
- } : {})
1886
- })
600
+ finishReason,
601
+ usage,
602
+ warnings,
603
+ providerMetadata,
604
+ request: {
605
+ body: requestParams
1887
606
  },
1888
- request: { body: args },
1889
607
  response: {
1890
608
  id: response.id,
1891
- modelId: response.model,
1892
- headers: responseHeaders
609
+ timestamp: new Date(response.createdAt * 1e3),
610
+ modelId: response.model
1893
611
  }
1894
612
  };
1895
613
  }
1896
614
  async doStream(options) {
1897
- var _a15;
1898
- const providerOptions = options.providerOptions || {};
1899
- const openrouterOptions = providerOptions.openrouter || {};
1900
- const args = __spreadValues(__spreadValues({}, this.getArgs(options)), openrouterOptions);
1901
- const { value: response, responseHeaders } = await postJsonToApi({
1902
- url: this.config.url({
1903
- path: "/chat/completions",
1904
- modelId: this.modelId
1905
- }),
1906
- headers: combineHeaders(this.config.headers(), options.headers),
1907
- body: __spreadProps(__spreadValues({}, args), {
1908
- stream: true,
1909
- // only include stream_options when in strict compatibility mode:
1910
- stream_options: this.config.compatibility === "strict" ? __spreadValues({
1911
- include_usage: true
1912
- }, ((_a15 = this.settings.usage) == null ? void 0 : _a15.include) ? { include_usage: true } : {}) : void 0
1913
- }),
1914
- failedResponseHandler: openrouterFailedResponseHandler,
1915
- successfulResponseHandler: createEventSourceResponseHandler(
1916
- OpenRouterStreamChatCompletionChunkSchema
1917
- ),
1918
- abortSignal: options.abortSignal,
1919
- fetch: this.config.fetch
615
+ const warnings = [];
616
+ const client = new OpenRouter({
617
+ apiKey: this.settings.apiKey,
618
+ serverURL: this.settings.baseURL,
619
+ userAgent: this.settings.userAgent
1920
620
  });
1921
- const toolCalls = [];
1922
- let finishReason = "other";
1923
- const usage = {
1924
- inputTokens: Number.NaN,
1925
- outputTokens: Number.NaN,
1926
- totalTokens: Number.NaN,
1927
- reasoningTokens: Number.NaN,
1928
- cachedInputTokens: Number.NaN
621
+ const openRouterInput = convertToOpenRouterMessages(options.prompt);
622
+ const tools = convertToolsToResponsesFormat(options.tools, warnings);
623
+ const toolChoice = convertToolChoiceToResponsesFormat(options.toolChoice);
624
+ const text = convertResponseFormatToText(options.responseFormat);
625
+ const requestParams = {
626
+ model: this.modelId,
627
+ input: openRouterInput,
628
+ stream: true,
629
+ ...options.maxOutputTokens !== void 0 && {
630
+ maxOutputTokens: options.maxOutputTokens
631
+ },
632
+ ...options.temperature !== void 0 && {
633
+ temperature: options.temperature
634
+ },
635
+ ...options.topP !== void 0 && { topP: options.topP },
636
+ ...tools.length > 0 && { tools },
637
+ ...toolChoice !== void 0 && { toolChoice },
638
+ ...text !== void 0 && { text }
1929
639
  };
1930
- const openrouterUsage = {};
1931
- const accumulatedReasoningDetails = [];
1932
- const accumulatedFileAnnotations = [];
1933
- let textStarted = false;
1934
- let reasoningStarted = false;
1935
- let textId;
1936
- let reasoningId;
1937
- let openrouterResponseId;
1938
- let provider;
1939
- return {
1940
- stream: response.pipeThrough(
1941
- new TransformStream({
1942
- transform(chunk, controller) {
1943
- var _a16, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
1944
- if (!chunk.success) {
1945
- finishReason = "error";
1946
- controller.enqueue({ type: "error", error: chunk.error });
1947
- return;
1948
- }
1949
- const value = chunk.value;
1950
- if ("error" in value) {
1951
- finishReason = "error";
1952
- controller.enqueue({ type: "error", error: value.error });
1953
- return;
1954
- }
1955
- if (value.provider) {
1956
- provider = value.provider;
1957
- }
1958
- if (value.id) {
1959
- openrouterResponseId = value.id;
1960
- controller.enqueue({
1961
- type: "response-metadata",
1962
- id: value.id
1963
- });
1964
- }
1965
- if (value.model) {
1966
- controller.enqueue({
1967
- type: "response-metadata",
1968
- modelId: value.model
1969
- });
1970
- }
1971
- if (value.usage != null) {
1972
- usage.inputTokens = value.usage.prompt_tokens;
1973
- usage.outputTokens = value.usage.completion_tokens;
1974
- usage.totalTokens = value.usage.prompt_tokens + value.usage.completion_tokens;
1975
- openrouterUsage.promptTokens = value.usage.prompt_tokens;
1976
- if (value.usage.prompt_tokens_details) {
1977
- const cachedInputTokens = (_a16 = value.usage.prompt_tokens_details.cached_tokens) != null ? _a16 : 0;
1978
- usage.cachedInputTokens = cachedInputTokens;
1979
- openrouterUsage.promptTokensDetails = {
1980
- cachedTokens: cachedInputTokens
1981
- };
1982
- }
1983
- openrouterUsage.completionTokens = value.usage.completion_tokens;
1984
- if (value.usage.completion_tokens_details) {
1985
- const reasoningTokens = (_b = value.usage.completion_tokens_details.reasoning_tokens) != null ? _b : 0;
1986
- usage.reasoningTokens = reasoningTokens;
1987
- openrouterUsage.completionTokensDetails = {
1988
- reasoningTokens
1989
- };
1990
- }
1991
- openrouterUsage.cost = value.usage.cost;
1992
- openrouterUsage.totalTokens = value.usage.total_tokens;
1993
- const upstreamInferenceCost = (_c = value.usage.cost_details) == null ? void 0 : _c.upstream_inference_cost;
1994
- if (upstreamInferenceCost != null) {
1995
- openrouterUsage.costDetails = {
1996
- upstreamInferenceCost
1997
- };
1998
- }
1999
- }
2000
- const choice = value.choices[0];
2001
- if ((choice == null ? void 0 : choice.finish_reason) != null) {
2002
- finishReason = mapOpenRouterFinishReason(choice.finish_reason);
2003
- }
2004
- if ((choice == null ? void 0 : choice.delta) == null) {
2005
- return;
2006
- }
2007
- const delta = choice.delta;
2008
- const emitReasoningChunk = (chunkText, providerMetadata) => {
2009
- if (!reasoningStarted) {
2010
- reasoningId = openrouterResponseId || generateId();
2011
- controller.enqueue({
2012
- providerMetadata,
2013
- type: "reasoning-start",
2014
- id: reasoningId
2015
- });
2016
- reasoningStarted = true;
2017
- }
2018
- controller.enqueue({
2019
- providerMetadata,
2020
- type: "reasoning-delta",
2021
- delta: chunkText,
2022
- id: reasoningId || generateId()
2023
- });
2024
- };
2025
- if (delta.reasoning_details && delta.reasoning_details.length > 0) {
2026
- for (const detail of delta.reasoning_details) {
2027
- if (detail.type === "reasoning.text" /* Text */) {
2028
- const lastDetail = accumulatedReasoningDetails[accumulatedReasoningDetails.length - 1];
2029
- if ((lastDetail == null ? void 0 : lastDetail.type) === "reasoning.text" /* Text */) {
2030
- lastDetail.text = (lastDetail.text || "") + (detail.text || "");
2031
- lastDetail.signature = lastDetail.signature || detail.signature;
2032
- lastDetail.format = lastDetail.format || detail.format;
2033
- } else {
2034
- accumulatedReasoningDetails.push(__spreadValues({}, detail));
2035
- }
2036
- } else {
2037
- accumulatedReasoningDetails.push(detail);
2038
- }
2039
- }
2040
- const reasoningMetadata = {
2041
- openrouter: {
2042
- reasoning_details: delta.reasoning_details
2043
- }
2044
- };
2045
- for (const detail of delta.reasoning_details) {
2046
- switch (detail.type) {
2047
- case "reasoning.text" /* Text */: {
2048
- if (detail.text) {
2049
- emitReasoningChunk(detail.text, reasoningMetadata);
2050
- }
2051
- break;
2052
- }
2053
- case "reasoning.encrypted" /* Encrypted */: {
2054
- if (detail.data) {
2055
- emitReasoningChunk("[REDACTED]", reasoningMetadata);
2056
- }
2057
- break;
2058
- }
2059
- case "reasoning.summary" /* Summary */: {
2060
- if (detail.summary) {
2061
- emitReasoningChunk(detail.summary, reasoningMetadata);
2062
- }
2063
- break;
2064
- }
2065
- default: {
2066
- detail;
2067
- break;
2068
- }
2069
- }
2070
- }
2071
- } else if (delta.reasoning) {
2072
- emitReasoningChunk(delta.reasoning);
2073
- }
2074
- if (delta.content) {
2075
- if (reasoningStarted && !textStarted) {
2076
- controller.enqueue({
2077
- type: "reasoning-end",
2078
- id: reasoningId || generateId()
2079
- });
2080
- reasoningStarted = false;
2081
- }
2082
- if (!textStarted) {
2083
- textId = openrouterResponseId || generateId();
2084
- controller.enqueue({
2085
- type: "text-start",
2086
- id: textId
2087
- });
2088
- textStarted = true;
2089
- }
2090
- controller.enqueue({
2091
- type: "text-delta",
2092
- delta: delta.content,
2093
- id: textId || generateId()
2094
- });
2095
- }
2096
- if (delta.annotations) {
2097
- for (const annotation of delta.annotations) {
2098
- if (annotation.type === "url_citation") {
2099
- controller.enqueue({
2100
- type: "source",
2101
- sourceType: "url",
2102
- id: annotation.url_citation.url,
2103
- url: annotation.url_citation.url,
2104
- title: annotation.url_citation.title,
2105
- providerMetadata: {
2106
- openrouter: {
2107
- content: annotation.url_citation.content || ""
2108
- }
2109
- }
2110
- });
2111
- } else if (annotation.type === "file") {
2112
- const file = annotation.file;
2113
- if (file && typeof file === "object" && "hash" in file && "name" in file) {
2114
- accumulatedFileAnnotations.push(
2115
- annotation
2116
- );
2117
- }
2118
- }
2119
- }
2120
- }
2121
- if (delta.tool_calls != null) {
2122
- for (const toolCallDelta of delta.tool_calls) {
2123
- const index = (_d = toolCallDelta.index) != null ? _d : toolCalls.length - 1;
2124
- if (toolCalls[index] == null) {
2125
- if (toolCallDelta.type !== "function") {
2126
- throw new InvalidResponseDataError({
2127
- data: toolCallDelta,
2128
- message: `Expected 'function' type.`
2129
- });
2130
- }
2131
- if (toolCallDelta.id == null) {
2132
- throw new InvalidResponseDataError({
2133
- data: toolCallDelta,
2134
- message: `Expected 'id' to be a string.`
2135
- });
2136
- }
2137
- if (((_e = toolCallDelta.function) == null ? void 0 : _e.name) == null) {
2138
- throw new InvalidResponseDataError({
2139
- data: toolCallDelta,
2140
- message: `Expected 'function.name' to be a string.`
2141
- });
2142
- }
2143
- toolCalls[index] = {
2144
- id: toolCallDelta.id,
2145
- type: "function",
2146
- function: {
2147
- name: toolCallDelta.function.name,
2148
- arguments: (_f = toolCallDelta.function.arguments) != null ? _f : ""
2149
- },
2150
- inputStarted: false,
2151
- sent: false
2152
- };
2153
- const toolCall2 = toolCalls[index];
2154
- if (toolCall2 == null) {
2155
- throw new InvalidResponseDataError({
2156
- data: { index, toolCallsLength: toolCalls.length },
2157
- message: `Tool call at index ${index} is missing after creation.`
2158
- });
2159
- }
2160
- if (((_g = toolCall2.function) == null ? void 0 : _g.name) != null && ((_h = toolCall2.function) == null ? void 0 : _h.arguments) != null && isParsableJson(toolCall2.function.arguments)) {
2161
- toolCall2.inputStarted = true;
2162
- controller.enqueue({
2163
- type: "tool-input-start",
2164
- id: toolCall2.id,
2165
- toolName: toolCall2.function.name
2166
- });
2167
- controller.enqueue({
2168
- type: "tool-input-delta",
2169
- id: toolCall2.id,
2170
- delta: toolCall2.function.arguments
2171
- });
2172
- controller.enqueue({
2173
- type: "tool-input-end",
2174
- id: toolCall2.id
2175
- });
2176
- controller.enqueue({
2177
- type: "tool-call",
2178
- toolCallId: toolCall2.id,
2179
- toolName: toolCall2.function.name,
2180
- input: toolCall2.function.arguments,
2181
- providerMetadata: {
2182
- openrouter: {
2183
- reasoning_details: accumulatedReasoningDetails
2184
- }
2185
- }
2186
- });
2187
- toolCall2.sent = true;
2188
- }
2189
- continue;
2190
- }
2191
- const toolCall = toolCalls[index];
2192
- if (toolCall == null) {
2193
- throw new InvalidResponseDataError({
2194
- data: {
2195
- index,
2196
- toolCallsLength: toolCalls.length,
2197
- toolCallDelta
2198
- },
2199
- message: `Tool call at index ${index} is missing during merge.`
2200
- });
2201
- }
2202
- if (!toolCall.inputStarted) {
2203
- toolCall.inputStarted = true;
2204
- controller.enqueue({
2205
- type: "tool-input-start",
2206
- id: toolCall.id,
2207
- toolName: toolCall.function.name
2208
- });
2209
- }
2210
- if (((_i = toolCallDelta.function) == null ? void 0 : _i.arguments) != null) {
2211
- toolCall.function.arguments += (_k = (_j = toolCallDelta.function) == null ? void 0 : _j.arguments) != null ? _k : "";
2212
- }
2213
- controller.enqueue({
2214
- type: "tool-input-delta",
2215
- id: toolCall.id,
2216
- delta: (_l = toolCallDelta.function.arguments) != null ? _l : ""
2217
- });
2218
- if (((_m = toolCall.function) == null ? void 0 : _m.name) != null && ((_n = toolCall.function) == null ? void 0 : _n.arguments) != null && isParsableJson(toolCall.function.arguments)) {
2219
- controller.enqueue({
2220
- type: "tool-call",
2221
- toolCallId: (_o = toolCall.id) != null ? _o : generateId(),
2222
- toolName: toolCall.function.name,
2223
- input: toolCall.function.arguments,
2224
- providerMetadata: {
2225
- openrouter: {
2226
- reasoning_details: accumulatedReasoningDetails
2227
- }
2228
- }
2229
- });
2230
- toolCall.sent = true;
2231
- }
2232
- }
2233
- }
2234
- if (delta.images != null) {
2235
- for (const image of delta.images) {
2236
- controller.enqueue({
2237
- type: "file",
2238
- mediaType: getMediaType(image.image_url.url, "image/jpeg"),
2239
- data: getBase64FromDataUrl(image.image_url.url)
2240
- });
2241
- }
2242
- }
2243
- },
2244
- flush(controller) {
2245
- var _a16;
2246
- const hasToolCalls = toolCalls.length > 0;
2247
- const hasEncryptedReasoning = accumulatedReasoningDetails.some(
2248
- (d) => d.type === "reasoning.encrypted" /* Encrypted */ && d.data
2249
- );
2250
- if (hasToolCalls && hasEncryptedReasoning && finishReason === "stop") {
2251
- finishReason = "tool-calls";
2252
- }
2253
- if (finishReason === "tool-calls") {
2254
- for (const toolCall of toolCalls) {
2255
- if (toolCall && !toolCall.sent) {
2256
- controller.enqueue({
2257
- type: "tool-call",
2258
- toolCallId: (_a16 = toolCall.id) != null ? _a16 : generateId(),
2259
- toolName: toolCall.function.name,
2260
- // Coerce invalid arguments to an empty JSON object
2261
- input: isParsableJson(toolCall.function.arguments) ? toolCall.function.arguments : "{}",
2262
- providerMetadata: {
2263
- openrouter: {
2264
- reasoning_details: accumulatedReasoningDetails
2265
- }
2266
- }
2267
- });
2268
- toolCall.sent = true;
2269
- }
2270
- }
2271
- }
2272
- if (reasoningStarted) {
2273
- controller.enqueue({
2274
- type: "reasoning-end",
2275
- id: reasoningId || generateId()
2276
- });
2277
- }
2278
- if (textStarted) {
2279
- controller.enqueue({
2280
- type: "text-end",
2281
- id: textId || generateId()
2282
- });
2283
- }
2284
- const openrouterMetadata = {
2285
- usage: openrouterUsage
2286
- };
2287
- if (provider !== void 0) {
2288
- openrouterMetadata.provider = provider;
2289
- }
2290
- if (accumulatedReasoningDetails.length > 0) {
2291
- openrouterMetadata.reasoning_details = accumulatedReasoningDetails;
2292
- }
2293
- if (accumulatedFileAnnotations.length > 0) {
2294
- openrouterMetadata.annotations = accumulatedFileAnnotations;
640
+ const combinedHeaders = normalizeHeaders(
641
+ combineHeaders(this.settings.headers, options.headers)
642
+ );
643
+ const eventStream = await client.beta.responses.send(requestParams, {
644
+ fetchOptions: {
645
+ signal: options.abortSignal,
646
+ headers: combinedHeaders
647
+ }
648
+ });
649
+ const state = createStreamState();
650
+ const transformedStream = new ReadableStream({
651
+ async start(controller) {
652
+ controller.enqueue({
653
+ type: "stream-start",
654
+ warnings
655
+ });
656
+ try {
657
+ for await (const event of eventStream) {
658
+ const parts = transformResponsesEvent(event, state);
659
+ for (const part of parts) {
660
+ controller.enqueue(part);
2295
661
  }
2296
- controller.enqueue({
2297
- type: "finish",
2298
- finishReason,
2299
- usage,
2300
- providerMetadata: {
2301
- openrouter: openrouterMetadata
2302
- }
2303
- });
2304
662
  }
2305
- })
2306
- ),
2307
- warnings: [],
2308
- request: { body: args },
2309
- response: { headers: responseHeaders }
663
+ } catch (error) {
664
+ controller.enqueue({
665
+ type: "error",
666
+ error
667
+ });
668
+ } finally {
669
+ controller.close();
670
+ }
671
+ }
672
+ });
673
+ return {
674
+ stream: transformedStream,
675
+ request: {
676
+ body: requestParams
677
+ }
2310
678
  };
2311
679
  }
2312
680
  };
2313
-
2314
- // src/completion/convert-to-openrouter-completion-prompt.ts
2315
- function convertToOpenRouterCompletionPrompt({
2316
- prompt,
2317
- inputFormat,
2318
- user = "user",
2319
- assistant = "assistant"
2320
- }) {
2321
- if (inputFormat === "prompt" && prompt.length === 1 && prompt[0] && prompt[0].role === "user" && prompt[0].content.length === 1 && prompt[0].content[0] && prompt[0].content[0].type === "text") {
2322
- return { prompt: prompt[0].content[0].text };
2323
- }
2324
- let text = "";
2325
- if (prompt[0] && prompt[0].role === "system") {
2326
- text += `${prompt[0].content}
2327
-
2328
- `;
2329
- prompt = prompt.slice(1);
2330
- }
2331
- for (const { role, content } of prompt) {
2332
- switch (role) {
2333
- case "system": {
2334
- throw new InvalidPromptError({
2335
- message: `Unexpected system message in prompt: ${content}`,
2336
- prompt
681
+ function createStreamState() {
682
+ return {
683
+ responseId: void 0,
684
+ responseModel: void 0,
685
+ responseCreated: void 0,
686
+ textStarted: false,
687
+ textId: "text-0",
688
+ reasoningStarted: false,
689
+ reasoningId: "reasoning-0",
690
+ textEnded: false,
691
+ reasoningEnded: false,
692
+ sourceIds: [],
693
+ toolCalls: /* @__PURE__ */ new Map()
694
+ };
695
+ }
696
+ function transformResponsesEvent(event, state) {
697
+ const parts = [];
698
+ switch (event.type) {
699
+ // Response lifecycle events
700
+ case "response.created":
701
+ case "response.in_progress": {
702
+ if (event.response) {
703
+ state.responseId = event.response.id;
704
+ state.responseModel = event.response.model;
705
+ state.responseCreated = event.response.createdAt;
706
+ }
707
+ break;
708
+ }
709
+ // Text streaming
710
+ case "response.output_text.delta": {
711
+ if (!state.textStarted) {
712
+ state.textStarted = true;
713
+ parts.push({
714
+ type: "text-start",
715
+ id: state.textId
2337
716
  });
2338
717
  }
2339
- case "user": {
2340
- const userMessage = content.map((part) => {
2341
- switch (part.type) {
2342
- case "text": {
2343
- return part.text;
2344
- }
2345
- case "file": {
2346
- throw new UnsupportedFunctionalityError({
2347
- functionality: "file attachments"
2348
- });
2349
- }
2350
- default: {
2351
- return "";
2352
- }
2353
- }
2354
- }).join("");
2355
- text += `${user}:
2356
- ${userMessage}
2357
-
2358
- `;
2359
- break;
718
+ if (event.delta && event.delta.length > 0) {
719
+ parts.push({
720
+ type: "text-delta",
721
+ id: state.textId,
722
+ delta: event.delta
723
+ });
2360
724
  }
2361
- case "assistant": {
2362
- const assistantMessage = content.map(
2363
- (part) => {
2364
- switch (part.type) {
2365
- case "text": {
2366
- return part.text;
2367
- }
2368
- case "tool-call": {
2369
- throw new UnsupportedFunctionalityError({
2370
- functionality: "tool-call messages"
2371
- });
2372
- }
2373
- case "tool-result": {
2374
- throw new UnsupportedFunctionalityError({
2375
- functionality: "tool-result messages"
2376
- });
2377
- }
2378
- case "reasoning": {
2379
- throw new UnsupportedFunctionalityError({
2380
- functionality: "reasoning messages"
2381
- });
2382
- }
2383
- case "file": {
2384
- throw new UnsupportedFunctionalityError({
2385
- functionality: "file attachments"
2386
- });
2387
- }
2388
- default: {
2389
- return "";
2390
- }
2391
- }
2392
- }
2393
- ).join("");
2394
- text += `${assistant}:
2395
- ${assistantMessage}
2396
-
2397
- `;
2398
- break;
725
+ break;
726
+ }
727
+ case "response.output_text.done": {
728
+ if (state.textStarted && !state.textEnded) {
729
+ state.textEnded = true;
730
+ parts.push({
731
+ type: "text-end",
732
+ id: state.textId
733
+ });
2399
734
  }
2400
- case "tool": {
2401
- throw new UnsupportedFunctionalityError({
2402
- functionality: "tool messages"
735
+ break;
736
+ }
737
+ // Reasoning streaming
738
+ case "response.reasoning_text.delta": {
739
+ if (!state.reasoningStarted) {
740
+ state.reasoningStarted = true;
741
+ parts.push({
742
+ type: "reasoning-start",
743
+ id: state.reasoningId
2403
744
  });
2404
745
  }
2405
- default: {
2406
- break;
746
+ if (event.delta && event.delta.length > 0) {
747
+ parts.push({
748
+ type: "reasoning-delta",
749
+ id: state.reasoningId,
750
+ delta: event.delta
751
+ });
2407
752
  }
753
+ break;
2408
754
  }
2409
- }
2410
- text += `${assistant}:
2411
- `;
2412
- return {
2413
- prompt: text
2414
- };
2415
- }
2416
-
2417
- // src/completion/schemas.ts
2418
- import { z as z8 } from "zod/v4";
2419
- var OpenRouterCompletionChunkSchema = z8.union([
2420
- z8.object({
2421
- id: z8.string().optional(),
2422
- model: z8.string().optional(),
2423
- choices: z8.array(
2424
- z8.object({
2425
- text: z8.string(),
2426
- reasoning: z8.string().nullish().optional(),
2427
- reasoning_details: ReasoningDetailArraySchema.nullish(),
2428
- finish_reason: z8.string().nullish(),
2429
- index: z8.number().nullish(),
2430
- logprobs: z8.object({
2431
- tokens: z8.array(z8.string()),
2432
- token_logprobs: z8.array(z8.number()),
2433
- top_logprobs: z8.array(z8.record(z8.string(), z8.number())).nullable()
2434
- }).passthrough().nullable().optional()
2435
- }).passthrough()
2436
- ),
2437
- usage: z8.object({
2438
- prompt_tokens: z8.number(),
2439
- prompt_tokens_details: z8.object({
2440
- cached_tokens: z8.number()
2441
- }).passthrough().nullish(),
2442
- completion_tokens: z8.number(),
2443
- completion_tokens_details: z8.object({
2444
- reasoning_tokens: z8.number()
2445
- }).passthrough().nullish(),
2446
- total_tokens: z8.number(),
2447
- cost: z8.number().optional(),
2448
- cost_details: z8.object({
2449
- upstream_inference_cost: z8.number().nullish()
2450
- }).passthrough().nullish()
2451
- }).passthrough().nullish()
2452
- }).passthrough(),
2453
- OpenRouterErrorResponseSchema
2454
- ]);
2455
-
2456
- // src/completion/index.ts
2457
- var OpenRouterCompletionLanguageModel = class {
2458
- constructor(modelId, settings, config) {
2459
- this.specificationVersion = "v2";
2460
- this.provider = "openrouter";
2461
- this.supportsImageUrls = true;
2462
- this.supportedUrls = {
2463
- "image/*": [
2464
- /^data:image\/[a-zA-Z]+;base64,/,
2465
- /^https?:\/\/.+\.(jpg|jpeg|png|gif|webp)$/i
2466
- ],
2467
- "text/*": [/^data:text\//, /^https?:\/\/.+$/],
2468
- "application/*": [/^data:application\//, /^https?:\/\/.+$/]
2469
- };
2470
- this.defaultObjectGenerationMode = void 0;
2471
- this.modelId = modelId;
2472
- this.settings = settings;
2473
- this.config = config;
2474
- }
2475
- getArgs({
2476
- prompt,
2477
- maxOutputTokens,
2478
- temperature,
2479
- topP,
2480
- frequencyPenalty,
2481
- presencePenalty,
2482
- seed,
2483
- responseFormat,
2484
- topK,
2485
- stopSequences,
2486
- tools,
2487
- toolChoice
2488
- }) {
2489
- const { prompt: completionPrompt } = convertToOpenRouterCompletionPrompt({
2490
- prompt,
2491
- inputFormat: "prompt"
2492
- });
2493
- if (tools == null ? void 0 : tools.length) {
2494
- throw new UnsupportedFunctionalityError({
2495
- functionality: "tools"
755
+ // Function call arguments streaming
756
+ case "response.function_call_arguments.delta": {
757
+ const toolCallId = event.itemId;
758
+ let toolState = state.toolCalls.get(toolCallId);
759
+ if (!toolState) {
760
+ toolState = { argumentsStarted: false };
761
+ state.toolCalls.set(toolCallId, toolState);
762
+ }
763
+ if (!toolState.argumentsStarted) {
764
+ toolState.argumentsStarted = true;
765
+ parts.push({
766
+ type: "tool-input-start",
767
+ id: toolCallId,
768
+ toolName: toolState.name ?? ""
769
+ // Will be filled in by output_item.added
770
+ });
771
+ }
772
+ if (event.delta && event.delta.length > 0) {
773
+ parts.push({
774
+ type: "tool-input-delta",
775
+ id: toolCallId,
776
+ delta: event.delta
777
+ });
778
+ }
779
+ break;
780
+ }
781
+ case "response.function_call_arguments.done": {
782
+ const toolCallId = event.itemId;
783
+ const toolState = state.toolCalls.get(toolCallId);
784
+ if (!toolState?.argumentsStarted) {
785
+ parts.push({
786
+ type: "tool-input-start",
787
+ id: toolCallId,
788
+ toolName: event.name
789
+ });
790
+ const args = event.arguments || "{}";
791
+ parts.push({
792
+ type: "tool-input-delta",
793
+ id: toolCallId,
794
+ delta: args
795
+ });
796
+ }
797
+ parts.push({
798
+ type: "tool-input-end",
799
+ id: toolCallId
800
+ });
801
+ parts.push({
802
+ type: "tool-call",
803
+ toolCallId,
804
+ toolName: event.name,
805
+ input: event.arguments || "{}"
806
+ });
807
+ break;
808
+ }
809
+ // Output item events (for function call metadata)
810
+ case "response.output_item.added": {
811
+ if (event.item.type === "function_call") {
812
+ const funcItem = event.item;
813
+ const toolCallId = funcItem.callId ?? `tool-${event.outputIndex}`;
814
+ const toolState = state.toolCalls.get(toolCallId) ?? {
815
+ argumentsStarted: false
816
+ };
817
+ toolState.name = funcItem.name;
818
+ state.toolCalls.set(toolCallId, toolState);
819
+ }
820
+ break;
821
+ }
822
+ // Annotation events (web search sources)
823
+ case "response.output_text.annotation.added": {
824
+ const annotation = event.annotation;
825
+ if (annotation.type === "url_citation") {
826
+ const urlAnnotation = annotation;
827
+ const sourceId = `source-${state.sourceIds.length}`;
828
+ state.sourceIds.push(sourceId);
829
+ parts.push({
830
+ type: "source",
831
+ sourceType: "url",
832
+ id: sourceId,
833
+ url: urlAnnotation.url,
834
+ title: urlAnnotation.title
835
+ });
836
+ }
837
+ break;
838
+ }
839
+ // Response completed - extract final usage data
840
+ case "response.completed": {
841
+ if (state.textStarted && !state.textEnded) {
842
+ state.textEnded = true;
843
+ parts.push({
844
+ type: "text-end",
845
+ id: state.textId
846
+ });
847
+ }
848
+ if (state.reasoningStarted && !state.reasoningEnded) {
849
+ state.reasoningEnded = true;
850
+ parts.push({
851
+ type: "reasoning-end",
852
+ id: state.reasoningId
853
+ });
854
+ }
855
+ const response = event.response;
856
+ parts.push({
857
+ type: "response-metadata",
858
+ id: response.id,
859
+ timestamp: response.createdAt ? new Date(response.createdAt * 1e3) : void 0,
860
+ modelId: response.model
861
+ });
862
+ const finishReason = mapOpenRouterFinishReason(
863
+ response.status === "completed" ? "stop" : response.status ?? "stop"
864
+ );
865
+ const usage = buildUsage(
866
+ response.usage ? {
867
+ inputTokens: response.usage.inputTokens,
868
+ outputTokens: response.usage.outputTokens
869
+ } : void 0
870
+ );
871
+ const providerMetadata = buildProviderMetadata({
872
+ id: response.id,
873
+ provider: void 0,
874
+ // Responses API doesn't expose provider
875
+ usage: response.usage ? {
876
+ promptTokens: response.usage.inputTokens,
877
+ completionTokens: response.usage.outputTokens,
878
+ totalTokens: response.usage.totalTokens,
879
+ cost: response.usage.cost ?? void 0,
880
+ // Map inputTokensDetails -> promptTokensDetails
881
+ promptTokensDetails: response.usage.inputTokensDetails ? {
882
+ cachedTokens: response.usage.inputTokensDetails.cachedTokens
883
+ } : void 0,
884
+ // Map outputTokensDetails -> completionTokensDetails
885
+ completionTokensDetails: response.usage.outputTokensDetails ? {
886
+ reasoningTokens: response.usage.outputTokensDetails.reasoningTokens
887
+ } : void 0
888
+ } : void 0
2496
889
  });
890
+ parts.push({
891
+ type: "finish",
892
+ finishReason,
893
+ usage,
894
+ providerMetadata
895
+ });
896
+ break;
2497
897
  }
2498
- if (toolChoice) {
2499
- throw new UnsupportedFunctionalityError({
2500
- functionality: "toolChoice"
898
+ // Response incomplete or failed
899
+ case "response.incomplete":
900
+ case "response.failed": {
901
+ if (state.textStarted && !state.textEnded) {
902
+ state.textEnded = true;
903
+ parts.push({
904
+ type: "text-end",
905
+ id: state.textId
906
+ });
907
+ }
908
+ if (state.reasoningStarted && !state.reasoningEnded) {
909
+ state.reasoningEnded = true;
910
+ parts.push({
911
+ type: "reasoning-end",
912
+ id: state.reasoningId
913
+ });
914
+ }
915
+ const response = event.response;
916
+ parts.push({
917
+ type: "response-metadata",
918
+ id: response.id,
919
+ timestamp: response.createdAt ? new Date(response.createdAt * 1e3) : void 0,
920
+ modelId: response.model
921
+ });
922
+ const finishReason = event.type === "response.failed" ? mapOpenRouterFinishReason("error") : mapOpenRouterFinishReason(response.status ?? "incomplete");
923
+ const usage = buildUsage(
924
+ response.usage ? {
925
+ inputTokens: response.usage.inputTokens,
926
+ outputTokens: response.usage.outputTokens
927
+ } : void 0
928
+ );
929
+ const providerMetadata = buildProviderMetadata({
930
+ id: response.id,
931
+ provider: void 0,
932
+ usage: response.usage ? {
933
+ promptTokens: response.usage.inputTokens,
934
+ completionTokens: response.usage.outputTokens,
935
+ totalTokens: response.usage.totalTokens,
936
+ cost: response.usage.cost ?? void 0,
937
+ // Map inputTokensDetails -> promptTokensDetails
938
+ promptTokensDetails: response.usage.inputTokensDetails ? {
939
+ cachedTokens: response.usage.inputTokensDetails.cachedTokens
940
+ } : void 0,
941
+ // Map outputTokensDetails -> completionTokensDetails
942
+ completionTokensDetails: response.usage.outputTokensDetails ? {
943
+ reasoningTokens: response.usage.outputTokensDetails.reasoningTokens
944
+ } : void 0
945
+ } : void 0
2501
946
  });
947
+ parts.push({
948
+ type: "finish",
949
+ finishReason,
950
+ usage,
951
+ providerMetadata
952
+ });
953
+ break;
2502
954
  }
2503
- return __spreadValues(__spreadValues({
2504
- // model id:
2505
- model: this.modelId,
2506
- models: this.settings.models,
2507
- // model specific settings:
2508
- logit_bias: this.settings.logitBias,
2509
- logprobs: typeof this.settings.logprobs === "number" ? this.settings.logprobs : typeof this.settings.logprobs === "boolean" ? this.settings.logprobs ? 0 : void 0 : void 0,
2510
- suffix: this.settings.suffix,
2511
- user: this.settings.user,
2512
- // standardized settings:
2513
- max_tokens: maxOutputTokens,
2514
- temperature,
2515
- top_p: topP,
2516
- frequency_penalty: frequencyPenalty,
2517
- presence_penalty: presencePenalty,
2518
- seed,
2519
- stop: stopSequences,
2520
- response_format: responseFormat,
2521
- top_k: topK,
2522
- // prompt:
2523
- prompt: completionPrompt,
2524
- // OpenRouter specific settings:
2525
- include_reasoning: this.settings.includeReasoning,
2526
- reasoning: this.settings.reasoning
2527
- }, this.config.extraBody), this.settings.extraBody);
2528
- }
2529
- async doGenerate(options) {
2530
- var _a15, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
2531
- const providerOptions = options.providerOptions || {};
2532
- const openrouterOptions = providerOptions.openrouter || {};
2533
- const args = __spreadValues(__spreadValues({}, this.getArgs(options)), openrouterOptions);
2534
- const { value: response, responseHeaders } = await postJsonToApi({
2535
- url: this.config.url({
2536
- path: "/completions",
2537
- modelId: this.modelId
2538
- }),
2539
- headers: combineHeaders(this.config.headers(), options.headers),
2540
- body: args,
2541
- failedResponseHandler: openrouterFailedResponseHandler,
2542
- successfulResponseHandler: createJsonResponseHandler(
2543
- OpenRouterCompletionChunkSchema
2544
- ),
2545
- abortSignal: options.abortSignal,
2546
- fetch: this.config.fetch
2547
- });
2548
- if ("error" in response) {
2549
- const errorData = response.error;
2550
- throw new APICallError({
2551
- message: errorData.message,
2552
- url: this.config.url({
2553
- path: "/completions",
2554
- modelId: this.modelId
2555
- }),
2556
- requestBodyValues: args,
2557
- statusCode: 200,
2558
- responseHeaders,
2559
- data: errorData
955
+ // Error event
956
+ case "error": {
957
+ const errorEvent = event;
958
+ parts.push({
959
+ type: "error",
960
+ error: new Error(
961
+ errorEvent.error?.message ?? "Unknown streaming error"
962
+ )
2560
963
  });
964
+ break;
2561
965
  }
2562
- const choice = response.choices[0];
2563
- if (!choice) {
2564
- throw new NoContentGeneratedError({
2565
- message: "No choice in OpenRouter completion response"
966
+ // Ignored events (handled implicitly or not needed)
967
+ case "response.output_item.done":
968
+ case "response.content_part.added":
969
+ case "response.content_part.done":
970
+ case "response.refusal.delta":
971
+ case "response.refusal.done":
972
+ case "response.reasoning_text.done":
973
+ case "response.reasoning_summary_part.added":
974
+ case "response.reasoning_summary_part.done":
975
+ case "response.reasoning_summary_text.delta":
976
+ case "response.reasoning_summary_text.done":
977
+ case "response.image_generation_call.in_progress":
978
+ case "response.image_generation_call.generating":
979
+ case "response.image_generation_call.partial_image":
980
+ case "response.image_generation_call.completed":
981
+ break;
982
+ }
983
+ return parts;
984
+ }
985
+ function convertToolsToResponsesFormat(tools, warnings) {
986
+ if (!tools || tools.length === 0) {
987
+ return [];
988
+ }
989
+ return tools.map((tool) => {
990
+ if (tool.type !== "function") {
991
+ warnings.push({
992
+ type: "unsupported",
993
+ feature: `tool type '${tool.type}'`,
994
+ details: `Only 'function' type tools are supported. Tool '${tool.name ?? "unknown"}' has type '${tool.type}'.`
2566
995
  });
996
+ return null;
2567
997
  }
998
+ const functionTool = tool;
2568
999
  return {
2569
- content: [
2570
- {
2571
- type: "text",
2572
- text: (_a15 = choice.text) != null ? _a15 : ""
2573
- }
2574
- ],
2575
- finishReason: mapOpenRouterFinishReason(choice.finish_reason),
2576
- usage: {
2577
- inputTokens: (_c = (_b = response.usage) == null ? void 0 : _b.prompt_tokens) != null ? _c : 0,
2578
- outputTokens: (_e = (_d = response.usage) == null ? void 0 : _d.completion_tokens) != null ? _e : 0,
2579
- totalTokens: ((_g = (_f = response.usage) == null ? void 0 : _f.prompt_tokens) != null ? _g : 0) + ((_i = (_h = response.usage) == null ? void 0 : _h.completion_tokens) != null ? _i : 0),
2580
- reasoningTokens: (_l = (_k = (_j = response.usage) == null ? void 0 : _j.completion_tokens_details) == null ? void 0 : _k.reasoning_tokens) != null ? _l : 0,
2581
- cachedInputTokens: (_o = (_n = (_m = response.usage) == null ? void 0 : _m.prompt_tokens_details) == null ? void 0 : _n.cached_tokens) != null ? _o : 0
2582
- },
2583
- warnings: [],
2584
- response: {
2585
- headers: responseHeaders
2586
- }
1000
+ type: "function",
1001
+ name: functionTool.name,
1002
+ description: functionTool.description ?? null,
1003
+ parameters: functionTool.inputSchema
2587
1004
  };
1005
+ }).filter((tool) => tool !== null);
1006
+ }
1007
+ function convertToolChoiceToResponsesFormat(toolChoice) {
1008
+ if (!toolChoice) {
1009
+ return void 0;
2588
1010
  }
2589
- async doStream(options) {
2590
- const providerOptions = options.providerOptions || {};
2591
- const openrouterOptions = providerOptions.openrouter || {};
2592
- const args = __spreadValues(__spreadValues({}, this.getArgs(options)), openrouterOptions);
2593
- const { value: response, responseHeaders } = await postJsonToApi({
2594
- url: this.config.url({
2595
- path: "/completions",
2596
- modelId: this.modelId
2597
- }),
2598
- headers: combineHeaders(this.config.headers(), options.headers),
2599
- body: __spreadProps(__spreadValues({}, args), {
2600
- stream: true,
2601
- // only include stream_options when in strict compatibility mode:
2602
- stream_options: this.config.compatibility === "strict" ? { include_usage: true } : void 0
2603
- }),
2604
- failedResponseHandler: openrouterFailedResponseHandler,
2605
- successfulResponseHandler: createEventSourceResponseHandler(
2606
- OpenRouterCompletionChunkSchema
2607
- ),
2608
- abortSignal: options.abortSignal,
2609
- fetch: this.config.fetch
2610
- });
2611
- let finishReason = "other";
2612
- const usage = {
2613
- inputTokens: Number.NaN,
2614
- outputTokens: Number.NaN,
2615
- totalTokens: Number.NaN,
2616
- reasoningTokens: Number.NaN,
2617
- cachedInputTokens: Number.NaN
1011
+ switch (toolChoice.type) {
1012
+ case "auto":
1013
+ return "auto";
1014
+ case "none":
1015
+ return "none";
1016
+ case "required":
1017
+ return "required";
1018
+ case "tool":
1019
+ return {
1020
+ type: "function",
1021
+ name: toolChoice.toolName
1022
+ };
1023
+ default:
1024
+ return void 0;
1025
+ }
1026
+ }
1027
+ function convertResponseFormatToText(responseFormat) {
1028
+ if (!responseFormat) {
1029
+ return void 0;
1030
+ }
1031
+ if (responseFormat.type === "text") {
1032
+ return {
1033
+ format: { type: "text" }
2618
1034
  };
2619
- const openrouterUsage = {};
1035
+ }
1036
+ if (responseFormat.type === "json") {
1037
+ if (responseFormat.schema) {
1038
+ return {
1039
+ format: {
1040
+ type: "json_schema",
1041
+ name: responseFormat.name ?? "response",
1042
+ description: responseFormat.description,
1043
+ schema: responseFormat.schema
1044
+ }
1045
+ };
1046
+ }
2620
1047
  return {
2621
- stream: response.pipeThrough(
2622
- new TransformStream({
2623
- transform(chunk, controller) {
2624
- var _a15, _b, _c;
2625
- if (!chunk.success) {
2626
- finishReason = "error";
2627
- controller.enqueue({ type: "error", error: chunk.error });
2628
- return;
2629
- }
2630
- const value = chunk.value;
2631
- if ("error" in value) {
2632
- finishReason = "error";
2633
- controller.enqueue({ type: "error", error: value.error });
2634
- return;
2635
- }
2636
- if (value.usage != null) {
2637
- usage.inputTokens = value.usage.prompt_tokens;
2638
- usage.outputTokens = value.usage.completion_tokens;
2639
- usage.totalTokens = value.usage.prompt_tokens + value.usage.completion_tokens;
2640
- openrouterUsage.promptTokens = value.usage.prompt_tokens;
2641
- if (value.usage.prompt_tokens_details) {
2642
- const cachedInputTokens = (_a15 = value.usage.prompt_tokens_details.cached_tokens) != null ? _a15 : 0;
2643
- usage.cachedInputTokens = cachedInputTokens;
2644
- openrouterUsage.promptTokensDetails = {
2645
- cachedTokens: cachedInputTokens
2646
- };
2647
- }
2648
- openrouterUsage.completionTokens = value.usage.completion_tokens;
2649
- if (value.usage.completion_tokens_details) {
2650
- const reasoningTokens = (_b = value.usage.completion_tokens_details.reasoning_tokens) != null ? _b : 0;
2651
- usage.reasoningTokens = reasoningTokens;
2652
- openrouterUsage.completionTokensDetails = {
2653
- reasoningTokens
2654
- };
2655
- }
2656
- openrouterUsage.cost = value.usage.cost;
2657
- openrouterUsage.totalTokens = value.usage.total_tokens;
2658
- const upstreamInferenceCost = (_c = value.usage.cost_details) == null ? void 0 : _c.upstream_inference_cost;
2659
- if (upstreamInferenceCost != null) {
2660
- openrouterUsage.costDetails = {
2661
- upstreamInferenceCost
2662
- };
2663
- }
2664
- }
2665
- const choice = value.choices[0];
2666
- if ((choice == null ? void 0 : choice.finish_reason) != null) {
2667
- finishReason = mapOpenRouterFinishReason(choice.finish_reason);
2668
- }
2669
- if ((choice == null ? void 0 : choice.text) != null) {
2670
- controller.enqueue({
2671
- type: "text-delta",
2672
- delta: choice.text,
2673
- id: generateId()
2674
- });
2675
- }
2676
- },
2677
- flush(controller) {
2678
- controller.enqueue({
2679
- type: "finish",
2680
- finishReason,
2681
- usage,
2682
- providerMetadata: {
2683
- openrouter: {
2684
- usage: openrouterUsage
2685
- }
2686
- }
2687
- });
2688
- }
2689
- })
2690
- ),
2691
- response: {
2692
- headers: responseHeaders
2693
- }
1048
+ format: { type: "json_object" }
2694
1049
  };
2695
1050
  }
2696
- };
2697
-
2698
- // src/embedding/schemas.ts
2699
- import { z as z9 } from "zod/v4";
2700
- var openrouterEmbeddingUsageSchema = z9.object({
2701
- prompt_tokens: z9.number(),
2702
- total_tokens: z9.number(),
2703
- cost: z9.number().optional()
2704
- });
2705
- var openrouterEmbeddingDataSchema = z9.object({
2706
- object: z9.literal("embedding"),
2707
- embedding: z9.array(z9.number()),
2708
- index: z9.number().optional()
2709
- });
2710
- var OpenRouterEmbeddingResponseSchema = z9.object({
2711
- id: z9.string().optional(),
2712
- object: z9.literal("list"),
2713
- data: z9.array(openrouterEmbeddingDataSchema),
2714
- model: z9.string(),
2715
- usage: openrouterEmbeddingUsageSchema.optional()
2716
- });
1051
+ return void 0;
1052
+ }
2717
1053
 
2718
- // src/embedding/index.ts
1054
+ // src/embedding/openrouter-embedding-model.ts
1055
+ import { combineHeaders as combineHeaders2, normalizeHeaders as normalizeHeaders2 } from "@ai-sdk/provider-utils";
1056
+ import { OpenRouter as OpenRouter2 } from "@openrouter/sdk";
2719
1057
  var OpenRouterEmbeddingModel = class {
2720
- constructor(modelId, settings, config) {
2721
- this.specificationVersion = "v2";
2722
- this.provider = "openrouter";
2723
- this.maxEmbeddingsPerCall = void 0;
2724
- this.supportsParallelCalls = true;
1058
+ specificationVersion = "v3";
1059
+ provider = "openrouter";
1060
+ modelId;
1061
+ settings;
1062
+ /**
1063
+ * Maximum number of embeddings that can be generated in a single API call.
1064
+ * Set to 2048 as a reasonable default for most embedding models.
1065
+ */
1066
+ maxEmbeddingsPerCall = 2048;
1067
+ /**
1068
+ * Whether the model supports parallel calls.
1069
+ */
1070
+ supportsParallelCalls = true;
1071
+ constructor(modelId, settings) {
2725
1072
  this.modelId = modelId;
2726
1073
  this.settings = settings;
2727
- this.config = config;
2728
1074
  }
2729
1075
  async doEmbed(options) {
2730
- var _a15;
2731
- const { values, abortSignal, headers } = options;
2732
- const args = __spreadValues(__spreadValues({
1076
+ const warnings = [];
1077
+ const client = new OpenRouter2({
1078
+ apiKey: this.settings.apiKey,
1079
+ serverURL: this.settings.baseURL,
1080
+ userAgent: this.settings.userAgent
1081
+ });
1082
+ const requestParams = {
2733
1083
  model: this.modelId,
2734
- input: values,
2735
- user: this.settings.user,
2736
- provider: this.settings.provider
2737
- }, this.config.extraBody), this.settings.extraBody);
2738
- const { value: responseValue, responseHeaders } = await postJsonToApi({
2739
- url: this.config.url({
2740
- path: "/embeddings",
2741
- modelId: this.modelId
2742
- }),
2743
- headers: combineHeaders(this.config.headers(), headers),
2744
- body: args,
2745
- failedResponseHandler: openrouterFailedResponseHandler,
2746
- successfulResponseHandler: createJsonResponseHandler(
2747
- OpenRouterEmbeddingResponseSchema
2748
- ),
2749
- abortSignal,
2750
- fetch: this.config.fetch
1084
+ input: options.values
1085
+ };
1086
+ const modelOptions = this.settings.modelOptions;
1087
+ if (modelOptions?.user) {
1088
+ requestParams.user = modelOptions.user;
1089
+ }
1090
+ if (modelOptions?.provider) {
1091
+ requestParams.provider = modelOptions.provider;
1092
+ }
1093
+ const combinedHeaders = normalizeHeaders2(
1094
+ combineHeaders2(this.settings.headers, options.headers)
1095
+ );
1096
+ const response = await client.embeddings.generate(requestParams, {
1097
+ fetchOptions: {
1098
+ signal: options.abortSignal,
1099
+ headers: combinedHeaders
1100
+ }
2751
1101
  });
2752
- return {
2753
- embeddings: responseValue.data.map((item) => item.embedding),
2754
- usage: responseValue.usage ? { tokens: responseValue.usage.prompt_tokens } : void 0,
2755
- providerMetadata: ((_a15 = responseValue.usage) == null ? void 0 : _a15.cost) ? {
2756
- openrouter: {
2757
- usage: {
2758
- cost: responseValue.usage.cost
2759
- }
2760
- }
2761
- } : void 0,
2762
- response: {
2763
- headers: responseHeaders,
2764
- body: responseValue
1102
+ if (typeof response === "string") {
1103
+ throw new Error(`Unexpected string response from embeddings API: ${response}`);
1104
+ }
1105
+ const responseBody = response;
1106
+ const sortedData = [...responseBody.data].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
1107
+ const embeddings = sortedData.map((item) => {
1108
+ if (typeof item.embedding === "string") {
1109
+ throw new Error("Base64 encoded embeddings are not supported");
2765
1110
  }
1111
+ return item.embedding;
1112
+ });
1113
+ return {
1114
+ embeddings,
1115
+ usage: responseBody.usage ? { tokens: responseBody.usage.promptTokens } : void 0,
1116
+ warnings
2766
1117
  };
2767
1118
  }
2768
1119
  };
2769
1120
 
2770
- // src/facade.ts
2771
- var OpenRouter = class {
1121
+ // src/image/openrouter-image-model.ts
1122
+ var OpenRouterImageModel = class {
1123
+ specificationVersion = "v3";
1124
+ provider = "openrouter";
1125
+ modelId;
2772
1126
  /**
2773
- * Creates a new OpenRouter provider instance.
1127
+ * Maximum number of images that can be generated in a single API call.
2774
1128
  */
2775
- constructor(options = {}) {
2776
- var _a15, _b;
2777
- this.baseURL = (_b = withoutTrailingSlash((_a15 = options.baseURL) != null ? _a15 : options.baseUrl)) != null ? _b : "https://openrouter.ai/api/v1";
2778
- this.apiKey = options.apiKey;
2779
- this.headers = options.headers;
2780
- this.api_keys = options.api_keys;
2781
- }
2782
- get baseConfig() {
2783
- return {
2784
- baseURL: this.baseURL,
2785
- headers: () => __spreadValues(__spreadValues({
2786
- Authorization: `Bearer ${loadApiKey({
2787
- apiKey: this.apiKey,
2788
- environmentVariableName: "OPENROUTER_API_KEY",
2789
- description: "OpenRouter"
2790
- })}`
2791
- }, this.headers), this.api_keys && Object.keys(this.api_keys).length > 0 && {
2792
- "X-Provider-API-Keys": JSON.stringify(this.api_keys)
2793
- })
2794
- };
2795
- }
2796
- chat(modelId, settings = {}) {
2797
- return new OpenRouterChatLanguageModel(modelId, settings, __spreadProps(__spreadValues({
2798
- provider: "openrouter.chat"
2799
- }, this.baseConfig), {
2800
- compatibility: "strict",
2801
- url: ({ path }) => `${this.baseURL}${path}`
2802
- }));
2803
- }
2804
- completion(modelId, settings = {}) {
2805
- return new OpenRouterCompletionLanguageModel(modelId, settings, __spreadProps(__spreadValues({
2806
- provider: "openrouter.completion"
2807
- }, this.baseConfig), {
2808
- compatibility: "strict",
2809
- url: ({ path }) => `${this.baseURL}${path}`
2810
- }));
2811
- }
2812
- textEmbeddingModel(modelId, settings = {}) {
2813
- return new OpenRouterEmbeddingModel(modelId, settings, __spreadProps(__spreadValues({
2814
- provider: "openrouter.embedding"
2815
- }, this.baseConfig), {
2816
- url: ({ path }) => `${this.baseURL}${path}`
2817
- }));
1129
+ maxImagesPerCall = 1;
1130
+ constructor(modelId, _settings) {
1131
+ this.modelId = modelId;
2818
1132
  }
2819
- /**
2820
- * @deprecated Use textEmbeddingModel instead
2821
- */
2822
- embedding(modelId, settings = {}) {
2823
- return this.textEmbeddingModel(modelId, settings);
1133
+ async doGenerate(_options) {
1134
+ throw new Error(
1135
+ "Image generation not yet supported. See: https://github.com/OpenRouterTeam/ai-sdk-provider/issues/new?title=Image+generation+support"
1136
+ );
2824
1137
  }
2825
1138
  };
2826
1139
 
2827
- // src/utils/remove-undefined.ts
2828
- function removeUndefinedEntries2(record) {
2829
- return Object.fromEntries(
2830
- Object.entries(record).filter(([, value]) => value !== null)
1140
+ // src/openrouter-provider.ts
1141
+ function createOpenRouter(options = {}) {
1142
+ const baseURL = withoutTrailingSlash(
1143
+ options.baseURL ?? options.baseUrl ?? "https://openrouter.ai/api/v1"
2831
1144
  );
2832
- }
2833
-
2834
- // src/utils/with-user-agent-suffix.ts
2835
- function withUserAgentSuffix(headers, ...userAgentSuffixParts) {
2836
- const cleanedHeaders = removeUndefinedEntries2(
2837
- headers != null ? headers : {}
1145
+ const getModelSettings = (modelOptions) => {
1146
+ const apiKey = loadApiKey({
1147
+ apiKey: options.apiKey,
1148
+ environmentVariableName: "OPENROUTER_API_KEY",
1149
+ description: "OpenRouter"
1150
+ });
1151
+ const providerUserAgentPart = `ai-sdk-provider/${"6.0.0-alpha.1"}`;
1152
+ const baseUserAgent = options.headers?.["user-agent"] ?? options.headers?.["User-Agent"] ?? SDK_METADATA.userAgent;
1153
+ const userAgent = baseUserAgent.includes("ai-sdk-provider/") ? baseUserAgent : `${baseUserAgent} ${providerUserAgentPart}`;
1154
+ const {
1155
+ "User-Agent": _ignoredUserAgent,
1156
+ "user-agent": _ignoredUserAgentLower,
1157
+ ...forwardHeaders
1158
+ } = options.headers ?? {};
1159
+ return {
1160
+ apiKey,
1161
+ baseURL,
1162
+ userAgent,
1163
+ headers: Object.keys(forwardHeaders).length > 0 ? forwardHeaders : void 0,
1164
+ fetch: options.fetch,
1165
+ extraBody: options.extraBody,
1166
+ modelOptions
1167
+ };
1168
+ };
1169
+ const languageModel = (modelId, modelOptions) => {
1170
+ return new OpenRouterChatLanguageModel(modelId, getModelSettings(modelOptions));
1171
+ };
1172
+ const embeddingModel = (modelId, modelOptions) => {
1173
+ return new OpenRouterEmbeddingModel(modelId, getModelSettings(modelOptions));
1174
+ };
1175
+ const imageModel = (modelId, modelOptions) => {
1176
+ return new OpenRouterImageModel(modelId, getModelSettings(modelOptions));
1177
+ };
1178
+ const provider = Object.assign(
1179
+ // Make provider callable - calling it directly creates a language model
1180
+ (modelId, modelOptions) => languageModel(modelId, modelOptions),
1181
+ {
1182
+ specificationVersion: "v3",
1183
+ languageModel,
1184
+ chat: languageModel,
1185
+ embeddingModel,
1186
+ textEmbeddingModel: embeddingModel,
1187
+ imageModel,
1188
+ image: imageModel,
1189
+ embedding: embeddingModel
1190
+ }
2838
1191
  );
2839
- const currentUserAgentHeader = cleanedHeaders["user-agent"] || "";
2840
- const newUserAgent = [currentUserAgentHeader, ...userAgentSuffixParts].filter(Boolean).join(" ");
2841
- return __spreadProps(__spreadValues({}, cleanedHeaders), {
2842
- "user-agent": newUserAgent
2843
- });
1192
+ return provider;
2844
1193
  }
2845
1194
 
2846
1195
  // src/version.ts
2847
- var VERSION = false ? "0.0.0-test" : "1.5.4";
1196
+ var VERSION = "6.0.0-alpha.0";
2848
1197
 
2849
- // src/provider.ts
2850
- function createOpenRouter(options = {}) {
2851
- var _a15, _b, _c;
2852
- const baseURL = (_b = withoutTrailingSlash((_a15 = options.baseURL) != null ? _a15 : options.baseUrl)) != null ? _b : "https://openrouter.ai/api/v1";
2853
- const compatibility = (_c = options.compatibility) != null ? _c : "compatible";
2854
- const getHeaders = () => withUserAgentSuffix(
2855
- __spreadValues(__spreadValues({
2856
- Authorization: `Bearer ${loadApiKey({
2857
- apiKey: options.apiKey,
2858
- environmentVariableName: "OPENROUTER_API_KEY",
2859
- description: "OpenRouter"
2860
- })}`
2861
- }, options.headers), options.api_keys && Object.keys(options.api_keys).length > 0 && {
2862
- "X-Provider-API-Keys": JSON.stringify(options.api_keys)
2863
- }),
2864
- `ai-sdk/openrouter/${VERSION}`
2865
- );
2866
- const createChatModel = (modelId, settings = {}) => new OpenRouterChatLanguageModel(modelId, settings, {
2867
- provider: "openrouter.chat",
2868
- url: ({ path }) => `${baseURL}${path}`,
2869
- headers: getHeaders,
2870
- compatibility,
2871
- fetch: options.fetch,
2872
- extraBody: options.extraBody
2873
- });
2874
- const createCompletionModel = (modelId, settings = {}) => new OpenRouterCompletionLanguageModel(modelId, settings, {
2875
- provider: "openrouter.completion",
2876
- url: ({ path }) => `${baseURL}${path}`,
2877
- headers: getHeaders,
2878
- compatibility,
2879
- fetch: options.fetch,
2880
- extraBody: options.extraBody
2881
- });
2882
- const createEmbeddingModel = (modelId, settings = {}) => new OpenRouterEmbeddingModel(modelId, settings, {
2883
- provider: "openrouter.embedding",
2884
- url: ({ path }) => `${baseURL}${path}`,
2885
- headers: getHeaders,
2886
- fetch: options.fetch,
2887
- extraBody: options.extraBody
2888
- });
2889
- const createLanguageModel = (modelId, settings) => {
2890
- if (new.target) {
2891
- throw new Error(
2892
- "The OpenRouter model function cannot be called with the new keyword."
2893
- );
2894
- }
2895
- if (modelId === "openai/gpt-3.5-turbo-instruct") {
2896
- return createCompletionModel(
2897
- modelId,
2898
- settings
2899
- );
2900
- }
2901
- return createChatModel(modelId, settings);
2902
- };
2903
- const provider = (modelId, settings) => createLanguageModel(modelId, settings);
2904
- provider.languageModel = createLanguageModel;
2905
- provider.chat = createChatModel;
2906
- provider.completion = createCompletionModel;
2907
- provider.textEmbeddingModel = createEmbeddingModel;
2908
- provider.embedding = createEmbeddingModel;
2909
- return provider;
2910
- }
2911
- var openrouter = createOpenRouter({
2912
- compatibility: "strict"
2913
- // strict for OpenRouter API
2914
- });
1198
+ // src/index.ts
1199
+ var openrouter = createOpenRouter();
2915
1200
  export {
2916
- OpenRouter,
1201
+ VERSION,
2917
1202
  createOpenRouter,
2918
1203
  openrouter
2919
1204
  };