@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.
@@ -271,10 +271,11 @@ async function* streamFromOpenAI(client, req, options) {
271
271
  }
272
272
  yield endEvent;
273
273
  }
274
- function appendOpenAIAssistantToolCall(history, toolCalls) {
274
+ function appendOpenAIAssistantToolCall(history, toolCalls, assistantText) {
275
+ const text = assistantText?.trim() ? assistantText : null;
275
276
  const wireMessage = {
276
277
  role: "assistant",
277
- content: null,
278
+ content: text,
278
279
  tool_calls: toolCalls.map((tc) => ({
279
280
  id: tc.id,
280
281
  type: "function",
@@ -288,7 +289,7 @@ function appendOpenAIAssistantToolCall(history, toolCalls) {
288
289
  ...history,
289
290
  {
290
291
  role: "assistant",
291
- content: "",
292
+ content: text ?? "",
292
293
  vendorRaw: wireMessage
293
294
  }
294
295
  ];
@@ -559,8 +560,8 @@ var OpenAIAdapter = class {
559
560
  includeCacheKey: this.forwardCacheKey()
560
561
  });
561
562
  }
562
- appendAssistantToolCall(history, toolCalls) {
563
- return appendOpenAIAssistantToolCall(history, toolCalls);
563
+ appendAssistantToolCall(history, toolCalls, assistantText) {
564
+ return appendOpenAIAssistantToolCall(history, toolCalls, assistantText);
564
565
  }
565
566
  appendToolResult(history, toolCallId, result) {
566
567
  return appendOpenAIToolResult(history, toolCallId, result);
@@ -1121,6 +1122,116 @@ var xaiFactory = {
1121
1122
  manifest: xaiManifest,
1122
1123
  create: () => new XaiAdapter()
1123
1124
  };
1125
+ var BEDROCK_MANTLE_DEFAULT_BASE_URL = "https://bedrock-mantle.us-east-1.api.aws/v1";
1126
+ var bedrockMantleManifest = {
1127
+ vendor: "bedrock-mantle",
1128
+ displayName: "AWS Bedrock (OpenAI-compatible)",
1129
+ authKinds: [
1130
+ {
1131
+ kind: "apiKey",
1132
+ displayName: "API Key",
1133
+ fields: [
1134
+ {
1135
+ name: "apiKey",
1136
+ label: "Bedrock API Key",
1137
+ type: "password",
1138
+ required: true,
1139
+ help: "Generate a long-term Bedrock API key in the AWS console (Amazon Bedrock \u2192 API keys)."
1140
+ },
1141
+ {
1142
+ name: "baseURL",
1143
+ label: "Base URL",
1144
+ type: "url",
1145
+ required: false,
1146
+ default: BEDROCK_MANTLE_DEFAULT_BASE_URL,
1147
+ help: "Change the region to match your key. xAI models are served only on the /openai/v1 path."
1148
+ }
1149
+ ]
1150
+ }
1151
+ ],
1152
+ /* A convenience seed, NOT the source of truth: the live `/v1/models`
1153
+ * endpoint is authoritative and grows continuously, which is why
1154
+ * `supportsModelListing` is true. Any model the endpoint lists can be used
1155
+ * by id without appearing here.
1156
+ *
1157
+ * AWS publishes no context-window metadata (neither `/v1/models` nor
1158
+ * `GetFoundationModel` returns it), so these limits are carried over from
1159
+ * the native-vendor manifests above for the same underlying models. Vision
1160
+ * follows Bedrock's `inputModalities`. */
1161
+ knownModels: [
1162
+ {
1163
+ id: "zai.glm-5",
1164
+ displayName: "GLM 5",
1165
+ capabilities: {
1166
+ streaming: true,
1167
+ tools: true,
1168
+ vision: false,
1169
+ systemPrompt: true,
1170
+ maxContextTokens: 202800
1171
+ }
1172
+ },
1173
+ {
1174
+ id: "zai.glm-4.7",
1175
+ displayName: "GLM 4.7",
1176
+ capabilities: {
1177
+ streaming: true,
1178
+ tools: true,
1179
+ vision: false,
1180
+ systemPrompt: true,
1181
+ maxContextTokens: 2e5
1182
+ }
1183
+ },
1184
+ {
1185
+ id: "moonshotai.kimi-k2.5",
1186
+ displayName: "Kimi K2.5",
1187
+ capabilities: {
1188
+ streaming: true,
1189
+ tools: true,
1190
+ vision: true,
1191
+ systemPrompt: true,
1192
+ maxContextTokens: 262144
1193
+ }
1194
+ },
1195
+ {
1196
+ id: "xai.grok-4.3",
1197
+ displayName: "Grok 4.3",
1198
+ capabilities: {
1199
+ streaming: true,
1200
+ tools: true,
1201
+ vision: true,
1202
+ systemPrompt: true,
1203
+ maxContextTokens: 1e6
1204
+ }
1205
+ }
1206
+ ],
1207
+ supportsModelListing: true,
1208
+ docsUrl: "https://docs.aws.amazon.com/bedrock/latest/userguide/inference-chat-completions.html"
1209
+ };
1210
+ var BedrockMantleAdapter = class extends OpenAIAdapter {
1211
+ vendor = bedrockMantleManifest.vendor;
1212
+ init(auth, client) {
1213
+ super.init(
1214
+ {
1215
+ ...auth,
1216
+ baseURL: auth.baseURL ?? BEDROCK_MANTLE_DEFAULT_BASE_URL
1217
+ },
1218
+ client
1219
+ );
1220
+ }
1221
+ knownModels() {
1222
+ return bedrockMantleManifest.knownModels;
1223
+ }
1224
+ // Mantle fronts many third-party runtimes with differing param strictness,
1225
+ // and prompt_cache_key is unverified against all of them — do not forward.
1226
+ forwardCacheKey() {
1227
+ return false;
1228
+ }
1229
+ };
1230
+ var bedrockMantleFactory = {
1231
+ vendor: bedrockMantleManifest.vendor,
1232
+ manifest: bedrockMantleManifest,
1233
+ create: () => new BedrockMantleAdapter()
1234
+ };
1124
1235
 
1125
1236
  // src/adapters/anthropic/adapter.ts
1126
1237
  var import_sdk = __toESM(require("@anthropic-ai/sdk"), 1);
@@ -1416,21 +1527,25 @@ var AnthropicAdapter = class {
1416
1527
  }
1417
1528
  yield endEvent;
1418
1529
  }
1419
- appendAssistantToolCall(history, toolCalls) {
1530
+ appendAssistantToolCall(history, toolCalls, assistantText) {
1531
+ const text = assistantText?.trim() ? assistantText : null;
1420
1532
  const wireMessage = {
1421
1533
  role: "assistant",
1422
- content: toolCalls.map((tc) => ({
1423
- type: "tool_use",
1424
- id: tc.id,
1425
- name: tc.name,
1426
- input: tc.arguments ?? {}
1427
- }))
1534
+ content: [
1535
+ ...text ? [{ type: "text", text }] : [],
1536
+ ...toolCalls.map((tc) => ({
1537
+ type: "tool_use",
1538
+ id: tc.id,
1539
+ name: tc.name,
1540
+ input: tc.arguments ?? {}
1541
+ }))
1542
+ ]
1428
1543
  };
1429
1544
  return [
1430
1545
  ...history,
1431
1546
  {
1432
1547
  role: "assistant",
1433
- content: "",
1548
+ content: text ?? "",
1434
1549
  vendorRaw: wireMessage
1435
1550
  }
1436
1551
  ];
@@ -1682,21 +1797,25 @@ async function* streamFromGemini(client, req) {
1682
1797
  };
1683
1798
  yield endEvent;
1684
1799
  }
1685
- function appendGeminiAssistantToolCall(history, toolCalls) {
1800
+ function appendGeminiAssistantToolCall(history, toolCalls, assistantText) {
1801
+ const text = assistantText?.trim() ? assistantText : null;
1686
1802
  const wireMessage = {
1687
1803
  role: "model",
1688
- parts: toolCalls.map((tc) => ({
1689
- functionCall: {
1690
- name: parseToolName(tc.id),
1691
- args: tc.arguments ?? {}
1692
- }
1693
- }))
1804
+ parts: [
1805
+ ...text ? [{ text }] : [],
1806
+ ...toolCalls.map((tc) => ({
1807
+ functionCall: {
1808
+ name: parseToolName(tc.id),
1809
+ args: tc.arguments ?? {}
1810
+ }
1811
+ }))
1812
+ ]
1694
1813
  };
1695
1814
  return [
1696
1815
  ...history,
1697
1816
  {
1698
1817
  role: "assistant",
1699
- content: "",
1818
+ content: text ?? "",
1700
1819
  vendorRaw: wireMessage
1701
1820
  }
1702
1821
  ];
@@ -1956,8 +2075,8 @@ var GoogleAdapter = class {
1956
2075
  stream(req) {
1957
2076
  return streamFromGemini(this.ensureClient(), req);
1958
2077
  }
1959
- appendAssistantToolCall(history, toolCalls) {
1960
- return appendGeminiAssistantToolCall(history, toolCalls);
2078
+ appendAssistantToolCall(history, toolCalls, assistantText) {
2079
+ return appendGeminiAssistantToolCall(history, toolCalls, assistantText);
1961
2080
  }
1962
2081
  appendToolResult(history, toolCallId, result) {
1963
2082
  return appendGeminiToolResult(history, toolCallId, result);
@@ -2162,8 +2281,8 @@ var VertexGeminiAdapter = class {
2162
2281
  stream(req) {
2163
2282
  return streamFromGemini(this.ensureClient(), req);
2164
2283
  }
2165
- appendAssistantToolCall(history, toolCalls) {
2166
- return appendGeminiAssistantToolCall(history, toolCalls);
2284
+ appendAssistantToolCall(history, toolCalls, assistantText) {
2285
+ return appendGeminiAssistantToolCall(history, toolCalls, assistantText);
2167
2286
  }
2168
2287
  appendToolResult(history, toolCallId, result) {
2169
2288
  return appendGeminiToolResult(history, toolCallId, result);
@@ -2360,8 +2479,8 @@ var VertexOpenAIAdapter = class {
2360
2479
  vendorMetadataExtractor: VERTEX_OPENAI_METADATA_EXTRACTOR
2361
2480
  });
2362
2481
  }
2363
- appendAssistantToolCall(history, toolCalls) {
2364
- return appendOpenAIAssistantToolCall(history, toolCalls);
2482
+ appendAssistantToolCall(history, toolCalls, assistantText) {
2483
+ return appendOpenAIAssistantToolCall(history, toolCalls, assistantText);
2365
2484
  }
2366
2485
  appendToolResult(history, toolCallId, result) {
2367
2486
  return appendOpenAIToolResult(history, toolCallId, result);
@@ -2847,22 +2966,26 @@ var BedrockAdapter = class {
2847
2966
  }
2848
2967
  yield endEvent;
2849
2968
  }
2850
- appendAssistantToolCall(history, toolCalls) {
2969
+ appendAssistantToolCall(history, toolCalls, assistantText) {
2970
+ const text = assistantText?.trim() ? assistantText : null;
2851
2971
  const wireMessage = {
2852
2972
  role: "assistant",
2853
- content: toolCalls.map((tc) => ({
2854
- toolUse: {
2855
- toolUseId: tc.id,
2856
- name: tc.name,
2857
- input: tc.arguments ?? {}
2858
- }
2859
- }))
2973
+ content: [
2974
+ ...text ? [{ text }] : [],
2975
+ ...toolCalls.map((tc) => ({
2976
+ toolUse: {
2977
+ toolUseId: tc.id,
2978
+ name: tc.name,
2979
+ input: tc.arguments ?? {}
2980
+ }
2981
+ }))
2982
+ ]
2860
2983
  };
2861
2984
  return [
2862
2985
  ...history,
2863
2986
  {
2864
2987
  role: "assistant",
2865
- content: "",
2988
+ content: text ?? "",
2866
2989
  vendorRaw: wireMessage
2867
2990
  }
2868
2991
  ];
@@ -3152,8 +3275,8 @@ var AzureOpenAIAdapter = class {
3152
3275
  vendorMetadataExtractor: AZURE_OPENAI_METADATA_EXTRACTOR
3153
3276
  });
3154
3277
  }
3155
- appendAssistantToolCall(history, toolCalls) {
3156
- return appendOpenAIAssistantToolCall(history, toolCalls);
3278
+ appendAssistantToolCall(history, toolCalls, assistantText) {
3279
+ return appendOpenAIAssistantToolCall(history, toolCalls, assistantText);
3157
3280
  }
3158
3281
  appendToolResult(history, toolCallId, result) {
3159
3282
  return appendOpenAIToolResult(history, toolCallId, result);
@@ -3441,6 +3564,7 @@ registerAdapter(basetenFactory);
3441
3564
  registerAdapter(moonshotFactory);
3442
3565
  registerAdapter(zaiFactory);
3443
3566
  registerAdapter(xaiFactory);
3567
+ registerAdapter(bedrockMantleFactory);
3444
3568
  registerAdapter(anthropicFactory);
3445
3569
  registerAdapter(googleFactory);
3446
3570
  registerAdapter(vertexGeminiFactory);
@@ -3807,6 +3931,55 @@ function runContractTests(harness) {
3807
3931
  (0, import_vitest.expect)(appended.role).toBe("assistant");
3808
3932
  (0, import_vitest.expect)(appended.vendorRaw).toBeDefined();
3809
3933
  });
3934
+ (0, import_vitest.it)("[22] appendAssistantToolCall carries pre-tool assistant text onto the wire", async () => {
3935
+ await harness.mockScenario("tool-call");
3936
+ const adapter = await init(harness);
3937
+ const first = await drainStream(adapter, {
3938
+ model: harness.toolCapableModel,
3939
+ messages: [{ role: "user", content: "go" }],
3940
+ tools: [
3941
+ { name: "test_tool", description: "x", parameters: { type: "object" } }
3942
+ ]
3943
+ });
3944
+ const tc = first.find(
3945
+ (e) => e.type === "toolCall"
3946
+ );
3947
+ (0, import_vitest.expect)(tc).toBeDefined();
3948
+ const preToolText = "Checking that now.";
3949
+ const history = adapter.appendAssistantToolCall(
3950
+ [{ role: "user", content: "go" }],
3951
+ [tc],
3952
+ preToolText
3953
+ );
3954
+ const appended = history[history.length - 1];
3955
+ (0, import_vitest.expect)(appended.content).toBe(preToolText);
3956
+ (0, import_vitest.expect)(JSON.stringify(appended.vendorRaw)).toContain(preToolText);
3957
+ });
3958
+ (0, import_vitest.it)("[23] appendAssistantToolCall omits empty pre-tool text from the wire", async () => {
3959
+ await harness.mockScenario("tool-call");
3960
+ const adapter = await init(harness);
3961
+ const first = await drainStream(adapter, {
3962
+ model: harness.toolCapableModel,
3963
+ messages: [{ role: "user", content: "go" }],
3964
+ tools: [
3965
+ { name: "test_tool", description: "x", parameters: { type: "object" } }
3966
+ ]
3967
+ });
3968
+ const tc = first.find(
3969
+ (e) => e.type === "toolCall"
3970
+ );
3971
+ (0, import_vitest.expect)(tc).toBeDefined();
3972
+ for (const blank of [void 0, "", " "]) {
3973
+ const history = adapter.appendAssistantToolCall(
3974
+ [{ role: "user", content: "go" }],
3975
+ [tc],
3976
+ blank
3977
+ );
3978
+ const appended = history[history.length - 1];
3979
+ (0, import_vitest.expect)(appended.content).toBe("");
3980
+ (0, import_vitest.expect)(JSON.stringify(appended.vendorRaw)).not.toContain('"text":""');
3981
+ }
3982
+ });
3810
3983
  });
3811
3984
  }
3812
3985
  async function init(harness) {
@@ -3871,13 +4044,15 @@ var FakeAdapter = class {
3871
4044
  throw new Error(`FakeAdapter: unknown scenario '${scenario}'`);
3872
4045
  }
3873
4046
  }
3874
- appendAssistantToolCall(history, toolCalls) {
4047
+ appendAssistantToolCall(history, toolCalls, assistantText) {
4048
+ const text = assistantText?.trim() ? assistantText : null;
3875
4049
  return [
3876
4050
  ...history,
3877
4051
  {
3878
4052
  role: "assistant",
3879
- content: "",
4053
+ content: text ?? "",
3880
4054
  vendorRaw: {
4055
+ ...text ? { text } : {},
3881
4056
  role: "assistant",
3882
4057
  toolCalls: toolCalls.map((tc) => ({
3883
4058
  id: tc.id,