@jambonz/llm 0.5.7 → 0.6.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/index.cjs CHANGED
@@ -101,6 +101,9 @@ function assertValidRequest(req) {
101
101
  "PromptRequest.messages must not contain role: 'system'. Pass the system prompt via PromptRequest.system instead."
102
102
  );
103
103
  }
104
+ if (req.cacheKey !== void 0 && (typeof req.cacheKey !== "string" || req.cacheKey.length === 0)) {
105
+ throw new Error("PromptRequest.cacheKey must be a non-empty string");
106
+ }
104
107
  }
105
108
 
106
109
  // src/adapters/openai/_streaming.ts
@@ -125,6 +128,9 @@ async function* streamFromOpenAI(client, req, options) {
125
128
  body.tool_choice = "auto";
126
129
  }
127
130
  if (req.temperature !== void 0) body.temperature = req.temperature;
131
+ if (req.cacheKey && options.includeCacheKey !== false) {
132
+ body.prompt_cache_key = req.cacheKey;
133
+ }
128
134
  const effectiveMaxTokens = req.maxTokens ?? options.defaultMaxTokens;
129
135
  if (effectiveMaxTokens !== void 0) {
130
136
  const param = options.tokensParam ?? (needsMaxCompletionTokens(req.model) ? "max_completion_tokens" : "max_tokens");
@@ -178,10 +184,12 @@ async function* streamFromOpenAI(client, req, options) {
178
184
  throw new Error(`OpenAI-compatible stream returned an error: ${message}`);
179
185
  }
180
186
  if (chunk.usage) {
187
+ const cachedTokens = chunk.usage.prompt_tokens_details?.cached_tokens;
181
188
  usage = {
182
- ...chunk.usage.prompt_tokens !== void 0 ? { inputTokens: chunk.usage.prompt_tokens } : {},
189
+ ...chunk.usage.prompt_tokens !== void 0 ? { inputTokens: chunk.usage.prompt_tokens - (cachedTokens ?? 0) } : {},
183
190
  ...chunk.usage.completion_tokens !== void 0 ? { outputTokens: chunk.usage.completion_tokens } : {},
184
- ...chunk.usage.total_tokens !== void 0 ? { totalTokens: chunk.usage.total_tokens } : {}
191
+ ...chunk.usage.total_tokens !== void 0 ? { totalTokens: chunk.usage.total_tokens } : {},
192
+ ...cachedTokens !== void 0 ? { cacheReadTokens: cachedTokens } : {}
185
193
  };
186
194
  }
187
195
  const choice = chunk.choices?.[0];
@@ -1234,6 +1242,16 @@ var AnthropicAdapter = class {
1234
1242
  if (req.system) body.system = req.system;
1235
1243
  if (tools) body.tools = tools;
1236
1244
  if (req.temperature !== void 0) body.temperature = req.temperature;
1245
+ if (req.cacheKey) {
1246
+ if (req.system) {
1247
+ body.system = [{ type: "text", text: req.system, cache_control: { type: "ephemeral" } }];
1248
+ }
1249
+ if (tools) {
1250
+ const lastTool = tools[tools.length - 1];
1251
+ lastTool.cache_control = { type: "ephemeral" };
1252
+ }
1253
+ markLastMessageForCaching(messages);
1254
+ }
1237
1255
  let stream;
1238
1256
  let responseHeaders;
1239
1257
  try {
@@ -1261,6 +1279,8 @@ var AnthropicAdapter = class {
1261
1279
  let stopReason;
1262
1280
  let inputTokens;
1263
1281
  let outputTokens;
1282
+ let cacheReadTokens;
1283
+ let cacheWriteTokens;
1264
1284
  try {
1265
1285
  for await (const event of stream) {
1266
1286
  if (req.signal?.aborted) {
@@ -1270,6 +1290,8 @@ var AnthropicAdapter = class {
1270
1290
  switch (event.type) {
1271
1291
  case "message_start":
1272
1292
  inputTokens = event.message?.usage?.input_tokens;
1293
+ cacheReadTokens = event.message?.usage?.cache_read_input_tokens;
1294
+ cacheWriteTokens = event.message?.usage?.cache_creation_input_tokens;
1273
1295
  break;
1274
1296
  case "content_block_start": {
1275
1297
  const block = event.content_block;
@@ -1351,7 +1373,11 @@ var AnthropicAdapter = class {
1351
1373
  const usage = inputTokens !== void 0 || outputTokens !== void 0 ? {
1352
1374
  ...inputTokens !== void 0 ? { inputTokens } : {},
1353
1375
  ...outputTokens !== void 0 ? { outputTokens } : {},
1354
- ...inputTokens !== void 0 && outputTokens !== void 0 ? { totalTokens: inputTokens + outputTokens } : {}
1376
+ ...inputTokens !== void 0 && outputTokens !== void 0 ? {
1377
+ totalTokens: inputTokens + outputTokens + (cacheReadTokens ?? 0) + (cacheWriteTokens ?? 0)
1378
+ } : {},
1379
+ ...cacheReadTokens !== void 0 ? { cacheReadTokens } : {},
1380
+ ...cacheWriteTokens !== void 0 ? { cacheWriteTokens } : {}
1355
1381
  } : void 0;
1356
1382
  const endEvent = {
1357
1383
  type: "end",
@@ -1450,6 +1476,23 @@ function buildWireMessages2(req) {
1450
1476
  }
1451
1477
  return out;
1452
1478
  }
1479
+ function markLastMessageForCaching(messages) {
1480
+ if (messages.length === 0) return;
1481
+ const last = messages[messages.length - 1];
1482
+ const cacheControl = { cache_control: { type: "ephemeral" } };
1483
+ if (typeof last.content === "string" && last.content.length > 0) {
1484
+ messages[messages.length - 1] = {
1485
+ ...last,
1486
+ content: [{ type: "text", text: last.content, ...cacheControl }]
1487
+ };
1488
+ } else if (Array.isArray(last.content) && last.content.length > 0) {
1489
+ const blocks = last.content;
1490
+ messages[messages.length - 1] = {
1491
+ ...last,
1492
+ content: [...blocks.slice(0, -1), { ...blocks[blocks.length - 1], ...cacheControl }]
1493
+ };
1494
+ }
1495
+ }
1453
1496
  function formatTools2(tools) {
1454
1497
  return tools.map((t) => ({
1455
1498
  name: t.name,
@@ -1512,6 +1555,11 @@ async function* streamFromGemini(client, req) {
1512
1555
  if (req.temperature !== void 0) config.temperature = req.temperature;
1513
1556
  if (req.maxTokens !== void 0) config.maxOutputTokens = req.maxTokens;
1514
1557
  if (req.signal) config.abortSignal = req.signal;
1558
+ if (req.providerParams) {
1559
+ for (const [key, value] of Object.entries(req.providerParams)) {
1560
+ if (!(key in config)) config[key] = value;
1561
+ }
1562
+ }
1515
1563
  let stream;
1516
1564
  try {
1517
1565
  stream = await client.models.generateContentStream({
@@ -1532,6 +1580,7 @@ async function* streamFromGemini(client, req) {
1532
1580
  let inputTokens;
1533
1581
  let outputTokens;
1534
1582
  let totalTokens;
1583
+ let cacheReadTokens;
1535
1584
  try {
1536
1585
  for await (const chunk of stream) {
1537
1586
  if (req.signal?.aborted) {
@@ -1540,7 +1589,12 @@ async function* streamFromGemini(client, req) {
1540
1589
  }
1541
1590
  const usage2 = chunk.usageMetadata;
1542
1591
  if (usage2) {
1543
- if (usage2.promptTokenCount !== void 0) inputTokens = usage2.promptTokenCount;
1592
+ if (usage2.cachedContentTokenCount !== void 0) {
1593
+ cacheReadTokens = usage2.cachedContentTokenCount;
1594
+ }
1595
+ if (usage2.promptTokenCount !== void 0) {
1596
+ inputTokens = usage2.promptTokenCount - (usage2.cachedContentTokenCount ?? 0);
1597
+ }
1544
1598
  if (usage2.candidatesTokenCount !== void 0) outputTokens = usage2.candidatesTokenCount;
1545
1599
  if (usage2.totalTokenCount !== void 0) totalTokens = usage2.totalTokenCount;
1546
1600
  }
@@ -1594,7 +1648,8 @@ async function* streamFromGemini(client, req) {
1594
1648
  const usage = inputTokens !== void 0 || outputTokens !== void 0 || totalTokens !== void 0 ? {
1595
1649
  ...inputTokens !== void 0 ? { inputTokens } : {},
1596
1650
  ...outputTokens !== void 0 ? { outputTokens } : {},
1597
- ...totalTokens !== void 0 ? { totalTokens } : {}
1651
+ ...totalTokens !== void 0 ? { totalTokens } : {},
1652
+ ...cacheReadTokens !== void 0 ? { cacheReadTokens } : {}
1598
1653
  } : void 0;
1599
1654
  const endEvent = {
1600
1655
  type: "end",
@@ -1736,6 +1791,39 @@ function isAbortError3(err) {
1736
1791
 
1737
1792
  // src/adapters/google/manifest.ts
1738
1793
  var GOOGLE_KNOWN_MODELS = [
1794
+ {
1795
+ id: "gemini-3.6-flash",
1796
+ displayName: "Gemini 3.6 Flash",
1797
+ capabilities: {
1798
+ streaming: true,
1799
+ tools: true,
1800
+ vision: true,
1801
+ systemPrompt: true,
1802
+ maxContextTokens: 1e6
1803
+ }
1804
+ },
1805
+ {
1806
+ id: "gemini-3.5-flash",
1807
+ displayName: "Gemini 3.5 Flash",
1808
+ capabilities: {
1809
+ streaming: true,
1810
+ tools: true,
1811
+ vision: true,
1812
+ systemPrompt: true,
1813
+ maxContextTokens: 1e6
1814
+ }
1815
+ },
1816
+ {
1817
+ id: "gemini-3.5-flash-lite",
1818
+ displayName: "Gemini 3.5 Flash-Lite",
1819
+ capabilities: {
1820
+ streaming: true,
1821
+ tools: true,
1822
+ vision: true,
1823
+ systemPrompt: true,
1824
+ maxContextTokens: 1e6
1825
+ }
1826
+ },
1739
1827
  {
1740
1828
  id: "gemini-2.5-pro",
1741
1829
  displayName: "Gemini 2.5 Pro",
@@ -1879,6 +1967,39 @@ var import_genai2 = require("@google/genai");
1879
1967
 
1880
1968
  // src/adapters/vertex-gemini/manifest.ts
1881
1969
  var VERTEX_GEMINI_KNOWN_MODELS = [
1970
+ {
1971
+ id: "gemini-3.6-flash",
1972
+ displayName: "Gemini 3.6 Flash",
1973
+ capabilities: {
1974
+ streaming: true,
1975
+ tools: true,
1976
+ vision: true,
1977
+ systemPrompt: true,
1978
+ maxContextTokens: 1e6
1979
+ }
1980
+ },
1981
+ {
1982
+ id: "gemini-3.5-flash",
1983
+ displayName: "Gemini 3.5 Flash",
1984
+ capabilities: {
1985
+ streaming: true,
1986
+ tools: true,
1987
+ vision: true,
1988
+ systemPrompt: true,
1989
+ maxContextTokens: 1e6
1990
+ }
1991
+ },
1992
+ {
1993
+ id: "gemini-3.5-flash-lite",
1994
+ displayName: "Gemini 3.5 Flash-Lite",
1995
+ capabilities: {
1996
+ streaming: true,
1997
+ tools: true,
1998
+ vision: true,
1999
+ systemPrompt: true,
2000
+ maxContextTokens: 1e6
2001
+ }
2002
+ },
1882
2003
  {
1883
2004
  id: "gemini-2.5-pro",
1884
2005
  displayName: "Gemini 2.5 Pro",
@@ -2562,6 +2683,15 @@ var BedrockAdapter = class {
2562
2683
  if (Object.keys(inferenceConfig).length > 0) {
2563
2684
  commandInput.inferenceConfig = inferenceConfig;
2564
2685
  }
2686
+ if (req.cacheKey) {
2687
+ if (commandInput.system) {
2688
+ commandInput.system.push({ cachePoint: { type: "default" } });
2689
+ }
2690
+ if (tools) {
2691
+ tools.tools.push({ cachePoint: { type: "default" } });
2692
+ }
2693
+ markLastMessageForCaching2(messages);
2694
+ }
2565
2695
  const command = new import_client_bedrock_runtime.ConverseStreamCommand(
2566
2696
  commandInput
2567
2697
  );
@@ -2585,6 +2715,8 @@ var BedrockAdapter = class {
2585
2715
  let inputTokens;
2586
2716
  let outputTokens;
2587
2717
  let totalTokens;
2718
+ let cacheReadTokens;
2719
+ let cacheWriteTokens;
2588
2720
  try {
2589
2721
  for await (const event of response.stream) {
2590
2722
  if (req.signal?.aborted) {
@@ -2637,6 +2769,8 @@ var BedrockAdapter = class {
2637
2769
  inputTokens = event.metadata.usage.inputTokens;
2638
2770
  outputTokens = event.metadata.usage.outputTokens;
2639
2771
  totalTokens = event.metadata.usage.totalTokens;
2772
+ cacheReadTokens = event.metadata.usage.cacheReadInputTokens;
2773
+ cacheWriteTokens = event.metadata.usage.cacheWriteInputTokens;
2640
2774
  }
2641
2775
  }
2642
2776
  }
@@ -2663,7 +2797,9 @@ var BedrockAdapter = class {
2663
2797
  const usage = inputTokens !== void 0 || outputTokens !== void 0 || totalTokens !== void 0 ? {
2664
2798
  ...inputTokens !== void 0 ? { inputTokens } : {},
2665
2799
  ...outputTokens !== void 0 ? { outputTokens } : {},
2666
- ...totalTokens !== void 0 ? { totalTokens } : {}
2800
+ ...totalTokens !== void 0 ? { totalTokens } : {},
2801
+ ...cacheReadTokens !== void 0 ? { cacheReadTokens } : {},
2802
+ ...cacheWriteTokens !== void 0 ? { cacheWriteTokens } : {}
2667
2803
  } : void 0;
2668
2804
  const endEvent = {
2669
2805
  type: "end",
@@ -2771,6 +2907,16 @@ function buildWireMessages3(req) {
2771
2907
  }
2772
2908
  return out;
2773
2909
  }
2910
+ function markLastMessageForCaching2(messages) {
2911
+ if (messages.length === 0) return;
2912
+ const last = messages[messages.length - 1];
2913
+ if (Array.isArray(last.content) && last.content.length > 0) {
2914
+ messages[messages.length - 1] = {
2915
+ ...last,
2916
+ content: [...last.content, { cachePoint: { type: "default" } }]
2917
+ };
2918
+ }
2919
+ }
2774
2920
  function formatTools4(tools) {
2775
2921
  return tools.map((t) => ({
2776
2922
  toolSpec: {
@@ -3126,7 +3272,10 @@ var GroqAdapter = class extends OpenAIAdapter {
3126
3272
  stream(req) {
3127
3273
  return streamFromOpenAI(this.ensureClient(), req, {
3128
3274
  knownModels: groqManifest.knownModels,
3129
- vendorMetadataExtractor: GROQ_METADATA_EXTRACTOR
3275
+ vendorMetadataExtractor: GROQ_METADATA_EXTRACTOR,
3276
+ // Groq caches automatically server-side and rejects/ignores undocumented
3277
+ // OpenAI params — do not forward prompt_cache_key.
3278
+ includeCacheKey: false
3130
3279
  });
3131
3280
  }
3132
3281
  };