@openrouter/ai-sdk-provider 2.8.0 → 2.9.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
@@ -184,6 +184,25 @@ type OpenRouterChatSettings = {
184
184
  */
185
185
  echo_upstream_body?: boolean;
186
186
  };
187
+ /**
188
+ * Structured-output options forwarded to OpenRouter's
189
+ * `response_format.json_schema` payload.
190
+ *
191
+ * Use this to opt out of strict mode for models whose providers don't
192
+ * advertise support for it (e.g. open-source models routed through
193
+ * non-OpenAI-compatible providers). When `strict` is left unset, the
194
+ * SDK defaults to `true` for backward compatibility.
195
+ *
196
+ * Only applies when a `responseFormat` with a `schema` is provided on
197
+ * the call site. Has no effect for `json_object` or text responses.
198
+ */
199
+ structuredOutputs?: {
200
+ /**
201
+ * Whether to set `response_format.json_schema.strict` on the outbound
202
+ * request. Defaults to `true` when omitted.
203
+ */
204
+ strict?: boolean;
205
+ };
187
206
  /**
188
207
  * Provider routing preferences to control request routing behavior
189
208
  */
package/dist/index.d.ts CHANGED
@@ -184,6 +184,25 @@ type OpenRouterChatSettings = {
184
184
  */
185
185
  echo_upstream_body?: boolean;
186
186
  };
187
+ /**
188
+ * Structured-output options forwarded to OpenRouter's
189
+ * `response_format.json_schema` payload.
190
+ *
191
+ * Use this to opt out of strict mode for models whose providers don't
192
+ * advertise support for it (e.g. open-source models routed through
193
+ * non-OpenAI-compatible providers). When `strict` is left unset, the
194
+ * SDK defaults to `true` for backward compatibility.
195
+ *
196
+ * Only applies when a `responseFormat` with a `schema` is provided on
197
+ * the call site. Has no effect for `json_object` or text responses.
198
+ */
199
+ structuredOutputs?: {
200
+ /**
201
+ * Whether to set `response_format.json_schema.strict` on the outbound
202
+ * request. Defaults to `true` when omitted.
203
+ */
204
+ strict?: boolean;
205
+ };
187
206
  /**
188
207
  * Provider routing preferences to control request routing behavior
189
208
  */
