@openrouter/ai-sdk-provider 1.5.4 → 6.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,2919 +1,1191 @@
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";
20
3
 
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;
4
+ // src/chat/openrouter-chat-language-model.ts
5
+ import { combineHeaders, normalizeHeaders } from "@ai-sdk/provider-utils";
6
+ import { OpenRouter } from "@openrouter/sdk";
299
7
 
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 };
8
+ // src/utils/build-provider-metadata.ts
9
+ function filterUndefined(obj) {
10
+ return Object.fromEntries(
11
+ Object.entries(obj).filter(([_, v]) => v !== void 0)
12
+ );
383
13
  }
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];
14
+ function buildProviderMetadata(response) {
15
+ if (!response) {
16
+ return void 0;
17
+ }
18
+ const usage = response.usage;
19
+ const usageMetadata = usage ? filterUndefined({
20
+ promptTokens: usage.promptTokens,
21
+ completionTokens: usage.completionTokens,
22
+ totalTokens: usage.totalTokens,
23
+ ...usage.promptTokensDetails && {
24
+ promptTokensDetails: filterUndefined({
25
+ cachedTokens: usage.promptTokensDetails.cachedTokens ?? void 0,
26
+ cacheWriteTokens: usage.promptTokensDetails.cacheWriteTokens ?? void 0,
27
+ audioTokens: usage.promptTokensDetails.audioTokens ?? void 0,
28
+ videoTokens: usage.promptTokensDetails.videoTokens ?? void 0
29
+ })
30
+ },
31
+ ...usage.completionTokensDetails && {
32
+ completionTokensDetails: filterUndefined({
33
+ reasoningTokens: usage.completionTokensDetails.reasoningTokens ?? void 0,
34
+ imageTokens: usage.completionTokensDetails.imageTokens ?? void 0
35
+ })
36
+ },
37
+ cost: usage.cost,
38
+ isByok: usage.isByok,
39
+ costDetails: usage.costDetails
40
+ }) : void 0;
41
+ const metadata = filterUndefined({
42
+ responseId: response.id,
43
+ provider: response.provider,
44
+ usage: usageMetadata
45
+ });
46
+ return {
47
+ openrouter: metadata
48
+ };
401
49
  }
402
50
 
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
- });
51
+ // src/utils/build-usage.ts
52
+ function buildUsage(usage) {
53
+ if (!usage) {
54
+ return {
55
+ inputTokens: {
56
+ total: 0,
57
+ noCache: void 0,
58
+ cacheRead: void 0,
59
+ cacheWrite: void 0
419
60
  },
420
- transform(chunk) {
421
- parser.feed(chunk);
422
- }
423
- });
61
+ outputTokens: {
62
+ total: 0,
63
+ text: void 0,
64
+ reasoning: void 0
65
+ },
66
+ raw: void 0
67
+ };
424
68
  }
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
- );
69
+ const rawUsage = {
70
+ inputTokens: usage.inputTokens ?? null,
71
+ outputTokens: usage.outputTokens ?? null,
72
+ ...usage.inputTokensDetails && {
73
+ inputTokensDetails: {
74
+ cachedTokens: usage.inputTokensDetails.cachedTokens ?? null
75
+ }
76
+ },
77
+ ...usage.outputTokensDetails && {
78
+ outputTokensDetails: {
79
+ reasoningTokens: usage.outputTokensDetails.reasoningTokens ?? null
80
+ }
81
+ }
82
+ };
83
+ return {
84
+ inputTokens: {
85
+ total: usage.inputTokens ?? 0,
86
+ noCache: void 0,
87
+ cacheRead: usage.inputTokensDetails?.cachedTokens,
88
+ cacheWrite: void 0
89
+ },
90
+ outputTokens: {
91
+ total: usage.outputTokens ?? 0,
92
+ text: void 0,
93
+ reasoning: usage.outputTokensDetails?.reasoningTokens
94
+ },
95
+ raw: rawUsage
96
+ };
451
97
  }
452
- function extractResponseHeaders(response) {
453
- return Object.fromEntries([...response.headers]);
98
+
99
+ // src/chat/convert-to-openrouter-messages.ts
100
+ function convertToOpenRouterMessages(prompt) {
101
+ const result = [];
102
+ for (const message of prompt) {
103
+ const converted = convertMessage(message);
104
+ result.push(...converted);
105
+ }
106
+ return result;
454
107
  }
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];
108
+ function convertMessage(message) {
109
+ const messageWithOptions = message;
110
+ const providerOptions = messageWithOptions.providerOptions;
111
+ const providerMetadata = messageWithOptions.providerMetadata;
112
+ switch (message.role) {
113
+ case "system":
114
+ return [{ role: "system", content: message.content }];
115
+ case "user":
116
+ return [convertUserMessage(message.content)];
117
+ case "assistant":
118
+ return convertAssistantMessage(message.content, providerMetadata, providerOptions);
119
+ case "tool":
120
+ return convertToolMessage(message.content);
121
+ default: {
122
+ const _exhaustive = message;
123
+ throw new Error(`Unknown message role: ${_exhaustive.role}`);
466
124
  }
467
- return chars.join("");
468
- };
469
- if (prefix == null) {
470
- return generator;
471
125
  }
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
126
  }
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
- });
127
+ function convertUserMessage(content) {
128
+ const convertedContent = [];
129
+ for (const part of content) {
130
+ switch (part.type) {
131
+ case "text": {
132
+ convertedContent.push({ type: "input_text", text: part.text });
133
+ break;
134
+ }
135
+ case "file":
136
+ convertedContent.push(convertFilePart(part));
137
+ break;
138
+ default: {
139
+ const _exhaustive = part;
140
+ throw new Error(`Unknown user content type: ${_exhaustive.type}`);
141
+ }
505
142
  }
506
143
  }
507
- return error;
144
+ return { role: "user", content: convertedContent };
508
145
  }
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
- });
146
+ function convertFilePart(part) {
147
+ const url = convertDataContent(part.data, part.mediaType);
148
+ if (part.mediaType.startsWith("image/")) {
149
+ return {
150
+ type: "input_image",
151
+ imageUrl: url,
152
+ detail: "auto"
153
+ };
527
154
  }
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
- });
155
+ return {
156
+ type: "input_file",
157
+ fileUrl: url
158
+ };
159
+ }
160
+ function convertDataContent(data, mediaType) {
161
+ if (data instanceof URL) {
162
+ return data.toString();
532
163
  }
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
- });
164
+ if (data instanceof Uint8Array) {
165
+ const base64 = uint8ArrayToBase64(data);
166
+ return `data:${mediaType};base64,${base64}`;
538
167
  }
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
- });
168
+ if (data.startsWith("http://") || data.startsWith("https://") || data.startsWith("data:")) {
169
+ return data;
543
170
  }
