@openrouter/ai-sdk-provider 2.2.4 → 2.3.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.
@@ -186,6 +186,25 @@ type OpenRouterChatSettings = {
186
186
  */
187
187
  engine?: Engine;
188
188
  };
189
+ /**
190
+ * Enable Anthropic automatic prompt caching by setting a top-level cache_control
191
+ * directive on the request body. When set to `{ type: 'ephemeral' }`, Anthropic
192
+ * will automatically cache eligible content in your prompts.
193
+ *
194
+ * Only works with Anthropic models through OpenRouter.
195
+ *
196
+ * @see https://platform.claude.com/docs/en/build-with-claude/prompt-caching#automatic-caching
197
+ * @see https://openrouter.ai/docs
198
+ */
199
+ cache_control?: {
200
+ type: 'ephemeral';
201
+ /**
202
+ * Optional time-to-live for the cache entry.
203
+ * - `'5m'` — 5 minutes (default when omitted)
204
+ * - `'1h'` — 1 hour
205
+ */
206
+ ttl?: '5m' | '1h';
207
+ };
189
208
  /**
190
209
  * Debug options for troubleshooting API requests.
191
210
  * Only works with streaming requests.
@@ -186,6 +186,25 @@ type OpenRouterChatSettings = {
186
186
  */
187
187
  engine?: Engine;
188
188
  };
