@jambonz/llm 0.5.6 → 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,11 +115,20 @@ 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");
118
124
  body[param] = effectiveMaxTokens;
119
125
  }
126
+ if (req.providerParams) {
127
+ const bodyRecord = body;
128
+ for (const [key, value] of Object.entries(req.providerParams)) {
129
+ if (!(key in bodyRecord)) bodyRecord[key] = value;
130
+ }
131
+ }
120
132
  const t0 = process.hrtime.bigint();
121
133
  let t1;
122
134
  let t2;
@@ -159,10 +171,12 @@ async function* streamFromOpenAI(client, req, options) {
159
171
  throw new Error(`OpenAI-compatible stream returned an error: ${message}`);
160
172
  }
161
173
  if (chunk.usage) {
174
+ const cachedTokens = chunk.usage.prompt_tokens_details?.cached_tokens;
162
175
  usage = {
163
- ...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) } : {},
164
177
  ...chunk.usage.completion_tokens !== void 0 ? { outputTokens: chunk.usage.completion_tokens } : {},
165
- ...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 } : {}
166
180
  };
167
181
  }
168
182
  const choice = chunk.choices?.[0];
@@ -1215,6 +1229,16 @@ var AnthropicAdapter = class {
1215
1229
  if (req.system) body.system = req.system;
1216
1230
  if (tools) body.tools = tools;
1217
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
+ }
1218
1242
  let stream;
1219
1243
  let responseHeaders;
