@openrouter/ai-sdk-provider 2.5.1 → 2.7.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.
@@ -2407,6 +2407,29 @@ function withStreamErrorHandling(source, onError) {
2407
2407
  });
2408
2408
  }
2409
2409
 
2410
+ // src/utils/deterministic-stringify.ts
2411
+ function deterministicStringify(value) {
2412
+ return JSON.stringify(sortKeys(value));
2413
+ }
2414
+ function sortKeys(value) {
2415
+ if (value === null || value === void 0) {
2416
+ return value;
2417
+ }
2418
+ if (Array.isArray(value)) {
2419
+ return value.map(sortKeys);
2420
+ }
2421
+ if (typeof value === "object") {
2422
+ const sorted = {};
2423
+ const entries = Object.entries(value);
2424
+ entries.sort(([a], [b]) => a.localeCompare(b));
2425
+ for (const [key, val] of entries) {
2426
+ sorted[key] = sortKeys(val);
2427
+ }
2428
+ return sorted;
2429
+ }
2430
+ return value;
2431
+ }
2432
+
2410
2433
  // src/utils/reasoning-details-duplicate-tracker.ts
2411
2434
  var _seenKeys;
2412
2435
  var ReasoningDetailsDuplicateTracker = class {
@@ -2738,7 +2761,7 @@ function convertToOpenRouterChatMessages(prompt) {
2738
2761
  type: "function",
2739
2762
  function: {
2740
2763
  name: part.toolName,
2741
- arguments: JSON.stringify(part.input)
2764
+ arguments: deterministicStringify(part.input)
2742
2765
  }
2743
2766
  });
2744
2767
  break;
