@ljoukov/llm 4.0.8 → 4.0.9

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/README.md CHANGED
@@ -687,9 +687,9 @@ See `docs/agent-telemetry.md` for event schema, design rationale, and backend ad
687
687
 
688
688
  Each LLM call writes:
689
689
 
690
- - `request.txt`, `request.metadata.json`, `tool_call_response.txt` (when the request includes tool outputs), and `input-<n>.<ext>` attachments immediately,
690
+ - `request.txt`, `request.metadata.json`, `tool_call_response.txt` plus `tool_call_response.json` (when the request includes tool outputs), and `input-<n>.<ext>` attachments immediately,
691
691
  - streamed `thoughts.txt` deltas during generation,
692
- - `response.txt` for assistant text responses, `tool_call.txt` when the model asks to call tools, `output-<n>.<ext>` for inline output media, and `response.metadata.json` at completion,
692
+ - `response.txt` for assistant text responses, `tool_call.txt` plus `tool_call.json` when the model asks to call tools, `output-<n>.<ext>` for inline output media, and `response.metadata.json` at completion,
693
693
  - `error.txt` plus `response.metadata.json` on failure.
694
694
 
695
695
  `image_url` data URLs are redacted in text/metadata logs (`data:...,...`) so base64 payloads are not printed inline.
