@jambonz/llm 0.5.7 → 0.6.1

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];
@@ -508,7 +516,8 @@ var OpenAIAdapter = class {
508
516
  stream(req) {
509
517
  return streamFromOpenAI(this.ensureClient(), req, {
510
518
  knownModels: this.knownModels(),
511
- vendorMetadataExtractor: OPENAI_METADATA_EXTRACTOR
519
+ vendorMetadataExtractor: OPENAI_METADATA_EXTRACTOR,
520
+ includeCacheKey: this.forwardCacheKey()
512
521
  });
513
522
  }
514
523
  appendAssistantToolCall(history, toolCalls) {
@@ -529,6 +538,20 @@ var OpenAIAdapter = class {
529
538
  knownModels() {
530
539
  return openAIManifest.knownModels;
531
540
  }
541
+ /**
542
+ * Whether to forward `req.cacheKey` as OpenAI's `prompt_cache_key` request
543
+ * param. Default true — verified accepted by OpenAI, Azure OpenAI,
544
+ * Vertex-OpenAI, and Baseten. OpenAI-compatible vendors whose param
545
+ * handling is strict or unverified (DeepSeek, Moonshot, Zai, xAI — and
546
+ * Groq via its own stream override) override to false: a strict backend
547
+ * rejects the whole request on an unknown body param, which is far worse
548
+ * than forgoing a cache-routing hint. Flip a vendor to true only after
549
+ * verifying its endpoint (including any proxy in front of it, e.g.
550
+ * Bedrock Mantle) tolerates the param.
551
+ */
552
+ forwardCacheKey() {
553
+ return true;
554
+ }
532
555
  async testCredential() {
533
556
  await this.ensureClient().models.list();
534
557
  }
@@ -641,6 +664,11 @@ var DeepSeekAdapter = class extends OpenAIAdapter {
641
664
  knownModels() {
642
665
  return deepseekManifest.knownModels;
643
666
  }
667
+ // DeepSeek caches automatically server-side; prompt_cache_key is
668
+ // unverified against its param handling — do not forward.
669
+ forwardCacheKey() {
670
+ return false;
671
+ }
644
672
  };
645
673
  var deepseekFactory = {
646
674
  vendor: deepseekManifest.vendor,
@@ -864,6 +892,12 @@ var MoonshotAdapter = class extends OpenAIAdapter {
864
892
  knownModels() {
865
893
  return moonshotManifest.knownModels;
866
894
  }
895
+ // Moonshot's param handling is strict (e.g. it rejects unknown template
896
+ // kwargs) and prompt_cache_key is unverified against both the native
897
+ // endpoint and Bedrock Mantle deployments — do not forward.
898
+ forwardCacheKey() {
899
+ return false;
900
+ }
867
901
  };
868
902
  var moonshotFactory = {
869
903
  vendor: moonshotManifest.vendor,
@@ -937,6 +971,11 @@ var ZaiAdapter = class extends OpenAIAdapter {
937
971
  knownModels() {
938
972
  return zaiManifest.knownModels;
939
973
  }
974
+ // prompt_cache_key is unverified against Zai's OpenAI-compat endpoint —
975
+ // do not forward.
976
+ forwardCacheKey() {
977
+ return false;
978
+ }
940
979
  };
941
980
  var zaiFactory = {
942
981
  vendor: zaiManifest.vendor,
@@ -1032,6 +1071,11 @@ var XaiAdapter = class extends OpenAIAdapter {
1032
1071
  knownModels() {
1033
1072
  return xaiManifest.knownModels;
1034
1073
  }
1074
+ // xAI caches automatically server-side; prompt_cache_key is unverified
1075
+ // against its param handling — do not forward.
1076
+ forwardCacheKey() {
1077
+ return false;
1078
+ }
1035
1079
  };
1036
1080
  var xaiFactory = {
1037
1081
  vendor: xaiManifest.vendor,
@@ -1182,6 +1226,16 @@ var AnthropicAdapter = class {
1182
1226
  if (req.system) body.system = req.system;
1183
1227
  if (tools) body.tools = tools;
1184
1228
  if (req.temperature !== void 0) body.temperature = req.temperature;
1229
+ if (req.cacheKey) {
1230
+ if (req.system) {
1231
+ body.system = [{ type: "text", text: req.system, cache_control: { type: "ephemeral" } }];
1232
+ }
1233
+ if (tools) {
1234
+ const lastTool = tools[tools.length - 1];
1235
+ lastTool.cache_control = { type: "ephemeral" };
1236
+ }
1237
+ markLastMessageForCaching(messages);
1238
+ }
1185
1239
  let stream;
1186
1240
  let responseHeaders;
1187
1241
  try {
@@ -1209,6 +1263,8 @@ var AnthropicAdapter = class {
1209
1263
  let stopReason;
1210
1264
  let inputTokens;
1211
1265
  let outputTokens;
1266
+ let cacheReadTokens;
1267
+ let cacheWriteTokens;
1212
1268
  try {
1213
1269
  for await (const event of stream) {
1214
1270
  if (req.signal?.aborted) {
@@ -1218,6 +1274,8 @@ var AnthropicAdapter = class {
1218
1274
  switch (event.type) {
1219
1275
  case "message_start":
1220
1276
  inputTokens = event.message?.usage?.input_tokens;
1277
+ cacheReadTokens = event.message?.usage?.cache_read_input_tokens;
1278
+ cacheWriteTokens = event.message?.usage?.cache_creation_input_tokens;
1221
1279
  break;
1222
1280
  case "content_block_start": {
1223
1281
  const block = event.content_block;
@@ -1299,7 +1357,11 @@ var AnthropicAdapter = class {
1299
1357
  const usage = inputTokens !== void 0 || outputTokens !== void 0 ? {
1300
1358
  ...inputTokens !== void 0 ? { inputTokens } : {},
1301
1359
  ...outputTokens !== void 0 ? { outputTokens } : {},
1302
- ...inputTokens !== void 0 && outputTokens !== void 0 ? { totalTokens: inputTokens + outputTokens } : {}
1360
+ ...inputTokens !== void 0 && outputTokens !== void 0 ? {
1361
+ totalTokens: inputTokens + outputTokens + (cacheReadTokens ?? 0) + (cacheWriteTokens ?? 0)
1362
+ } : {},
1363
+ ...cacheReadTokens !== void 0 ? { cacheReadTokens } : {},
1364
+ ...cacheWriteTokens !== void 0 ? { cacheWriteTokens } : {}
1303
1365
  } : void 0;
1304
1366
  const endEvent = {
1305
1367
  type: "end",
@@ -1398,6 +1460,23 @@ function buildWireMessages2(req) {
1398
1460
  }
1399
1461
  return out;
1400
1462
  }
1463
+ function markLastMessageForCaching(messages) {
1464
+ if (messages.length === 0) return;
1465
+ const last = messages[messages.length - 1];
1466
+ const cacheControl = { cache_control: { type: "ephemeral" } };
1467
+ if (typeof last.content === "string" && last.content.length > 0) {
1468
+ messages[messages.length - 1] = {
1469
+ ...last,
1470
+ content: [{ type: "text", text: last.content, ...cacheControl }]
1471
+ };
1472
+ } else if (Array.isArray(last.content) && last.content.length > 0) {
1473
+ const blocks = last.content;
1474
+ messages[messages.length - 1] = {
1475
+ ...last,
1476
+ content: [...blocks.slice(0, -1), { ...blocks[blocks.length - 1], ...cacheControl }]
1477
+ };
1478
+ }
1479
+ }
1401
1480
  function formatTools2(tools) {
1402
1481
  return tools.map((t) => ({
1403
1482
  name: t.name,
@@ -1460,6 +1539,11 @@ async function* streamFromGemini(client, req) {
1460
1539
  if (req.temperature !== void 0) config.temperature = req.temperature;
1461
1540
  if (req.maxTokens !== void 0) config.maxOutputTokens = req.maxTokens;
1462
1541
  if (req.signal) config.abortSignal = req.signal;
1542
+ if (req.providerParams) {
1543
+ for (const [key, value] of Object.entries(req.providerParams)) {
1544
+ if (!(key in config)) config[key] = value;
1545
+ }
1546
+ }
1463
1547
  let stream;
1464
1548
  try {
1465
1549
  stream = await client.models.generateContentStream({
@@ -1480,6 +1564,7 @@ async function* streamFromGemini(client, req) {
1480
1564
  let inputTokens;
1481
1565
  let outputTokens;
1482
1566
  let totalTokens;
1567
+ let cacheReadTokens;
1483
1568
  try {
1484
1569
  for await (const chunk of stream) {
1485
1570
  if (req.signal?.aborted) {
@@ -1488,7 +1573,12 @@ async function* streamFromGemini(client, req) {
1488
1573
  }
1489
1574
  const usage2 = chunk.usageMetadata;
1490
1575
  if (usage2) {
1491
- if (usage2.promptTokenCount !== void 0) inputTokens = usage2.promptTokenCount;
1576
+ if (usage2.cachedContentTokenCount !== void 0) {
1577
+ cacheReadTokens = usage2.cachedContentTokenCount;
1578
+ }
1579
+ if (usage2.promptTokenCount !== void 0) {
1580
+ inputTokens = usage2.promptTokenCount - (usage2.cachedContentTokenCount ?? 0);
1581
+ }
1492
1582
  if (usage2.candidatesTokenCount !== void 0) outputTokens = usage2.candidatesTokenCount;
1493
1583
  if (usage2.totalTokenCount !== void 0) totalTokens = usage2.totalTokenCount;
1494
1584
  }
@@ -1542,7 +1632,8 @@ async function* streamFromGemini(client, req) {
1542
1632
  const usage = inputTokens !== void 0 || outputTokens !== void 0 || totalTokens !== void 0 ? {
1543
1633
  ...inputTokens !== void 0 ? { inputTokens } : {},
1544
1634
  ...outputTokens !== void 0 ? { outputTokens } : {},
1545
- ...totalTokens !== void 0 ? { totalTokens } : {}
1635
+ ...totalTokens !== void 0 ? { totalTokens } : {},
1636
+ ...cacheReadTokens !== void 0 ? { cacheReadTokens } : {}
1546
1637
  } : void 0;
1547
1638
  const endEvent = {
1548
1639
  type: "end",
@@ -1684,6 +1775,39 @@ function isAbortError3(err) {
1684
1775
 
1685
1776
  // src/adapters/google/manifest.ts
1686
1777
  var GOOGLE_KNOWN_MODELS = [
1778
+ {
1779
+ id: "gemini-3.6-flash",
1780
+ displayName: "Gemini 3.6 Flash",
1781
+ capabilities: {
1782
+ streaming: true,
1783
+ tools: true,
1784
+ vision: true,
1785
+ systemPrompt: true,
1786
+ maxContextTokens: 1e6
1787
+ }
1788
+ },
1789
+ {
1790
+ id: "gemini-3.5-flash",
1791
+ displayName: "Gemini 3.5 Flash",
1792
+ capabilities: {
1793
+ streaming: true,
1794
+ tools: true,
1795
+ vision: true,
1796
+ systemPrompt: true,
1797
+ maxContextTokens: 1e6
1798
+ }
1799
+ },
1800
+ {
1801
+ id: "gemini-3.5-flash-lite",
1802
+ displayName: "Gemini 3.5 Flash-Lite",
1803
+ capabilities: {
1804
+ streaming: true,
1805
+ tools: true,
1806
+ vision: true,
1807
+ systemPrompt: true,
1808
+ maxContextTokens: 1e6
1809
+ }
1810
+ },
1687
1811
  {
1688
1812
  id: "gemini-2.5-pro",
1689
1813
  displayName: "Gemini 2.5 Pro",
@@ -1827,6 +1951,39 @@ import { GoogleGenAI as GoogleGenAI2 } from "@google/genai";
1827
1951
 
1828
1952
  // src/adapters/vertex-gemini/manifest.ts
1829
1953
  var VERTEX_GEMINI_KNOWN_MODELS = [
1954
+ {
1955
+ id: "gemini-3.6-flash",
1956
+ displayName: "Gemini 3.6 Flash",
1957
+ capabilities: {
1958
+ streaming: true,
1959
+ tools: true,
1960
+ vision: true,
1961
+ systemPrompt: true,
1962
+ maxContextTokens: 1e6
1963
+ }
1964
+ },
1965
+ {
1966
+ id: "gemini-3.5-flash",
1967
+ displayName: "Gemini 3.5 Flash",
1968
+ capabilities: {
1969
+ streaming: true,
1970
+ tools: true,
1971
+ vision: true,
1972
+ systemPrompt: true,
1973
+ maxContextTokens: 1e6
1974
+ }
1975
+ },
1976
+ {
1977
+ id: "gemini-3.5-flash-lite",
1978
+ displayName: "Gemini 3.5 Flash-Lite",
1979
+ capabilities: {
1980
+ streaming: true,
1981
+ tools: true,
1982
+ vision: true,
1983
+ systemPrompt: true,
1984
+ maxContextTokens: 1e6
1985
+ }
1986
+ },
1830
1987
  {
1831
1988
  id: "gemini-2.5-pro",
1832
1989
  displayName: "Gemini 2.5 Pro",
@@ -2516,6 +2673,15 @@ var BedrockAdapter = class {
2516
2673
  if (Object.keys(inferenceConfig).length > 0) {
2517
2674
  commandInput.inferenceConfig = inferenceConfig;
2518
2675
  }
2676
+ if (req.cacheKey) {
2677
+ if (commandInput.system) {
2678
+ commandInput.system.push({ cachePoint: { type: "default" } });
2679
+ }
2680
+ if (tools) {
2681
+ tools.tools.push({ cachePoint: { type: "default" } });
2682
+ }
2683
+ markLastMessageForCaching2(messages);
2684
+ }
2519
2685
  const command = new ConverseStreamCommand(
2520
2686
  commandInput
2521
2687
  );
@@ -2539,6 +2705,8 @@ var BedrockAdapter = class {
2539
2705
  let inputTokens;
2540
2706
  let outputTokens;
2541
2707
  let totalTokens;
2708
+ let cacheReadTokens;
2709
+ let cacheWriteTokens;
2542
2710
  try {
2543
2711
  for await (const event of response.stream) {
2544
2712
  if (req.signal?.aborted) {
@@ -2591,6 +2759,8 @@ var BedrockAdapter = class {
2591
2759
  inputTokens = event.metadata.usage.inputTokens;
2592
2760
  outputTokens = event.metadata.usage.outputTokens;
2593
2761
  totalTokens = event.metadata.usage.totalTokens;
2762
+ cacheReadTokens = event.metadata.usage.cacheReadInputTokens;
2763
+ cacheWriteTokens = event.metadata.usage.cacheWriteInputTokens;
2594
2764
  }
2595
2765
  }
2596
2766
  }
@@ -2617,7 +2787,9 @@ var BedrockAdapter = class {
2617
2787
  const usage = inputTokens !== void 0 || outputTokens !== void 0 || totalTokens !== void 0 ? {
2618
2788
  ...inputTokens !== void 0 ? { inputTokens } : {},
2619
2789
  ...outputTokens !== void 0 ? { outputTokens } : {},
2620
- ...totalTokens !== void 0 ? { totalTokens } : {}
2790
+ ...totalTokens !== void 0 ? { totalTokens } : {},
2791
+ ...cacheReadTokens !== void 0 ? { cacheReadTokens } : {},
2792
+ ...cacheWriteTokens !== void 0 ? { cacheWriteTokens } : {}
2621
2793
  } : void 0;
2622
2794
  const endEvent = {
2623
2795
  type: "end",
@@ -2725,6 +2897,16 @@ function buildWireMessages3(req) {
2725
2897
  }
2726
2898
  return out;
2727
2899
  }
2900
+ function markLastMessageForCaching2(messages) {
2901
+ if (messages.length === 0) return;
2902
+ const last = messages[messages.length - 1];
2903
+ if (Array.isArray(last.content) && last.content.length > 0) {
2904
+ messages[messages.length - 1] = {
2905
+ ...last,
2906
+ content: [...last.content, { cachePoint: { type: "default" } }]
2907
+ };
2908
+ }
2909
+ }
2728
2910
  function formatTools4(tools) {
2729
2911
  return tools.map((t) => ({
2730
2912
  toolSpec: {
@@ -3080,7 +3262,10 @@ var GroqAdapter = class extends OpenAIAdapter {
3080
3262
  stream(req) {
3081
3263
  return streamFromOpenAI(this.ensureClient(), req, {
3082
3264
  knownModels: groqManifest.knownModels,
3083
- vendorMetadataExtractor: GROQ_METADATA_EXTRACTOR
3265
+ vendorMetadataExtractor: GROQ_METADATA_EXTRACTOR,
3266
+ // Groq caches automatically server-side and rejects/ignores undocumented
3267
+ // OpenAI params — do not forward prompt_cache_key.
3268
+ includeCacheKey: false
3084
3269
  });
3085
3270
  }
3086
3271
  };
@@ -3201,7 +3386,10 @@ var HuggingfaceAdapter = class extends OpenAIAdapter {
3201
3386
  stream(req) {
3202
3387
  return streamFromOpenAI(this.ensureClient(), req, {
3203
3388
  knownModels: huggingfaceManifest.knownModels,
3204
- vendorMetadataExtractor: HUGGINGFACE_METADATA_EXTRACTOR
3389
+ vendorMetadataExtractor: HUGGINGFACE_METADATA_EXTRACTOR,
3390
+ // The HF router fronts many third-party inference providers;
3391
+ // prompt_cache_key is unverified against them — do not forward.
3392
+ includeCacheKey: false
3205
3393
  });
3206
3394
  }
3207
3395
  };