@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.
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as ServiceAccountJson, A as AuthSpec, a as AuthKindSchema, P as PromptRequest, b as AdapterFactory, c as AdapterManifest, C as CreateLlmArgs, L as LlmAdapter } from './types-rZZBXYhB.cjs';
2
- export { d as ApiKeyAuth, e as AuthKind, f as AzureOpenAIApiKeyAuth, B as BedrockApiKeyAuth, g as BedrockIamAuth, h as ClientOptions, i as ContentBlock, F as FinishReason, j as FormField, k as FormFieldType, G as GoogleApiKeyAuth, l as GoogleServiceAccountAuth, J as JsonSchema, m as LlmEvent, M as Message, n as ModelCapabilities, o as ModelInfo, R as Role, T as Tool, p as ToolCallEvent, U as Usage, V as VertexServiceAccountAuth } from './types-rZZBXYhB.cjs';
1
+ import { S as ServiceAccountJson, A as AuthSpec, a as AuthKindSchema, P as PromptRequest, b as AdapterFactory, c as AdapterManifest, C as CreateLlmArgs, L as LlmAdapter } from './types-Y0o6rN3B.cjs';
2
+ export { d as ApiKeyAuth, e as AuthKind, f as AzureOpenAIApiKeyAuth, B as BedrockApiKeyAuth, g as BedrockIamAuth, h as ClientOptions, i as ContentBlock, F as FinishReason, j as FormField, k as FormFieldType, G as GoogleApiKeyAuth, l as GoogleServiceAccountAuth, J as JsonSchema, m as LlmEvent, M as Message, n as ModelCapabilities, o as ModelInfo, R as Role, T as Tool, p as ToolCallEvent, U as Usage, V as VertexServiceAccountAuth } from './types-Y0o6rN3B.cjs';
3
3
 
4
4
  /**
5
5
  * The loose "DB-shape" input accepted by `normalizeAuth`.
@@ -73,6 +73,7 @@ declare function pickAuthKind(vendor: string, raw: RawCredentialLoose): AuthKind
73
73
  * - `model` is a non-empty string.
74
74
  * - `messages` is a non-empty array.
75
75
  * - `messages` does not contain `role: 'system'` turns (use `system` top-level).
76
+ * - `cacheKey`, when present, is a non-empty string.
76
77
  */
77
78
  declare function assertValidRequest(req: PromptRequest): void;
78
79
 
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as ServiceAccountJson, A as AuthSpec, a as AuthKindSchema, P as PromptRequest, b as AdapterFactory, c as AdapterManifest, C as CreateLlmArgs, L as LlmAdapter } from './types-rZZBXYhB.js';
2
- export { d as ApiKeyAuth, e as AuthKind, f as AzureOpenAIApiKeyAuth, B as BedrockApiKeyAuth, g as BedrockIamAuth, h as ClientOptions, i as ContentBlock, F as FinishReason, j as FormField, k as FormFieldType, G as GoogleApiKeyAuth, l as GoogleServiceAccountAuth, J as JsonSchema, m as LlmEvent, M as Message, n as ModelCapabilities, o as ModelInfo, R as Role, T as Tool, p as ToolCallEvent, U as Usage, V as VertexServiceAccountAuth } from './types-rZZBXYhB.js';
1
+ import { S as ServiceAccountJson, A as AuthSpec, a as AuthKindSchema, P as PromptRequest, b as AdapterFactory, c as AdapterManifest, C as CreateLlmArgs, L as LlmAdapter } from './types-Y0o6rN3B.js';
2
+ export { d as ApiKeyAuth, e as AuthKind, f as AzureOpenAIApiKeyAuth, B as BedrockApiKeyAuth, g as BedrockIamAuth, h as ClientOptions, i as ContentBlock, F as FinishReason, j as FormField, k as FormFieldType, G as GoogleApiKeyAuth, l as GoogleServiceAccountAuth, J as JsonSchema, m as LlmEvent, M as Message, n as ModelCapabilities, o as ModelInfo, R as Role, T as Tool, p as ToolCallEvent, U as Usage, V as VertexServiceAccountAuth } from './types-Y0o6rN3B.js';
3
3
 
