@jaypie/llm 1.3.14 → 1.3.15
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/cjs/index.cjs +192 -17
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +20 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/operate/adapters/BedrockAdapter.d.ts +6 -0
- package/dist/cjs/operate/adapters/OpenAiAdapter.d.ts +5 -0
- package/dist/cjs/operate/adapters/OpenRouterAdapter.d.ts +11 -0
- package/dist/cjs/operate/types.d.ts +6 -1
- package/dist/cjs/providers/anthropic/types.d.ts +14 -1
- package/dist/cjs/types/LlmProvider.interface.d.ts +19 -0
- package/dist/cjs/util/cacheControl.d.ts +21 -0
- package/dist/cjs/util/index.d.ts +1 -0
- package/dist/esm/index.d.ts +20 -1
- package/dist/esm/index.js +192 -17
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/operate/adapters/BedrockAdapter.d.ts +6 -0
- package/dist/esm/operate/adapters/OpenAiAdapter.d.ts +5 -0
- package/dist/esm/operate/adapters/OpenRouterAdapter.d.ts +11 -0
- package/dist/esm/operate/types.d.ts +6 -1
- package/dist/esm/providers/anthropic/types.d.ts +14 -1
- package/dist/esm/types/LlmProvider.interface.d.ts +19 -0
- package/dist/esm/util/cacheControl.d.ts +21 -0
- package/dist/esm/util/index.d.ts +1 -0
- package/package.json +1 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -501,6 +501,12 @@ function sumUsageByProviderModel(usage) {
|
|
|
501
501
|
totals[key].output += item.output;
|
|
502
502
|
totals[key].reasoning += item.reasoning;
|
|
503
503
|
totals[key].total += item.total;
|
|
504
|
+
if (item.cacheRead !== undefined) {
|
|
505
|
+
totals[key].cacheRead = (totals[key].cacheRead ?? 0) + item.cacheRead;
|
|
506
|
+
}
|
|
507
|
+
if (item.cacheWrite !== undefined) {
|
|
508
|
+
totals[key].cacheWrite = (totals[key].cacheWrite ?? 0) + item.cacheWrite;
|
|
509
|
+
}
|
|
504
510
|
}
|
|
505
511
|
return totals;
|
|
506
512
|
}
|
|
@@ -526,6 +532,7 @@ function buildExchangeEnvelope({ duration, initialHistoryLength, input, options,
|
|
|
526
532
|
return {
|
|
527
533
|
ids: extractResponseIds(response.responses),
|
|
528
534
|
request: {
|
|
535
|
+
cache: options.cache,
|
|
529
536
|
data: options.data,
|
|
530
537
|
effort: options.effort,
|
|
531
538
|
explain: options.explain,
|
|
@@ -563,6 +570,38 @@ function buildExchangeEnvelope({ duration, initialHistoryLength, input, options,
|
|
|
563
570
|
};
|
|
564
571
|
}
|
|
565
572
|
|
|
573
|
+
const CACHE_TTL_DEFAULT = "5m";
|
|
574
|
+
/**
|
|
575
|
+
* Normalize the caller-facing `cache` option into a concrete decision.
|
|
576
|
+
* - `undefined` / `true` → enabled at the default TTL
|
|
577
|
+
* - `false` / `0` → disabled
|
|
578
|
+
* - `"5m"` / `"1h"` → enabled at that TTL
|
|
579
|
+
*/
|
|
580
|
+
function resolveCache(cache) {
|
|
581
|
+
if (cache === false || cache === 0) {
|
|
582
|
+
return { enabled: false, ttl: CACHE_TTL_DEFAULT };
|
|
583
|
+
}
|
|
584
|
+
if (cache === "5m" || cache === "1h") {
|
|
585
|
+
return { enabled: true, ttl: cache };
|
|
586
|
+
}
|
|
587
|
+
// undefined or true
|
|
588
|
+
return { enabled: true, ttl: CACHE_TTL_DEFAULT };
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Deterministic short key for providers with automatic, prefix-based caching
|
|
592
|
+
* (e.g. OpenAI `prompt_cache_key`). Derived from the stable prefix so the same
|
|
593
|
+
* system prompt + tools + model always routes to the same cache. Dependency-
|
|
594
|
+
* free FNV-1a; not cryptographic.
|
|
595
|
+
*/
|
|
596
|
+
function promptCacheKey(seed) {
|
|
597
|
+
let hash = 0x811c9dc5;
|
|
598
|
+
for (let i = 0; i < seed.length; i += 1) {
|
|
599
|
+
hash ^= seed.charCodeAt(i);
|
|
600
|
+
hash = Math.imul(hash, 0x01000193);
|
|
601
|
+
}
|
|
602
|
+
return `jaypie-${(hash >>> 0).toString(16)}`;
|
|
603
|
+
}
|
|
604
|
+
|
|
566
605
|
/**
|
|
567
606
|
* Type guard to check if an item is a dedicated reasoning item (OpenAI)
|
|
568
607
|
*/
|
|
@@ -1414,6 +1453,14 @@ function tallyOperate({ toolCallNames = [], turns, usage = [], }) {
|
|
|
1414
1453
|
usageByModel[key].output += item.output;
|
|
1415
1454
|
usageByModel[key].reasoning += item.reasoning;
|
|
1416
1455
|
usageByModel[key].total += item.total;
|
|
1456
|
+
if (item.cacheRead !== undefined) {
|
|
1457
|
+
usageByModel[key].cacheRead =
|
|
1458
|
+
(usageByModel[key].cacheRead ?? 0) + item.cacheRead;
|
|
1459
|
+
}
|
|
1460
|
+
if (item.cacheWrite !== undefined) {
|
|
1461
|
+
usageByModel[key].cacheWrite =
|
|
1462
|
+
(usageByModel[key].cacheWrite ?? 0) + item.cacheWrite;
|
|
1463
|
+
}
|
|
1417
1464
|
}
|
|
1418
1465
|
llm.usage = usageByModel;
|
|
1419
1466
|
}
|
|
@@ -1520,27 +1567,26 @@ const MODULE$1 = {
|
|
|
1520
1567
|
//
|
|
1521
1568
|
// Helpers
|
|
1522
1569
|
//
|
|
1523
|
-
//
|
|
1524
|
-
//
|
|
1525
|
-
//
|
|
1526
|
-
//
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
: module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
|
|
1570
|
+
// Native dynamic import that neither rollup nor tsc rewrites to require(), so
|
|
1571
|
+
// a CJS-bundled build still loads @jaypie/dynamodb's ESM entry and shares the
|
|
1572
|
+
// host's initialized module instance. A require()-based resolution would load
|
|
1573
|
+
// the CJS build, whose module-level client state is separate from the ESM
|
|
1574
|
+
// instance an ESM host initializes (dual-package hazard, issue #429).
|
|
1575
|
+
const dynamicImport = new Function("s", "return import(s)");
|
|
1530
1576
|
let resolved$1 = false;
|
|
1531
1577
|
let cachedSdk$2 = null;
|
|
1532
1578
|
/**
|
|
1533
1579
|
* Lazily resolve @jaypie/dynamodb's storeExchange. Returns null (and never
|
|
1534
1580
|
* throws) when the peer is absent. Cached after the first attempt.
|
|
1535
1581
|
*/
|
|
1536
|
-
function resolveExchangeStore() {
|
|
1582
|
+
async function resolveExchangeStore() {
|
|
1537
1583
|
if (resolved$1) {
|
|
1538
1584
|
return cachedSdk$2;
|
|
1539
1585
|
}
|
|
1540
1586
|
resolved$1 = true;
|
|
1541
1587
|
try {
|
|
1542
|
-
const dynamodb =
|
|
1543
|
-
const sdk = dynamodb?.default ?? dynamodb;
|
|
1588
|
+
const dynamodb = await dynamicImport(MODULE$1.JAYPIE_DYNAMODB);
|
|
1589
|
+
const sdk = (dynamodb?.default ?? dynamodb);
|
|
1544
1590
|
if (sdk && typeof sdk.storeExchange === "function") {
|
|
1545
1591
|
cachedSdk$2 = sdk;
|
|
1546
1592
|
}
|
|
@@ -1563,7 +1609,7 @@ async function persistExchange(envelope) {
|
|
|
1563
1609
|
if (!isExchangeStoreEnabled()) {
|
|
1564
1610
|
return;
|
|
1565
1611
|
}
|
|
1566
|
-
const sdk = resolveExchangeStore();
|
|
1612
|
+
const sdk = (await resolveExchangeStore());
|
|
1567
1613
|
if (!sdk) {
|
|
1568
1614
|
return;
|
|
1569
1615
|
}
|
|
@@ -2351,8 +2397,19 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
2351
2397
|
PROVIDER.ANTHROPIC.MAX_TOKENS.DEFAULT,
|
|
2352
2398
|
stream: false,
|
|
2353
2399
|
};
|
|
2400
|
+
const cache = resolveCache(request.cache);
|
|
2401
|
+
const cacheControl = cache.enabled
|
|
2402
|
+
? {
|
|
2403
|
+
type: "ephemeral",
|
|
2404
|
+
...(cache.ttl === "1h" ? { ttl: "1h" } : {}),
|
|
2405
|
+
}
|
|
2406
|
+
: undefined;
|
|
2354
2407
|
if (request.system) {
|
|
2355
|
-
|
|
2408
|
+
// A cache breakpoint on the system block caches tools+system together
|
|
2409
|
+
// (render order is tools -> system -> messages).
|
|
2410
|
+
anthropicRequest.system = cacheControl
|
|
2411
|
+
? [{ type: "text", text: request.system, cache_control: cacheControl }]
|
|
2412
|
+
: request.system;
|
|
2356
2413
|
}
|
|
2357
2414
|
const useFallbackStructuredOutput = Boolean(request.format) &&
|
|
2358
2415
|
!this.supportsStructuredOutput(anthropicRequest.model);
|
|
@@ -2369,7 +2426,7 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
2369
2426
|
});
|
|
2370
2427
|
}
|
|
2371
2428
|
if (allTools.length > 0) {
|
|
2372
|
-
anthropicRequest.tools = allTools.map((tool) => ({
|
|
2429
|
+
anthropicRequest.tools = allTools.map((tool, index) => ({
|
|
2373
2430
|
name: tool.name,
|
|
2374
2431
|
description: tool.description,
|
|
2375
2432
|
input_schema: {
|
|
@@ -2377,6 +2434,11 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
2377
2434
|
type: "object",
|
|
2378
2435
|
},
|
|
2379
2436
|
type: "custom",
|
|
2437
|
+
// Breakpoint on the last tool caches the tool list (which renders
|
|
2438
|
+
// before system) when there is no system prompt to anchor it.
|
|
2439
|
+
...(cacheControl && index === allTools.length - 1
|
|
2440
|
+
? { cache_control: cacheControl }
|
|
2441
|
+
: {}),
|
|
2380
2442
|
}));
|
|
2381
2443
|
anthropicRequest.tool_choice = useFallbackStructuredOutput
|
|
2382
2444
|
? { type: "any" }
|
|
@@ -2679,6 +2741,12 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
2679
2741
|
output: usage.output_tokens,
|
|
2680
2742
|
reasoning: usage.thinking_tokens || 0,
|
|
2681
2743
|
total: usage.input_tokens + usage.output_tokens,
|
|
2744
|
+
...(usage.cache_read_input_tokens !== undefined
|
|
2745
|
+
? { cacheRead: usage.cache_read_input_tokens }
|
|
2746
|
+
: {}),
|
|
2747
|
+
...(usage.cache_creation_input_tokens !== undefined
|
|
2748
|
+
? { cacheWrite: usage.cache_creation_input_tokens }
|
|
2749
|
+
: {}),
|
|
2682
2750
|
provider: this.name,
|
|
2683
2751
|
model,
|
|
2684
2752
|
};
|
|
@@ -2931,6 +2999,14 @@ function isTemperatureDeprecationError$3(error) {
|
|
|
2931
2999
|
const msg = error?.message ?? "";
|
|
2932
3000
|
return /temperature.*deprecated|deprecated.*temperature/i.test(msg);
|
|
2933
3001
|
}
|
|
3002
|
+
function isCachePointUnsupportedError(error) {
|
|
3003
|
+
const name = error?.constructor?.name ?? "";
|
|
3004
|
+
const msg = error?.message ?? "";
|
|
3005
|
+
// A model that cannot cache rejects the cachePoint block with a
|
|
3006
|
+
// ValidationException naming caching / cachePoint.
|
|
3007
|
+
return (/ValidationException|invalid|not support/i.test(name + " " + msg) &&
|
|
3008
|
+
/cachePoint|cache_point|prompt caching|caching/i.test(msg));
|
|
3009
|
+
}
|
|
2934
3010
|
function extractJson(text) {
|
|
2935
3011
|
// Try direct parse first
|
|
2936
3012
|
try {
|
|
@@ -2966,6 +3042,13 @@ class BedrockAdapter extends BaseProviderAdapter {
|
|
|
2966
3042
|
this.defaultModel = PROVIDER.BEDROCK.DEFAULT;
|
|
2967
3043
|
this._modelsFallbackToStructuredOutputTool = new Set();
|
|
2968
3044
|
this._modelsWithoutTemperature = new Set();
|
|
3045
|
+
this._modelsWithoutCachePoint = new Set();
|
|
3046
|
+
}
|
|
3047
|
+
rememberModelRejectsCachePoint(model) {
|
|
3048
|
+
this._modelsWithoutCachePoint.add(model);
|
|
3049
|
+
}
|
|
3050
|
+
supportsCachePoint(model) {
|
|
3051
|
+
return !this._modelsWithoutCachePoint.has(model);
|
|
2969
3052
|
}
|
|
2970
3053
|
rememberModelRejectsOutputConfig(model) {
|
|
2971
3054
|
this._modelsFallbackToStructuredOutputTool.add(model);
|
|
@@ -3102,11 +3185,51 @@ class BedrockAdapter extends BaseProviderAdapter {
|
|
|
3102
3185
|
};
|
|
3103
3186
|
}
|
|
3104
3187
|
}
|
|
3188
|
+
// Prompt caching via Converse cachePoint blocks (5-min default TTL; the
|
|
3189
|
+
// `ttl` option does not apply). Gated per model — cachePoint 400s on
|
|
3190
|
+
// unsupported models, which executeRequest catches and denylists.
|
|
3191
|
+
if (resolveCache(request.cache).enabled && this.supportsCachePoint(model)) {
|
|
3192
|
+
const cachePoint = { cachePoint: { type: "default" } };
|
|
3193
|
+
if (bedrockRequest.system && bedrockRequest.system.length > 0) {
|
|
3194
|
+
bedrockRequest.system = [
|
|
3195
|
+
...bedrockRequest.system,
|
|
3196
|
+
cachePoint,
|
|
3197
|
+
];
|
|
3198
|
+
}
|
|
3199
|
+
if (bedrockRequest.toolConfig?.tools?.length) {
|
|
3200
|
+
bedrockRequest.toolConfig = {
|
|
3201
|
+
...bedrockRequest.toolConfig,
|
|
3202
|
+
tools: [
|
|
3203
|
+
...bedrockRequest.toolConfig.tools,
|
|
3204
|
+
cachePoint,
|
|
3205
|
+
],
|
|
3206
|
+
};
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3105
3209
|
if (request.providerOptions) {
|
|
3106
3210
|
Object.assign(bedrockRequest, request.providerOptions);
|
|
3107
3211
|
}
|
|
3108
3212
|
return bedrockRequest;
|
|
3109
3213
|
}
|
|
3214
|
+
/** Remove any cachePoint blocks so the request can be retried unsupported. */
|
|
3215
|
+
stripCachePoints(request) {
|
|
3216
|
+
const stripped = { ...request };
|
|
3217
|
+
if (stripped.system) {
|
|
3218
|
+
stripped.system = stripped.system.filter((block) => !("cachePoint" in block));
|
|
3219
|
+
}
|
|
3220
|
+
if (stripped.toolConfig?.tools) {
|
|
3221
|
+
stripped.toolConfig = {
|
|
3222
|
+
...stripped.toolConfig,
|
|
3223
|
+
tools: stripped.toolConfig.tools.filter((tool) => !("cachePoint" in tool)),
|
|
3224
|
+
};
|
|
3225
|
+
}
|
|
3226
|
+
return stripped;
|
|
3227
|
+
}
|
|
3228
|
+
requestHasCachePoints(request) {
|
|
3229
|
+
const inSystem = (request.system ?? []).some((block) => "cachePoint" in block);
|
|
3230
|
+
const inTools = (request.toolConfig?.tools ?? []).some((tool) => "cachePoint" in tool);
|
|
3231
|
+
return inSystem || inTools;
|
|
3232
|
+
}
|
|
3110
3233
|
formatTools(toolkit) {
|
|
3111
3234
|
return toolkit.tools.map((tool) => ({
|
|
3112
3235
|
name: tool.name,
|
|
@@ -3161,6 +3284,15 @@ class BedrockAdapter extends BaseProviderAdapter {
|
|
|
3161
3284
|
const fallbackRequest = this.toFallbackStructuredOutputRequest(bedrockRequest);
|
|
3162
3285
|
return (await bedrockClient.send(new ConverseCommand(fallbackRequest), signal ? { abortSignal: signal } : undefined));
|
|
3163
3286
|
}
|
|
3287
|
+
if (this.requestHasCachePoints(bedrockRequest) &&
|
|
3288
|
+
isCachePointUnsupportedError(error)) {
|
|
3289
|
+
this.rememberModelRejectsCachePoint(bedrockRequest.modelId || this.defaultModel);
|
|
3290
|
+
const retryRequest = this.stripCachePoints(bedrockRequest);
|
|
3291
|
+
const response = (await bedrockClient.send(new ConverseCommand(retryRequest), signal ? { abortSignal: signal } : undefined));
|
|
3292
|
+
if (wantsStructuredOutput)
|
|
3293
|
+
response.__jaypieStructuredOutput = true;
|
|
3294
|
+
return response;
|
|
3295
|
+
}
|
|
3164
3296
|
throw error;
|
|
3165
3297
|
}
|
|
3166
3298
|
}
|
|
@@ -3304,6 +3436,12 @@ class BedrockAdapter extends BaseProviderAdapter {
|
|
|
3304
3436
|
reasoning: 0,
|
|
3305
3437
|
total: usage?.totalTokens ??
|
|
3306
3438
|
(usage?.inputTokens ?? 0) + (usage?.outputTokens ?? 0),
|
|
3439
|
+
...(usage?.cacheReadInputTokens !== undefined
|
|
3440
|
+
? { cacheRead: usage.cacheReadInputTokens }
|
|
3441
|
+
: {}),
|
|
3442
|
+
...(usage?.cacheWriteInputTokens !== undefined
|
|
3443
|
+
? { cacheWrite: usage.cacheWriteInputTokens }
|
|
3444
|
+
: {}),
|
|
3307
3445
|
provider: this.name,
|
|
3308
3446
|
model,
|
|
3309
3447
|
};
|
|
@@ -5475,6 +5613,13 @@ class OpenAiAdapter extends BaseProviderAdapter {
|
|
|
5475
5613
|
supportsReasoningEffort(model) {
|
|
5476
5614
|
return isReasoningModel(model);
|
|
5477
5615
|
}
|
|
5616
|
+
/**
|
|
5617
|
+
* Whether to emit `prompt_cache_key` (OpenAI Responses API). Overridable by
|
|
5618
|
+
* OpenAI-compatible subclasses whose backend rejects the field.
|
|
5619
|
+
*/
|
|
5620
|
+
supportsPromptCacheKey() {
|
|
5621
|
+
return true;
|
|
5622
|
+
}
|
|
5478
5623
|
/** Translate a normalized effort to this provider's `reasoning.effort` value. */
|
|
5479
5624
|
mapReasoningEffort(effort, model) {
|
|
5480
5625
|
return toOpenAiEffort(effort, { model });
|
|
@@ -5514,6 +5659,18 @@ class OpenAiAdapter extends BaseProviderAdapter {
|
|
|
5514
5659
|
summary: "auto",
|
|
5515
5660
|
};
|
|
5516
5661
|
}
|
|
5662
|
+
// OpenAI prompt caching is automatic (prefix-based). Setting a stable
|
|
5663
|
+
// prompt_cache_key routes repeat traffic to the same cache across
|
|
5664
|
+
// instances. Keyed on the stable prefix only (system + instructions +
|
|
5665
|
+
// tools + model), never the volatile user turn.
|
|
5666
|
+
if (this.supportsPromptCacheKey() && resolveCache(request.cache).enabled) {
|
|
5667
|
+
openaiRequest.prompt_cache_key = promptCacheKey(JSON.stringify([
|
|
5668
|
+
model,
|
|
5669
|
+
request.system ?? "",
|
|
5670
|
+
request.instructions ?? "",
|
|
5671
|
+
request.tools ?? [],
|
|
5672
|
+
]));
|
|
5673
|
+
}
|
|
5517
5674
|
if (request.providerOptions) {
|
|
5518
5675
|
Object.assign(openaiRequest, request.providerOptions);
|
|
5519
5676
|
}
|
|
@@ -5772,11 +5929,13 @@ class OpenAiAdapter extends BaseProviderAdapter {
|
|
|
5772
5929
|
model,
|
|
5773
5930
|
};
|
|
5774
5931
|
}
|
|
5932
|
+
const cachedTokens = openaiResponse.usage.input_tokens_details?.cached_tokens;
|
|
5775
5933
|
return {
|
|
5776
5934
|
input: openaiResponse.usage.input_tokens || 0,
|
|
5777
5935
|
output: openaiResponse.usage.output_tokens || 0,
|
|
5778
5936
|
reasoning: openaiResponse.usage.output_tokens_details?.reasoning_tokens || 0,
|
|
5779
5937
|
total: openaiResponse.usage.total_tokens || 0,
|
|
5938
|
+
...(cachedTokens !== undefined ? { cacheRead: cachedTokens } : {}),
|
|
5780
5939
|
provider: this.name,
|
|
5781
5940
|
model,
|
|
5782
5941
|
};
|
|
@@ -6168,7 +6327,13 @@ class OpenRouterAdapter extends BaseProviderAdapter {
|
|
|
6168
6327
|
//
|
|
6169
6328
|
buildRequest(request) {
|
|
6170
6329
|
// Convert messages to OpenRouter format (OpenAI-compatible)
|
|
6171
|
-
const
|
|
6330
|
+
const cache = resolveCache(request.cache);
|
|
6331
|
+
const messages = this.convertMessagesToOpenRouter(request.messages, request.system, cache.enabled
|
|
6332
|
+
? {
|
|
6333
|
+
type: "ephemeral",
|
|
6334
|
+
...(cache.ttl === "1h" ? { ttl: "1h" } : {}),
|
|
6335
|
+
}
|
|
6336
|
+
: undefined);
|
|
6172
6337
|
// Append instructions to last message if provided
|
|
6173
6338
|
if (request.instructions && messages.length > 0) {
|
|
6174
6339
|
const lastMsg = messages[messages.length - 1];
|
|
@@ -6532,11 +6697,15 @@ class OpenRouterAdapter extends BaseProviderAdapter {
|
|
|
6532
6697
|
}
|
|
6533
6698
|
// SDK returns camelCase, but support snake_case as fallback
|
|
6534
6699
|
const usage = openRouterResponse.usage;
|
|
6700
|
+
const cachedTokens = usage.promptTokensDetails?.cachedTokens ??
|
|
6701
|
+
usage.promptTokensDetails?.cached_tokens ??
|
|
6702
|
+
usage.prompt_tokens_details?.cached_tokens;
|
|
6535
6703
|
return {
|
|
6536
6704
|
input: usage.promptTokens || usage.prompt_tokens || 0,
|
|
6537
6705
|
output: usage.completionTokens || usage.completion_tokens || 0,
|
|
6538
6706
|
reasoning: usage.completionTokensDetails?.reasoningTokens || 0,
|
|
6539
6707
|
total: usage.totalTokens || usage.total_tokens || 0,
|
|
6708
|
+
...(cachedTokens !== undefined ? { cacheRead: cachedTokens } : {}),
|
|
6540
6709
|
provider: this.name,
|
|
6541
6710
|
model,
|
|
6542
6711
|
};
|
|
@@ -6737,13 +6906,17 @@ class OpenRouterAdapter extends BaseProviderAdapter {
|
|
|
6737
6906
|
const choice = response.choices[0];
|
|
6738
6907
|
return choice?.message?.content ?? undefined;
|
|
6739
6908
|
}
|
|
6740
|
-
convertMessagesToOpenRouter(messages, system) {
|
|
6909
|
+
convertMessagesToOpenRouter(messages, system, cacheControl) {
|
|
6741
6910
|
const openRouterMessages = [];
|
|
6742
|
-
// Add system message if provided
|
|
6911
|
+
// Add system message if provided. A cache_control breakpoint on the system
|
|
6912
|
+
// content is forwarded by OpenRouter to Anthropic/Gemini backends (and
|
|
6913
|
+
// ignored by others), caching the stable system prefix.
|
|
6743
6914
|
if (system) {
|
|
6744
6915
|
openRouterMessages.push({
|
|
6745
6916
|
role: "system",
|
|
6746
|
-
content:
|
|
6917
|
+
content: cacheControl
|
|
6918
|
+
? [{ type: "text", text: system, cache_control: cacheControl }]
|
|
6919
|
+
: system,
|
|
6747
6920
|
});
|
|
6748
6921
|
}
|
|
6749
6922
|
for (const msg of messages) {
|
|
@@ -8455,6 +8628,7 @@ class OperateLoop {
|
|
|
8455
8628
|
}
|
|
8456
8629
|
buildInitialRequest(state, options) {
|
|
8457
8630
|
return {
|
|
8631
|
+
cache: options.cache,
|
|
8458
8632
|
effort: options.effort,
|
|
8459
8633
|
format: state.formattedFormat,
|
|
8460
8634
|
instructions: options.instructions,
|
|
@@ -9088,6 +9262,7 @@ class StreamLoop {
|
|
|
9088
9262
|
}
|
|
9089
9263
|
buildInitialRequest(state, options) {
|
|
9090
9264
|
return {
|
|
9265
|
+
cache: options.cache,
|
|
9091
9266
|
effort: options.effort,
|
|
9092
9267
|
format: state.formattedFormat,
|
|
9093
9268
|
instructions: options.instructions,
|