fix(provider): improve prompt caching

This commit is contained in:
Aiden Cline 2026-07-08 19:22:45 -05:00
commit 439a5a5281
19 changed files with 857 additions and 58 deletions

View file

@ -0,0 +1,126 @@
diff --git a/dist/index.js b/dist/index.js
index ce8d4ae802f5909937840b856866c2d735800212..d448e921d51cb0a4f752cdad09080b21d4d8e8a9 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -139,11 +139,12 @@ function convertCohereUsage(tokens) {
}
const inputTokens = tokens.input_tokens;
const outputTokens = tokens.output_tokens;
+ const cacheReadTokens = tokens.cached_tokens ?? 0;
return {
inputTokens: {
total: inputTokens,
- noCache: inputTokens,
- cacheRead: void 0,
+ noCache: Math.max(0, inputTokens - cacheReadTokens),
+ cacheRead: cacheReadTokens || void 0,
cacheWrite: void 0
},
outputTokens: {
@@ -672,7 +673,8 @@ var cohereChatResponseSchema = import_v43.z.object({
}),
tokens: import_v43.z.object({
input_tokens: import_v43.z.number(),
- output_tokens: import_v43.z.number()
+ output_tokens: import_v43.z.number(),
+ cached_tokens: import_v43.z.number().optional()
})
})
});
@@ -732,7 +734,8 @@ var cohereChatChunkSchema = import_v43.z.discriminatedUnion("type", [
usage: import_v43.z.object({
tokens: import_v43.z.object({
input_tokens: import_v43.z.number(),
- output_tokens: import_v43.z.number()
+ output_tokens: import_v43.z.number(),
+ cached_tokens: import_v43.z.number().optional()
})
})
})
diff --git a/dist/index.mjs b/dist/index.mjs
index 44f26c27511f9089cdcc9456590dee100b070b0a..d3f585b58208430404135060af5774a7ecfa65d0 100644
--- a/dist/index.mjs
+++ b/dist/index.mjs
@@ -126,11 +126,12 @@ function convertCohereUsage(tokens) {
}
const inputTokens = tokens.input_tokens;
const outputTokens = tokens.output_tokens;
+ const cacheReadTokens = tokens.cached_tokens ?? 0;
return {
inputTokens: {
total: inputTokens,
- noCache: inputTokens,
- cacheRead: void 0,
+ noCache: Math.max(0, inputTokens - cacheReadTokens),
+ cacheRead: cacheReadTokens || void 0,
cacheWrite: void 0
},
outputTokens: {
@@ -661,7 +662,8 @@ var cohereChatResponseSchema = z3.object({
}),
tokens: z3.object({
input_tokens: z3.number(),
- output_tokens: z3.number()
+ output_tokens: z3.number(),
+ cached_tokens: z3.number().optional()
})
})
});
@@ -721,7 +723,8 @@ var cohereChatChunkSchema = z3.discriminatedUnion("type", [
usage: z3.object({
tokens: z3.object({
input_tokens: z3.number(),
- output_tokens: z3.number()
+ output_tokens: z3.number(),
+ cached_tokens: z3.number().optional()
})
})
})
diff --git a/src/cohere-chat-language-model.ts b/src/cohere-chat-language-model.ts
index e4971ccf255ffff77c38fd35caccf84a032ad150..93fea74a08c0f9bcc6c63689fbc9e72669cdeba1 100644
--- a/src/cohere-chat-language-model.ts
+++ b/src/cohere-chat-language-model.ts
@@ -495,6 +495,7 @@ const cohereChatResponseSchema = z.object({
tokens: z.object({
input_tokens: z.number(),
output_tokens: z.number(),
+ cached_tokens: z.number().optional(),
}),
}),
});
@@ -558,6 +559,7 @@ const cohereChatChunkSchema = z.discriminatedUnion('type', [
tokens: z.object({
input_tokens: z.number(),
output_tokens: z.number(),
+ cached_tokens: z.number().optional(),
}),
}),
}),
diff --git a/src/convert-cohere-usage.ts b/src/convert-cohere-usage.ts
index 0f83a63ab85ab2c2ae136edd509e1f738553041f..3913f8d221c8980916769a9890b56030f69a6070 100644
--- a/src/convert-cohere-usage.ts
+++ b/src/convert-cohere-usage.ts
@@ -3,6 +3,7 @@ import { LanguageModelV3Usage } from '@ai-sdk/provider';
export type CohereUsageTokens = {
input_tokens: number;
output_tokens: number;
+ cached_tokens?: number;
};
export function convertCohereUsage(
@@ -27,12 +28,13 @@ export function convertCohereUsage(
const inputTokens = tokens.input_tokens;
const outputTokens = tokens.output_tokens;
+ const cacheReadTokens = tokens.cached_tokens ?? 0;
return {
inputTokens: {
total: inputTokens,
- noCache: inputTokens,
- cacheRead: undefined,
+ noCache: Math.max(0, inputTokens - cacheReadTokens),
+ cacheRead: cacheReadTokens || undefined,
cacheWrite: undefined,
},
outputTokens: {

View file

@ -0,0 +1,67 @@
diff --git a/dist/index.js b/dist/index.js
index 45a104f2e0775761858eac2a82ced64bceba1f5e..36861246fa3aea50f82a233aec8c8edaef0ff6e3 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -57,13 +57,14 @@ function convertGroqUsage(usage) {
}
const promptTokens = (_a = usage.prompt_tokens) != null ? _a : 0;
const completionTokens = (_b = usage.completion_tokens) != null ? _b : 0;
- const reasoningTokens = (_d = (_c = usage.completion_tokens_details) == null ? void 0 : _c.reasoning_tokens) != null ? _d : void 0;
+ const cacheReadTokens = (_d = (_c = usage.prompt_tokens_details) == null ? void 0 : _c.cached_tokens) != null ? _d : 0;
+ const reasoningTokens = usage.completion_tokens_details?.reasoning_tokens ?? void 0;
const textTokens = reasoningTokens != null ? completionTokens - reasoningTokens : completionTokens;
return {
inputTokens: {
total: promptTokens,
- noCache: promptTokens,
- cacheRead: void 0,
+ noCache: Math.max(0, promptTokens - cacheReadTokens),
+ cacheRead: cacheReadTokens || void 0,
cacheWrite: void 0
},
outputTokens: {
diff --git a/dist/index.mjs b/dist/index.mjs
index c644c32235d8fa88c51c0fc6958feb1da4877c96..6aa35dd173cd67dbdd2f6f3a5fe2d901cbaafae8 100644
--- a/dist/index.mjs
+++ b/dist/index.mjs
@@ -44,13 +44,14 @@ function convertGroqUsage(usage) {
}
const promptTokens = (_a = usage.prompt_tokens) != null ? _a : 0;
const completionTokens = (_b = usage.completion_tokens) != null ? _b : 0;
- const reasoningTokens = (_d = (_c = usage.completion_tokens_details) == null ? void 0 : _c.reasoning_tokens) != null ? _d : void 0;
+ const cacheReadTokens = (_d = (_c = usage.prompt_tokens_details) == null ? void 0 : _c.cached_tokens) != null ? _d : 0;
+ const reasoningTokens = usage.completion_tokens_details?.reasoning_tokens ?? void 0;
const textTokens = reasoningTokens != null ? completionTokens - reasoningTokens : completionTokens;
return {
inputTokens: {
total: promptTokens,
- noCache: promptTokens,
- cacheRead: void 0,
+ noCache: Math.max(0, promptTokens - cacheReadTokens),
+ cacheRead: cacheReadTokens || void 0,
cacheWrite: void 0
},
outputTokens: {
diff --git a/src/convert-groq-usage.ts b/src/convert-groq-usage.ts
index a16809223b068bc1d682ef9156ccc82ce89ace5c..732b14508609106bd5946ba9f9323c7863ee2da5 100644
--- a/src/convert-groq-usage.ts
+++ b/src/convert-groq-usage.ts
@@ -40,6 +40,7 @@ export function convertGroqUsage(
const promptTokens = usage.prompt_tokens ?? 0;
const completionTokens = usage.completion_tokens ?? 0;
+ const cacheReadTokens = usage.prompt_tokens_details?.cached_tokens ?? 0;
const reasoningTokens =
usage.completion_tokens_details?.reasoning_tokens ?? undefined;
const textTokens =
@@ -50,8 +51,8 @@ export function convertGroqUsage(
return {
inputTokens: {
total: promptTokens,
- noCache: promptTokens,
- cacheRead: undefined,
+ noCache: Math.max(0, promptTokens - cacheReadTokens),
+ cacheRead: cacheReadTokens || undefined,
cacheWrite: undefined,
},
outputTokens: {

View file

@ -0,0 +1,84 @@
diff --git a/dist/index.d.ts b/dist/index.d.ts
index 1ca9113bed2728a616db773a8e08d8d6957447d7..15408ec429dc210b5fa43589d81b69c93bf27b2d 100644
--- a/dist/index.d.ts
+++ b/dist/index.d.ts
@@ -14,6 +14,7 @@ declare const mistralLanguageModelOptions: z.ZodObject<{
none: "none";
high: "high";
}>>;
+ promptCacheKey: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
type MistralLanguageModelOptions = z.infer<typeof mistralLanguageModelOptions>;
diff --git a/dist/index.js b/dist/index.js
index 45735e524aaff54ea058c99c729c5ffd3c507058..6aca5f6f13da0054ede31c1f1a692e4eaed37d34 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -268,7 +268,8 @@ var mistralLanguageModelOptions = import_v4.z.object({
* - `'high'`: Enable reasoning
* - `'none'`: Disable reasoning
*/
- reasoningEffort: import_v4.z.enum(["high", "none"]).optional()
+ reasoningEffort: import_v4.z.enum(["high", "none"]).optional(),
+ promptCacheKey: import_v4.z.string().optional()
});
// src/mistral-error.ts
@@ -413,6 +414,7 @@ var MistralChatLanguageModel = class {
top_p: topP,
random_seed: seed,
reasoning_effort: options.reasoningEffort,
+ prompt_cache_key: options.promptCacheKey,
// response format:
response_format: (responseFormat == null ? void 0 : responseFormat.type) === "json" ? structuredOutputs && (responseFormat == null ? void 0 : responseFormat.schema) != null ? {
type: "json_schema",
diff --git a/dist/index.mjs b/dist/index.mjs
index 4c22df1cd78a1ba81309c8a86ceecefef4ba4aea..30cd3b1f503860109b7fa2107cd1eb17b70c96be 100644
--- a/dist/index.mjs
+++ b/dist/index.mjs
@@ -256,7 +256,8 @@ var mistralLanguageModelOptions = z.object({
* - `'high'`: Enable reasoning
* - `'none'`: Disable reasoning
*/
- reasoningEffort: z.enum(["high", "none"]).optional()
+ reasoningEffort: z.enum(["high", "none"]).optional(),
+ promptCacheKey: z.string().optional()
});
// src/mistral-error.ts
@@ -403,6 +404,7 @@ var MistralChatLanguageModel = class {
top_p: topP,
random_seed: seed,
reasoning_effort: options.reasoningEffort,
+ prompt_cache_key: options.promptCacheKey,
// response format:
response_format: (responseFormat == null ? void 0 : responseFormat.type) === "json" ? structuredOutputs && (responseFormat == null ? void 0 : responseFormat.schema) != null ? {
type: "json_schema",
diff --git a/src/mistral-chat-language-model.ts b/src/mistral-chat-language-model.ts
index 480c472d534bedbe8897979673453bd1c29a70b7..e46496da94f7d4af9822897202ca6baae67dae3a 100644
--- a/src/mistral-chat-language-model.ts
+++ b/src/mistral-chat-language-model.ts
@@ -129,6 +129,7 @@ export class MistralChatLanguageModel implements LanguageModelV3 {
top_p: topP,
random_seed: seed,
reasoning_effort: options.reasoningEffort,
+ prompt_cache_key: options.promptCacheKey,
// response format:
response_format:
diff --git a/src/mistral-chat-options.ts b/src/mistral-chat-options.ts
index 80fff45fba2c378fa06962f071946bcd2b882a0b..b4fdfa51f3bf11a4220e010c8aca92482cb0c3db 100644
--- a/src/mistral-chat-options.ts
+++ b/src/mistral-chat-options.ts
@@ -62,6 +62,11 @@ export const mistralLanguageModelOptions = z.object({
* - `'none'`: Disable reasoning
*/
reasoningEffort: z.enum(['high', 'none']).optional(),
+
+ /**
+ * A stable identifier used to route requests with shared prompt prefixes.
+ */
+ promptCacheKey: z.string().optional(),
});
export type MistralLanguageModelOptions = z.infer<

View file

@ -0,0 +1,101 @@
diff --git a/dist/index.js b/dist/index.js
index d73573b013e61903991cad2074187a3ade8921ab..ca7923e551f8f0cb4c27e0afd5f6cb3bc3b6c1c4 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -73,7 +73,7 @@ function convertOpenAICompatibleChatUsage(usage) {
}
const promptTokens = (_a = usage.prompt_tokens) != null ? _a : 0;
const completionTokens = (_b = usage.completion_tokens) != null ? _b : 0;
- const cacheReadTokens = (_d = (_c = usage.prompt_tokens_details) == null ? void 0 : _c.cached_tokens) != null ? _d : 0;
+ const cacheReadTokens = usage.prompt_tokens_details?.cached_tokens ?? usage.cached_tokens ?? 0;
const reasoningTokens = (_f = (_e = usage.completion_tokens_details) == null ? void 0 : _e.reasoning_tokens) != null ? _f : 0;
return {
inputTokens: {
@@ -894,6 +894,7 @@ var openaiCompatibleTokenUsageSchema = import_v43.z.looseObject({
prompt_tokens: import_v43.z.number().nullish(),
completion_tokens: import_v43.z.number().nullish(),
total_tokens: import_v43.z.number().nullish(),
+ cached_tokens: import_v43.z.number().nullish(),
prompt_tokens_details: import_v43.z.object({
cached_tokens: import_v43.z.number().nullish()
}).nullish(),
diff --git a/dist/index.mjs b/dist/index.mjs
index 237c9e209da9da2d4d557eb309e1adc2157c1426..fb45541149086633e27f02c89b742208222bb185 100644
--- a/dist/index.mjs
+++ b/dist/index.mjs
@@ -53,7 +53,7 @@ function convertOpenAICompatibleChatUsage(usage) {
}
const promptTokens = (_a = usage.prompt_tokens) != null ? _a : 0;
const completionTokens = (_b = usage.completion_tokens) != null ? _b : 0;
- const cacheReadTokens = (_d = (_c = usage.prompt_tokens_details) == null ? void 0 : _c.cached_tokens) != null ? _d : 0;
+ const cacheReadTokens = usage.prompt_tokens_details?.cached_tokens ?? usage.cached_tokens ?? 0;
const reasoningTokens = (_f = (_e = usage.completion_tokens_details) == null ? void 0 : _e.reasoning_tokens) != null ? _f : 0;
return {
inputTokens: {
@@ -881,6 +881,7 @@ var openaiCompatibleTokenUsageSchema = z3.looseObject({
prompt_tokens: z3.number().nullish(),
completion_tokens: z3.number().nullish(),
total_tokens: z3.number().nullish(),
+ cached_tokens: z3.number().nullish(),
prompt_tokens_details: z3.object({
cached_tokens: z3.number().nullish()
}).nullish(),
diff --git a/dist/internal/index.js b/dist/internal/index.js
index 0858632db778d30b303fbc9d5d46a29582d6e5b5..bb414ac25b77cb1a492cf6ad64f24decae38dd31 100644
--- a/dist/internal/index.js
+++ b/dist/internal/index.js
@@ -249,7 +249,7 @@ function convertOpenAICompatibleChatUsage(usage) {
}
const promptTokens = (_a = usage.prompt_tokens) != null ? _a : 0;
const completionTokens = (_b = usage.completion_tokens) != null ? _b : 0;
- const cacheReadTokens = (_d = (_c = usage.prompt_tokens_details) == null ? void 0 : _c.cached_tokens) != null ? _d : 0;
+ const cacheReadTokens = usage.prompt_tokens_details?.cached_tokens ?? usage.cached_tokens ?? 0;
const reasoningTokens = (_f = (_e = usage.completion_tokens_details) == null ? void 0 : _e.reasoning_tokens) != null ? _f : 0;
return {
inputTokens: {
diff --git a/dist/internal/index.mjs b/dist/internal/index.mjs
index 116b2deee86bf07cf981a64c000c7a4f3e42b52e..de08e6248318a0e48866ba41e9788485d56dd5cb 100644
--- a/dist/internal/index.mjs
+++ b/dist/internal/index.mjs
@@ -224,7 +224,7 @@ function convertOpenAICompatibleChatUsage(usage) {
}
const promptTokens = (_a = usage.prompt_tokens) != null ? _a : 0;
const completionTokens = (_b = usage.completion_tokens) != null ? _b : 0;
- const cacheReadTokens = (_d = (_c = usage.prompt_tokens_details) == null ? void 0 : _c.cached_tokens) != null ? _d : 0;
+ const cacheReadTokens = usage.prompt_tokens_details?.cached_tokens ?? usage.cached_tokens ?? 0;
const reasoningTokens = (_f = (_e = usage.completion_tokens_details) == null ? void 0 : _e.reasoning_tokens) != null ? _f : 0;
return {
inputTokens: {
diff --git a/src/chat/convert-openai-compatible-chat-usage.ts b/src/chat/convert-openai-compatible-chat-usage.ts
index 7982309cd99afbf7eb84a733c37446ac3cb9b626..454f14fe45af3f7475ba0371173c3342f242ccd9 100644
--- a/src/chat/convert-openai-compatible-chat-usage.ts
+++ b/src/chat/convert-openai-compatible-chat-usage.ts
@@ -5,6 +5,7 @@ export function convertOpenAICompatibleChatUsage(
| {
prompt_tokens?: number | null;
completion_tokens?: number | null;
+ cached_tokens?: number | null;
prompt_tokens_details?: {
cached_tokens?: number | null;
} | null;
@@ -34,7 +35,7 @@ export function convertOpenAICompatibleChatUsage(
const promptTokens = usage.prompt_tokens ?? 0;
const completionTokens = usage.completion_tokens ?? 0;
- const cacheReadTokens = usage.prompt_tokens_details?.cached_tokens ?? 0;
+ const cacheReadTokens = usage.prompt_tokens_details?.cached_tokens ?? usage.cached_tokens ?? 0;
const reasoningTokens =
usage.completion_tokens_details?.reasoning_tokens ?? 0;
diff --git a/src/chat/openai-compatible-chat-language-model.ts b/src/chat/openai-compatible-chat-language-model.ts
index 9a803cb1173e1454facb73ae430ede0ea0a3d2de..fd2c8e78ad850e11492b6be51922a382664739bf 100644
--- a/src/chat/openai-compatible-chat-language-model.ts
+++ b/src/chat/openai-compatible-chat-language-model.ts
@@ -723,6 +723,7 @@ const openaiCompatibleTokenUsageSchema = z
prompt_tokens: z.number().nullish(),
completion_tokens: z.number().nullish(),
total_tokens: z.number().nullish(),
+ cached_tokens: z.number().nullish(),
prompt_tokens_details: z
.object({
cached_tokens: z.number().nullish(),

View file

@ -0,0 +1,34 @@
diff --git a/dist/index.js b/dist/index.js
index 6fb7ae621cf5fe350ec9295af8f3d3507d265df3..ee16ee3af60f461fe2b074eb9cab44ea586c97ee 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -283,10 +283,10 @@ function createAiGateway(options) {
function parseAiGatewayOptions(options) {
const headers = new Headers();
if (options.skipCache === true) {
- headers.set("cf-skip-cache", "true");
+ headers.set("cf-aig-skip-cache", "true");
}
if (options.cacheTtl) {
- headers.set("cf-cache-ttl", options.cacheTtl.toString());
+ headers.set("cf-aig-cache-ttl", options.cacheTtl.toString());
}
if (options.metadata) {
headers.set("cf-aig-metadata", JSON.stringify(options.metadata));
diff --git a/dist/index.mjs b/dist/index.mjs
index 1c00a815117b5854073eb9c01cafa5bc273a367e..11acdaa65021952eb52622ebd3d9b87633e5f8e5 100644
--- a/dist/index.mjs
+++ b/dist/index.mjs
@@ -254,10 +254,10 @@ function createAiGateway(options) {
function parseAiGatewayOptions(options) {
const headers = new Headers();
if (options.skipCache === true) {
- headers.set("cf-skip-cache", "true");
+ headers.set("cf-aig-skip-cache", "true");
}
if (options.cacheTtl) {
- headers.set("cf-cache-ttl", options.cacheTtl.toString());
+ headers.set("cf-aig-cache-ttl", options.cacheTtl.toString());
}
if (options.metadata) {
headers.set("cf-aig-metadata", JSON.stringify(options.metadata));