1220
1244
  try {
@@ -1242,6 +1266,8 @@ var AnthropicAdapter = class {
1242
1266
  let stopReason;
1243
1267
  let inputTokens;
1244
1268
  let outputTokens;
1269
+ let cacheReadTokens;
1270
+ let cacheWriteTokens;
1245
1271
  try {
1246
1272
  for await (const event of stream) {
1247
1273
  if (req.signal?.aborted) {
@@ -1251,6 +1277,8 @@ var AnthropicAdapter = class {
1251
1277
  switch (event.type) {
1252
1278
  case "message_start":
1253
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;
1254
1282
  break;
1255
1283
  case "content_block_start": {
1256
1284
  const block = event.content_block;
@@ -1332,7 +1360,11 @@ var AnthropicAdapter = class {
1332
1360
  const usage = inputTokens !== void 0 || outputTokens !== void 0 ? {
1333
1361
  ...inputTokens !== void 0 ? { inputTokens } : {},
1334
1362
  ...outputTokens !== void 0 ? { outputTokens } : {},
1335
- ...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 } : {}
1336
1368
  } : void 0;
1337
1369
  const endEvent = {
1338
1370
  type: "end",
@@ -1431,6 +1463,23 @@ function buildWireMessages2(req) {
1431
1463
  }
1432
1464
  return out;
1433
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
+ }
1434
1483
  function formatTools2(tools) {
1435
1484
  return tools.map((t) => ({
1436
1485
  name: t.name,
@@ -1493,6 +1542,11 @@ async function* streamFromGemini(client, req) {
1493
1542
  if (req.temperature !== void 0) config.temperature = req.temperature;
1494
1543
  if (req.maxTokens !== void 0) config.maxOutputTokens = req.maxTokens;
1495
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
+ }
1496
1550
  let stream;
1497
1551
  try {
1498
1552
  stream = await client.models.generateContentStream({
@@ -1513,6 +1567,7 @@ async function* streamFromGemini(client, req) {
1513
1567
  let inputTokens;
1514
1568
  let outputTokens;
1515
1569
  let totalTokens;
1570
+ let cacheReadTokens;
1516
1571
  try {
1517
1572
  for await (const chunk of stream) {
1518
1573
  if (req.signal?.aborted) {
@@ -1521,7 +1576,12 @@ async function* streamFromGemini(client, req) {
1521
1576
  }
1522
1577
  const usage2 = chunk.usageMetadata;
1523
1578
  if (usage2) {
1524
- 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
+ }
1525
1585
  if (usage2.candidatesTokenCount !== void 0) outputTokens = usage2.candidatesTokenCount;
1526
1586
  if (usage2.totalTokenCount !== void 0) totalTokens = usage2.totalTokenCount;
1527
1587
  }
@@ -1575,7 +1635,8 @@ async function* streamFromGemini(client, req) {
1575
1635
  const usage = inputTokens !== void 0 || outputTokens !== void 0 || totalTokens !== void 0 ? {
1576
1636
  ...inputTokens !== void 0 ? { inputTokens } : {},
1577
1637
  ...outputTokens !== void 0 ? { outputTokens } : {},
1578
- ...totalTokens !== void 0 ? { totalTokens } : {}
1638
+ ...totalTokens !== void 0 ? { totalTokens } : {},
1639
+ ...cacheReadTokens !== void 0 ? { cacheReadTokens } : {}
1579
1640
  } : void 0;
1580
1641
  const endEvent = {
1581
1642
  type: "end",
@@ -1717,6 +1778,39 @@ function isAbortError3(err) {
1717
1778
 
1718
1779
  // src/adapters/google/manifest.ts
1719
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
+ },
1720
1814
  {
1721
1815
  id: "gemini-2.5-pro",
1722
1816
  displayName: "Gemini 2.5 Pro",
@@ -1860,6 +1954,39 @@ var import_genai2 = require("@google/genai");
1860
1954
 
1861
1955
  // src/adapters/vertex-gemini/manifest.ts
1862
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
+ },
1863
1990
  {
1864
1991
  id: "gemini-2.5-pro",
1865
1992
  displayName: "Gemini 2.5 Pro",
@@ -2543,6 +2670,15 @@ var BedrockAdapter = class {
2543
2670
  if (Object.keys(inferenceConfig).length > 0) {
2544
2671
  commandInput.inferenceConfig = inferenceConfig;
2545
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
+ }
2546
2682
  const command = new import_client_bedrock_runtime.ConverseStreamCommand(
2547
2683
  commandInput
2548
2684
  );
@@ -2566,6 +2702,8 @@ var BedrockAdapter = class {
2566
2702
  let inputTokens;
2567
2703
  let outputTokens;
2568
2704
  let totalTokens;
2705
+ let cacheReadTokens;
2706
+ let cacheWriteTokens;
2569
2707
  try {
2570
2708
  for await (const event of response.stream) {
2571
2709
  if (req.signal?.aborted) {
@@ -2618,6 +2756,8 @@ var BedrockAdapter = class {
2618
2756
  inputTokens = event.metadata.usage.inputTokens;
2619
2757
  outputTokens = event.metadata.usage.outputTokens;
2620
2758
  totalTokens = event.metadata.usage.totalTokens;
2759
+ cacheReadTokens = event.metadata.usage.cacheReadInputTokens;
2760
+ cacheWriteTokens = event.metadata.usage.cacheWriteInputTokens;
2621
2761
  }
2622
2762
  }
2623
2763
  }
@@ -2644,7 +2784,9 @@ var BedrockAdapter = class {
2644
2784
  const usage = inputTokens !== void 0 || outputTokens !== void 0 || totalTokens !== void 0 ? {
2645
2785
  ...inputTokens !== void 0 ? { inputTokens } : {},
2646
2786
  ...outputTokens !== void 0 ? { outputTokens } : {},
2647
- ...totalTokens !== void 0 ? { totalTokens } : {}
2787
+ ...totalTokens !== void 0 ? { totalTokens } : {},
2788
+ ...cacheReadTokens !== void 0 ? { cacheReadTokens } : {},
2789
+ ...cacheWriteTokens !== void 0 ? { cacheWriteTokens } : {}
2648
2790
  } : void 0;
2649
2791
  const endEvent = {
2650
2792
  type: "end",
@@ -2752,6 +2894,16 @@ function buildWireMessages3(req) {
2752
2894
  }
2753
2895
  return out;
2754
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
+ }
2755
2907
  function formatTools4(tools) {
2756
2908
  return tools.map((t) => ({
2757
2909
  toolSpec: {
@@ -3107,7 +3259,10 @@ var GroqAdapter = class extends OpenAIAdapter {
3107
3259
  stream(req) {
3108
3260
  return streamFromOpenAI(this.ensureClient(), req, {
3109
3261
  knownModels: groqManifest.knownModels,
3110
- 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
3111
3266
  });
3112
3267
  }
3113
3268
  };