@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.
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,11 +128,20 @@ 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");
131
137
  body[param] = effectiveMaxTokens;
132
138
  }
139
+ if (req.providerParams) {
140
+ const bodyRecord = body;
141
+ for (const [key, value] of Object.entries(req.providerParams)) {
142
+ if (!(key in bodyRecord)) bodyRecord[key] = value;
143
+ }
144
+ }
133
145
  const t0 = process.hrtime.bigint();
134
146
  let t1;
135
147
  let t2;
@@ -172,10 +184,12 @@ async function* streamFromOpenAI(client, req, options) {
172
184
  throw new Error(`OpenAI-compatible stream returned an error: ${message}`);
173
185
  }
174
186
  if (chunk.usage) {
187
+ const cachedTokens = chunk.usage.prompt_tokens_details?.cached_tokens;
175
188
  usage = {
176
- ...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) } : {},
177
190
  ...chunk.usage.completion_tokens !== void 0 ? { outputTokens: chunk.usage.completion_tokens } : {},
178
- ...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 } : {}
179
193
  };
180
194
  }
181
195
  const choice = chunk.choices?.[0];
@@ -1228,6 +1242,16 @@ var AnthropicAdapter = class {
1228
1242
  if (req.system) body.system = req.system;
1229
1243
  if (tools) body.tools = tools;
1230
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
+ }
1231
1255
  let stream;
1232
1256
  let responseHeaders;
