@openrouter/ai-sdk-provider 2.5.0 → 2.6.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
@@ -2076,6 +2076,46 @@ var postToApi = async ({
2076
2076
  throw handleFetchError({ error, url, requestBodyValues: body.values });
2077
2077
  }
2078
2078
  };
2079
+ function tool(tool2) {
2080
+ return tool2;
2081
+ }
2082
+ function createProviderToolFactory({
2083
+ id,
2084
+ inputSchema
2085
+ }) {
2086
+ return (_a16) => {
2087
+ var _b16 = _a16, {
2088
+ execute,
2089
+ outputSchema,
2090
+ needsApproval,
2091
+ toModelOutput,
2092
+ onInputStart,
2093
+ onInputDelta,
2094
+ onInputAvailable
2095
+ } = _b16, args = __objRest(_b16, [
2096
+ "execute",
2097
+ "outputSchema",
2098
+ "needsApproval",
2099
+ "toModelOutput",
2100
+ "onInputStart",
2101
+ "onInputDelta",
2102
+ "onInputAvailable"
2103
+ ]);
2104
+ return tool({
2105
+ type: "provider",
2106
+ id,
2107
+ args,
2108
+ inputSchema,
2109
+ outputSchema,
2110
+ execute,
2111
+ needsApproval,
2112
+ toModelOutput,
2113
+ onInputStart,
2114
+ onInputDelta,
2115
+ onInputAvailable
2116
+ });
2117
+ };
2118
+ }
2079
2119
  var createJsonErrorResponseHandler = ({
2080
2120
  errorSchema,
2081
2121
  errorToMessage,
@@ -2442,6 +2482,29 @@ function withStreamErrorHandling(source, onError) {
2442
2482
  });
2443
2483
  }
2444
2484
 
2485
+ // src/utils/deterministic-stringify.ts
2486
+ function deterministicStringify(value) {
2487
+ return JSON.stringify(sortKeys(value));
2488
+ }
2489
+ function sortKeys(value) {
2490
+ if (value === null || value === void 0) {
2491
+ return value;
2492
+ }
2493
+ if (Array.isArray(value)) {
2494
+ return value.map(sortKeys);
2495
+ }
2496
+ if (typeof value === "object") {
2497
+ const sorted = {};
2498
+ const entries = Object.entries(value);
2499
+ entries.sort(([a], [b]) => a.localeCompare(b));
2500
+ for (const [key, val] of entries) {
2501
+ sorted[key] = sortKeys(val);
2502
+ }
2503
+ return sorted;
2504
+ }
2505
+ return value;
2506
+ }
2507
+
2445
2508
  // src/utils/reasoning-details-duplicate-tracker.ts
2446
2509
  var _seenKeys;
2447
2510
  var ReasoningDetailsDuplicateTracker = class {
@@ -2773,7 +2836,7 @@ function convertToOpenRouterChatMessages(prompt) {
2773
2836
  type: "function",
2774
2837
  function: {
2775
2838
  name: part.toolName,
2776
- arguments: JSON.stringify(part.input)
2839
+ arguments: deterministicStringify(part.input)
2777
2840
  }
2778
2841
  });
2779
2842
  break;
@@ -2801,7 +2864,7 @@ function convertToOpenRouterChatMessages(prompt) {
2801
2864
  return true;
2802
2865
  }
2803
2866
  const format = (_a17 = detail.format) != null ? _a17 : DEFAULT_REASONING_FORMAT;
2804
- if (format !== "anthropic-claude-v1" /* AnthropicClaudeV1 */) {
2867
+ if (format !== "anthropic-claude-v1" /* AnthropicClaudeV1 */ && format !== "google-gemini-v1" /* GoogleGeminiV1 */) {
2805
2868
  return true;
2806
2869
  }
2807
2870
  return !!detail.signature;
@@ -2810,7 +2873,7 @@ function convertToOpenRouterChatMessages(prompt) {
2810
2873
  const logger = globalThis.AI_SDK_LOG_WARNINGS;
2811
2874
  if (logger !== false && typeof logger !== "function") {
2812
2875
  console.warn(
2813
- "[openrouter] Some reasoning_details entries were removed because they were missing signatures. See https://github.com/OpenRouterTeam/ai-sdk-provider/issues/423 for more details."
2876
+ "[openrouter] Some reasoning_details entries were removed because they were missing signatures. See https://github.com/OpenRouterTeam/ai-sdk-provider/issues/423 and https://github.com/OpenRouterTeam/ai-sdk-provider/issues/418 for more details."
2814
2877
  );
2815
2878
  }
2816
2879
  }
@@ -3336,16 +3399,21 @@ var OpenRouterChatLanguageModel = class {
3336
3399
  cache_control: this.settings.cache_control
3337
3400
  }, this.config.extraBody), this.settings.extraBody);