package/dist/index.cjs CHANGED
@@ -2881,6 +2881,33 @@ function ensureTrailingNewline(value) {
2881
2881
  function hasNonEmptyText(value) {
2882
2882
  return typeof value === "string" && value.length > 0;
2883
2883
  }
2884
+ function hasLogArtifactValue(value) {
2885
+ if (value === null || value === void 0) {
2886
+ return false;
2887
+ }
2888
+ if (typeof value === "string") {
2889
+ return value.length > 0;
2890
+ }
2891
+ if (Array.isArray(value)) {
2892
+ return value.length > 0;
2893
+ }
2894
+ if (typeof value === "object") {
2895
+ return Object.keys(value).length > 0;
2896
+ }
2897
+ return true;
2898
+ }
2899
+ function serialiseJsonArtifact(value) {
2900
+ if (!hasLogArtifactValue(value)) {
2901
+ return void 0;
2902
+ }
2903
+ try {
2904
+ return `${JSON.stringify(sanitiseLogValue(value), null, 2)}
2905
+ `;
2906
+ } catch {
2907
+ return `${JSON.stringify(String(value), null, 2)}
2908
+ `;
2909
+ }
2910
+ }
2884
2911
  function redactDataUrlPayload(value) {
2885
2912
  if (!value.toLowerCase().startsWith("data:")) {
2886
2913
  return value;
@@ -3161,7 +3188,9 @@ var AgentLoggingSessionImpl = class {
3161
3188
  const responsePath = import_node_path3.default.join(baseDir, "response.txt");
3162
3189
  const thoughtsPath = import_node_path3.default.join(baseDir, "thoughts.txt");
3163
3190
  const toolCallPath = import_node_path3.default.join(baseDir, "tool_call.txt");
3191
+ const toolCallJsonPath = import_node_path3.default.join(baseDir, "tool_call.json");
3164
3192
  const toolCallResponsePath = import_node_path3.default.join(baseDir, "tool_call_response.txt");
3193
+ const toolCallResponseJsonPath = import_node_path3.default.join(baseDir, "tool_call_response.json");
3165
3194
  const errorPath = import_node_path3.default.join(baseDir, "error.txt");
3166
3195
  const responseMetadataPath = import_node_path3.default.join(baseDir, "response.metadata.json");
3167
3196
  let chain = this.ensureReady.then(async () => {
@@ -3192,6 +3221,10 @@ var AgentLoggingSessionImpl = class {
3192
3221
  "utf8"
3193
3222
  );
3194
3223
  }
3224
+ const toolCallResponseJson = serialiseJsonArtifact(input.toolCallResponsePayload);
3225
+ if (toolCallResponseJson) {
3226
+ await (0, import_promises.writeFile)(toolCallResponseJsonPath, toolCallResponseJson, "utf8");
3227
+ }
3195
3228
  }).catch(() => void 0);
3196
3229
  this.track(chain);
3197
3230
  let closed = false;
@@ -3230,6 +3263,10 @@ var AgentLoggingSessionImpl = class {
3230
3263
  if (hasNonEmptyText(options?.toolCallText)) {
3231
3264
  await (0, import_promises.writeFile)(toolCallPath, ensureTrailingNewline(options.toolCallText), "utf8");
3232
3265
  }
3266
+ const toolCallJson = serialiseJsonArtifact(options?.toolCallPayload);
3267
+ if (toolCallJson) {
3268
+ await (0, import_promises.writeFile)(toolCallJsonPath, toolCallJson, "utf8");
3269
+ }
3233
3270
  await this.writeAttachments(baseDir, options?.attachments);
3234
3271
  const payload = {
3235
3272
  capturedAt: toIsoNow(),
@@ -3259,6 +3296,10 @@ var AgentLoggingSessionImpl = class {
3259
3296
  if (hasNonEmptyText(options?.toolCallText)) {
3260
3297
  await (0, import_promises.writeFile)(toolCallPath, ensureTrailingNewline(options.toolCallText), "utf8");
3261
3298
  }
3299
+ const toolCallJson = serialiseJsonArtifact(options?.toolCallPayload);
3300
+ if (toolCallJson) {
3301
+ await (0, import_promises.writeFile)(toolCallJsonPath, toolCallJson, "utf8");
3302
+ }
3262
3303
  await this.writeAttachments(baseDir, options?.attachments);
3263
3304
  await (0, import_promises.writeFile)(errorPath, ensureTrailingNewline(toErrorMessage(error)), "utf8");
3264
3305
  const payload = {
@@ -5236,6 +5277,10 @@ function collectLoggedAttachmentsFromGeminiParts(parts, prefix) {
5236
5277
  return collectLoggedAttachmentsFromLlmParts(convertGooglePartsToLlmParts(parts), prefix);
5237
5278
  }
5238
5279
  function extractToolCallResponseTextFromOpenAiInput(input) {
5280
+ const responses = extractToolCallResponsePayloadFromOpenAiInput(input);
5281
+ return serialiseLogArtifactText(responses);
5282
+ }
5283
+ function extractToolCallResponsePayloadFromOpenAiInput(input) {
5239
5284
  if (!Array.isArray(input)) {
5240
5285
  return void 0;
5241
5286
  }
@@ -5252,9 +5297,13 @@ function extractToolCallResponseTextFromOpenAiInput(input) {
5252
5297
  }
5253
5298
  ];
5254
5299
  });
5255
- return serialiseLogArtifactText(responses);
5300
+ return responses.length > 0 ? responses : void 0;
5256
5301
  }
5257
5302
  function extractToolCallResponseTextFromFireworksMessages(messages) {
5303
+ const responses = extractToolCallResponsePayloadFromFireworksMessages(messages);
5304
+ return serialiseLogArtifactText(responses);
5305
+ }
5306
+ function extractToolCallResponsePayloadFromFireworksMessages(messages) {
5258
5307
  if (!Array.isArray(messages)) {
5259
5308
  return void 0;
5260
5309
  }
@@ -5269,9 +5318,13 @@ function extractToolCallResponseTextFromFireworksMessages(messages) {
5269
5318
  }
5270
5319
  ];
5271
5320
  });
5272
- return serialiseLogArtifactText(responses);
5321
+ return responses.length > 0 ? responses : void 0;
5273
5322
  }
5274
5323
  function extractToolCallResponseTextFromGeminiContents(contents) {
5324
+ const responses = extractToolCallResponsePayloadFromGeminiContents(contents);
5325
+ return serialiseLogArtifactText(responses);
5326
+ }
5327
+ function extractToolCallResponsePayloadFromGeminiContents(contents) {
5275
5328
  if (!Array.isArray(contents)) {
5276
5329
  return void 0;
5277
5330
  }
@@ -5294,40 +5347,36 @@ function extractToolCallResponseTextFromGeminiContents(contents) {
5294
5347
  }
5295
5348
  }
5296
5349
  }
