@openrouter/ai-sdk-provider 2.3.2 → 2.4.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.
@@ -2148,11 +2148,13 @@ function isDefinedOrNotNull(value) {
2148
2148
  var ReasoningFormat = /* @__PURE__ */ ((ReasoningFormat2) => {
2149
2149
  ReasoningFormat2["Unknown"] = "unknown";
2150
2150
  ReasoningFormat2["OpenAIResponsesV1"] = "openai-responses-v1";
2151
+ ReasoningFormat2["AzureOpenAIResponsesV1"] = "azure-openai-responses-v1";
2151
2152
  ReasoningFormat2["XAIResponsesV1"] = "xai-responses-v1";
2152
2153
  ReasoningFormat2["AnthropicClaudeV1"] = "anthropic-claude-v1";
2153
2154
  ReasoningFormat2["GoogleGeminiV1"] = "google-gemini-v1";
2154
2155
  return ReasoningFormat2;
2155
2156
  })(ReasoningFormat || {});
2157
+ var DEFAULT_REASONING_FORMAT = "anthropic-claude-v1" /* AnthropicClaudeV1 */;
2156
2158
 
2157
2159
  // src/schemas/reasoning-details.ts
2158
2160
  var CommonReasoningDetailSchema = z.object({
@@ -2305,7 +2307,11 @@ var OpenRouterProviderMetadataSchema = z3.object({
2305
2307
  }).catchall(z3.any());
2306
2308
  var OpenRouterProviderOptionsSchema = z3.object({
2307
2309
  openrouter: z3.object({
2308
- reasoning_details: z3.array(ReasoningDetailUnionSchema).optional(),
2310
+ // Use ReasoningDetailArraySchema (with unknown fallback) instead of
2311
+ // z.array(ReasoningDetailUnionSchema) so that a single malformed entry
2312
+ // (e.g., a future format not yet in the enum) is individually dropped
2313
+ // rather than causing the entire array to fail parsing.
2314
+ reasoning_details: ReasoningDetailArraySchema.optional(),
2309
2315
  annotations: z3.array(FileAnnotationSchema).optional()
2310
2316
  }).optional()
2311
2317
  }).optional();
@@ -2376,6 +2382,31 @@ function createFinishReason(unified, raw) {
2376
2382
  return { unified, raw };
2377
2383
  }
2378
2384
 
2385
+ // src/utils/with-stream-error-handling.ts
2386
+ function withStreamErrorHandling(source, onError) {
2387
+ const reader = source.getReader();
2388
+ return new ReadableStream({
2389
+ async pull(controller) {
2390
+ try {
2391
+ const { done, value } = await reader.read();
2392
+ if (done) {
2393
+ controller.close();
2394
+ } else {
2395
+ controller.enqueue(value);
2396
+ }
2397
+ } catch (err) {
2398
+ onError(err);
2399
+ reader.cancel().catch(() => {
2400
+ });
2401
+ controller.close();
2402
+ }
2403
+ },
2404
+ cancel(reason) {
2405
+ reader.cancel(reason);
2406
+ }
2407
+ });
2408
+ }
2409
+
2379
2410
  // src/utils/reasoning-details-duplicate-tracker.ts
2380
2411
  var _seenKeys;
2381
2412
  var ReasoningDetailsDuplicateTracker = class {
@@ -2717,8 +2748,24 @@ function convertToOpenRouterChatMessages(prompt) {
2717
2748
  const candidateReasoningDetails = messageReasoningDetails && Array.isArray(messageReasoningDetails) && messageReasoningDetails.length > 0 ? messageReasoningDetails : findFirstReasoningDetails(content);
2718
2749
  let finalReasoningDetails;
2719
2750
  if (candidateReasoningDetails && candidateReasoningDetails.length > 0) {
2751
+ const validDetails = candidateReasoningDetails.filter((detail) => {
2752
+ var _a17;
2753
+ if (detail.type !== "reasoning.text" /* Text */) {
2754
+ return true;
2755
+ }
2756
+ const format = (_a17 = detail.format) != null ? _a17 : DEFAULT_REASONING_FORMAT;
2757
+ if (format !== "anthropic-claude-v1" /* AnthropicClaudeV1 */) {
2758
+ return true;
2759
+ }
2760
+ return !!detail.signature;
2761
+ });
2762
+ if (validDetails.length < candidateReasoningDetails.length) {
2763
+ console.warn(
2764
+ "[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."
2765
+ );
2766
+ }
2720
2767
  const uniqueDetails = [];
2721
- for (const detail of candidateReasoningDetails) {
2768
+ for (const detail of validDetails) {
2722
2769
  if (reasoningDetailsTracker.upsert(detail)) {
2723
2770
  uniqueDetails.push(detail);
2724
2771
  }
@@ -2747,6 +2794,7 @@ function convertToOpenRouterChatMessages(prompt) {
2747
2794
  role: "tool",
2748
2795
  tool_call_id: toolResponse.toolCallId,
2749
2796
  content: content2,
2797
+ name: toolResponse.toolName,
2750
2798
  cache_control: (_h = getCacheControl(providerOptions)) != null ? _h : getCacheControl(toolResponse.providerOptions)
2751
2799
  });
2752
2800
  }
@@ -2888,13 +2936,14 @@ function filenameFromUrl(url) {
2888
2936
  }
2889
2937
  }
2890
2938
  function findFirstReasoningDetails(content) {
2891
- var _a16, _b16, _c;
2939
+ var _a16, _b16, _c, _d;
2892
2940
  for (const part of content) {
2893
2941
  if (part.type === "tool-call") {
2894
- const openrouter = (_a16 = part.providerOptions) == null ? void 0 : _a16.openrouter;
2895
- const details = openrouter == null ? void 0 : openrouter.reasoning_details;
2896
- if (Array.isArray(details) && details.length > 0) {
2897
- return details;
2942
+ const parsed = OpenRouterProviderOptionsSchema.safeParse(
2943
+ part.providerOptions
2944
+ );
2945
+ if (parsed.success && ((_b16 = (_a16 = parsed.data) == null ? void 0 : _a16.openrouter) == null ? void 0 : _b16.reasoning_details) && parsed.data.openrouter.reasoning_details.length > 0) {
2946
+ return parsed.data.openrouter.reasoning_details;
2898
2947
  }
2899
2948
  }
2900
2949
  }
@@ -2903,7 +2952,7 @@ function findFirstReasoningDetails(content) {
2903
2952
  const parsed = OpenRouterProviderOptionsSchema.safeParse(
2904
2953
  part.providerOptions
2905
2954
  );
2906
- if (parsed.success && ((_c = (_b16 = parsed.data) == null ? void 0 : _b16.openrouter) == null ? void 0 : _c.reasoning_details) && parsed.data.openrouter.reasoning_details.length > 0) {
2955
+ if (parsed.success && ((_d = (_c = parsed.data) == null ? void 0 : _c.openrouter) == null ? void 0 : _d.reasoning_details) && parsed.data.openrouter.reasoning_details.length > 0) {
2907
2956
  return parsed.data.openrouter.reasoning_details;
2908
2957
  }
2909
2958
  }
@@ -3408,7 +3457,8 @@ var OpenRouterChatLanguageModel = class {
3408
3457
  (d) => d.type === "reasoning.encrypted" /* Encrypted */ && d.data
3409
3458
  );
3410
3459
  const shouldOverrideFinishReason = hasToolCalls && hasEncryptedReasoning && choice.finish_reason === "stop";
3411
- const effectiveFinishReason = shouldOverrideFinishReason ? createFinishReason("tool-calls", (_j = choice.finish_reason) != null ? _j : void 0) : mapOpenRouterFinishReason(choice.finish_reason);
3460
+ const mappedFinishReason = shouldOverrideFinishReason ? createFinishReason("tool-calls", (_j = choice.finish_reason) != null ? _j : void 0) : mapOpenRouterFinishReason(choice.finish_reason);
3461
+ const effectiveFinishReason = hasToolCalls && mappedFinishReason.unified === "other" ? createFinishReason("tool-calls", mappedFinishReason.raw) : mappedFinishReason;
3412
3462
  return {
3413
3463
  content,
3414
3464
  finishReason: effectiveFinishReason,
@@ -3472,6 +3522,10 @@ var OpenRouterChatLanguageModel = class {
3472
3522
  abortSignal: options.abortSignal,
3473
3523
  fetch: this.config.fetch
3474
3524
  });
3525
+ let streamError;
3526
+ const safeResponse = withStreamErrorHandling(response, (err) => {
3527
+ streamError = err;
3528
+ });
3475
3529
  const toolCalls = [];
3476
3530
  let finishReason = createFinishReason("other");
3477
3531
  const usage = {
@@ -3500,7 +3554,7 @@ var OpenRouterChatLanguageModel = class {
3500
3554
  let openrouterResponseId;
3501
3555
  let provider;
3502
3556
  return {
3503
- stream: response.pipeThrough(
3557
+ stream: safeResponse.pipeThrough(
3504
3558
  new TransformStream({
3505
3559
  transform(chunk, controller) {
3506
3560
  var _a17, _b17, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u;
@@ -3822,12 +3876,19 @@ var OpenRouterChatLanguageModel = class {
3822
3876
  flush(controller) {
3823
3877
  var _a17;
3824
3878
  const hasToolCalls = toolCalls.length > 0;
3879
+ if (streamError != null) {
3880
+ finishReason = createFinishReason("error");
3881
+ controller.enqueue({ type: "error", error: streamError });
3882
+ }
3825
3883
  const hasEncryptedReasoning = accumulatedReasoningDetails.some(
3826
3884
  (d) => d.type === "reasoning.encrypted" /* Encrypted */ && d.data
3827
3885
  );
3828
3886
  if (hasToolCalls && hasEncryptedReasoning && finishReason.unified === "stop") {
3829
3887
  finishReason = createFinishReason("tool-calls", finishReason.raw);
3830
3888
  }
3889
+ if (hasToolCalls && finishReason.unified === "other") {
3890
+ finishReason = createFinishReason("tool-calls", finishReason.raw);
3891
+ }
3831
3892
  if (finishReason.unified === "tool-calls") {
3832
3893
  for (const toolCall of toolCalls) {
3833
3894
  if (toolCall && !toolCall.sent) {
@@ -4213,6 +4274,10 @@ var OpenRouterCompletionLanguageModel = class {
4213
4274
  abortSignal: options.abortSignal,
4214
4275
  fetch: this.config.fetch
4215
4276
  });
4277
+ let streamError;
4278
+ const safeResponse = withStreamErrorHandling(response, (err) => {
4279
+ streamError = err;
4280
+ });
4216
4281
  let finishReason = createFinishReason("other");
4217
4282
  const usage = {
4218
4283
  inputTokens: {
@@ -4232,7 +4297,7 @@ var OpenRouterCompletionLanguageModel = class {
4232
4297
  let provider;
4233
4298
  let rawUsage;
4234
4299
  return {
4235
- stream: response.pipeThrough(
4300
+ stream: safeResponse.pipeThrough(
4236
4301
  new TransformStream({
4237
4302
  transform(chunk, controller) {
4238
4303
  var _a16, _b16, _c, _d, _e;
@@ -4296,6 +4361,10 @@ var OpenRouterCompletionLanguageModel = class {
4296
4361
  }
4297
4362
  },
4298
4363
  flush(controller) {
4364
+ if (streamError != null) {
4365
+ finishReason = createFinishReason("error");
4366
+ controller.enqueue({ type: "error", error: streamError });
4367
+ }
4299
4368
  usage.raw = rawUsage;
4300
4369
  const openrouterMetadata = {
4301
4370
  usage: openrouterUsage