@posthog/ai 7.3.1 → 7.4.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.cjs CHANGED
@@ -3,10 +3,14 @@
3
3
  var openai = require('openai');
4
4
  var buffer = require('buffer');
5
5
  var uuid = require('uuid');
6
+ var core = require('@posthog/core');
6
7
  var AnthropicOriginal = require('@anthropic-ai/sdk');
7
8
  var genai = require('@google/genai');
8
9
 
9
- function _interopNamespaceDefault(e) {
10
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
+
12
+ function _interopNamespace(e) {
13
+ if (e && e.__esModule) return e;
10
14
  var n = Object.create(null);
11
15
  if (e) {
12
16
  Object.keys(e).forEach(function (k) {
@@ -23,9 +27,10 @@ function _interopNamespaceDefault(e) {
23
27
  return Object.freeze(n);
24
28
  }
25
29
 
26
- var uuid__namespace = /*#__PURE__*/_interopNamespaceDefault(uuid);
30
+ var uuid__namespace = /*#__PURE__*/_interopNamespace(uuid);
31
+ var AnthropicOriginal__default = /*#__PURE__*/_interopDefault(AnthropicOriginal);
27
32
 
28
- var version = "7.3.1";
33
+ var version = "7.4.0";
29
34
 
30
35
  // Type guards for safer type checking
31
36
  const isString = value => {
@@ -493,6 +498,33 @@ function addDefaults(params) {
493
498
  traceId: params.traceId ?? uuid.v4()
494
499
  };
495
500
  }
501
+ const sendEventWithErrorToPosthog = async ({
502
+ client,
503
+ traceId,
504
+ error,
505
+ ...args
506
+ }) => {
507
+ const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
508
+ const properties = {
509
+ client,
510
+ traceId,
511
+ httpStatus,
512
+ error: JSON.stringify(error),
513
+ ...args
514
+ };
515
+ const enrichedError = error;
516
+ if (client.options?.enableExceptionAutocapture) {
517
+ // assign a uuid that can be used to link the trace and exception events
518
+ const exceptionId = core.uuidv7();
519
+ client.captureException(error, undefined, {
520
+ $ai_trace_id: traceId
521
+ }, exceptionId);
522
+ enrichedError.__posthog_previously_captured_error = true;
523
+ properties.exceptionId = exceptionId;
524
+ }
525
+ await sendEventToPosthog(properties);
526
+ return enrichedError;
527
+ };
496
528
  const sendEventToPosthog = async ({
497
529
  client,
498
530
  eventType = AIEvent.Generation,
@@ -507,8 +539,8 @@ const sendEventToPosthog = async ({
507
539
  params,
508
540
  httpStatus = 200,
509
541
  usage = {},
510
- isError = false,
511
542
  error,
543
+ exceptionId,
512
544
  tools,
513
545
  captureImmediate = false
514
546
  }) => {
@@ -520,10 +552,11 @@ const sendEventToPosthog = async ({
520
552
  const safeOutput = sanitizeValues(output);
521
553
  const safeError = sanitizeValues(error);
522
554
  let errorData = {};
523
- if (isError) {
555
+ if (error) {
524
556
  errorData = {
525
557
  $ai_is_error: true,
526
- $ai_error: safeError
558
+ $ai_error: safeError,
559
+ $exception_event_id: exceptionId
527
560
  };
528
561
  }
529
562
  let costOverrideData = {};
@@ -589,6 +622,7 @@ const sendEventToPosthog = async ({
589
622
  } else {
590
623
  client.capture(event);
591
624
  }
625
+ return Promise.resolve();
592
626
  };
593
627
  function formatOpenAIResponsesInput(input, instructions) {
594
628
  const messages = [];
@@ -996,8 +1030,7 @@ let WrappedCompletions$1 = class WrappedCompletions extends Completions {
996
1030
  tools: availableTools
997
1031
  });
998
1032
  } catch (error) {
999
- const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
1000
- await sendEventToPosthog({
1033
+ const enrichedError = await sendEventWithErrorToPosthog({
1001
1034
  client: this.phClient,
1002
1035
  ...posthogParams,
1003
1036
  model: openAIParams.model,
@@ -1007,14 +1040,13 @@ let WrappedCompletions$1 = class WrappedCompletions extends Completions {
1007
1040
  latency: 0,
1008
1041
  baseURL: this.baseURL,
1009
1042
  params: body,
1010
- httpStatus,
1011
1043
  usage: {
1012
1044
  inputTokens: 0,
1013
1045
  outputTokens: 0
1014
1046
  },
1015
- isError: true,
1016
- error: JSON.stringify(error)
1047
+ error
1017
1048
  });
1049
+ throw enrichedError;
1018
1050
  }
1019
1051
  })();
1020
1052
  // Return the other stream to the user
@@ -1067,7 +1099,6 @@ let WrappedCompletions$1 = class WrappedCompletions extends Completions {
1067
1099
  inputTokens: 0,
1068
1100
  outputTokens: 0
1069
1101
  },
1070
- isError: true,
1071
1102
  error: JSON.stringify(error)
1072
1103
  });
1073
1104
  throw error;
@@ -1150,8 +1181,7 @@ let WrappedResponses$1 = class WrappedResponses extends Responses {
1150
1181
  tools: availableTools
1151
1182
  });
1152
1183
  } catch (error) {
1153
- const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
1154
- await sendEventToPosthog({
1184
+ const enrichedError = await sendEventWithErrorToPosthog({
1155
1185
  client: this.phClient,
1156
1186
  ...posthogParams,
1157
1187
  model: openAIParams.model,
@@ -1161,14 +1191,13 @@ let WrappedResponses$1 = class WrappedResponses extends Responses {
1161
1191
  latency: 0,
1162
1192
  baseURL: this.baseURL,
1163
1193
  params: body,
1164
- httpStatus,
1165
1194
  usage: {
1166
1195
  inputTokens: 0,
1167
1196
  outputTokens: 0
1168
1197
  },
1169
- isError: true,
1170
- error: JSON.stringify(error)
1198
+ error: error
1171
1199
  });
1200
+ throw enrichedError;
1172
1201
  }
1173
1202
  })();
1174
1203
  return stream2;
@@ -1222,7 +1251,6 @@ let WrappedResponses$1 = class WrappedResponses extends Responses {
1222
1251
  inputTokens: 0,
1223
1252
  outputTokens: 0
1224
1253
  },
1225
- isError: true,
1226
1254
  error: JSON.stringify(error)
1227
1255
  });
1228
1256
  throw error;
@@ -1264,8 +1292,7 @@ let WrappedResponses$1 = class WrappedResponses extends Responses {
1264
1292
  });
1265
1293
  return result;
1266
1294
  }, async error => {
1267
- const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
1268
- await sendEventToPosthog({
1295
+ const enrichedError = await sendEventWithErrorToPosthog({
1269
1296
  client: this.phClient,
1270
1297
  ...posthogParams,
1271
1298
  model: openAIParams.model,
@@ -1275,15 +1302,13 @@ let WrappedResponses$1 = class WrappedResponses extends Responses {
1275
1302
  latency: 0,
1276
1303
  baseURL: this.baseURL,
1277
1304
  params: body,
1278
- httpStatus,
1279
1305
  usage: {
1280
1306
  inputTokens: 0,
1281
1307
  outputTokens: 0
1282
1308
  },
1283
- isError: true,
1284
1309
  error: JSON.stringify(error)
1285
1310
  });
1286
- throw error;
1311
+ throw enrichedError;
1287
1312
  });
1288
1313
  return wrappedPromise;
1289
1314
  } finally {
@@ -1343,7 +1368,6 @@ let WrappedEmbeddings$1 = class WrappedEmbeddings extends Embeddings {
1343
1368
  usage: {
1344
1369
  inputTokens: 0
1345
1370
  },
1346
- isError: true,
1347
1371
  error: JSON.stringify(error)
1348
1372
  });
1349
1373
  throw error;
@@ -1411,8 +1435,7 @@ class WrappedTranscriptions extends Transcriptions {
1411
1435
  tools: availableTools
1412
1436
  });
1413
1437
  } catch (error) {
1414
- const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
1415
- await sendEventToPosthog({
1438
+ const enrichedError = await sendEventWithErrorToPosthog({
1416
1439
  client: this.phClient,
1417
1440
  ...posthogParams,
1418
1441
  model: openAIParams.model,
@@ -1422,14 +1445,13 @@ class WrappedTranscriptions extends Transcriptions {
1422
1445
  latency: 0,
1423
1446
  baseURL: this.baseURL,
1424
1447
  params: body,
1425
- httpStatus,
1426
1448
  usage: {
1427
1449
  inputTokens: 0,
1428
1450
  outputTokens: 0
1429
1451
  },
1430
- isError: true,
1431
- error: JSON.stringify(error)
1452
+ error: error
1432
1453
  });
1454
+ throw enrichedError;
1433
1455
  }
1434
1456
  })();
1435
1457
  return stream2;
@@ -1459,8 +1481,7 @@ class WrappedTranscriptions extends Transcriptions {
1459
1481
  return result;
1460
1482
  }
1461
1483
  }, async error => {
1462
- const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
1463
- await sendEventToPosthog({
1484
+ const enrichedError = await sendEventWithErrorToPosthog({
1464
1485
  client: this.phClient,
1465
1486
  ...posthogParams,
1466
1487
  model: openAIParams.model,
@@ -1470,15 +1491,13 @@ class WrappedTranscriptions extends Transcriptions {
1470
1491
  latency: 0,
1471
1492
  baseURL: this.baseURL,
1472
1493
  params: body,
1473
- httpStatus,
1474
1494
  usage: {
1475
1495
  inputTokens: 0,
1476
1496
  outputTokens: 0
1477
1497
  },
1478
- isError: true,
1479
- error: JSON.stringify(error)
1498
+ error: error
1480
1499
  });
1481
- throw error;
1500
+ throw enrichedError;
1482
1501
  });
1483
1502
  return wrappedPromise;
1484
1503
  }
@@ -1629,8 +1648,7 @@ class WrappedCompletions extends openai.AzureOpenAI.Chat.Completions {
1629
1648
  usage
1630
1649
  });
1631
1650
  } catch (error) {
1632
- const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
1633
- await sendEventToPosthog({
1651
+ const enrichedError = await sendEventWithErrorToPosthog({
1634
1652
  client: this.phClient,
1635
1653
  ...posthogParams,
1636
1654
  model: openAIParams.model,
@@ -1640,14 +1658,13 @@ class WrappedCompletions extends openai.AzureOpenAI.Chat.Completions {
1640
1658
  latency: 0,
1641
1659
  baseURL: this.baseURL,
1642
1660
  params: body,
1643
- httpStatus,
1644
1661
  usage: {
1645
1662
  inputTokens: 0,
1646
1663
  outputTokens: 0
1647
1664
  },
1648
- isError: true,
1649
- error: JSON.stringify(error)
1665
+ error: error
1650
1666
  });
1667
+ throw enrichedError;
1651
1668
  }
1652
1669
  })();
1653
1670
  // Return the other stream to the user
@@ -1696,7 +1713,6 @@ class WrappedCompletions extends openai.AzureOpenAI.Chat.Completions {
1696
1713
  inputTokens: 0,
1697
1714
  outputTokens: 0
1698
1715
  },
1699
- isError: true,
1700
1716
  error: JSON.stringify(error)
1701
1717
  });
1702
1718
  throw error;
@@ -1765,8 +1781,7 @@ class WrappedResponses extends openai.AzureOpenAI.Responses {
1765
1781
  usage
1766
1782
  });
