@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.
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];
@@ -1189,6 +1197,16 @@ var AnthropicAdapter = class {
1189
1197
  if (req.system) body.system = req.system;
1190
1198
  if (tools) body.tools = tools;
1191
1199
  if (req.temperature !== void 0) body.temperature = req.temperature;
1200
+ if (req.cacheKey) {
1201
+ if (req.system) {
1202
+ body.system = [{ type: "text", text: req.system, cache_control: { type: "ephemeral" } }];
1203
+ }
1204
+ if (tools) {
1205
+ const lastTool = tools[tools.length - 1];
1206
+ lastTool.cache_control = { type: "ephemeral" };
1207
+ }
1208
+ markLastMessageForCaching(messages);
1209
+ }
1192
1210
  let stream;
1193
1211
  let responseHeaders;
1194
1212
  try {
@@ -1216,6 +1234,8 @@ var AnthropicAdapter = class {
1216
1234
  let stopReason;
1217
1235
  let inputTokens;
1218
1236
  let outputTokens;
1237
+ let cacheReadTokens;
1238
+ let cacheWriteTokens;
1219
1239
  try {
1220
1240
  for await (const event of stream) {
1221
1241
  if (req.signal?.aborted) {
@@ -1225,6 +1245,8 @@ var AnthropicAdapter = class {
1225
1245
  switch (event.type) {
1226
1246
  case "message_start":
1227
1247
  inputTokens = event.message?.usage?.input_tokens;
1248
+ cacheReadTokens = event.message?.usage?.cache_read_input_tokens;
1249
+ cacheWriteTokens = event.message?.usage?.cache_creation_input_tokens;
1228
1250
  break;
1229
1251
  case "content_block_start": {
1230
1252
  const block = event.content_block;
@@ -1306,7 +1328,11 @@ var AnthropicAdapter = class {
1306
1328
  const usage = inputTokens !== void 0 || outputTokens !== void 0 ? {
1307
1329
  ...inputTokens !== void 0 ? { inputTokens } : {},
1308
1330
  ...outputTokens !== void 0 ? { outputTokens } : {},
1309
- ...inputTokens !== void 0 && outputTokens !== void 0 ? { totalTokens: inputTokens + outputTokens } : {}
1331
+ ...inputTokens !== void 0 && outputTokens !== void 0 ? {
1332
+ totalTokens: inputTokens + outputTokens + (cacheReadTokens ?? 0) + (cacheWriteTokens ?? 0)
1333
+ } : {},
1334
+ ...cacheReadTokens !== void 0 ? { cacheReadTokens } : {},
1335
+ ...cacheWriteTokens !== void 0 ? { cacheWriteTokens } : {}
1310
1336
  } : void 0;
1311
1337
  const endEvent = {
1312
1338
  type: "end",
@@ -1405,6 +1431,23 @@ function buildWireMessages2(req) {
1405
1431
  }
1406
1432
  return out;
1407
1433
  }
1434
+ function markLastMessageForCaching(messages) {
1435
+ if (messages.length === 0) return;
1436
+ const last = messages[messages.length - 1];
1437
+ const cacheControl = { cache_control: { type: "ephemeral" } };
1438
+ if (typeof last.content === "string" && last.content.length > 0) {
1439
+ messages[messages.length - 1] = {
1440
+ ...last,
1441
+ content: [{ type: "text", text: last.content, ...cacheControl }]
1442
+ };
1443
+ } else if (Array.isArray(last.content) && last.content.length > 0) {
1444
+ const blocks = last.content;
1445
+ messages[messages.length - 1] = {
1446
+ ...last,
1447
+ content: [...blocks.slice(0, -1), { ...blocks[blocks.length - 1], ...cacheControl }]
1448
+ };
1449
+ }
1450
+ }
1408
1451
  function formatTools2(tools) {
1409
1452
  return tools.map((t) => ({
1410
1453
  name: t.name,
@@ -1467,6 +1510,11 @@ async function* streamFromGemini(client, req) {
1467
1510
  if (req.temperature !== void 0) config.temperature = req.temperature;
1468
1511
  if (req.maxTokens !== void 0) config.maxOutputTokens = req.maxTokens;
1469
1512
  if (req.signal) config.abortSignal = req.signal;
1513
+ if (req.providerParams) {
1514
+ for (const [key, value] of Object.entries(req.providerParams)) {
1515
+ if (!(key in config)) config[key] = value;
1516
+ }
1517
+ }
1470
1518
  let stream;
1471
1519
  try {
1472
1520
  stream = await client.models.generateContentStream({
@@ -1487,6 +1535,7 @@ async function* streamFromGemini(client, req) {
1487
1535
  let inputTokens;
1488
1536
  let outputTokens;
1489
1537
  let totalTokens;
1538
+ let cacheReadTokens;
1490
1539
  try {
1491
1540
  for await (const chunk of stream) {
1492
1541
  if (req.signal?.aborted) {
@@ -1495,7 +1544,12 @@ async function* streamFromGemini(client, req) {
1495
1544
  }
1496
1545
  const usage2 = chunk.usageMetadata;
1497
1546
  if (usage2) {
1498
- if (usage2.promptTokenCount !== void 0) inputTokens = usage2.promptTokenCount;
1547
+ if (usage2.cachedContentTokenCount !== void 0) {
1548
+ cacheReadTokens = usage2.cachedContentTokenCount;
1549
+ }
1550
+ if (usage2.promptTokenCount !== void 0) {
1551
+ inputTokens = usage2.promptTokenCount - (usage2.cachedContentTokenCount ?? 0);
1552
+ }
1499
1553
  if (usage2.candidatesTokenCount !== void 0) outputTokens = usage2.candidatesTokenCount;
1500
1554
  if (usage2.totalTokenCount !== void 0) totalTokens = usage2.totalTokenCount;
1501
1555
  }
@@ -1549,7 +1603,8 @@ async function* streamFromGemini(client, req) {
1549
1603
  const usage = inputTokens !== void 0 || outputTokens !== void 0 || totalTokens !== void 0 ? {
1550
1604
  ...inputTokens !== void 0 ? { inputTokens } : {},
1551
1605
  ...outputTokens !== void 0 ? { outputTokens } : {},
1552
- ...totalTokens !== void 0 ? { totalTokens } : {}
1606
+ ...totalTokens !== void 0 ? { totalTokens } : {},
1607
+ ...cacheReadTokens !== void 0 ? { cacheReadTokens } : {}
1553
1608
  } : void 0;
1554
1609
  const endEvent = {
1555
1610
  type: "end",
@@ -1691,6 +1746,39 @@ function isAbortError3(err) {
1691
1746
 
1692
1747
  // src/adapters/google/manifest.ts
1693
1748
  var GOOGLE_KNOWN_MODELS = [
1749
+ {
1750
+ id: "gemini-3.6-flash",
1751
+ displayName: "Gemini 3.6 Flash",
1752
+ capabilities: {
1753
+ streaming: true,
1754
+ tools: true,
1755
+ vision: true,
1756
+ systemPrompt: true,
1757
+ maxContextTokens: 1e6
1758
+ }
1759
+ },
1760
+ {
1761
+ id: "gemini-3.5-flash",
1762
+ displayName: "Gemini 3.5 Flash",
1763
+ capabilities: {
1764
+ streaming: true,
1765
+ tools: true,
1766
+ vision: true,
1767
+ systemPrompt: true,
1768
+ maxContextTokens: 1e6
1769
+ }
1770
+ },
1771
+ {
1772
+ id: "gemini-3.5-flash-lite",
1773
+ displayName: "Gemini 3.5 Flash-Lite",
1774
+ capabilities: {
1775
+ streaming: true,
1776
+ tools: true,
1777
+ vision: true,
1778
+ systemPrompt: true,
1779
+ maxContextTokens: 1e6
1780
+ }
1781
+ },
1694
1782
  {
1695
1783
  id: "gemini-2.5-pro",
1696
1784
  displayName: "Gemini 2.5 Pro",
@@ -1834,6 +1922,39 @@ import { GoogleGenAI as GoogleGenAI2 } from "@google/genai";
1834
1922
 
1835
1923
  // src/adapters/vertex-gemini/manifest.ts
1836
1924
  var VERTEX_GEMINI_KNOWN_MODELS = [
1925
+ {
1926
+ id: "gemini-3.6-flash",
1927
+ displayName: "Gemini 3.6 Flash",
1928
+ capabilities: {
1929
+ streaming: true,
1930
+ tools: true,
1931
+ vision: true,
1932
+ systemPrompt: true,
1933
+ maxContextTokens: 1e6
1934
+ }
1935
+ },
1936
+ {
1937
+ id: "gemini-3.5-flash",
1938
+ displayName: "Gemini 3.5 Flash",
1939
+ capabilities: {
1940
+ streaming: true,
1941
+ tools: true,
1942
+ vision: true,
1943
+ systemPrompt: true,
1944
+ maxContextTokens: 1e6
1945
+ }
1946
+ },
1947
+ {
1948
+ id: "gemini-3.5-flash-lite",
1949
+ displayName: "Gemini 3.5 Flash-Lite",
1950
+ capabilities: {
1951
+ streaming: true,
1952
+ tools: true,
1953
+ vision: true,
1954
+ systemPrompt: true,
1955
+ maxContextTokens: 1e6
1956
+ }
1957
+ },
1837
1958
  {
1838
1959
  id: "gemini-2.5-pro",
1839
1960
  displayName: "Gemini 2.5 Pro",
@@ -2523,6 +2644,15 @@ var BedrockAdapter = class {
2523
2644
  if (Object.keys(inferenceConfig).length > 0) {
2524
2645
  commandInput.inferenceConfig = inferenceConfig;
2525
2646
  }
2647
+ if (req.cacheKey) {
2648
+ if (commandInput.system) {
2649
+ commandInput.system.push({ cachePoint: { type: "default" } });
2650
+ }
2651
+ if (tools) {
2652
+ tools.tools.push({ cachePoint: { type: "default" } });
2653
+ }
2654
+ markLastMessageForCaching2(messages);
2655
+ }
2526
2656
  const command = new ConverseStreamCommand(
2527
2657
  commandInput
2528
2658
  );
@@ -2546,6 +2676,8 @@ var BedrockAdapter = class {
2546
2676
  let inputTokens;
2547
2677
  let outputTokens;
2548
2678
  let totalTokens;
2679
+ let cacheReadTokens;
2680
+ let cacheWriteTokens;
2549
2681
  try {
2550
2682
  for await (const event of response.stream) {
2551
2683
  if (req.signal?.aborted) {
@@ -2598,6 +2730,8 @@ var BedrockAdapter = class {
2598
2730
  inputTokens = event.metadata.usage.inputTokens;
2599
2731
  outputTokens = event.metadata.usage.outputTokens;
2600
2732
  totalTokens = event.metadata.usage.totalTokens;
2733
+ cacheReadTokens = event.metadata.usage.cacheReadInputTokens;
2734
+ cacheWriteTokens = event.metadata.usage.cacheWriteInputTokens;
2601
2735
  }
2602
2736
  }
2603
2737
  }
@@ -2624,7 +2758,9 @@ var BedrockAdapter = class {
2624
2758
  const usage = inputTokens !== void 0 || outputTokens !== void 0 || totalTokens !== void 0 ? {
2625
2759
  ...inputTokens !== void 0 ? { inputTokens } : {},
2626
2760
  ...outputTokens !== void 0 ? { outputTokens } : {},
2627
- ...totalTokens !== void 0 ? { totalTokens } : {}
2761
+ ...totalTokens !== void 0 ? { totalTokens } : {},
2762
+ ...cacheReadTokens !== void 0 ? { cacheReadTokens } : {},
2763
+ ...cacheWriteTokens !== void 0 ? { cacheWriteTokens } : {}
2628
2764
  } : void 0;
2629
2765
  const endEvent = {
2630
2766
  type: "end",
@@ -2732,6 +2868,16 @@ function buildWireMessages3(req) {
2732
2868
  }
2733
2869
  return out;
2734
2870
  }
2871
+ function markLastMessageForCaching2(messages) {
2872
+ if (messages.length === 0) return;
2873
+ const last = messages[messages.length - 1];
2874
+ if (Array.isArray(last.content) && last.content.length > 0) {
2875
+ messages[messages.length - 1] = {
2876
+ ...last,
2877
+ content: [...last.content, { cachePoint: { type: "default" } }]
2878
+ };
2879
+ }
2880
+ }
2735
2881
  function formatTools4(tools) {
2736
2882
  return tools.map((t) => ({
2737
2883
  toolSpec: {
@@ -3087,7 +3233,10 @@ var GroqAdapter = class extends OpenAIAdapter {
3087
3233
  stream(req) {
3088
3234
  return streamFromOpenAI(this.ensureClient(), req, {
3089
3235
  knownModels: groqManifest.knownModels,
3090
- vendorMetadataExtractor: GROQ_METADATA_EXTRACTOR
3236
+ vendorMetadataExtractor: GROQ_METADATA_EXTRACTOR,
3237
+ // Groq caches automatically server-side and rejects/ignores undocumented
3238
+ // OpenAI params — do not forward prompt_cache_key.
3239
+ includeCacheKey: false
3091
3240
  });
3092
3241
  }
3093
3242
  };