189
+ /**
190
+ * Enable Anthropic automatic prompt caching by setting a top-level cache_control
191
+ * directive on the request body. When set to `{ type: 'ephemeral' }`, Anthropic
192
+ * will automatically cache eligible content in your prompts.
193
+ *
194
+ * Only works with Anthropic models through OpenRouter.
195
+ *
196
+ * @see https://platform.claude.com/docs/en/build-with-claude/prompt-caching#automatic-caching
197
+ * @see https://openrouter.ai/docs
198
+ */
199
+ cache_control?: {
200
+ type: 'ephemeral';
201
+ /**
202
+ * Optional time-to-live for the cache entry.
203
+ * - `'5m'` — 5 minutes (default when omitted)
204
+ * - `'1h'` — 1 hour
205
+ */
206
+ ttl?: '5m' | '1h';
207
+ };
189
208
  /**
190
209
  * Debug options for troubleshooting API requests.
191
210
  * Only works with streaming requests.
@@ -2248,9 +2248,58 @@ var OpenRouterErrorResponseSchema = import_v42.z.object({
2248
2248
  param: import_v42.z.any().nullable().optional().default(null)
2249
2249
  }).passthrough()
2250
2250
  }).passthrough();
2251
+ function extractErrorMessage(data) {
2252
+ const error = data.error;
2253
+ const metadata = error.metadata;
2254
+ if (!metadata) {
2255
+ return data.error.message;
2256
+ }
2257
+ const parts = [];
2258
+ if (typeof metadata.provider_name === "string" && metadata.provider_name) {
2259
+ parts.push(`[${metadata.provider_name}]`);
2260
+ }
2261
+ const raw = metadata.raw;
2262
+ const rawMessage = extractRawMessage(raw);
2263
+ if (rawMessage && rawMessage !== data.error.message) {
2264
+ parts.push(rawMessage);
2265
+ } else {
2266
+ parts.push(data.error.message);
2267
+ }
2268
+ return parts.join(" ");
2269
+ }
2270
+ function extractRawMessage(raw) {
2271
+ if (typeof raw === "string") {
2272
+ try {
2273
+ const parsed = JSON.parse(raw);
2274
+ if (typeof parsed === "object" && parsed !== null) {
2275
+ return extractRawMessage(parsed);
2276
+ }
2277
+ return raw;
2278
+ } catch (e) {
2279
+ return raw;
2280
+ }
2281
+ }
2282
+ if (typeof raw !== "object" || raw === null) {
2283
+ return void 0;
2284
+ }
2285
+ const obj = raw;
2286
+ for (const field of ["message", "error", "detail", "details", "msg"]) {
2287
+ const value = obj[field];
2288
+ if (typeof value === "string" && value.length > 0) {
2289
+ return value;
2290
+ }
2291
+ if (typeof value === "object" && value !== null) {
2292
+ const nested = extractRawMessage(value);
2293
+ if (nested) {
2294
+ return nested;
2295
+ }
2296
+ }
2297
+ }
2298
+ return void 0;
2299
+ }
2251
2300
  var openrouterFailedResponseHandler = createJsonErrorResponseHandler({
2252
2301
  errorSchema: OpenRouterErrorResponseSchema,
2253
- errorToMessage: (data) => data.error.message
2302
+ errorToMessage: extractErrorMessage
2254
2303
  });
2255
2304
 
2256
2305
  // src/schemas/provider-metadata.ts
@@ -3096,7 +3145,9 @@ var OpenRouterChatLanguageModel = class {
3096
3145
  // Provider routing settings:
3097
3146
  provider: this.settings.provider,
3098
3147
  // Debug settings:
3099
- debug: this.settings.debug
3148
+ debug: this.settings.debug,
3149
+ // Anthropic automatic caching:
3150
+ cache_control: this.settings.cache_control
3100
3151
  }, this.config.extraBody), this.settings.extraBody);
3101
3152
  if (tools && tools.length > 0) {
3102
3153
  const mappedTools = tools.filter(
@@ -3117,10 +3168,11 @@ var OpenRouterChatLanguageModel = class {
3117
3168
  return baseArgs;
3118
3169
  }
3119
3170
  async doGenerate(options) {
3120
- var _a16, _b16, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v;
3171
+ var _b16, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w;
3121
3172
  const providerOptions = options.providerOptions || {};
3122
3173
  const openrouterOptions = providerOptions.openrouter || {};
3123
- const args = __spreadValues(__spreadValues({}, this.getArgs(options)), openrouterOptions);
3174
+ const _a16 = openrouterOptions, { cacheControl } = _a16, restOpenrouterOptions = __objRest(_a16, ["cacheControl"]);
3175
+ const args = __spreadValues(__spreadValues(__spreadValues({}, this.getArgs(options)), restOpenrouterOptions), cacheControl != null && !("cache_control" in restOpenrouterOptions) ? { cache_control: cacheControl } : {});
3124
3176
  const { value: responseValue, responseHeaders } = await postJsonToApi({
3125
3177
  url: this.config.url({
3126
3178
  path: "/chat/completions",
@@ -3157,7 +3209,7 @@ var OpenRouterChatLanguageModel = class {
3157
3209
  });
3158
3210
  }
3159
3211
  const usageInfo = response.usage ? computeTokenUsage(response.usage) : emptyUsage();
3160
- const reasoningDetails = (_a16 = choice.message.reasoning_details) != null ? _a16 : [];
3212
+ const reasoningDetails = (_b16 = choice.message.reasoning_details) != null ? _b16 : [];
3161
3213
  const reasoning = reasoningDetails.length > 0 ? reasoningDetails.map((detail) => {
3162
3214
  switch (detail.type) {
3163
3215
  case "reasoning.text" /* Text */: {
@@ -3226,9 +3278,9 @@ var OpenRouterChatLanguageModel = class {
3226
3278
  for (const toolCall of choice.message.tool_calls) {
3227
3279
  content.push({
3228
3280
  type: "tool-call",
3229
- toolCallId: (_b16 = toolCall.id) != null ? _b16 : generateId(),
3281
+ toolCallId: (_c = toolCall.id) != null ? _c : generateId(),
3230
3282
  toolName: toolCall.function.name,
3231
- input: (_c = toolCall.function.arguments) != null ? _c : "{}",
3283
+ input: (_d = toolCall.function.arguments) != null ? _d : "{}",
3232
3284
  providerMetadata: !reasoningDetailsAttachedToToolCall ? {
3233
3285
  openrouter: {
3234
3286
  reasoning_details: reasoningDetails
@@ -3255,19 +3307,19 @@ var OpenRouterChatLanguageModel = class {
3255
3307
  sourceType: "url",
3256
3308
  id: annotation.url_citation.url,
3257
3309
  url: annotation.url_citation.url,
3258
- title: (_d = annotation.url_citation.title) != null ? _d : "",
3310
+ title: (_e = annotation.url_citation.title) != null ? _e : "",
3259
3311
  providerMetadata: {
3260
3312
  openrouter: {
3261
- content: (_e = annotation.url_citation.content) != null ? _e : "",
3262
- startIndex: (_f = annotation.url_citation.start_index) != null ? _f : 0,
3263
- endIndex: (_g = annotation.url_citation.end_index) != null ? _g : 0
3313
+ content: (_f = annotation.url_citation.content) != null ? _f : "",
3314
+ startIndex: (_g = annotation.url_citation.start_index) != null ? _g : 0,
3315
+ endIndex: (_h = annotation.url_citation.end_index) != null ? _h : 0
3264
3316
  }
3265
3317
  }
3266
3318
  });
3267
3319
  }
3268
3320
  }
3269
3321
  }
3270
- const fileAnnotations = (_h = choice.message.annotations) == null ? void 0 : _h.filter(
3322
+ const fileAnnotations = (_i = choice.message.annotations) == null ? void 0 : _i.filter(
3271
3323
  (a) => a.type === "file"
3272
3324
  );
3273
3325
  const hasToolCalls = choice.message.tool_calls && choice.message.tool_calls.length > 0;
@@ -3275,7 +3327,7 @@ var OpenRouterChatLanguageModel = class {
3275
3327
  (d) => d.type === "reasoning.encrypted" /* Encrypted */ && d.data
3276
3328
  );
3277
3329
  const shouldOverrideFinishReason = hasToolCalls && hasEncryptedReasoning && choice.finish_reason === "stop";
3278
- const effectiveFinishReason = shouldOverrideFinishReason ? createFinishReason("tool-calls", (_i = choice.finish_reason) != null ? _i : void 0) : mapOpenRouterFinishReason(choice.finish_reason);
3330
+ const effectiveFinishReason = shouldOverrideFinishReason ? createFinishReason("tool-calls", (_j = choice.finish_reason) != null ? _j : void 0) : mapOpenRouterFinishReason(choice.finish_reason);
3279
3331
  return {
3280
3332
  content,
3281
3333
  finishReason: effectiveFinishReason,
@@ -3283,22 +3335,22 @@ var OpenRouterChatLanguageModel = class {
3283
3335
  warnings: [],
3284
3336
  providerMetadata: {
3285
3337
  openrouter: OpenRouterProviderMetadataSchema.parse({
3286
- provider: (_j = response.provider) != null ? _j : "",
3287
- reasoning_details: (_k = choice.message.reasoning_details) != null ? _k : [],
3338
+ provider: (_k = response.provider) != null ? _k : "",
3339
+ reasoning_details: (_l = choice.message.reasoning_details) != null ? _l : [],
3288
3340
  annotations: fileAnnotations && fileAnnotations.length > 0 ? fileAnnotations : void 0,
3289
3341
  usage: __spreadValues(__spreadValues(__spreadValues(__spreadValues({
3290
- promptTokens: (_l = usageInfo.inputTokens.total) != null ? _l : 0,
3291
- completionTokens: (_m = usageInfo.outputTokens.total) != null ? _m : 0,
3292
- totalTokens: ((_n = usageInfo.inputTokens.total) != null ? _n : 0) + ((_o = usageInfo.outputTokens.total) != null ? _o : 0)
3293
- }, ((_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 ? {
3342
+ promptTokens: (_m = usageInfo.inputTokens.total) != null ? _m : 0,
3343
+ completionTokens: (_n = usageInfo.outputTokens.total) != null ? _n : 0,
3344
+ totalTokens: ((_o = usageInfo.inputTokens.total) != null ? _o : 0) + ((_p = usageInfo.outputTokens.total) != null ? _p : 0)
3345
+ }, ((_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 ? {
3294
3346
  promptTokensDetails: {
3295
3347
  cachedTokens: response.usage.prompt_tokens_details.cached_tokens
3296
3348
  }
3297
- } : {}), ((_t = (_s = response.usage) == null ? void 0 : _s.completion_tokens_details) == null ? void 0 : _t.reasoning_tokens) != null ? {
3349
+ } : {}), ((_u = (_t = response.usage) == null ? void 0 : _t.completion_tokens_details) == null ? void 0 : _u.reasoning_tokens) != null ? {
3298
3350
  completionTokensDetails: {
3299
3351
  reasoningTokens: response.usage.completion_tokens_details.reasoning_tokens
3300
3352
  }
3301
- } : {}), ((_v = (_u = response.usage) == null ? void 0 : _u.cost_details) == null ? void 0 : _v.upstream_inference_cost) != null ? {
3353
+ } : {}), ((_w = (_v = response.usage) == null ? void 0 : _v.cost_details) == null ? void 0 : _w.upstream_inference_cost) != null ? {
3302
3354
  costDetails: {
3303
3355
  upstreamInferenceCost: response.usage.cost_details.upstream_inference_cost
3304
3356
  }
@@ -3314,10 +3366,11 @@ var OpenRouterChatLanguageModel = class {
3314
3366
  };
3315
3367
  }
3316
3368
  async doStream(options) {
3317
- var _a16;
3369
+ var _b16;
3318
3370
  const providerOptions = options.providerOptions || {};
3319
3371
  const openrouterOptions = providerOptions.openrouter || {};
3320
- const args = __spreadValues(__spreadValues({}, this.getArgs(options)), openrouterOptions);
3372
+ const _a16 = openrouterOptions, { cacheControl } = _a16, restOpenrouterOptions = __objRest(_a16, ["cacheControl"]);
3373
+ const args = __spreadValues(__spreadValues(__spreadValues({}, this.getArgs(options)), restOpenrouterOptions), cacheControl != null && !("cache_control" in restOpenrouterOptions) ? { cache_control: cacheControl } : {});
3321
3374
  const { value: response, responseHeaders } = await postJsonToApi({
3322
3375
  url: this.config.url({
3323
3376
  path: "/chat/completions",
@@ -3329,7 +3382,7 @@ var OpenRouterChatLanguageModel = class {
3329
3382
  // only include stream_options when in strict compatibility mode:
3330
3383
  stream_options: this.config.compatibility === "strict" ? __spreadValues({
3331
3384
  include_usage: true
3332
- }, ((_a16 = this.settings.usage) == null ? void 0 : _a16.include) ? { include_usage: true } : {}) : void 0
3385
+ }, ((_b16 = this.settings.usage) == null ? void 0 : _b16.include) ? { include_usage: true } : {}) : void 0
3333
3386
  }),
3334
3387
  failedResponseHandler: openrouterFailedResponseHandler,
3335
3388
  successfulResponseHandler: createEventSourceResponseHandler(
@@ -3369,7 +3422,7 @@ var OpenRouterChatLanguageModel = class {
3369
3422
  stream: response.pipeThrough(
3370
3423
  new TransformStream({
3371
3424
  transform(chunk, controller) {
3372
- var _a17, _b16, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u;
3425
+ var _a17, _b17, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u;
3373
3426
  if (options.includeRawChunks) {
3374
3427
  controller.enqueue({ type: "raw", rawValue: chunk.rawValue });
3375
3428
  }
@@ -3406,7 +3459,7 @@ var OpenRouterChatLanguageModel = class {
3406
3459
  Object.assign(usage.outputTokens, computed.outputTokens);
3407
3460
  rawUsage = value.usage;
3408
3461
  const promptTokens = (_a17 = value.usage.prompt_tokens) != null ? _a17 : 0;
3409
- const completionTokens = (_b16 = value.usage.completion_tokens) != null ? _b16 : 0;
3462
+ const completionTokens = (_b17 = value.usage.completion_tokens) != null ? _b17 : 0;
3410
3463
  openrouterUsage.promptTokens = promptTokens;
3411
3464
  if (value.usage.prompt_tokens_details) {
3412
3465
  openrouterUsage.promptTokensDetails = {