4
4
  /**
5
5
  * The loose "DB-shape" input accepted by `normalizeAuth`.
@@ -73,6 +73,7 @@ declare function pickAuthKind(vendor: string, raw: RawCredentialLoose): AuthKind
73
73
  * - `model` is a non-empty string.
74
74
  * - `messages` is a non-empty array.
75
75
  * - `messages` does not contain `role: 'system'` turns (use `system` top-level).
76
+ * - `cacheKey`, when present, is a non-empty string.
76
77
  */
77
78
  declare function assertValidRequest(req: PromptRequest): void;
78
79
 
package/dist/index.js CHANGED
@@ -56,6 +56,9 @@ function assertValidRequest(req) {
56
56
  "PromptRequest.messages must not contain role: 'system'. Pass the system prompt via PromptRequest.system instead."
57
57
  );
58
58
  }
59
+ if (req.cacheKey !== void 0 && (typeof req.cacheKey !== "string" || req.cacheKey.length === 0)) {
60
+ throw new Error("PromptRequest.cacheKey must be a non-empty string");
61
+ }
59
62
  }
60
63
 
61
64
  // src/adapters/openai/_streaming.ts
@@ -80,6 +83,9 @@ async function* streamFromOpenAI(client, req, options) {
80
83
  body.tool_choice = "auto";
81
84
  }
82
85
  if (req.temperature !== void 0) body.temperature = req.temperature;
86
+ if (req.cacheKey && options.includeCacheKey !== false) {
87
+ body.prompt_cache_key = req.cacheKey;
88
+ }
83
89
  const effectiveMaxTokens = req.maxTokens ?? options.defaultMaxTokens;
