@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.
package/dist/index.d.mts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as _ai_sdk_provider from '@ai-sdk/provider';
2
2
  import { LanguageModelV3, LanguageModelV3CallOptions, LanguageModelV3Content, LanguageModelV3FinishReason, LanguageModelV3Usage, SharedV3Warning, LanguageModelV3ResponseMetadata, SharedV3Headers, LanguageModelV3StreamPart, EmbeddingModelV3, SharedV3ProviderMetadata, ImageModelV3, ImageModelV3CallOptions, ImageModelV3ProviderMetadata, ImageModelV3Usage, ProviderV3 } from '@ai-sdk/provider';
3
3
  export { LanguageModelV3, LanguageModelV3Prompt } from '@ai-sdk/provider';
4
+ import { ProviderToolFactory } from '@ai-sdk/provider-utils';
4
5
  import { z } from 'zod/v4';
5
6
 
6
7
  /**
@@ -372,6 +373,40 @@ type OpenRouterSharedSettings = OpenRouterProviderOptions & {
372
373
  */
373
374
  include: boolean;
374
375
  };
376
+ /**
377
+ * Default temperature for model calls. Controls randomness in the output.
378
+ * Can be overridden at call time via generateText/streamText options.
379
+ * Range: 0 to 2, where 0 is deterministic and higher values are more random.
380
+ */
381
+ temperature?: number;
382
+ /**
383
+ * Default top-p (nucleus sampling) for model calls.
384
+ * Can be overridden at call time via generateText/streamText options.
385
+ * Range: 0 to 1.
386
+ */
387
+ topP?: number;
388
+ /**
389
+ * Default top-k sampling for model calls.
390
+ * Can be overridden at call time via generateText/streamText options.
391
+ */
392
+ topK?: number;
393
+ /**
394
+ * Default frequency penalty for model calls.
395
+ * Can be overridden at call time via generateText/streamText options.
396
+ * Range: -2 to 2.
397
+ */
398
+ frequencyPenalty?: number;
399
+ /**
400
+ * Default presence penalty for model calls.
401
+ * Can be overridden at call time via generateText/streamText options.
402
+ * Range: -2 to 2.
403
+ */
404
+ presencePenalty?: number;
405
+ /**
406
+ * Default maximum number of tokens to generate.
407
+ * Can be overridden at call time via generateText/streamText options.
408
+ */
409
+ maxTokens?: number;
375
410
  };