5297
- return serialiseLogArtifactText(responses);
5350
+ return responses.length > 0 ? responses : void 0;
5298
5351
  }
5299
- function serialiseOpenAiStyleToolCallsForLogging(calls) {
5300
- return serialiseLogArtifactText(
5301
- calls.map((call) => {
5302
- if (call.kind === "custom") {
5303
- return {
5304
- kind: call.kind,
5305
- name: call.name,
5306
- callId: call.callId,
5307
- itemId: call.itemId,
5308
- input: call.input
5309
- };
5310
- }
5311
- const { value, error } = parseOpenAiToolArguments(call.arguments);
5352
+ function toLoggedOpenAiStyleToolCalls(calls) {
5353
+ return calls.map((call) => {
5354
+ if (call.kind === "custom") {
5312
5355
  return {
5313
5356
  kind: call.kind,
5314
5357
  name: call.name,
5315
5358
  callId: call.callId,
5316
5359
  itemId: call.itemId,
5317
- arguments: value,
5318
- ...error ? { parseError: error, rawArguments: call.arguments } : {}
5360
+ input: call.input
5319
5361
  };
5320
- })
5321
- );
5362
+ }
5363
+ const { value, error } = parseOpenAiToolArguments(call.arguments);
5364
+ return {
5365
+ kind: call.kind,
5366
+ name: call.name,
5367
+ callId: call.callId,
5368
+ itemId: call.itemId,
5369
+ arguments: value,
5370
+ ...error ? { parseError: error, rawArguments: call.arguments } : {}
5371
+ };
5372
+ });
5322
5373
  }
5323
- function serialiseGeminiToolCallsForLogging(calls) {
5324
- return serialiseLogArtifactText(
5325
- calls.map((call) => ({
5326
- name: call.name ?? "unknown",
5327
- callId: typeof call.id === "string" ? call.id : void 0,
5328
- arguments: sanitiseLogValue(call.args ?? {})
5329
- }))
5330
- );
5374
+ function toLoggedGeminiToolCalls(calls) {
5375
+ return calls.map((call) => ({
5376
+ name: call.name ?? "unknown",
5377
+ callId: typeof call.id === "string" ? call.id : void 0,
5378
+ arguments: sanitiseLogValue(call.args ?? {})
5379
+ }));
5331
5380
  }
5332
5381
  function startLlmCallLoggerFromContents(options) {
5333
5382
  const session = getCurrentAgentLoggingSession();
@@ -5407,6 +5456,13 @@ function startLlmCallLoggerFromPayload(options) {
5407
5456
  ) : extractToolCallResponseTextFromGeminiContents(
5408
5457
  options.requestPayload.contents
5409
5458
  );
5459
+ const toolCallResponsePayload = options.provider === "openai" || options.provider === "chatgpt" ? extractToolCallResponsePayloadFromOpenAiInput(
5460
+ options.requestPayload.input
5461
+ ) : options.provider === "fireworks" ? extractToolCallResponsePayloadFromFireworksMessages(
5462
+ options.requestPayload.messages
5463
+ ) : extractToolCallResponsePayloadFromGeminiContents(
5464
+ options.requestPayload.contents
5465
+ );
5410
5466
  return session.startLlmCall({
5411
5467
  provider: options.provider,
5412
5468
  modelId: options.modelId,
@@ -5416,7 +5472,8 @@ function startLlmCallLoggerFromPayload(options) {
5416
5472
  ...getCurrentToolCallContext() ? { toolContext: getCurrentToolCallContext() } : {}
5417
5473
  },
5418
5474
  attachments,
5419
- toolCallResponseText
5475
+ toolCallResponseText,
5476
+ toolCallResponsePayload
5420
5477
  });
5421
5478
  }
5422
5479
  async function runTextCall(params) {
@@ -6247,6 +6304,7 @@ async function runToolLoop(request) {
6247
6304
  let responseText = "";
6248
6305
  let reasoningSummary = "";
6249
6306
  let stepToolCallText;
6307
+ let stepToolCallPayload;
6250
6308
  const stepRequestPayload = {
6251
6309
  model: providerInfo.model,
6252
6310
  input,
@@ -6356,7 +6414,7 @@ async function runToolLoop(request) {
6356
6414
  emitEvent({ type: "usage", usage: usageTokens, costUsd: stepCostUsd, modelVersion });
6357
6415
  }
6358
6416
  const responseToolCalls = extractOpenAiToolCalls(finalResponse.output);
6359
- stepToolCallText = serialiseOpenAiStyleToolCallsForLogging(
6417
+ stepToolCallPayload = toLoggedOpenAiStyleToolCalls(
6360
6418
  responseToolCalls.map(
6361
6419
  (call) => call.kind === "custom" ? {
6362
6420
  kind: call.kind,
@@ -6373,6 +6431,7 @@ async function runToolLoop(request) {
6373
6431
  }
6374
6432
  )
6375
6433
  );
6434
+ stepToolCallText = serialiseLogArtifactText(stepToolCallPayload);
6376
6435
  const stepToolCalls = [];
6377
6436
  if (responseToolCalls.length === 0) {
6378
6437
  const steeringInput2 = steeringInternal?.drainPendingContents() ?? [];
@@ -6538,6 +6597,7 @@ async function runToolLoop(request) {
6538
6597
  stepCallLogger?.complete({
6539
6598
  responseText,
6540
6599
  toolCallText: stepToolCallText,
6600
+ toolCallPayload: stepToolCallPayload,
6541
6601
  metadata: {
6542
6602
  provider: "openai",
6543
6603
  model: request.model,
@@ -6558,6 +6618,7 @@ async function runToolLoop(request) {
6558
6618
  stepCallLogger?.fail(error, {
6559
6619
  responseText,
6560
6620
  toolCallText: stepToolCallText,
6621
+ toolCallPayload: stepToolCallPayload,
6561
6622
  metadata: {
6562
6623
  provider: "openai",
6563
6624
  model: request.model,
@@ -6592,6 +6653,7 @@ async function runToolLoop(request) {
6592
6653
  let responseText = "";
6593
6654
  let reasoningSummaryText = "";
6594
6655
  let stepToolCallText;
6656
+ let stepToolCallPayload;
6595
6657
  const markFirstModelEvent = () => {
6596
6658
  if (firstModelEventAtMs === void 0) {
6597
6659
  firstModelEventAtMs = Date.now();
@@ -6660,7 +6722,7 @@ async function runToolLoop(request) {
6660
6722
  stepCallLogger?.appendResponseDelta(responseText);
6661
6723
  }
6662
6724
  const responseToolCalls = response.toolCalls ?? [];
6663
- stepToolCallText = serialiseOpenAiStyleToolCallsForLogging(
6725
+ stepToolCallPayload = toLoggedOpenAiStyleToolCalls(
6664
6726
  responseToolCalls.map(
6665
6727
  (call) => call.kind === "custom" ? {
6666
6728
  kind: call.kind,
@@ -6677,6 +6739,7 @@ async function runToolLoop(request) {
6677
6739
  }
6678
6740
  )
6679
6741
  );
6742
+ stepToolCallText = serialiseLogArtifactText(stepToolCallPayload);
6680
6743
  if (responseToolCalls.length === 0) {
6681
6744
  const steeringInput2 = steeringInternal?.drainPendingContents() ?? [];
6682
6745
  const steeringItems2 = steeringInput2.length > 0 ? toChatGptInput(steeringInput2).input : [];
@@ -6849,6 +6912,7 @@ async function runToolLoop(request) {
6849
6912
  stepCallLogger?.complete({
6850
6913
  responseText,
6851
6914
  toolCallText: stepToolCallText,
6915
+ toolCallPayload: stepToolCallPayload,
6852
6916
  metadata: {
6853
6917
  provider: "chatgpt",
6854
6918
  model: request.model,
@@ -6867,6 +6931,7 @@ async function runToolLoop(request) {
6867
6931
  stepCallLogger?.fail(error, {
6868
6932
  responseText,
6869
6933
  toolCallText: stepToolCallText,
6934
+ toolCallPayload: stepToolCallPayload,
6870
6935
  metadata: {
6871
6936
  provider: "chatgpt",
6872
6937
  model: request.model,
@@ -6897,6 +6962,7 @@ async function runToolLoop(request) {
6897
6962
  let responseText = "";
6898
6963
  let blocked = false;
6899
6964
  let stepToolCallText;
6965
+ let stepToolCallPayload;
6900
6966
  const stepRequestPayload = {
6901
6967
  model: providerInfo.model,
6902
6968
  messages,
@@ -6961,7 +7027,7 @@ async function runToolLoop(request) {
6961
7027
  });
6962
7028
  }
6963
7029
  const responseToolCalls = extractFireworksToolCalls(message);
6964
- stepToolCallText = serialiseOpenAiStyleToolCallsForLogging(
7030
+ stepToolCallPayload = toLoggedOpenAiStyleToolCalls(
6965
7031
  responseToolCalls.map((call) => ({
6966
7032
  kind: "function",
6967
7033
  name: call.name,
@@ -6969,6 +7035,7 @@ async function runToolLoop(request) {
6969
7035
  callId: call.id
6970
7036
  }))
6971
7037
  );
7038
+ stepToolCallText = serialiseLogArtifactText(stepToolCallPayload);
6972
7039
  if (responseToolCalls.length === 0) {
6973
7040
  const steeringInput2 = steeringInternal?.drainPendingContents() ?? [];
6974
7041
  const steeringMessages = steeringInput2.length > 0 ? toFireworksMessages(steeringInput2) : [];
@@ -7121,6 +7188,7 @@ async function runToolLoop(request) {
7121
7188
  stepCallLogger?.complete({
7122
7189
  responseText,
7123
7190
  toolCallText: stepToolCallText,
7191
+ toolCallPayload: stepToolCallPayload,
7124
7192
  metadata: {
7125
7193
  provider: "fireworks",
7126
7194
  model: request.model,
@@ -7149,6 +7217,7 @@ async function runToolLoop(request) {
7149
7217
  stepCallLogger?.fail(error, {
7150
7218
  responseText,
7151
7219
  toolCallText: stepToolCallText,
7220
+ toolCallPayload: stepToolCallPayload,
7152
7221
  metadata: {
7153
7222
  provider: "fireworks",
7154
7223
  model: request.model,
@@ -7177,6 +7246,7 @@ async function runToolLoop(request) {
7177
7246
  let responseText = "";
7178
7247
  let thoughtsText = "";
7179
7248
  let stepToolCallText;
7249
+ let stepToolCallPayload;
7180
7250
  const markFirstModelEvent = () => {
7181
7251
  if (firstModelEventAtMs === void 0) {
7182
7252
  firstModelEventAtMs = Date.now();
@@ -7307,7 +7377,8 @@ async function runToolLoop(request) {
7307
7377
  responseImages: 0
7308
7378
  });
7309
7379
  totalCostUsd += stepCostUsd;
7310
- stepToolCallText = serialiseGeminiToolCallsForLogging(response.functionCalls);
7380
+ stepToolCallPayload = toLoggedGeminiToolCalls(response.functionCalls);
7381
+ stepToolCallText = serialiseLogArtifactText(stepToolCallPayload);
7311
7382
  if (response.functionCalls.length === 0) {
7312
7383
  const steeringInput2 = steeringInternal?.drainPendingContents() ?? [];
7313
7384
  finalText = responseText;
@@ -7476,6 +7547,7 @@ async function runToolLoop(request) {
7476
7547
  responseText,
7477
7548
  attachments: responseOutputAttachments,
7478
7549
  toolCallText: stepToolCallText,
7550
+ toolCallPayload: stepToolCallPayload,
7479
7551
  metadata: {
7480
7552
  provider: "gemini",
7481
7553
  model: request.model,
@@ -7498,6 +7570,7 @@ async function runToolLoop(request) {
7498
7570
  stepCallLogger?.fail(error, {
7499
7571
  responseText,
7500
7572
  toolCallText: stepToolCallText,
7573
+ toolCallPayload: stepToolCallPayload,
7501
7574
  metadata: {
7502
7575
  provider: "gemini",
7503
7576
  model: request.model,