@openrouter/ai-sdk-provider 2.0.4 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -4,6 +4,9 @@ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
4
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
6
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __typeError = (msg) => {
8
+ throw TypeError(msg);
9
+ };
7
10
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
11
  var __spreadValues = (a, b) => {
9
12
  for (var prop in b || (b = {}))
@@ -29,6 +32,9 @@ var __objRest = (source, exclude) => {
29
32
  }
30
33
  return target;
31
34
  };
35
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
36
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
37
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
32
38
 
33
39
  // node_modules/.pnpm/@ai-sdk+provider@3.0.0/node_modules/@ai-sdk/provider/dist/index.mjs
34
40
  var marker = "vercel.ai.error";
@@ -2316,6 +2322,54 @@ function createFinishReason(unified, raw) {
2316
2322
  return { unified, raw };
2317
2323
  }
2318
2324
 
2325
+ // src/utils/reasoning-details-duplicate-tracker.ts
2326
+ var _seenKeys;
2327
+ var ReasoningDetailsDuplicateTracker = class {
2328
+ constructor() {
2329
+ __privateAdd(this, _seenKeys, /* @__PURE__ */ new Set());
2330
+ }
2331
+ /**
2332
+ * Attempts to track a detail.
2333
+ * Returns true if this is a NEW detail (not seen before and has valid key),
2334
+ * false if it was skipped (no valid key) or already seen (duplicate).
2335
+ */
2336
+ upsert(detail) {
2337
+ const key = this.getCanonicalKey(detail);
2338
+ if (key === null) {
2339
+ return false;
2340
+ }
2341
+ if (__privateGet(this, _seenKeys).has(key)) {
2342
+ return false;
2343
+ }
2344
+ __privateGet(this, _seenKeys).add(key);
2345
+ return true;
2346
+ }
2347
+ getCanonicalKey(detail) {
2348
+ switch (detail.type) {
2349
+ case "reasoning.summary" /* Summary */:
2350
+ return detail.summary;
2351
+ case "reasoning.encrypted" /* Encrypted */:
2352
+ if (detail.id) {
2353
+ return detail.id;
2354
+ }
2355
+ return detail.data;
2356
+ case "reasoning.text" /* Text */: {
2357
+ if (detail.text) {
2358
+ return detail.text;
2359
+ }
2360
+ if (detail.signature) {
2361
+ return detail.signature;
2362
+ }
2363
+ return null;
2364
+ }
2365
+ default: {
2366
+ return null;
2367
+ }
2368
+ }
2369
+ }
2370
+ };
2371
+ _seenKeys = new WeakMap();
2372
+
2319
2373
  // src/types/openrouter-chat-completions-input.ts