3338
3401
  if (tools && tools.length > 0) {
3339
- const mappedTools = tools.filter(
3340
- (tool) => tool.type === "function"
3341
- ).map((tool) => ({
3342
- type: "function",
3343
- function: {
3344
- name: tool.name,
3345
- description: tool.description,
3346
- parameters: tool.inputSchema
3402
+ const mappedTools = [];
3403
+ for (const tool2 of tools) {
3404
+ if (tool2.type === "function") {
3405
+ mappedTools.push({
3406
+ type: "function",
3407
+ function: {
3408
+ name: tool2.name,
3409
+ description: tool2.description,
3410
+ parameters: tool2.inputSchema
3411
+ }
3412
+ });
3413
+ } else if (tool2.type === "provider") {
3414
+ mappedTools.push(mapProviderTool(tool2));
3347
3415
  }
3348
- }));
3416
+ }
3349
3417
  return __spreadProps(__spreadValues({}, baseArgs), {
3350
3418
  tools: mappedTools,
3351
3419
  tool_choice: toolChoice ? getChatCompletionToolChoice(toolChoice) : void 0
@@ -3354,7 +3422,7 @@ var OpenRouterChatLanguageModel = class {
3354
3422
  return baseArgs;
3355
3423
  }
3356
3424
  async doGenerate(options) {
3357
- var _b16, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w;
3425
+ var _b16, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v;
3358
3426
  const providerOptions = options.providerOptions || {};
3359
3427
  const openrouterOptions = providerOptions.openrouter || {};
3360
3428
  const _a16 = openrouterOptions, { cacheControl } = _a16, restOpenrouterOptions = __objRest(_a16, ["cacheControl"]);
@@ -3450,12 +3518,18 @@ var OpenRouterChatLanguageModel = class {
3450
3518
  }
3451
3519
  if (choice.message.tool_calls) {
3452
3520
  let reasoningDetailsAttachedToToolCall = false;
3521
+ const seenToolCallIds = /* @__PURE__ */ new Set();
3453
3522
  for (const toolCall of choice.message.tool_calls) {
3523
+ let toolCallId = toolCall.id;
3524
+ if (!toolCallId || seenToolCallIds.has(toolCallId)) {
3525
+ toolCallId = generateId();
3526
+ }
3527
+ seenToolCallIds.add(toolCallId);
3454
3528
  content.push({
3455
3529
  type: "tool-call",
3456
- toolCallId: (_c = toolCall.id) != null ? _c : generateId(),
3530
+ toolCallId,
3457
3531
  toolName: toolCall.function.name,
3458
- input: (_d = toolCall.function.arguments) != null ? _d : "{}",
3532
+ input: (_c = toolCall.function.arguments) != null ? _c : "{}",
3459
3533
  providerMetadata: !reasoningDetailsAttachedToToolCall ? {
3460
3534
  openrouter: {
3461
3535
  reasoning_details: reasoningDetails
@@ -3482,19 +3556,19 @@ var OpenRouterChatLanguageModel = class {
3482
3556
  sourceType: "url",
3483
3557
  id: annotation.url_citation.url,
3484
3558
  url: annotation.url_citation.url,
3485
- title: (_e = annotation.url_citation.title) != null ? _e : "",
3559
+ title: (_d = annotation.url_citation.title) != null ? _d : "",
3486
3560
  providerMetadata: {
3487
3561
  openrouter: {
3488
- content: (_f = annotation.url_citation.content) != null ? _f : "",
3489
- startIndex: (_g = annotation.url_citation.start_index) != null ? _g : 0,
3490
- endIndex: (_h = annotation.url_citation.end_index) != null ? _h : 0
3562
+ content: (_e = annotation.url_citation.content) != null ? _e : "",
3563
+ startIndex: (_f = annotation.url_citation.start_index) != null ? _f : 0,
3564
+ endIndex: (_g = annotation.url_citation.end_index) != null ? _g : 0
3491
3565
  }
3492
3566
  }
3493
3567
  });
3494
3568
  }
3495
3569
  }
3496
3570
  }
3497
- const fileAnnotations = (_i = choice.message.annotations) == null ? void 0 : _i.filter(
3571
+ const fileAnnotations = (_h = choice.message.annotations) == null ? void 0 : _h.filter(
3498
3572
  (a) => a.type === "file"
3499
3573
  );
3500
3574
  const hasToolCalls = choice.message.tool_calls && choice.message.tool_calls.length > 0;
@@ -3502,7 +3576,7 @@ var OpenRouterChatLanguageModel = class {
3502
3576
  (d) => d.type === "reasoning.encrypted" /* Encrypted */ && d.data
3503
3577
  );
3504
3578
  const shouldOverrideFinishReason = hasToolCalls && hasEncryptedReasoning && choice.finish_reason === "stop";
3505
- const mappedFinishReason = shouldOverrideFinishReason ? createFinishReason("tool-calls", (_j = choice.finish_reason) != null ? _j : void 0) : mapOpenRouterFinishReason(choice.finish_reason);
3579
+ const mappedFinishReason = shouldOverrideFinishReason ? createFinishReason("tool-calls", (_i = choice.finish_reason) != null ? _i : void 0) : mapOpenRouterFinishReason(choice.finish_reason);
3506
3580
  const effectiveFinishReason = hasToolCalls && mappedFinishReason.unified === "other" ? createFinishReason("tool-calls", mappedFinishReason.raw) : mappedFinishReason;
3507
3581
  return {
3508
3582
  content,
@@ -3511,22 +3585,22 @@ var OpenRouterChatLanguageModel = class {
3511
3585
  warnings: [],
3512
3586
  providerMetadata: {
3513
3587
  openrouter: OpenRouterProviderMetadataSchema.parse({
3514
- provider: (_k = response.provider) != null ? _k : "",
3515
- reasoning_details: (_l = choice.message.reasoning_details) != null ? _l : [],
3588
+ provider: (_j = response.provider) != null ? _j : "",
3589
+ reasoning_details: (_k = choice.message.reasoning_details) != null ? _k : [],
3516
3590
  annotations: fileAnnotations && fileAnnotations.length > 0 ? fileAnnotations : void 0,
3517
3591
  usage: __spreadValues(__spreadValues(__spreadValues(__spreadValues({
3518
- promptTokens: (_m = usageInfo.inputTokens.total) != null ? _m : 0,
3519
- completionTokens: (_n = usageInfo.outputTokens.total) != null ? _n : 0,
3520
- totalTokens: ((_o = usageInfo.inputTokens.total) != null ? _o : 0) + ((_p = usageInfo.outputTokens.total) != null ? _p : 0)
3521
- }, ((_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 ? {
3592
+ promptTokens: (_l = usageInfo.inputTokens.total) != null ? _l : 0,
3593
+ completionTokens: (_m = usageInfo.outputTokens.total) != null ? _m : 0,
3594
+ totalTokens: ((_n = usageInfo.inputTokens.total) != null ? _n : 0) + ((_o = usageInfo.outputTokens.total) != null ? _o : 0)
3595
+ }, ((_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 ? {
3522
3596
  promptTokensDetails: {
3523
3597
  cachedTokens: response.usage.prompt_tokens_details.cached_tokens
3524
3598
  }
3525
- } : {}), ((_u = (_t = response.usage) == null ? void 0 : _t.completion_tokens_details) == null ? void 0 : _u.reasoning_tokens) != null ? {
3599
+ } : {}), ((_t = (_s = response.usage) == null ? void 0 : _s.completion_tokens_details) == null ? void 0 : _t.reasoning_tokens) != null ? {
3526
3600
  completionTokensDetails: {
3527
3601
  reasoningTokens: response.usage.completion_tokens_details.reasoning_tokens
3528
3602
  }
3529
- } : {}), ((_w = (_v = response.usage) == null ? void 0 : _v.cost_details) == null ? void 0 : _w.upstream_inference_cost) != null ? {
3603
+ } : {}), ((_v = (_u = response.usage) == null ? void 0 : _u.cost_details) == null ? void 0 : _v.upstream_inference_cost) != null ? {
3530
3604
  costDetails: {
3531
3605
  upstreamInferenceCost: response.usage.cost_details.upstream_inference_cost
3532
3606
  }
@@ -3573,6 +3647,7 @@ var OpenRouterChatLanguageModel = class {
3573
3647
  streamError = err;
3574
3648
  });
3575
3649
  const toolCalls = [];
3650
+ const seenToolCallIds = /* @__PURE__ */ new Set();
3576
3651
  let finishReason = createFinishReason("other");
3577
3652
  const usage = {
3578
3653
  inputTokens: {
@@ -3803,24 +3878,23 @@ var OpenRouterChatLanguageModel = class {
3803
3878
  message: `Expected 'function' type.`
3804
3879
  });
3805
3880
  }
3806
- if (toolCallDelta.id == null) {
3807
- throw new InvalidResponseDataError({
3808
- data: toolCallDelta,
3809
- message: `Expected 'id' to be a string.`
3810
- });
3811
- }
3812
3881
  if (((_k = toolCallDelta.function) == null ? void 0 : _k.name) == null) {
3813
3882
  throw new InvalidResponseDataError({
3814
3883
  data: toolCallDelta,
3815
3884
  message: `Expected 'function.name' to be a string.`
3816
3885
  });
3817
3886
  }
3887
+ let toolCallId = (_l = toolCallDelta.id) != null ? _l : "";
3888
+ if (!toolCallId || seenToolCallIds.has(toolCallId)) {
3889
+ toolCallId = generateId();
3890
+ }
3891
+ seenToolCallIds.add(toolCallId);
3818
3892
  toolCalls[index] = {
3819
- id: toolCallDelta.id,
3893
+ id: toolCallId,
3820
3894
  type: "function",
3821
3895
  function: {
3822
3896
  name: toolCallDelta.function.name,
3823
- arguments: (_l = toolCallDelta.function.arguments) != null ? _l : ""
3897
+ arguments: (_m = toolCallDelta.function.arguments) != null ? _m : ""
3824
3898
  },
3825
3899
  inputStarted: false,
3826
3900
  sent: false
@@ -3832,7 +3906,7 @@ var OpenRouterChatLanguageModel = class {
3832
3906
  message: `Tool call at index ${index} is missing after creation.`
3833
3907
  });
3834
3908
  }
3835
- if (((_m = toolCall2.function) == null ? void 0 : _m.name) != null && ((_n = toolCall2.function) == null ? void 0 : _n.arguments) != null && isParsableJson(toolCall2.function.arguments)) {
3909
+ if (((_n = toolCall2.function) == null ? void 0 : _n.name) != null && ((_o = toolCall2.function) == null ? void 0 : _o.arguments) != null && isParsableJson(toolCall2.function.arguments)) {
3836
3910
  toolCall2.inputStarted = true;
3837
3911
  controller.enqueue({
3838
3912
  type: "tool-input-start",
@@ -3890,22 +3964,22 @@ var OpenRouterChatLanguageModel = class {
3890
3964
  });
3891
3965
  }
3892
3966
  }
3893
- if (((_o = toolCallDelta.function) == null ? void 0 : _o.arguments) != null) {
3894
- toolCall.function.arguments += (_q = (_p = toolCallDelta.function) == null ? void 0 : _p.arguments) != null ? _q : "";
3967
+ if (((_p = toolCallDelta.function) == null ? void 0 : _p.arguments) != null) {
3968
+ toolCall.function.arguments += (_r = (_q = toolCallDelta.function) == null ? void 0 : _q.arguments) != null ? _r : "";
3895
3969
  }
3896
3970
  controller.enqueue({
3897
3971
  type: "tool-input-delta",
3898
3972
  id: toolCall.id,
3899
- delta: (_r = toolCallDelta.function.arguments) != null ? _r : ""
3973
+ delta: (_s = toolCallDelta.function.arguments) != null ? _s : ""
3900
3974
  });
3901
- if (((_s = toolCall.function) == null ? void 0 : _s.name) != null && ((_t = toolCall.function) == null ? void 0 : _t.arguments) != null && isParsableJson(toolCall.function.arguments)) {
3975
+ if (((_t = toolCall.function) == null ? void 0 : _t.name) != null && ((_u = toolCall.function) == null ? void 0 : _u.arguments) != null && isParsableJson(toolCall.function.arguments)) {
3902
3976
  controller.enqueue({
3903
3977
  type: "tool-input-end",
3904
3978
  id: toolCall.id
3905
3979
  });
3906
3980
  controller.enqueue({
3907
3981
  type: "tool-call",
3908
- toolCallId: (_u = toolCall.id) != null ? _u : generateId(),
3982
+ toolCallId: toolCall.id,
3909
3983
  toolName: toolCall.function.name,
3910
3984
  input: toolCall.function.arguments,
3911
3985
  providerMetadata: !reasoningDetailsAttachedToToolCall ? {
@@ -3930,7 +4004,6 @@ var OpenRouterChatLanguageModel = class {
3930
4004
  }
3931
4005
  },
3932
4006
  flush(controller) {
3933
- var _a17;
3934
4007
  const hasToolCalls = toolCalls.length > 0;
3935
4008
  if (streamError != null) {
3936
4009
  finishReason = createFinishReason("error");
@@ -3967,7 +4040,7 @@ var OpenRouterChatLanguageModel = class {
3967
4040
  });
3968
4041
  controller.enqueue({
3969
4042
  type: "tool-call",
3970
- toolCallId: (_a17 = toolCall.id) != null ? _a17 : generateId(),
4043
+ toolCallId: toolCall.id,
3971
4044
  toolName: toolCall.function.name,
3972
4045
  input,
3973
4046
  providerMetadata: !reasoningDetailsAttachedToToolCall ? {
@@ -4030,6 +4103,22 @@ var OpenRouterChatLanguageModel = class {
4030
4103
  };
4031
4104
  }
4032
4105
  };
4106
+ function mapProviderTool(tool2) {
4107
+ const [provider, toolName] = tool2.id.split(".");
4108
+ const apiToolType = `${provider}:${toolName}`;
4109
+ const mappedArgs = {};
4110
+ for (const [key, value] of Object.entries(tool2.args)) {
4111
+ if (value !== void 0) {
4112
+ mappedArgs[camelToSnake(key)] = value;
4113
+ }
4114
+ }
4115
+ return __spreadValues({
4116
+ type: apiToolType
4117
+ }, mappedArgs);
4118
+ }
4119
+ function camelToSnake(str) {
4120
+ return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
4121
+ }
4033
4122
 
4034
4123
  // src/completion/convert-to-openrouter-completion-prompt.ts
4035
4124
  function convertToOpenRouterCompletionPrompt({
@@ -4758,6 +4847,17 @@ function convertImageFileToContentPart(file) {
4758
4847
  };
4759
4848
  }
4760
4849
 
4850
+ // src/tool/web-search.ts
4851
+ import { z as z11 } from "zod/v4";
4852
+ var webSearchInputSchema = z11.object({
4853
+ /** Search results returned by the server tool */
4854
+ results: z11.array(z11.unknown()).optional()
4855
+ });
4856
+ var webSearch = createProviderToolFactory({
4857
+ id: "openrouter.web_search",
4858
+ inputSchema: webSearchInputSchema
4859
+ });
4860
+
4761
4861
  // src/utils/remove-undefined.ts
4762
4862
  function removeUndefinedEntries(record) {
4763
4863
  return Object.fromEntries(
@@ -4799,7 +4899,7 @@ function withUserAgentSuffix2(headers, ...userAgentSuffixParts) {
4799
4899
  }
4800
4900
 
4801
4901
  // src/version.ts
4802
- var VERSION2 = false ? "0.0.0-test" : "2.5.0";
4902
+ var VERSION2 = false ? "0.0.0-test" : "2.6.0";
4803
4903
 
4804
4904
  // src/provider.ts
4805
4905
  function createOpenRouter(options = {}) {
@@ -4869,6 +4969,9 @@ function createOpenRouter(options = {}) {
4869
4969
  provider.textEmbeddingModel = createEmbeddingModel;
4870
4970
  provider.embedding = createEmbeddingModel;
4871
4971
  provider.imageModel = createImageModel;
4972
+ provider.tools = {
4973
+ webSearch
4974
+ };
4872
4975
  return provider;
4873
4976
  }
4874
4977
  var openrouter = createOpenRouter({