@modelprofile.com/flexharness-models 10.13.0 → 10.14.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_ts_models/plugins.d.ts +1 -1
- package/dist_ts_models/plugins.js +1 -1
- package/dist_ts_models/smartai.cache.d.ts +80 -2
- package/dist_ts_models/smartai.cache.js +174 -29
- package/package.json +1 -1
- package/readme.md +37 -0
- package/ts_models/plugins.ts +1 -0
- package/ts_models/readme.md +37 -0
- package/ts_models/smartai.cache.ts +249 -34
|
@@ -3,6 +3,6 @@ export { generateText, jsonSchema, RetryError, StreamProviderError, streamText,
|
|
|
3
3
|
export type { ModelMessage, StreamTextResult, ToolSet } from 'ai';
|
|
4
4
|
import { APICallError } from '@ai-sdk/provider';
|
|
5
5
|
export { APICallError };
|
|
6
|
-
export type { JSONObject, JSONValue, LanguageModelV3, LanguageModelV3Prompt, LanguageModelV4, LanguageModelV4Middleware, LanguageModelV4Prompt, LanguageModelV4StreamPart, SharedV4Headers, } from '@ai-sdk/provider';
|
|
6
|
+
export type { JSONObject, JSONValue, LanguageModelV3, LanguageModelV3Prompt, LanguageModelV4, LanguageModelV4CallOptions, LanguageModelV4Middleware, LanguageModelV4Prompt, LanguageModelV4StreamPart, SharedV4Headers, } from '@ai-sdk/provider';
|
|
7
7
|
import { isAbortError, isProviderStreamError } from '@ai-sdk/provider-utils';
|
|
8
8
|
export { isAbortError, isProviderStreamError };
|
|
@@ -7,4 +7,4 @@ export { APICallError };
|
|
|
7
7
|
// ai-sdk provider utilities
|
|
8
8
|
import { isAbortError, isProviderStreamError } from '@ai-sdk/provider-utils';
|
|
9
9
|
export { isAbortError, isProviderStreamError };
|
|
10
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
10
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzX21vZGVscy9wbHVnaW5zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQWM7QUFDZCxPQUFPLEVBQUUsWUFBWSxFQUFFLFVBQVUsRUFBRSxVQUFVLEVBQUUsbUJBQW1CLEVBQUUsVUFBVSxFQUFFLElBQUksRUFBRSxNQUFNLElBQUksQ0FBQztBQUVqRyxPQUFPLEVBQUUsWUFBWSxFQUFFLFVBQVUsRUFBRSxVQUFVLEVBQUUsbUJBQW1CLEVBQUUsVUFBVSxFQUFFLElBQUksRUFBRSxDQUFDO0FBR3ZGLDRCQUE0QjtBQUM1QixPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFFaEQsT0FBTyxFQUFFLFlBQVksRUFBRSxDQUFDO0FBY3hCLDRCQUE0QjtBQUM1QixPQUFPLEVBQUUsWUFBWSxFQUFFLHFCQUFxQixFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFFN0UsT0FBTyxFQUFFLFlBQVksRUFBRSxxQkFBcUIsRUFBRSxDQUFDIn0=
|
|
@@ -5,10 +5,37 @@ export type TSmartAiCacheRetention = 'ephemeral' | '1h' | 'in_memory' | '24h';
|
|
|
5
5
|
export interface ISmartAiCacheOptions {
|
|
6
6
|
/** Provider-specific message cache marker namespace. Usually inferred from the model. */
|
|
7
7
|
provider?: TSmartAiMessageCacheProvider;
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* The OpenAI `prompt_cache_key`, sent as given. Without it the key is derived from the request's
|
|
10
|
+
* stable prefix, its instructions and tool definitions, so sessions that share a prefix share
|
|
11
|
+
* the provider's cache; see `deriveSmartAiPromptCacheKey`.
|
|
12
|
+
*/
|
|
9
13
|
key?: string;
|
|
10
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* Mixed into the derived key: requests of different scopes, such as tenants, never share a key,
|
|
16
|
+
* which keeps their cache routing and the provider's cache accounting apart.
|
|
17
|
+
*/
|
|
18
|
+
keyScope?: string;
|
|
19
|
+
/**
|
|
20
|
+
* How many keys a derived key is spread over on OpenAI models that route by key (before GPT-5.6),
|
|
21
|
+
* each session keeping one. OpenAI suggests about 15 requests a minute per key, so size it as the
|
|
22
|
+
* prefix's peak requests a minute across all processes divided by 15. Defaults to 1.
|
|
23
|
+
*/
|
|
24
|
+
keyBuckets?: number;
|
|
25
|
+
/**
|
|
26
|
+
* Anthropic: `'1h'` writes one-hour breakpoints instead of the five-minute default. OpenAI models
|
|
27
|
+
* before GPT-5.6: `'in_memory'` or `'24h'` is sent as `prompt_cache_retention`; without it the
|
|
28
|
+
* provider's default applies (24 hours, or in memory for Zero Data Retention organizations).
|
|
29
|
+
*/
|
|
11
30
|
retention?: TSmartAiCacheRetention;
|
|
31
|
+
/**
|
|
32
|
+
* Anthropic: the lifetime of the stable prefix's breakpoints, the tool definitions and system
|
|
33
|
+
* prompt, apart from the conversation's. `'1h'` (the default) keeps the prefix for sessions that
|
|
34
|
+
* start minutes apart while the growing conversation keeps five-minute breakpoints; writing it
|
|
35
|
+
* costs twice the input price instead of 1.25 times, once an hour on steady traffic.
|
|
36
|
+
* `'ephemeral'` writes it for five minutes. Never shorter than `retention`.
|
|
37
|
+
*/
|
|
38
|
+
prefixRetention?: 'ephemeral' | '1h';
|
|
12
39
|
/** Configured maximum idle scheduling budget, not a promise of cache retention or a cache hit. */
|
|
13
40
|
warmDurationMs?: number;
|
|
14
41
|
}
|
|
@@ -16,12 +43,62 @@ export type TSmartAiCacheSetting = boolean | 'auto' | ISmartAiCacheOptions;
|
|
|
16
43
|
export declare function mergeSmartAiProviderOptions(defaults?: TSmartAiProviderOptions, overrides?: TSmartAiProviderOptions): TSmartAiProviderOptions | undefined;
|
|
17
44
|
export declare function resolveSmartAiCacheProvider(provider?: string, modelId?: string): TSmartAiMessageCacheProvider | undefined;
|
|
18
45
|
export declare function getSmartAiMessageCacheProviderOptions(provider: TSmartAiMessageCacheProvider, options?: ISmartAiCacheOptions): TSmartAiProviderOptions;
|
|
46
|
+
type TSmartAiCacheableTool = NonNullable<plugins.LanguageModelV4CallOptions['tools']>[number];
|
|
19
47
|
export declare function applySmartAiPromptCaching(prompt: plugins.LanguageModelV4Prompt, options?: ISmartAiCacheOptions): plugins.LanguageModelV4Prompt;
|
|
48
|
+
/**
|
|
49
|
+
* Places cache markers on a request: its prompt and, for Anthropic, its tool definitions. See
|
|
50
|
+
* `planCacheMarkers` for where they go.
|
|
51
|
+
*/
|
|
52
|
+
export declare function applySmartAiRequestCaching<TRequest extends {
|
|
53
|
+
prompt: plugins.LanguageModelV4Prompt;
|
|
54
|
+
tools?: TSmartAiCacheableTool[];
|
|
55
|
+
}>(request: TRequest, options?: ISmartAiCacheOptions): TRequest;
|
|
20
56
|
export declare function createSmartAiCachingMiddleware(options?: ISmartAiCacheOptions): plugins.LanguageModelV4Middleware;
|
|
57
|
+
/**
|
|
58
|
+
* OpenAI routes a request to the machine that holds its cached prefix by a hash of the prefix's
|
|
59
|
+
* first tokens and the `prompt_cache_key`, and a key is meant to carry about 15 requests a minute
|
|
60
|
+
* across all prefixes using it before traffic overflows to other machines
|
|
61
|
+
* (https://developers.openai.com/api/docs/guides/prompt-caching, "Prompt cache keys" and
|
|
62
|
+
* "Cache location", read 2026-10-07). `keyBuckets` is sized by it.
|
|
63
|
+
*/
|
|
64
|
+
export declare const OPENAI_PROMPT_CACHE_KEY_REQUESTS_PER_MINUTE = 15;
|
|
65
|
+
/** A tool definition as a request sends it, for deriving a prompt cache key. */
|
|
66
|
+
export interface ISmartAiPromptCacheTool {
|
|
67
|
+
type?: string;
|
|
68
|
+
name: string;
|
|
69
|
+
description?: string;
|
|
70
|
+
inputSchema?: unknown;
|
|
71
|
+
strict?: boolean;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* A `prompt_cache_key` for the prefix every turn of a request repeats: its instructions and tool
|
|
75
|
+
* definitions, in the order they are sent, and the host's scope. Requests with the same prefix get
|
|
76
|
+
* the same key, so their sessions reach the machine holding it; any change to the prefix changes
|
|
77
|
+
* the key. A scope of letters, digits, `_` and `-` up to 40 characters stays readable in the key,
|
|
78
|
+
* any other is hashed. With `buckets`, sessions spread over that many keys, each keeping one, so a
|
|
79
|
+
* session's growing conversation stays with the machine that caches it.
|
|
80
|
+
*/
|
|
81
|
+
export declare function deriveSmartAiPromptCacheKey(input: {
|
|
82
|
+
system?: string;
|
|
83
|
+
tools?: readonly ISmartAiPromptCacheTool[];
|
|
84
|
+
scope?: string;
|
|
85
|
+
/** With `buckets` above 1: the session, which keeps its bucket for every request it sends. */
|
|
86
|
+
affinity?: string;
|
|
87
|
+
buckets?: number;
|
|
88
|
+
}): string;
|
|
89
|
+
/**
|
|
90
|
+
* Whether an OpenAI model routes its prompt cache by itself: from GPT-5.6 on the key only separates
|
|
91
|
+
* cache accounting (https://developers.openai.com/api/docs/guides/prompt-caching, "Summary of model
|
|
92
|
+
* differences", read 2026-10-07). Earlier and unrecognized models are routed by the key.
|
|
93
|
+
*/
|
|
94
|
+
export declare function openAiRoutesPromptCacheAutomatically(modelId: string | undefined): boolean;
|
|
21
95
|
export declare function getSmartAiCacheProviderOptions(input: {
|
|
22
96
|
provider?: string;
|
|
23
97
|
modelId?: string;
|
|
24
98
|
cache?: TSmartAiCacheSetting;
|
|
99
|
+
/** The derived `prompt_cache_key` (`deriveSmartAiPromptCacheKey`); a configured `key` takes precedence. */
|
|
100
|
+
promptCacheKey?: string;
|
|
101
|
+
/** The key used when neither a configured nor a derived key is given. */
|
|
25
102
|
sessionId?: string;
|
|
26
103
|
}): TSmartAiProviderOptions | undefined;
|
|
27
104
|
export declare function applySmartAiCacheProviderOptions(input: {
|
|
@@ -29,6 +106,7 @@ export declare function applySmartAiCacheProviderOptions(input: {
|
|
|
29
106
|
modelId?: string;
|
|
30
107
|
providerOptions?: TSmartAiProviderOptions;
|
|
31
108
|
cache?: TSmartAiCacheSetting;
|
|
109
|
+
promptCacheKey?: string;
|
|
32
110
|
sessionId?: string;
|
|
33
111
|
}): TSmartAiProviderOptions | undefined;
|
|
34
112
|
/** Observation at the model dispatch boundary; no field certifies a cache write or hit. */
|
|
@@ -126,37 +126,181 @@ function applyCacheToMessage(message, provider, options) {
|
|
|
126
126
|
}
|
|
127
127
|
return applyProviderOptionsDefaults(message, providerOptions);
|
|
128
128
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
129
|
+
/** Anthropic accepts at most four cache breakpoints per request; the AI SDK ignores any beyond. */
|
|
130
|
+
const MAX_CACHE_MARKERS = 4;
|
|
131
|
+
/** Whether a message, one of its parts, or a tool already carries a caller's cache marker. */
|
|
132
|
+
function hasCacheMarker(item, provider) {
|
|
133
|
+
if (!item)
|
|
134
|
+
return false;
|
|
135
|
+
const [namespace, controls] = Object.entries(getSmartAiMessageCacheProviderOptions(provider))[0];
|
|
136
|
+
const caller = item.providerOptions?.[namespace];
|
|
137
|
+
return isObject(caller) && (Object.keys(controls).some((key) => Object.prototype.hasOwnProperty.call(caller, key))
|
|
138
|
+
|| (namespace === 'anthropic' && Object.prototype.hasOwnProperty.call(caller, 'cache_control')));
|
|
139
|
+
}
|
|
140
|
+
function cacheMarkerCount(message, provider) {
|
|
141
|
+
const own = hasCacheMarker(message, provider) ? 1 : 0;
|
|
142
|
+
const parts = Array.isArray(message.content)
|
|
143
|
+
? message.content.filter((part) => hasCacheMarker(part, provider)).length
|
|
144
|
+
: 0;
|
|
145
|
+
return own + parts;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Where a request's cache markers go, within the provider's limit of four, counting markers a
|
|
149
|
+
* caller set itself. The first two system messages are the stable ones (later system messages may
|
|
150
|
+
* carry per-request content); the second, or the only one, ends the stable prefix and also caches
|
|
151
|
+
* the tool definitions rendered before it. Without a system message the last tool definition ends
|
|
152
|
+
* the prefix. In order: that end of the prefix, the last message, the message before it (the rolling
|
|
153
|
+
* breakpoints of a growing conversation), and the first system message.
|
|
154
|
+
*/
|
|
155
|
+
function planCacheMarkers(prompt, tools, provider) {
|
|
156
|
+
const marked = new Set();
|
|
157
|
+
const plan = { messages: new Set(), prefixMessages: new Set(), callerMarkers: 0 };
|
|
158
|
+
// The provider counts every block that carries a marker: a message's own and each marked part.
|
|
159
|
+
prompt.forEach((message, index) => {
|
|
160
|
+
const blocks = cacheMarkerCount(message, provider);
|
|
161
|
+
if (blocks > 0)
|
|
162
|
+
marked.add(index);
|
|
163
|
+
plan.callerMarkers += blocks;
|
|
164
|
+
});
|
|
165
|
+
const markedTools = (tools ?? []).filter((definition) => hasCacheMarker(definition, provider)).length;
|
|
166
|
+
const toolMarked = markedTools > 0;
|
|
167
|
+
plan.callerMarkers += markedTools;
|
|
168
|
+
let budget = MAX_CACHE_MARKERS - plan.callerMarkers;
|
|
169
|
+
const system = prompt.flatMap((message, index) => message.role === 'system' ? [index] : []).slice(0, 2);
|
|
170
|
+
const nonSystem = prompt.flatMap((message, index) => message.role === 'system' ? [] : [index]);
|
|
171
|
+
const rolling = nonSystem.slice(-2).reverse();
|
|
172
|
+
// A one-hour prefix marker must precede every five-minute one.
|
|
173
|
+
const isPrefix = (index) => rolling.every((rollingIndex) => index < rollingIndex) ? true : undefined;
|
|
174
|
+
const candidates = [];
|
|
175
|
+
if (system.length > 0) {
|
|
176
|
+
candidates.push({ message: system[system.length - 1], prefix: isPrefix(system[system.length - 1]) });
|
|
177
|
+
}
|
|
178
|
+
else if (provider === 'anthropic' && tools && tools.length > 0 && !toolMarked) {
|
|
179
|
+
candidates.push({ tool: tools.length - 1 });
|
|
180
|
+
}
|
|
181
|
+
for (const index of rolling)
|
|
182
|
+
candidates.push({ message: index });
|
|
183
|
+
if (system.length > 1)
|
|
184
|
+
candidates.push({ message: system[0], prefix: isPrefix(system[0]) });
|
|
185
|
+
for (const candidate of candidates) {
|
|
186
|
+
if ('tool' in candidate) {
|
|
187
|
+
if (budget <= 0)
|
|
188
|
+
continue;
|
|
189
|
+
plan.tool = candidate.tool;
|
|
190
|
+
budget--;
|
|
140
191
|
continue;
|
|
141
192
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
193
|
+
if (plan.messages.has(candidate.message))
|
|
194
|
+
continue;
|
|
195
|
+
if (!marked.has(candidate.message)) {
|
|
196
|
+
if (budget <= 0)
|
|
197
|
+
continue;
|
|
198
|
+
budget--;
|
|
199
|
+
}
|
|
200
|
+
plan.messages.add(candidate.message);
|
|
201
|
+
if (candidate.prefix)
|
|
202
|
+
plan.prefixMessages.add(candidate.message);
|
|
146
203
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
204
|
+
return plan;
|
|
205
|
+
}
|
|
206
|
+
export function applySmartAiPromptCaching(prompt, options = {}) {
|
|
207
|
+
return applySmartAiRequestCaching({ prompt }, options).prompt;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Places cache markers on a request: its prompt and, for Anthropic, its tool definitions. See
|
|
211
|
+
* `planCacheMarkers` for where they go.
|
|
212
|
+
*/
|
|
213
|
+
export function applySmartAiRequestCaching(request, options = {}) {
|
|
214
|
+
const provider = options.provider ?? 'anthropic';
|
|
215
|
+
const plan = planCacheMarkers(request.prompt, request.tools, provider);
|
|
216
|
+
// A one-hour breakpoint must precede five-minute ones: the prefix never lives shorter than the
|
|
217
|
+
// rest, and a caller's own markers, whose lifetimes and places are its own, keep the prefix at the
|
|
218
|
+
// request's retention.
|
|
219
|
+
const prefixOptions = options.retention === '1h'
|
|
220
|
+
|| ((options.prefixRetention ?? '1h') === '1h' && plan.callerMarkers === 0)
|
|
221
|
+
? { ...options, retention: '1h' }
|
|
222
|
+
: options;
|
|
223
|
+
const prompt = plan.messages.size === 0
|
|
224
|
+
? request.prompt
|
|
225
|
+
: request.prompt.map((message, index) => plan.messages.has(index)
|
|
226
|
+
? applyCacheToMessage(message, provider, plan.prefixMessages.has(index) ? prefixOptions : options)
|
|
227
|
+
: message);
|
|
228
|
+
const tools = plan.tool === undefined || !request.tools
|
|
229
|
+
? request.tools
|
|
230
|
+
: request.tools.map((definition, index) => index === plan.tool
|
|
231
|
+
? applyProviderOptionsDefaults(definition, getSmartAiMessageCacheProviderOptions(provider, prefixOptions))
|
|
232
|
+
: definition);
|
|
233
|
+
return { ...request, prompt, ...(tools ? { tools } : {}) };
|
|
150
234
|
}
|
|
151
235
|
export function createSmartAiCachingMiddleware(options = {}) {
|
|
152
236
|
return {
|
|
153
237
|
specificationVersion: 'v4',
|
|
154
|
-
transformParams: async ({ params }) => (
|
|
155
|
-
...params,
|
|
156
|
-
prompt: applySmartAiPromptCaching(params.prompt, options),
|
|
157
|
-
}),
|
|
238
|
+
transformParams: async ({ params }) => applySmartAiRequestCaching(params, options),
|
|
158
239
|
};
|
|
159
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* OpenAI routes a request to the machine that holds its cached prefix by a hash of the prefix's
|
|
243
|
+
* first tokens and the `prompt_cache_key`, and a key is meant to carry about 15 requests a minute
|
|
244
|
+
* across all prefixes using it before traffic overflows to other machines
|
|
245
|
+
* (https://developers.openai.com/api/docs/guides/prompt-caching, "Prompt cache keys" and
|
|
246
|
+
* "Cache location", read 2026-10-07). `keyBuckets` is sized by it.
|
|
247
|
+
*/
|
|
248
|
+
export const OPENAI_PROMPT_CACHE_KEY_REQUESTS_PER_MINUTE = 15;
|
|
249
|
+
const hash64 = (text) => {
|
|
250
|
+
// Two 32-bit FNV-1a lanes: a routing key needs a stable digest, not a cryptographic one.
|
|
251
|
+
let low = 0x811c9dc5;
|
|
252
|
+
let high = 0xcbf29ce4;
|
|
253
|
+
for (let index = 0; index < text.length; index++) {
|
|
254
|
+
const code = text.charCodeAt(index);
|
|
255
|
+
low = Math.imul(low ^ code, 0x01000193) >>> 0;
|
|
256
|
+
high = Math.imul(high ^ code ^ (low >>> 7), 0x01000193) >>> 0;
|
|
257
|
+
}
|
|
258
|
+
return high.toString(16).padStart(8, '0') + low.toString(16).padStart(8, '0');
|
|
259
|
+
};
|
|
260
|
+
/**
|
|
261
|
+
* A `prompt_cache_key` for the prefix every turn of a request repeats: its instructions and tool
|
|
262
|
+
* definitions, in the order they are sent, and the host's scope. Requests with the same prefix get
|
|
263
|
+
* the same key, so their sessions reach the machine holding it; any change to the prefix changes
|
|
264
|
+
* the key. A scope of letters, digits, `_` and `-` up to 40 characters stays readable in the key,
|
|
265
|
+
* any other is hashed. With `buckets`, sessions spread over that many keys, each keeping one, so a
|
|
266
|
+
* session's growing conversation stays with the machine that caches it.
|
|
267
|
+
*/
|
|
268
|
+
export function deriveSmartAiPromptCacheKey(input) {
|
|
269
|
+
const prefix = hash64(JSON.stringify([
|
|
270
|
+
input.system ?? '',
|
|
271
|
+
(input.tools ?? []).map((definition) => [
|
|
272
|
+
definition.type ?? 'function',
|
|
273
|
+
definition.name,
|
|
274
|
+
definition.description ?? '',
|
|
275
|
+
definition.inputSchema ?? null,
|
|
276
|
+
definition.strict ?? null,
|
|
277
|
+
]),
|
|
278
|
+
]));
|
|
279
|
+
const buckets = input.buckets ?? 1;
|
|
280
|
+
if (!Number.isSafeInteger(buckets) || buckets < 1)
|
|
281
|
+
throw new Error('keyBuckets must be a positive integer.');
|
|
282
|
+
const bucket = buckets > 1 && input.affinity !== undefined
|
|
283
|
+
? Number.parseInt(hash64(input.affinity).slice(8), 16) % buckets
|
|
284
|
+
: 0;
|
|
285
|
+
const key = bucket === 0 ? prefix : `${prefix}.${bucket}`;
|
|
286
|
+
if (input.scope === undefined || input.scope === '')
|
|
287
|
+
return key;
|
|
288
|
+
const scope = /^[A-Za-z0-9_-]{1,40}$/.test(input.scope) ? input.scope : `s${hash64(input.scope)}`;
|
|
289
|
+
return `${scope}.${key}`;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Whether an OpenAI model routes its prompt cache by itself: from GPT-5.6 on the key only separates
|
|
293
|
+
* cache accounting (https://developers.openai.com/api/docs/guides/prompt-caching, "Summary of model
|
|
294
|
+
* differences", read 2026-10-07). Earlier and unrecognized models are routed by the key.
|
|
295
|
+
*/
|
|
296
|
+
export function openAiRoutesPromptCacheAutomatically(modelId) {
|
|
297
|
+
const match = /(?:^|[^a-z0-9])gpt-(\d+)(?:\.(\d+))?/i.exec(modelId ?? '');
|
|
298
|
+
if (!match)
|
|
299
|
+
return false;
|
|
300
|
+
const major = Number(match[1]);
|
|
301
|
+
const minor = Number(match[2] ?? 0);
|
|
302
|
+
return major > 5 || (major === 5 && minor >= 6);
|
|
303
|
+
}
|
|
160
304
|
function isOpenAiProvider(provider) {
|
|
161
305
|
const providerLower = provider?.toLowerCase() ?? '';
|
|
162
306
|
return providerLower === 'openai' || providerLower.startsWith('openai.') || providerLower.includes('@ai-sdk/openai');
|
|
@@ -166,16 +310,17 @@ export function getSmartAiCacheProviderOptions(input) {
|
|
|
166
310
|
if (!cacheOptions)
|
|
167
311
|
return undefined;
|
|
168
312
|
if (isOpenAiProvider(input.provider)) {
|
|
169
|
-
const key = cacheOptions.key ?? input.sessionId;
|
|
313
|
+
const key = cacheOptions.key ?? input.promptCacheKey ?? input.sessionId;
|
|
314
|
+
// GPT-5.6 and later take `prompt_cache_options.ttl` instead, whose only value is its default.
|
|
315
|
+
const retention = (cacheOptions.retention === '24h' || cacheOptions.retention === 'in_memory')
|
|
316
|
+
&& !openAiRoutesPromptCacheAutomatically(input.modelId)
|
|
317
|
+
? cacheOptions.retention
|
|
318
|
+
: undefined;
|
|
170
319
|
return {
|
|
171
320
|
openai: {
|
|
172
321
|
store: false,
|
|
173
322
|
...(key ? { promptCacheKey: key } : {}),
|
|
174
|
-
...(
|
|
175
|
-
? { promptCacheRetention: cacheOptions.retention }
|
|
176
|
-
: key
|
|
177
|
-
? { promptCacheRetention: 'in_memory' }
|
|
178
|
-
: {}),
|
|
323
|
+
...(retention ? { promptCacheRetention: retention } : {}),
|
|
179
324
|
},
|
|
180
325
|
};
|
|
181
326
|
}
|
|
@@ -258,4 +403,4 @@ export function getSmartAiCacheWaitMs(requestedMs, dispatch, now = Date.now()) {
|
|
|
258
403
|
const headroom = Math.min(1000, Math.ceil(dispatch.warmDurationMs / 10));
|
|
259
404
|
return Math.max(0, Math.min(maximum, Math.floor(dispatch.idleDeadline - now - headroom)));
|
|
260
405
|
}
|
|
261
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhaS5jYWNoZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzX21vZGVscy9zbWFydGFpLmNhY2hlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQTBCQSxTQUFTLFFBQVEsQ0FBQyxLQUFjO0lBQzlCLE9BQU8sT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLEtBQUssS0FBSyxJQUFJLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDO0FBQzlFLENBQUM7QUFFRCxTQUFTLGlCQUFpQixDQUFDLFFBQTRCLEVBQUUsU0FBOEI7SUFDckYsTUFBTSxNQUFNLEdBQXVCLEVBQUUsR0FBRyxRQUFRLEVBQUUsQ0FBQztJQUVuRCxJQUFJLENBQUMsU0FBUztRQUFFLE9BQU8sTUFBTSxDQUFDO0lBRTlCLEtBQUssTUFBTSxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsSUFBSSxNQUFNLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUM7UUFDckQsTUFBTSxRQUFRLEdBQUcsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQzdCLElBQUksUUFBUSxDQUFDLFFBQVEsQ0FBQyxJQUFJLFFBQVEsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQzFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsR0FBRyxpQkFBaUIsQ0FBQyxRQUE4QixFQUFFLEtBQTJCLENBQUMsQ0FBQztZQUM3RixTQUFTO1FBQ1gsQ0FBQztRQUNELE1BQU0sQ0FBQyxHQUFHLENBQUMsR0FBRyxLQUEwQixDQUFDO0lBQzNDLENBQUM7SUFFRCxPQUFPLE1BQU0sQ0FBQztBQUNoQixDQUFDO0FBRUQsTUFBTSxVQUFVLDJCQUEyQixDQUN6QyxRQUFrQyxFQUNsQyxTQUFtQztJQUVuQyxJQUFJLENBQUMsUUFBUTtRQUFFLE9BQU8sU0FBUyxDQUFDO0lBQ2hDLElBQUksQ0FBQyxTQUFTO1FBQUUsT0FBTyxRQUFRLENBQUM7SUFDaEMsT0FBTyxpQkFBaUIs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
|
|
406
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhaS5jYWNoZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzX21vZGVscy9zbWFydGFpLmNhY2hlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQXFEQSxTQUFTLFFBQVEsQ0FBQyxLQUFjO0lBQzlCLE9BQU8sT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLEtBQUssS0FBSyxJQUFJLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDO0FBQzlFLENBQUM7QUFFRCxTQUFTLGlCQUFpQixDQUFDLFFBQTRCLEVBQUUsU0FBOEI7SUFDckYsTUFBTSxNQUFNLEdBQXVCLEVBQUUsR0FBRyxRQUFRLEVBQUUsQ0FBQztJQUVuRCxJQUFJLENBQUMsU0FBUztRQUFFLE9BQU8sTUFBTSxDQUFDO0lBRTlCLEtBQUssTUFBTSxDQUFDLEdBQUcsRUFBRSxLQUFLLENBQUMsSUFBSSxNQUFNLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUM7UUFDckQsTUFBTSxRQUFRLEdBQUcsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQzdCLElBQUksUUFBUSxDQUFDLFFBQVEsQ0FBQyxJQUFJLFFBQVEsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQzFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsR0FBRyxpQkFBaUIsQ0FBQyxRQUE4QixFQUFFLEtBQTJCLENBQUMsQ0FBQztZQUM3RixTQUFTO1FBQ1gsQ0FBQztRQUNELE1BQU0sQ0FBQyxHQUFHLENBQUMsR0FBRyxLQUEwQixDQUFDO0lBQzNDLENBQUM7SUFFRCxPQUFPLE1BQU0sQ0FBQztBQUNoQixDQUFDO0FBRUQsTUFBTSxVQUFVLDJCQUEyQixDQUN6QyxRQUFrQyxFQUNsQyxTQUFtQztJQUVuQyxJQUFJLENBQUMsUUFBUTtRQUFFLE9BQU8sU0FBUyxDQUFDO0lBQ2hDLElBQUksQ0FBQyxTQUFTO1FBQUUsT0FBTyxRQUFRLENBQUM7SUFDaEMsT0FBTyxpQkFBaUIs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
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"author": "Task Venture Capital GmbH",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"name": "@modelprofile.com/flexharness-models",
|
|
5
|
-
"version": "10.
|
|
5
|
+
"version": "10.14.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"description": "Provider-independent model contracts, prompt caching and explicit model registration.",
|
|
8
8
|
"main": "./dist_ts_models/index.js",
|
package/readme.md
CHANGED
|
@@ -48,6 +48,43 @@ media type and the content decoded in the stated charset (UTF-8 by default), in
|
|
|
48
48
|
than any backtick run in the content. Images, PDFs, other binary files, URLs and file references
|
|
49
49
|
pass unchanged; a file that names text but does not decode fails before the request is sent.
|
|
50
50
|
|
|
51
|
+
### Prompt caching
|
|
52
|
+
|
|
53
|
+
`TSmartAiCacheSetting` is `'auto'` (the default of a FlexHarness session), `false`, or an
|
|
54
|
+
`ISmartAiCacheOptions` object. It decides the cache hints a request carries:
|
|
55
|
+
|
|
56
|
+
- **Anthropic** (and the message-level dialects of OpenRouter, Bedrock and OpenAI-compatible
|
|
57
|
+
servers): `applySmartAiRequestCaching({ prompt, tools }, options)` places at most four
|
|
58
|
+
breakpoints, counting markers the caller set itself. The first two system messages are the stable
|
|
59
|
+
ones; the second, or the only one, ends the stable prefix and caches the tool definitions rendered
|
|
60
|
+
before it. Without a system message the last tool definition ends the prefix, so sessions share
|
|
61
|
+
it. The last message and the one before it carry the rolling breakpoints of a growing
|
|
62
|
+
conversation, then the first system message. On Anthropic the stable prefix's breakpoints last an
|
|
63
|
+
hour (`prefixRetention`, default `'1h'`), so sessions that start minutes apart still read it, while the
|
|
64
|
+
conversation keeps five-minute breakpoints; a one-hour write costs twice the input price, a
|
|
65
|
+
five-minute write 1.25 times, and on steady traffic the prefix is written once an hour.
|
|
66
|
+
`prefixRetention: 'ephemeral'` writes the prefix for five minutes; `retention: '1h'` makes every
|
|
67
|
+
breakpoint last an hour. A request that carries markers of the caller's own
|
|
68
|
+
keeps the prefix at `retention`, since a one-hour breakpoint may not follow a five-minute one.
|
|
69
|
+
`createSmartAiCachingMiddleware(options)` applies it to every call of a model. Each model has a
|
|
70
|
+
minimum cacheable prefix (512 to 4,096 tokens); a breakpoint below it neither writes nor reads and
|
|
71
|
+
costs nothing, so the planner places breakpoints without counting tokens it cannot count before
|
|
72
|
+
the request is sent.
|
|
73
|
+
- **OpenAI** (Responses and the ChatGPT Codex backend): `store: false` and a `prompt_cache_key`.
|
|
74
|
+
`deriveSmartAiPromptCacheKey({ system, tools, scope })` derives the key from the instructions and
|
|
75
|
+
tool definitions every turn repeats, so sessions with one prefix reach the machine that caches it;
|
|
76
|
+
any change to the prefix changes the key. `keyScope` keeps tenants apart, and `key` replaces the
|
|
77
|
+
derived key. OpenAI suggests about 15 requests a minute per key
|
|
78
|
+
(`OPENAI_PROMPT_CACHE_KEY_REQUESTS_PER_MINUTE`); `keyBuckets` spreads a prefix over that many keys,
|
|
79
|
+
each session keeping one by its session id, on models that route by key; requests without a
|
|
80
|
+
session id take the first. From GPT-5.6 on OpenAI routes by itself
|
|
81
|
+
and the key only separates cache accounting (`openAiRoutesPromptCacheAutomatically`).
|
|
82
|
+
`retention: 'in_memory' | '24h'` is sent as `prompt_cache_retention` to earlier models; without it
|
|
83
|
+
the provider's default applies.
|
|
84
|
+
|
|
85
|
+
`getSmartAiCacheProviderOptions` and `applySmartAiCacheProviderOptions` return the provider options
|
|
86
|
+
of a setting; a caller's own options win.
|
|
87
|
+
|
|
51
88
|
## License and Legal Information
|
|
52
89
|
|
|
53
90
|
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the repository license file.
|
package/ts_models/plugins.ts
CHANGED
package/ts_models/readme.md
CHANGED
|
@@ -48,6 +48,43 @@ media type and the content decoded in the stated charset (UTF-8 by default), in
|
|
|
48
48
|
than any backtick run in the content. Images, PDFs, other binary files, URLs and file references
|
|
49
49
|
pass unchanged; a file that names text but does not decode fails before the request is sent.
|
|
50
50
|
|
|
51
|
+
### Prompt caching
|
|
52
|
+
|
|
53
|
+
`TSmartAiCacheSetting` is `'auto'` (the default of a FlexHarness session), `false`, or an
|
|
54
|
+
`ISmartAiCacheOptions` object. It decides the cache hints a request carries:
|
|
55
|
+
|
|
56
|
+
- **Anthropic** (and the message-level dialects of OpenRouter, Bedrock and OpenAI-compatible
|
|
57
|
+
servers): `applySmartAiRequestCaching({ prompt, tools }, options)` places at most four
|
|
58
|
+
breakpoints, counting markers the caller set itself. The first two system messages are the stable
|
|
59
|
+
ones; the second, or the only one, ends the stable prefix and caches the tool definitions rendered
|
|
60
|
+
before it. Without a system message the last tool definition ends the prefix, so sessions share
|
|
61
|
+
it. The last message and the one before it carry the rolling breakpoints of a growing
|
|
62
|
+
conversation, then the first system message. On Anthropic the stable prefix's breakpoints last an
|
|
63
|
+
hour (`prefixRetention`, default `'1h'`), so sessions that start minutes apart still read it, while the
|
|
64
|
+
conversation keeps five-minute breakpoints; a one-hour write costs twice the input price, a
|
|
65
|
+
five-minute write 1.25 times, and on steady traffic the prefix is written once an hour.
|
|
66
|
+
`prefixRetention: 'ephemeral'` writes the prefix for five minutes; `retention: '1h'` makes every
|
|
67
|
+
breakpoint last an hour. A request that carries markers of the caller's own
|
|
68
|
+
keeps the prefix at `retention`, since a one-hour breakpoint may not follow a five-minute one.
|
|
69
|
+
`createSmartAiCachingMiddleware(options)` applies it to every call of a model. Each model has a
|
|
70
|
+
minimum cacheable prefix (512 to 4,096 tokens); a breakpoint below it neither writes nor reads and
|
|
71
|
+
costs nothing, so the planner places breakpoints without counting tokens it cannot count before
|
|
72
|
+
the request is sent.
|
|
73
|
+
- **OpenAI** (Responses and the ChatGPT Codex backend): `store: false` and a `prompt_cache_key`.
|
|
74
|
+
`deriveSmartAiPromptCacheKey({ system, tools, scope })` derives the key from the instructions and
|
|
75
|
+
tool definitions every turn repeats, so sessions with one prefix reach the machine that caches it;
|
|
76
|
+
any change to the prefix changes the key. `keyScope` keeps tenants apart, and `key` replaces the
|
|
77
|
+
derived key. OpenAI suggests about 15 requests a minute per key
|
|
78
|
+
(`OPENAI_PROMPT_CACHE_KEY_REQUESTS_PER_MINUTE`); `keyBuckets` spreads a prefix over that many keys,
|
|
79
|
+
each session keeping one by its session id, on models that route by key; requests without a
|
|
80
|
+
session id take the first. From GPT-5.6 on OpenAI routes by itself
|
|
81
|
+
and the key only separates cache accounting (`openAiRoutesPromptCacheAutomatically`).
|
|
82
|
+
`retention: 'in_memory' | '24h'` is sent as `prompt_cache_retention` to earlier models; without it
|
|
83
|
+
the provider's default applies.
|
|
84
|
+
|
|
85
|
+
`getSmartAiCacheProviderOptions` and `applySmartAiCacheProviderOptions` return the provider options
|
|
86
|
+
of a setting; a caller's own options win.
|
|
87
|
+
|
|
51
88
|
## License and Legal Information
|
|
52
89
|
|
|
53
90
|
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the repository license file.
|
|
@@ -14,10 +14,37 @@ export type TSmartAiCacheRetention = 'ephemeral' | '1h' | 'in_memory' | '24h';
|
|
|
14
14
|
export interface ISmartAiCacheOptions {
|
|
15
15
|
/** Provider-specific message cache marker namespace. Usually inferred from the model. */
|
|
16
16
|
provider?: TSmartAiMessageCacheProvider;
|
|
17
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* The OpenAI `prompt_cache_key`, sent as given. Without it the key is derived from the request's
|
|
19
|
+
* stable prefix, its instructions and tool definitions, so sessions that share a prefix share
|
|
20
|
+
* the provider's cache; see `deriveSmartAiPromptCacheKey`.
|
|
21
|
+
*/
|
|
18
22
|
key?: string;
|
|
19
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* Mixed into the derived key: requests of different scopes, such as tenants, never share a key,
|
|
25
|
+
* which keeps their cache routing and the provider's cache accounting apart.
|
|
26
|
+
*/
|
|
27
|
+
keyScope?: string;
|
|
28
|
+
/**
|
|
29
|
+
* How many keys a derived key is spread over on OpenAI models that route by key (before GPT-5.6),
|
|
30
|
+
* each session keeping one. OpenAI suggests about 15 requests a minute per key, so size it as the
|
|
31
|
+
* prefix's peak requests a minute across all processes divided by 15. Defaults to 1.
|
|
32
|
+
*/
|
|
33
|
+
keyBuckets?: number;
|
|
34
|
+
/**
|
|
35
|
+
* Anthropic: `'1h'` writes one-hour breakpoints instead of the five-minute default. OpenAI models
|
|
36
|
+
* before GPT-5.6: `'in_memory'` or `'24h'` is sent as `prompt_cache_retention`; without it the
|
|
37
|
+
* provider's default applies (24 hours, or in memory for Zero Data Retention organizations).
|
|
38
|
+
*/
|
|
20
39
|
retention?: TSmartAiCacheRetention;
|
|
40
|
+
/**
|
|
41
|
+
* Anthropic: the lifetime of the stable prefix's breakpoints, the tool definitions and system
|
|
42
|
+
* prompt, apart from the conversation's. `'1h'` (the default) keeps the prefix for sessions that
|
|
43
|
+
* start minutes apart while the growing conversation keeps five-minute breakpoints; writing it
|
|
44
|
+
* costs twice the input price instead of 1.25 times, once an hour on steady traffic.
|
|
45
|
+
* `'ephemeral'` writes it for five minutes. Never shorter than `retention`.
|
|
46
|
+
*/
|
|
47
|
+
prefixRetention?: 'ephemeral' | '1h';
|
|
21
48
|
/** Configured maximum idle scheduling budget, not a promise of cache retention or a cache hit. */
|
|
22
49
|
warmDurationMs?: number;
|
|
23
50
|
}
|
|
@@ -176,46 +203,229 @@ function applyCacheToMessage(
|
|
|
176
203
|
return applyProviderOptionsDefaults(message, providerOptions);
|
|
177
204
|
}
|
|
178
205
|
|
|
179
|
-
|
|
206
|
+
/** Anthropic accepts at most four cache breakpoints per request; the AI SDK ignores any beyond. */
|
|
207
|
+
const MAX_CACHE_MARKERS = 4;
|
|
208
|
+
|
|
209
|
+
type TSmartAiPromptMessage = plugins.LanguageModelV4Prompt[number];
|
|
210
|
+
type TSmartAiCacheableTool = NonNullable<plugins.LanguageModelV4CallOptions['tools']>[number];
|
|
211
|
+
|
|
212
|
+
/** Whether a message, one of its parts, or a tool already carries a caller's cache marker. */
|
|
213
|
+
function hasCacheMarker(
|
|
214
|
+
item: { providerOptions?: TSmartAiProviderOptions } | undefined,
|
|
215
|
+
provider: TSmartAiMessageCacheProvider,
|
|
216
|
+
): boolean {
|
|
217
|
+
if (!item) return false;
|
|
218
|
+
const [namespace, controls] = Object.entries(getSmartAiMessageCacheProviderOptions(provider))[0];
|
|
219
|
+
const caller = item.providerOptions?.[namespace];
|
|
220
|
+
return isObject(caller) && (
|
|
221
|
+
Object.keys(controls).some((key) => Object.prototype.hasOwnProperty.call(caller, key))
|
|
222
|
+
|| (namespace === 'anthropic' && Object.prototype.hasOwnProperty.call(caller, 'cache_control'))
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function cacheMarkerCount(message: TSmartAiPromptMessage, provider: TSmartAiMessageCacheProvider): number {
|
|
227
|
+
const own = hasCacheMarker(message as { providerOptions?: TSmartAiProviderOptions }, provider) ? 1 : 0;
|
|
228
|
+
const parts = Array.isArray(message.content)
|
|
229
|
+
? message.content.filter((part) => hasCacheMarker(part as { providerOptions?: TSmartAiProviderOptions }, provider)).length
|
|
230
|
+
: 0;
|
|
231
|
+
return own + parts;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
interface ICacheMarkerPlan {
|
|
235
|
+
/** Markers the caller set itself, counted per block as the provider counts them. */
|
|
236
|
+
callerMarkers: number;
|
|
237
|
+
messages: Set<number>;
|
|
238
|
+
/** The messages among `messages` that end the stable prefix rather than the conversation. */
|
|
239
|
+
prefixMessages: Set<number>;
|
|
240
|
+
/** The tool definition that ends the stable prefix of a request without a system message. */
|
|
241
|
+
tool?: number;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Where a request's cache markers go, within the provider's limit of four, counting markers a
|
|
246
|
+
* caller set itself. The first two system messages are the stable ones (later system messages may
|
|
247
|
+
* carry per-request content); the second, or the only one, ends the stable prefix and also caches
|
|
248
|
+
* the tool definitions rendered before it. Without a system message the last tool definition ends
|
|
249
|
+
* the prefix. In order: that end of the prefix, the last message, the message before it (the rolling
|
|
250
|
+
* breakpoints of a growing conversation), and the first system message.
|
|
251
|
+
*/
|
|
252
|
+
function planCacheMarkers(
|
|
180
253
|
prompt: plugins.LanguageModelV4Prompt,
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
254
|
+
tools: readonly TSmartAiCacheableTool[] | undefined,
|
|
255
|
+
provider: TSmartAiMessageCacheProvider,
|
|
256
|
+
): ICacheMarkerPlan {
|
|
257
|
+
const marked = new Set<number>();
|
|
258
|
+
const plan: ICacheMarkerPlan = { messages: new Set(), prefixMessages: new Set(), callerMarkers: 0 };
|
|
259
|
+
// The provider counts every block that carries a marker: a message's own and each marked part.
|
|
260
|
+
prompt.forEach((message, index) => {
|
|
261
|
+
const blocks = cacheMarkerCount(message, provider);
|
|
262
|
+
if (blocks > 0) marked.add(index);
|
|
263
|
+
plan.callerMarkers += blocks;
|
|
264
|
+
});
|
|
265
|
+
const markedTools = (tools ?? []).filter((definition) =>
|
|
266
|
+
hasCacheMarker(definition as { providerOptions?: TSmartAiProviderOptions }, provider)).length;
|
|
267
|
+
const toolMarked = markedTools > 0;
|
|
268
|
+
plan.callerMarkers += markedTools;
|
|
269
|
+
let budget = MAX_CACHE_MARKERS - plan.callerMarkers;
|
|
270
|
+
const system = prompt.flatMap((message, index) => message.role === 'system' ? [index] : []).slice(0, 2);
|
|
271
|
+
const nonSystem = prompt.flatMap((message, index) => message.role === 'system' ? [] : [index]);
|
|
272
|
+
const rolling = nonSystem.slice(-2).reverse();
|
|
273
|
+
// A one-hour prefix marker must precede every five-minute one.
|
|
274
|
+
const isPrefix = (index: number): true | undefined => rolling.every((rollingIndex) => index < rollingIndex) ? true : undefined;
|
|
275
|
+
const candidates: Array<{ message: number; prefix?: true | undefined } | { tool: number }> = [];
|
|
276
|
+
if (system.length > 0) {
|
|
277
|
+
candidates.push({ message: system[system.length - 1], prefix: isPrefix(system[system.length - 1]) });
|
|
278
|
+
} else if (provider === 'anthropic' && tools && tools.length > 0 && !toolMarked) {
|
|
279
|
+
candidates.push({ tool: tools.length - 1 });
|
|
280
|
+
}
|
|
281
|
+
for (const index of rolling) candidates.push({ message: index });
|
|
282
|
+
if (system.length > 1) candidates.push({ message: system[0], prefix: isPrefix(system[0]) });
|
|
283
|
+
for (const candidate of candidates) {
|
|
284
|
+
if ('tool' in candidate) {
|
|
285
|
+
if (budget <= 0) continue;
|
|
286
|
+
plan.tool = candidate.tool;
|
|
287
|
+
budget--;
|
|
193
288
|
continue;
|
|
194
289
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
290
|
+
if (plan.messages.has(candidate.message)) continue;
|
|
291
|
+
if (!marked.has(candidate.message)) {
|
|
292
|
+
if (budget <= 0) continue;
|
|
293
|
+
budget--;
|
|
294
|
+
}
|
|
295
|
+
plan.messages.add(candidate.message);
|
|
296
|
+
if (candidate.prefix) plan.prefixMessages.add(candidate.message);
|
|
200
297
|
}
|
|
298
|
+
return plan;
|
|
299
|
+
}
|
|
201
300
|
|
|
202
|
-
|
|
301
|
+
export function applySmartAiPromptCaching(
|
|
302
|
+
prompt: plugins.LanguageModelV4Prompt,
|
|
303
|
+
options: ISmartAiCacheOptions = {},
|
|
304
|
+
): plugins.LanguageModelV4Prompt {
|
|
305
|
+
return applySmartAiRequestCaching({ prompt }, options).prompt;
|
|
306
|
+
}
|
|
203
307
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
308
|
+
/**
|
|
309
|
+
* Places cache markers on a request: its prompt and, for Anthropic, its tool definitions. See
|
|
310
|
+
* `planCacheMarkers` for where they go.
|
|
311
|
+
*/
|
|
312
|
+
export function applySmartAiRequestCaching<
|
|
313
|
+
TRequest extends { prompt: plugins.LanguageModelV4Prompt; tools?: TSmartAiCacheableTool[] },
|
|
314
|
+
>(request: TRequest, options: ISmartAiCacheOptions = {}): TRequest {
|
|
315
|
+
const provider = options.provider ?? 'anthropic';
|
|
316
|
+
const plan = planCacheMarkers(request.prompt, request.tools, provider);
|
|
317
|
+
// A one-hour breakpoint must precede five-minute ones: the prefix never lives shorter than the
|
|
318
|
+
// rest, and a caller's own markers, whose lifetimes and places are its own, keep the prefix at the
|
|
319
|
+
// request's retention.
|
|
320
|
+
const prefixOptions: ISmartAiCacheOptions = options.retention === '1h'
|
|
321
|
+
|| ((options.prefixRetention ?? '1h') === '1h' && plan.callerMarkers === 0)
|
|
322
|
+
? { ...options, retention: '1h' }
|
|
323
|
+
: options;
|
|
324
|
+
const prompt = plan.messages.size === 0
|
|
325
|
+
? request.prompt
|
|
326
|
+
: request.prompt.map((message, index) => plan.messages.has(index)
|
|
327
|
+
? applyCacheToMessage(message, provider, plan.prefixMessages.has(index) ? prefixOptions : options)
|
|
328
|
+
: message,
|
|
329
|
+
) as plugins.LanguageModelV4Prompt;
|
|
330
|
+
const tools = plan.tool === undefined || !request.tools
|
|
331
|
+
? request.tools
|
|
332
|
+
: request.tools.map((definition, index) => index === plan.tool
|
|
333
|
+
? applyProviderOptionsDefaults(
|
|
334
|
+
definition as TSmartAiCacheableTool & { providerOptions?: TSmartAiProviderOptions },
|
|
335
|
+
getSmartAiMessageCacheProviderOptions(provider, prefixOptions),
|
|
336
|
+
)
|
|
337
|
+
: definition);
|
|
338
|
+
return { ...request, prompt, ...(tools ? { tools } : {}) };
|
|
207
339
|
}
|
|
208
340
|
|
|
209
341
|
export function createSmartAiCachingMiddleware(options: ISmartAiCacheOptions = {}): plugins.LanguageModelV4Middleware {
|
|
210
342
|
return {
|
|
211
343
|
specificationVersion: 'v4',
|
|
212
|
-
transformParams: async ({ params }) => (
|
|
213
|
-
...params,
|
|
214
|
-
prompt: applySmartAiPromptCaching(params.prompt, options),
|
|
215
|
-
}),
|
|
344
|
+
transformParams: async ({ params }) => applySmartAiRequestCaching(params, options),
|
|
216
345
|
};
|
|
217
346
|
}
|
|
218
347
|
|
|
348
|
+
/**
|
|
349
|
+
* OpenAI routes a request to the machine that holds its cached prefix by a hash of the prefix's
|
|
350
|
+
* first tokens and the `prompt_cache_key`, and a key is meant to carry about 15 requests a minute
|
|
351
|
+
* across all prefixes using it before traffic overflows to other machines
|
|
352
|
+
* (https://developers.openai.com/api/docs/guides/prompt-caching, "Prompt cache keys" and
|
|
353
|
+
* "Cache location", read 2026-10-07). `keyBuckets` is sized by it.
|
|
354
|
+
*/
|
|
355
|
+
export const OPENAI_PROMPT_CACHE_KEY_REQUESTS_PER_MINUTE = 15;
|
|
356
|
+
|
|
357
|
+
/** A tool definition as a request sends it, for deriving a prompt cache key. */
|
|
358
|
+
export interface ISmartAiPromptCacheTool {
|
|
359
|
+
type?: string;
|
|
360
|
+
name: string;
|
|
361
|
+
description?: string;
|
|
362
|
+
inputSchema?: unknown;
|
|
363
|
+
strict?: boolean;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const hash64 = (text: string): string => {
|
|
367
|
+
// Two 32-bit FNV-1a lanes: a routing key needs a stable digest, not a cryptographic one.
|
|
368
|
+
let low = 0x811c9dc5;
|
|
369
|
+
let high = 0xcbf29ce4;
|
|
370
|
+
for (let index = 0; index < text.length; index++) {
|
|
371
|
+
const code = text.charCodeAt(index);
|
|
372
|
+
low = Math.imul(low ^ code, 0x01000193) >>> 0;
|
|
373
|
+
high = Math.imul(high ^ code ^ (low >>> 7), 0x01000193) >>> 0;
|
|
374
|
+
}
|
|
375
|
+
return high.toString(16).padStart(8, '0') + low.toString(16).padStart(8, '0');
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* A `prompt_cache_key` for the prefix every turn of a request repeats: its instructions and tool
|
|
380
|
+
* definitions, in the order they are sent, and the host's scope. Requests with the same prefix get
|
|
381
|
+
* the same key, so their sessions reach the machine holding it; any change to the prefix changes
|
|
382
|
+
* the key. A scope of letters, digits, `_` and `-` up to 40 characters stays readable in the key,
|
|
383
|
+
* any other is hashed. With `buckets`, sessions spread over that many keys, each keeping one, so a
|
|
384
|
+
* session's growing conversation stays with the machine that caches it.
|
|
385
|
+
*/
|
|
386
|
+
export function deriveSmartAiPromptCacheKey(input: {
|
|
387
|
+
system?: string;
|
|
388
|
+
tools?: readonly ISmartAiPromptCacheTool[];
|
|
389
|
+
scope?: string;
|
|
390
|
+
/** With `buckets` above 1: the session, which keeps its bucket for every request it sends. */
|
|
391
|
+
affinity?: string;
|
|
392
|
+
buckets?: number;
|
|
393
|
+
}): string {
|
|
394
|
+
const prefix = hash64(JSON.stringify([
|
|
395
|
+
input.system ?? '',
|
|
396
|
+
(input.tools ?? []).map((definition) => [
|
|
397
|
+
definition.type ?? 'function',
|
|
398
|
+
definition.name,
|
|
399
|
+
definition.description ?? '',
|
|
400
|
+
definition.inputSchema ?? null,
|
|
401
|
+
definition.strict ?? null,
|
|
402
|
+
]),
|
|
403
|
+
]));
|
|
404
|
+
const buckets = input.buckets ?? 1;
|
|
405
|
+
if (!Number.isSafeInteger(buckets) || buckets < 1) throw new Error('keyBuckets must be a positive integer.');
|
|
406
|
+
const bucket = buckets > 1 && input.affinity !== undefined
|
|
407
|
+
? Number.parseInt(hash64(input.affinity).slice(8), 16) % buckets
|
|
408
|
+
: 0;
|
|
409
|
+
const key = bucket === 0 ? prefix : `${prefix}.${bucket}`;
|
|
410
|
+
if (input.scope === undefined || input.scope === '') return key;
|
|
411
|
+
const scope = /^[A-Za-z0-9_-]{1,40}$/.test(input.scope) ? input.scope : `s${hash64(input.scope)}`;
|
|
412
|
+
return `${scope}.${key}`;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Whether an OpenAI model routes its prompt cache by itself: from GPT-5.6 on the key only separates
|
|
417
|
+
* cache accounting (https://developers.openai.com/api/docs/guides/prompt-caching, "Summary of model
|
|
418
|
+
* differences", read 2026-10-07). Earlier and unrecognized models are routed by the key.
|
|
419
|
+
*/
|
|
420
|
+
export function openAiRoutesPromptCacheAutomatically(modelId: string | undefined): boolean {
|
|
421
|
+
const match = /(?:^|[^a-z0-9])gpt-(\d+)(?:\.(\d+))?/i.exec(modelId ?? '');
|
|
422
|
+
if (!match) return false;
|
|
423
|
+
const major = Number(match[1]);
|
|
424
|
+
const minor = Number(match[2] ?? 0);
|
|
425
|
+
return major > 5 || (major === 5 && minor >= 6);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
|
|
219
429
|
function isOpenAiProvider(provider?: string): boolean {
|
|
220
430
|
const providerLower = provider?.toLowerCase() ?? '';
|
|
221
431
|
return providerLower === 'openai' || providerLower.startsWith('openai.') || providerLower.includes('@ai-sdk/openai');
|
|
@@ -225,22 +435,26 @@ export function getSmartAiCacheProviderOptions(input: {
|
|
|
225
435
|
provider?: string;
|
|
226
436
|
modelId?: string;
|
|
227
437
|
cache?: TSmartAiCacheSetting;
|
|
438
|
+
/** The derived `prompt_cache_key` (`deriveSmartAiPromptCacheKey`); a configured `key` takes precedence. */
|
|
439
|
+
promptCacheKey?: string;
|
|
440
|
+
/** The key used when neither a configured nor a derived key is given. */
|
|
228
441
|
sessionId?: string;
|
|
229
442
|
}): TSmartAiProviderOptions | undefined {
|
|
230
443
|
const cacheOptions = cacheOptionsFromSetting(input.cache);
|
|
231
444
|
if (!cacheOptions) return undefined;
|
|
232
445
|
|
|
233
446
|
if (isOpenAiProvider(input.provider)) {
|
|
234
|
-
const key = cacheOptions.key ?? input.sessionId;
|
|
447
|
+
const key = cacheOptions.key ?? input.promptCacheKey ?? input.sessionId;
|
|
448
|
+
// GPT-5.6 and later take `prompt_cache_options.ttl` instead, whose only value is its default.
|
|
449
|
+
const retention = (cacheOptions.retention === '24h' || cacheOptions.retention === 'in_memory')
|
|
450
|
+
&& !openAiRoutesPromptCacheAutomatically(input.modelId)
|
|
451
|
+
? cacheOptions.retention
|
|
452
|
+
: undefined;
|
|
235
453
|
return {
|
|
236
454
|
openai: {
|
|
237
455
|
store: false,
|
|
238
456
|
...(key ? { promptCacheKey: key } : {}),
|
|
239
|
-
...(
|
|
240
|
-
? { promptCacheRetention: cacheOptions.retention }
|
|
241
|
-
: key
|
|
242
|
-
? { promptCacheRetention: 'in_memory' }
|
|
243
|
-
: {}),
|
|
457
|
+
...(retention ? { promptCacheRetention: retention } : {}),
|
|
244
458
|
},
|
|
245
459
|
};
|
|
246
460
|
}
|
|
@@ -253,6 +467,7 @@ export function applySmartAiCacheProviderOptions(input: {
|
|
|
253
467
|
modelId?: string;
|
|
254
468
|
providerOptions?: TSmartAiProviderOptions;
|
|
255
469
|
cache?: TSmartAiCacheSetting;
|
|
470
|
+
promptCacheKey?: string;
|
|
256
471
|
sessionId?: string;
|
|
257
472
|
}): TSmartAiProviderOptions | undefined {
|
|
258
473
|
return mergeSmartAiProviderOptions(
|