84
90
  if (effectiveMaxTokens !== void 0) {
85
91
  const param = options.tokensParam ?? (needsMaxCompletionTokens(req.model) ? "max_completion_tokens" : "max_tokens");
@@ -133,10 +139,12 @@ async function* streamFromOpenAI(client, req, options) {
133
139
  throw new Error(`OpenAI-compatible stream returned an error: ${message}`);
134
140
  }
135
141
  if (chunk.usage) {
142
+ const cachedTokens = chunk.usage.prompt_tokens_details?.cached_tokens;
136
143
  usage = {
137
- ...chunk.usage.prompt_tokens !== void 0 ? { inputTokens: chunk.usage.prompt_tokens } : {},
144
+ ...chunk.usage.prompt_tokens !== void 0 ? { inputTokens: chunk.usage.prompt_tokens - (cachedTokens ?? 0) } : {},
138
145
  ...chunk.usage.completion_tokens !== void 0 ? { outputTokens: chunk.usage.completion_tokens } : {},
139
- ...chunk.usage.total_tokens !== void 0 ? { totalTokens: chunk.usage.total_tokens } : {}
146
+ ...chunk.usage.total_tokens !== void 0 ? { totalTokens: chunk.usage.total_tokens } : {},
147
+ ...cachedTokens !== void 0 ? { cacheReadTokens: cachedTokens } : {}
140
148
  };
141
149
  }
142
150
  const choice = chunk.choices?.[0];
@@ -515,7 +523,8 @@ var OpenAIAdapter = class {
515
523
  stream(req) {
516
524
  return streamFromOpenAI(this.ensureClient(), req, {
517
525
  knownModels: this.knownModels(),
518
- vendorMetadataExtractor: OPENAI_METADATA_EXTRACTOR
526
+ vendorMetadataExtractor: OPENAI_METADATA_EXTRACTOR,
527
+ includeCacheKey: this.forwardCacheKey()
519
528
  });
520
529
  }
521
530
  appendAssistantToolCall(history, toolCalls) {
@@ -536,6 +545,20 @@ var OpenAIAdapter = class {
536
545
  knownModels() {
537
546
  return openAIManifest.knownModels;
538
547
  }
548
+ /**
549
+ * Whether to forward `req.cacheKey` as OpenAI's `prompt_cache_key` request
550
+ * param. Default true — verified accepted by OpenAI, Azure OpenAI,
551
+ * Vertex-OpenAI, and Baseten. OpenAI-compatible vendors whose param
552
+ * handling is strict or unverified (DeepSeek, Moonshot, Zai, xAI — and
553
+ * Groq via its own stream override) override to false: a strict backend
554
+ * rejects the whole request on an unknown body param, which is far worse
555
+ * than forgoing a cache-routing hint. Flip a vendor to true only after
556
+ * verifying its endpoint (including any proxy in front of it, e.g.
557
+ * Bedrock Mantle) tolerates the param.
558
+ */
559
+ forwardCacheKey() {
560
+ return true;
561
+ }
539
562
  async testCredential() {
540
563
  await this.ensureClient().models.list();
541
564
  }
@@ -648,6 +671,11 @@ var DeepSeekAdapter = class extends OpenAIAdapter {
648
671
  knownModels() {
649
672
  return deepseekManifest.knownModels;
650
673
  }
674
+ // DeepSeek caches automatically server-side; prompt_cache_key is
675
+ // unverified against its param handling — do not forward.
676
+ forwardCacheKey() {
677
+ return false;
678
+ }
651
679
  };
652
680
  var deepseekFactory = {
653
681
  vendor: deepseekManifest.vendor,
@@ -871,6 +899,12 @@ var MoonshotAdapter = class extends OpenAIAdapter {
871
899
  knownModels() {
872
900
  return moonshotManifest.knownModels;
873
901
  }
902
+ // Moonshot's param handling is strict (e.g. it rejects unknown template
903
+ // kwargs) and prompt_cache_key is unverified against both the native
904
+ // endpoint and Bedrock Mantle deployments — do not forward.
905
+ forwardCacheKey() {
906
+ return false;
907
+ }
874
908
  };
875
909
  var moonshotFactory = {
876
910
  vendor: moonshotManifest.vendor,
@@ -944,6 +978,11 @@ var ZaiAdapter = class extends OpenAIAdapter {
944
978
  knownModels() {
945
979
  return zaiManifest.knownModels;
946
980
  }
981
+ // prompt_cache_key is unverified against Zai's OpenAI-compat endpoint —
982
+ // do not forward.
983
+ forwardCacheKey() {
984
+ return false;
985
+ }
947
986
  };
948
987
  var zaiFactory = {
949
988
  vendor: zaiManifest.vendor,
@@ -1039,6 +1078,11 @@ var XaiAdapter = class extends OpenAIAdapter {
1039
1078
  knownModels() {
1040
1079
  return xaiManifest.knownModels;
1041
1080
  }
1081
+ // xAI caches automatically server-side; prompt_cache_key is unverified
1082
+ // against its param handling — do not forward.
1083
+ forwardCacheKey() {
1084
+ return false;
1085
+ }
1042
1086
  };
1043
1087
  var xaiFactory = {
1044
1088
  vendor: xaiManifest.vendor,
@@ -1189,6 +1233,16 @@ var AnthropicAdapter = class {
1189
1233
  if (req.system) body.system = req.system;
1190
1234
  if (tools) body.tools = tools;
1191
1235
  if (req.temperature !== void 0) body.temperature = req.temperature;
1236
+ if (req.cacheKey) {
1237
+ if (req.system) {
1238
+ body.system = [{ type: "text", text: req.system, cache_control: { type: "ephemeral" } }];
1239
+ }
1240
+ if (tools) {
1241
+ const lastTool = tools[tools.length - 1];
1242
+ lastTool.cache_control = { type: "ephemeral" };
1243
+ }
1244
+ markLastMessageForCaching(messages);
1245
+ }
1192
1246
  let stream;
1193
1247
  let responseHeaders;
1194
1248
  try {
@@ -1216,6 +1270,8 @@ var AnthropicAdapter = class {
1216
1270
  let stopReason;
1217
1271
  let inputTokens;
1218
1272
  let outputTokens;
1273
+ let cacheReadTokens;
1274
+ let cacheWriteTokens;
1219
1275
  try {
1220
1276
  for await (const event of stream) {
1221
1277
  if (req.signal?.aborted) {
@@ -1225,6 +1281,8 @@ var AnthropicAdapter = class {
1225
1281
  switch (event.type) {
1226
1282
  case "message_start":
1227
1283
  inputTokens = event.message?.usage?.input_tokens;
1284
+ cacheReadTokens = event.message?.usage?.cache_read_input_tokens;
1285
+ cacheWriteTokens = event.message?.usage?.cache_creation_input_tokens;
1228
1286
  break;
1229
1287
  case "content_block_start": {
1230
1288
  const block = event.content_block;
@@ -1306,7 +1364,11 @@ var AnthropicAdapter = class {
1306
1364
  const usage = inputTokens !== void 0 || outputTokens !== void 0 ? {
1307
1365
  ...inputTokens !== void 0 ? { inputTokens } : {},
1308
1366
  ...outputTokens !== void 0 ? { outputTokens } : {},
1309
- ...inputTokens !== void 0 && outputTokens !== void 0 ? { totalTokens: inputTokens + outputTokens } : {}
1367
+ ...inputTokens !== void 0 && outputTokens !== void 0 ? {
1368
+ totalTokens: inputTokens + outputTokens + (cacheReadTokens ?? 0) + (cacheWriteTokens ?? 0)
1369
+ } : {},
1370
+ ...cacheReadTokens !== void 0 ? { cacheReadTokens } : {},
1371
+ ...cacheWriteTokens !== void 0 ? { cacheWriteTokens } : {}
1310
1372
  } : void 0;
1311
1373
  const endEvent = {
1312
1374
  type: "end",
@@ -1405,6 +1467,23 @@ function buildWireMessages2(req) {
1405
1467
  }
1406
1468
  return out;
1407
1469
  }
1470
+ function markLastMessageForCaching(messages) {
1471
+ if (messages.length === 0) return;
1472
+ const last = messages[messages.length - 1];
1473
+ const cacheControl = { cache_control: { type: "ephemeral" } };
1474
+ if (typeof last.content === "string" && last.content.length > 0) {
1475
+ messages[messages.length - 1] = {
1476
+ ...last,
1477
+ content: [{ type: "text", text: last.content, ...cacheControl }]
1478
+ };
1479
+ } else if (Array.isArray(last.content) && last.content.length > 0) {
1480
+ const blocks = last.content;
1481
+ messages[messages.length - 1] = {
1482
+ ...last,
1483
+ content: [...blocks.slice(0, -1), { ...blocks[blocks.length - 1], ...cacheControl }]
1484
+ };
1485
+ }
1486
+ }
1408
1487
  function formatTools2(tools) {
1409
1488
  return tools.map((t) => ({
1410
1489
  name: t.name,
@@ -1467,6 +1546,11 @@ async function* streamFromGemini(client, req) {
1467
1546
  if (req.temperature !== void 0) config.temperature = req.temperature;
1468
1547
  if (req.maxTokens !== void 0) config.maxOutputTokens = req.maxTokens;
1469
1548
  if (req.signal) config.abortSignal = req.signal;
1549
+ if (req.providerParams) {
1550
+ for (const [key, value] of Object.entries(req.providerParams)) {
1551
+ if (!(key in config)) config[key] = value;
1552
+ }
1553
+ }
1470
1554
  let stream;
1471
1555
  try {
1472
1556
  stream = await client.models.generateContentStream({
@@ -1487,6 +1571,7 @@ async function* streamFromGemini(client, req) {
1487
1571
  let inputTokens;
1488
1572
  let outputTokens;
1489
1573
  let totalTokens;
1574
+ let cacheReadTokens;
1490
1575
  try {
1491
1576
  for await (const chunk of stream) {
1492
1577
  if (req.signal?.aborted) {
@@ -1495,7 +1580,12 @@ async function* streamFromGemini(client, req) {
1495
1580
  }
1496
1581
  const usage2 = chunk.usageMetadata;
1497
1582
  if (usage2) {
1498
- if (usage2.promptTokenCount !== void 0) inputTokens = usage2.promptTokenCount;
1583
+ if (usage2.cachedContentTokenCount !== void 0) {
1584
+ cacheReadTokens = usage2.cachedContentTokenCount;
1585
+ }
1586
+ if (usage2.promptTokenCount !== void 0) {
1587
+ inputTokens = usage2.promptTokenCount - (usage2.cachedContentTokenCount ?? 0);
1588
+ }
1499
1589
  if (usage2.candidatesTokenCount !== void 0) outputTokens = usage2.candidatesTokenCount;
1500
1590
  if (usage2.totalTokenCount !== void 0) totalTokens = usage2.totalTokenCount;
1501
1591
  }
@@ -1549,7 +1639,8 @@ async function* streamFromGemini(client, req) {
1549
1639
  const usage = inputTokens !== void 0 || outputTokens !== void 0 || totalTokens !== void 0 ? {
1550
1640
  ...inputTokens !== void 0 ? { inputTokens } : {},
1551
1641
  ...outputTokens !== void 0 ? { outputTokens } : {},
1552
- ...totalTokens !== void 0 ? { totalTokens } : {}
1642
+ ...totalTokens !== void 0 ? { totalTokens } : {},
1643
+ ...cacheReadTokens !== void 0 ? { cacheReadTokens } : {}
1553
1644
  } : void 0;
1554
1645
  const endEvent = {
1555
1646
  type: "end",
@@ -1691,6 +1782,39 @@ function isAbortError3(err) {
1691
1782
 
1692
1783
  // src/adapters/google/manifest.ts
1693
1784
  var GOOGLE_KNOWN_MODELS = [
1785
+ {
1786
+ id: "gemini-3.6-flash",
1787
+ displayName: "Gemini 3.6 Flash",
1788
+ capabilities: {
1789
+ streaming: true,
1790
+ tools: true,
1791
+ vision: true,
1792
+ systemPrompt: true,
1793
+ maxContextTokens: 1e6
1794
+ }
1795
+ },
1796
+ {
1797
+ id: "gemini-3.5-flash",
1798
+ displayName: "Gemini 3.5 Flash",
1799
+ capabilities: {
1800
+ streaming: true,
1801
+ tools: true,
1802
+ vision: true,
1803
+ systemPrompt: true,
1804
+ maxContextTokens: 1e6
1805
+ }
1806
+ },
1807
+ {
1808
+ id: "gemini-3.5-flash-lite",
1809
+ displayName: "Gemini 3.5 Flash-Lite",
1810
+ capabilities: {
1811
+ streaming: true,
1812
+ tools: true,
1813
+ vision: true,
1814
+ systemPrompt: true,
1815
+ maxContextTokens: 1e6
1816
+ }
1817
+ },
1694
1818
  {
1695
1819
  id: "gemini-2.5-pro",
1696
1820
  displayName: "Gemini 2.5 Pro",
@@ -1834,6 +1958,39 @@ import { GoogleGenAI as GoogleGenAI2 } from "@google/genai";
1834
1958
 
1835
1959
  // src/adapters/vertex-gemini/manifest.ts
1836
1960
  var VERTEX_GEMINI_KNOWN_MODELS = [
1961
+ {
1962
+ id: "gemini-3.6-flash",
1963
+ displayName: "Gemini 3.6 Flash",
1964
+ capabilities: {
1965
+ streaming: true,
1966
+ tools: true,
1967
+ vision: true,
1968
+ systemPrompt: true,
1969
+ maxContextTokens: 1e6
1970
+ }
1971
+ },
1972
+ {
1973
+ id: "gemini-3.5-flash",
1974
+ displayName: "Gemini 3.5 Flash",
1975
+ capabilities: {
1976
+ streaming: true,
1977
+ tools: true,
1978
+ vision: true,
1979
+ systemPrompt: true,
1980
+ maxContextTokens: 1e6
1981
+ }
1982
+ },
1983
+ {
1984
+ id: "gemini-3.5-flash-lite",
1985
+ displayName: "Gemini 3.5 Flash-Lite",
1986
+ capabilities: {
1987
+ streaming: true,
1988
+ tools: true,
1989
+ vision: true,
1990
+ systemPrompt: true,
1991
+ maxContextTokens: 1e6
1992
+ }
1993
+ },
1837
1994
  {
1838
1995
  id: "gemini-2.5-pro",
1839
1996
  displayName: "Gemini 2.5 Pro",
@@ -2523,6 +2680,15 @@ var BedrockAdapter = class {
2523
2680
  if (Object.keys(inferenceConfig).length > 0) {
2524
2681
  commandInput.inferenceConfig = inferenceConfig;
2525
2682
  }
2683
+ if (req.cacheKey) {
2684
+ if (commandInput.system) {
2685
+ commandInput.system.push({ cachePoint: { type: "default" } });
2686
+ }
2687
+ if (tools) {
2688
+ tools.tools.push({ cachePoint: { type: "default" } });
2689
+ }
2690
+ markLastMessageForCaching2(messages);
2691
+ }
2526
2692
  const command = new ConverseStreamCommand(
2527
2693
  commandInput
2528
2694
  );
@@ -2546,6 +2712,8 @@ var BedrockAdapter = class {
2546
2712
  let inputTokens;
2547
2713
  let outputTokens;
2548
2714
  let totalTokens;
2715
+ let cacheReadTokens;
2716
+ let cacheWriteTokens;
2549
2717
  try {
2550
2718
  for await (const event of response.stream) {
2551
2719
  if (req.signal?.aborted) {
@@ -2598,6 +2766,8 @@ var BedrockAdapter = class {
2598
2766
  inputTokens = event.metadata.usage.inputTokens;
2599
2767
  outputTokens = event.metadata.usage.outputTokens;
2600
2768
  totalTokens = event.metadata.usage.totalTokens;
2769
+ cacheReadTokens = event.metadata.usage.cacheReadInputTokens;
2770
+ cacheWriteTokens = event.metadata.usage.cacheWriteInputTokens;
2601
2771
  }
2602
2772
  }
2603
2773
  }
@@ -2624,7 +2794,9 @@ var BedrockAdapter = class {
2624
2794
  const usage = inputTokens !== void 0 || outputTokens !== void 0 || totalTokens !== void 0 ? {
2625
2795
  ...inputTokens !== void 0 ? { inputTokens } : {},
2626
2796
  ...outputTokens !== void 0 ? { outputTokens } : {},
2627
- ...totalTokens !== void 0 ? { totalTokens } : {}
2797
+ ...totalTokens !== void 0 ? { totalTokens } : {},
2798
+ ...cacheReadTokens !== void 0 ? { cacheReadTokens } : {},
2799
+ ...cacheWriteTokens !== void 0 ? { cacheWriteTokens } : {}
2628
2800
  } : void 0;
2629
2801
  const endEvent = {
2630
2802
  type: "end",
@@ -2732,6 +2904,16 @@ function buildWireMessages3(req) {
2732
2904
  }
2733
2905
  return out;
2734
2906
  }
2907
+ function markLastMessageForCaching2(messages) {
2908
+ if (messages.length === 0) return;
2909
+ const last = messages[messages.length - 1];
2910
+ if (Array.isArray(last.content) && last.content.length > 0) {
2911
+ messages[messages.length - 1] = {
2912
+ ...last,
2913
+ content: [...last.content, { cachePoint: { type: "default" } }]
2914
+ };
2915
+ }
2916
+ }
2735
2917
  function formatTools4(tools) {
2736
2918
  return tools.map((t) => ({
2737
2919
  toolSpec: {
@@ -3087,7 +3269,10 @@ var GroqAdapter = class extends OpenAIAdapter {
3087
3269
  stream(req) {
3088
3270
  return streamFromOpenAI(this.ensureClient(), req, {
3089
3271
  knownModels: groqManifest.knownModels,
3090
- vendorMetadataExtractor: GROQ_METADATA_EXTRACTOR
3272
+ vendorMetadataExtractor: GROQ_METADATA_EXTRACTOR,
3273
+ // Groq caches automatically server-side and rejects/ignores undocumented
3274
+ // OpenAI params — do not forward prompt_cache_key.
3275
+ includeCacheKey: false
3091
3276
  });
3092
3277
  }
3093
3278
  };
@@ -3208,7 +3393,10 @@ var HuggingfaceAdapter = class extends OpenAIAdapter {
3208
3393
  stream(req) {
3209
3394
  return streamFromOpenAI(this.ensureClient(), req, {
3210
3395
  knownModels: huggingfaceManifest.knownModels,
3211
- vendorMetadataExtractor: HUGGINGFACE_METADATA_EXTRACTOR
3396
+ vendorMetadataExtractor: HUGGINGFACE_METADATA_EXTRACTOR,
3397
+ // The HF router fronts many third-party inference providers;
3398
+ // prompt_cache_key is unverified against them — do not forward.
3399
+ includeCacheKey: false
3212
3400
  });
3213
3401
  }
3214
3402
  };