376
411
  /**
377
412
  * Usage accounting response
@@ -613,6 +648,19 @@ declare class OpenRouterImageModel implements ImageModelV3 {
613
648
  }>;
614
649
  }
615
650
 
651
+ /**
652
+ * Configuration args for the web search provider tool.
653
+ * These are mapped to snake_case in the API request.
654
+ */
655
+ type WebSearchToolArgs = {
656
+ /** Maximum number of search results to include */
657
+ maxResults?: number;
658
+ /** Custom search prompt to guide the search query */
659
+ searchPrompt?: string;
660
+ /** Search engine to use: 'auto', 'native', or 'exa' */
661
+ engine?: 'auto' | Engine;
662
+ };
663
+
616
664
  interface OpenRouterProvider extends ProviderV3 {
617
665
  (modelId: OpenRouterChatModelId, settings?: OpenRouterCompletionSettings): OpenRouterCompletionLanguageModel;
618
666
  (modelId: OpenRouterChatModelId, settings?: OpenRouterChatSettings): OpenRouterChatLanguageModel;
@@ -639,6 +687,17 @@ interface OpenRouterProvider extends ProviderV3 {
639
687
  Creates an OpenRouter image model for image generation.
640
688
  */
641
689
  imageModel(modelId: OpenRouterImageModelId, settings?: OpenRouterImageSettings): OpenRouterImageModel;
690
+ /**
691
+ * Provider-defined tools for OpenRouter server tools.
692
+ */
693
+ readonly tools: {
694
+ /**
695
+ * Creates an OpenRouter web search server tool.
696
+ *
697
+ * @see https://openrouter.ai/docs/guides/features/server-tools/web-search
698
+ */
699
+ webSearch: ProviderToolFactory<unknown, WebSearchToolArgs>;
700
+ };
642
701
  }
643
702
  interface OpenRouterProviderSettings {
644
703
  /**
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import * as _ai_sdk_provider from '@ai-sdk/provider';
2
2
  import { LanguageModelV3, LanguageModelV3CallOptions, LanguageModelV3Content, LanguageModelV3FinishReason, LanguageModelV3Usage, SharedV3Warning, LanguageModelV3ResponseMetadata, SharedV3Headers, LanguageModelV3StreamPart, EmbeddingModelV3, SharedV3ProviderMetadata, ImageModelV3, ImageModelV3CallOptions, ImageModelV3ProviderMetadata, ImageModelV3Usage, ProviderV3 } from '@ai-sdk/provider';
3
3
  export { LanguageModelV3, LanguageModelV3Prompt } from '@ai-sdk/provider';
4
+ import { ProviderToolFactory } from '@ai-sdk/provider-utils';
4
5
  import { z } from 'zod/v4';
5
6
 
6
7
  /**
@@ -372,6 +373,40 @@ type OpenRouterSharedSettings = OpenRouterProviderOptions & {
372
373
  */
373
374
  include: boolean;
374
375
  };
376
+ /**
377
+ * Default temperature for model calls. Controls randomness in the output.
378
+ * Can be overridden at call time via generateText/streamText options.
379
+ * Range: 0 to 2, where 0 is deterministic and higher values are more random.
380
+ */
381
+ temperature?: number;
382
+ /**
383
+ * Default top-p (nucleus sampling) for model calls.
384
+ * Can be overridden at call time via generateText/streamText options.
385
+ * Range: 0 to 1.
386
+ */
387
+ topP?: number;
388
+ /**
389
+ * Default top-k sampling for model calls.
390
+ * Can be overridden at call time via generateText/streamText options.
391
+ */
392
+ topK?: number;
393
+ /**
394
+ * Default frequency penalty for model calls.
395
+ * Can be overridden at call time via generateText/streamText options.
396
+ * Range: -2 to 2.
397
+ */
398
+ frequencyPenalty?: number;
399
+ /**
400
+ * Default presence penalty for model calls.
401
+ * Can be overridden at call time via generateText/streamText options.
402
+ * Range: -2 to 2.
403
+ */
404
+ presencePenalty?: number;
405
+ /**
406
+ * Default maximum number of tokens to generate.
407
+ * Can be overridden at call time via generateText/streamText options.
408
+ */
409
+ maxTokens?: number;
375
410
  };