1767
1783
  } catch (error) {
1768
- const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
1769
- await sendEventToPosthog({
1784
+ const enrichedError = await sendEventWithErrorToPosthog({
1770
1785
  client: this.phClient,
1771
1786
  ...posthogParams,
1772
1787
  model: openAIParams.model,
@@ -1776,14 +1791,13 @@ class WrappedResponses extends openai.AzureOpenAI.Responses {
1776
1791
  latency: 0,
1777
1792
  baseURL: this.baseURL,
1778
1793
  params: body,
1779
- httpStatus,
1780
1794
  usage: {
1781
1795
  inputTokens: 0,
1782
1796
  outputTokens: 0
1783
1797
  },
1784
- isError: true,
1785
- error: JSON.stringify(error)
1798
+ error: error
1786
1799
  });
1800
+ throw enrichedError;
1787
1801
  }
1788
1802
  })();
1789
1803
  return stream2;
@@ -1831,7 +1845,6 @@ class WrappedResponses extends openai.AzureOpenAI.Responses {
1831
1845
  inputTokens: 0,
1832
1846
  outputTokens: 0
1833
1847
  },
1834
- isError: true,
1835
1848
  error: JSON.stringify(error)
1836
1849
  });
1837
1850
  throw error;
@@ -1883,7 +1896,6 @@ class WrappedResponses extends openai.AzureOpenAI.Responses {
1883
1896
  inputTokens: 0,
1884
1897
  outputTokens: 0
1885
1898
  },