package/dist/index.js CHANGED
@@ -3053,9 +3053,9 @@ function convertToOpenRouterChatMessages(prompt) {
3053
3053
  const parsedProviderOptions = OpenRouterProviderOptionsSchema.safeParse(providerOptions);
3054
3054
  const messageReasoningDetails = parsedProviderOptions.success ? (_e = (_d = parsedProviderOptions.data) == null ? void 0 : _d.openrouter) == null ? void 0 : _e.reasoning_details : void 0;
3055
3055
  const messageAnnotations = parsedProviderOptions.success ? (_g = (_f = parsedProviderOptions.data) == null ? void 0 : _f.openrouter) == null ? void 0 : _g.annotations : void 0;
3056
- const candidateReasoningDetails = messageReasoningDetails && Array.isArray(messageReasoningDetails) && messageReasoningDetails.length > 0 ? messageReasoningDetails : findFirstReasoningDetails(content);
3056
+ const candidateReasoningDetails = messageReasoningDetails && Array.isArray(messageReasoningDetails) ? messageReasoningDetails : findFirstReasoningDetails(content);
3057
3057
  let finalReasoningDetails;
3058
- if (candidateReasoningDetails && candidateReasoningDetails.length > 0) {
3058
+ if (candidateReasoningDetails) {
3059
3059
  const validDetails = candidateReasoningDetails.filter((detail) => {
3060
3060
  var _a17;
3061
3061
  if (detail.type !== "reasoning.text" /* Text */) {
@@ -3081,9 +3081,9 @@ function convertToOpenRouterChatMessages(prompt) {
3081
3081
  uniqueDetails.push(detail);
3082
3082
  }
3083
3083
  }
3084
- finalReasoningDetails = uniqueDetails.length > 0 ? uniqueDetails : void 0;
3084
+ finalReasoningDetails = uniqueDetails;
3085
3085
  }
3086
- const effectiveReasoning = reasoning && finalReasoningDetails ? reasoning : void 0;
3086
+ const effectiveReasoning = reasoning && finalReasoningDetails && finalReasoningDetails.length > 0 ? reasoning : void 0;
3087
3087
  messages.push({
3088
3088
  role: "assistant",
3089
3089
  content: text,
@@ -3527,7 +3527,7 @@ var OpenRouterChatLanguageModel = class {
3527
3527
  this.supportedUrls = {
3528
3528
  "image/*": [
3529
3529
  /^data:image\/[a-zA-Z]+;base64,/,
3530
- /^https?:\/\/.+\.(jpg|jpeg|png|gif|webp)$/i
3530
+ /^https?:\/\/.+\.(jpg|jpeg|png|gif|webp)(?:[?#].*)?$/i
3531
3531
  ],
3532
3532
  // 'text/*': [/^data:text\//, /^https?:\/\/.+$/],
3533
3533
  "application/*": [/^data:application\//, /^https?:\/\/.+$/]
@@ -3550,7 +3550,7 @@ var OpenRouterChatLanguageModel = class {
3550
3550
  tools,
3551
3551
  toolChoice
3552
3552
  }) {
3553
- var _a16, _b16;
3553
+ var _a16, _b16, _c, _d;
3554
3554
  const baseArgs = __spreadValues(__spreadValues({
3555
3555
  // model id:
3556
3556
  model: this.modelId,
@@ -3573,8 +3573,8 @@ var OpenRouterChatLanguageModel = class {
3573
3573
  type: "json_schema",
3574
3574
  json_schema: __spreadValues({
3575
3575
  schema: responseFormat.schema,
3576
- strict: true,
3577
- name: (_a16 = responseFormat.name) != null ? _a16 : "response"
3576
+ strict: (_b16 = (_a16 = this.settings.structuredOutputs) == null ? void 0 : _a16.strict) != null ? _b16 : true,
3577
+ name: (_c = responseFormat.name) != null ? _c : "response"
3578
3578
  }, responseFormat.description && {
3579
3579
  description: responseFormat.description
3580
3580
  })
@@ -3600,7 +3600,7 @@ var OpenRouterChatLanguageModel = class {
3600
3600
  const mappedTools = [];
3601
3601
  for (const tool2 of tools) {
3602
3602
  if (tool2.type === "function") {
3603
- const openrouterOptions = (_b16 = tool2.providerOptions) == null ? void 0 : _b16.openrouter;
3603
+ const openrouterOptions = (_d = tool2.providerOptions) == null ? void 0 : _d.openrouter;
3604
3604
  const eagerInputStreaming = openrouterOptions == null ? void 0 : openrouterOptions.eager_input_streaming;
3605
3605
  mappedTools.push(__spreadValues({
3606
3606
  type: "function",
@@ -4010,15 +4010,17 @@ var OpenRouterChatLanguageModel = class {
4010
4010
  controller.enqueue({
4011
4011
  type: "reasoning-end",
4012
4012
  id: reasoningId || generateId(),
4013
- // Include accumulated reasoning_details so the AI SDK can update
4014
- // the reasoning part's providerMetadata with the correct signature.
4015
- // The signature typically arrives in the last reasoning delta,
4013
+ // Always include accumulated reasoning_details so the AI SDK can
4014
+ // update the reasoning part's providerMetadata with the correct
4015
+ // signature. The signature typically arrives in the last delta,
4016
4016
  // but reasoning-start only carries the first delta's metadata.
4017
- providerMetadata: accumulatedReasoningDetails.length > 0 ? {
4017
+ // An empty array is intentional — it signals the provider produced
4018
+ // no reasoning tokens this turn (e.g. DeepSeek V4).
4019
+ providerMetadata: {
4018
4020
  openrouter: {
4019
4021
  reasoning_details: accumulatedReasoningDetails
4020
4022
  }
4021
- } : void 0
4023
+ }
4022
4024
  });
4023
4025
  reasoningStarted = false;
4024
4026
  }
@@ -4167,7 +4169,7 @@ var OpenRouterChatLanguageModel = class {
4167
4169
  id: toolCall.id,
4168
4170
  delta: (_s = toolCallDelta.function.arguments) != null ? _s : ""
4169
4171
  });
4170
- if (((_t = toolCall.function) == null ? void 0 : _t.name) != null && ((_u = toolCall.function) == null ? void 0 : _u.arguments) != null && isParsableJson(toolCall.function.arguments)) {
4172
+ if (!toolCall.sent && ((_t = toolCall.function) == null ? void 0 : _t.name) != null && ((_u = toolCall.function) == null ? void 0 : _u.arguments) != null && isParsableJson(toolCall.function.arguments)) {
4171
4173
  controller.enqueue({
4172
4174
  type: "tool-input-end",
4173
4175
  id: toolCall.id
@@ -4253,13 +4255,14 @@ var OpenRouterChatLanguageModel = class {
4253
4255
  controller.enqueue({
4254
4256
  type: "reasoning-end",
4255
4257
  id: reasoningId || generateId(),
4256
- // Include accumulated reasoning_details so the AI SDK can update
4257
- // the reasoning part's providerMetadata with the correct signature.
4258
- providerMetadata: accumulatedReasoningDetails.length > 0 ? {
4258
+ // Always include accumulated reasoning_details so the AI SDK can
4259
+ // update the reasoning part's providerMetadata. An empty array is
4260
+ // intentional it signals the provider produced no reasoning tokens.
4261
+ providerMetadata: {
4259
4262
  openrouter: {
4260
4263
  reasoning_details: accumulatedReasoningDetails
4261
4264
  }
4262
- } : void 0
4265
+ }
4263
4266
  });
4264
4267
  }
4265
4268
  if (textStarted) {
@@ -4274,9 +4277,7 @@ var OpenRouterChatLanguageModel = class {
4274
4277
  if (provider !== void 0) {
4275
4278
  openrouterMetadata.provider = provider;
4276
4279
  }
4277
- if (accumulatedReasoningDetails.length > 0) {
4278
- openrouterMetadata.reasoning_details = accumulatedReasoningDetails;
4279
- }
4280
+ openrouterMetadata.reasoning_details = accumulatedReasoningDetails;
4280
4281
  if (accumulatedFileAnnotations.length > 0) {
4281
4282
  openrouterMetadata.annotations = accumulatedFileAnnotations;
4282
4283
  }
@@ -4474,7 +4475,7 @@ var OpenRouterCompletionLanguageModel = class {
4474
4475
  this.supportedUrls = {
4475
4476
  "image/*": [
4476
4477
  /^data:image\/[a-zA-Z]+;base64,/,
4477
- /^https?:\/\/.+\.(jpg|jpeg|png|gif|webp)$/i
4478
+ /^https?:\/\/.+\.(jpg|jpeg|png|gif|webp)(?:[?#].*)?$/i
4478
4479
  ],
4479
4480
  "text/*": [/^data:text\//, /^https?:\/\/.+$/],
4480
4481
  "application/*": [/^data:application\//, /^https?:\/\/.+$/]
@@ -5100,7 +5101,7 @@ function withUserAgentSuffix2(headers, ...userAgentSuffixParts) {
5100
5101
  }
5101
5102
 
5102
5103
  // src/version.ts
5103
- var VERSION2 = false ? "0.0.0-test" : "2.8.0";
5104
+ var VERSION2 = false ? "0.0.0-test" : "2.9.0";
5104
5105
 
5105
5106
  // src/video/schemas.ts
5106
5107
  var import_v411 = require("zod/v4");