376
411
  /**
377
412
  * Usage accounting response
@@ -613,6 +648,19 @@ declare class OpenRouterImageModel implements ImageModelV3 {
613
648
  }>;
614
649
  }
615
650
 
651
+ /**
652
+ * Configuration args for the web search provider tool.
653
+ * These are mapped to snake_case in the API request.
654
+ */
655
+ type WebSearchToolArgs = {
656
+ /** Maximum number of search results to include */
657
+ maxResults?: number;
658
+ /** Custom search prompt to guide the search query */
659
+ searchPrompt?: string;
660
+ /** Search engine to use: 'auto', 'native', or 'exa' */
661
+ engine?: 'auto' | Engine;
662
+ };
663
+
616
664
  interface OpenRouterProvider extends ProviderV3 {
617
665
  (modelId: OpenRouterChatModelId, settings?: OpenRouterCompletionSettings): OpenRouterCompletionLanguageModel;
618
666
  (modelId: OpenRouterChatModelId, settings?: OpenRouterChatSettings): OpenRouterChatLanguageModel;
@@ -639,6 +687,17 @@ interface OpenRouterProvider extends ProviderV3 {
639
687
  Creates an OpenRouter image model for image generation.
640
688
  */
641
689
  imageModel(modelId: OpenRouterImageModelId, settings?: OpenRouterImageSettings): OpenRouterImageModel;
690
+ /**
691
+ * Provider-defined tools for OpenRouter server tools.
692
+ */
693
+ readonly tools: {
694
+ /**
695
+ * Creates an OpenRouter web search server tool.
696
+ *
697
+ * @see https://openrouter.ai/docs/guides/features/server-tools/web-search
698
+ */
699
+ webSearch: ProviderToolFactory<unknown, WebSearchToolArgs>;
700
+ };
642
701
  }
643
702
  interface OpenRouterProviderSettings {
644
703
  /**
package/dist/index.js CHANGED
@@ -2109,6 +2109,46 @@ var postToApi = async ({
2109
2109
  throw handleFetchError({ error, url, requestBodyValues: body.values });
2110
2110
  }
2111
2111
  };
2112
+ function tool(tool2) {
2113
+ return tool2;
2114
+ }
2115
+ function createProviderToolFactory({
2116
+ id,
2117
+ inputSchema
2118
+ }) {
2119
+ return (_a16) => {
2120
+ var _b16 = _a16, {
2121
+ execute,
2122
+ outputSchema,
2123
+ needsApproval,
2124
+ toModelOutput,
2125
+ onInputStart,
2126
+ onInputDelta,
2127
+ onInputAvailable
2128
+ } = _b16, args = __objRest(_b16, [
2129
+ "execute",
2130
+ "outputSchema",
2131
+ "needsApproval",
2132
+ "toModelOutput",
2133
+ "onInputStart",
2134
+ "onInputDelta",
2135
+ "onInputAvailable"
2136
+ ]);
2137
+ return tool({
2138
+ type: "provider",
2139
+ id,
2140
+ args,
2141
+ inputSchema,
2142
+ outputSchema,
2143
+ execute,
2144
+ needsApproval,
2145
+ toModelOutput,
2146
+ onInputStart,
2147
+ onInputDelta,
2148
+ onInputAvailable
2149
+ });
2150
+ };
2151
+ }
2112
2152
  var createJsonErrorResponseHandler = ({
2113
2153
  errorSchema,
2114
2154
  errorToMessage,
@@ -2475,6 +2515,29 @@ function withStreamErrorHandling(source, onError) {
2475
2515
  });
2476
2516
  }
2477
2517
 
2518
+ // src/utils/deterministic-stringify.ts
2519
+ function deterministicStringify(value) {
2520
+ return JSON.stringify(sortKeys(value));
2521
+ }
2522
+ function sortKeys(value) {
2523
+ if (value === null || value === void 0) {
2524
+ return value;
2525
+ }
2526
+ if (Array.isArray(value)) {
2527
+ return value.map(sortKeys);
2528
+ }
2529
+ if (typeof value === "object") {
2530
+ const sorted = {};
2531
+ const entries = Object.entries(value);
2532
+ entries.sort(([a], [b]) => a.localeCompare(b));
2533
+ for (const [key, val] of entries) {
2534
+ sorted[key] = sortKeys(val);
2535
+ }
2536
+ return sorted;
2537
+ }
2538
+ return value;
2539
+ }
2540
+
2478
2541
  // src/utils/reasoning-details-duplicate-tracker.ts
2479
2542
  var _seenKeys;
2480
2543
  var ReasoningDetailsDuplicateTracker = class {
@@ -2806,7 +2869,7 @@ function convertToOpenRouterChatMessages(prompt) {
2806
2869
  type: "function",
2807
2870
  function: {
2808
2871
  name: part.toolName,
2809
- arguments: JSON.stringify(part.input)
2872
+ arguments: deterministicStringify(part.input)
2810
2873
  }
2811
2874
  });
2812
2875
  break;
@@ -3322,7 +3385,7 @@ var OpenRouterChatLanguageModel = class {
3322
3385
  tools,
3323
3386
  toolChoice
3324
3387
  }) {
3325
- var _a16;
3388
+ var _a16, _b16;
3326
3389
  const baseArgs = __spreadValues(__spreadValues({
3327
3390
  // model id:
3328
3391
  model: this.modelId,
@@ -3333,12 +3396,12 @@ var OpenRouterChatLanguageModel = class {
3333
3396
  top_logprobs: typeof this.settings.logprobs === "number" ? this.settings.logprobs : typeof this.settings.logprobs === "boolean" ? this.settings.logprobs ? 0 : void 0 : void 0,
3334
3397
  user: this.settings.user,
3335
3398
  parallel_tool_calls: this.settings.parallelToolCalls,
3336
- // standardized settings:
3337
- max_tokens: maxOutputTokens,
3338
- temperature,
3339
- top_p: topP,
3340
- frequency_penalty: frequencyPenalty,
3341
- presence_penalty: presencePenalty,
3399
+ // standardized settings (call-level options override model-level settings):
3400
+ max_tokens: maxOutputTokens != null ? maxOutputTokens : this.settings.maxTokens,
3401
+ temperature: temperature != null ? temperature : this.settings.temperature,
3402
+ top_p: topP != null ? topP : this.settings.topP,
3403
+ frequency_penalty: frequencyPenalty != null ? frequencyPenalty : this.settings.frequencyPenalty,
3404
+ presence_penalty: presencePenalty != null ? presencePenalty : this.settings.presencePenalty,
3342
3405
  seed,
3343
3406
  stop: stopSequences,
3344
3407
  response_format: (responseFormat == null ? void 0 : responseFormat.type) === "json" ? responseFormat.schema != null ? {
@@ -3351,7 +3414,7 @@ var OpenRouterChatLanguageModel = class {
3351
3414
  description: responseFormat.description
3352
3415
  })
3353
3416
  } : { type: "json_object" } : void 0,
3354
- top_k: topK,
3417
+ top_k: topK != null ? topK : this.settings.topK,
3355
3418
  // messages:
3356
3419
  messages: convertToOpenRouterChatMessages(prompt),
3357
3420
  // OpenRouter specific settings:
@@ -3369,16 +3432,25 @@ var OpenRouterChatLanguageModel = class {
3369
3432
  cache_control: this.settings.cache_control
3370
3433
  }, this.config.extraBody), this.settings.extraBody);
3371
3434
  if (tools && tools.length > 0) {
3372
- const mappedTools = tools.filter(
3373
- (tool) => tool.type === "function"
3374
- ).map((tool) => ({
3375
- type: "function",
3376
- function: {
3377
- name: tool.name,
3378
- description: tool.description,
3379
- parameters: tool.inputSchema
3435
+ const mappedTools = [];
3436
+ for (const tool2 of tools) {
3437
+ if (tool2.type === "function") {
3438
+ const openrouterOptions = (_b16 = tool2.providerOptions) == null ? void 0 : _b16.openrouter;
3439
+ const eagerInputStreaming = openrouterOptions == null ? void 0 : openrouterOptions.eager_input_streaming;
3440
+ mappedTools.push(__spreadValues({
3441
+ type: "function",
3442
+ function: {
3443
+ name: tool2.name,
3444
+ description: tool2.description,
3445
+ parameters: tool2.inputSchema
3446
+ }
3447
+ }, eagerInputStreaming != null && {
3448
+ eager_input_streaming: eagerInputStreaming
3449
+ }));
3450
+ } else if (tool2.type === "provider") {
3451
+ mappedTools.push(mapProviderTool(tool2));
3380
3452
  }
3381
- }));
3453
+ }
3382
3454
  return __spreadProps(__spreadValues({}, baseArgs), {
3383
3455
  tools: mappedTools,
3384
3456
  tool_choice: toolChoice ? getChatCompletionToolChoice(toolChoice) : void 0
@@ -3387,7 +3459,7 @@ var OpenRouterChatLanguageModel = class {
3387
3459
  return baseArgs;
3388
3460
  }
3389
3461
  async doGenerate(options) {
3390
- var _b16, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w;
3462
+ var _b16, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v;
3391
3463
  const providerOptions = options.providerOptions || {};
3392
3464
  const openrouterOptions = providerOptions.openrouter || {};
3393
3465
  const _a16 = openrouterOptions, { cacheControl } = _a16, restOpenrouterOptions = __objRest(_a16, ["cacheControl"]);
@@ -3483,12 +3555,18 @@ var OpenRouterChatLanguageModel = class {
3483
3555
  }
3484
3556
  if (choice.message.tool_calls) {
3485
3557
  let reasoningDetailsAttachedToToolCall = false;
3558
+ const seenToolCallIds = /* @__PURE__ */ new Set();
3486
3559
  for (const toolCall of choice.message.tool_calls) {
3560
+ let toolCallId = toolCall.id;
3561
+ if (!toolCallId || seenToolCallIds.has(toolCallId)) {
3562
+ toolCallId = generateId();
3563
+ }
3564
+ seenToolCallIds.add(toolCallId);
3487
3565
  content.push({
3488
3566
  type: "tool-call",
3489
- toolCallId: (_c = toolCall.id) != null ? _c : generateId(),
3567
+ toolCallId,
3490
3568
  toolName: toolCall.function.name,
3491
- input: (_d = toolCall.function.arguments) != null ? _d : "{}",
3569
+ input: (_c = toolCall.function.arguments) != null ? _c : "{}",
3492
3570
  providerMetadata: !reasoningDetailsAttachedToToolCall ? {
3493
3571
  openrouter: {
3494
3572
  reasoning_details: reasoningDetails
@@ -3515,19 +3593,19 @@ var OpenRouterChatLanguageModel = class {
3515
3593
  sourceType: "url",
3516
3594
  id: annotation.url_citation.url,
3517
3595
  url: annotation.url_citation.url,
3518
- title: (_e = annotation.url_citation.title) != null ? _e : "",
3596
+ title: (_d = annotation.url_citation.title) != null ? _d : "",
3519
3597
  providerMetadata: {
3520
3598
  openrouter: {
3521
- content: (_f = annotation.url_citation.content) != null ? _f : "",
3522
- startIndex: (_g = annotation.url_citation.start_index) != null ? _g : 0,
3523
- endIndex: (_h = annotation.url_citation.end_index) != null ? _h : 0
3599
+ content: (_e = annotation.url_citation.content) != null ? _e : "",
3600
+ startIndex: (_f = annotation.url_citation.start_index) != null ? _f : 0,
3601
+ endIndex: (_g = annotation.url_citation.end_index) != null ? _g : 0
3524
3602
  }
3525
3603
  }
3526
3604
  });
3527
3605
  }
3528
3606
  }
3529
3607
  }
3530
- const fileAnnotations = (_i = choice.message.annotations) == null ? void 0 : _i.filter(
3608
+ const fileAnnotations = (_h = choice.message.annotations) == null ? void 0 : _h.filter(
3531
3609
  (a) => a.type === "file"
3532
3610
  );
3533
3611
  const hasToolCalls = choice.message.tool_calls && choice.message.tool_calls.length > 0;
@@ -3535,7 +3613,7 @@ var OpenRouterChatLanguageModel = class {
3535
3613
  (d) => d.type === "reasoning.encrypted" /* Encrypted */ && d.data
3536
3614
  );
3537
3615
  const shouldOverrideFinishReason = hasToolCalls && hasEncryptedReasoning && choice.finish_reason === "stop";
3538
- const mappedFinishReason = shouldOverrideFinishReason ? createFinishReason("tool-calls", (_j = choice.finish_reason) != null ? _j : void 0) : mapOpenRouterFinishReason(choice.finish_reason);
3616
+ const mappedFinishReason = shouldOverrideFinishReason ? createFinishReason("tool-calls", (_i = choice.finish_reason) != null ? _i : void 0) : mapOpenRouterFinishReason(choice.finish_reason);
3539
3617
  const effectiveFinishReason = hasToolCalls && mappedFinishReason.unified === "other" ? createFinishReason("tool-calls", mappedFinishReason.raw) : mappedFinishReason;
3540
3618
  return {
3541
3619
  content,
@@ -3544,22 +3622,22 @@ var OpenRouterChatLanguageModel = class {
3544
3622
  warnings: [],
3545
3623
  providerMetadata: {
3546
3624
  openrouter: OpenRouterProviderMetadataSchema.parse({
3547
- provider: (_k = response.provider) != null ? _k : "",
3548
- reasoning_details: (_l = choice.message.reasoning_details) != null ? _l : [],
3625
+ provider: (_j = response.provider) != null ? _j : "",
3626
+ reasoning_details: (_k = choice.message.reasoning_details) != null ? _k : [],
3549
3627
  annotations: fileAnnotations && fileAnnotations.length > 0 ? fileAnnotations : void 0,
3550
3628
  usage: __spreadValues(__spreadValues(__spreadValues(__spreadValues({
3551
- promptTokens: (_m = usageInfo.inputTokens.total) != null ? _m : 0,
3552
- completionTokens: (_n = usageInfo.outputTokens.total) != null ? _n : 0,
3553
- totalTokens: ((_o = usageInfo.inputTokens.total) != null ? _o : 0) + ((_p = usageInfo.outputTokens.total) != null ? _p : 0)
3554
- }, ((_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 ? {
3629
+ promptTokens: (_l = usageInfo.inputTokens.total) != null ? _l : 0,
3630
+ completionTokens: (_m = usageInfo.outputTokens.total) != null ? _m : 0,
3631
+ totalTokens: ((_n = usageInfo.inputTokens.total) != null ? _n : 0) + ((_o = usageInfo.outputTokens.total) != null ? _o : 0)
3632
+ }, ((_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 ? {
3555
3633
  promptTokensDetails: {
3556
3634
  cachedTokens: response.usage.prompt_tokens_details.cached_tokens
3557
3635
  }
3558
- } : {}), ((_u = (_t = response.usage) == null ? void 0 : _t.completion_tokens_details) == null ? void 0 : _u.reasoning_tokens) != null ? {
3636
+ } : {}), ((_t = (_s = response.usage) == null ? void 0 : _s.completion_tokens_details) == null ? void 0 : _t.reasoning_tokens) != null ? {
3559
3637
  completionTokensDetails: {
3560
3638
  reasoningTokens: response.usage.completion_tokens_details.reasoning_tokens
3561
3639
  }
3562
- } : {}), ((_w = (_v = response.usage) == null ? void 0 : _v.cost_details) == null ? void 0 : _w.upstream_inference_cost) != null ? {
3640
+ } : {}), ((_v = (_u = response.usage) == null ? void 0 : _u.cost_details) == null ? void 0 : _v.upstream_inference_cost) != null ? {
3563
3641
  costDetails: {
3564
3642
  upstreamInferenceCost: response.usage.cost_details.upstream_inference_cost
3565
3643
  }
@@ -3606,6 +3684,7 @@ var OpenRouterChatLanguageModel = class {
3606
3684
  streamError = err;
3607
3685
  });
3608
3686
  const toolCalls = [];
3687
+ const seenToolCallIds = /* @__PURE__ */ new Set();
3609
3688
  let finishReason = createFinishReason("other");
3610
3689
  const usage = {
3611
3690
  inputTokens: {
@@ -3705,18 +3784,16 @@ var OpenRouterChatLanguageModel = class {
3705
3784
  return;
3706
3785
  }
3707
3786
  const delta = choice.delta;
3708
- const emitReasoningChunk = (chunkText, providerMetadata) => {
3787
+ const emitReasoningChunk = (chunkText) => {
3709
3788
  if (!reasoningStarted) {
3710
3789
  reasoningId = generateId();
3711
3790
  controller.enqueue({
3712
- providerMetadata,
3713
3791
  type: "reasoning-start",
3714
3792
  id: reasoningId
3715
3793
  });
3716
3794
  reasoningStarted = true;
3717
3795
  }
3718
3796
  controller.enqueue({
3719
- providerMetadata,
3720
3797
  type: "reasoning-delta",
3721
3798
  delta: chunkText,
3722
3799
  id: reasoningId || generateId()
@@ -3738,15 +3815,10 @@ var OpenRouterChatLanguageModel = class {
3738
3815
  }
3739
3816
  }
3740
3817
  if (!textStarted) {
3741
- const reasoningMetadata = {
3742
- openrouter: {
3743
- reasoning_details: accumulatedReasoningDetails.map((d) => __spreadValues({}, d))
3744
- }
3745
- };
3746
3818
  for (const detail of delta.reasoning_details) {
3747
3819
  switch (detail.type) {
3748
3820
  case "reasoning.text" /* Text */: {
3749
- emitReasoningChunk(detail.text || "", reasoningMetadata);
3821
+ emitReasoningChunk(detail.text || "");
3750
3822
  break;
3751
3823
  }
3752
3824
  case "reasoning.encrypted" /* Encrypted */: {
@@ -3754,7 +3826,7 @@ var OpenRouterChatLanguageModel = class {
3754
3826
  }
3755
3827
  case "reasoning.summary" /* Summary */: {
3756
3828
  if (detail.summary) {
3757
- emitReasoningChunk(detail.summary, reasoningMetadata);
3829
+ emitReasoningChunk(detail.summary);
3758
3830
  }
3759
3831
  break;
3760
3832
  }
@@ -3836,24 +3908,23 @@ var OpenRouterChatLanguageModel = class {
3836
3908
  message: `Expected 'function' type.`
3837
3909
  });
3838
3910
  }
3839
- if (toolCallDelta.id == null) {
3840
- throw new InvalidResponseDataError({
3841
- data: toolCallDelta,
3842
- message: `Expected 'id' to be a string.`
3843
- });
3844
- }
3845
3911
  if (((_k = toolCallDelta.function) == null ? void 0 : _k.name) == null) {
3846
3912
  throw new InvalidResponseDataError({
3847
3913
  data: toolCallDelta,
3848
3914
  message: `Expected 'function.name' to be a string.`
3849
3915
  });
3850
3916
  }
3917
+ let toolCallId = (_l = toolCallDelta.id) != null ? _l : "";
3918
+ if (!toolCallId || seenToolCallIds.has(toolCallId)) {
3919
+ toolCallId = generateId();
3920
+ }
3921
+ seenToolCallIds.add(toolCallId);
3851
3922
  toolCalls[index] = {
3852
- id: toolCallDelta.id,
3923
+ id: toolCallId,
3853
3924
  type: "function",
3854
3925
  function: {
3855
3926
  name: toolCallDelta.function.name,
3856
- arguments: (_l = toolCallDelta.function.arguments) != null ? _l : ""
3927
+ arguments: (_m = toolCallDelta.function.arguments) != null ? _m : ""
3857
3928
  },
3858
3929
  inputStarted: false,
3859
3930
  sent: false
@@ -3865,7 +3936,7 @@ var OpenRouterChatLanguageModel = class {
3865
3936
  message: `Tool call at index ${index} is missing after creation.`
3866
3937
  });
3867
3938
  }
3868
- if (((_m = toolCall2.function) == null ? void 0 : _m.name) != null && ((_n = toolCall2.function) == null ? void 0 : _n.arguments) != null && isParsableJson(toolCall2.function.arguments)) {
3939
+ if (((_n = toolCall2.function) == null ? void 0 : _n.name) != null && ((_o = toolCall2.function) == null ? void 0 : _o.arguments) != null && isParsableJson(toolCall2.function.arguments)) {
3869
3940
  toolCall2.inputStarted = true;
3870
3941
  controller.enqueue({
3871
3942
  type: "tool-input-start",
@@ -3923,22 +3994,22 @@ var OpenRouterChatLanguageModel = class {
3923
3994
  });
3924
3995
  }
3925
3996
  }
3926
- if (((_o = toolCallDelta.function) == null ? void 0 : _o.arguments) != null) {
3927
- toolCall.function.arguments += (_q = (_p = toolCallDelta.function) == null ? void 0 : _p.arguments) != null ? _q : "";
3997
+ if (((_p = toolCallDelta.function) == null ? void 0 : _p.arguments) != null) {
3998
+ toolCall.function.arguments += (_r = (_q = toolCallDelta.function) == null ? void 0 : _q.arguments) != null ? _r : "";
3928
3999
  }
3929
4000
  controller.enqueue({
3930
4001
  type: "tool-input-delta",
3931
4002
  id: toolCall.id,
3932
- delta: (_r = toolCallDelta.function.arguments) != null ? _r : ""
4003
+ delta: (_s = toolCallDelta.function.arguments) != null ? _s : ""
3933
4004
  });
3934
- if (((_s = toolCall.function) == null ? void 0 : _s.name) != null && ((_t = toolCall.function) == null ? void 0 : _t.arguments) != null && isParsableJson(toolCall.function.arguments)) {
4005
+ if (((_t = toolCall.function) == null ? void 0 : _t.name) != null && ((_u = toolCall.function) == null ? void 0 : _u.arguments) != null && isParsableJson(toolCall.function.arguments)) {
3935
4006
  controller.enqueue({
3936
4007
  type: "tool-input-end",
3937
4008
  id: toolCall.id
3938
4009
  });
3939
4010
  controller.enqueue({
3940
4011
  type: "tool-call",
3941
- toolCallId: (_u = toolCall.id) != null ? _u : generateId(),
4012
+ toolCallId: toolCall.id,
3942
4013
  toolName: toolCall.function.name,
3943
4014
  input: toolCall.function.arguments,
3944
4015
  providerMetadata: !reasoningDetailsAttachedToToolCall ? {
@@ -3963,7 +4034,6 @@ var OpenRouterChatLanguageModel = class {
3963
4034
  }
3964
4035
  },
3965
4036
  flush(controller) {
3966
- var _a17;
3967
4037
  const hasToolCalls = toolCalls.length > 0;
3968
4038
  if (streamError != null) {
3969
4039
  finishReason = createFinishReason("error");
@@ -4000,7 +4070,7 @@ var OpenRouterChatLanguageModel = class {
4000
4070
  });
4001
4071
  controller.enqueue({
4002
4072
  type: "tool-call",
4003
- toolCallId: (_a17 = toolCall.id) != null ? _a17 : generateId(),
4073
+ toolCallId: toolCall.id,
4004
4074
  toolName: toolCall.function.name,
4005
4075
  input,
4006
4076
  providerMetadata: !reasoningDetailsAttachedToToolCall ? {
@@ -4045,6 +4115,12 @@ var OpenRouterChatLanguageModel = class {
4045
4115
  if (accumulatedFileAnnotations.length > 0) {
4046
4116
  openrouterMetadata.annotations = accumulatedFileAnnotations;
4047
4117
  }
4118
+ if (usage.inputTokens.total === void 0 && openrouterUsage.promptTokens !== void 0) {
4119
+ usage.inputTokens.total = openrouterUsage.promptTokens;
4120
+ }
4121
+ if (usage.outputTokens.total === void 0 && openrouterUsage.completionTokens !== void 0) {
4122
+ usage.outputTokens.total = openrouterUsage.completionTokens;
4123
+ }
4048
4124
  usage.raw = rawUsage;
4049
4125
  controller.enqueue({
4050
4126
  type: "finish",
@@ -4063,6 +4139,22 @@ var OpenRouterChatLanguageModel = class {
4063
4139
  };
4064
4140
  }
4065
4141
  };
4142
+ function mapProviderTool(tool2) {
4143
+ const [provider, toolName] = tool2.id.split(".");
4144
+ const apiToolType = `${provider}:${toolName}`;
4145
+ const mappedArgs = {};
4146
+ for (const [key, value] of Object.entries(tool2.args)) {
4147
+ if (value !== void 0) {
4148
+ mappedArgs[camelToSnake(key)] = value;
4149
+ }
4150
+ }
4151
+ return __spreadValues({
4152
+ type: apiToolType
4153
+ }, mappedArgs);
4154
+ }
4155
+ function camelToSnake(str) {
4156
+ return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
4157
+ }
4066
4158
 
4067
4159
  // src/completion/convert-to-openrouter-completion-prompt.ts
4068
4160
  function convertToOpenRouterCompletionPrompt({
@@ -4264,16 +4356,16 @@ var OpenRouterCompletionLanguageModel = class {
4264
4356
  logprobs: typeof this.settings.logprobs === "number" ? this.settings.logprobs : typeof this.settings.logprobs === "boolean" ? this.settings.logprobs ? 0 : void 0 : void 0,
4265
4357
  suffix: this.settings.suffix,
4266
4358
  user: this.settings.user,
4267
- // standardized settings:
4268
- max_tokens: maxOutputTokens,
4269
- temperature,
4270
- top_p: topP,
4271
- frequency_penalty: frequencyPenalty,
4272
- presence_penalty: presencePenalty,
4359
+ // standardized settings (call-level options override model-level settings):
4360
+ max_tokens: maxOutputTokens != null ? maxOutputTokens : this.settings.maxTokens,
4361
+ temperature: temperature != null ? temperature : this.settings.temperature,
4362
+ top_p: topP != null ? topP : this.settings.topP,
4363
+ frequency_penalty: frequencyPenalty != null ? frequencyPenalty : this.settings.frequencyPenalty,
4364
+ presence_penalty: presencePenalty != null ? presencePenalty : this.settings.presencePenalty,
4273
4365
  seed,
4274
4366
  stop: stopSequences,
4275
4367
  response_format: responseFormat,
4276
- top_k: topK,
4368
+ top_k: topK != null ? topK : this.settings.topK,
4277
4369
  // prompt:
4278
4370
  prompt: completionPrompt,
4279
4371
  // OpenRouter specific settings:
@@ -4791,6 +4883,17 @@ function convertImageFileToContentPart(file) {
4791
4883
  };
4792
4884
  }
4793
4885
 
4886
+ // src/tool/web-search.ts
4887
+ var import_v410 = require("zod/v4");
4888
+ var webSearchInputSchema = import_v410.z.object({
4889
+ /** Search results returned by the server tool */
4890
+ results: import_v410.z.array(import_v410.z.unknown()).optional()
4891
+ });
4892
+ var webSearch = createProviderToolFactory({
4893
+ id: "openrouter.web_search",
4894
+ inputSchema: webSearchInputSchema
4895
+ });
4896
+
4794
4897
  // src/utils/remove-undefined.ts
4795
4898
  function removeUndefinedEntries(record) {
4796
4899
  return Object.fromEntries(
@@ -4832,7 +4935,7 @@ function withUserAgentSuffix2(headers, ...userAgentSuffixParts) {
4832
4935
  }
4833
4936
 
4834
4937
  // src/version.ts
4835
- var VERSION2 = false ? "0.0.0-test" : "2.5.1";
4938
+ var VERSION2 = false ? "0.0.0-test" : "2.7.0";
4836
4939
 
4837
4940
  // src/provider.ts
4838
4941
  function createOpenRouter(options = {}) {
@@ -4902,6 +5005,9 @@ function createOpenRouter(options = {}) {
4902
5005
  provider.textEmbeddingModel = createEmbeddingModel;
4903
5006
  provider.embedding = createEmbeddingModel;
4904
5007
  provider.imageModel = createImageModel;
5008
+ provider.tools = {
5009
+ webSearch
5010
+ };
4905
5011
  return provider;
4906
5012
  }
4907
5013
  var openrouter = createOpenRouter({