@@ -3254,7 +3277,7 @@ var OpenRouterChatLanguageModel = class {
3254
3277
  tools,
3255
3278
  toolChoice
3256
3279
  }) {
3257
- var _a16;
3280
+ var _a16, _b16;
3258
3281
  const baseArgs = __spreadValues(__spreadValues({
3259
3282
  // model id:
3260
3283
  model: this.modelId,
@@ -3265,12 +3288,12 @@ var OpenRouterChatLanguageModel = class {
3265
3288
  top_logprobs: typeof this.settings.logprobs === "number" ? this.settings.logprobs : typeof this.settings.logprobs === "boolean" ? this.settings.logprobs ? 0 : void 0 : void 0,
3266
3289
  user: this.settings.user,
3267
3290
  parallel_tool_calls: this.settings.parallelToolCalls,
3268
- // standardized settings:
3269
- max_tokens: maxOutputTokens,
3270
- temperature,
3271
- top_p: topP,
3272
- frequency_penalty: frequencyPenalty,
3273
- presence_penalty: presencePenalty,
3291
+ // standardized settings (call-level options override model-level settings):
3292
+ max_tokens: maxOutputTokens != null ? maxOutputTokens : this.settings.maxTokens,
3293
+ temperature: temperature != null ? temperature : this.settings.temperature,
3294
+ top_p: topP != null ? topP : this.settings.topP,
3295
+ frequency_penalty: frequencyPenalty != null ? frequencyPenalty : this.settings.frequencyPenalty,
3296
+ presence_penalty: presencePenalty != null ? presencePenalty : this.settings.presencePenalty,
3274
3297
  seed,
3275
3298
  stop: stopSequences,
3276
3299
  response_format: (responseFormat == null ? void 0 : responseFormat.type) === "json" ? responseFormat.schema != null ? {
@@ -3283,7 +3306,7 @@ var OpenRouterChatLanguageModel = class {
3283
3306
  description: responseFormat.description
3284
3307
  })
3285
3308
  } : { type: "json_object" } : void 0,
3286
- top_k: topK,
3309
+ top_k: topK != null ? topK : this.settings.topK,
3287
3310
  // messages:
3288
3311
  messages: convertToOpenRouterChatMessages(prompt),
3289
3312
  // OpenRouter specific settings:
@@ -3301,16 +3324,25 @@ var OpenRouterChatLanguageModel = class {
3301
3324
  cache_control: this.settings.cache_control
3302
3325
  }, this.config.extraBody), this.settings.extraBody);
3303
3326
  if (tools && tools.length > 0) {
3304
- const mappedTools = tools.filter(
3305
- (tool) => tool.type === "function"
3306
- ).map((tool) => ({
3307
- type: "function",
3308
- function: {
3309
- name: tool.name,
3310
- description: tool.description,
3311
- parameters: tool.inputSchema
3327
+ const mappedTools = [];
3328
+ for (const tool of tools) {
3329
+ if (tool.type === "function") {
3330
+ const openrouterOptions = (_b16 = tool.providerOptions) == null ? void 0 : _b16.openrouter;
3331
+ const eagerInputStreaming = openrouterOptions == null ? void 0 : openrouterOptions.eager_input_streaming;
3332
+ mappedTools.push(__spreadValues({
3333
+ type: "function",
3334
+ function: {
3335
+ name: tool.name,
3336
+ description: tool.description,
3337
+ parameters: tool.inputSchema
3338
+ }
3339
+ }, eagerInputStreaming != null && {
3340
+ eager_input_streaming: eagerInputStreaming
3341
+ }));
3342
+ } else if (tool.type === "provider") {
3343
+ mappedTools.push(mapProviderTool(tool));
3312
3344
  }
3313
- }));
3345
+ }
3314
3346
  return __spreadProps(__spreadValues({}, baseArgs), {
3315
3347
  tools: mappedTools,
3316
3348
  tool_choice: toolChoice ? getChatCompletionToolChoice(toolChoice) : void 0
@@ -3319,7 +3351,7 @@ var OpenRouterChatLanguageModel = class {
3319
3351
  return baseArgs;
3320
3352
  }
3321
3353
  async doGenerate(options) {
3322
- var _b16, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w;
3354
+ var _b16, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v;
3323
3355
  const providerOptions = options.providerOptions || {};
3324
3356
  const openrouterOptions = providerOptions.openrouter || {};
3325
3357
  const _a16 = openrouterOptions, { cacheControl } = _a16, restOpenrouterOptions = __objRest(_a16, ["cacheControl"]);
@@ -3415,12 +3447,18 @@ var OpenRouterChatLanguageModel = class {
3415
3447
  }
3416
3448
  if (choice.message.tool_calls) {
3417
3449
  let reasoningDetailsAttachedToToolCall = false;
3450
+ const seenToolCallIds = /* @__PURE__ */ new Set();
3418
3451
  for (const toolCall of choice.message.tool_calls) {
3452
+ let toolCallId = toolCall.id;
3453
+ if (!toolCallId || seenToolCallIds.has(toolCallId)) {
3454
+ toolCallId = generateId();
3455
+ }
3456
+ seenToolCallIds.add(toolCallId);
3419
3457
  content.push({
3420
3458
  type: "tool-call",
3421
- toolCallId: (_c = toolCall.id) != null ? _c : generateId(),
3459
+ toolCallId,
3422
3460
  toolName: toolCall.function.name,
3423
- input: (_d = toolCall.function.arguments) != null ? _d : "{}",
3461
+ input: (_c = toolCall.function.arguments) != null ? _c : "{}",
3424
3462
  providerMetadata: !reasoningDetailsAttachedToToolCall ? {
3425
3463
  openrouter: {
3426
3464
  reasoning_details: reasoningDetails
@@ -3447,19 +3485,19 @@ var OpenRouterChatLanguageModel = class {
3447
3485
  sourceType: "url",
3448
3486
  id: annotation.url_citation.url,
3449
3487
  url: annotation.url_citation.url,
3450
- title: (_e = annotation.url_citation.title) != null ? _e : "",
3488
+ title: (_d = annotation.url_citation.title) != null ? _d : "",
3451
3489
  providerMetadata: {
3452
3490
  openrouter: {
3453
- content: (_f = annotation.url_citation.content) != null ? _f : "",
3454
- startIndex: (_g = annotation.url_citation.start_index) != null ? _g : 0,
3455
- endIndex: (_h = annotation.url_citation.end_index) != null ? _h : 0
3491
+ content: (_e = annotation.url_citation.content) != null ? _e : "",
3492
+ startIndex: (_f = annotation.url_citation.start_index) != null ? _f : 0,
3493
+ endIndex: (_g = annotation.url_citation.end_index) != null ? _g : 0
3456
3494
  }
3457
3495
  }
3458
3496
  });
3459
3497
  }
3460
3498
  }
3461
3499
  }
3462
- const fileAnnotations = (_i = choice.message.annotations) == null ? void 0 : _i.filter(
3500
+ const fileAnnotations = (_h = choice.message.annotations) == null ? void 0 : _h.filter(
3463
3501
  (a) => a.type === "file"
3464
3502
  );
3465
3503
  const hasToolCalls = choice.message.tool_calls && choice.message.tool_calls.length > 0;
@@ -3467,7 +3505,7 @@ var OpenRouterChatLanguageModel = class {
3467
3505
  (d) => d.type === "reasoning.encrypted" /* Encrypted */ && d.data
3468
3506
  );
3469
3507
  const shouldOverrideFinishReason = hasToolCalls && hasEncryptedReasoning && choice.finish_reason === "stop";
3470
- const mappedFinishReason = shouldOverrideFinishReason ? createFinishReason("tool-calls", (_j = choice.finish_reason) != null ? _j : void 0) : mapOpenRouterFinishReason(choice.finish_reason);
3508
+ const mappedFinishReason = shouldOverrideFinishReason ? createFinishReason("tool-calls", (_i = choice.finish_reason) != null ? _i : void 0) : mapOpenRouterFinishReason(choice.finish_reason);
3471
3509
  const effectiveFinishReason = hasToolCalls && mappedFinishReason.unified === "other" ? createFinishReason("tool-calls", mappedFinishReason.raw) : mappedFinishReason;
3472
3510
  return {
3473
3511
  content,
@@ -3476,22 +3514,22 @@ var OpenRouterChatLanguageModel = class {
3476
3514
  warnings: [],
3477
3515
  providerMetadata: {
3478
3516
  openrouter: OpenRouterProviderMetadataSchema.parse({
3479
- provider: (_k = response.provider) != null ? _k : "",
3480
- reasoning_details: (_l = choice.message.reasoning_details) != null ? _l : [],
3517
+ provider: (_j = response.provider) != null ? _j : "",
3518
+ reasoning_details: (_k = choice.message.reasoning_details) != null ? _k : [],
3481
3519
  annotations: fileAnnotations && fileAnnotations.length > 0 ? fileAnnotations : void 0,
3482
3520
  usage: __spreadValues(__spreadValues(__spreadValues(__spreadValues({
3483
- promptTokens: (_m = usageInfo.inputTokens.total) != null ? _m : 0,
3484
- completionTokens: (_n = usageInfo.outputTokens.total) != null ? _n : 0,
3485
- totalTokens: ((_o = usageInfo.inputTokens.total) != null ? _o : 0) + ((_p = usageInfo.outputTokens.total) != null ? _p : 0)
3486
- }, ((_q = response.usage) == null ? void 0 : _q.cost) != null ? { cost: response.usage.cost } : {}), ((_s = (_r = response.usage) == null ? void 0 : _r.prompt_tokens_details) == null ? void 0 : _s.cached_tokens) != null ? {
3521
+ promptTokens: (_l = usageInfo.inputTokens.total) != null ? _l : 0,
3522
+ completionTokens: (_m = usageInfo.outputTokens.total) != null ? _m : 0,
3523
+ totalTokens: ((_n = usageInfo.inputTokens.total) != null ? _n : 0) + ((_o = usageInfo.outputTokens.total) != null ? _o : 0)
3524
+ }, ((_p = response.usage) == null ? void 0 : _p.cost) != null ? { cost: response.usage.cost } : {}), ((_r = (_q = response.usage) == null ? void 0 : _q.prompt_tokens_details) == null ? void 0 : _r.cached_tokens) != null ? {
3487
3525
  promptTokensDetails: {
3488
3526
  cachedTokens: response.usage.prompt_tokens_details.cached_tokens
3489
3527
  }
3490
- } : {}), ((_u = (_t = response.usage) == null ? void 0 : _t.completion_tokens_details) == null ? void 0 : _u.reasoning_tokens) != null ? {
3528
+ } : {}), ((_t = (_s = response.usage) == null ? void 0 : _s.completion_tokens_details) == null ? void 0 : _t.reasoning_tokens) != null ? {
3491
3529
  completionTokensDetails: {
3492
3530
  reasoningTokens: response.usage.completion_tokens_details.reasoning_tokens
3493
3531
  }
3494
- } : {}), ((_w = (_v = response.usage) == null ? void 0 : _v.cost_details) == null ? void 0 : _w.upstream_inference_cost) != null ? {
3532
+ } : {}), ((_v = (_u = response.usage) == null ? void 0 : _u.cost_details) == null ? void 0 : _v.upstream_inference_cost) != null ? {
3495
3533
  costDetails: {
3496
3534
  upstreamInferenceCost: response.usage.cost_details.upstream_inference_cost
3497
3535
  }
@@ -3538,6 +3576,7 @@ var OpenRouterChatLanguageModel = class {
3538
3576
  streamError = err;
3539
3577
  });
3540
3578
  const toolCalls = [];
3579
+ const seenToolCallIds = /* @__PURE__ */ new Set();
3541
3580
  let finishReason = createFinishReason("other");
3542
3581
  const usage = {
3543
3582
  inputTokens: {
@@ -3637,18 +3676,16 @@ var OpenRouterChatLanguageModel = class {
3637
3676
  return;
3638
3677
  }
3639
3678
  const delta = choice.delta;
3640
- const emitReasoningChunk = (chunkText, providerMetadata) => {
3679
+ const emitReasoningChunk = (chunkText) => {
3641
3680
  if (!reasoningStarted) {
3642
3681
  reasoningId = generateId();
3643
3682
  controller.enqueue({
3644
- providerMetadata,
3645
3683
  type: "reasoning-start",
3646
3684
  id: reasoningId
3647
3685
  });
3648
3686
  reasoningStarted = true;
3649
3687
  }
3650
3688
  controller.enqueue({
3651
- providerMetadata,
3652
3689
  type: "reasoning-delta",
3653
3690
  delta: chunkText,
3654
3691
  id: reasoningId || generateId()
@@ -3670,15 +3707,10 @@ var OpenRouterChatLanguageModel = class {
3670
3707
  }
3671
3708
  }
3672
3709
  if (!textStarted) {
3673
- const reasoningMetadata = {
3674
- openrouter: {
3675
- reasoning_details: accumulatedReasoningDetails.map((d) => __spreadValues({}, d))
3676
- }
3677
- };
3678
3710
  for (const detail of delta.reasoning_details) {
3679
3711
  switch (detail.type) {
3680
3712
  case "reasoning.text" /* Text */: {
3681
- emitReasoningChunk(detail.text || "", reasoningMetadata);
3713
+ emitReasoningChunk(detail.text || "");
3682
3714
  break;
3683
3715
  }
3684
3716
  case "reasoning.encrypted" /* Encrypted */: {
@@ -3686,7 +3718,7 @@ var OpenRouterChatLanguageModel = class {
3686
3718
  }
3687
3719
  case "reasoning.summary" /* Summary */: {
3688
3720
  if (detail.summary) {
3689
- emitReasoningChunk(detail.summary, reasoningMetadata);
3721
+ emitReasoningChunk(detail.summary);
3690
3722
  }
3691
3723
  break;
3692
3724
  }
@@ -3768,24 +3800,23 @@ var OpenRouterChatLanguageModel = class {
3768
3800
  message: `Expected 'function' type.`
3769
3801
  });
3770
3802
  }
3771
- if (toolCallDelta.id == null) {
3772
- throw new InvalidResponseDataError({
3773
- data: toolCallDelta,
3774
- message: `Expected 'id' to be a string.`
3775
- });
3776
- }
3777
3803
  if (((_k = toolCallDelta.function) == null ? void 0 : _k.name) == null) {
3778
3804
  throw new InvalidResponseDataError({
3779
3805
  data: toolCallDelta,
3780
3806
  message: `Expected 'function.name' to be a string.`
3781
3807
  });
3782
3808
  }
3809
+ let toolCallId = (_l = toolCallDelta.id) != null ? _l : "";
3810
+ if (!toolCallId || seenToolCallIds.has(toolCallId)) {
3811
+ toolCallId = generateId();
3812
+ }
3813
+ seenToolCallIds.add(toolCallId);
3783
3814
  toolCalls[index] = {
3784
- id: toolCallDelta.id,
3815
+ id: toolCallId,
3785
3816
  type: "function",
3786
3817
  function: {
3787
3818
  name: toolCallDelta.function.name,
3788
- arguments: (_l = toolCallDelta.function.arguments) != null ? _l : ""
3819
+ arguments: (_m = toolCallDelta.function.arguments) != null ? _m : ""
3789
3820
  },
3790
3821
  inputStarted: false,
3791
3822
  sent: false
@@ -3797,7 +3828,7 @@ var OpenRouterChatLanguageModel = class {
3797
3828
  message: `Tool call at index ${index} is missing after creation.`
3798
3829
  });
3799
3830
  }
3800
- if (((_m = toolCall2.function) == null ? void 0 : _m.name) != null && ((_n = toolCall2.function) == null ? void 0 : _n.arguments) != null && isParsableJson(toolCall2.function.arguments)) {
3831
+ if (((_n = toolCall2.function) == null ? void 0 : _n.name) != null && ((_o = toolCall2.function) == null ? void 0 : _o.arguments) != null && isParsableJson(toolCall2.function.arguments)) {
3801
3832
  toolCall2.inputStarted = true;
3802
3833
  controller.enqueue({
3803
3834
  type: "tool-input-start",
@@ -3855,22 +3886,22 @@ var OpenRouterChatLanguageModel = class {
3855
3886
  });
3856
3887
  }
3857
3888
  }
3858
- if (((_o = toolCallDelta.function) == null ? void 0 : _o.arguments) != null) {
3859
- toolCall.function.arguments += (_q = (_p = toolCallDelta.function) == null ? void 0 : _p.arguments) != null ? _q : "";
3889
+ if (((_p = toolCallDelta.function) == null ? void 0 : _p.arguments) != null) {
3890
+ toolCall.function.arguments += (_r = (_q = toolCallDelta.function) == null ? void 0 : _q.arguments) != null ? _r : "";
3860
3891
  }
3861
3892
  controller.enqueue({
3862
3893
  type: "tool-input-delta",
3863
3894
  id: toolCall.id,
3864
- delta: (_r = toolCallDelta.function.arguments) != null ? _r : ""
3895
+ delta: (_s = toolCallDelta.function.arguments) != null ? _s : ""
3865
3896
  });
3866
- if (((_s = toolCall.function) == null ? void 0 : _s.name) != null && ((_t = toolCall.function) == null ? void 0 : _t.arguments) != null && isParsableJson(toolCall.function.arguments)) {
3897
+ if (((_t = toolCall.function) == null ? void 0 : _t.name) != null && ((_u = toolCall.function) == null ? void 0 : _u.arguments) != null && isParsableJson(toolCall.function.arguments)) {
3867
3898
  controller.enqueue({
3868
3899
  type: "tool-input-end",
3869
3900
  id: toolCall.id
3870
3901
  });
3871
3902
  controller.enqueue({
3872
3903
  type: "tool-call",
3873
- toolCallId: (_u = toolCall.id) != null ? _u : generateId(),
3904
+ toolCallId: toolCall.id,
3874
3905
  toolName: toolCall.function.name,
3875
3906
  input: toolCall.function.arguments,
3876
3907
  providerMetadata: !reasoningDetailsAttachedToToolCall ? {
@@ -3895,7 +3926,6 @@ var OpenRouterChatLanguageModel = class {
3895
3926
  }
3896
3927
  },
3897
3928
  flush(controller) {
3898
- var _a17;
3899
3929
  const hasToolCalls = toolCalls.length > 0;
3900
3930
  if (streamError != null) {
3901
3931
  finishReason = createFinishReason("error");
@@ -3932,7 +3962,7 @@ var OpenRouterChatLanguageModel = class {
3932
3962
  });
3933
3963
  controller.enqueue({
3934
3964
  type: "tool-call",
3935
- toolCallId: (_a17 = toolCall.id) != null ? _a17 : generateId(),
3965
+ toolCallId: toolCall.id,
3936
3966
  toolName: toolCall.function.name,
3937
3967
  input,
3938
3968
  providerMetadata: !reasoningDetailsAttachedToToolCall ? {
@@ -3977,6 +4007,12 @@ var OpenRouterChatLanguageModel = class {
3977
4007
  if (accumulatedFileAnnotations.length > 0) {
3978
4008
  openrouterMetadata.annotations = accumulatedFileAnnotations;
3979
4009
  }
4010
+ if (usage.inputTokens.total === void 0 && openrouterUsage.promptTokens !== void 0) {
4011
+ usage.inputTokens.total = openrouterUsage.promptTokens;
4012
+ }
4013
+ if (usage.outputTokens.total === void 0 && openrouterUsage.completionTokens !== void 0) {
4014
+ usage.outputTokens.total = openrouterUsage.completionTokens;
4015
+ }
3980
4016
  usage.raw = rawUsage;
3981
4017
  controller.enqueue({
3982
4018
  type: "finish",
@@ -3995,6 +4031,22 @@ var OpenRouterChatLanguageModel = class {
3995
4031
  };
3996
4032
  }
3997
4033
  };
4034
+ function mapProviderTool(tool) {
4035
+ const [provider, toolName] = tool.id.split(".");
4036
+ const apiToolType = `${provider}:${toolName}`;
4037
+ const mappedArgs = {};
4038
+ for (const [key, value] of Object.entries(tool.args)) {
4039
+ if (value !== void 0) {
4040
+ mappedArgs[camelToSnake(key)] = value;
4041
+ }
4042
+ }
4043
+ return __spreadValues({
4044
+ type: apiToolType
4045
+ }, mappedArgs);
4046
+ }
4047
+ function camelToSnake(str) {
4048
+ return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
4049
+ }
3998
4050
 
3999
4051
  // src/completion/convert-to-openrouter-completion-prompt.ts
4000
4052
  function convertToOpenRouterCompletionPrompt({
@@ -4196,16 +4248,16 @@ var OpenRouterCompletionLanguageModel = class {
4196
4248
  logprobs: typeof this.settings.logprobs === "number" ? this.settings.logprobs : typeof this.settings.logprobs === "boolean" ? this.settings.logprobs ? 0 : void 0 : void 0,
4197
4249
  suffix: this.settings.suffix,
4198
4250
  user: this.settings.user,
4199
- // standardized settings:
4200
- max_tokens: maxOutputTokens,
4201
- temperature,
4202
- top_p: topP,
4203
- frequency_penalty: frequencyPenalty,
4204
- presence_penalty: presencePenalty,
4251
+ // standardized settings (call-level options override model-level settings):
4252
+ max_tokens: maxOutputTokens != null ? maxOutputTokens : this.settings.maxTokens,
4253
+ temperature: temperature != null ? temperature : this.settings.temperature,
4254
+ top_p: topP != null ? topP : this.settings.topP,
4255
+ frequency_penalty: frequencyPenalty != null ? frequencyPenalty : this.settings.frequencyPenalty,
4256
+ presence_penalty: presencePenalty != null ? presencePenalty : this.settings.presencePenalty,
4205
4257
  seed,
4206
4258
  stop: stopSequences,
4207
4259
  response_format: responseFormat,
4208
- top_k: topK,
4260
+ top_k: topK != null ? topK : this.settings.topK,
4209
4261
  // prompt:
4210
4262
  prompt: completionPrompt,
4211
4263
  // OpenRouter specific settings: