@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.
@@ -88,6 +88,9 @@ function assertValidRequest(req) {
88
88
  "PromptRequest.messages must not contain role: 'system'. Pass the system prompt via PromptRequest.system instead."
89
89
  );
90
90
  }
91
+ if (req.cacheKey !== void 0 && (typeof req.cacheKey !== "string" || req.cacheKey.length === 0)) {
92
+ throw new Error("PromptRequest.cacheKey must be a non-empty string");
93
+ }
91
94
  }
92
95
 
93
96
  // src/adapters/openai/_streaming.ts
@@ -112,6 +115,9 @@ async function* streamFromOpenAI(client, req, options) {
112
115
  body.tool_choice = "auto";
113
116
  }
114
117
  if (req.temperature !== void 0) body.temperature = req.temperature;
118
+ if (req.cacheKey && options.includeCacheKey !== false) {
119
+ body.prompt_cache_key = req.cacheKey;
120
+ }
115
121
  const effectiveMaxTokens = req.maxTokens ?? options.defaultMaxTokens;
116
122
  if (effectiveMaxTokens !== void 0) {
117
123
  const param = options.tokensParam ?? (needsMaxCompletionTokens(req.model) ? "max_completion_tokens" : "max_tokens");
@@ -165,10 +171,12 @@ async function* streamFromOpenAI(client, req, options) {
165
171
  throw new Error(`OpenAI-compatible stream returned an error: ${message}`);
166
172
  }
167
173
  if (chunk.usage) {
174
+ const cachedTokens = chunk.usage.prompt_tokens_details?.cached_tokens;
168
175
  usage = {
169
- ...chunk.usage.prompt_tokens !== void 0 ? { inputTokens: chunk.usage.prompt_tokens } : {},
176
+ ...chunk.usage.prompt_tokens !== void 0 ? { inputTokens: chunk.usage.prompt_tokens - (cachedTokens ?? 0) } : {},
170
177
  ...chunk.usage.completion_tokens !== void 0 ? { outputTokens: chunk.usage.completion_tokens } : {},
171
- ...chunk.usage.total_tokens !== void 0 ? { totalTokens: chunk.usage.total_tokens } : {}
178
+ ...chunk.usage.total_tokens !== void 0 ? { totalTokens: chunk.usage.total_tokens } : {},
179
+ ...cachedTokens !== void 0 ? { cacheReadTokens: cachedTokens } : {}
172
180
  };
173
181
  }
174
182
  const choice = chunk.choices?.[0];
@@ -1221,6 +1229,16 @@ var AnthropicAdapter = class {
1221
1229
  if (req.system) body.system = req.system;
1222
1230
  if (tools) body.tools = tools;
1223
1231
  if (req.temperature !== void 0) body.temperature = req.temperature;
1232
+ if (req.cacheKey) {
1233
+ if (req.system) {
1234
+ body.system = [{ type: "text", text: req.system, cache_control: { type: "ephemeral" } }];
1235
+ }
1236
+ if (tools) {
1237
+ const lastTool = tools[tools.length - 1];
1238
+ lastTool.cache_control = { type: "ephemeral" };
1239
+ }
1240
+ markLastMessageForCaching(messages);
1241
+ }
1224
1242
  let stream;
1225
1243
  let responseHeaders;
1226
1244
  try {
@@ -1248,6 +1266,8 @@ var AnthropicAdapter = class {
1248
1266
  let stopReason;
1249
1267
  let inputTokens;
1250
1268
  let outputTokens;
1269
+ let cacheReadTokens;
1270
+ let cacheWriteTokens;
1251
1271
  try {
1252
1272
  for await (const event of stream) {
1253
1273
  if (req.signal?.aborted) {
@@ -1257,6 +1277,8 @@ var AnthropicAdapter = class {
1257
1277
  switch (event.type) {
1258
1278
  case "message_start":
1259
1279
  inputTokens = event.message?.usage?.input_tokens;
1280
+ cacheReadTokens = event.message?.usage?.cache_read_input_tokens;
1281
+ cacheWriteTokens = event.message?.usage?.cache_creation_input_tokens;
1260
1282
  break;
1261
1283
  case "content_block_start": {
1262
1284
  const block = event.content_block;
@@ -1338,7 +1360,11 @@ var AnthropicAdapter = class {
1338
1360
  const usage = inputTokens !== void 0 || outputTokens !== void 0 ? {
1339
1361
  ...inputTokens !== void 0 ? { inputTokens } : {},
1340
1362
  ...outputTokens !== void 0 ? { outputTokens } : {},
1341
- ...inputTokens !== void 0 && outputTokens !== void 0 ? { totalTokens: inputTokens + outputTokens } : {}
1363
+ ...inputTokens !== void 0 && outputTokens !== void 0 ? {
1364
+ totalTokens: inputTokens + outputTokens + (cacheReadTokens ?? 0) + (cacheWriteTokens ?? 0)
1365
+ } : {},
1366
+ ...cacheReadTokens !== void 0 ? { cacheReadTokens } : {},
1367
+ ...cacheWriteTokens !== void 0 ? { cacheWriteTokens } : {}
1342
1368
  } : void 0;
1343
1369
  const endEvent = {
1344
1370
  type: "end",
@@ -1437,6 +1463,23 @@ function buildWireMessages2(req) {
1437
1463
  }
1438
1464
  return out;
1439
1465
  }
1466
+ function markLastMessageForCaching(messages) {
1467
+ if (messages.length === 0) return;
1468
+ const last = messages[messages.length - 1];
1469
+ const cacheControl = { cache_control: { type: "ephemeral" } };
1470
+ if (typeof last.content === "string" && last.content.length > 0) {
1471
+ messages[messages.length - 1] = {
1472
+ ...last,
1473
+ content: [{ type: "text", text: last.content, ...cacheControl }]
1474
+ };
1475
+ } else if (Array.isArray(last.content) && last.content.length > 0) {
1476
+ const blocks = last.content;
1477
+ messages[messages.length - 1] = {
1478
+ ...last,
1479
+ content: [...blocks.slice(0, -1), { ...blocks[blocks.length - 1], ...cacheControl }]
1480
+ };
1481
+ }
1482
+ }
1440
1483
  function formatTools2(tools) {
1441
1484
  return tools.map((t) => ({
1442
1485
  name: t.name,
@@ -1499,6 +1542,11 @@ async function* streamFromGemini(client, req) {
1499
1542
  if (req.temperature !== void 0) config.temperature = req.temperature;
1500
1543
  if (req.maxTokens !== void 0) config.maxOutputTokens = req.maxTokens;
1501
1544
  if (req.signal) config.abortSignal = req.signal;
1545
+ if (req.providerParams) {
1546
+ for (const [key, value] of Object.entries(req.providerParams)) {
1547
+ if (!(key in config)) config[key] = value;
1548
+ }
1549
+ }
1502
1550
  let stream;
1503
1551
  try {
1504
1552
  stream = await client.models.generateContentStream({
@@ -1519,6 +1567,7 @@ async function* streamFromGemini(client, req) {
1519
1567
  let inputTokens;
1520
1568
  let outputTokens;
1521
1569
  let totalTokens;
1570
+ let cacheReadTokens;
1522
1571
  try {
1523
1572
  for await (const chunk of stream) {
1524
1573
  if (req.signal?.aborted) {
@@ -1527,7 +1576,12 @@ async function* streamFromGemini(client, req) {
1527
1576
  }
1528
1577
  const usage2 = chunk.usageMetadata;
1529
1578
  if (usage2) {
1530
- if (usage2.promptTokenCount !== void 0) inputTokens = usage2.promptTokenCount;
1579
+ if (usage2.cachedContentTokenCount !== void 0) {
1580
+ cacheReadTokens = usage2.cachedContentTokenCount;
1581
+ }
1582
+ if (usage2.promptTokenCount !== void 0) {
1583
+ inputTokens = usage2.promptTokenCount - (usage2.cachedContentTokenCount ?? 0);
1584
+ }
1531
1585
  if (usage2.candidatesTokenCount !== void 0) outputTokens = usage2.candidatesTokenCount;
1532
1586
  if (usage2.totalTokenCount !== void 0) totalTokens = usage2.totalTokenCount;
1533
1587
  }
@@ -1581,7 +1635,8 @@ async function* streamFromGemini(client, req) {
1581
1635
  const usage = inputTokens !== void 0 || outputTokens !== void 0 || totalTokens !== void 0 ? {
1582
1636
  ...inputTokens !== void 0 ? { inputTokens } : {},
1583
1637
  ...outputTokens !== void 0 ? { outputTokens } : {},
1584
- ...totalTokens !== void 0 ? { totalTokens } : {}
1638
+ ...totalTokens !== void 0 ? { totalTokens } : {},
1639
+ ...cacheReadTokens !== void 0 ? { cacheReadTokens } : {}
1585
1640
  } : void 0;
1586
1641
  const endEvent = {
1587
1642
  type: "end",
@@ -1723,6 +1778,39 @@ function isAbortError3(err) {
1723
1778
 
1724
1779
  // src/adapters/google/manifest.ts
1725
1780
  var GOOGLE_KNOWN_MODELS = [
1781
+ {
1782
+ id: "gemini-3.6-flash",
1783
+ displayName: "Gemini 3.6 Flash",
1784
+ capabilities: {
1785
+ streaming: true,
1786
+ tools: true,
1787
+ vision: true,
1788
+ systemPrompt: true,
1789
+ maxContextTokens: 1e6
1790
+ }
1791
+ },
1792
+ {
1793
+ id: "gemini-3.5-flash",
1794
+ displayName: "Gemini 3.5 Flash",
1795
+ capabilities: {
1796
+ streaming: true,
1797
+ tools: true,
1798
+ vision: true,
1799
+ systemPrompt: true,
1800
+ maxContextTokens: 1e6
1801
+ }
1802
+ },
1803
+ {
1804
+ id: "gemini-3.5-flash-lite",
1805
+ displayName: "Gemini 3.5 Flash-Lite",
1806
+ capabilities: {
1807
+ streaming: true,
1808
+ tools: true,
1809
+ vision: true,
1810
+ systemPrompt: true,
1811
+ maxContextTokens: 1e6
1812
+ }
1813
+ },
1726
1814
  {
1727
1815
  id: "gemini-2.5-pro",
1728
1816
  displayName: "Gemini 2.5 Pro",
@@ -1866,6 +1954,39 @@ var import_genai2 = require("@google/genai");
1866
1954
 
1867
1955
  // src/adapters/vertex-gemini/manifest.ts
1868
1956
  var VERTEX_GEMINI_KNOWN_MODELS = [
1957
+ {
1958
+ id: "gemini-3.6-flash",
1959
+ displayName: "Gemini 3.6 Flash",
1960
+ capabilities: {
1961
+ streaming: true,
1962
+ tools: true,
1963
+ vision: true,
1964
+ systemPrompt: true,
1965
+ maxContextTokens: 1e6
1966
+ }
1967
+ },
1968
+ {
1969
+ id: "gemini-3.5-flash",
1970
+ displayName: "Gemini 3.5 Flash",
1971
+ capabilities: {
1972
+ streaming: true,
1973
+ tools: true,
1974
+ vision: true,
1975
+ systemPrompt: true,
1976
+ maxContextTokens: 1e6
1977
+ }
1978
+ },
1979
+ {
1980
+ id: "gemini-3.5-flash-lite",
1981
+ displayName: "Gemini 3.5 Flash-Lite",
1982
+ capabilities: {
1983
+ streaming: true,
1984
+ tools: true,
1985
+ vision: true,
1986
+ systemPrompt: true,
1987
+ maxContextTokens: 1e6
1988
+ }
1989
+ },
1869
1990
  {
1870
1991
  id: "gemini-2.5-pro",
1871
1992
  displayName: "Gemini 2.5 Pro",
@@ -2549,6 +2670,15 @@ var BedrockAdapter = class {
2549
2670
  if (Object.keys(inferenceConfig).length > 0) {
2550
2671
  commandInput.inferenceConfig = inferenceConfig;
2551
2672
  }
2673
+ if (req.cacheKey) {
2674
+ if (commandInput.system) {
2675
+ commandInput.system.push({ cachePoint: { type: "default" } });
2676
+ }
2677
+ if (tools) {
2678
+ tools.tools.push({ cachePoint: { type: "default" } });
2679
+ }
2680
+ markLastMessageForCaching2(messages);
2681
+ }
2552
2682
  const command = new import_client_bedrock_runtime.ConverseStreamCommand(
2553
2683
  commandInput
2554
2684
  );
@@ -2572,6 +2702,8 @@ var BedrockAdapter = class {
2572
2702
  let inputTokens;
2573
2703
  let outputTokens;
2574
2704
  let totalTokens;
2705
+ let cacheReadTokens;
2706
+ let cacheWriteTokens;
2575
2707
  try {
2576
2708
  for await (const event of response.stream) {
2577
2709
  if (req.signal?.aborted) {
@@ -2624,6 +2756,8 @@ var BedrockAdapter = class {
2624
2756
  inputTokens = event.metadata.usage.inputTokens;
2625
2757
  outputTokens = event.metadata.usage.outputTokens;
2626
2758
  totalTokens = event.metadata.usage.totalTokens;
2759
+ cacheReadTokens = event.metadata.usage.cacheReadInputTokens;
2760
+ cacheWriteTokens = event.metadata.usage.cacheWriteInputTokens;
2627
2761
  }
2628
2762
  }
2629
2763
  }
@@ -2650,7 +2784,9 @@ var BedrockAdapter = class {
2650
2784
  const usage = inputTokens !== void 0 || outputTokens !== void 0 || totalTokens !== void 0 ? {
2651
2785
  ...inputTokens !== void 0 ? { inputTokens } : {},
2652
2786
  ...outputTokens !== void 0 ? { outputTokens } : {},
2653
- ...totalTokens !== void 0 ? { totalTokens } : {}
2787
+ ...totalTokens !== void 0 ? { totalTokens } : {},
2788
+ ...cacheReadTokens !== void 0 ? { cacheReadTokens } : {},
2789
+ ...cacheWriteTokens !== void 0 ? { cacheWriteTokens } : {}
2654
2790
  } : void 0;
2655
2791
  const endEvent = {
2656
2792
  type: "end",
@@ -2758,6 +2894,16 @@ function buildWireMessages3(req) {
2758
2894
  }
2759
2895
  return out;
2760
2896
  }
2897
+ function markLastMessageForCaching2(messages) {
2898
+ if (messages.length === 0) return;
2899
+ const last = messages[messages.length - 1];
2900
+ if (Array.isArray(last.content) && last.content.length > 0) {
2901
+ messages[messages.length - 1] = {
2902
+ ...last,
2903
+ content: [...last.content, { cachePoint: { type: "default" } }]
2904
+ };
2905
+ }
2906
+ }
2761
2907
  function formatTools4(tools) {
2762
2908
  return tools.map((t) => ({
2763
2909
  toolSpec: {
@@ -3113,7 +3259,10 @@ var GroqAdapter = class extends OpenAIAdapter {
3113
3259
  stream(req) {
3114
3260
  return streamFromOpenAI(this.ensureClient(), req, {
3115
3261
  knownModels: groqManifest.knownModels,
3116
- vendorMetadataExtractor: GROQ_METADATA_EXTRACTOR
3262
+ vendorMetadataExtractor: GROQ_METADATA_EXTRACTOR,
3263
+ // Groq caches automatically server-side and rejects/ignores undocumented
3264
+ // OpenAI params — do not forward prompt_cache_key.
3265
+ includeCacheKey: false
3117
3266
  });
3118
3267
  }
3119
3268
  };