@openrouter/ai-sdk-provider 0.4.1 → 0.4.3

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/README.md CHANGED
@@ -107,3 +107,39 @@ There are 3 ways to pass extra body to OpenRouter:
107
107
  messages: [{ role: 'user', content: 'Hello' }],
108
108
  });
109
109
  ```
110
+
111
+ ## Anthropic Prompt Caching
112
+
113
+ You can include Anthropic-specific options directly in your messages when using functions like `streamText`. The OpenRouter provider will automatically convert these messages to the correct format internally.
114
+
115
+ ### Basic Usage
116
+
117
+ ```typescript
118
+ import { createOpenRouter } from '@openrouter/ai-sdk-provider';
119
+ import { streamText } from 'ai';
120
+
121
+ const openrouter = createOpenRouter({ apiKey: 'your-api-key' });
122
+ const model = openrouter('anthropic/<supported-caching-model>');
123
+
124
+ await streamText({
125
+ model,
126
+ messages: [
127
+ {
128
+ role: 'system',
129
+ content: 'You are a helpful assistant.',
130
+ // Add provider options at the message level
131
+ providerMetadata: {
132
+ openrouter: {
133
+ // cache_control also works
134
+ // cache_control: { type: 'ephemeral' }
135
+ cacheControl: { type: 'ephemeral' },
136
+ },
137
+ },
138
+ },
139
+ {
140
+ role: 'user',
141
+ content: 'Hello, how are you?',
142
+ },
143
+ ],
144
+ });
145
+ ```
package/dist/index.js CHANGED
@@ -66,39 +66,61 @@ var import_zod2 = require("zod");
66
66
  // src/convert-to-openrouter-chat-messages.ts
67
67
  var import_provider_utils = require("@ai-sdk/provider-utils");
68
68
  function convertToOpenRouterChatMessages(prompt) {
69
- var _a;
69
+ var _a, _b, _c;
70
70
  const messages = [];
71
- for (const { role, content } of prompt) {
71
+ function getCacheControl(providerMetadata) {
72
+ var _a2;
73
+ const anthropic = providerMetadata == null ? void 0 : providerMetadata.anthropic;
74
+ const cacheControlValue = (_a2 = anthropic == null ? void 0 : anthropic.cacheControl) != null ? _a2 : anthropic == null ? void 0 : anthropic.cache_control;
75
+ return cacheControlValue;
76
+ }
77
+ for (const { role, content, providerMetadata } of prompt) {
72
78
  switch (role) {
73
79
  case "system": {
74
- messages.push({ role: "system", content });
80
+ messages.push({
81
+ role: "system",
82
+ content,
83
+ cache_control: getCacheControl(providerMetadata)
84
+ });
75
85
  break;
76
86
  }
77
87
  case "user": {
78
88
  if (content.length === 1 && ((_a = content[0]) == null ? void 0 : _a.type) === "text") {
79
- messages.push({ role: "user", content: content[0].text });
89
+ messages.push({
90
+ role: "user",
91
+ content: content[0].text,
92
+ cache_control: (_b = getCacheControl(providerMetadata)) != null ? _b : getCacheControl(content[0].providerMetadata)
93
+ });
80
94
  break;
81
95
  }
96
+ const messageCacheControl = getCacheControl(providerMetadata);
82
97
  const contentParts = content.map(
83
98
  (part) => {
84
- var _a2;
99
+ var _a2, _b2, _c2;
85
100
  switch (part.type) {
86
101
  case "text":
87
102
  return {
88
103
  type: "text",
89
- text: part.text
104
+ text: part.text,
105
+ // For text parts, only use part-specific cache control
106
+ cache_control: getCacheControl(part.providerMetadata)
90
107
  };
91
108
  case "image":
92
109
  return {
93
110
  type: "image_url",
94
111
  image_url: {
95
- url: part.image instanceof URL ? part.image.toString() : `data:${(_a2 = part.mimeType) != null ? _a2 : "image/jpeg"};base64,${(0, import_provider_utils.convertUint8ArrayToBase64)(part.image)}`
96
- }
112
+ url: part.image instanceof URL ? part.image.toString() : `data:${(_a2 = part.mimeType) != null ? _a2 : "image/jpeg"};base64,${(0, import_provider_utils.convertUint8ArrayToBase64)(
113
+ part.image
114
+ )}`
115
+ },
116
+ // For image parts, use part-specific or message-level cache control
117
+ cache_control: (_b2 = getCacheControl(part.providerMetadata)) != null ? _b2 : messageCacheControl
97
118
  };
98
119
  case "file":
99
120
  return {
100
121
  type: "text",
101
- text: part.data instanceof URL ? part.data.toString() : part.data
122
+ text: part.data instanceof URL ? part.data.toString() : part.data,
123
+ cache_control: (_c2 = getCacheControl(part.providerMetadata)) != null ? _c2 : messageCacheControl
102
124
  };
103
125
  default: {
104
126
  const _exhaustiveCheck = part;
@@ -148,7 +170,8 @@ function convertToOpenRouterChatMessages(prompt) {
148
170
  messages.push({
149
171
  role: "assistant",
150
172
  content: text,
151
- tool_calls: toolCalls.length > 0 ? toolCalls : void 0
173
+ tool_calls: toolCalls.length > 0 ? toolCalls : void 0,
174
+ cache_control: getCacheControl(providerMetadata)
152
175
  });
153
176
  break;
154
177
  }
@@ -157,7 +180,8 @@ function convertToOpenRouterChatMessages(prompt) {
157
180
  messages.push({
158
181
  role: "tool",
159
182
  tool_call_id: toolResponse.toolCallId,
160
- content: JSON.stringify(toolResponse.result)
183
+ content: JSON.stringify(toolResponse.result),
184
+ cache_control: (_c = getCacheControl(providerMetadata)) != null ? _c : getCacheControl(toolResponse.providerMetadata)
161
185
  });
162
186
  }
163
187
  break;
@@ -204,7 +228,7 @@ function mapOpenRouterFinishReason(finishReason) {
204
228
  // src/openrouter-error.ts
205
229
  var import_provider_utils2 = require("@ai-sdk/provider-utils");
206
230
  var import_zod = require("zod");
207
- var openAIErrorDataSchema = import_zod.z.object({
231
+ var OpenRouterErrorResponseSchema = import_zod.z.object({
208
232
  error: import_zod.z.object({
209
233
  message: import_zod.z.string(),
210
234
  type: import_zod.z.string(),
@@ -213,7 +237,7 @@ var openAIErrorDataSchema = import_zod.z.object({
213
237
  })
214
238
  });
215
239
  var openrouterFailedResponseHandler = (0, import_provider_utils2.createJsonErrorResponseHandler)({
216
- errorSchema: openAIErrorDataSchema,
240
+ errorSchema: OpenRouterErrorResponseSchema,
217
241
  errorToMessage: (data) => data.error.message
218
242
  });
219
243
 
@@ -309,7 +333,7 @@ var OpenRouterChatLanguageModel = class {
309
333
  }
310
334
  }
311
335
  async doGenerate(options) {
312
- var _b, _c, _d;
336
+ var _b, _c, _d, _e, _f, _g, _h;
313
337
  const args = this.getArgs(options);
314
338
  const { responseHeaders, value: response } = await (0, import_provider_utils3.postJsonToApi)({
315
339
  url: this.config.url({
@@ -320,17 +344,21 @@ var OpenRouterChatLanguageModel = class {
320
344
  body: args,
321
345
  failedResponseHandler: openrouterFailedResponseHandler,
322
346
  successfulResponseHandler: (0, import_provider_utils3.createJsonResponseHandler)(
323
- openAIChatResponseSchema
347
+ OpenRouterNonStreamChatCompletionResponseSchema
324
348
  ),
325
349
  abortSignal: options.abortSignal,
326
350
  fetch: this.config.fetch
327
351
  });
328
352
  const _a = args, { messages: rawPrompt } = _a, rawSettings = __objRest(_a, ["messages"]);
329
353
  const choice = response.choices[0];
330
- if (choice == null) {
354
+ if (!choice) {
331
355
  throw new Error("No choice in response");
332
356
  }
333
357
  return {
358
+ response: {
359
+ id: response.id,
360
+ modelId: response.model
361
+ },
334
362
  text: (_b = choice.message.content) != null ? _b : void 0,
335
363
  reasoning: (_c = choice.message.reasoning) != null ? _c : void 0,
336
364
  toolCalls: (_d = choice.message.tool_calls) == null ? void 0 : _d.map((toolCall) => {
@@ -344,8 +372,8 @@ var OpenRouterChatLanguageModel = class {
344
372
  }),
345
373
  finishReason: mapOpenRouterFinishReason(choice.finish_reason),
346
374
  usage: {
347
- promptTokens: response.usage.prompt_tokens,
348
- completionTokens: response.usage.completion_tokens
375
+ promptTokens: (_f = (_e = response.usage) == null ? void 0 : _e.prompt_tokens) != null ? _f : 0,
376
+ completionTokens: (_h = (_g = response.usage) == null ? void 0 : _g.completion_tokens) != null ? _h : 0
349
377
  },
350
378
  rawCall: { rawPrompt, rawSettings },
351
379
  rawResponse: { headers: responseHeaders },
@@ -368,7 +396,7 @@ var OpenRouterChatLanguageModel = class {
368
396
  }),
369
397
  failedResponseHandler: openrouterFailedResponseHandler,
370
398
  successfulResponseHandler: (0, import_provider_utils3.createEventSourceResponseHandler)(
371
- openrouterChatChunkSchema
399
+ OpenRouterStreamChatCompletionChunkSchema
372
400
  ),
373
401
  abortSignal: options.abortSignal,
374
402
  fetch: this.config.fetch
@@ -403,6 +431,12 @@ var OpenRouterChatLanguageModel = class {
403
431
  id: value.id
404
432
  });
405
433
  }
434
+ if (value.model) {
435
+ controller.enqueue({
436
+ type: "response-metadata",
437
+ modelId: value.model
438
+ });
439
+ }
406
440
  if (value.usage != null) {
407
441
  usage = {
408
442
  promptTokens: value.usage.prompt_tokens,
@@ -530,7 +564,16 @@ var OpenRouterChatLanguageModel = class {
530
564
  };
531
565
  }
532
566
  };
533
- var openAIChatResponseSchema = import_zod2.z.object({
567
+ var OpenRouterChatCompletionBaseResponseSchema = import_zod2.z.object({
568
+ id: import_zod2.z.string().optional(),
569
+ model: import_zod2.z.string().optional(),
570
+ usage: import_zod2.z.object({
571
+ prompt_tokens: import_zod2.z.number(),
572
+ completion_tokens: import_zod2.z.number(),
573
+ total_tokens: import_zod2.z.number()
574
+ }).nullish()
575
+ });
576
+ var OpenRouterNonStreamChatCompletionResponseSchema = OpenRouterChatCompletionBaseResponseSchema.extend({
534
577
  choices: import_zod2.z.array(
535
578
  import_zod2.z.object({
536
579
  message: import_zod2.z.object({
@@ -565,15 +608,10 @@ var openAIChatResponseSchema = import_zod2.z.object({
565
608
  }).nullable().optional(),
566
609
  finish_reason: import_zod2.z.string().optional().nullable()
567
610
  })
568
- ),
569
- usage: import_zod2.z.object({
570
- prompt_tokens: import_zod2.z.number(),
571
- completion_tokens: import_zod2.z.number()
572
- })
611
+ )
573
612
  });
574
- var openrouterChatChunkSchema = import_zod2.z.union([
575
- import_zod2.z.object({
576
- id: import_zod2.z.string().optional(),
613
+ var OpenRouterStreamChatCompletionChunkSchema = import_zod2.z.union([
614
+ OpenRouterChatCompletionBaseResponseSchema.extend({
577
615
  choices: import_zod2.z.array(
578
616
  import_zod2.z.object({
579
617
  delta: import_zod2.z.object({
@@ -609,13 +647,9 @@ var openrouterChatChunkSchema = import_zod2.z.union([
609
647
  finish_reason: import_zod2.z.string().nullable().optional(),
610
648
  index: import_zod2.z.number()
611
649
  })
612
- ),
613
- usage: import_zod2.z.object({
614
- prompt_tokens: import_zod2.z.number(),
615
- completion_tokens: import_zod2.z.number()
616
- }).nullish()
650
+ )
617
651
  }),
618
- openAIErrorDataSchema
652
+ OpenRouterErrorResponseSchema
619
653
  ]);
620
654
  function prepareToolsAndToolChoice(mode) {
621
655
  var _a;
@@ -893,6 +927,7 @@ var OpenRouterCompletionLanguageModel = class {
893
927
  }
894
928
  }
895
929
  async doGenerate(options) {
930
+ var _b, _c, _d, _e, _f;
896
931
  const args = this.getArgs(options);
897
932
  const { responseHeaders, value: response } = await (0, import_provider_utils4.postJsonToApi)({
898
933
  url: this.config.url({
@@ -903,22 +938,29 @@ var OpenRouterCompletionLanguageModel = class {
903
938
  body: args,
904
939
  failedResponseHandler: openrouterFailedResponseHandler,
905
940
  successfulResponseHandler: (0, import_provider_utils4.createJsonResponseHandler)(
906
- openAICompletionResponseSchema
941
+ OpenRouterCompletionChunkSchema
907
942
  ),
908
943
  abortSignal: options.abortSignal,
909
944
  fetch: this.config.fetch
910
945
  });
911
946
  const _a = args, { prompt: rawPrompt } = _a, rawSettings = __objRest(_a, ["prompt"]);
947
+ if ("error" in response) {
948
+ throw new Error(`${response.error.message}`);
949
+ }
912
950
  const choice = response.choices[0];
913
951
  if (!choice) {
914
952
  throw new Error("No choice in OpenRouter completion response");
915
953
  }
916
954
  return {
917
- text: choice.text,
955
+ response: {
956
+ id: response.id,
957
+ modelId: response.model
958
+ },
959
+ text: (_b = choice.text) != null ? _b : "",
918
960
  reasoning: choice.reasoning || void 0,
919
961
  usage: {
920
- promptTokens: response.usage.prompt_tokens,
921
- completionTokens: response.usage.completion_tokens
962
+ promptTokens: (_d = (_c = response.usage) == null ? void 0 : _c.prompt_tokens) != null ? _d : 0,
963
+ completionTokens: (_f = (_e = response.usage) == null ? void 0 : _e.completion_tokens) != null ? _f : 0
922
964
  },
923
965
  finishReason: mapOpenRouterFinishReason(choice.finish_reason),
924
966
  logprobs: mapOpenRouterCompletionLogProbs(choice.logprobs),
@@ -942,7 +984,7 @@ var OpenRouterCompletionLanguageModel = class {
942
984
  }),
943
985
  failedResponseHandler: openrouterFailedResponseHandler,
944
986
  successfulResponseHandler: (0, import_provider_utils4.createEventSourceResponseHandler)(
945
- openrouterCompletionChunkSchema
987
+ OpenRouterCompletionChunkSchema
946
988
  ),
947
989
  abortSignal: options.abortSignal,
948
990
  fetch: this.config.fetch
@@ -1009,29 +1051,14 @@ var OpenRouterCompletionLanguageModel = class {
1009
1051
  };
1010
1052
  }
1011
1053
  };
1012
- var openAICompletionResponseSchema = import_zod3.z.object({
1013
- choices: import_zod3.z.array(
1014
- import_zod3.z.object({
1015
- text: import_zod3.z.string(),
1016
- reasoning: import_zod3.z.string().nullish().optional(),
1017
- finish_reason: import_zod3.z.string(),
1018
- logprobs: import_zod3.z.object({
1019
- tokens: import_zod3.z.array(import_zod3.z.string()),
1020
- token_logprobs: import_zod3.z.array(import_zod3.z.number()),
1021
- top_logprobs: import_zod3.z.array(import_zod3.z.record(import_zod3.z.string(), import_zod3.z.number())).nullable()
1022
- }).nullable().optional()
1023
- })
1024
- ),
1025
- usage: import_zod3.z.object({
1026
- prompt_tokens: import_zod3.z.number(),
1027
- completion_tokens: import_zod3.z.number()
1028
- })
1029
- });
1030
- var openrouterCompletionChunkSchema = import_zod3.z.union([
1054
+ var OpenRouterCompletionChunkSchema = import_zod3.z.union([
1031
1055
  import_zod3.z.object({
1056
+ id: import_zod3.z.string().optional(),
1057
+ model: import_zod3.z.string().optional(),
1032
1058
  choices: import_zod3.z.array(
1033
1059
  import_zod3.z.object({
1034
1060
  text: import_zod3.z.string(),
1061
+ reasoning: import_zod3.z.string().nullish().optional(),
1035
1062
  finish_reason: import_zod3.z.string().nullish(),
1036
1063
  index: import_zod3.z.number(),
1037
1064
  logprobs: import_zod3.z.object({
@@ -1046,7 +1073,7 @@ var openrouterCompletionChunkSchema = import_zod3.z.union([
1046
1073
  completion_tokens: import_zod3.z.number()
1047
1074
  }).optional().nullable()
1048
1075
  }),
1049
- openAIErrorDataSchema
1076
+ OpenRouterErrorResponseSchema
1050
1077
  ]);
1051
1078
 
1052
1079
  // src/openrouter-facade.ts