2320
2374
  var OPENROUTER_AUDIO_FORMATS = [
2321
2375
  "wav",
@@ -2447,6 +2501,7 @@ function getCacheControl(providerMetadata) {
2447
2501
  function convertToOpenRouterChatMessages(prompt) {
2448
2502
  var _a16, _b16, _c, _d, _e, _f, _g, _h;
2449
2503
  const messages = [];
2504
+ const reasoningDetailsTracker = new ReasoningDetailsDuplicateTracker();
2450
2505
  for (const { role, content, providerOptions } of prompt) {
2451
2506
  switch (role) {
2452
2507
  case "system": {
@@ -2589,7 +2644,17 @@ function convertToOpenRouterChatMessages(prompt) {
2589
2644
  const parsedProviderOptions = OpenRouterProviderOptionsSchema.safeParse(providerOptions);
2590
2645
  const messageReasoningDetails = parsedProviderOptions.success ? (_e = (_d = parsedProviderOptions.data) == null ? void 0 : _d.openrouter) == null ? void 0 : _e.reasoning_details : void 0;
2591
2646
  const messageAnnotations = parsedProviderOptions.success ? (_g = (_f = parsedProviderOptions.data) == null ? void 0 : _f.openrouter) == null ? void 0 : _g.annotations : void 0;
2592
- const finalReasoningDetails = messageReasoningDetails && Array.isArray(messageReasoningDetails) && messageReasoningDetails.length > 0 ? messageReasoningDetails : findFirstReasoningDetails(content);
2647
+ const candidateReasoningDetails = messageReasoningDetails && Array.isArray(messageReasoningDetails) && messageReasoningDetails.length > 0 ? messageReasoningDetails : findFirstReasoningDetails(content);
2648
+ let finalReasoningDetails;
2649
+ if (candidateReasoningDetails && candidateReasoningDetails.length > 0) {
2650
+ const uniqueDetails = [];
2651
+ for (const detail of candidateReasoningDetails) {
2652
+ if (reasoningDetailsTracker.upsert(detail)) {
2653
+ uniqueDetails.push(detail);
2654
+ }
2655
+ }
2656
+ finalReasoningDetails = uniqueDetails.length > 0 ? uniqueDetails : void 0;
2657
+ }
2593
2658
  messages.push({
2594
2659
  role: "assistant",
2595
2660
  content: text,
@@ -2758,13 +2823,14 @@ var OpenRouterNonStreamChatCompletionResponseSchema = z7.union([
2758
2823
  annotations: z7.array(
2759
2824
  z7.union([
2760
2825
  // URL citation from web search
2826
+ // title, start_index, end_index are optional as some upstream providers may omit them
2761
2827
  z7.object({
2762
2828
  type: z7.literal("url_citation"),
2763
2829
  url_citation: z7.object({
2764
- end_index: z7.number(),
2765
- start_index: z7.number(),
2766
- title: z7.string(),
2767
2830
  url: z7.string(),
2831
+ title: z7.string().optional(),
2832
+ start_index: z7.number().optional(),
2833
+ end_index: z7.number().optional(),
2768
2834
  content: z7.string().optional()
2769
2835
  }).passthrough()
2770
2836
  }).passthrough(),
@@ -2841,13 +2907,14 @@ var OpenRouterStreamChatCompletionChunkSchema = z7.union([
2841
2907
  annotations: z7.array(
2842
2908
  z7.union([
2843
2909
  // URL citation from web search
2910
+ // title, start_index, end_index are optional as some upstream providers may omit them
2844
2911
  z7.object({
2845
2912
  type: z7.literal("url_citation"),
2846
2913
  url_citation: z7.object({
2847
- end_index: z7.number(),
2848
- start_index: z7.number(),
2849
- title: z7.string(),
2850
2914
  url: z7.string(),
2915
+ title: z7.string().optional(),
2916
+ start_index: z7.number().optional(),
2917
+ end_index: z7.number().optional(),
2851
2918
  content: z7.string().optional()
2852
2919
  }).passthrough()
2853
2920
  }).passthrough(),
@@ -2994,7 +3061,7 @@ var OpenRouterChatLanguageModel = class {
2994
3061
  return baseArgs;
2995
3062
  }
2996
3063
  async doGenerate(options) {
2997
- var _a16, _b16, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
3064
+ var _a16, _b16, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B;
2998
3065
  const providerOptions = options.providerOptions || {};
2999
3066
  const openrouterOptions = providerOptions.openrouter || {};
3000
3067
  const args = __spreadValues(__spreadValues({}, this.getArgs(options)), openrouterOptions);
@@ -3044,7 +3111,8 @@ var OpenRouterChatLanguageModel = class {
3044
3111
  total: (_d = response.usage.completion_tokens) != null ? _d : 0,
3045
3112
  text: void 0,
3046
3113
  reasoning: (_f = (_e = response.usage.completion_tokens_details) == null ? void 0 : _e.reasoning_tokens) != null ? _f : void 0
3047
- }
3114
+ },
3115
+ raw: response.usage
3048
3116
  } : {
3049
3117
  inputTokens: {
3050
3118
  total: 0,
@@ -3056,7 +3124,8 @@ var OpenRouterChatLanguageModel = class {
3056
3124
  total: 0,
3057
3125
  text: void 0,
3058
3126
  reasoning: void 0
3059
- }
3127
+ },
3128
+ raw: void 0
3060
3129
  };
3061
3130
  const reasoningDetails = (_g = choice.message.reasoning_details) != null ? _g : [];
3062
3131
  const reasoning = reasoningDetails.length > 0 ? reasoningDetails.map((detail) => {
@@ -3156,17 +3225,19 @@ var OpenRouterChatLanguageModel = class {
3156
3225
  sourceType: "url",
3157
3226
  id: annotation.url_citation.url,
3158
3227
  url: annotation.url_citation.url,
3159
- title: annotation.url_citation.title,
3228
+ title: (_j = annotation.url_citation.title) != null ? _j : "",
3160
3229
  providerMetadata: {
3161
3230
  openrouter: {
3162
- content: annotation.url_citation.content || ""
3231
+ content: (_k = annotation.url_citation.content) != null ? _k : "",
3232
+ startIndex: (_l = annotation.url_citation.start_index) != null ? _l : 0,
3233
+ endIndex: (_m = annotation.url_citation.end_index) != null ? _m : 0
3163
3234
  }
3164
3235
  }
3165
3236
  });
3166
3237
  }
3167
3238
  }
3168
3239
  }
3169
- const fileAnnotations = (_j = choice.message.annotations) == null ? void 0 : _j.filter(
3240
+ const fileAnnotations = (_n = choice.message.annotations) == null ? void 0 : _n.filter(
3170
3241
  (a) => a.type === "file"
3171
3242
  );
3172
3243
  const hasToolCalls = choice.message.tool_calls && choice.message.tool_calls.length > 0;
@@ -3174,7 +3245,7 @@ var OpenRouterChatLanguageModel = class {
3174
3245
  (d) => d.type === "reasoning.encrypted" /* Encrypted */ && d.data
3175
3246
  );
3176
3247
  const shouldOverrideFinishReason = hasToolCalls && hasEncryptedReasoning && choice.finish_reason === "stop";
3177
- const effectiveFinishReason = shouldOverrideFinishReason ? createFinishReason("tool-calls", (_k = choice.finish_reason) != null ? _k : void 0) : mapOpenRouterFinishReason(choice.finish_reason);
3248
+ const effectiveFinishReason = shouldOverrideFinishReason ? createFinishReason("tool-calls", (_o = choice.finish_reason) != null ? _o : void 0) : mapOpenRouterFinishReason(choice.finish_reason);
3178
3249
  return {
3179
3250
  content,
3180
3251
  finishReason: effectiveFinishReason,
@@ -3182,23 +3253,22 @@ var OpenRouterChatLanguageModel = class {
3182
3253
  warnings: [],
3183
3254
  providerMetadata: {
3184
3255
  openrouter: OpenRouterProviderMetadataSchema.parse({
3185
- provider: (_l = response.provider) != null ? _l : "",
3186
- reasoning_details: (_m = choice.message.reasoning_details) != null ? _m : [],
3256
+ provider: (_p = response.provider) != null ? _p : "",
3257
+ reasoning_details: (_q = choice.message.reasoning_details) != null ? _q : [],
3187
3258
  annotations: fileAnnotations && fileAnnotations.length > 0 ? fileAnnotations : void 0,
3188
- usage: __spreadValues(__spreadValues(__spreadValues({
3189
- promptTokens: (_n = usageInfo.inputTokens.total) != null ? _n : 0,
3190
- completionTokens: (_o = usageInfo.outputTokens.total) != null ? _o : 0,
3191
- totalTokens: ((_p = usageInfo.inputTokens.total) != null ? _p : 0) + ((_q = usageInfo.outputTokens.total) != null ? _q : 0),
3192
- cost: (_r = response.usage) == null ? void 0 : _r.cost
3193
- }, ((_t = (_s = response.usage) == null ? void 0 : _s.prompt_tokens_details) == null ? void 0 : _t.cached_tokens) != null ? {
3259
+ usage: __spreadValues(__spreadValues(__spreadValues(__spreadValues({
3260
+ promptTokens: (_r = usageInfo.inputTokens.total) != null ? _r : 0,
3261
+ completionTokens: (_s = usageInfo.outputTokens.total) != null ? _s : 0,
3262
+ totalTokens: ((_t = usageInfo.inputTokens.total) != null ? _t : 0) + ((_u = usageInfo.outputTokens.total) != null ? _u : 0)
3263
+ }, ((_v = response.usage) == null ? void 0 : _v.cost) != null ? { cost: response.usage.cost } : {}), ((_x = (_w = response.usage) == null ? void 0 : _w.prompt_tokens_details) == null ? void 0 : _x.cached_tokens) != null ? {
3194
3264
  promptTokensDetails: {
3195
3265
  cachedTokens: response.usage.prompt_tokens_details.cached_tokens
3196
3266
  }
3197
- } : {}), ((_v = (_u = response.usage) == null ? void 0 : _u.completion_tokens_details) == null ? void 0 : _v.reasoning_tokens) != null ? {
3267
+ } : {}), ((_z = (_y = response.usage) == null ? void 0 : _y.completion_tokens_details) == null ? void 0 : _z.reasoning_tokens) != null ? {
3198
3268
  completionTokensDetails: {
3199
3269
  reasoningTokens: response.usage.completion_tokens_details.reasoning_tokens
3200
3270
  }
3201
- } : {}), ((_x = (_w = response.usage) == null ? void 0 : _w.cost_details) == null ? void 0 : _x.upstream_inference_cost) != null ? {
3271
+ } : {}), ((_B = (_A = response.usage) == null ? void 0 : _A.cost_details) == null ? void 0 : _B.upstream_inference_cost) != null ? {
3202
3272
  costDetails: {
3203
3273
  upstreamInferenceCost: response.usage.cost_details.upstream_inference_cost
3204
3274
  }
@@ -3251,9 +3321,11 @@ var OpenRouterChatLanguageModel = class {
3251
3321
  total: void 0,
3252
3322
  text: void 0,
3253
3323
  reasoning: void 0
3254
- }
3324
+ },
3325
+ raw: void 0
3255
3326
  };
3256
3327
  const openrouterUsage = {};
3328
+ let rawUsage;
3257
3329
  const accumulatedReasoningDetails = [];
3258
3330
  let reasoningDetailsAttachedToToolCall = false;
3259
3331
  const accumulatedFileAnnotations = [];
@@ -3267,7 +3339,10 @@ var OpenRouterChatLanguageModel = class {
3267
3339
  stream: response.pipeThrough(
3268
3340
  new TransformStream({
3269
3341
  transform(chunk, controller) {
3270
- var _a17, _b16, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
3342
+ var _a17, _b16, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s;
3343
+ if (options.includeRawChunks) {
3344
+ controller.enqueue({ type: "raw", rawValue: chunk.rawValue });
3345
+ }
3271
3346
  if (!chunk.success) {
3272
3347
  finishReason = createFinishReason("error");
3273
3348
  controller.enqueue({ type: "error", error: chunk.error });
@@ -3298,6 +3373,7 @@ var OpenRouterChatLanguageModel = class {
3298
3373
  if (value.usage != null) {
3299
3374
  usage.inputTokens.total = value.usage.prompt_tokens;
3300
3375
  usage.outputTokens.total = value.usage.completion_tokens;
3376
+ rawUsage = value.usage;
3301
3377
  openrouterUsage.promptTokens = value.usage.prompt_tokens;
3302
3378
  if (value.usage.prompt_tokens_details) {
3303
3379
  const cachedInputTokens = (_a17 = value.usage.prompt_tokens_details.cached_tokens) != null ? _a17 : 0;
@@ -3314,7 +3390,9 @@ var OpenRouterChatLanguageModel = class {
3314
3390
  reasoningTokens
3315
3391
  };
3316
3392
  }
3317
- openrouterUsage.cost = value.usage.cost;
3393
+ if (value.usage.cost != null) {
3394
+ openrouterUsage.cost = value.usage.cost;
3395
+ }
3318
3396
  openrouterUsage.totalTokens = value.usage.total_tokens;
3319
3397
  const upstreamInferenceCost = (_c = value.usage.cost_details) == null ? void 0 : _c.upstream_inference_cost;
3320
3398
  if (upstreamInferenceCost != null) {
@@ -3427,10 +3505,12 @@ var OpenRouterChatLanguageModel = class {
3427
3505
  sourceType: "url",
3428
3506
  id: annotation.url_citation.url,
3429
3507
  url: annotation.url_citation.url,
3430
- title: annotation.url_citation.title,
3508
+ title: (_d = annotation.url_citation.title) != null ? _d : "",
3431
3509
  providerMetadata: {
3432
3510
  openrouter: {
3433
- content: annotation.url_citation.content || ""
3511
+ content: (_e = annotation.url_citation.content) != null ? _e : "",
3512
+ startIndex: (_f = annotation.url_citation.start_index) != null ? _f : 0,
3513
+ endIndex: (_g = annotation.url_citation.end_index) != null ? _g : 0
3434
3514
  }
3435
3515
  }
3436
3516
  });
@@ -3446,7 +3526,7 @@ var OpenRouterChatLanguageModel = class {
3446
3526
  }
3447
3527
  if (delta.tool_calls != null) {
3448
3528
  for (const toolCallDelta of delta.tool_calls) {
3449
- const index = (_d = toolCallDelta.index) != null ? _d : toolCalls.length - 1;
3529
+ const index = (_h = toolCallDelta.index) != null ? _h : toolCalls.length - 1;
3450
3530
  if (toolCalls[index] == null) {
3451
3531
  if (toolCallDelta.type !== "function") {
3452
3532
  throw new InvalidResponseDataError({
@@ -3460,7 +3540,7 @@ var OpenRouterChatLanguageModel = class {
3460
3540
  message: `Expected 'id' to be a string.`
3461
3541
  });
3462
3542
  }
3463
- if (((_e = toolCallDelta.function) == null ? void 0 : _e.name) == null) {
3543
+ if (((_i = toolCallDelta.function) == null ? void 0 : _i.name) == null) {
3464
3544
  throw new InvalidResponseDataError({
3465
3545
  data: toolCallDelta,
3466
3546
  message: `Expected 'function.name' to be a string.`
@@ -3471,7 +3551,7 @@ var OpenRouterChatLanguageModel = class {
3471
3551
  type: "function",
3472
3552
  function: {
3473
3553
  name: toolCallDelta.function.name,
3474
- arguments: (_f = toolCallDelta.function.arguments) != null ? _f : ""
3554
+ arguments: (_j = toolCallDelta.function.arguments) != null ? _j : ""
3475
3555
  },
3476
3556
  inputStarted: false,
3477
3557
  sent: false
@@ -3483,7 +3563,7 @@ var OpenRouterChatLanguageModel = class {
3483
3563
  message: `Tool call at index ${index} is missing after creation.`
3484
3564
  });
3485
3565
  }
3486
- if (((_g = toolCall2.function) == null ? void 0 : _g.name) != null && ((_h = toolCall2.function) == null ? void 0 : _h.arguments) != null && isParsableJson(toolCall2.function.arguments)) {
3566
+ if (((_k = toolCall2.function) == null ? void 0 : _k.name) != null && ((_l = toolCall2.function) == null ? void 0 : _l.arguments) != null && isParsableJson(toolCall2.function.arguments)) {
3487
3567
  toolCall2.inputStarted = true;
3488
3568
  controller.enqueue({
3489
3569
  type: "tool-input-start",
@@ -3534,18 +3614,18 @@ var OpenRouterChatLanguageModel = class {
3534
3614
  toolName: toolCall.function.name
3535
3615
  });
3536
3616
  }
3537
- if (((_i = toolCallDelta.function) == null ? void 0 : _i.arguments) != null) {
3538
- toolCall.function.arguments += (_k = (_j = toolCallDelta.function) == null ? void 0 : _j.arguments) != null ? _k : "";
3617
+ if (((_m = toolCallDelta.function) == null ? void 0 : _m.arguments) != null) {
3618
+ toolCall.function.arguments += (_o = (_n = toolCallDelta.function) == null ? void 0 : _n.arguments) != null ? _o : "";
3539
3619
  }
3540
3620
  controller.enqueue({
3541
3621
  type: "tool-input-delta",
3542
3622
  id: toolCall.id,
3543
- delta: (_l = toolCallDelta.function.arguments) != null ? _l : ""
3623
+ delta: (_p = toolCallDelta.function.arguments) != null ? _p : ""
3544
3624
  });
3545
- if (((_m = toolCall.function) == null ? void 0 : _m.name) != null && ((_n = toolCall.function) == null ? void 0 : _n.arguments) != null && isParsableJson(toolCall.function.arguments)) {
3625
+ if (((_q = toolCall.function) == null ? void 0 : _q.name) != null && ((_r = toolCall.function) == null ? void 0 : _r.arguments) != null && isParsableJson(toolCall.function.arguments)) {
3546
3626
  controller.enqueue({
3547
3627
  type: "tool-call",
3548
- toolCallId: (_o = toolCall.id) != null ? _o : generateId(),
3628
+ toolCallId: (_s = toolCall.id) != null ? _s : generateId(),
3549
3629
  toolName: toolCall.function.name,
3550
3630
  input: toolCall.function.arguments,
3551
3631
  providerMetadata: !reasoningDetailsAttachedToToolCall ? {
@@ -3622,6 +3702,7 @@ var OpenRouterChatLanguageModel = class {
3622
3702
  if (accumulatedFileAnnotations.length > 0) {
3623
3703
  openrouterMetadata.annotations = accumulatedFileAnnotations;
3624
3704
  }
3705
+ usage.raw = rawUsage;
3625
3706
  controller.enqueue({
3626
3707
  type: "finish",
3627
3708
  finishReason,
@@ -3749,6 +3830,7 @@ var OpenRouterCompletionChunkSchema = z8.union([
3749
3830
  z8.object({
3750
3831
  id: z8.string().optional(),
3751
3832
  model: z8.string().optional(),
3833
+ provider: z8.string().optional(),
3752
3834
  choices: z8.array(
3753
3835
  z8.object({
3754
3836
  text: z8.string(),
@@ -3856,7 +3938,7 @@ var OpenRouterCompletionLanguageModel = class {
3856
3938
  }, this.config.extraBody), this.settings.extraBody);
3857
3939
  }
3858
3940
  async doGenerate(options) {
3859
- var _a16, _b16, _c, _d, _e, _f, _g, _h, _i, _j, _k;
3941
+ var _a16, _b16, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B;
3860
3942
  const providerOptions = options.providerOptions || {};
3861
3943
  const openrouterOptions = providerOptions.openrouter || {};
3862
3944
  const args = __spreadValues(__spreadValues({}, this.getArgs(options)), openrouterOptions);
@@ -3913,9 +3995,32 @@ var OpenRouterCompletionLanguageModel = class {
3913
3995
  total: (_h = (_g = response.usage) == null ? void 0 : _g.completion_tokens) != null ? _h : 0,
3914
3996
  text: void 0,
3915
3997
  reasoning: (_k = (_j = (_i = response.usage) == null ? void 0 : _i.completion_tokens_details) == null ? void 0 : _j.reasoning_tokens) != null ? _k : void 0
3916
- }
3998
+ },
3999
+ raw: (_l = response.usage) != null ? _l : void 0
3917
4000
  },
3918
4001
  warnings: [],
4002
+ providerMetadata: {
4003
+ openrouter: OpenRouterProviderMetadataSchema.parse({
4004
+ provider: (_m = response.provider) != null ? _m : "",
4005
+ usage: __spreadValues(__spreadValues(__spreadValues(__spreadValues({
4006
+ promptTokens: (_o = (_n = response.usage) == null ? void 0 : _n.prompt_tokens) != null ? _o : 0,
4007
+ completionTokens: (_q = (_p = response.usage) == null ? void 0 : _p.completion_tokens) != null ? _q : 0,
4008
+ totalTokens: ((_s = (_r = response.usage) == null ? void 0 : _r.prompt_tokens) != null ? _s : 0) + ((_u = (_t = response.usage) == null ? void 0 : _t.completion_tokens) != null ? _u : 0)
4009
+ }, ((_v = response.usage) == null ? void 0 : _v.cost) != null ? { cost: response.usage.cost } : {}), ((_x = (_w = response.usage) == null ? void 0 : _w.prompt_tokens_details) == null ? void 0 : _x.cached_tokens) != null ? {
4010
+ promptTokensDetails: {
4011
+ cachedTokens: response.usage.prompt_tokens_details.cached_tokens
4012
+ }
4013
+ } : {}), ((_z = (_y = response.usage) == null ? void 0 : _y.completion_tokens_details) == null ? void 0 : _z.reasoning_tokens) != null ? {
4014
+ completionTokensDetails: {
4015
+ reasoningTokens: response.usage.completion_tokens_details.reasoning_tokens
4016
+ }
4017
+ } : {}), ((_B = (_A = response.usage) == null ? void 0 : _A.cost_details) == null ? void 0 : _B.upstream_inference_cost) != null ? {
4018
+ costDetails: {
4019
+ upstreamInferenceCost: response.usage.cost_details.upstream_inference_cost
4020
+ }
4021
+ } : {})
4022
+ })
4023
+ },
3919
4024
  response: {
3920
4025
  headers: responseHeaders
3921
4026
  }
@@ -3955,14 +4060,20 @@ var OpenRouterCompletionLanguageModel = class {
3955
4060
  total: void 0,
3956
4061
  text: void 0,
3957
4062
  reasoning: void 0
3958
- }
4063
+ },
4064
+ raw: void 0
3959
4065
  };
3960
4066
  const openrouterUsage = {};
4067
+ let provider;
4068
+ let rawUsage;
3961
4069
  return {
3962
4070
  stream: response.pipeThrough(
3963
4071
  new TransformStream({
3964
4072
  transform(chunk, controller) {
3965
4073
  var _a16, _b16, _c;
4074
+ if (options.includeRawChunks) {
4075
+ controller.enqueue({ type: "raw", rawValue: chunk.rawValue });
4076
+ }
3966
4077
  if (!chunk.success) {
3967
4078
  finishReason = createFinishReason("error");
3968
4079
  controller.enqueue({ type: "error", error: chunk.error });
@@ -3974,9 +4085,13 @@ var OpenRouterCompletionLanguageModel = class {
3974
4085
  controller.enqueue({ type: "error", error: value.error });
3975
4086
  return;
3976
4087
  }
4088
+ if (value.provider) {
4089
+ provider = value.provider;
4090
+ }
3977
4091
  if (value.usage != null) {
3978
4092
  usage.inputTokens.total = value.usage.prompt_tokens;
3979
4093
  usage.outputTokens.total = value.usage.completion_tokens;
4094
+ rawUsage = value.usage;
3980
4095
  openrouterUsage.promptTokens = value.usage.prompt_tokens;
3981
4096
  if (value.usage.prompt_tokens_details) {
3982
4097
  const cachedInputTokens = (_a16 = value.usage.prompt_tokens_details.cached_tokens) != null ? _a16 : 0;
@@ -3993,7 +4108,9 @@ var OpenRouterCompletionLanguageModel = class {
3993
4108
  reasoningTokens
3994
4109
  };
3995
4110
  }
3996
- openrouterUsage.cost = value.usage.cost;
4111
+ if (value.usage.cost != null) {
4112
+ openrouterUsage.cost = value.usage.cost;
4113
+ }
3997
4114
  openrouterUsage.totalTokens = value.usage.total_tokens;
3998
4115
  const upstreamInferenceCost = (_c = value.usage.cost_details) == null ? void 0 : _c.upstream_inference_cost;
3999
4116
  if (upstreamInferenceCost != null) {
@@ -4015,14 +4132,19 @@ var OpenRouterCompletionLanguageModel = class {
4015
4132
  }
4016
4133
  },
4017
4134
  flush(controller) {
4135
+ usage.raw = rawUsage;
4136
+ const openrouterMetadata = {
4137
+ usage: openrouterUsage
4138
+ };
4139
+ if (provider !== void 0) {
4140
+ openrouterMetadata.provider = provider;
4141
+ }
4018
4142
  controller.enqueue({
4019
4143
  type: "finish",
4020
4144
  finishReason,
4021
4145
  usage,
4022
4146
  providerMetadata: {
4023
- openrouter: {
4024
- usage: openrouterUsage
4025
- }
4147
+ openrouter: openrouterMetadata
4026
4148
  }
4027
4149
  });
4028
4150
  }
@@ -4052,6 +4174,7 @@ var OpenRouterEmbeddingResponseSchema = z9.object({
4052
4174
  object: z9.literal("list"),
4053
4175
  data: z9.array(openrouterEmbeddingDataSchema),
4054
4176
  model: z9.string(),
4177
+ provider: z9.string().optional(),
4055
4178
  usage: openrouterEmbeddingUsageSchema.optional()
4056
4179
  });
4057
4180
 
@@ -4067,7 +4190,7 @@ var OpenRouterEmbeddingModel = class {
4067
4190
  this.config = config;
4068
4191
  }
4069
4192
  async doEmbed(options) {
4070
- var _a16;
4193
+ var _a16, _b16, _c, _d, _e, _f;
4071
4194
  const { values, abortSignal, headers } = options;
4072
4195
  const args = __spreadValues(__spreadValues({
4073
4196
  model: this.modelId,
@@ -4092,13 +4215,16 @@ var OpenRouterEmbeddingModel = class {
4092
4215
  return {
4093
4216
  embeddings: responseValue.data.map((item) => item.embedding),
4094
4217
  usage: responseValue.usage ? { tokens: responseValue.usage.prompt_tokens } : void 0,
4095
- providerMetadata: ((_a16 = responseValue.usage) == null ? void 0 : _a16.cost) ? {
4096
- openrouter: {
4097
- usage: {
4098
- cost: responseValue.usage.cost
4099
- }
4100
- }
4101
- } : void 0,
4218
+ providerMetadata: {
4219
+ openrouter: OpenRouterProviderMetadataSchema.parse({
4220
+ provider: (_a16 = responseValue.provider) != null ? _a16 : "",
4221
+ usage: __spreadValues({
4222
+ promptTokens: (_c = (_b16 = responseValue.usage) == null ? void 0 : _b16.prompt_tokens) != null ? _c : 0,
4223
+ completionTokens: 0,
4224
+ totalTokens: (_e = (_d = responseValue.usage) == null ? void 0 : _d.total_tokens) != null ? _e : 0
4225
+ }, ((_f = responseValue.usage) == null ? void 0 : _f.cost) != null ? { cost: responseValue.usage.cost } : {})
4226
+ })
4227
+ },
4102
4228
  response: {
4103
4229
  headers: responseHeaders,
4104
4230
  body: responseValue
@@ -4165,27 +4291,188 @@ var OpenRouter = class {
4165
4291
  }
4166
4292
  };
4167
4293
 
4294
+ // src/image/schemas.ts
4295
+ import { z as z10 } from "zod/v4";
4296
+ var OpenRouterImageResponseSchema = z10.object({
4297
+ id: z10.string().optional(),
4298
+ object: z10.string().optional(),
4299
+ created: z10.number().optional(),
4300
+ model: z10.string(),
4301
+ choices: z10.array(
4302
+ z10.object({
4303
+ index: z10.number(),
4304
+ message: z10.object({
4305
+ role: z10.string(),
4306
+ content: z10.string().nullable().optional(),
4307
+ images: z10.array(
4308
+ z10.object({
4309
+ type: z10.literal("image_url"),
4310
+ image_url: z10.object({
4311
+ url: z10.string()
4312
+ })
4313
+ }).passthrough()
4314
+ ).optional()
4315
+ }).passthrough(),
4316
+ finish_reason: z10.string().nullable().optional()
4317
+ }).passthrough()
4318
+ ),
4319
+ usage: z10.object({
4320
+ prompt_tokens: z10.number(),
4321
+ completion_tokens: z10.number(),
4322
+ total_tokens: z10.number()
4323
+ }).passthrough().optional()
4324
+ }).passthrough();
4325
+
4326
+ // src/image/index.ts
4327
+ var OpenRouterImageModel = class {
4328
+ constructor(modelId, settings, config) {
4329
+ this.specificationVersion = "v3";
4330
+ this.provider = "openrouter";
4331
+ this.maxImagesPerCall = 1;
4332
+ this.modelId = modelId;
4333
+ this.settings = settings;
4334
+ this.config = config;
4335
+ }
4336
+ async doGenerate(options) {
4337
+ var _a16;
4338
+ const {
4339
+ prompt,
4340
+ n,
4341
+ size,
4342
+ aspectRatio,
4343
+ seed,
4344
+ files,
4345
+ mask,
4346
+ abortSignal,
4347
+ headers,
4348
+ providerOptions
4349
+ } = options;
4350
+ const openrouterOptions = (providerOptions == null ? void 0 : providerOptions.openrouter) || {};
4351
+ const warnings = [];
4352
+ if (files !== void 0 && files.length > 0) {
4353
+ throw new UnsupportedFunctionalityError({
4354
+ functionality: "image editing (files parameter)"
4355
+ });
4356
+ }
4357
+ if (mask !== void 0) {
4358
+ throw new UnsupportedFunctionalityError({
4359
+ functionality: "image inpainting (mask parameter)"
4360
+ });
4361
+ }
4362
+ if (n > 1) {
4363
+ warnings.push({
4364
+ type: "unsupported",
4365
+ feature: "n > 1",
4366
+ details: `OpenRouter image generation returns 1 image per call. Requested ${n} images.`
4367
+ });
4368
+ }
4369
+ if (size !== void 0) {
4370
+ warnings.push({
4371
+ type: "unsupported",
4372
+ feature: "size",
4373
+ details: "Use aspectRatio instead. Size parameter is not supported by OpenRouter image generation."
4374
+ });
4375
+ }
4376
+ const imageConfig = aspectRatio !== void 0 ? { aspect_ratio: aspectRatio } : void 0;
4377
+ const body = __spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues({
4378
+ model: this.modelId,
4379
+ messages: [
4380
+ {
4381
+ role: "user",
4382
+ content: prompt != null ? prompt : ""
4383
+ }
4384
+ ],
4385
+ modalities: ["image", "text"]
4386
+ }, imageConfig !== void 0 && { image_config: imageConfig }), seed !== void 0 && { seed }), this.settings.user !== void 0 && { user: this.settings.user }), this.settings.provider !== void 0 && {
4387
+ provider: this.settings.provider
4388
+ }), this.config.extraBody), this.settings.extraBody), openrouterOptions);
4389
+ const { value: responseValue, responseHeaders } = await postJsonToApi({
4390
+ url: this.config.url({
4391
+ path: "/chat/completions",
4392
+ modelId: this.modelId
4393
+ }),
4394
+ headers: combineHeaders(this.config.headers(), headers),
4395
+ body,
4396
+ failedResponseHandler: openrouterFailedResponseHandler,
4397
+ successfulResponseHandler: createJsonResponseHandler(
4398
+ OpenRouterImageResponseSchema
4399
+ ),
4400
+ abortSignal,
4401
+ fetch: this.config.fetch
4402
+ });
4403
+ const choice = responseValue.choices[0];
4404
+ if (!choice) {
4405
+ throw new NoContentGeneratedError({
4406
+ message: "No choice in response"
4407
+ });
4408
+ }
4409
+ const images = [];
4410
+ if ((_a16 = choice.message) == null ? void 0 : _a16.images) {
4411
+ for (const image of choice.message.images) {
4412
+ const dataUrl = image.image_url.url;
4413
+ images.push(getBase64FromDataUrl(dataUrl));
4414
+ }
4415
+ }
4416
+ const usage = responseValue.usage ? {
4417
+ inputTokens: responseValue.usage.prompt_tokens,
4418
+ outputTokens: responseValue.usage.completion_tokens,
4419
+ totalTokens: responseValue.usage.total_tokens
4420
+ } : void 0;
4421
+ return {
4422
+ images,
4423
+ warnings,
4424
+ response: {
4425
+ timestamp: /* @__PURE__ */ new Date(),
4426
+ modelId: responseValue.model,
4427
+ headers: responseHeaders
4428
+ },
4429
+ usage
4430
+ };
4431
+ }
4432
+ };
4433
+
4168
4434
  // src/utils/remove-undefined.ts
4169
4435
  function removeUndefinedEntries(record) {
4170
4436
  return Object.fromEntries(
4171
- Object.entries(record).filter(([, value]) => value !== null)
4437
+ Object.entries(record).filter(([, value]) => value != null)
4172
4438
  );
4173
4439
  }
4174
4440
 
4175
4441
  // src/utils/with-user-agent-suffix.ts
4442
+ function normalizeHeaders2(headers) {
4443
+ if (!headers) {
4444
+ return {};
4445
+ }
4446
+ if (headers instanceof Headers) {
4447
+ return Object.fromEntries(headers.entries());
4448
+ }
4449
+ if (Array.isArray(headers)) {
4450
+ return Object.fromEntries(headers);
4451
+ }
4452
+ return headers;
4453
+ }
4454
+ function findHeaderKey(headers, targetKey) {
4455
+ const lowerTarget = targetKey.toLowerCase();
4456
+ return Object.keys(headers).find((key) => key.toLowerCase() === lowerTarget);
4457
+ }
4176
4458
  function withUserAgentSuffix2(headers, ...userAgentSuffixParts) {
4177
- const cleanedHeaders = removeUndefinedEntries(
4178
- headers != null ? headers : {}
4459
+ const normalizedHeaders = normalizeHeaders2(headers);
4460
+ const cleanedHeaders = removeUndefinedEntries(normalizedHeaders);
4461
+ const existingUserAgentKey = findHeaderKey(cleanedHeaders, "user-agent");
4462
+ const existingUserAgentValue = existingUserAgentKey ? cleanedHeaders[existingUserAgentKey] : void 0;
4463
+ const userAgent = (existingUserAgentValue == null ? void 0 : existingUserAgentValue.trim()) ? existingUserAgentValue : userAgentSuffixParts.filter(Boolean).join(" ");
4464
+ const headersWithoutUserAgent = Object.fromEntries(
4465
+ Object.entries(cleanedHeaders).filter(
4466
+ ([key]) => key.toLowerCase() !== "user-agent"
4467
+ )
4179
4468
  );
4180
- const currentUserAgentHeader = cleanedHeaders["user-agent"] || "";
4181
- const newUserAgent = [currentUserAgentHeader, ...userAgentSuffixParts].filter(Boolean).join(" ");
4182
- return __spreadProps(__spreadValues({}, cleanedHeaders), {
4183
- "user-agent": newUserAgent
4469
+ return __spreadProps(__spreadValues({}, headersWithoutUserAgent), {
4470
+ "user-agent": userAgent
4184
4471
  });
4185
4472
  }
4186
4473
 
4187
4474
  // src/version.ts
4188
- var VERSION2 = false ? "0.0.0-test" : "2.0.4";
4475
+ var VERSION2 = false ? "0.0.0-test" : "2.1.1";
4189
4476
 
4190
4477
  // src/provider.ts
4191
4478
  function createOpenRouter(options = {}) {
@@ -4227,6 +4514,13 @@ function createOpenRouter(options = {}) {
4227
4514
  fetch: options.fetch,
4228
4515
  extraBody: options.extraBody
4229
4516
  });
4517
+ const createImageModel = (modelId, settings = {}) => new OpenRouterImageModel(modelId, settings, {
4518
+ provider: "openrouter.image",
4519
+ url: ({ path }) => `${baseURL}${path}`,
4520
+ headers: getHeaders,
4521
+ fetch: options.fetch,
4522
+ extraBody: options.extraBody
4523
+ });
4230
4524
  const createLanguageModel = (modelId, settings) => {
4231
4525
  if (new.target) {
4232
4526
  throw new Error(
@@ -4247,6 +4541,7 @@ function createOpenRouter(options = {}) {
4247
4541
  provider.completion = createCompletionModel;
4248
4542
  provider.textEmbeddingModel = createEmbeddingModel;
4249
4543
  provider.embedding = createEmbeddingModel;
4544
+ provider.imageModel = createImageModel;
4250
4545
  return provider;
4251
4546
  }
4252
4547
  var openrouter = createOpenRouter({