@jambonz/llm 0.6.1 → 0.6.3

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-Y0o6rN3B.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-dbgrfviJ.cjs';
2
2
 
3
3
  /**
4
4
  * Named scenarios the harness must be able to stage. Each scenario configures
@@ -129,7 +129,7 @@ declare class FakeAdapter implements LlmAdapter<ApiKeyAuth> {
129
129
  constructor(state: FakeState);
130
130
  init(auth: ApiKeyAuth, _client?: ClientOptions): void;
131
131
  stream(req: PromptRequest): AsyncIterable<LlmEvent>;
132
- appendAssistantToolCall(history: Message[], toolCalls: ReadonlyArray<ToolCallEvent>): Message[];
132
+ appendAssistantToolCall(history: Message[], toolCalls: ReadonlyArray<ToolCallEvent>, assistantText?: string): Message[];
133
133
  appendToolResult(history: Message[], toolCallId: string, result: unknown): Message[];
134
134
  listAvailableModels(): Promise<ModelInfo[]>;
135
135
  testCredential(): Promise<void>;
@@ -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-Y0o6rN3B.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-dbgrfviJ.js';
2
2
 
3
3
  /**
4
4
  * Named scenarios the harness must be able to stage. Each scenario configures
@@ -129,7 +129,7 @@ declare class FakeAdapter implements LlmAdapter<ApiKeyAuth> {
129
129
  constructor(state: FakeState);
130
130
  init(auth: ApiKeyAuth, _client?: ClientOptions): void;
131
131
  stream(req: PromptRequest): AsyncIterable<LlmEvent>;
132
- appendAssistantToolCall(history: Message[], toolCalls: ReadonlyArray<ToolCallEvent>): Message[];
132
+ appendAssistantToolCall(history: Message[], toolCalls: ReadonlyArray<ToolCallEvent>, assistantText?: string): Message[];
133
133
  appendToolResult(history: Message[], toolCallId: string, result: unknown): Message[];
134
134
  listAvailableModels(): Promise<ModelInfo[]>;
135
135
  testCredential(): Promise<void>;
@@ -232,10 +232,11 @@ async function* streamFromOpenAI(client, req, options) {
232
232
  }
233
233
  yield endEvent;
234
234
  }
235
- function appendOpenAIAssistantToolCall(history, toolCalls) {
235
+ function appendOpenAIAssistantToolCall(history, toolCalls, assistantText) {
236
+ const text = assistantText?.trim() ? assistantText : null;
236
237
  const wireMessage = {
237
238
  role: "assistant",
238
- content: null,
239
+ content: text,
239
240
  tool_calls: toolCalls.map((tc) => ({
240
241
  id: tc.id,
241
242
  type: "function",
@@ -249,7 +250,7 @@ function appendOpenAIAssistantToolCall(history, toolCalls) {
249
250
  ...history,
250
251
  {
251
252
  role: "assistant",
252
- content: "",
253
+ content: text ?? "",
253
254
  vendorRaw: wireMessage
254
255
  }
255
256
  ];
@@ -520,8 +521,8 @@ var OpenAIAdapter = class {
520
521
  includeCacheKey: this.forwardCacheKey()
521
522
  });
522
523
  }
523
- appendAssistantToolCall(history, toolCalls) {
524
- return appendOpenAIAssistantToolCall(history, toolCalls);
524
+ appendAssistantToolCall(history, toolCalls, assistantText) {
525
+ return appendOpenAIAssistantToolCall(history, toolCalls, assistantText);
525
526
  }
526
527
  appendToolResult(history, toolCallId, result) {
527
528
  return appendOpenAIToolResult(history, toolCallId, result);
@@ -1082,6 +1083,116 @@ var xaiFactory = {
1082
1083
  manifest: xaiManifest,
1083
1084
  create: () => new XaiAdapter()
1084
1085
  };
1086
+ var BEDROCK_MANTLE_DEFAULT_BASE_URL = "https://bedrock-mantle.us-east-1.api.aws/v1";
1087
+ var bedrockMantleManifest = {
1088
+ vendor: "bedrock-mantle",
1089
+ displayName: "AWS Bedrock (OpenAI-compatible)",
1090
+ authKinds: [
1091
+ {
1092
+ kind: "apiKey",
1093
+ displayName: "API Key",
1094
+ fields: [
1095
+ {
1096
+ name: "apiKey",
1097
+ label: "Bedrock API Key",
1098
+ type: "password",
1099
+ required: true,
1100
+ help: "Generate a long-term Bedrock API key in the AWS console (Amazon Bedrock \u2192 API keys)."
1101
+ },
1102
+ {
1103
+ name: "baseURL",
1104
+ label: "Base URL",
1105
+ type: "url",
1106
+ required: false,
1107
+ default: BEDROCK_MANTLE_DEFAULT_BASE_URL,
1108
+ help: "Change the region to match your key. xAI models are served only on the /openai/v1 path."
1109
+ }
1110
+ ]
1111
+ }
1112
+ ],
1113
+ /* A convenience seed, NOT the source of truth: the live `/v1/models`
1114
+ * endpoint is authoritative and grows continuously, which is why
1115
+ * `supportsModelListing` is true. Any model the endpoint lists can be used
1116
+ * by id without appearing here.
1117
+ *
1118
+ * AWS publishes no context-window metadata (neither `/v1/models` nor
1119
+ * `GetFoundationModel` returns it), so these limits are carried over from
1120
+ * the native-vendor manifests above for the same underlying models. Vision
1121
+ * follows Bedrock's `inputModalities`. */
1122
+ knownModels: [
1123
+ {
1124
+ id: "zai.glm-5",
1125
+ displayName: "GLM 5",
1126
+ capabilities: {
1127
+ streaming: true,
1128
+ tools: true,
1129
+ vision: false,
1130
+ systemPrompt: true,
1131
+ maxContextTokens: 202800
1132
+ }
1133
+ },
1134
+ {
1135
+ id: "zai.glm-4.7",
1136
+ displayName: "GLM 4.7",
1137
+ capabilities: {
1138
+ streaming: true,
1139
+ tools: true,
1140
+ vision: false,
1141
+ systemPrompt: true,
1142
+ maxContextTokens: 2e5
1143
+ }
1144
+ },
1145
+ {
1146
+ id: "moonshotai.kimi-k2.5",
1147
+ displayName: "Kimi K2.5",
1148
+ capabilities: {
1149
+ streaming: true,
1150
+ tools: true,
1151
+ vision: true,
1152
+ systemPrompt: true,
1153
+ maxContextTokens: 262144
1154
+ }
1155
+ },
1156
+ {
1157
+ id: "xai.grok-4.3",
1158
+ displayName: "Grok 4.3",
1159
+ capabilities: {
1160
+ streaming: true,
1161
+ tools: true,
1162
+ vision: true,
1163
+ systemPrompt: true,
1164
+ maxContextTokens: 1e6
1165
+ }
1166
+ }
1167
+ ],
1168
+ supportsModelListing: true,
1169
+ docsUrl: "https://docs.aws.amazon.com/bedrock/latest/userguide/inference-chat-completions.html"
1170
+ };
1171
+ var BedrockMantleAdapter = class extends OpenAIAdapter {
1172
+ vendor = bedrockMantleManifest.vendor;
1173
+ init(auth, client) {
1174
+ super.init(
1175
+ {
1176
+ ...auth,
1177
+ baseURL: auth.baseURL ?? BEDROCK_MANTLE_DEFAULT_BASE_URL
1178
+ },
1179
+ client
1180
+ );
1181
+ }
1182
+ knownModels() {
1183
+ return bedrockMantleManifest.knownModels;
1184
+ }
1185
+ // Mantle fronts many third-party runtimes with differing param strictness,
1186
+ // and prompt_cache_key is unverified against all of them — do not forward.
1187
+ forwardCacheKey() {
1188
+ return false;
1189
+ }
1190
+ };
1191
+ var bedrockMantleFactory = {
1192
+ vendor: bedrockMantleManifest.vendor,
1193
+ manifest: bedrockMantleManifest,
1194
+ create: () => new BedrockMantleAdapter()
1195
+ };
1085
1196
 
