@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.
@@ -1,4 +1,4 @@
1
- import { b as AdapterFactory, e as AuthKind, A as AuthSpec, L as LlmAdapter, d as ApiKeyAuth, h as ClientOptions, P as PromptRequest, m as LlmEvent, M as Message, p as ToolCallEvent, o as ModelInfo, c as AdapterManifest } from '../types-BEgQaQIb.cjs';
1
+ import { b as AdapterFactory, e as AuthKind, A as AuthSpec, L as LlmAdapter, d as ApiKeyAuth, h as ClientOptions, P as PromptRequest, m as LlmEvent, M as Message, p as ToolCallEvent, o as ModelInfo, c as AdapterManifest } from '../types-Y0o6rN3B.cjs';
2
2
 
3
3
  /**
4
4
  * Named scenarios the harness must be able to stage. Each scenario configures
@@ -1,4 +1,4 @@
1
- import { b as AdapterFactory, e as AuthKind, A as AuthSpec, L as LlmAdapter, d as ApiKeyAuth, h as ClientOptions, P as PromptRequest, m as LlmEvent, M as Message, p as ToolCallEvent, o as ModelInfo, c as AdapterManifest } from '../types-BEgQaQIb.js';
1
+ import { b as AdapterFactory, e as AuthKind, A as AuthSpec, L as LlmAdapter, d as ApiKeyAuth, h as ClientOptions, P as PromptRequest, m as LlmEvent, M as Message, p as ToolCallEvent, o as ModelInfo, c as AdapterManifest } from '../types-Y0o6rN3B.js';
2
2
 
3
3
  /**
4
4
  * Named scenarios the harness must be able to stage. Each scenario configures
@@ -49,6 +49,9 @@ function assertValidRequest(req) {
49
49
  "PromptRequest.messages must not contain role: 'system'. Pass the system prompt via PromptRequest.system instead."
50
50
  );
51
51
  }
52
+ if (req.cacheKey !== void 0 && (typeof req.cacheKey !== "string" || req.cacheKey.length === 0)) {
53
+ throw new Error("PromptRequest.cacheKey must be a non-empty string");
54
+ }
52
55
  }
53
56
 
54
57
  // src/adapters/openai/_streaming.ts
@@ -73,11 +76,20 @@ async function* streamFromOpenAI(client, req, options) {
73
76
  body.tool_choice = "auto";
74
77
  }
75
78
  if (req.temperature !== void 0) body.temperature = req.temperature;
79
+ if (req.cacheKey && options.includeCacheKey !== false) {
80
+ body.prompt_cache_key = req.cacheKey;
81
+ }
76
82
  const effectiveMaxTokens = req.maxTokens ?? options.defaultMaxTokens;
77
83
  if (effectiveMaxTokens !== void 0) {
78
84
  const param = options.tokensParam ?? (needsMaxCompletionTokens(req.model) ? "max_completion_tokens" : "max_tokens");
79
85
  body[param] = effectiveMaxTokens;
80
86
  }
87
+ if (req.providerParams) {
88
+ const bodyRecord = body;
89
+ for (const [key, value] of Object.entries(req.providerParams)) {
90
+ if (!(key in bodyRecord)) bodyRecord[key] = value;
91
+ }
92
+ }
81
93
  const t0 = process.hrtime.bigint();
82
94
  let t1;
83
95
  let t2;
@@ -120,10 +132,12 @@ async function* streamFromOpenAI(client, req, options) {
120
132
  throw new Error(`OpenAI-compatible stream returned an error: ${message}`);
121
133
  }
122
134
  if (chunk.usage) {
135
+ const cachedTokens = chunk.usage.prompt_tokens_details?.cached_tokens;
123
136
  usage = {
124
- ...chunk.usage.prompt_tokens !== void 0 ? { inputTokens: chunk.usage.prompt_tokens } : {},
137
+ ...chunk.usage.prompt_tokens !== void 0 ? { inputTokens: chunk.usage.prompt_tokens - (cachedTokens ?? 0) } : {},
125
138
  ...chunk.usage.completion_tokens !== void 0 ? { outputTokens: chunk.usage.completion_tokens } : {},
126
- ...chunk.usage.total_tokens !== void 0 ? { totalTokens: chunk.usage.total_tokens } : {}
139
+ ...chunk.usage.total_tokens !== void 0 ? { totalTokens: chunk.usage.total_tokens } : {},
140
+ ...cachedTokens !== void 0 ? { cacheReadTokens: cachedTokens } : {}
127
141
  };
128
142
  }
129
143
  const choice = chunk.choices?.[0];
@@ -1176,6 +1190,16 @@ var AnthropicAdapter = class {
1176
1190
  if (req.system) body.system = req.system;
1177
1191
  if (tools) body.tools = tools;
1178
1192
  if (req.temperature !== void 0) body.temperature = req.temperature;
1193
+ if (req.cacheKey) {
1194
+ if (req.system) {
1195
+ body.system = [{ type: "text", text: req.system, cache_control: { type: "ephemeral" } }];
1196
+ }
1197
+ if (tools) {
1198
+ const lastTool = tools[tools.length - 1];
1199
+ lastTool.cache_control = { type: "ephemeral" };
1200
+ }
1201
+ markLastMessageForCaching(messages);
1202
+ }
1179
1203
  let stream;
1180
1204
  let responseHeaders;
1181
1205
  try {
@@ -1203,6 +1227,8 @@ var AnthropicAdapter = class {
1203
1227
  let stopReason;
1204
1228
  let inputTokens;
1205
1229
  let outputTokens;
1230
+ let cacheReadTokens;
1231
+ let cacheWriteTokens;
1206
1232
  try {
1207
1233
  for await (const event of stream) {
1208
1234
  if (req.signal?.aborted) {
@@ -1212,6 +1238,8 @@ var AnthropicAdapter = class {
1212
1238
  switch (event.type) {
1213
1239
  case "message_start":
1214
1240
  inputTokens = event.message?.usage?.input_tokens;
1241
+ cacheReadTokens = event.message?.usage?.cache_read_input_tokens;
1242
+ cacheWriteTokens = event.message?.usage?.cache_creation_input_tokens;
1215
1243
  break;
1216
1244
  case "content_block_start": {
1217
1245
  const block = event.content_block;
@@ -1293,7 +1321,11 @@ var AnthropicAdapter = class {
1293
1321
  const usage = inputTokens !== void 0 || outputTokens !== void 0 ? {
1294
1322
  ...inputTokens !== void 0 ? { inputTokens } : {},
1295
1323
  ...outputTokens !== void 0 ? { outputTokens } : {},
1296
- ...inputTokens !== void 0 && outputTokens !== void 0 ? { totalTokens: inputTokens + outputTokens } : {}
1324
+ ...inputTokens !== void 0 && outputTokens !== void 0 ? {
1325
+ totalTokens: inputTokens + outputTokens + (cacheReadTokens ?? 0) + (cacheWriteTokens ?? 0)
1326
+ } : {},
1327
+ ...cacheReadTokens !== void 0 ? { cacheReadTokens } : {},
1328
+ ...cacheWriteTokens !== void 0 ? { cacheWriteTokens } : {}
1297
1329
  } : void 0;
1298
1330
  const endEvent = {
1299
1331
  type: "end",
@@ -1392,6 +1424,23 @@ function buildWireMessages2(req) {
1392
1424
  }
1393
1425
  return out;
1394
1426
  }
1427
+ function markLastMessageForCaching(messages) {
1428
+ if (messages.length === 0) return;
1429
+ const last = messages[messages.length - 1];
1430
+ const cacheControl = { cache_control: { type: "ephemeral" } };
1431
+ if (typeof last.content === "string" && last.content.length > 0) {
1432
+ messages[messages.length - 1] = {
1433
+ ...last,
1434
+ content: [{ type: "text", text: last.content, ...cacheControl }]
1435
+ };
1436
+ } else if (Array.isArray(last.content) && last.content.length > 0) {
1437
+ const blocks = last.content;
1438
+ messages[messages.length - 1] = {
1439
+ ...last,
1440
+ content: [...blocks.slice(0, -1), { ...blocks[blocks.length - 1], ...cacheControl }]
1441
+ };
1442
+ }
1443
+ }
1395
1444
  function formatTools2(tools) {
1396
1445
  return tools.map((t) => ({
1397
1446
  name: t.name,
@@ -1454,6 +1503,11 @@ async function* streamFromGemini(client, req) {
1454
1503
  if (req.temperature !== void 0) config.temperature = req.temperature;
1455
1504
  if (req.maxTokens !== void 0) config.maxOutputTokens = req.maxTokens;
1456
1505
  if (req.signal) config.abortSignal = req.signal;
1506
+ if (req.providerParams) {
1507
+ for (const [key, value] of Object.entries(req.providerParams)) {
1508
+ if (!(key in config)) config[key] = value;
1509
+ }
1510
+ }
1457
1511
  let stream;
1458
1512
  try {
1459
1513
  stream = await client.models.generateContentStream({
@@ -1474,6 +1528,7 @@ async function* streamFromGemini(client, req) {
1474
1528
  let inputTokens;
1475
1529
  let outputTokens;
1476
1530
  let totalTokens;
1531
+ let cacheReadTokens;
1477
1532
  try {
1478
1533
  for await (const chunk of stream) {
1479
1534
  if (req.signal?.aborted) {
@@ -1482,7 +1537,12 @@ async function* streamFromGemini(client, req) {
1482
1537
  }
1483
1538
  const usage2 = chunk.usageMetadata;
1484
1539
  if (usage2) {
1485
- if (usage2.promptTokenCount !== void 0) inputTokens = usage2.promptTokenCount;
1540
+ if (usage2.cachedContentTokenCount !== void 0) {
1541
+ cacheReadTokens = usage2.cachedContentTokenCount;
1542
+ }
1543
+ if (usage2.promptTokenCount !== void 0) {
1544
+ inputTokens = usage2.promptTokenCount - (usage2.cachedContentTokenCount ?? 0);
1545
+ }
1486
1546
  if (usage2.candidatesTokenCount !== void 0) outputTokens = usage2.candidatesTokenCount;
1487
1547
  if (usage2.totalTokenCount !== void 0) totalTokens = usage2.totalTokenCount;
1488
1548
  }
@@ -1536,7 +1596,8 @@ async function* streamFromGemini(client, req) {
1536
1596
  const usage = inputTokens !== void 0 || outputTokens !== void 0 || totalTokens !== void 0 ? {
1537
1597
  ...inputTokens !== void 0 ? { inputTokens } : {},
1538
1598
  ...outputTokens !== void 0 ? { outputTokens } : {},
1539
- ...totalTokens !== void 0 ? { totalTokens } : {}
1599
+ ...totalTokens !== void 0 ? { totalTokens } : {},
1600
+ ...cacheReadTokens !== void 0 ? { cacheReadTokens } : {}
1540
1601
  } : void 0;
1541
1602
  const endEvent = {
1542
1603
  type: "end",
@@ -1678,6 +1739,39 @@ function isAbortError3(err) {
1678
1739
 
1679
1740
  // src/adapters/google/manifest.ts
1680
1741
  var GOOGLE_KNOWN_MODELS = [
1742
+ {
1743
+ id: "gemini-3.6-flash",
1744
+ displayName: "Gemini 3.6 Flash",
1745
+ capabilities: {
1746
+ streaming: true,
1747
+ tools: true,
1748
+ vision: true,
1749
+ systemPrompt: true,
1750
+ maxContextTokens: 1e6
1751
+ }
1752
+ },
1753
+ {
1754
+ id: "gemini-3.5-flash",
1755
+ displayName: "Gemini 3.5 Flash",
1756
+ capabilities: {
1757
+ streaming: true,
1758
+ tools: true,
1759
+ vision: true,
1760
+ systemPrompt: true,
1761
+ maxContextTokens: 1e6
1762
+ }
1763
+ },
1764
+ {
1765
+ id: "gemini-3.5-flash-lite",
1766
+ displayName: "Gemini 3.5 Flash-Lite",
1767
+ capabilities: {
1768
+ streaming: true,
1769
+ tools: true,
1770
+ vision: true,
1771
+ systemPrompt: true,
1772
+ maxContextTokens: 1e6
1773
+ }
1774
+ },
1681
1775
  {
1682
1776
  id: "gemini-2.5-pro",
1683
1777
  displayName: "Gemini 2.5 Pro",
@@ -1821,6 +1915,39 @@ import { GoogleGenAI as GoogleGenAI2 } from "@google/genai";
1821
1915
 
1822
1916
  // src/adapters/vertex-gemini/manifest.ts
1823
1917
  var VERTEX_GEMINI_KNOWN_MODELS = [
1918
+ {
1919
+ id: "gemini-3.6-flash",
1920
+ displayName: "Gemini 3.6 Flash",
1921
+ capabilities: {
1922
+ streaming: true,
1923
+ tools: true,
1924
+ vision: true,
1925
+ systemPrompt: true,
1926
+ maxContextTokens: 1e6
1927
+ }
1928
+ },
1929
+ {
1930
+ id: "gemini-3.5-flash",
1931
+ displayName: "Gemini 3.5 Flash",
1932
+ capabilities: {
1933
+ streaming: true,
1934
+ tools: true,
1935
+ vision: true,
1936
+ systemPrompt: true,
1937
+ maxContextTokens: 1e6
1938
+ }
1939
+ },
1940
+ {
1941
+ id: "gemini-3.5-flash-lite",
1942
+ displayName: "Gemini 3.5 Flash-Lite",
1943
+ capabilities: {
1944
+ streaming: true,
1945
+ tools: true,
1946
+ vision: true,
1947
+ systemPrompt: true,
1948
+ maxContextTokens: 1e6
1949
+ }
1950
+ },
1824
1951
  {
1825
1952
  id: "gemini-2.5-pro",
1826
1953
  displayName: "Gemini 2.5 Pro",
@@ -2510,6 +2637,15 @@ var BedrockAdapter = class {
2510
2637
  if (Object.keys(inferenceConfig).length > 0) {
2511
2638
  commandInput.inferenceConfig = inferenceConfig;
2512
2639
  }
2640
+ if (req.cacheKey) {
2641
+ if (commandInput.system) {
2642
+ commandInput.system.push({ cachePoint: { type: "default" } });
2643
+ }
2644
+ if (tools) {
2645
+ tools.tools.push({ cachePoint: { type: "default" } });
2646
+ }
2647
+ markLastMessageForCaching2(messages);
2648
+ }
2513
2649
  const command = new ConverseStreamCommand(
2514
2650
  commandInput
2515
2651
  );
@@ -2533,6 +2669,8 @@ var BedrockAdapter = class {
2533
2669
  let inputTokens;
2534
2670
  let outputTokens;
2535
2671
  let totalTokens;
2672
+ let cacheReadTokens;
2673
+ let cacheWriteTokens;
2536
2674
  try {
2537
2675
  for await (const event of response.stream) {
2538
2676
  if (req.signal?.aborted) {
@@ -2585,6 +2723,8 @@ var BedrockAdapter = class {
2585
2723
  inputTokens = event.metadata.usage.inputTokens;
2586
2724
  outputTokens = event.metadata.usage.outputTokens;
2587
2725
  totalTokens = event.metadata.usage.totalTokens;
2726
+ cacheReadTokens = event.metadata.usage.cacheReadInputTokens;
2727
+ cacheWriteTokens = event.metadata.usage.cacheWriteInputTokens;
2588
2728
  }
2589
2729
  }
2590
2730
  }
@@ -2611,7 +2751,9 @@ var BedrockAdapter = class {
2611
2751
  const usage = inputTokens !== void 0 || outputTokens !== void 0 || totalTokens !== void 0 ? {
2612
2752
  ...inputTokens !== void 0 ? { inputTokens } : {},
2613
2753
  ...outputTokens !== void 0 ? { outputTokens } : {},
2614
- ...totalTokens !== void 0 ? { totalTokens } : {}
2754
+ ...totalTokens !== void 0 ? { totalTokens } : {},
2755
+ ...cacheReadTokens !== void 0 ? { cacheReadTokens } : {},
2756
+ ...cacheWriteTokens !== void 0 ? { cacheWriteTokens } : {}
2615
2757
  } : void 0;
2616
2758
  const endEvent = {
2617
2759
  type: "end",
@@ -2719,6 +2861,16 @@ function buildWireMessages3(req) {
2719
2861
  }
2720
2862
  return out;
2721
2863
  }
2864
+ function markLastMessageForCaching2(messages) {
2865
+ if (messages.length === 0) return;
2866
+ const last = messages[messages.length - 1];
2867
+ if (Array.isArray(last.content) && last.content.length > 0) {
2868
+ messages[messages.length - 1] = {
2869
+ ...last,
2870
+ content: [...last.content, { cachePoint: { type: "default" } }]
2871
+ };
2872
+ }
2873
+ }
2722
2874
  function formatTools4(tools) {
2723
2875
  return tools.map((t) => ({
2724
2876
  toolSpec: {
@@ -3074,7 +3226,10 @@ var GroqAdapter = class extends OpenAIAdapter {
3074
3226
  stream(req) {
3075
3227
  return streamFromOpenAI(this.ensureClient(), req, {
3076
3228
  knownModels: groqManifest.knownModels,
3077
- vendorMetadataExtractor: GROQ_METADATA_EXTRACTOR
3229
+ vendorMetadataExtractor: GROQ_METADATA_EXTRACTOR,
3230
+ // Groq caches automatically server-side and rejects/ignores undocumented
3231
+ // OpenAI params — do not forward prompt_cache_key.
3232
+ includeCacheKey: false
3078
3233
  });
3079
3234
  }
3080
3235
  };