@jaypie/llm 1.3.14 → 1.3.16
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/constants.d.ts +56 -1
- package/dist/cjs/index.cjs +369 -19
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +80 -4
- package/dist/cjs/index.d.ts +2 -2
- package/dist/cjs/operate/adapters/BedrockAdapter.d.ts +8 -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/constants.d.ts +56 -1
- package/dist/esm/index.d.ts +80 -4
- package/dist/esm/index.js +369 -19
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/operate/adapters/BedrockAdapter.d.ts +8 -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/esm/index.js
CHANGED
|
@@ -43,8 +43,8 @@ const MODEL = {
|
|
|
43
43
|
QWEN: "accounts/fireworks/models/qwen3p7-plus",
|
|
44
44
|
},
|
|
45
45
|
// Google
|
|
46
|
-
GEMINI_FLASH: "gemini-3.
|
|
47
|
-
GEMINI_FLASH_LITE: "gemini-3.
|
|
46
|
+
GEMINI_FLASH: "gemini-3.6-flash",
|
|
47
|
+
GEMINI_FLASH_LITE: "gemini-3.5-flash-lite",
|
|
48
48
|
GEMINI_PRO: "gemini-3.1-pro-preview",
|
|
49
49
|
// OpenAI
|
|
50
50
|
SOL: "gpt-5.6-sol",
|
|
@@ -65,6 +65,177 @@ const MODEL = {
|
|
|
65
65
|
SONNET: "anthropic/claude-sonnet-5",
|
|
66
66
|
},
|
|
67
67
|
};
|
|
68
|
+
/**
|
|
69
|
+
* Standard list price per million tokens, keyed by literal model id (verified
|
|
70
|
+
* 2026-07-21). Keys are string literals rather than `MODEL.*` references so a
|
|
71
|
+
* model retired from the catalog keeps its price here: historic ids stay
|
|
72
|
+
* priceable after they leave `MODEL.*`, which is what makes replaying old
|
|
73
|
+
* usage records possible.
|
|
74
|
+
*
|
|
75
|
+
* Caveats the numbers cannot carry:
|
|
76
|
+
* - **Standard rate only.** Introductory, batch, flex, priority, fast-mode, and
|
|
77
|
+
* data-residency rates are excluded. Sonnet 5 is listed at its standard
|
|
78
|
+
* $3/$15, not the introductory rate.
|
|
79
|
+
* - **Cache writes are Anthropic-only.** Fireworks writes bill at the input
|
|
80
|
+
* rate. OpenAI and xAI discount reads automatically and publish no write
|
|
81
|
+
* premium. Google charges nothing to write an implicit cache; explicit
|
|
82
|
+
* caching bills storage per hour, a unit this table does not carry (and one
|
|
83
|
+
* `@jaypie/llm` does not wire).
|
|
84
|
+
* - **Short-context tier.** Long-prompt surcharges are not modeled: Gemini 3.1
|
|
85
|
+
* Pro doubles above 200K, Grok doubles at 200K, GPT-5.5 is 2x in / 1.5x out
|
|
86
|
+
* above 272K.
|
|
87
|
+
* - **Text rates.** Gemini prices audio input higher than text/image/video.
|
|
88
|
+
* - **Proxies are deliberately absent.** Bedrock (`PROVIDER.BEDROCK.*`) and
|
|
89
|
+
* OpenRouter (`MODEL.OPENROUTER.*`) resell many vendors and price per backend
|
|
90
|
+
* route, so no single rate is correct for a proxy id. Price those against the
|
|
91
|
+
* backend model, or against the proxy's own published rate.
|
|
92
|
+
*
|
|
93
|
+
* Unlisted ids return `undefined` — callers must handle a miss.
|
|
94
|
+
*/
|
|
95
|
+
const COST = {
|
|
96
|
+
// Anthropic — https://platform.claude.com/docs/en/about-claude/pricing
|
|
97
|
+
"claude-3-5-haiku-20241022": {
|
|
98
|
+
cachedInputRead: 0.08,
|
|
99
|
+
cachedInputWrite: { "1h": 1.6, "5m": 1.0 },
|
|
100
|
+
input: 0.8,
|
|
101
|
+
output: 4.0,
|
|
102
|
+
},
|
|
103
|
+
"claude-fable-5": {
|
|
104
|
+
cachedInputRead: 1.0,
|
|
105
|
+
cachedInputWrite: { "1h": 20.0, "5m": 12.5 },
|
|
106
|
+
input: 10.0,
|
|
107
|
+
output: 50.0,
|
|
108
|
+
},
|
|
109
|
+
"claude-haiku-4-5": {
|
|
110
|
+
cachedInputRead: 0.1,
|
|
111
|
+
cachedInputWrite: { "1h": 2.0, "5m": 1.25 },
|
|
112
|
+
input: 1.0,
|
|
113
|
+
output: 5.0,
|
|
114
|
+
},
|
|
115
|
+
"claude-mythos-5": {
|
|
116
|
+
cachedInputRead: 1.0,
|
|
117
|
+
cachedInputWrite: { "1h": 20.0, "5m": 12.5 },
|
|
118
|
+
input: 10.0,
|
|
119
|
+
output: 50.0,
|
|
120
|
+
},
|
|
121
|
+
"claude-opus-4-1": {
|
|
122
|
+
cachedInputRead: 1.5,
|
|
123
|
+
cachedInputWrite: { "1h": 30.0, "5m": 18.75 },
|
|
124
|
+
input: 15.0,
|
|
125
|
+
output: 75.0,
|
|
126
|
+
},
|
|
127
|
+
"claude-opus-4-5": {
|
|
128
|
+
cachedInputRead: 0.5,
|
|
129
|
+
cachedInputWrite: { "1h": 10.0, "5m": 6.25 },
|
|
130
|
+
input: 5.0,
|
|
131
|
+
output: 25.0,
|
|
132
|
+
},
|
|
133
|
+
"claude-opus-4-6": {
|
|
134
|
+
cachedInputRead: 0.5,
|
|
135
|
+
cachedInputWrite: { "1h": 10.0, "5m": 6.25 },
|
|
136
|
+
input: 5.0,
|
|
137
|
+
output: 25.0,
|
|
138
|
+
},
|
|
139
|
+
"claude-opus-4-7": {
|
|
140
|
+
cachedInputRead: 0.5,
|
|
141
|
+
cachedInputWrite: { "1h": 10.0, "5m": 6.25 },
|
|
142
|
+
input: 5.0,
|
|
143
|
+
output: 25.0,
|
|
144
|
+
},
|
|
145
|
+
"claude-opus-4-8": {
|
|
146
|
+
cachedInputRead: 0.5,
|
|
147
|
+
cachedInputWrite: { "1h": 10.0, "5m": 6.25 },
|
|
148
|
+
input: 5.0,
|
|
149
|
+
output: 25.0,
|
|
150
|
+
},
|
|
151
|
+
"claude-sonnet-4-20250514": {
|
|
152
|
+
cachedInputRead: 0.3,
|
|
153
|
+
cachedInputWrite: { "1h": 6.0, "5m": 3.75 },
|
|
154
|
+
input: 3.0,
|
|
155
|
+
output: 15.0,
|
|
156
|
+
},
|
|
157
|
+
"claude-sonnet-4-5": {
|
|
158
|
+
cachedInputRead: 0.3,
|
|
159
|
+
cachedInputWrite: { "1h": 6.0, "5m": 3.75 },
|
|
160
|
+
input: 3.0,
|
|
161
|
+
output: 15.0,
|
|
162
|
+
},
|
|
163
|
+
"claude-sonnet-4-6": {
|
|
164
|
+
cachedInputRead: 0.3,
|
|
165
|
+
cachedInputWrite: { "1h": 6.0, "5m": 3.75 },
|
|
166
|
+
input: 3.0,
|
|
167
|
+
output: 15.0,
|
|
168
|
+
},
|
|
169
|
+
"claude-sonnet-5": {
|
|
170
|
+
cachedInputRead: 0.3,
|
|
171
|
+
cachedInputWrite: { "1h": 6.0, "5m": 3.75 },
|
|
172
|
+
input: 3.0,
|
|
173
|
+
output: 15.0,
|
|
174
|
+
},
|
|
175
|
+
// Fireworks — https://docs.fireworks.ai/serverless/pricing
|
|
176
|
+
"accounts/fireworks/models/deepseek-v4-pro": {
|
|
177
|
+
cachedInputRead: 0.145,
|
|
178
|
+
input: 1.74,
|
|
179
|
+
output: 3.48,
|
|
180
|
+
},
|
|
181
|
+
"accounts/fireworks/models/glm-5p2": {
|
|
182
|
+
cachedInputRead: 0.14,
|
|
183
|
+
input: 1.4,
|
|
184
|
+
output: 4.4,
|
|
185
|
+
},
|
|
186
|
+
"accounts/fireworks/models/kimi-k2p7-code": {
|
|
187
|
+
cachedInputRead: 0.19,
|
|
188
|
+
input: 0.95,
|
|
189
|
+
output: 4.0,
|
|
190
|
+
},
|
|
191
|
+
"accounts/fireworks/models/minimax-m2p7": {
|
|
192
|
+
cachedInputRead: 0.06,
|
|
193
|
+
input: 0.3,
|
|
194
|
+
output: 1.2,
|
|
195
|
+
},
|
|
196
|
+
"accounts/fireworks/models/qwen3p7-plus": {
|
|
197
|
+
cachedInputRead: 0.08,
|
|
198
|
+
input: 0.4,
|
|
199
|
+
output: 1.6,
|
|
200
|
+
},
|
|
201
|
+
// Google — https://ai.google.dev/gemini-api/docs/pricing
|
|
202
|
+
"gemini-2.5-flash": { cachedInputRead: 0.03, input: 0.3, output: 2.5 },
|
|
203
|
+
"gemini-3.1-flash-lite": {
|
|
204
|
+
cachedInputRead: 0.025,
|
|
205
|
+
input: 0.25,
|
|
206
|
+
output: 1.5,
|
|
207
|
+
},
|
|
208
|
+
"gemini-3.1-flash-lite-preview": {
|
|
209
|
+
cachedInputRead: 0.025,
|
|
210
|
+
input: 0.25,
|
|
211
|
+
output: 1.5,
|
|
212
|
+
},
|
|
213
|
+
"gemini-3.1-pro-preview": { cachedInputRead: 0.2, input: 2.0, output: 12.0 },
|
|
214
|
+
"gemini-3.5-flash": { cachedInputRead: 0.15, input: 1.5, output: 9.0 },
|
|
215
|
+
// Flash-Lite 3.5 has no separate cache-read rate on the standard tier
|
|
216
|
+
"gemini-3.5-flash-lite": { input: 0.3, output: 2.5 },
|
|
217
|
+
"gemini-3.6-flash": { cachedInputRead: 0.15, input: 1.5, output: 7.5 },
|
|
218
|
+
// OpenAI — https://developers.openai.com/api/docs/pricing
|
|
219
|
+
"gpt-5.4": { cachedInputRead: 0.25, input: 2.5, output: 15.0 },
|
|
220
|
+
"gpt-5.4-mini": { cachedInputRead: 0.075, input: 0.75, output: 4.5 },
|
|
221
|
+
"gpt-5.4-nano": { cachedInputRead: 0.02, input: 0.2, output: 1.25 },
|
|
222
|
+
"gpt-5.5": { cachedInputRead: 0.5, input: 5.0, output: 30.0 },
|
|
223
|
+
"gpt-5.6-luna": { cachedInputRead: 0.1, input: 1.0, output: 6.0 },
|
|
224
|
+
"gpt-5.6-sol": { cachedInputRead: 0.5, input: 5.0, output: 30.0 },
|
|
225
|
+
"gpt-5.6-terra": { cachedInputRead: 0.25, input: 2.5, output: 15.0 },
|
|
226
|
+
// xAI — https://docs.x.ai/docs/models ("grok-latest" aliases grok-4.3-latest)
|
|
227
|
+
"grok-4-1-fast-non-reasoning": {
|
|
228
|
+
cachedInputRead: 0.05,
|
|
229
|
+
input: 0.2,
|
|
230
|
+
output: 0.5,
|
|
231
|
+
},
|
|
232
|
+
"grok-4-1-fast-reasoning": {
|
|
233
|
+
cachedInputRead: 0.05,
|
|
234
|
+
input: 0.2,
|
|
235
|
+
output: 0.5,
|
|
236
|
+
},
|
|
237
|
+
"grok-latest": { cachedInputRead: 0.2, input: 1.25, output: 2.5 },
|
|
238
|
+
};
|
|
68
239
|
const GOOGLE_PROVIDER = {
|
|
69
240
|
// https://ai.google.dev/gemini-api/docs/models
|
|
70
241
|
DEFAULT: MODEL.GEMINI_FLASH,
|
|
@@ -270,6 +441,7 @@ const ALL = {
|
|
|
270
441
|
var constants = /*#__PURE__*/Object.freeze({
|
|
271
442
|
__proto__: null,
|
|
272
443
|
ALL: ALL,
|
|
444
|
+
COST: COST,
|
|
273
445
|
DEFAULT: DEFAULT,
|
|
274
446
|
EFFORT: EFFORT,
|
|
275
447
|
MODEL: MODEL,
|
|
@@ -498,6 +670,12 @@ function sumUsageByProviderModel(usage) {
|
|
|
498
670
|
totals[key].output += item.output;
|
|
499
671
|
totals[key].reasoning += item.reasoning;
|
|
500
672
|
totals[key].total += item.total;
|
|
673
|
+
if (item.cacheRead !== undefined) {
|
|
674
|
+
totals[key].cacheRead = (totals[key].cacheRead ?? 0) + item.cacheRead;
|
|
675
|
+
}
|
|
676
|
+
if (item.cacheWrite !== undefined) {
|
|
677
|
+
totals[key].cacheWrite = (totals[key].cacheWrite ?? 0) + item.cacheWrite;
|
|
678
|
+
}
|
|
501
679
|
}
|
|
502
680
|
return totals;
|
|
503
681
|
}
|
|
@@ -523,6 +701,7 @@ function buildExchangeEnvelope({ duration, initialHistoryLength, input, options,
|
|
|
523
701
|
return {
|
|
524
702
|
ids: extractResponseIds(response.responses),
|
|
525
703
|
request: {
|
|
704
|
+
cache: options.cache,
|
|
526
705
|
data: options.data,
|
|
527
706
|
effort: options.effort,
|
|
528
707
|
explain: options.explain,
|
|
@@ -560,6 +739,38 @@ function buildExchangeEnvelope({ duration, initialHistoryLength, input, options,
|
|
|
560
739
|
};
|
|
561
740
|
}
|
|
562
741
|
|
|
742
|
+
const CACHE_TTL_DEFAULT = "5m";
|
|
743
|
+
/**
|
|
744
|
+
* Normalize the caller-facing `cache` option into a concrete decision.
|
|
745
|
+
* - `undefined` / `true` → enabled at the default TTL
|
|
746
|
+
* - `false` / `0` → disabled
|
|
747
|
+
* - `"5m"` / `"1h"` → enabled at that TTL
|
|
748
|
+
*/
|
|
749
|
+
function resolveCache(cache) {
|
|
750
|
+
if (cache === false || cache === 0) {
|
|
751
|
+
return { enabled: false, ttl: CACHE_TTL_DEFAULT };
|
|
752
|
+
}
|
|
753
|
+
if (cache === "5m" || cache === "1h") {
|
|
754
|
+
return { enabled: true, ttl: cache };
|
|
755
|
+
}
|
|
756
|
+
// undefined or true
|
|
757
|
+
return { enabled: true, ttl: CACHE_TTL_DEFAULT };
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* Deterministic short key for providers with automatic, prefix-based caching
|
|
761
|
+
* (e.g. OpenAI `prompt_cache_key`). Derived from the stable prefix so the same
|
|
762
|
+
* system prompt + tools + model always routes to the same cache. Dependency-
|
|
763
|
+
* free FNV-1a; not cryptographic.
|
|
764
|
+
*/
|
|
765
|
+
function promptCacheKey(seed) {
|
|
766
|
+
let hash = 0x811c9dc5;
|
|
767
|
+
for (let i = 0; i < seed.length; i += 1) {
|
|
768
|
+
hash ^= seed.charCodeAt(i);
|
|
769
|
+
hash = Math.imul(hash, 0x01000193);
|
|
770
|
+
}
|
|
771
|
+
return `jaypie-${(hash >>> 0).toString(16)}`;
|
|
772
|
+
}
|
|
773
|
+
|
|
563
774
|
/**
|
|
564
775
|
* Type guard to check if an item is a dedicated reasoning item (OpenAI)
|
|
565
776
|
*/
|
|
@@ -1411,6 +1622,14 @@ function tallyOperate({ toolCallNames = [], turns, usage = [], }) {
|
|
|
1411
1622
|
usageByModel[key].output += item.output;
|
|
1412
1623
|
usageByModel[key].reasoning += item.reasoning;
|
|
1413
1624
|
usageByModel[key].total += item.total;
|
|
1625
|
+
if (item.cacheRead !== undefined) {
|
|
1626
|
+
usageByModel[key].cacheRead =
|
|
1627
|
+
(usageByModel[key].cacheRead ?? 0) + item.cacheRead;
|
|
1628
|
+
}
|
|
1629
|
+
if (item.cacheWrite !== undefined) {
|
|
1630
|
+
usageByModel[key].cacheWrite =
|
|
1631
|
+
(usageByModel[key].cacheWrite ?? 0) + item.cacheWrite;
|
|
1632
|
+
}
|
|
1414
1633
|
}
|
|
1415
1634
|
llm.usage = usageByModel;
|
|
1416
1635
|
}
|
|
@@ -1517,27 +1736,26 @@ const MODULE$1 = {
|
|
|
1517
1736
|
//
|
|
1518
1737
|
// Helpers
|
|
1519
1738
|
//
|
|
1520
|
-
//
|
|
1521
|
-
//
|
|
1522
|
-
//
|
|
1523
|
-
//
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
: createRequire(import.meta.url);
|
|
1739
|
+
// Native dynamic import that neither rollup nor tsc rewrites to require(), so
|
|
1740
|
+
// a CJS-bundled build still loads @jaypie/dynamodb's ESM entry and shares the
|
|
1741
|
+
// host's initialized module instance. A require()-based resolution would load
|
|
1742
|
+
// the CJS build, whose module-level client state is separate from the ESM
|
|
1743
|
+
// instance an ESM host initializes (dual-package hazard, issue #429).
|
|
1744
|
+
const dynamicImport = new Function("s", "return import(s)");
|
|
1527
1745
|
let resolved$1 = false;
|
|
1528
1746
|
let cachedSdk$2 = null;
|
|
1529
1747
|
/**
|
|
1530
1748
|
* Lazily resolve @jaypie/dynamodb's storeExchange. Returns null (and never
|
|
1531
1749
|
* throws) when the peer is absent. Cached after the first attempt.
|
|
1532
1750
|
*/
|
|
1533
|
-
function resolveExchangeStore() {
|
|
1751
|
+
async function resolveExchangeStore() {
|
|
1534
1752
|
if (resolved$1) {
|
|
1535
1753
|
return cachedSdk$2;
|
|
1536
1754
|
}
|
|
1537
1755
|
resolved$1 = true;
|
|
1538
1756
|
try {
|
|
1539
|
-
const dynamodb =
|
|
1540
|
-
const sdk = dynamodb?.default ?? dynamodb;
|
|
1757
|
+
const dynamodb = await dynamicImport(MODULE$1.JAYPIE_DYNAMODB);
|
|
1758
|
+
const sdk = (dynamodb?.default ?? dynamodb);
|
|
1541
1759
|
if (sdk && typeof sdk.storeExchange === "function") {
|
|
1542
1760
|
cachedSdk$2 = sdk;
|
|
1543
1761
|
}
|
|
@@ -1560,7 +1778,7 @@ async function persistExchange(envelope) {
|
|
|
1560
1778
|
if (!isExchangeStoreEnabled()) {
|
|
1561
1779
|
return;
|
|
1562
1780
|
}
|
|
1563
|
-
const sdk = resolveExchangeStore();
|
|
1781
|
+
const sdk = (await resolveExchangeStore());
|
|
1564
1782
|
if (!sdk) {
|
|
1565
1783
|
return;
|
|
1566
1784
|
}
|
|
@@ -2348,8 +2566,19 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
2348
2566
|
PROVIDER.ANTHROPIC.MAX_TOKENS.DEFAULT,
|
|
2349
2567
|
stream: false,
|
|
2350
2568
|
};
|
|
2569
|
+
const cache = resolveCache(request.cache);
|
|
2570
|
+
const cacheControl = cache.enabled
|
|
2571
|
+
? {
|
|
2572
|
+
type: "ephemeral",
|
|
2573
|
+
...(cache.ttl === "1h" ? { ttl: "1h" } : {}),
|
|
2574
|
+
}
|
|
2575
|
+
: undefined;
|
|
2351
2576
|
if (request.system) {
|
|
2352
|
-
|
|
2577
|
+
// A cache breakpoint on the system block caches tools+system together
|
|
2578
|
+
// (render order is tools -> system -> messages).
|
|
2579
|
+
anthropicRequest.system = cacheControl
|
|
2580
|
+
? [{ type: "text", text: request.system, cache_control: cacheControl }]
|
|
2581
|
+
: request.system;
|
|
2353
2582
|
}
|
|
2354
2583
|
const useFallbackStructuredOutput = Boolean(request.format) &&
|
|
2355
2584
|
!this.supportsStructuredOutput(anthropicRequest.model);
|
|
@@ -2366,7 +2595,7 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
2366
2595
|
});
|
|
2367
2596
|
}
|
|
2368
2597
|
if (allTools.length > 0) {
|
|
2369
|
-
anthropicRequest.tools = allTools.map((tool) => ({
|
|
2598
|
+
anthropicRequest.tools = allTools.map((tool, index) => ({
|
|
2370
2599
|
name: tool.name,
|
|
2371
2600
|
description: tool.description,
|
|
2372
2601
|
input_schema: {
|
|
@@ -2374,6 +2603,11 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
2374
2603
|
type: "object",
|
|
2375
2604
|
},
|
|
2376
2605
|
type: "custom",
|
|
2606
|
+
// Breakpoint on the last tool caches the tool list (which renders
|
|
2607
|
+
// before system) when there is no system prompt to anchor it.
|
|
2608
|
+
...(cacheControl && index === allTools.length - 1
|
|
2609
|
+
? { cache_control: cacheControl }
|
|
2610
|
+
: {}),
|
|
2377
2611
|
}));
|
|
2378
2612
|
anthropicRequest.tool_choice = useFallbackStructuredOutput
|
|
2379
2613
|
? { type: "any" }
|
|
@@ -2676,6 +2910,12 @@ class AnthropicAdapter extends BaseProviderAdapter {
|
|
|
2676
2910
|
output: usage.output_tokens,
|
|
2677
2911
|
reasoning: usage.thinking_tokens || 0,
|
|
2678
2912
|
total: usage.input_tokens + usage.output_tokens,
|
|
2913
|
+
...(usage.cache_read_input_tokens !== undefined
|
|
2914
|
+
? { cacheRead: usage.cache_read_input_tokens }
|
|
2915
|
+
: {}),
|
|
2916
|
+
...(usage.cache_creation_input_tokens !== undefined
|
|
2917
|
+
? { cacheWrite: usage.cache_creation_input_tokens }
|
|
2918
|
+
: {}),
|
|
2679
2919
|
provider: this.name,
|
|
2680
2920
|
model,
|
|
2681
2921
|
};
|
|
@@ -2928,6 +3168,17 @@ function isTemperatureDeprecationError$3(error) {
|
|
|
2928
3168
|
const msg = error?.message ?? "";
|
|
2929
3169
|
return /temperature.*deprecated|deprecated.*temperature/i.test(msg);
|
|
2930
3170
|
}
|
|
3171
|
+
/** Exported for tests; not part of the package's public surface. */
|
|
3172
|
+
function isCachePointUnsupportedError(error) {
|
|
3173
|
+
const name = error?.constructor?.name ?? "";
|
|
3174
|
+
const msg = error?.message ?? "";
|
|
3175
|
+
// A model that cannot cache rejects the cachePoint block with a
|
|
3176
|
+
// ValidationException naming caching / cachePoint. Bedrock words this as
|
|
3177
|
+
// "You invoked an unsupported model or your request did not allow prompt
|
|
3178
|
+
// caching" — "unsupported" is one word, so it must be matched separately
|
|
3179
|
+
// from "not support".
|
|
3180
|
+
return (/ValidationException|invalid|not support|unsupported/i.test(name + " " + msg) && /cachePoint|cache_point|prompt caching|caching/i.test(msg));
|
|
3181
|
+
}
|
|
2931
3182
|
function extractJson(text) {
|
|
2932
3183
|
// Try direct parse first
|
|
2933
3184
|
try {
|
|
@@ -2963,6 +3214,13 @@ class BedrockAdapter extends BaseProviderAdapter {
|
|
|
2963
3214
|
this.defaultModel = PROVIDER.BEDROCK.DEFAULT;
|
|
2964
3215
|
this._modelsFallbackToStructuredOutputTool = new Set();
|
|
2965
3216
|
this._modelsWithoutTemperature = new Set();
|
|
3217
|
+
this._modelsWithoutCachePoint = new Set();
|
|
3218
|
+
}
|
|
3219
|
+
rememberModelRejectsCachePoint(model) {
|
|
3220
|
+
this._modelsWithoutCachePoint.add(model);
|
|
3221
|
+
}
|
|
3222
|
+
supportsCachePoint(model) {
|
|
3223
|
+
return !this._modelsWithoutCachePoint.has(model);
|
|
2966
3224
|
}
|
|
2967
3225
|
rememberModelRejectsOutputConfig(model) {
|
|
2968
3226
|
this._modelsFallbackToStructuredOutputTool.add(model);
|
|
@@ -3099,11 +3357,51 @@ class BedrockAdapter extends BaseProviderAdapter {
|
|
|
3099
3357
|
};
|
|
3100
3358
|
}
|
|
3101
3359
|
}
|
|
3360
|
+
// Prompt caching via Converse cachePoint blocks (5-min default TTL; the
|
|
3361
|
+
// `ttl` option does not apply). Gated per model — cachePoint 400s on
|
|
3362
|
+
// unsupported models, which executeRequest catches and denylists.
|
|
3363
|
+
if (resolveCache(request.cache).enabled && this.supportsCachePoint(model)) {
|
|
3364
|
+
const cachePoint = { cachePoint: { type: "default" } };
|
|
3365
|
+
if (bedrockRequest.system && bedrockRequest.system.length > 0) {
|
|
3366
|
+
bedrockRequest.system = [
|
|
3367
|
+
...bedrockRequest.system,
|
|
3368
|
+
cachePoint,
|
|
3369
|
+
];
|
|
3370
|
+
}
|
|
3371
|
+
if (bedrockRequest.toolConfig?.tools?.length) {
|
|
3372
|
+
bedrockRequest.toolConfig = {
|
|
3373
|
+
...bedrockRequest.toolConfig,
|
|
3374
|
+
tools: [
|
|
3375
|
+
...bedrockRequest.toolConfig.tools,
|
|
3376
|
+
cachePoint,
|
|
3377
|
+
],
|
|
3378
|
+
};
|
|
3379
|
+
}
|
|
3380
|
+
}
|
|
3102
3381
|
if (request.providerOptions) {
|
|
3103
3382
|
Object.assign(bedrockRequest, request.providerOptions);
|
|
3104
3383
|
}
|
|
3105
3384
|
return bedrockRequest;
|
|
3106
3385
|
}
|
|
3386
|
+
/** Remove any cachePoint blocks so the request can be retried unsupported. */
|
|
3387
|
+
stripCachePoints(request) {
|
|
3388
|
+
const stripped = { ...request };
|
|
3389
|
+
if (stripped.system) {
|
|
3390
|
+
stripped.system = stripped.system.filter((block) => !("cachePoint" in block));
|
|
3391
|
+
}
|
|
3392
|
+
if (stripped.toolConfig?.tools) {
|
|
3393
|
+
stripped.toolConfig = {
|
|
3394
|
+
...stripped.toolConfig,
|
|
3395
|
+
tools: stripped.toolConfig.tools.filter((tool) => !("cachePoint" in tool)),
|
|
3396
|
+
};
|
|
3397
|
+
}
|
|
3398
|
+
return stripped;
|
|
3399
|
+
}
|
|
3400
|
+
requestHasCachePoints(request) {
|
|
3401
|
+
const inSystem = (request.system ?? []).some((block) => "cachePoint" in block);
|
|
3402
|
+
const inTools = (request.toolConfig?.tools ?? []).some((tool) => "cachePoint" in tool);
|
|
3403
|
+
return inSystem || inTools;
|
|
3404
|
+
}
|
|
3107
3405
|
formatTools(toolkit) {
|
|
3108
3406
|
return toolkit.tools.map((tool) => ({
|
|
3109
3407
|
name: tool.name,
|
|
@@ -3158,6 +3456,15 @@ class BedrockAdapter extends BaseProviderAdapter {
|
|
|
3158
3456
|
const fallbackRequest = this.toFallbackStructuredOutputRequest(bedrockRequest);
|
|
3159
3457
|
return (await bedrockClient.send(new ConverseCommand(fallbackRequest), signal ? { abortSignal: signal } : undefined));
|
|
3160
3458
|
}
|
|
3459
|
+
if (this.requestHasCachePoints(bedrockRequest) &&
|
|
3460
|
+
isCachePointUnsupportedError(error)) {
|
|
3461
|
+
this.rememberModelRejectsCachePoint(bedrockRequest.modelId || this.defaultModel);
|
|
3462
|
+
const retryRequest = this.stripCachePoints(bedrockRequest);
|
|
3463
|
+
const response = (await bedrockClient.send(new ConverseCommand(retryRequest), signal ? { abortSignal: signal } : undefined));
|
|
3464
|
+
if (wantsStructuredOutput)
|
|
3465
|
+
response.__jaypieStructuredOutput = true;
|
|
3466
|
+
return response;
|
|
3467
|
+
}
|
|
3161
3468
|
throw error;
|
|
3162
3469
|
}
|
|
3163
3470
|
}
|
|
@@ -3301,6 +3608,12 @@ class BedrockAdapter extends BaseProviderAdapter {
|
|
|
3301
3608
|
reasoning: 0,
|
|
3302
3609
|
total: usage?.totalTokens ??
|
|
3303
3610
|
(usage?.inputTokens ?? 0) + (usage?.outputTokens ?? 0),
|
|
3611
|
+
...(usage?.cacheReadInputTokens !== undefined
|
|
3612
|
+
? { cacheRead: usage.cacheReadInputTokens }
|
|
3613
|
+
: {}),
|
|
3614
|
+
...(usage?.cacheWriteInputTokens !== undefined
|
|
3615
|
+
? { cacheWrite: usage.cacheWriteInputTokens }
|
|
3616
|
+
: {}),
|
|
3304
3617
|
provider: this.name,
|
|
3305
3618
|
model,
|
|
3306
3619
|
};
|
|
@@ -5472,6 +5785,13 @@ class OpenAiAdapter extends BaseProviderAdapter {
|
|
|
5472
5785
|
supportsReasoningEffort(model) {
|
|
5473
5786
|
return isReasoningModel(model);
|
|
5474
5787
|
}
|
|
5788
|
+
/**
|
|
5789
|
+
* Whether to emit `prompt_cache_key` (OpenAI Responses API). Overridable by
|
|
5790
|
+
* OpenAI-compatible subclasses whose backend rejects the field.
|
|
5791
|
+
*/
|
|
5792
|
+
supportsPromptCacheKey() {
|
|
5793
|
+
return true;
|
|
5794
|
+
}
|
|
5475
5795
|
/** Translate a normalized effort to this provider's `reasoning.effort` value. */
|
|
5476
5796
|
mapReasoningEffort(effort, model) {
|
|
5477
5797
|
return toOpenAiEffort(effort, { model });
|
|
@@ -5511,6 +5831,18 @@ class OpenAiAdapter extends BaseProviderAdapter {
|
|
|
5511
5831
|
summary: "auto",
|
|
5512
5832
|
};
|
|
5513
5833
|
}
|
|
5834
|
+
// OpenAI prompt caching is automatic (prefix-based). Setting a stable
|
|
5835
|
+
// prompt_cache_key routes repeat traffic to the same cache across
|
|
5836
|
+
// instances. Keyed on the stable prefix only (system + instructions +
|
|
5837
|
+
// tools + model), never the volatile user turn.
|
|
5838
|
+
if (this.supportsPromptCacheKey() && resolveCache(request.cache).enabled) {
|
|
5839
|
+
openaiRequest.prompt_cache_key = promptCacheKey(JSON.stringify([
|
|
5840
|
+
model,
|
|
5841
|
+
request.system ?? "",
|
|
5842
|
+
request.instructions ?? "",
|
|
5843
|
+
request.tools ?? [],
|
|
5844
|
+
]));
|
|
5845
|
+
}
|
|
5514
5846
|
if (request.providerOptions) {
|
|
5515
5847
|
Object.assign(openaiRequest, request.providerOptions);
|
|
5516
5848
|
}
|
|
@@ -5769,11 +6101,13 @@ class OpenAiAdapter extends BaseProviderAdapter {
|
|
|
5769
6101
|
model,
|
|
5770
6102
|
};
|
|
5771
6103
|
}
|
|
6104
|
+
const cachedTokens = openaiResponse.usage.input_tokens_details?.cached_tokens;
|
|
5772
6105
|
return {
|
|
5773
6106
|
input: openaiResponse.usage.input_tokens || 0,
|
|
5774
6107
|
output: openaiResponse.usage.output_tokens || 0,
|
|
5775
6108
|
reasoning: openaiResponse.usage.output_tokens_details?.reasoning_tokens || 0,
|
|
5776
6109
|
total: openaiResponse.usage.total_tokens || 0,
|
|
6110
|
+
...(cachedTokens !== undefined ? { cacheRead: cachedTokens } : {}),
|
|
5777
6111
|
provider: this.name,
|
|
5778
6112
|
model,
|
|
5779
6113
|
};
|
|
@@ -6165,7 +6499,13 @@ class OpenRouterAdapter extends BaseProviderAdapter {
|
|
|
6165
6499
|
//
|
|
6166
6500
|
buildRequest(request) {
|
|
6167
6501
|
// Convert messages to OpenRouter format (OpenAI-compatible)
|
|
6168
|
-
const
|
|
6502
|
+
const cache = resolveCache(request.cache);
|
|
6503
|
+
const messages = this.convertMessagesToOpenRouter(request.messages, request.system, cache.enabled
|
|
6504
|
+
? {
|
|
6505
|
+
type: "ephemeral",
|
|
6506
|
+
...(cache.ttl === "1h" ? { ttl: "1h" } : {}),
|
|
6507
|
+
}
|
|
6508
|
+
: undefined);
|
|
6169
6509
|
// Append instructions to last message if provided
|
|
6170
6510
|
if (request.instructions && messages.length > 0) {
|
|
6171
6511
|
const lastMsg = messages[messages.length - 1];
|
|
@@ -6529,11 +6869,15 @@ class OpenRouterAdapter extends BaseProviderAdapter {
|
|
|
6529
6869
|
}
|
|
6530
6870
|
// SDK returns camelCase, but support snake_case as fallback
|
|
6531
6871
|
const usage = openRouterResponse.usage;
|
|
6872
|
+
const cachedTokens = usage.promptTokensDetails?.cachedTokens ??
|
|
6873
|
+
usage.promptTokensDetails?.cached_tokens ??
|
|
6874
|
+
usage.prompt_tokens_details?.cached_tokens;
|
|
6532
6875
|
return {
|
|
6533
6876
|
input: usage.promptTokens || usage.prompt_tokens || 0,
|
|
6534
6877
|
output: usage.completionTokens || usage.completion_tokens || 0,
|
|
6535
6878
|
reasoning: usage.completionTokensDetails?.reasoningTokens || 0,
|
|
6536
6879
|
total: usage.totalTokens || usage.total_tokens || 0,
|
|
6880
|
+
...(cachedTokens !== undefined ? { cacheRead: cachedTokens } : {}),
|
|
6537
6881
|
provider: this.name,
|
|
6538
6882
|
model,
|
|
6539
6883
|
};
|
|
@@ -6734,13 +7078,17 @@ class OpenRouterAdapter extends BaseProviderAdapter {
|
|
|
6734
7078
|
const choice = response.choices[0];
|
|
6735
7079
|
return choice?.message?.content ?? undefined;
|
|
6736
7080
|
}
|
|
6737
|
-
convertMessagesToOpenRouter(messages, system) {
|
|
7081
|
+
convertMessagesToOpenRouter(messages, system, cacheControl) {
|
|
6738
7082
|
const openRouterMessages = [];
|
|
6739
|
-
// Add system message if provided
|
|
7083
|
+
// Add system message if provided. A cache_control breakpoint on the system
|
|
7084
|
+
// content is forwarded by OpenRouter to Anthropic/Gemini backends (and
|
|
7085
|
+
// ignored by others), caching the stable system prefix.
|
|
6740
7086
|
if (system) {
|
|
6741
7087
|
openRouterMessages.push({
|
|
6742
7088
|
role: "system",
|
|
6743
|
-
content:
|
|
7089
|
+
content: cacheControl
|
|
7090
|
+
? [{ type: "text", text: system, cache_control: cacheControl }]
|
|
7091
|
+
: system,
|
|
6744
7092
|
});
|
|
6745
7093
|
}
|
|
6746
7094
|
for (const msg of messages) {
|
|
@@ -8452,6 +8800,7 @@ class OperateLoop {
|
|
|
8452
8800
|
}
|
|
8453
8801
|
buildInitialRequest(state, options) {
|
|
8454
8802
|
return {
|
|
8803
|
+
cache: options.cache,
|
|
8455
8804
|
effort: options.effort,
|
|
8456
8805
|
format: state.formattedFormat,
|
|
8457
8806
|
instructions: options.instructions,
|
|
@@ -9085,6 +9434,7 @@ class StreamLoop {
|
|
|
9085
9434
|
}
|
|
9086
9435
|
buildInitialRequest(state, options) {
|
|
9087
9436
|
return {
|
|
9437
|
+
cache: options.cache,
|
|
9088
9438
|
effort: options.effort,
|
|
9089
9439
|
format: state.formattedFormat,
|
|
9090
9440
|
instructions: options.instructions,
|