1086
1197
  // src/adapters/anthropic/adapter.ts
1087
1198
  import Anthropic from "@anthropic-ai/sdk";
@@ -1377,21 +1488,25 @@ var AnthropicAdapter = class {
1377
1488
  }
1378
1489
  yield endEvent;
1379
1490
  }
1380
- appendAssistantToolCall(history, toolCalls) {
1491
+ appendAssistantToolCall(history, toolCalls, assistantText) {
1492
+ const text = assistantText?.trim() ? assistantText : null;
1381
1493
  const wireMessage = {
1382
1494
  role: "assistant",
1383
- content: toolCalls.map((tc) => ({
1384
- type: "tool_use",
1385
- id: tc.id,
1386
- name: tc.name,
1387
- input: tc.arguments ?? {}
1388
- }))
1495
+ content: [
1496
+ ...text ? [{ type: "text", text }] : [],
1497
+ ...toolCalls.map((tc) => ({
1498
+ type: "tool_use",
1499
+ id: tc.id,
1500
+ name: tc.name,
1501
+ input: tc.arguments ?? {}
1502
+ }))
1503
+ ]
1389
1504
  };
1390
1505
  return [
1391
1506
  ...history,
1392
1507
  {
1393
1508
  role: "assistant",
1394
- content: "",
1509
+ content: text ?? "",
1395
1510
  vendorRaw: wireMessage
1396
1511
  }
1397
1512
  ];
@@ -1643,21 +1758,25 @@ async function* streamFromGemini(client, req) {
1643
1758
  };
1644
1759
  yield endEvent;
1645
1760
  }
1646
- function appendGeminiAssistantToolCall(history, toolCalls) {
1761
+ function appendGeminiAssistantToolCall(history, toolCalls, assistantText) {
1762
+ const text = assistantText?.trim() ? assistantText : null;
1647
1763
  const wireMessage = {
1648
1764
  role: "model",
1649
- parts: toolCalls.map((tc) => ({
1650
- functionCall: {
1651
- name: parseToolName(tc.id),
1652
- args: tc.arguments ?? {}
1653
- }
1654
- }))
1765
+ parts: [
1766
+ ...text ? [{ text }] : [],
1767
+ ...toolCalls.map((tc) => ({
1768
+ functionCall: {
1769
+ name: parseToolName(tc.id),
1770
+ args: tc.arguments ?? {}
1771
+ }
1772
+ }))
1773
+ ]
1655
1774
  };
1656
1775
  return [
1657
1776
  ...history,
1658
1777
  {
1659
1778
  role: "assistant",
1660
- content: "",
1779
+ content: text ?? "",
1661
1780
  vendorRaw: wireMessage
1662
1781
  }
1663
1782
  ];
@@ -1917,8 +2036,8 @@ var GoogleAdapter = class {
1917
2036
  stream(req) {
1918
2037
  return streamFromGemini(this.ensureClient(), req);
1919
2038
  }
1920
- appendAssistantToolCall(history, toolCalls) {
1921
- return appendGeminiAssistantToolCall(history, toolCalls);
2039
+ appendAssistantToolCall(history, toolCalls, assistantText) {
2040
+ return appendGeminiAssistantToolCall(history, toolCalls, assistantText);
1922
2041
  }
1923
2042
  appendToolResult(history, toolCallId, result) {
1924
2043
  return appendGeminiToolResult(history, toolCallId, result);
@@ -2123,8 +2242,8 @@ var VertexGeminiAdapter = class {
2123
2242
  stream(req) {
2124
2243
  return streamFromGemini(this.ensureClient(), req);
2125
2244
  }
2126
- appendAssistantToolCall(history, toolCalls) {
2127
- return appendGeminiAssistantToolCall(history, toolCalls);
2245
+ appendAssistantToolCall(history, toolCalls, assistantText) {
2246
+ return appendGeminiAssistantToolCall(history, toolCalls, assistantText);
2128
2247
  }
2129
2248
  appendToolResult(history, toolCallId, result) {
2130
2249
  return appendGeminiToolResult(history, toolCallId, result);
@@ -2321,8 +2440,8 @@ var VertexOpenAIAdapter = class {
2321
2440
  vendorMetadataExtractor: VERTEX_OPENAI_METADATA_EXTRACTOR
2322
2441
  });
2323
2442
  }
2324
- appendAssistantToolCall(history, toolCalls) {
2325
- return appendOpenAIAssistantToolCall(history, toolCalls);
2443
+ appendAssistantToolCall(history, toolCalls, assistantText) {
2444
+ return appendOpenAIAssistantToolCall(history, toolCalls, assistantText);
2326
2445
  }
2327
2446
  appendToolResult(history, toolCallId, result) {
2328
2447
  return appendOpenAIToolResult(history, toolCallId, result);
@@ -2814,22 +2933,26 @@ var BedrockAdapter = class {
2814
2933
  }
2815
2934
  yield endEvent;
2816
2935
  }
2817
- appendAssistantToolCall(history, toolCalls) {
2936
+ appendAssistantToolCall(history, toolCalls, assistantText) {
2937
+ const text = assistantText?.trim() ? assistantText : null;
2818
2938
  const wireMessage = {
2819
2939
  role: "assistant",
2820
- content: toolCalls.map((tc) => ({
2821
- toolUse: {
2822
- toolUseId: tc.id,
2823
- name: tc.name,
2824
- input: tc.arguments ?? {}
2825
- }
2826
- }))
2940
+ content: [
2941
+ ...text ? [{ text }] : [],
2942
+ ...toolCalls.map((tc) => ({
2943
+ toolUse: {
2944
+ toolUseId: tc.id,
2945
+ name: tc.name,
2946
+ input: tc.arguments ?? {}
2947
+ }
2948
+ }))
2949
+ ]
2827
2950
  };
2828
2951
  return [
2829
2952
  ...history,
2830
2953
  {
2831
2954
  role: "assistant",
2832
- content: "",
2955
+ content: text ?? "",
2833
2956
  vendorRaw: wireMessage
2834
2957
  }
2835
2958
  ];
@@ -3119,8 +3242,8 @@ var AzureOpenAIAdapter = class {
3119
3242
  vendorMetadataExtractor: AZURE_OPENAI_METADATA_EXTRACTOR
3120
3243
  });
3121
3244
  }
3122
- appendAssistantToolCall(history, toolCalls) {
3123
- return appendOpenAIAssistantToolCall(history, toolCalls);
3245
+ appendAssistantToolCall(history, toolCalls, assistantText) {
3246
+ return appendOpenAIAssistantToolCall(history, toolCalls, assistantText);
3124
3247
  }
3125
3248
  appendToolResult(history, toolCallId, result) {
3126
3249
  return appendOpenAIToolResult(history, toolCallId, result);
@@ -3408,6 +3531,7 @@ registerAdapter(basetenFactory);
3408
3531
  registerAdapter(moonshotFactory);
3409
3532
  registerAdapter(zaiFactory);
3410
3533
  registerAdapter(xaiFactory);
3534
+ registerAdapter(bedrockMantleFactory);
3411
3535
  registerAdapter(anthropicFactory);
3412
3536
  registerAdapter(googleFactory);
3413
3537
  registerAdapter(vertexGeminiFactory);
@@ -3774,6 +3898,55 @@ function runContractTests(harness) {
3774
3898
  expect(appended.role).toBe("assistant");
3775
3899
  expect(appended.vendorRaw).toBeDefined();
3776
3900
  });
3901
+ it("[22] appendAssistantToolCall carries pre-tool assistant text onto the wire", async () => {
3902
+ await harness.mockScenario("tool-call");
3903
+ const adapter = await init(harness);
3904
+ const first = await drainStream(adapter, {
3905
+ model: harness.toolCapableModel,
3906
+ messages: [{ role: "user", content: "go" }],
3907
+ tools: [
3908
+ { name: "test_tool", description: "x", parameters: { type: "object" } }
3909
+ ]
3910
+ });
3911
+ const tc = first.find(
3912
+ (e) => e.type === "toolCall"
3913
+ );
3914
+ expect(tc).toBeDefined();
3915
+ const preToolText = "Checking that now.";
3916
+ const history = adapter.appendAssistantToolCall(
3917
+ [{ role: "user", content: "go" }],
3918
+ [tc],
3919
+ preToolText
3920
+ );
3921
+ const appended = history[history.length - 1];
3922
+ expect(appended.content).toBe(preToolText);
3923
+ expect(JSON.stringify(appended.vendorRaw)).toContain(preToolText);
3924
+ });
3925
+ it("[23] appendAssistantToolCall omits empty pre-tool text from the wire", async () => {
3926
+ await harness.mockScenario("tool-call");
3927
+ const adapter = await init(harness);
3928
+ const first = await drainStream(adapter, {
3929
+ model: harness.toolCapableModel,
3930
+ messages: [{ role: "user", content: "go" }],
3931
+ tools: [
3932
+ { name: "test_tool", description: "x", parameters: { type: "object" } }
3933
+ ]
3934
+ });
3935
+ const tc = first.find(
3936
+ (e) => e.type === "toolCall"
3937
+ );
3938
+ expect(tc).toBeDefined();
3939
+ for (const blank of [void 0, "", " "]) {
3940
+ const history = adapter.appendAssistantToolCall(
3941
+ [{ role: "user", content: "go" }],
3942
+ [tc],
3943
+ blank
3944
+ );
3945
+ const appended = history[history.length - 1];
3946
+ expect(appended.content).toBe("");
3947
+ expect(JSON.stringify(appended.vendorRaw)).not.toContain('"text":""');
3948
+ }
3949
+ });
3777
3950
  });
3778
3951
  }
3779
3952
  async function init(harness) {
@@ -3838,13 +4011,15 @@ var FakeAdapter = class {
3838
4011
  throw new Error(`FakeAdapter: unknown scenario '${scenario}'`);
3839
4012
  }
3840
4013
  }
3841
- appendAssistantToolCall(history, toolCalls) {
4014
+ appendAssistantToolCall(history, toolCalls, assistantText) {
4015
+ const text = assistantText?.trim() ? assistantText : null;
3842
4016
  return [
3843
4017
  ...history,
3844
4018
  {
3845
4019
  role: "assistant",
3846
- content: "",
4020
+ content: text ?? "",
3847
4021
  vendorRaw: {
4022
+ ...text ? { text } : {},
3848
4023
  role: "assistant",
3849
4024
  toolCalls: toolCalls.map((tc) => ({
3850
4025
  id: tc.id,