@posthog/ai 4.2.1 → 4.3.1

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/lib/index.d.ts CHANGED
@@ -23,7 +23,7 @@ interface MonitoringParams {
23
23
  posthogModelOverride?: string;
24
24
  posthogProviderOverride?: string;
25
25
  posthogCostOverride?: CostOverride;
26
- fullDebug?: boolean;
26
+ posthogCaptureImmediate?: boolean;
27
27
  }
28
28
  interface CostOverride {
29
29
  inputCost: number;
@@ -95,7 +95,7 @@ interface ClientOptions {
95
95
  posthogModelOverride?: string;
96
96
  posthogProviderOverride?: string;
97
97
  posthogCostOverride?: CostOverride;
98
- fullDebug?: boolean;
98
+ posthogCaptureImmediate?: boolean;
99
99
  }
100
100
  declare const wrapVercelLanguageModel: (model: LanguageModelV1, phClient: PostHog, options: ClientOptions) => LanguageModelV1;
101
101
 
package/lib/index.esm.js CHANGED
@@ -90,7 +90,7 @@ function sanitizeValues(obj) {
90
90
  }
91
91
  return jsonSafe;
92
92
  }
93
- const sendEventToPosthog = ({
93
+ const sendEventToPosthog = async ({
94
94
  client,
95
95
  distinctId,
96
96
  traceId,
@@ -106,82 +106,77 @@ const sendEventToPosthog = ({
106
106
  isError = false,
107
107
  error,
108
108
  tools,
109
- fullDebug = false
109
+ captureImmediate = false
110
110
  }) => {
111
- if (client.capture) {
112
- // sanitize input and output for UTF-8 validity
113
- const safeInput = sanitizeValues(input);
114
- const safeOutput = sanitizeValues(output);
115
- const safeError = sanitizeValues(error);
116
- let errorData = {};
117
- if (isError) {
118
- errorData = {
119
- $ai_is_error: true,
120
- $ai_error: safeError
121
- };
122
- }
123
- let costOverrideData = {};
124
- if (params.posthogCostOverride) {
125
- const inputCostUSD = (params.posthogCostOverride.inputCost ?? 0) * (usage.inputTokens ?? 0);
126
- const outputCostUSD = (params.posthogCostOverride.outputCost ?? 0) * (usage.outputTokens ?? 0);
127
- costOverrideData = {
128
- $ai_input_cost_usd: inputCostUSD,
129
- $ai_output_cost_usd: outputCostUSD,
130
- $ai_total_cost_usd: inputCostUSD + outputCostUSD
131
- };
132
- }
133
- const additionalTokenValues = {
134
- ...(usage.reasoningTokens ? {
135
- $ai_reasoning_tokens: usage.reasoningTokens
136
- } : {}),
137
- ...(usage.cacheReadInputTokens ? {
138
- $ai_cache_read_input_tokens: usage.cacheReadInputTokens
139
- } : {}),
140
- ...(usage.cacheCreationInputTokens ? {
141
- $ai_cache_creation_input_tokens: usage.cacheCreationInputTokens
142
- } : {})
111
+ if (!client.capture) {
112
+ return Promise.resolve();
113
+ }
114
+ // sanitize input and output for UTF-8 validity
115
+ const safeInput = sanitizeValues(input);
116
+ const safeOutput = sanitizeValues(output);
117
+ const safeError = sanitizeValues(error);
118
+ let errorData = {};
119
+ if (isError) {
120
+ errorData = {
121
+ $ai_is_error: true,
122
+ $ai_error: safeError
143
123
  };
144
- const properties = {
145
- $ai_provider: params.posthogProviderOverride ?? provider,
146
- $ai_model: params.posthogModelOverride ?? model,
147
- $ai_model_parameters: getModelParams(params),
148
- $ai_input: withPrivacyMode(client, params.posthogPrivacyMode ?? false, safeInput),
149
- $ai_output_choices: withPrivacyMode(client, params.posthogPrivacyMode ?? false, safeOutput),
150
- $ai_http_status: httpStatus,
151
- $ai_input_tokens: usage.inputTokens ?? 0,
152
- $ai_output_tokens: usage.outputTokens ?? 0,
153
- ...additionalTokenValues,
154
- $ai_latency: latency,
155
- $ai_trace_id: traceId,
156
- $ai_base_url: baseURL,
157
- ...params.posthogProperties,
158
- ...(distinctId ? {} : {
159
- $process_person_profile: false
160
- }),
161
- ...(tools ? {
162
- $ai_tools: tools
163
- } : {}),
164
- ...errorData,
165
- ...costOverrideData
124
+ }
125
+ let costOverrideData = {};
126
+ if (params.posthogCostOverride) {
127
+ const inputCostUSD = (params.posthogCostOverride.inputCost ?? 0) * (usage.inputTokens ?? 0);
128
+ const outputCostUSD = (params.posthogCostOverride.outputCost ?? 0) * (usage.outputTokens ?? 0);
129
+ costOverrideData = {
130
+ $ai_input_cost_usd: inputCostUSD,
131
+ $ai_output_cost_usd: outputCostUSD,
132
+ $ai_total_cost_usd: inputCostUSD + outputCostUSD
166
133
  };
167
- if (fullDebug) {
168
- // @ts-ignore
169
- console.log('Sending event to PostHog', JSON.stringify(properties));
170
- try {
171
- // @ts-ignore
172
- console.log('Size of properties (kb)', Math.round(Buffer.byteLength(JSON.stringify(properties), STRING_FORMAT) / 1024 * 10000) / 10000);
173
- // @ts-ignore
174
- console.log('Size of properties (mb)', Math.round(Buffer.byteLength(JSON.stringify(properties), STRING_FORMAT) / 1024 / 1024 * 10000) / 10000);
175
- } catch (error) {
176
- console.error('Error printing size of properties', error);
177
- }
178
- }
179
- client.capture({
180
- distinctId: distinctId ?? traceId,
181
- event: '$ai_generation',
182
- properties,
183
- groups: params.posthogGroups
184
- });
134
+ }
135
+ const additionalTokenValues = {
136
+ ...(usage.reasoningTokens ? {
137
+ $ai_reasoning_tokens: usage.reasoningTokens
138
+ } : {}),
139
+ ...(usage.cacheReadInputTokens ? {
140
+ $ai_cache_read_input_tokens: usage.cacheReadInputTokens
141
+ } : {}),
142
+ ...(usage.cacheCreationInputTokens ? {
143
+ $ai_cache_creation_input_tokens: usage.cacheCreationInputTokens
144
+ } : {})
145
+ };
146
+ const properties = {
147
+ $ai_provider: params.posthogProviderOverride ?? provider,
148
+ $ai_model: params.posthogModelOverride ?? model,
149
+ $ai_model_parameters: getModelParams(params),
150
+ $ai_input: withPrivacyMode(client, params.posthogPrivacyMode ?? false, safeInput),
151
+ $ai_output_choices: withPrivacyMode(client, params.posthogPrivacyMode ?? false, safeOutput),
152
+ $ai_http_status: httpStatus,
153
+ $ai_input_tokens: usage.inputTokens ?? 0,
154
+ $ai_output_tokens: usage.outputTokens ?? 0,
155
+ ...additionalTokenValues,
156
+ $ai_latency: latency,
157
+ $ai_trace_id: traceId,
158
+ $ai_base_url: baseURL,
159
+ ...params.posthogProperties,
160
+ ...(distinctId ? {} : {
161
+ $process_person_profile: false
162
+ }),
163
+ ...(tools ? {
164
+ $ai_tools: tools
165
+ } : {}),
166
+ ...errorData,
167
+ ...costOverrideData
168
+ };
169
+ const event = {
170
+ distinctId: distinctId ?? traceId,
171
+ event: '$ai_generation',
172
+ properties,
173
+ groups: params.posthogGroups
174
+ };
175
+ if (captureImmediate) {
176
+ // await capture promise to send single event in serverless environments
177
+ await client.captureImmediate(event);
178
+ } else {
179
+ client.capture(event);
185
180
  }
186
181
  };
187
182
 
@@ -216,6 +211,7 @@ class WrappedCompletions$1 extends OpenAIOrignal.Chat.Completions {
216
211
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
217
212
  posthogPrivacyMode = false,
218
213
  posthogGroups,
214
+ posthogCaptureImmediate,
219
215
  ...openAIParams
220
216
  } = body;
221
217
  const traceId = posthogTraceId ?? v4();
@@ -245,7 +241,7 @@ class WrappedCompletions$1 extends OpenAIOrignal.Chat.Completions {
245
241
  }
246
242
  }
247
243
  const latency = (Date.now() - startTime) / 1000;
248
- sendEventToPosthog({
244
+ await sendEventToPosthog({
249
245
  client: this.phClient,
250
246
  distinctId: posthogDistinctId ?? traceId,
251
247
  traceId,
@@ -260,10 +256,11 @@ class WrappedCompletions$1 extends OpenAIOrignal.Chat.Completions {
260
256
  baseURL: this.baseURL ?? '',
261
257
  params: body,
262
258
  httpStatus: 200,
263
- usage
259
+ usage,
260
+ captureImmediate: posthogCaptureImmediate
264
261
  });
265
262
  } catch (error) {
266
- sendEventToPosthog({
263
+ await sendEventToPosthog({
267
264
  client: this.phClient,
268
265
  distinctId: posthogDistinctId ?? traceId,
269
266
  traceId,
@@ -280,7 +277,8 @@ class WrappedCompletions$1 extends OpenAIOrignal.Chat.Completions {
280
277
  outputTokens: 0
281
278
  },
282
279
  isError: true,
283
- error: JSON.stringify(error)
280
+ error: JSON.stringify(error),
281
+ captureImmediate: posthogCaptureImmediate
284
282
  });
285
283
  }
286
284
  })();
@@ -290,10 +288,10 @@ class WrappedCompletions$1 extends OpenAIOrignal.Chat.Completions {
290
288
  return value;
291
289
  });
292
290
  } else {
293
- const wrappedPromise = parentPromise.then(result => {
291
+ const wrappedPromise = parentPromise.then(async result => {
294
292
  if ('choices' in result) {
295
293
  const latency = (Date.now() - startTime) / 1000;
296
- sendEventToPosthog({
294
+ await sendEventToPosthog({
297
295
  client: this.phClient,
298
296
  distinctId: posthogDistinctId ?? traceId,
299
297
  traceId,
@@ -310,12 +308,13 @@ class WrappedCompletions$1 extends OpenAIOrignal.Chat.Completions {
310
308
  outputTokens: result.usage?.completion_tokens ?? 0,
311
309
  reasoningTokens: result.usage?.completion_tokens_details?.reasoning_tokens ?? 0,
312
310
  cacheReadInputTokens: result.usage?.prompt_tokens_details?.cached_tokens ?? 0
313
- }
311
+ },
312
+ captureImmediate: posthogCaptureImmediate
314
313
  });
315
314
  }
316
315
  return result;
317
- }, error => {
318
- sendEventToPosthog({
316
+ }, async error => {
317
+ await sendEventToPosthog({
319
318
  client: this.phClient,
320
319
  distinctId: posthogDistinctId ?? traceId,
321
320
  traceId,
@@ -332,7 +331,8 @@ class WrappedCompletions$1 extends OpenAIOrignal.Chat.Completions {
332
331
  outputTokens: 0
333
332
  },
334
333
  isError: true,
335
- error: JSON.stringify(error)
334
+ error: JSON.stringify(error),
335
+ captureImmediate: posthogCaptureImmediate
336
336
  });
337
337
  throw error;
338
338
  });
@@ -372,6 +372,7 @@ class WrappedCompletions extends AzureOpenAI.Chat.Completions {
372
372
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
373
373
  posthogPrivacyMode = false,
374
374
  posthogGroups,
375
+ posthogCaptureImmediate,
375
376
  ...openAIParams
376
377
  } = body;
377
378
  const traceId = posthogTraceId ?? v4();
@@ -405,7 +406,7 @@ class WrappedCompletions extends AzureOpenAI.Chat.Completions {
405
406
  }
406
407
  }
407
408
  const latency = (Date.now() - startTime) / 1000;
408
- sendEventToPosthog({
409
+ await sendEventToPosthog({
409
410
  client: this.phClient,
410
411
  distinctId: posthogDistinctId ?? traceId,
411
412
  traceId,
@@ -420,11 +421,12 @@ class WrappedCompletions extends AzureOpenAI.Chat.Completions {
420
421
  baseURL: this.baseURL ?? '',
421
422
  params: body,
422
423
  httpStatus: 200,
423
- usage
424
+ usage,
425
+ captureImmediate: posthogCaptureImmediate
424
426
  });
425
427
  } catch (error) {
426
428
  // error handling
427
- sendEventToPosthog({
429
+ await sendEventToPosthog({
428
430
  client: this.phClient,
429
431
  distinctId: posthogDistinctId ?? traceId,
430
432
  traceId,
@@ -441,7 +443,8 @@ class WrappedCompletions extends AzureOpenAI.Chat.Completions {
441
443
  outputTokens: 0
442
444
  },
443
445
  isError: true,
444
- error: JSON.stringify(error)
446
+ error: JSON.stringify(error),
447
+ captureImmediate: posthogCaptureImmediate
445
448
  });
446
449
  }
447
450
  })();
@@ -451,14 +454,14 @@ class WrappedCompletions extends AzureOpenAI.Chat.Completions {
451
454
  return value;
452
455
  });
453
456
  } else {
454
- const wrappedPromise = parentPromise.then(result => {
457
+ const wrappedPromise = parentPromise.then(async result => {
455
458
  if ('choices' in result) {
456
459
  const latency = (Date.now() - startTime) / 1000;
457
460
  let model = openAIParams.model;
458
461
  if (result.model != model) {
459
462
  model = result.model;
460
463
  }
461
- sendEventToPosthog({
464
+ await sendEventToPosthog({
462
465
  client: this.phClient,
463
466
  distinctId: posthogDistinctId ?? traceId,
464
467
  traceId,
@@ -475,12 +478,13 @@ class WrappedCompletions extends AzureOpenAI.Chat.Completions {
475
478
  outputTokens: result.usage?.completion_tokens ?? 0,
476
479
  reasoningTokens: result.usage?.completion_tokens_details?.reasoning_tokens ?? 0,
477
480
  cacheReadInputTokens: result.usage?.prompt_tokens_details?.cached_tokens ?? 0
478
- }
481
+ },
482
+ captureImmediate: posthogCaptureImmediate
479
483
  });
480
484
  }
481
485
  return result;
482
- }, error => {
483
- sendEventToPosthog({
486
+ }, async error => {
487
+ await sendEventToPosthog({
484
488
  client: this.phClient,
485
489
  distinctId: posthogDistinctId ?? traceId,
486
490
  traceId,
@@ -497,7 +501,8 @@ class WrappedCompletions extends AzureOpenAI.Chat.Completions {
497
501
  outputTokens: 0
498
502
  },
499
503
  isError: true,
500
- error: JSON.stringify(error)
504
+ error: JSON.stringify(error),
505
+ captureImmediate: posthogCaptureImmediate
501
506
  });
502
507
  throw error;
503
508
  });
@@ -596,7 +601,9 @@ const mapVercelPrompt = prompt => {
596
601
  // Trim the inputs array until its JSON size fits within MAX_OUTPUT_SIZE
597
602
  let serialized = JSON.stringify(inputs);
598
603
  let removedCount = 0;
599
- while (Buffer.byteLength(serialized, 'utf8') > MAX_OUTPUT_SIZE && inputs.length > 0) {
604
+ // We need to keep track of the initial size of the inputs array because we're going to be mutating it
605
+ let initialSize = inputs.length;
606
+ for (let i = 0; i < initialSize && Buffer.byteLength(serialized, 'utf8') > MAX_OUTPUT_SIZE; i++) {
600
607
  inputs.shift();
601
608
  removedCount++;
602
609
  serialized = JSON.stringify(inputs);
@@ -604,7 +611,7 @@ const mapVercelPrompt = prompt => {
604
611
  if (removedCount > 0) {
605
612
  // Add one placeholder to indicate how many were removed
606
613
  inputs.unshift({
607
- role: 'assistant',
614
+ role: 'posthog',
608
615
  content: `[${removedCount} message${removedCount === 1 ? '' : 's'} removed due to size limit]`
609
616
  });
610
617
  }
@@ -710,7 +717,7 @@ const createInstrumentationMiddleware = (phClient, model, options) => {
710
717
  cacheCreationInputTokens: providerMetadata.anthropic.cacheCreationInputTokens
711
718
  } : {})
712
719
  };
713
- sendEventToPosthog({
720
+ await sendEventToPosthog({
714
721
  client: phClient,
715
722
  distinctId: options.posthogDistinctId,
716
723
  traceId: options.posthogTraceId,
@@ -730,12 +737,12 @@ const createInstrumentationMiddleware = (phClient, model, options) => {
730
737
  outputTokens: result.usage.completionTokens,
731
738
  ...additionalTokenValues
732
739
  },
733
- fullDebug: options.fullDebug
740
+ captureImmediate: options.posthogCaptureImmediate
734
741
  });
735
742
  return result;
736
743
  } catch (error) {
737
744
  const modelId = model.modelId;
738
- sendEventToPosthog({
745
+ await sendEventToPosthog({
739
746
  client: phClient,
740
747
  distinctId: options.posthogDistinctId,
741
748
  traceId: options.posthogTraceId,
@@ -753,7 +760,7 @@ const createInstrumentationMiddleware = (phClient, model, options) => {
753
760
  },
754
761
  isError: true,
755
762
  error: truncate(JSON.stringify(error)),
756
- fullDebug: options.fullDebug
763
+ captureImmediate: options.posthogCaptureImmediate
757
764
  });
758
765
  throw error;
759
766
  }
@@ -802,9 +809,9 @@ const createInstrumentationMiddleware = (phClient, model, options) => {
802
809
  }
803
810
  controller.enqueue(chunk);
804
811
  },
805
- flush() {
812
+ flush: async () => {
806
813
  const latency = (Date.now() - startTime) / 1000;
807
- sendEventToPosthog({
814
+ await sendEventToPosthog({
808
815
  client: phClient,
809
816
  distinctId: options.posthogDistinctId,
810
817
  traceId: options.posthogTraceId,
@@ -820,7 +827,7 @@ const createInstrumentationMiddleware = (phClient, model, options) => {
820
827
  params: mergedParams,
821
828
  httpStatus: 200,
822
829
  usage,
823
- fullDebug: options.fullDebug
830
+ captureImmediate: options.posthogCaptureImmediate
824
831
  });
825
832
  }
826
833
  });
@@ -829,7 +836,7 @@ const createInstrumentationMiddleware = (phClient, model, options) => {
829
836
  ...rest
830
837
  };
831
838
  } catch (error) {
832
- sendEventToPosthog({
839
+ await sendEventToPosthog({
833
840
  client: phClient,
834
841
  distinctId: options.posthogDistinctId,
835
842
  traceId: options.posthogTraceId,
@@ -847,7 +854,7 @@ const createInstrumentationMiddleware = (phClient, model, options) => {
847
854
  },
848
855
  isError: true,
849
856
  error: truncate(JSON.stringify(error)),
850
- fullDebug: options.fullDebug
857
+ captureImmediate: options.posthogCaptureImmediate
851
858
  });
852
859
  throw error;
853
860
  }
@@ -893,6 +900,7 @@ class WrappedMessages extends AnthropicOriginal.Messages {
893
900
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
894
901
  posthogPrivacyMode = false,
895
902
  posthogGroups,
903
+ posthogCaptureImmediate,
896
904
  ...anthropicParams
897
905
  } = body;
898
906
  const traceId = posthogTraceId ?? v4();
@@ -928,7 +936,7 @@ class WrappedMessages extends AnthropicOriginal.Messages {
928
936
  }
929
937
  }
930
938
  const latency = (Date.now() - startTime) / 1000;
931
- sendEventToPosthog({
939
+ await sendEventToPosthog({
932
940
  client: this.phClient,
933
941
  distinctId: posthogDistinctId ?? traceId,
934
942
  traceId,
@@ -943,11 +951,12 @@ class WrappedMessages extends AnthropicOriginal.Messages {
943
951
  baseURL: this.baseURL ?? '',
944
952
  params: body,
945
953
  httpStatus: 200,
946
- usage
954
+ usage,
955
+ captureImmediate: posthogCaptureImmediate
947
956
  });
948
957
  } catch (error) {
949
958
  // error handling
950
- sendEventToPosthog({
959
+ await sendEventToPosthog({
951
960
  client: this.phClient,
952
961
  distinctId: posthogDistinctId ?? traceId,
953
962
  traceId,
@@ -964,7 +973,8 @@ class WrappedMessages extends AnthropicOriginal.Messages {
964
973
  outputTokens: 0
965
974
  },
966
975
  isError: true,
967
- error: JSON.stringify(error)
976
+ error: JSON.stringify(error),
977
+ captureImmediate: posthogCaptureImmediate
968
978
  });
969
979
  }
970
980
  })();
@@ -974,10 +984,10 @@ class WrappedMessages extends AnthropicOriginal.Messages {
974
984
  return value;
975
985
  });
976
986
  } else {
977
- const wrappedPromise = parentPromise.then(result => {
987
+ const wrappedPromise = parentPromise.then(async result => {
978
988
  if ('content' in result) {
979
989
  const latency = (Date.now() - startTime) / 1000;
980
- sendEventToPosthog({
990
+ await sendEventToPosthog({
981
991
  client: this.phClient,
982
992
  distinctId: posthogDistinctId ?? traceId,
983
993
  traceId,
@@ -994,12 +1004,13 @@ class WrappedMessages extends AnthropicOriginal.Messages {
994
1004
  outputTokens: result.usage.output_tokens ?? 0,
995
1005
  cacheCreationInputTokens: result.usage.cache_creation_input_tokens ?? 0,
996
1006
  cacheReadInputTokens: result.usage.cache_read_input_tokens ?? 0
997
- }
1007
+ },
1008
+ captureImmediate: posthogCaptureImmediate
998
1009
  });
999
1010
  }
1000
1011
  return result;
1001
- }, error => {
1002
- sendEventToPosthog({
1012
+ }, async error => {
1013
+ await sendEventToPosthog({
1003
1014
  client: this.phClient,
1004
1015
  distinctId: posthogDistinctId ?? traceId,
1005
1016
  traceId,
@@ -1016,7 +1027,8 @@ class WrappedMessages extends AnthropicOriginal.Messages {
1016
1027
  outputTokens: 0
1017
1028
  },
1018
1029
  isError: true,
1019
- error: JSON.stringify(error)
1030
+ error: JSON.stringify(error),
1031
+ captureImmediate: posthogCaptureImmediate
1020
1032
  });
1021
1033
  throw error;
1022
1034
  });