1886
- isError: true,
1887
1899
  error: JSON.stringify(error)
1888
1900
  });
1889
1901
  throw error;
@@ -1941,7 +1953,6 @@ class WrappedEmbeddings extends openai.AzureOpenAI.Embeddings {
1941
1953
  usage: {
1942
1954
  inputTokens: 0
1943
1955
  },
1944
- isError: true,
1945
1956
  error: JSON.stringify(error)
1946
1957
  });
1947
1958
  throw error;
@@ -2269,7 +2280,7 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
2269
2280
  return result;
2270
2281
  } catch (error) {
2271
2282
  const modelId = model.modelId;
2272
- await sendEventToPosthog({
2283
+ const enrichedError = await sendEventWithErrorToPosthog({
2273
2284
  client: phClient,
2274
2285
  distinctId: mergedOptions.posthogDistinctId,
2275
2286
  traceId: mergedOptions.posthogTraceId ?? uuid.v4(),
@@ -2280,17 +2291,15 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
2280
2291
  latency: 0,
2281
2292
  baseURL: '',
2282
2293
  params: mergedParams,
2283
- httpStatus: error?.status ? error.status : 500,
2284
2294
  usage: {
2285
2295
  inputTokens: 0,
2286
2296
  outputTokens: 0
2287
2297
  },
2288
- isError: true,
2289
- error: truncate(JSON.stringify(error)),
2298
+ error: error,
2290
2299
  tools: availableTools,
2291
2300
  captureImmediate: mergedOptions.posthogCaptureImmediate
2292
2301
  });
2293
- throw error;
2302
+ throw enrichedError;
2294
2303
  }
2295
2304
  },
2296
2305
  doStream: async params => {
@@ -2427,7 +2436,7 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
2427
2436
  ...rest
2428
2437
  };
2429
2438
  } catch (error) {
2430
- await sendEventToPosthog({
2439
+ const enrichedError = await sendEventWithErrorToPosthog({
2431
2440
  client: phClient,
2432
2441
  distinctId: mergedOptions.posthogDistinctId,
2433
2442
  traceId: mergedOptions.posthogTraceId ?? uuid.v4(),
@@ -2438,24 +2447,22 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
2438
2447
  latency: 0,
2439
2448
  baseURL: '',
2440
2449
  params: mergedParams,
2441
- httpStatus: error?.status ? error.status : 500,
2442
2450
  usage: {
2443
2451
  inputTokens: 0,
2444
2452
  outputTokens: 0
2445
2453
  },
2446
- isError: true,
2447
- error: truncate(JSON.stringify(error)),
2454
+ error: error,
2448
2455
  tools: availableTools,
2449
2456
  captureImmediate: mergedOptions.posthogCaptureImmediate
2450
2457
  });
2451
- throw error;
2458
+ throw enrichedError;
2452
2459
  }
2453
2460
  }
2454
2461
  };
