@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.
@@ -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-rZZBXYhB.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-rZZBXYhB.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,6 +76,9 @@ 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");
@@ -126,10 +132,12 @@ async function* streamFromOpenAI(client, req, options) {
126
132
  throw new Error(`OpenAI-compatible stream returned an error: ${message}`);
127
133
  }
128
134
  if (chunk.usage) {
135
+ const cachedTokens = chunk.usage.prompt_tokens_details?.cached_tokens;
129
136
  usage = {
130
- ...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) } : {},
131
138
  ...chunk.usage.completion_tokens !== void 0 ? { outputTokens: chunk.usage.completion_tokens } : {},
132
- ...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 } : {}
133
141
  };
134
142
  }
135
143
  const choice = chunk.choices?.[0];
@@ -1182,6 +1190,16 @@ var AnthropicAdapter = class {
1182
1190
  if (req.system) body.system = req.system;
1183
1191
  if (tools) body.tools = tools;
1184
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
+ }
1185
1203
  let stream;
1186
1204
  let responseHeaders;
1187
1205
  try {
@@ -1209,6 +1227,8 @@ var AnthropicAdapter = class {
1209
1227
  let stopReason;
1210
1228
  let inputTokens;
1211
1229
  let outputTokens;
1230
+ let cacheReadTokens;
1231
+ let cacheWriteTokens;
1212
1232
  try {
1213
1233
  for await (const event of stream) {
1214
1234
  if (req.signal?.aborted) {
@@ -1218,6 +1238,8 @@ var AnthropicAdapter = class {
1218
1238
  switch (event.type) {
1219
1239
  case "message_start":
1220
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;
1221
1243
  break;
1222
1244
  case "content_block_start": {
1223
1245
  const block = event.content_block;
@@ -1299,7 +1321,11 @@ var AnthropicAdapter = class {
1299
1321
  const usage = inputTokens !== void 0 || outputTokens !== void 0 ? {
1300
1322
  ...inputTokens !== void 0 ? { inputTokens } : {},
1301
1323
  ...outputTokens !== void 0 ? { outputTokens } : {},
1302
- ...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 } : {}
1303
1329
  } : void 0;
1304
1330
  const endEvent = {
1305
1331
  type: "end",
@@ -1398,6 +1424,23 @@ function buildWireMessages2(req) {
1398
1424
  }
1399
1425
  return out;
1400
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
+ }
1401
1444
  function formatTools2(tools) {
1402
1445
  return tools.map((t) => ({
1403
1446
  name: t.name,
@@ -1460,6 +1503,11 @@ async function* streamFromGemini(client, req) {
1460
1503
  if (req.temperature !== void 0) config.temperature = req.temperature;
1461
1504
  if (req.maxTokens !== void 0) config.maxOutputTokens = req.maxTokens;
1462
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
+ }
1463
1511
  let stream;
1464
1512
  try {
1465
1513
  stream = await client.models.generateContentStream({
@@ -1480,6 +1528,7 @@ async function* streamFromGemini(client, req) {
1480
1528
  let inputTokens;
1481
1529
  let outputTokens;
1482
1530
  let totalTokens;
1531
+ let cacheReadTokens;
1483
1532
  try {
1484
1533
  for await (const chunk of stream) {
1485
1534
  if (req.signal?.aborted) {
@@ -1488,7 +1537,12 @@ async function* streamFromGemini(client, req) {
1488
1537
  }
1489
1538
  const usage2 = chunk.usageMetadata;
1490
1539
  if (usage2) {
1491
- 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
+ }
1492
1546
  if (usage2.candidatesTokenCount !== void 0) outputTokens = usage2.candidatesTokenCount;
1493
1547
  if (usage2.totalTokenCount !== void 0) totalTokens = usage2.totalTokenCount;
1494
1548
  }
@@ -1542,7 +1596,8 @@ async function* streamFromGemini(client, req) {
1542
1596
  const usage = inputTokens !== void 0 || outputTokens !== void 0 || totalTokens !== void 0 ? {
1543
1597
  ...inputTokens !== void 0 ? { inputTokens } : {},
1544
1598
  ...outputTokens !== void 0 ? { outputTokens } : {},
1545
- ...totalTokens !== void 0 ? { totalTokens } : {}
1599
+ ...totalTokens !== void 0 ? { totalTokens } : {},
1600
+ ...cacheReadTokens !== void 0 ? { cacheReadTokens } : {}
1546
1601
  } : void 0;
1547
1602
  const endEvent = {
1548
1603
  type: "end",
@@ -1684,6 +1739,39 @@ function isAbortError3(err) {
1684
1739
 
1685
1740
  // src/adapters/google/manifest.ts
1686
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
+ },
1687
1775
  {
1688
1776
  id: "gemini-2.5-pro",
1689
1777
  displayName: "Gemini 2.5 Pro",
@@ -1827,6 +1915,39 @@ import { GoogleGenAI as GoogleGenAI2 } from "@google/genai";
1827
1915
 
1828
1916
  // src/adapters/vertex-gemini/manifest.ts
1829
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
+ },
1830
1951
  {
1831
1952
  id: "gemini-2.5-pro",
1832
1953
  displayName: "Gemini 2.5 Pro",
@@ -2516,6 +2637,15 @@ var BedrockAdapter = class {
2516
2637
  if (Object.keys(inferenceConfig).length > 0) {
2517
2638
  commandInput.inferenceConfig = inferenceConfig;
2518
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
+ }
2519
2649
  const command = new ConverseStreamCommand(
2520
2650
  commandInput
2521
2651
  );
@@ -2539,6 +2669,8 @@ var BedrockAdapter = class {
2539
2669
  let inputTokens;
2540
2670
  let outputTokens;
2541
2671
  let totalTokens;
2672
+ let cacheReadTokens;
2673
+ let cacheWriteTokens;
2542
2674
  try {
2543
2675
  for await (const event of response.stream) {
2544
2676
  if (req.signal?.aborted) {
@@ -2591,6 +2723,8 @@ var BedrockAdapter = class {
2591
2723
  inputTokens = event.metadata.usage.inputTokens;
2592
2724
  outputTokens = event.metadata.usage.outputTokens;
2593
2725
  totalTokens = event.metadata.usage.totalTokens;
2726
+ cacheReadTokens = event.metadata.usage.cacheReadInputTokens;
2727
+ cacheWriteTokens = event.metadata.usage.cacheWriteInputTokens;
2594
2728
  }
2595
2729
  }
2596
2730
  }
@@ -2617,7 +2751,9 @@ var BedrockAdapter = class {
2617
2751
  const usage = inputTokens !== void 0 || outputTokens !== void 0 || totalTokens !== void 0 ? {
2618
2752
  ...inputTokens !== void 0 ? { inputTokens } : {},
2619
2753
  ...outputTokens !== void 0 ? { outputTokens } : {},
2620
- ...totalTokens !== void 0 ? { totalTokens } : {}
2754
+ ...totalTokens !== void 0 ? { totalTokens } : {},
2755
+ ...cacheReadTokens !== void 0 ? { cacheReadTokens } : {},
2756
+ ...cacheWriteTokens !== void 0 ? { cacheWriteTokens } : {}
2621
2757
  } : void 0;
2622
2758
  const endEvent = {
2623
2759
  type: "end",
@@ -2725,6 +2861,16 @@ function buildWireMessages3(req) {
2725
2861
  }
2726
2862
  return out;
2727
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
+ }
2728
2874
  function formatTools4(tools) {
2729
2875
  return tools.map((t) => ({
2730
2876
  toolSpec: {
@@ -3080,7 +3226,10 @@ var GroqAdapter = class extends OpenAIAdapter {
3080
3226
  stream(req) {
3081
3227
  return streamFromOpenAI(this.ensureClient(), req, {
3082
3228
  knownModels: groqManifest.knownModels,
3083
- 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
3084
3233
  });
3085
3234
  }
3086
3235
  };