@jambonz/llm 0.1.0 → 0.2.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.
@@ -1,4 +1,4 @@
1
- import { a as AdapterFactory, d as AuthKind, A as AuthSpec, l as LlmEvent, M as Message, L as LlmAdapter, c as ApiKeyAuth, g as ClientOptions, P as PromptRequest, n as ModelInfo, b as AdapterManifest } from '../types-D8rgMCbi.cjs';
1
+ import { a as AdapterFactory, d as AuthKind, A as AuthSpec, L as LlmAdapter, c as ApiKeyAuth, g as ClientOptions, P as PromptRequest, l as LlmEvent, M as Message, o as ToolCallEvent, n as ModelInfo, b as AdapterManifest } from '../types-bq1XMi_T.cjs';
2
2
 
3
3
  /**
4
4
  * Named scenarios the harness must be able to stage. Each scenario configures
@@ -88,19 +88,6 @@ interface ContractHarness {
88
88
  * (toolCallStart precedes toolCall) runs; if false, that check is skipped.
89
89
  */
90
90
  emitsToolCallStart: boolean;
91
- /**
92
- * Construct an assistant `Message` (with `vendorRaw` populated in the shape
93
- * THIS adapter expects back on re-submission) given a `toolCall` event the
94
- * adapter previously emitted. Used by checks #13 and #20 to round-trip a
95
- * realistic history through a subsequent `stream()` call.
96
- *
97
- * Implementation tip: set `vendorRaw` to the full vendor-native assistant
98
- * message — the same shape your adapter puts in history after emitting a
99
- * tool call during streaming.
100
- */
101
- buildAssistantWithToolCall(tc: Extract<LlmEvent, {
102
- type: 'toolCall';
103
- }>): Message;
104
91
  }
105
92
 
106
93
  /**
@@ -142,6 +129,7 @@ declare class FakeAdapter implements LlmAdapter<ApiKeyAuth> {
142
129
  constructor(state: FakeState);
143
130
  init(auth: ApiKeyAuth, _client?: ClientOptions): void;
144
131
  stream(req: PromptRequest): AsyncIterable<LlmEvent>;
132
+ appendAssistantToolCall(history: Message[], toolCalls: ReadonlyArray<ToolCallEvent>): Message[];
145
133
  appendToolResult(history: Message[], toolCallId: string, result: unknown): Message[];
146
134
  listAvailableModels(): Promise<ModelInfo[]>;
147
135
  private ensureInitialized;
@@ -1,4 +1,4 @@
1
- import { a as AdapterFactory, d as AuthKind, A as AuthSpec, l as LlmEvent, M as Message, L as LlmAdapter, c as ApiKeyAuth, g as ClientOptions, P as PromptRequest, n as ModelInfo, b as AdapterManifest } from '../types-D8rgMCbi.js';
1
+ import { a as AdapterFactory, d as AuthKind, A as AuthSpec, L as LlmAdapter, c as ApiKeyAuth, g as ClientOptions, P as PromptRequest, l as LlmEvent, M as Message, o as ToolCallEvent, n as ModelInfo, b as AdapterManifest } from '../types-bq1XMi_T.js';
2
2
 
3
3
  /**
4
4
  * Named scenarios the harness must be able to stage. Each scenario configures
@@ -88,19 +88,6 @@ interface ContractHarness {
88
88
  * (toolCallStart precedes toolCall) runs; if false, that check is skipped.
89
89
  */
90
90
  emitsToolCallStart: boolean;
91
- /**
92
- * Construct an assistant `Message` (with `vendorRaw` populated in the shape
93
- * THIS adapter expects back on re-submission) given a `toolCall` event the
94
- * adapter previously emitted. Used by checks #13 and #20 to round-trip a
95
- * realistic history through a subsequent `stream()` call.
96
- *
97
- * Implementation tip: set `vendorRaw` to the full vendor-native assistant
98
- * message — the same shape your adapter puts in history after emitting a
99
- * tool call during streaming.
100
- */
101
- buildAssistantWithToolCall(tc: Extract<LlmEvent, {
102
- type: 'toolCall';
103
- }>): Message;
104
91
  }