544
- return apiKey;
171
+ return `data:${mediaType};base64,${data}`;
545
172
  }
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;
173
+ function uint8ArrayToBase64(bytes) {
174
+ let binary = "";
175
+ for (const byte of bytes) {
176
+ binary += String.fromCharCode(byte);
552
177
  }
553
- if (suspectProtoRx.test(text) === false && suspectConstructorRx.test(text) === false) {
554
- return obj;
555
- }
556
- return filter(obj);
178
+ return btoa(binary);
557
179
  }
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
- }
180
+ function extractReasoningDetails(content, providerMetadata, providerOptions) {
181
+ const messageLevel = providerOptions?.openrouter?.reasoning_details ?? providerMetadata?.openrouter?.reasoning_details;
182
+ if (messageLevel && Array.isArray(messageLevel) && messageLevel.length > 0) {
183
+ return messageLevel;
184
+ }
185
+ for (const part of content) {
186
+ if (part.type === "reasoning") {
187
+ const partWithMeta = part;
188
+ const partLevel = partWithMeta.providerOptions?.openrouter?.reasoning_details ?? partWithMeta.providerMetadata?.openrouter?.reasoning_details;
189
+ if (partLevel && Array.isArray(partLevel) && partLevel.length > 0) {
190
+ return partLevel;
575
191
  }
576
192
  }
577
193
  }
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;
194
+ return void 0;
595
195
  }
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 };
196
+ function transformReasoningToApiFormat(sdkItems) {
197
+ const apiItems = [];
198
+ for (const rawItem of sdkItems) {
199
+ if (typeof rawItem !== "object" || rawItem === null) {
200
+ continue;
633
201
  }
634
- return {
635
- success: false,
636
- error: TypeValidationError.wrap({ value, cause: result.error }),
637
- rawValue: value
202
+ const item = rawItem;
203
+ const baseProps = {
204
+ id: item.id,
205
+ format: item.format ?? null
638
206
  };
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;
207
+ let index = item.index ?? 0;
208
+ if (item.type === "reasoning.text" && item.text !== void 0) {
209
+ apiItems.push({
210
+ type: "reasoning.text",
211
+ text: item.text,
212
+ signature: item.signature ?? null,
213
+ index,
214
+ ...baseProps
215
+ });
216
+ continue;
655
217
  }
656
- return validateTypes({ value, schema });
657
- } catch (error) {
658
- if (JSONParseError.isInstance(error) || TypeValidationError.isInstance(error)) {
659
- throw error;
218
+ if (item.type === "reasoning.summary" && item.summary !== void 0) {
219
+ apiItems.push({
220
+ type: "reasoning.summary",
221
+ summary: typeof item.summary === "string" ? item.summary : "",
222
+ index,
223
+ ...baseProps
224
+ });
225
+ continue;
660
226
  }
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 };
227
+ if (item.type === "reasoning.encrypted" && item.data !== void 0) {
228
+ apiItems.push({
229
+ type: "reasoning.encrypted",
230
+ data: item.data,
231
+ index,
232
+ ...baseProps
233
+ });
234
+ continue;
672
235
  }
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;
236
+ if (item.type === "reasoning" || item.content || item.summary || item.encryptedContent) {
237
+ if (item.content && Array.isArray(item.content)) {
238
+ for (const contentItem of item.content) {
239
+ if (contentItem.type === "reasoning_text" && contentItem.text) {
240
+ apiItems.push({
241
+ type: "reasoning.text",
242
+ text: contentItem.text,
243
+ signature: item.signature ?? null,
244
+ index: index++,
245
+ ...baseProps
246
+ });
247
+ }
699
248
  }
700
- controller.enqueue(await safeParseJSON({ text: data, schema }));
701
249
  }
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;
250
+ if (item.summary && Array.isArray(item.summary)) {
251
+ for (const summaryItem of item.summary) {
252
+ if (summaryItem.type === "summary_text" && summaryItem.text) {
253
+ apiItems.push({
254
+ type: "reasoning.summary",
255
+ summary: summaryItem.text,
256
+ index: index++,
257
+ ...baseProps
258
+ });
259
+ }
756
260
  }
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
261
  }
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
- }
262
+ if (item.encryptedContent) {
263
+ apiItems.push({
264
+ type: "reasoning.encrypted",
265
+ data: item.encryptedContent,
266
+ index: index++,
267
+ ...baseProps
268
+ });
779
269
  }
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
270
  }
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
271
  }
272
+ return apiItems;
1037
273
  }
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;
274
+ function buildReasoningFromDetails(items) {
275
+ if (items.length === 0) {
276
+ return void 0;
1062
277
  }
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}`;
278
+ const reasoning = {};
279
+ const textItems = items.filter((i) => i.type === "reasoning.text" && i.text);
280
+ if (textItems.length > 0) {
281
+ reasoning.text = textItems.map((i) => i.text).join("");
1074
282
  }
1075
- const stringUrl = part.data.toString();
1076
- if (isUrl({
1077
- url: stringUrl,
1078
- protocols: /* @__PURE__ */ new Set(["http:", "https:"])
1079
- })) {
1080
- return stringUrl;
283
+ const summaryItems = items.filter(
284
+ (i) => i.type === "reasoning.summary" && i.summary
285
+ );
286
+ if (summaryItems.length > 0) {
287
+ reasoning.summary = summaryItems.map((i) => i.summary).join("");
1081
288
  }
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
- );
289
+ const encryptedItem = items.find(
290
+ (i) => i.type === "reasoning.encrypted" && i.data
291
+ );
292
+ if (encryptedItem?.data) {
293
+ reasoning.encrypted = encryptedItem.data;
1142
294
  }
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
- );
295
+ if (!reasoning.text && !reasoning.summary && !reasoning.encrypted) {
296
+ return void 0;
1156
297
  }
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;
298
+ return reasoning;
1166
299
  }
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
- });
300
+ function convertAssistantMessage(content, providerMetadata, providerOptions) {
301
+ const result = [];
302
+ let textContent = "";
303
+ const sdkReasoningDetails = extractReasoningDetails(
304
+ content,
305
+ providerMetadata,
306
+ providerOptions
307
+ );
308
+ const reasoningItems = sdkReasoningDetails ? transformReasoningToApiFormat(sdkReasoningDetails) : [];
309
+ const reasoning = buildReasoningFromDetails(reasoningItems);
310
+ for (const part of content) {
311
+ switch (part.type) {
312
+ case "text":
313
+ textContent += part.text;
1178
314
  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
- });
315
+ case "reasoning":
316
+ textContent += part.text;
1273
317
  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)
318
+ case "tool-call":
319
+ result.push({
320
+ type: "function_call",
321
+ callId: part.toolCallId,
322
+ name: part.toolName,
323
+ arguments: typeof part.input === "string" ? part.input : JSON.stringify(part.input)
1332
324
  });
1333
325
  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
- }
326
+ case "file":
1345
327
  break;
1346
- }
1347
- default: {
328
+ case "tool-result":
329
+ result.push(convertToolResult(part));
1348
330
  break;
331
+ default: {
332
+ const _exhaustive = part;
333
+ throw new Error(
334
+ `Unknown assistant content type: ${_exhaustive.type}`
335
+ );
1349
336
  }
1350
337
  }
1351
338
  }
1352
- return messages;
339
+ if (textContent) {
340
+ const assistantMessage = {
341
+ role: "assistant",
342
+ content: textContent
343
+ };
344
+ if (reasoning) {
345
+ assistantMessage.reasoning = reasoning;
346
+ }
347
+ result.unshift(assistantMessage);
348
+ } else if (reasoning) {
349
+ const assistantMessage = {
350
+ role: "assistant",
351
+ content: ""
352
+ };
353
+ assistantMessage.reasoning = reasoning;
354
+ result.unshift(assistantMessage);
355
+ }
356
+ return result;
1353
357
  }
1354
- function getToolResultContent(input) {
1355
- return input.output.type === "text" ? input.output.value : JSON.stringify(input.output.value);
358
+ function convertToolMessage(content) {
359
+ const result = [];
360
+ for (const part of content) {
361
+ if (part.type === "tool-result") {
362
+ result.push(convertToolResult(part));
363
+ }
364
+ }
365
+ return result;
1356
366
  }
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": {
367
+ function convertToolResult(part) {
368
+ const output = convertToolResultOutput(part.output);
369
+ return {
370
+ type: "function_call_output",
371
+ callId: part.toolCallId,
372
+ output: output.value,
373
+ status: output.isError ? "incomplete" : "completed"
374
+ };
375
+ }
376
+ function convertToolResultOutput(output) {
377
+ switch (output.type) {
378
+ case "text":
379
+ return { value: output.value, isError: false };
380
+ case "json":
381
+ return { value: JSON.stringify(output.value), isError: false };
382
+ case "execution-denied":
1378
383
  return {
1379
- type: "function",
1380
- function: { name: toolChoice.toolName }
384
+ value: `Execution denied: ${output.reason ?? "No reason provided"}`,
385
+ isError: true
1381
386
  };
387
+ case "error-text":
388
+ return { value: output.value, isError: true };
389
+ case "error-json":
390
+ return { value: JSON.stringify(output.value), isError: true };
391
+ case "content": {
392
+ const textParts = output.value.filter((item) => item.type === "text").map((item) => item.text);
393
+ return { value: textParts.join("\n"), isError: false };
1382
394
  }
1383
395
  default: {
1384
- toolChoice;
1385
- throw new InvalidArgumentError({
1386
- argument: "toolChoice",
1387
- message: `Invalid tool choice type: ${JSON.stringify(toolChoice)}`
1388
- });
396
+ const _exhaustive = output;
397
+ throw new Error(`Unknown tool result output type: ${_exhaustive.type}`);
1389
398
  }
1390
399
  }
1391
400
  }
1392
401
 
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));
402
+ // src/chat/extract-reasoning-details.ts
403
+ function extractReasoningDetails2(response) {
404
+ const extractedDetails = [];
405
+ for (const outputItem of response.output) {
406
+ if ("type" in outputItem && outputItem.type === "reasoning") {
407
+ const reasoningItem = outputItem;
408
+ extractedDetails.push({
409
+ type: "reasoning",
410
+ id: reasoningItem.id,
411
+ content: reasoningItem.content,
412
+ summary: reasoningItem.summary,
413
+ encryptedContent: reasoningItem.encryptedContent,
414
+ signature: reasoningItem.signature,
415
+ format: reasoningItem.format
416
+ });
417
+ }
418
+ }
419
+ return extractedDetails.length > 0 ? extractedDetails : void 0;
420
+ }
421
+ function hasEncryptedReasoning(reasoningDetails) {
422
+ if (!reasoningDetails) {
423
+ return false;
424
+ }
425
+ return reasoningDetails.some((d) => {
426
+ if (typeof d === "object" && d !== null) {
427
+ const obj = d;
428
+ return obj.encryptedContent != null || obj.type === "reasoning.encrypted";
429
+ }
430
+ return false;
431
+ });
432
+ }
433
+ function buildReasoningProviderMetadata(reasoningDetails) {
434
+ if (!reasoningDetails || reasoningDetails.length === 0) {
435
+ return void 0;
436
+ }
437
+ return {
438
+ openrouter: {
439
+ reasoning_details: reasoningDetails
440
+ }
441
+ };
442
+ }
1409
443
 
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
- ]);
444
+ // src/chat/map-openrouter-finish-reason.ts
445
+ function mapOpenRouterFinishReason(finishReason) {
446
+ if (finishReason == null) {
447
+ return { unified: "other", raw: void 0 };
448
+ }
449
+ switch (finishReason) {
450
+ case "end_turn":
451
+ case "stop":
452
+ case "stop_sequence":
453
+ return { unified: "stop", raw: finishReason };
454
+ case "max_tokens":
455
+ case "length":
456
+ return { unified: "length", raw: finishReason };
457
+ case "tool_use":
458
+ case "tool_calls":
459
+ return { unified: "tool-calls", raw: finishReason };
460
+ case "content_filter":
461
+ return { unified: "content-filter", raw: finishReason };
462
+ case "error":
463
+ return { unified: "error", raw: finishReason };
464
+ default:
465
+ return { unified: "other", raw: finishReason };
466
+ }
467
+ }
1594
468
 
1595
- // src/chat/index.ts
469
+ // src/chat/openrouter-chat-language-model.ts
1596
470
  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
- };
471
+ specificationVersion = "v3";
472
+ provider = "openrouter";
473
+ modelId;
474
+ settings;
475
+ /**
476
+ * Supported URL patterns by media type.
477
+ * OpenRouter Chat API only supports image URLs natively.
478
+ * PDF URLs are not supported - use PDF data URIs or the Responses API instead.
479
+ */
480
+ supportedUrls = {
481
+ "image/*": [/^https?:\/\/.*$/]
482
+ };
483
+ constructor(modelId, settings) {
1610
484
  this.modelId = modelId;
1611
485
  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
486
  }
1690
487
  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
488
+ const warnings = [];
489
+ const client = new OpenRouter({
490
+ apiKey: this.settings.apiKey,
491
+ serverURL: this.settings.baseURL
1708
492
  });
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;
493
+ const openRouterInput = convertToOpenRouterMessages(options.prompt);
494
+ const tools = convertToolsToResponsesFormat(options.tools, warnings);
495
+ const toolChoice = convertToolChoiceToResponsesFormat(options.toolChoice);
496
+ const text = convertResponseFormatToText(options.responseFormat);
497
+ const requestParams = {
498
+ model: this.modelId,
499
+ input: openRouterInput,
500
+ stream: false,
501
+ ...options.maxOutputTokens !== void 0 && {
502
+ maxOutputTokens: options.maxOutputTokens
503
+ },
504
+ ...options.temperature !== void 0 && {
505
+ temperature: options.temperature
506
+ },
507
+ ...options.topP !== void 0 && { topP: options.topP },
508
+ ...tools.length > 0 && { tools },
509
+ ...toolChoice !== void 0 && { toolChoice },
510
+ ...text !== void 0 && { text }
511
+ };
512
+ const combinedHeaders = normalizeHeaders(
513
+ combineHeaders(this.settings.headers, options.headers)
514
+ );
515
+ const response = await client.beta.responses.send(requestParams, {
516
+ fetchOptions: {
517
+ signal: options.abortSignal,
518
+ headers: combinedHeaders
519
+ }
520
+ });
521
+ const content = [];
522
+ const reasoningDetails = extractReasoningDetails2(response);
523
+ const reasoningMetadata = buildReasoningProviderMetadata(reasoningDetails);
524
+ for (const outputItem of response.output) {
525
+ if (outputItem.type === "reasoning") {
526
+ const reasoningItem = outputItem;
527
+ 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("") || "";
528
+ if (reasoningText) {
529
+ content.push({
530
+ type: "reasoning",
531
+ text: reasoningText,
532
+ ...reasoningMetadata && { providerMetadata: reasoningMetadata }
533
+ });
1773
534
  }
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
- };
535
+ } else if (outputItem.type === "message") {
536
+ const messageItem = outputItem;
537
+ for (const contentItem of messageItem.content) {
538
+ if (contentItem.type === "output_text" && contentItem.text) {
539
+ content.push({
540
+ type: "text",
541
+ text: contentItem.text
542
+ });
1785
543
  }
1786
- break;
1787
544
  }
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) {
545
+ } else if (outputItem.type === "function_call") {
546
+ const functionCallItem = outputItem;
1809
547
  content.push({
1810
548
  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)
549
+ toolCallId: functionCallItem.callId,
550
+ toolName: functionCallItem.name,
551
+ // Default to empty object when arguments is undefined/empty
552
+ // (some providers omit arguments for tools with no parameters)
553
+ input: functionCallItem.arguments || "{}",
554
+ ...reasoningMetadata && { providerMetadata: reasoningMetadata }
1828
555
  });
1829
556
  }
1830
557
  }
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
- }
558
+ if (response.outputText && !content.some((c) => c.type === "text")) {
559
+ content.push({
560
+ type: "text",
561
+ text: response.outputText
562
+ });
1848
563
  }
1849
- const fileAnnotations = (_k = choice.message.annotations) == null ? void 0 : _k.filter(
1850
- (a) => a.type === "file"
564
+ let finishReason = mapOpenRouterFinishReason(
565
+ response.status === "completed" ? "stop" : response.status ?? "stop"
1851
566
  );
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
567
+ const hasToolCalls = content.some((c) => c.type === "tool-call");
568
+ if (hasToolCalls && hasEncryptedReasoning(reasoningDetails) && finishReason.unified === "stop") {
569
+ finishReason = { unified: "tool-calls", raw: finishReason.raw };
570
+ }
571
+ const usage = buildUsage(
572
+ response.usage ? {
573
+ inputTokens: response.usage.inputTokens,
574
+ outputTokens: response.usage.outputTokens
575
+ } : void 0
1855
576
  );
1856
- const shouldOverrideFinishReason = hasToolCalls && hasEncryptedReasoning && choice.finish_reason === "stop";
1857
- const effectiveFinishReason = shouldOverrideFinishReason ? "tool-calls" : mapOpenRouterFinishReason(choice.finish_reason);
577
+ const providerMetadata = buildProviderMetadata({
578
+ id: response.id,
579
+ provider: void 0,
580
+ // Responses API doesn't expose provider in response
581
+ usage: response.usage ? {
582
+ promptTokens: response.usage.inputTokens,
583
+ completionTokens: response.usage.outputTokens,
584
+ totalTokens: response.usage.totalTokens,
585
+ cost: response.usage.cost ?? void 0,
586
+ // Map inputTokensDetails -> promptTokensDetails
587
+ promptTokensDetails: response.usage.inputTokensDetails ? {
588
+ cachedTokens: response.usage.inputTokensDetails.cachedTokens
589
+ } : void 0,
590
+ // Map outputTokensDetails -> completionTokensDetails
591
+ completionTokensDetails: response.usage.outputTokensDetails ? {
592
+ reasoningTokens: response.usage.outputTokensDetails.reasoningTokens
593
+ } : void 0
594
+ } : void 0
595
+ });
1858
596
  return {
1859
597
  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
- })
598
+ finishReason,
599
+ usage,
600
+ warnings,
601
+ providerMetadata,
602
+ request: {
603
+ body: requestParams
1887
604
  },
1888
- request: { body: args },
1889
605
  response: {
1890
606
  id: response.id,
1891
- modelId: response.model,
1892
- headers: responseHeaders
607
+ timestamp: new Date(response.createdAt * 1e3),
608
+ modelId: response.model
1893
609
  }
1894
610
  };
1895
611
  }
1896
612
  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
613
+ const warnings = [];
614
+ const client = new OpenRouter({
615
+ apiKey: this.settings.apiKey,
616
+ serverURL: this.settings.baseURL
1920
617
  });
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
618
+ const openRouterInput = convertToOpenRouterMessages(options.prompt);
619
+ const tools = convertToolsToResponsesFormat(options.tools, warnings);
620
+ const toolChoice = convertToolChoiceToResponsesFormat(options.toolChoice);
621
+ const text = convertResponseFormatToText(options.responseFormat);
622
+ const requestParams = {
623
+ model: this.modelId,
624
+ input: openRouterInput,
625
+ stream: true,
626
+ ...options.maxOutputTokens !== void 0 && {
627
+ maxOutputTokens: options.maxOutputTokens
628
+ },
629
+ ...options.temperature !== void 0 && {
630
+ temperature: options.temperature
631
+ },
632
+ ...options.topP !== void 0 && { topP: options.topP },
633
+ ...tools.length > 0 && { tools },
634
+ ...toolChoice !== void 0 && { toolChoice },
635
+ ...text !== void 0 && { text }
1929
636
  };
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;
637
+ const combinedHeaders = normalizeHeaders(
638
+ combineHeaders(this.settings.headers, options.headers)
639
+ );
640
+ const eventStream = await client.beta.responses.send(requestParams, {
641
+ fetchOptions: {
642
+ signal: options.abortSignal,
643
+ headers: combinedHeaders
644
+ }
645
+ });
646
+ const state = createStreamState();
647
+ const transformedStream = new ReadableStream({
648
+ async start(controller) {
649
+ controller.enqueue({
650
+ type: "stream-start",
651
+ warnings
652
+ });
653
+ try {
654
+ for await (const event of eventStream) {
655
+ const parts = transformResponsesEvent(event, state);
656
+ for (const part of parts) {
657
+ controller.enqueue(part);
2295
658
  }
2296
- controller.enqueue({
2297
- type: "finish",
2298
- finishReason,
2299
- usage,
2300
- providerMetadata: {
2301
- openrouter: openrouterMetadata
2302
- }
2303
- });
2304
659
  }
2305
- })
2306
- ),
2307
- warnings: [],
2308
- request: { body: args },
2309
- response: { headers: responseHeaders }
660
+ } catch (error) {
661
+ controller.enqueue({
662
+ type: "error",
663
+ error
664
+ });
665
+ } finally {
666
+ controller.close();
667
+ }
668
+ }
669
+ });
670
+ return {
671
+ stream: transformedStream,
672
+ request: {
673
+ body: requestParams
674
+ }
2310
675
  };
2311
676
  }
2312
677
  };
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
678
+ function createStreamState() {
679
+ return {
680
+ responseId: void 0,
681
+ responseModel: void 0,
682
+ responseCreated: void 0,
683
+ textStarted: false,
684
+ textId: "text-0",
685
+ reasoningStarted: false,
686
+ reasoningId: "reasoning-0",
687
+ textEnded: false,
688
+ reasoningEnded: false,
689
+ sourceIds: [],
690
+ toolCalls: /* @__PURE__ */ new Map()
691
+ };
692
+ }
693
+ function transformResponsesEvent(event, state) {
694
+ const parts = [];
695
+ switch (event.type) {
696
+ // Response lifecycle events
697
+ case "response.created":
698
+ case "response.in_progress": {
699
+ if (event.response) {
700
+ state.responseId = event.response.id;
701
+ state.responseModel = event.response.model;
702
+ state.responseCreated = event.response.createdAt;
703
+ }
704
+ break;
705
+ }
706
+ // Text streaming
707
+ case "response.output_text.delta": {
708
+ if (!state.textStarted) {
709
+ state.textStarted = true;
710
+ parts.push({
711
+ type: "text-start",
712
+ id: state.textId
2337
713
  });
2338
714
  }
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;
715
+ if (event.delta && event.delta.length > 0) {
716
+ parts.push({
717
+ type: "text-delta",
718
+ id: state.textId,
719
+ delta: event.delta
720
+ });
2360
721
  }
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;
722
+ break;
723
+ }
724
+ case "response.output_text.done": {
725
+ if (state.textStarted && !state.textEnded) {
726
+ state.textEnded = true;
727
+ parts.push({
728
+ type: "text-end",
729
+ id: state.textId
730
+ });
2399
731
  }
2400
- case "tool": {
2401
- throw new UnsupportedFunctionalityError({
2402
- functionality: "tool messages"
732
+ break;
733
+ }
734
+ // Reasoning streaming
735
+ case "response.reasoning_text.delta": {
736
+ if (!state.reasoningStarted) {
737
+ state.reasoningStarted = true;
738
+ parts.push({
739
+ type: "reasoning-start",
740
+ id: state.reasoningId
2403
741
  });
2404
742
  }
2405
- default: {
2406
- break;
743
+ if (event.delta && event.delta.length > 0) {
744
+ parts.push({
745
+ type: "reasoning-delta",
746
+ id: state.reasoningId,
747
+ delta: event.delta
748
+ });
2407
749
  }
750
+ break;
2408
751
  }
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"
752
+ // Function call arguments streaming
753
+ case "response.function_call_arguments.delta": {
754
+ const toolCallId = event.itemId;
755
+ let toolState = state.toolCalls.get(toolCallId);
756
+ if (!toolState) {
757
+ toolState = { argumentsStarted: false };
758
+ state.toolCalls.set(toolCallId, toolState);
759
+ }
760
+ if (!toolState.argumentsStarted) {
761
+ toolState.argumentsStarted = true;
762
+ parts.push({
763
+ type: "tool-input-start",
764
+ id: toolCallId,
765
+ toolName: toolState.name ?? ""
766
+ // Will be filled in by output_item.added
767
+ });
768
+ }
769
+ if (event.delta && event.delta.length > 0) {
770
+ parts.push({
771
+ type: "tool-input-delta",
772
+ id: toolCallId,
773
+ delta: event.delta
774
+ });
775
+ }
776
+ break;
777
+ }
778
+ case "response.function_call_arguments.done": {
779
+ const toolCallId = event.itemId;
780
+ const toolState = state.toolCalls.get(toolCallId);
781
+ if (!toolState?.argumentsStarted) {
782
+ parts.push({
783
+ type: "tool-input-start",
784
+ id: toolCallId,
785
+ toolName: event.name
786
+ });
787
+ const args = event.arguments || "{}";
788
+ parts.push({
789
+ type: "tool-input-delta",
790
+ id: toolCallId,
791
+ delta: args
792
+ });
793
+ }
794
+ parts.push({
795
+ type: "tool-input-end",
796
+ id: toolCallId
797
+ });
798
+ parts.push({
799
+ type: "tool-call",
800
+ toolCallId,
801
+ toolName: event.name,
802
+ input: event.arguments || "{}"
803
+ });
804
+ break;
805
+ }
806
+ // Output item events (for function call metadata)
807
+ case "response.output_item.added": {
808
+ if (event.item.type === "function_call") {
809
+ const funcItem = event.item;
810
+ const toolCallId = funcItem.callId ?? `tool-${event.outputIndex}`;
811
+ const toolState = state.toolCalls.get(toolCallId) ?? {
812
+ argumentsStarted: false
813
+ };
814
+ toolState.name = funcItem.name;
815
+ state.toolCalls.set(toolCallId, toolState);
816
+ }
817
+ break;
818
+ }
819
+ // Annotation events (web search sources)
820
+ case "response.output_text.annotation.added": {
821
+ const annotation = event.annotation;
822
+ if (annotation.type === "url_citation") {
823
+ const urlAnnotation = annotation;
824
+ const sourceId = `source-${state.sourceIds.length}`;
825
+ state.sourceIds.push(sourceId);
826
+ parts.push({
827
+ type: "source",
828
+ sourceType: "url",
829
+ id: sourceId,
830
+ url: urlAnnotation.url,
831
+ title: urlAnnotation.title
832
+ });
833
+ }
834
+ break;
835
+ }
836
+ // Response completed - extract final usage data
837
+ case "response.completed": {
838
+ if (state.textStarted && !state.textEnded) {
839
+ state.textEnded = true;
840
+ parts.push({
841
+ type: "text-end",
842
+ id: state.textId
843
+ });
844
+ }
845
+ if (state.reasoningStarted && !state.reasoningEnded) {
846
+ state.reasoningEnded = true;
847
+ parts.push({
848
+ type: "reasoning-end",
849
+ id: state.reasoningId
850
+ });
851
+ }
852
+ const response = event.response;
853
+ parts.push({
854
+ type: "response-metadata",
855
+ id: response.id,
856
+ timestamp: response.createdAt ? new Date(response.createdAt * 1e3) : void 0,
857
+ modelId: response.model
858
+ });
859
+ const finishReason = mapOpenRouterFinishReason(
860
+ response.status === "completed" ? "stop" : response.status ?? "stop"
861
+ );
862
+ const usage = buildUsage(
863
+ response.usage ? {
864
+ inputTokens: response.usage.inputTokens,
865
+ outputTokens: response.usage.outputTokens
866
+ } : void 0
867
+ );
868
+ const providerMetadata = buildProviderMetadata({
869
+ id: response.id,
870
+ provider: void 0,
871
+ // Responses API doesn't expose provider
872
+ usage: response.usage ? {
873
+ promptTokens: response.usage.inputTokens,
874
+ completionTokens: response.usage.outputTokens,
875
+ totalTokens: response.usage.totalTokens,
876
+ cost: response.usage.cost ?? void 0,
877
+ // Map inputTokensDetails -> promptTokensDetails
878
+ promptTokensDetails: response.usage.inputTokensDetails ? {
879
+ cachedTokens: response.usage.inputTokensDetails.cachedTokens
880
+ } : void 0,
881
+ // Map outputTokensDetails -> completionTokensDetails
882
+ completionTokensDetails: response.usage.outputTokensDetails ? {
883
+ reasoningTokens: response.usage.outputTokensDetails.reasoningTokens
884
+ } : void 0
885
+ } : void 0
2496
886
  });
887
+ parts.push({
888
+ type: "finish",
889
+ finishReason,
890
+ usage,
891
+ providerMetadata
892
+ });
893
+ break;
2497
894
  }
2498
- if (toolChoice) {
2499
- throw new UnsupportedFunctionalityError({
2500
- functionality: "toolChoice"
895
+ // Response incomplete or failed
896
+ case "response.incomplete":
897
+ case "response.failed": {
898
+ if (state.textStarted && !state.textEnded) {
899
+ state.textEnded = true;
900
+ parts.push({
901
+ type: "text-end",
902
+ id: state.textId
903
+ });
904
+ }
905
+ if (state.reasoningStarted && !state.reasoningEnded) {
906
+ state.reasoningEnded = true;
907
+ parts.push({
908
+ type: "reasoning-end",
909
+ id: state.reasoningId
910
+ });
911
+ }
912
+ const response = event.response;
913
+ parts.push({
914
+ type: "response-metadata",
915
+ id: response.id,
916
+ timestamp: response.createdAt ? new Date(response.createdAt * 1e3) : void 0,
917
+ modelId: response.model
918
+ });
919
+ const finishReason = event.type === "response.failed" ? mapOpenRouterFinishReason("error") : mapOpenRouterFinishReason(response.status ?? "incomplete");
920
+ const usage = buildUsage(
921
+ response.usage ? {
922
+ inputTokens: response.usage.inputTokens,
923
+ outputTokens: response.usage.outputTokens
924
+ } : void 0
925
+ );
926
+ const providerMetadata = buildProviderMetadata({
927
+ id: response.id,
928
+ provider: void 0,
929
+ usage: response.usage ? {
930
+ promptTokens: response.usage.inputTokens,
931
+ completionTokens: response.usage.outputTokens,
932
+ totalTokens: response.usage.totalTokens,
933
+ cost: response.usage.cost ?? void 0,
934
+ // Map inputTokensDetails -> promptTokensDetails
935
+ promptTokensDetails: response.usage.inputTokensDetails ? {
936
+ cachedTokens: response.usage.inputTokensDetails.cachedTokens
937
+ } : void 0,
938
+ // Map outputTokensDetails -> completionTokensDetails
939
+ completionTokensDetails: response.usage.outputTokensDetails ? {
940
+ reasoningTokens: response.usage.outputTokensDetails.reasoningTokens
941
+ } : void 0
942
+ } : void 0
2501
943
  });
944
+ parts.push({
945
+ type: "finish",
946
+ finishReason,
947
+ usage,
948
+ providerMetadata
949
+ });
950
+ break;
2502
951
  }
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
952
+ // Error event
953
+ case "error": {
954
+ const errorEvent = event;
955
+ parts.push({
956
+ type: "error",
957
+ error: new Error(
958
+ errorEvent.error?.message ?? "Unknown streaming error"
959
+ )
2560
960
  });
961
+ break;
2561
962
  }
2562
- const choice = response.choices[0];
2563
- if (!choice) {
2564
- throw new NoContentGeneratedError({
2565
- message: "No choice in OpenRouter completion response"
963
+ // Ignored events (handled implicitly or not needed)
964
+ case "response.output_item.done":
965
+ case "response.content_part.added":
966
+ case "response.content_part.done":
967
+ case "response.refusal.delta":
968
+ case "response.refusal.done":
969
+ case "response.reasoning_text.done":
970
+ case "response.reasoning_summary_part.added":
971
+ case "response.reasoning_summary_part.done":
972
+ case "response.reasoning_summary_text.delta":
973
+ case "response.reasoning_summary_text.done":
974
+ case "response.image_generation_call.in_progress":
975
+ case "response.image_generation_call.generating":
976
+ case "response.image_generation_call.partial_image":
977
+ case "response.image_generation_call.completed":
978
+ break;
979
+ }
980
+ return parts;
981
+ }
982
+ function convertToolsToResponsesFormat(tools, warnings) {
983
+ if (!tools || tools.length === 0) {
984
+ return [];
985
+ }
986
+ return tools.map((tool) => {
987
+ if (tool.type !== "function") {
988
+ warnings.push({
989
+ type: "unsupported",
990
+ feature: `tool type '${tool.type}'`,
991
+ details: `Only 'function' type tools are supported. Tool '${tool.name ?? "unknown"}' has type '${tool.type}'.`
2566
992
  });
993
+ return null;
2567
994
  }
995
+ const functionTool = tool;
2568
996
  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
- }
997
+ type: "function",
998
+ name: functionTool.name,
999
+ description: functionTool.description ?? null,
1000
+ parameters: functionTool.inputSchema
2587
1001
  };
1002
+ }).filter((tool) => tool !== null);
1003
+ }
1004
+ function convertToolChoiceToResponsesFormat(toolChoice) {
1005
+ if (!toolChoice) {
1006
+ return void 0;
2588
1007
  }
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
1008
+ switch (toolChoice.type) {
1009
+ case "auto":
1010
+ return "auto";
1011
+ case "none":
1012
+ return "none";
1013
+ case "required":
1014
+ return "required";
1015
+ case "tool":
1016
+ return {
1017
+ type: "function",
1018
+ name: toolChoice.toolName
1019
+ };
1020
+ default:
1021
+ return void 0;
1022
+ }
1023
+ }
1024
+ function convertResponseFormatToText(responseFormat) {
1025
+ if (!responseFormat) {
1026
+ return void 0;
1027
+ }
1028
+ if (responseFormat.type === "text") {
1029
+ return {
1030
+ format: { type: "text" }
2618
1031
  };
2619
- const openrouterUsage = {};
1032
+ }
1033
+ if (responseFormat.type === "json") {
1034
+ if (responseFormat.schema) {
1035
+ return {
1036
+ format: {
1037
+ type: "json_schema",
1038
+ name: responseFormat.name ?? "response",
1039
+ description: responseFormat.description,
1040
+ schema: responseFormat.schema
1041
+ }
1042
+ };
1043
+ }
2620
1044
  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
- }
1045
+ format: { type: "json_object" }
2694
1046
  };
2695
1047
  }
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
- });
1048
+ return void 0;
1049
+ }
2717
1050
 
2718
- // src/embedding/index.ts
1051
+ // src/embedding/openrouter-embedding-model.ts
1052
+ import { combineHeaders as combineHeaders2, normalizeHeaders as normalizeHeaders2 } from "@ai-sdk/provider-utils";
1053
+ import { OpenRouter as OpenRouter2 } from "@openrouter/sdk";
2719
1054
  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;
1055
+ specificationVersion = "v3";
1056
+ provider = "openrouter";
1057
+ modelId;
1058
+ settings;
1059
+ /**
1060
+ * Maximum number of embeddings that can be generated in a single API call.
1061
+ * Set to 2048 as a reasonable default for most embedding models.
1062
+ */
1063
+ maxEmbeddingsPerCall = 2048;
1064
+ /**
1065
+ * Whether the model supports parallel calls.
1066
+ */
1067
+ supportsParallelCalls = true;
1068
+ constructor(modelId, settings) {
2725
1069
  this.modelId = modelId;
2726
1070
  this.settings = settings;
2727
- this.config = config;
2728
1071
  }
2729
1072
  async doEmbed(options) {
2730
- var _a15;
2731
- const { values, abortSignal, headers } = options;
2732
- const args = __spreadValues(__spreadValues({
1073
+ const warnings = [];
1074
+ const client = new OpenRouter2({
1075
+ apiKey: this.settings.apiKey,
1076
+ serverURL: this.settings.baseURL
1077
+ });
1078
+ const requestParams = {
2733
1079
  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
1080
+ input: options.values
1081
+ };
1082
+ const modelOptions = this.settings.modelOptions;
1083
+ if (modelOptions?.user) {
1084
+ requestParams.user = modelOptions.user;
1085
+ }
1086
+ if (modelOptions?.provider) {
1087
+ requestParams.provider = modelOptions.provider;
1088
+ }
1089
+ const combinedHeaders = normalizeHeaders2(
1090
+ combineHeaders2(this.settings.headers, options.headers)
1091
+ );
1092
+ const response = await client.embeddings.generate(requestParams, {
1093
+ fetchOptions: {
1094
+ signal: options.abortSignal,
1095
+ headers: combinedHeaders
1096
+ }
2751
1097
  });
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
1098
+ if (typeof response === "string") {
1099
+ throw new Error(`Unexpected string response from embeddings API: ${response}`);
1100
+ }
1101
+ const responseBody = response;
1102
+ const sortedData = [...responseBody.data].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
1103
+ const embeddings = sortedData.map((item) => {
1104
+ if (typeof item.embedding === "string") {
1105
+ throw new Error("Base64 encoded embeddings are not supported");
2765
1106
  }
1107
+ return item.embedding;
1108
+ });
1109
+ return {
1110
+ embeddings,
1111
+ usage: responseBody.usage ? { tokens: responseBody.usage.promptTokens } : void 0,
1112
+ warnings
2766
1113
  };
2767
1114
  }
2768
1115
  };
2769
1116
 
2770
- // src/facade.ts
2771
- var OpenRouter = class {
1117
+ // src/image/openrouter-image-model.ts
1118
+ var OpenRouterImageModel = class {
1119
+ specificationVersion = "v3";
1120
+ provider = "openrouter";
1121
+ modelId;
2772
1122
  /**
2773
- * Creates a new OpenRouter provider instance.
1123
+ * Maximum number of images that can be generated in a single API call.
2774
1124
  */
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
- }));
1125
+ maxImagesPerCall = 1;
1126
+ constructor(modelId, _settings) {
1127
+ this.modelId = modelId;
2818
1128
  }
2819
- /**
2820
- * @deprecated Use textEmbeddingModel instead
2821
- */
2822
- embedding(modelId, settings = {}) {
2823
- return this.textEmbeddingModel(modelId, settings);
1129
+ async doGenerate(_options) {
1130
+ throw new Error(
1131
+ "Image generation not yet supported. See: https://github.com/OpenRouterTeam/ai-sdk-provider/issues/new?title=Image+generation+support"
1132
+ );
2824
1133
  }
2825
1134
  };
2826
1135
 
2827
- // src/utils/remove-undefined.ts
2828
- function removeUndefinedEntries2(record) {
2829
- return Object.fromEntries(
2830
- Object.entries(record).filter(([, value]) => value !== null)
1136
+ // src/openrouter-provider.ts
1137
+ function createOpenRouter(options = {}) {
1138
+ const baseURL = withoutTrailingSlash(
1139
+ options.baseURL ?? options.baseUrl ?? "https://openrouter.ai/api/v1"
2831
1140
  );
2832
- }
2833
-
2834
- // src/utils/with-user-agent-suffix.ts
2835
- function withUserAgentSuffix(headers, ...userAgentSuffixParts) {
2836
- const cleanedHeaders = removeUndefinedEntries2(
2837
- headers != null ? headers : {}
1141
+ const getModelSettings = (modelOptions) => {
1142
+ const apiKey = loadApiKey({
1143
+ apiKey: options.apiKey,
1144
+ environmentVariableName: "OPENROUTER_API_KEY",
1145
+ description: "OpenRouter"
1146
+ });
1147
+ return {
1148
+ apiKey,
1149
+ baseURL,
1150
+ headers: options.headers,
1151
+ fetch: options.fetch,
1152
+ extraBody: options.extraBody,
1153
+ modelOptions
1154
+ };
1155
+ };
1156
+ const languageModel = (modelId, modelOptions) => {
1157
+ return new OpenRouterChatLanguageModel(modelId, getModelSettings(modelOptions));
1158
+ };
1159
+ const embeddingModel = (modelId, modelOptions) => {
1160
+ return new OpenRouterEmbeddingModel(modelId, getModelSettings(modelOptions));
1161
+ };
1162
+ const imageModel = (modelId, modelOptions) => {
1163
+ return new OpenRouterImageModel(modelId, getModelSettings(modelOptions));
1164
+ };
1165
+ const provider = Object.assign(
1166
+ // Make provider callable - calling it directly creates a language model
1167
+ (modelId, modelOptions) => languageModel(modelId, modelOptions),
1168
+ {
1169
+ specificationVersion: "v3",
1170
+ languageModel,
1171
+ chat: languageModel,
1172
+ embeddingModel,
1173
+ textEmbeddingModel: embeddingModel,
1174
+ imageModel,
1175
+ image: imageModel,
1176
+ embedding: embeddingModel
1177
+ }
2838
1178
  );
2839
- const currentUserAgentHeader = cleanedHeaders["user-agent"] || "";
2840
- const newUserAgent = [currentUserAgentHeader, ...userAgentSuffixParts].filter(Boolean).join(" ");
2841
- return __spreadProps(__spreadValues({}, cleanedHeaders), {
2842
- "user-agent": newUserAgent
2843
- });
1179
+ return provider;
2844
1180
  }
2845
1181
 
2846
1182
  // src/version.ts
2847
- var VERSION = false ? "0.0.0-test" : "1.5.4";
1183
+ var VERSION = "6.0.0-alpha.0";
2848
1184
 
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
- });
1185
+ // src/index.ts
1186
+ var openrouter = createOpenRouter();
2915
1187
  export {
2916
- OpenRouter,
1188
+ VERSION,
2917
1189
  createOpenRouter,
2918
1190
  openrouter
2919
1191
  };