1233
1257
  try {
@@ -1255,6 +1279,8 @@ var AnthropicAdapter = class {
1255
1279
  let stopReason;
1256
1280
  let inputTokens;
1257
1281
  let outputTokens;
1282
+ let cacheReadTokens;
1283
+ let cacheWriteTokens;
1258
1284
  try {
1259
1285
  for await (const event of stream) {
1260
1286
  if (req.signal?.aborted) {
@@ -1264,6 +1290,8 @@ var AnthropicAdapter = class {
1264
1290
  switch (event.type) {
1265
1291
  case "message_start":
1266
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;
1267
1295
  break;
1268
1296
  case "content_block_start": {
1269
1297
  const block = event.content_block;
@@ -1345,7 +1373,11 @@ var AnthropicAdapter = class {
1345
1373
  const usage = inputTokens !== void 0 || outputTokens !== void 0 ? {
1346
1374
  ...inputTokens !== void 0 ? { inputTokens } : {},
1347
1375
  ...outputTokens !== void 0 ? { outputTokens } : {},
1348
- ...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 } : {}
1349
1381
  } : void 0;
1350
1382
  const endEvent = {
1351
1383
  type: "end",
@@ -1444,6 +1476,23 @@ function buildWireMessages2(req) {
1444
1476
  }
1445
1477
  return out;
1446
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
+ }
1447
1496
  function formatTools2(tools) {
1448
1497
  return tools.map((t) => ({
1449
1498
  name: t.name,
@@ -1506,6 +1555,11 @@ async function* streamFromGemini(client, req) {
1506
1555
  if (req.temperature !== void 0) config.temperature = req.temperature;
1507
1556
  if (req.maxTokens !== void 0) config.maxOutputTokens = req.maxTokens;
1508
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
+ }
1509
1563
  let stream;
1510
1564
  try {
1511
1565
  stream = await client.models.generateContentStream({
@@ -1526,6 +1580,7 @@ async function* streamFromGemini(client, req) {
1526
1580
  let inputTokens;
1527
1581
  let outputTokens;
1528
1582
  let totalTokens;
1583
+ let cacheReadTokens;
1529
1584
  try {
1530
1585
  for await (const chunk of stream) {
1531
1586
  if (req.signal?.aborted) {
@@ -1534,7 +1589,12 @@ async function* streamFromGemini(client, req) {
1534
1589
  }
1535
1590
  const usage2 = chunk.usageMetadata;
1536
1591
  if (usage2) {
1537
- 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
+ }
1538
1598
  if (usage2.candidatesTokenCount !== void 0) outputTokens = usage2.candidatesTokenCount;
1539
1599
  if (usage2.totalTokenCount !== void 0) totalTokens = usage2.totalTokenCount;
1540
1600
  }
@@ -1588,7 +1648,8 @@ async function* streamFromGemini(client, req) {
1588
1648
  const usage = inputTokens !== void 0 || outputTokens !== void 0 || totalTokens !== void 0 ? {
1589
1649
  ...inputTokens !== void 0 ? { inputTokens } : {},
1590
1650
  ...outputTokens !== void 0 ? { outputTokens } : {},
1591
- ...totalTokens !== void 0 ? { totalTokens } : {}
1651
+ ...totalTokens !== void 0 ? { totalTokens } : {},
1652
+ ...cacheReadTokens !== void 0 ? { cacheReadTokens } : {}
1592
1653
  } : void 0;
1593
1654
  const endEvent = {
1594
1655
  type: "end",
@@ -1730,6 +1791,39 @@ function isAbortError3(err) {
1730
1791
 
1731
1792
  // src/adapters/google/manifest.ts
1732
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
+ },
1733
1827
  {
1734
1828
  id: "gemini-2.5-pro",
1735
1829
  displayName: "Gemini 2.5 Pro",
@@ -1873,6 +1967,39 @@ var import_genai2 = require("@google/genai");
1873
1967
 
1874
1968
  // src/adapters/vertex-gemini/manifest.ts
1875
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
+ },
1876
2003
  {
1877
2004
  id: "gemini-2.5-pro",
1878
2005
  displayName: "Gemini 2.5 Pro",
@@ -2556,6 +2683,15 @@ var BedrockAdapter = class {
2556
2683
  if (Object.keys(inferenceConfig).length > 0) {
2557
2684
  commandInput.inferenceConfig = inferenceConfig;
2558
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
+ }
2559
2695
  const command = new import_client_bedrock_runtime.ConverseStreamCommand(
2560
2696
  commandInput
2561
2697
  );
@@ -2579,6 +2715,8 @@ var BedrockAdapter = class {
2579
2715
  let inputTokens;
2580
2716
  let outputTokens;
2581
2717
  let totalTokens;
2718
+ let cacheReadTokens;
2719
+ let cacheWriteTokens;
2582
2720
  try {
2583
2721
  for await (const event of response.stream) {
2584
2722
  if (req.signal?.aborted) {
@@ -2631,6 +2769,8 @@ var BedrockAdapter = class {
2631
2769
  inputTokens = event.metadata.usage.inputTokens;
2632
2770
  outputTokens = event.metadata.usage.outputTokens;
2633
2771
  totalTokens = event.metadata.usage.totalTokens;
2772
+ cacheReadTokens = event.metadata.usage.cacheReadInputTokens;
2773
+ cacheWriteTokens = event.metadata.usage.cacheWriteInputTokens;
2634
2774
  }
2635
2775
  }
2636
2776
  }
@@ -2657,7 +2797,9 @@ var BedrockAdapter = class {
2657
2797
  const usage = inputTokens !== void 0 || outputTokens !== void 0 || totalTokens !== void 0 ? {
2658
2798
  ...inputTokens !== void 0 ? { inputTokens } : {},
2659
2799
  ...outputTokens !== void 0 ? { outputTokens } : {},
2660
- ...totalTokens !== void 0 ? { totalTokens } : {}
2800
+ ...totalTokens !== void 0 ? { totalTokens } : {},
2801
+ ...cacheReadTokens !== void 0 ? { cacheReadTokens } : {},
2802
+ ...cacheWriteTokens !== void 0 ? { cacheWriteTokens } : {}
2661
2803
  } : void 0;
2662
2804
  const endEvent = {
2663
2805
  type: "end",
@@ -2765,6 +2907,16 @@ function buildWireMessages3(req) {
2765
2907
  }
2766
2908
  return out;
2767
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
+ }
2768
2920
  function formatTools4(tools) {
2769
2921
  return tools.map((t) => ({
2770
2922
  toolSpec: {
@@ -3120,7 +3272,10 @@ var GroqAdapter = class extends OpenAIAdapter {
3120
3272
  stream(req) {
3121
3273
  return streamFromOpenAI(this.ensureClient(), req, {
3122
3274
  knownModels: groqManifest.knownModels,
3123
- 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
3124
3279
  });
3125
3280
  }
3126
3281
  };