105
92
 
106
93
  /**
@@ -142,6 +129,7 @@ declare class FakeAdapter implements LlmAdapter<ApiKeyAuth> {
142
129
  constructor(state: FakeState);
143
130
  init(auth: ApiKeyAuth, _client?: ClientOptions): void;
144
131
  stream(req: PromptRequest): AsyncIterable<LlmEvent>;
132
+ appendAssistantToolCall(history: Message[], toolCalls: ReadonlyArray<ToolCallEvent>): Message[];
145
133
  appendToolResult(history: Message[], toolCallId: string, result: unknown): Message[];
146
134
  listAvailableModels(): Promise<ModelInfo[]>;
147
135
  private ensureInitialized;
@@ -173,6 +173,28 @@ async function* streamFromOpenAI(client, req, options) {
173
173
  };
174
174
  yield endEvent;
175
175
  }
176
+ function appendOpenAIAssistantToolCall(history, toolCalls) {
177
+ const wireMessage = {
178
+ role: "assistant",
179
+ content: null,
180
+ tool_calls: toolCalls.map((tc) => ({
181
+ id: tc.id,
182
+ type: "function",
183
+ function: {
184
+ name: tc.name,
185
+ arguments: typeof tc.arguments === "string" ? tc.arguments : JSON.stringify(tc.arguments ?? {})
186
+ }
187
+ }))
188
+ };
189
+ return [
190
+ ...history,
191
+ {
192
+ role: "assistant",
193
+ content: "",
194
+ vendorRaw: wireMessage
195
+ }
196
+ ];
197
+ }
176
198
  function appendOpenAIToolResult(history, toolCallId, result) {
177
199
  const content = typeof result === "string" ? result : JSON.stringify(result);
178
200
  const wireMessage = {
@@ -394,6 +416,9 @@ var OpenAIAdapter = class {
394
416
  knownModels: openAIManifest.knownModels
395
417
  });
396
418
  }
419
+ appendAssistantToolCall(history, toolCalls) {
420
+ return appendOpenAIAssistantToolCall(history, toolCalls);
421
+ }
397
422
  appendToolResult(history, toolCallId, result) {
398
423
  return appendOpenAIToolResult(history, toolCallId, result);
399
424
  }
@@ -744,6 +769,25 @@ var AnthropicAdapter = class {
744
769
  };
745
770
  yield endEvent;
746
771
  }
772
+ appendAssistantToolCall(history, toolCalls) {
773
+ const wireMessage = {
774
+ role: "assistant",
775
+ content: toolCalls.map((tc) => ({
776
+ type: "tool_use",
777
+ id: tc.id,
778
+ name: tc.name,
779
+ input: tc.arguments ?? {}
780
+ }))
781
+ };
782
+ return [
783
+ ...history,
784
+ {
785
+ role: "assistant",
786
+ content: "",
787
+ vendorRaw: wireMessage
788
+ }
789
+ ];
790
+ }
747
791
  appendToolResult(history, toolCallId, result) {
748
792
  const content = typeof result === "string" ? result : JSON.stringify(result);
749
793
  const wireMessage = {
@@ -959,6 +1003,25 @@ async function* streamFromGemini(client, req) {
959
1003
  };
960
1004
  yield endEvent;
961
1005
  }
1006
+ function appendGeminiAssistantToolCall(history, toolCalls) {
1007
+ const wireMessage = {
1008
+ role: "model",
1009
+ parts: toolCalls.map((tc) => ({
1010
+ functionCall: {
1011
+ name: parseToolName(tc.id),
1012
+ args: tc.arguments ?? {}
1013
+ }
1014
+ }))
1015
+ };
1016
+ return [
1017
+ ...history,
1018
+ {
1019
+ role: "assistant",
1020
+ content: "",
1021
+ vendorRaw: wireMessage
1022
+ }
1023
+ ];
1024
+ }
962
1025
  function appendGeminiToolResult(history, toolCallId, result) {
963
1026
  const name = parseToolName(toolCallId);
964
1027
  const content = typeof result === "string" ? result : JSON.stringify(result);
@@ -1181,6 +1244,9 @@ var GoogleAdapter = class {
1181
1244
  stream(req) {
1182
1245
  return streamFromGemini(this.ensureClient(), req);
1183
1246
  }
1247
+ appendAssistantToolCall(history, toolCalls) {
1248
+ return appendGeminiAssistantToolCall(history, toolCalls);
1249
+ }
1184
1250
  appendToolResult(history, toolCallId, result) {
1185
1251
  return appendGeminiToolResult(history, toolCallId, result);
1186
1252
  }
@@ -1348,6 +1414,9 @@ var VertexGeminiAdapter = class {
1348
1414
  stream(req) {
1349
1415
  return streamFromGemini(this.ensureClient(), req);
1350
1416
  }
1417
+ appendAssistantToolCall(history, toolCalls) {
1418
+ return appendGeminiAssistantToolCall(history, toolCalls);
1419
+ }
1351
1420
  appendToolResult(history, toolCallId, result) {
1352
1421
  return appendGeminiToolResult(history, toolCallId, result);
1353
1422
  }
@@ -1514,6 +1583,9 @@ var VertexOpenAIAdapter = class {
1514
1583
  knownModels: vertexOpenAIManifest.knownModels
1515
1584
  });
1516
1585
  }
1586
+ appendAssistantToolCall(history, toolCalls) {
1587
+ return appendOpenAIAssistantToolCall(history, toolCalls);
1588
+ }
1517
1589
  appendToolResult(history, toolCallId, result) {
1518
1590
  return appendOpenAIToolResult(history, toolCallId, result);
1519
1591
  }
@@ -1906,6 +1978,26 @@ var BedrockAdapter = class {
1906
1978
  };
1907
1979
  yield endEvent;
1908
1980
  }
1981
+ appendAssistantToolCall(history, toolCalls) {
1982
+ const wireMessage = {
1983
+ role: "assistant",
1984
+ content: toolCalls.map((tc) => ({
1985
+ toolUse: {
1986
+ toolUseId: tc.id,
1987
+ name: tc.name,
1988
+ input: tc.arguments ?? {}
1989
+ }
1990
+ }))
1991
+ };
1992
+ return [
1993
+ ...history,
1994
+ {
1995
+ role: "assistant",
1996
+ content: "",
1997
+ vendorRaw: wireMessage
1998
+ }
1999
+ ];
2000
+ }
1909
2001
  appendToolResult(history, toolCallId, result) {
1910
2002
  const text = typeof result === "string" ? result : JSON.stringify(result);
1911
2003
  const wireMessage = {
@@ -2233,12 +2325,11 @@ function runContractTests(harness) {
2233
2325
  (e) => e.type === "toolCall"
2234
2326
  );
2235
2327
  expect(tc).toBeDefined();
2236
- const history = [
2237
- { role: "user", content: "call it" },
2238
- harness.buildAssistantWithToolCall(tc)
2239
- ];
2240
- const withResult = adapter.appendToolResult(history, tc.id, { ok: true });
2241
- expect(withResult.length).toBe(history.length + 1);
2328
+ const baseHistory = [{ role: "user", content: "call it" }];
2329
+ const afterAssistant = adapter.appendAssistantToolCall(baseHistory, [tc]);
2330
+ expect(afterAssistant.length).toBe(baseHistory.length + 1);
2331
+ const withResult = adapter.appendToolResult(afterAssistant, tc.id, { ok: true });
2332
+ expect(withResult.length).toBe(afterAssistant.length + 1);
2242
2333
  await harness.mockScenario("simple-stream");
2243
2334
  await expect(
2244
2335
  drainStream(adapter, {
@@ -2326,11 +2417,11 @@ function runContractTests(harness) {
2326
2417
  (e) => e.type === "toolCall"
2327
2418
  );
2328
2419
  expect(tc).toBeDefined();
2329
- const history = [
2330
- { role: "user", content: "go" },
2331
- harness.buildAssistantWithToolCall(tc)
2332
- ];
2333
- const withResult = adapter.appendToolResult(history, tc.id, { ok: true });
2420
+ const afterAssistant = adapter.appendAssistantToolCall(
2421
+ [{ role: "user", content: "go" }],
2422
+ [tc]
2423
+ );
2424
+ const withResult = adapter.appendToolResult(afterAssistant, tc.id, { ok: true });
2334
2425
  await harness.mockScenario("simple-stream");
2335
2426
  await drainStream(adapter, {
2336
2427
  model: harness.toolCapableModel,
@@ -2340,6 +2431,28 @@ function runContractTests(harness) {
2340
2431
  expect(captured).not.toBeNull();
2341
2432
  expect(captured.vendorRawHonored).toBe(true);
2342
2433
  });
2434
+ it("[21] appendAssistantToolCall output + appendToolResult re-stream preserves tool-call id on wire", async () => {
2435
+ await harness.mockScenario("tool-call");
2436
+ const adapter = await init(harness);
2437
+ const first = await drainStream(adapter, {
2438
+ model: harness.toolCapableModel,
2439
+ messages: [{ role: "user", content: "go" }],
2440
+ tools: [
2441
+ { name: "test_tool", description: "x", parameters: { type: "object" } }
2442
+ ]
2443
+ });
2444
+ const tc = first.find(
2445
+ (e) => e.type === "toolCall"
2446
+ );
2447
+ expect(tc).toBeDefined();
2448
+ const history = adapter.appendAssistantToolCall(
2449
+ [{ role: "user", content: "go" }],
2450
+ [tc]
2451
+ );
2452
+ const appended = history[history.length - 1];
2453
+ expect(appended.role).toBe("assistant");
2454
+ expect(appended.vendorRaw).toBeDefined();
2455
+ });
2343
2456
  });
2344
2457
  }
2345
2458
  async function init(harness) {
@@ -2404,6 +2517,23 @@ var FakeAdapter = class {
2404
2517
  throw new Error(`FakeAdapter: unknown scenario '${scenario}'`);
2405
2518
  }
2406
2519
  }
2520
+ appendAssistantToolCall(history, toolCalls) {
2521
+ return [
2522
+ ...history,
2523
+ {
2524
+ role: "assistant",
2525
+ content: "",
2526
+ vendorRaw: {
2527
+ role: "assistant",
2528
+ toolCalls: toolCalls.map((tc) => ({
2529
+ id: tc.id,
2530
+ name: tc.name,
2531
+ arguments: tc.arguments
2532
+ }))
2533
+ }
2534
+ }
2535
+ ];
2536
+ }
2407
2537
  appendToolResult(history, toolCallId, result) {
2408
2538
  const content = typeof result === "string" ? result : JSON.stringify(result);
2409
2539
  return [
@@ -2586,17 +2716,7 @@ function createFakeHarness() {
2586
2716
  },
2587
2717
  toolCapableModel: "fake-tool-model",
2588
2718
  nonToolCapableModel: "fake-plain-model",
2589
- emitsToolCallStart: true,
2590
- buildAssistantWithToolCall(tc) {
2591
- return {
2592
- role: "assistant",
2593
- content: "",
2594
- vendorRaw: {
2595
- role: "assistant",
2596
- toolCall: { id: tc.id, name: tc.name, arguments: tc.arguments }
2597
- }
2598
- };
2599
- }
2719
+ emitsToolCallStart: true
2600
2720
  };
2601
2721
  }
2602
2722
  export {