@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.
@@ -6,6 +6,7 @@ import { z } from 'zod/v4';
6
6
  declare enum ReasoningFormat {
7
7
  Unknown = "unknown",
8
8
  OpenAIResponsesV1 = "openai-responses-v1",
9
+ AzureOpenAIResponsesV1 = "azure-openai-responses-v1",
9
10
  XAIResponsesV1 = "xai-responses-v1",
10
11
  AnthropicClaudeV1 = "anthropic-claude-v1",
11
12
  GoogleGeminiV1 = "google-gemini-v1"
@@ -6,6 +6,7 @@ import { z } from 'zod/v4';
6
6
  declare enum ReasoningFormat {
7
7
  Unknown = "unknown",
8
8
  OpenAIResponsesV1 = "openai-responses-v1",
9
+ AzureOpenAIResponsesV1 = "azure-openai-responses-v1",
9
10
  XAIResponsesV1 = "xai-responses-v1",
10
11
  AnthropicClaudeV1 = "anthropic-claude-v1",
11
12
  GoogleGeminiV1 = "google-gemini-v1"
@@ -2182,11 +2182,13 @@ function isDefinedOrNotNull(value) {
2182
2182
  var ReasoningFormat = /* @__PURE__ */ ((ReasoningFormat2) => {
2183
2183
  ReasoningFormat2["Unknown"] = "unknown";
2184
2184
  ReasoningFormat2["OpenAIResponsesV1"] = "openai-responses-v1";
2185
+ ReasoningFormat2["AzureOpenAIResponsesV1"] = "azure-openai-responses-v1";
2185
2186
  ReasoningFormat2["XAIResponsesV1"] = "xai-responses-v1";
2186
2187
  ReasoningFormat2["AnthropicClaudeV1"] = "anthropic-claude-v1";
2187
2188
  ReasoningFormat2["GoogleGeminiV1"] = "google-gemini-v1";
2188
2189
  return ReasoningFormat2;
2189
2190
  })(ReasoningFormat || {});
2191
+ var DEFAULT_REASONING_FORMAT = "anthropic-claude-v1" /* AnthropicClaudeV1 */;
2190
2192
 
2191
2193
  // src/schemas/reasoning-details.ts
2192
2194
  var CommonReasoningDetailSchema = import_v4.z.object({
@@ -2339,7 +2341,11 @@ var OpenRouterProviderMetadataSchema = import_v43.z.object({
2339
2341
  }).catchall(import_v43.z.any());
2340
2342
  var OpenRouterProviderOptionsSchema = import_v43.z.object({
2341
2343
  openrouter: import_v43.z.object({
2342
- reasoning_details: import_v43.z.array(ReasoningDetailUnionSchema).optional(),
2344
+ // Use ReasoningDetailArraySchema (with unknown fallback) instead of
2345
+ // z.array(ReasoningDetailUnionSchema) so that a single malformed entry
2346
+ // (e.g., a future format not yet in the enum) is individually dropped
2347
+ // rather than causing the entire array to fail parsing.
2348
+ reasoning_details: ReasoningDetailArraySchema.optional(),
2343
2349
  annotations: import_v43.z.array(FileAnnotationSchema).optional()
2344
2350
  }).optional()
2345
2351
  }).optional();
@@ -2410,6 +2416,31 @@ function createFinishReason(unified, raw) {
2410
2416
  return { unified, raw };
2411
2417
  }
2412
2418
 
2419
+ // src/utils/with-stream-error-handling.ts
2420
+ function withStreamErrorHandling(source, onError) {
2421
+ const reader = source.getReader();
2422
+ return new ReadableStream({
2423
+ async pull(controller) {
2424
+ try {
2425
+ const { done, value } = await reader.read();
2426
+ if (done) {
2427
+ controller.close();
2428
+ } else {
2429
+ controller.enqueue(value);
2430
+ }
2431
+ } catch (err) {
2432
+ onError(err);
2433
+ reader.cancel().catch(() => {
2434
+ });
2435
+ controller.close();
2436
+ }
2437
+ },
2438
+ cancel(reason) {
2439
+ reader.cancel(reason);
2440
+ }
2441
+ });
2442
+ }
2443
+
2413
2444
  // src/utils/reasoning-details-duplicate-tracker.ts
2414
2445
  var _seenKeys;
2415
2446
  var ReasoningDetailsDuplicateTracker = class {
@@ -2751,8 +2782,24 @@ function convertToOpenRouterChatMessages(prompt) {
2751
2782
  const candidateReasoningDetails = messageReasoningDetails && Array.isArray(messageReasoningDetails) && messageReasoningDetails.length > 0 ? messageReasoningDetails : findFirstReasoningDetails(content);
2752
2783
  let finalReasoningDetails;
2753
2784
  if (candidateReasoningDetails && candidateReasoningDetails.length > 0) {
2785
+ const validDetails = candidateReasoningDetails.filter((detail) => {
2786
+ var _a17;
2787
+ if (detail.type !== "reasoning.text" /* Text */) {
2788
+ return true;
2789
+ }
2790
+ const format = (_a17 = detail.format) != null ? _a17 : DEFAULT_REASONING_FORMAT;
2791
+ if (format !== "anthropic-claude-v1" /* AnthropicClaudeV1 */) {
2792
+ return true;
2793
+ }
2794
+ return !!detail.signature;
2795
+ });
2796
+ if (validDetails.length < candidateReasoningDetails.length) {
2797
+ console.warn(
2798
+ "[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."
2799
+ );
2800
+ }
2754
2801
  const uniqueDetails = [];
2755
- for (const detail of candidateReasoningDetails) {
2802
+ for (const detail of validDetails) {
2756
2803
  if (reasoningDetailsTracker.upsert(detail)) {
2757
2804
  uniqueDetails.push(detail);
2758
2805
  }
@@ -2781,6 +2828,7 @@ function convertToOpenRouterChatMessages(prompt) {
2781
2828
  role: "tool",
2782
2829
  tool_call_id: toolResponse.toolCallId,
2783
2830
  content: content2,
2831
+ name: toolResponse.toolName,
2784
2832
  cache_control: (_h = getCacheControl(providerOptions)) != null ? _h : getCacheControl(toolResponse.providerOptions)
2785
2833
  });
2786
2834
  }
@@ -2922,13 +2970,14 @@ function filenameFromUrl(url) {
2922
2970
  }
2923
2971
  }
2924
2972
  function findFirstReasoningDetails(content) {
2925
- var _a16, _b16, _c;
2973
+ var _a16, _b16, _c, _d;
2926
2974
  for (const part of content) {
2927
2975
  if (part.type === "tool-call") {
2928
- const openrouter = (_a16 = part.providerOptions) == null ? void 0 : _a16.openrouter;
2929
- const details = openrouter == null ? void 0 : openrouter.reasoning_details;
2930
- if (Array.isArray(details) && details.length > 0) {
2931
- return details;
2976
+ const parsed = OpenRouterProviderOptionsSchema.safeParse(
2977
+ part.providerOptions
2978
+ );
2979
+ 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) {
2980
+ return parsed.data.openrouter.reasoning_details;
2932
2981
  }
2933
2982
  }
2934
2983
  }
@@ -2937,7 +2986,7 @@ function findFirstReasoningDetails(content) {
2937
2986
  const parsed = OpenRouterProviderOptionsSchema.safeParse(
2938
2987
  part.providerOptions
2939
2988
  );
2940
- 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) {
2989
+ 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) {
2941
2990
  return parsed.data.openrouter.reasoning_details;
2942
2991
  }
2943
2992
  }
@@ -3442,7 +3491,8 @@ var OpenRouterChatLanguageModel = class {
3442
3491
  (d) => d.type === "reasoning.encrypted" /* Encrypted */ && d.data
3443
3492
  );
3444
3493
  const shouldOverrideFinishReason = hasToolCalls && hasEncryptedReasoning && choice.finish_reason === "stop";
3445
- const effectiveFinishReason = shouldOverrideFinishReason ? createFinishReason("tool-calls", (_j = choice.finish_reason) != null ? _j : void 0) : mapOpenRouterFinishReason(choice.finish_reason);
3494
+ const mappedFinishReason = shouldOverrideFinishReason ? createFinishReason("tool-calls", (_j = choice.finish_reason) != null ? _j : void 0) : mapOpenRouterFinishReason(choice.finish_reason);
3495
+ const effectiveFinishReason = hasToolCalls && mappedFinishReason.unified === "other" ? createFinishReason("tool-calls", mappedFinishReason.raw) : mappedFinishReason;
3446
3496
  return {
3447
3497
  content,
3448
3498
  finishReason: effectiveFinishReason,
@@ -3506,6 +3556,10 @@ var OpenRouterChatLanguageModel = class {
3506
3556
  abortSignal: options.abortSignal,
3507
3557
  fetch: this.config.fetch
3508
3558
  });
3559
+ let streamError;
3560
+ const safeResponse = withStreamErrorHandling(response, (err) => {
3561
+ streamError = err;
3562
+ });
3509
3563
  const toolCalls = [];
3510
3564
  let finishReason = createFinishReason("other");
3511
3565
  const usage = {
@@ -3534,7 +3588,7 @@ var OpenRouterChatLanguageModel = class {
3534
3588
  let openrouterResponseId;
3535
3589
  let provider;
3536
3590
  return {
3537
- stream: response.pipeThrough(
3591
+ stream: safeResponse.pipeThrough(
3538
3592
  new TransformStream({
3539
3593
  transform(chunk, controller) {
3540
3594
  var _a17, _b17, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u;
@@ -3856,12 +3910,19 @@ var OpenRouterChatLanguageModel = class {
3856
3910
  flush(controller) {
3857
3911
  var _a17;
3858
3912
  const hasToolCalls = toolCalls.length > 0;
3913
+ if (streamError != null) {
3914
+ finishReason = createFinishReason("error");
3915
+ controller.enqueue({ type: "error", error: streamError });
3916
+ }
3859
3917
  const hasEncryptedReasoning = accumulatedReasoningDetails.some(
3860
3918
  (d) => d.type === "reasoning.encrypted" /* Encrypted */ && d.data
3861
3919
  );
3862
3920
  if (hasToolCalls && hasEncryptedReasoning && finishReason.unified === "stop") {
3863
3921
  finishReason = createFinishReason("tool-calls", finishReason.raw);
3864
3922
  }
3923
+ if (hasToolCalls && finishReason.unified === "other") {
3924
+ finishReason = createFinishReason("tool-calls", finishReason.raw);
3925
+ }
3865
3926
  if (finishReason.unified === "tool-calls") {
3866
3927
  for (const toolCall of toolCalls) {
3867
3928
  if (toolCall && !toolCall.sent) {
@@ -4247,6 +4308,10 @@ var OpenRouterCompletionLanguageModel = class {
4247
4308
  abortSignal: options.abortSignal,
4248
4309
  fetch: this.config.fetch
4249
4310
  });
4311
+ let streamError;
4312
+ const safeResponse = withStreamErrorHandling(response, (err) => {
4313
+ streamError = err;
4314
+ });
4250
4315
  let finishReason = createFinishReason("other");
4251
4316
  const usage = {
4252
4317
  inputTokens: {
@@ -4266,7 +4331,7 @@ var OpenRouterCompletionLanguageModel = class {
4266
4331
  let provider;
4267
4332
  let rawUsage;
4268
4333
  return {
4269
- stream: response.pipeThrough(
4334
+ stream: safeResponse.pipeThrough(
4270
4335
  new TransformStream({
4271
4336
  transform(chunk, controller) {
4272
4337
  var _a16, _b16, _c, _d, _e;
@@ -4330,6 +4395,10 @@ var OpenRouterCompletionLanguageModel = class {
4330
4395
  }
4331
4396
  },
4332
4397
  flush(controller) {
4398
+ if (streamError != null) {
4399
+ finishReason = createFinishReason("error");
4400
+ controller.enqueue({ type: "error", error: streamError });
4401
+ }
4333
4402
  usage.raw = rawUsage;
4334
4403
  const openrouterMetadata = {
4335
4404
  usage: openrouterUsage