2455
2462
  return wrappedModel;
2456
2463
  };
2457
2464
 
2458
- class PostHogAnthropic extends AnthropicOriginal {
2465
+ class PostHogAnthropic extends AnthropicOriginal__default.default {
2459
2466
  constructor(config) {
2460
2467
  const {
2461
2468
  posthog,
@@ -2466,7 +2473,7 @@ class PostHogAnthropic extends AnthropicOriginal {
2466
2473
  this.messages = new WrappedMessages(this, this.phClient);
2467
2474
  }
2468
2475
  }
2469
- class WrappedMessages extends AnthropicOriginal.Messages {
2476
+ class WrappedMessages extends AnthropicOriginal__default.default.Messages {
2470
2477
  constructor(parentClient, phClient) {
2471
2478
  super(parentClient);
2472
2479
  this.phClient = phClient;
@@ -2605,8 +2612,7 @@ class WrappedMessages extends AnthropicOriginal.Messages {
2605
2612
  tools: availableTools
2606
2613
  });
2607
2614
  } catch (error) {
2608
- // error handling
2609
- await sendEventToPosthog({
2615
+ const enrichedError = await sendEventWithErrorToPosthog({
2610
2616
  client: this.phClient,
2611
2617
  ...posthogParams,
2612
2618
  model: anthropicParams.model,
@@ -2616,14 +2622,13 @@ class WrappedMessages extends AnthropicOriginal.Messages {
2616
2622
  latency: 0,
2617
2623
  baseURL: this.baseURL,
2618
2624
  params: body,
2619
- httpStatus: error?.status ? error.status : 500,
2620
2625
  usage: {
2621
2626
  inputTokens: 0,
2622
2627
  outputTokens: 0
2623
2628
  },
2624
- isError: true,
2625
- error: JSON.stringify(error)
2629
+ error: error
2626
2630
  });
2631
+ throw enrichedError;
2627
2632
  }
2628
2633
  })();
2629
2634
  // Return the other stream to the user
@@ -2674,7 +2679,6 @@ class WrappedMessages extends AnthropicOriginal.Messages {
2674
2679
  inputTokens: 0,
2675
2680
  outputTokens: 0
2676
2681
  },
2677
- isError: true,
2678
2682
  error: JSON.stringify(error)
2679
2683
  });
2680
2684
  throw error;
@@ -2734,7 +2738,7 @@ class WrappedModels {
2734
2738
  return response;
2735
2739
  } catch (error) {
2736
2740
  const latency = (Date.now() - startTime) / 1000;
2737
- await sendEventToPosthog({
2741
+ const enrichedError = await sendEventWithErrorToPosthog({
2738
2742
  client: this.phClient,
2739
2743
  ...posthogParams,
2740
2744
  model: geminiParams.model,
@@ -2744,15 +2748,13 @@ class WrappedModels {
2744
2748
  latency,
2745
2749
  baseURL: 'https://generativelanguage.googleapis.com',
2746
2750
  params: params,
2747
- httpStatus: error?.status ?? 500,
2748
2751
  usage: {
2749
2752
  inputTokens: 0,
2750
2753
  outputTokens: 0
2751
2754
  },
2752
- isError: true,
2753
- error: JSON.stringify(error)
2755
+ error: error
2754
2756
  });
2755
- throw error;
2757
+ throw enrichedError;
2756
2758
  }
2757
2759
  }
2758
2760
  async *generateContentStream(params) {
@@ -2854,7 +2856,7 @@ class WrappedModels {
2854
2856
  });
2855
2857
  } catch (error) {
2856
2858
  const latency = (Date.now() - startTime) / 1000;
2857
- await sendEventToPosthog({
2859
+ const enrichedError = await sendEventWithErrorToPosthog({
2858
2860
  client: this.phClient,
2859
2861
  ...posthogParams,
2860
2862
  model: geminiParams.model,
@@ -2864,15 +2866,13 @@ class WrappedModels {
2864
2866
  latency,
2865
2867
  baseURL: 'https://generativelanguage.googleapis.com',
2866
2868
  params: params,
2867
- httpStatus: error?.status ?? 500,
2868
2869
  usage: {
2869
2870
  inputTokens: 0,
2870
2871
  outputTokens: 0
2871
2872
  },
2872
- isError: true,
2873
- error: JSON.stringify(error)
2873
+ error: error
2874
2874
  });
2875
- throw error;
2875
+ throw enrichedError;
2876
2876
  }
2877
2877
  }
2878
2878
  formatPartsAsContentBlocks(parts) {
@@ -3252,6 +3252,64 @@ function mapKeys(fields, mapper, map) {
3252
3252
  return mapped;
3253
3253
  }
3254
3254
 
3255
+ //#region src/load/validation.ts
3256
+ /**
3257
+ * Sentinel key used to mark escaped user objects during serialization.
3258
+ *
3259
+ * When a plain object contains 'lc' key (which could be confused with LC objects),
3260
+ * we wrap it as `{"__lc_escaped__": {...original...}}`.
3261
+ */
3262
+ const LC_ESCAPED_KEY = "__lc_escaped__";
3263
+ /**
3264
+ * Check if an object needs escaping to prevent confusion with LC objects.
3265
+ *
3266
+ * An object needs escaping if:
3267
+ * 1. It has an `'lc'` key (could be confused with LC serialization format)
3268
+ * 2. It has only the escape key (would be mistaken for an escaped object)
3269
+ */
3270
+ function needsEscaping(obj) {
3271
+ return "lc" in obj || Object.keys(obj).length === 1 && LC_ESCAPED_KEY in obj;
3272
+ }
3273
+ /**
3274
+ * Wrap an object in the escape marker.
3275
+ *
3276
+ * @example
3277
+ * ```typescript
3278
+ * {"key": "value"} // becomes {"__lc_escaped__": {"key": "value"}}
3279
+ * ```
3280
+ */
3281
+ function escapeObject(obj) {
3282
+ return { [LC_ESCAPED_KEY]: obj };
3283
+ }
3284
+ /**
3285
+ * Check if an object looks like a Serializable instance (duck typing).
3286
+ */
3287
+ function isSerializableLike(obj) {
3288
+ return obj !== null && typeof obj === "object" && "lc_serializable" in obj && typeof obj.toJSON === "function";
3289
+ }
3290
+ /**
3291
+ * Escape a value if it needs escaping (contains `lc` key).
3292
+ *
3293
+ * This is a simpler version of `serializeValue` that doesn't handle Serializable
3294
+ * objects - it's meant to be called on kwargs values that have already been
3295
+ * processed by `toJSON()`.
3296
+ *
3297
+ * @param value - The value to potentially escape.
3298
+ * @returns The value with any `lc`-containing objects wrapped in escape markers.
3299
+ */
3300
+ function escapeIfNeeded(value) {
3301
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
3302
+ if (isSerializableLike(value)) return value;
3303
+ const record = value;
3304
+ if (needsEscaping(record)) return escapeObject(record);
3305
+ const result = {};
3306
+ for (const [key, val] of Object.entries(record)) result[key] = escapeIfNeeded(val);
3307
+ return result;
3308
+ }
3309
+ if (Array.isArray(value)) return value.map((item) => escapeIfNeeded(item));
3310
+ return value;
3311
+ }
3312
+
3255
3313
  //#region src/load/serializable.ts
3256
3314
  var serializable_exports = {};
3257
3315
  __export(serializable_exports, {
@@ -3373,11 +3431,15 @@ var Serializable = class Serializable {
3373
3431
  }
3374
3432
  if (last in read && read[last] !== void 0) write[last] = write[last] || read[last];
3375
3433
  });
3434
+ const escapedKwargs = {};
3435
+ for (const [key, value] of Object.entries(kwargs)) escapedKwargs[key] = escapeIfNeeded(value);
3436
+ const kwargsWithSecrets = Object.keys(secrets).length ? replaceSecrets(escapedKwargs, secrets) : escapedKwargs;
3437
+ const processedKwargs = mapKeys(kwargsWithSecrets, keyToJson, aliases);
3376
3438
  return {
3377
3439
  lc: 1,
3378
3440
  type: "constructor",
3379
3441
  id: this.lc_id,
3380
- kwargs: mapKeys(Object.keys(secrets).length ? replaceSecrets(kwargs, secrets) : kwargs, keyToJson, aliases)
3442
+ kwargs: processedKwargs
3381
3443
  };
3382
3444
  }
3383
3445
  toJSONNotImplemented() {
@@ -3838,7 +3900,10 @@ class LangChainCallbackHandler extends BaseCallbackHandler {
3838
3900
  eventProperties['$ai_output_tokens'] = outputTokens;
3839
3901
  // Add additional token data to properties
3840
3902
  if (additionalTokenData.cacheReadInputTokens) {
3841
- eventProperties['$ai_cache_read_tokens'] = additionalTokenData.cacheReadInputTokens;
3903
+ eventProperties['$ai_cache_read_input_tokens'] = additionalTokenData.cacheReadInputTokens;
3904
+ }
3905
+ if (additionalTokenData.cacheWriteInputTokens) {
3906
+ eventProperties['$ai_cache_creation_input_tokens'] = additionalTokenData.cacheWriteInputTokens;
3842
3907
  }
3843
3908
  if (additionalTokenData.reasoningTokens) {
3844
3909
  eventProperties['$ai_reasoning_tokens'] = additionalTokenData.reasoningTokens;
@@ -4002,6 +4067,14 @@ class LangChainCallbackHandler extends BaseCallbackHandler {
4002
4067
  additionalTokenData.cacheReadInputTokens = usage.input_token_details.cache_read;
4003
4068
  } else if (usage.cachedPromptTokens != null) {
4004
4069
  additionalTokenData.cacheReadInputTokens = usage.cachedPromptTokens;
4070
+ } else if (usage.cache_read_input_tokens != null) {
4071
+ additionalTokenData.cacheReadInputTokens = usage.cache_read_input_tokens;
4072
+ }
4073
+ // Check for cache write/creation tokens in various formats
4074
+ if (usage.cache_creation_input_tokens != null) {
4075
+ additionalTokenData.cacheWriteInputTokens = usage.cache_creation_input_tokens;
4076
+ } else if (usage.input_token_details?.cache_creation != null) {
4077
+ additionalTokenData.cacheWriteInputTokens = usage.input_token_details.cache_creation;
4005
4078
  }
4006
4079
  // Check for reasoning tokens in various formats
4007
4080
  if (usage.completion_tokens_details?.reasoning_tokens != null) {
@@ -4047,8 +4120,10 @@ class LangChainCallbackHandler extends BaseCallbackHandler {
4047
4120
  if (webSearchCount !== undefined) {
4048
4121
  additionalTokenData.webSearchCount = webSearchCount;
4049
4122
  }
4050
- // For Anthropic providers, LangChain reports input_tokens as the sum of input and cache read tokens.
4123
+ // For Anthropic providers, LangChain reports input_tokens as the sum of all input tokens.
4051
4124
  // Our cost calculation expects them to be separate for Anthropic, so we subtract cache tokens.
4125
+ // Both cache_read and cache_write tokens should be subtracted since Anthropic's raw API
4126
+ // reports input_tokens as tokens NOT read from or used to create a cache.
4052
4127
  // For other providers (OpenAI, etc.), input_tokens already excludes cache tokens as expected.
4053
4128
  // Match logic consistent with plugin-server: exact match on provider OR substring match on model
4054
4129
  let isAnthropic = false;
@@ -4057,8 +4132,11 @@ class LangChainCallbackHandler extends BaseCallbackHandler {
4057
4132
  } else if (model && model.toLowerCase().includes('anthropic')) {
4058
4133
  isAnthropic = true;
4059
4134
  }
4060
- if (isAnthropic && parsedUsage.input && additionalTokenData.cacheReadInputTokens) {
4061
- parsedUsage.input = Math.max(parsedUsage.input - additionalTokenData.cacheReadInputTokens, 0);
4135
+ if (isAnthropic && parsedUsage.input) {
4136
+ const cacheTokens = (additionalTokenData.cacheReadInputTokens || 0) + (additionalTokenData.cacheWriteInputTokens || 0);
4137
+ if (cacheTokens > 0) {
4138
+ parsedUsage.input = Math.max(parsedUsage.input - cacheTokens, 0);
4139
+ }
4062
4140
  }
4063
4141
  return [parsedUsage.input, parsedUsage.output, additionalTokenData];
4064
4142
  }