@posthog/ai 4.2.1 → 4.3.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/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,75 @@ 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) return Promise.resolve();
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
143
121
  };
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
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
166
131
  };
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
- });
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
+ } : {})
143
+ };
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
166
+ };
167
+ const event = {
168
+ distinctId: distinctId ?? traceId,
169
+ event: '$ai_generation',
170
+ properties,
171
+ groups: params.posthogGroups
172
+ };
173
+ if (captureImmediate) {
174
+ // await capture promise to send single event in serverless environments
175
+ await client.captureImmediate(event);
176
+ } else {
177
+ client.capture(event);
185
178
  }
186
179
  };
187
180
 
@@ -216,6 +209,7 @@ class WrappedCompletions$1 extends OpenAIOrignal.Chat.Completions {
216
209
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
217
210
  posthogPrivacyMode = false,
218
211
  posthogGroups,
212
+ posthogCaptureImmediate,
219
213
  ...openAIParams
220
214
  } = body;
221
215
  const traceId = posthogTraceId ?? v4();
@@ -245,7 +239,7 @@ class WrappedCompletions$1 extends OpenAIOrignal.Chat.Completions {
245
239
  }
246
240
  }
247
241
  const latency = (Date.now() - startTime) / 1000;
248
- sendEventToPosthog({
242
+ await sendEventToPosthog({
249
243
  client: this.phClient,
250
244
  distinctId: posthogDistinctId ?? traceId,
251
245
  traceId,
@@ -260,10 +254,11 @@ class WrappedCompletions$1 extends OpenAIOrignal.Chat.Completions {
260
254
  baseURL: this.baseURL ?? '',
261
255
  params: body,
262
256
  httpStatus: 200,
263
- usage
257
+ usage,
258
+ captureImmediate: posthogCaptureImmediate
264
259
  });
265
260
  } catch (error) {
266
- sendEventToPosthog({
261
+ await sendEventToPosthog({
267
262
  client: this.phClient,
268
263
  distinctId: posthogDistinctId ?? traceId,
269
264
  traceId,
@@ -280,7 +275,8 @@ class WrappedCompletions$1 extends OpenAIOrignal.Chat.Completions {
280
275
  outputTokens: 0
281
276
  },
282
277
  isError: true,
283
- error: JSON.stringify(error)
278
+ error: JSON.stringify(error),
279
+ captureImmediate: posthogCaptureImmediate
284
280
  });
285
281
  }
286
282
  })();
@@ -290,10 +286,10 @@ class WrappedCompletions$1 extends OpenAIOrignal.Chat.Completions {
290
286
  return value;
291
287
  });
292
288
  } else {
293
- const wrappedPromise = parentPromise.then(result => {
289
+ const wrappedPromise = parentPromise.then(async result => {
294
290
  if ('choices' in result) {
295
291
  const latency = (Date.now() - startTime) / 1000;
296
- sendEventToPosthog({
292
+ await sendEventToPosthog({
297
293
  client: this.phClient,
298
294
  distinctId: posthogDistinctId ?? traceId,
299
295
  traceId,
@@ -310,12 +306,13 @@ class WrappedCompletions$1 extends OpenAIOrignal.Chat.Completions {
310
306
  outputTokens: result.usage?.completion_tokens ?? 0,
311
307
  reasoningTokens: result.usage?.completion_tokens_details?.reasoning_tokens ?? 0,
312
308
  cacheReadInputTokens: result.usage?.prompt_tokens_details?.cached_tokens ?? 0
313
- }
309
+ },
310
+ captureImmediate: posthogCaptureImmediate
314
311
  });
315
312
  }
316
313
  return result;
317
- }, error => {
318
- sendEventToPosthog({
314
+ }, async error => {
315
+ await sendEventToPosthog({
319
316
  client: this.phClient,
320
317
  distinctId: posthogDistinctId ?? traceId,
321
318
  traceId,
@@ -332,7 +329,8 @@ class WrappedCompletions$1 extends OpenAIOrignal.Chat.Completions {
332
329
  outputTokens: 0
333
330
  },
334
331
  isError: true,
335
- error: JSON.stringify(error)
332
+ error: JSON.stringify(error),
333
+ captureImmediate: posthogCaptureImmediate
336
334
  });
337
335
  throw error;
338
336
  });
@@ -372,6 +370,7 @@ class WrappedCompletions extends AzureOpenAI.Chat.Completions {
372
370
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
373
371
  posthogPrivacyMode = false,
374
372
  posthogGroups,
373
+ posthogCaptureImmediate,
375
374
  ...openAIParams
376
375
  } = body;
377
376
  const traceId = posthogTraceId ?? v4();
@@ -405,7 +404,7 @@ class WrappedCompletions extends AzureOpenAI.Chat.Completions {
405
404
  }
406
405
  }
407
406
  const latency = (Date.now() - startTime) / 1000;
408
- sendEventToPosthog({
407
+ await sendEventToPosthog({
409
408
  client: this.phClient,
410
409
  distinctId: posthogDistinctId ?? traceId,
411
410
  traceId,
@@ -420,11 +419,12 @@ class WrappedCompletions extends AzureOpenAI.Chat.Completions {
420
419
  baseURL: this.baseURL ?? '',
421
420
  params: body,
422
421
  httpStatus: 200,
423
- usage
422
+ usage,
423
+ captureImmediate: posthogCaptureImmediate
424
424
  });
425
425
  } catch (error) {
426
426
  // error handling
427
- sendEventToPosthog({
427
+ await sendEventToPosthog({
428
428
  client: this.phClient,
429
429
  distinctId: posthogDistinctId ?? traceId,
430
430
  traceId,
@@ -441,7 +441,8 @@ class WrappedCompletions extends AzureOpenAI.Chat.Completions {
441
441
  outputTokens: 0
442
442
  },
443
443
  isError: true,
444
- error: JSON.stringify(error)
444
+ error: JSON.stringify(error),
445
+ captureImmediate: posthogCaptureImmediate
445
446
  });
446
447
  }
447
448
  })();
@@ -451,14 +452,14 @@ class WrappedCompletions extends AzureOpenAI.Chat.Completions {
451
452
  return value;
452
453
  });
453
454
  } else {
454
- const wrappedPromise = parentPromise.then(result => {
455
+ const wrappedPromise = parentPromise.then(async result => {
455
456
  if ('choices' in result) {
456
457
  const latency = (Date.now() - startTime) / 1000;
457
458
  let model = openAIParams.model;
458
459
  if (result.model != model) {
459
460
  model = result.model;
460
461
  }
461
- sendEventToPosthog({
462
+ await sendEventToPosthog({
462
463
  client: this.phClient,
463
464
  distinctId: posthogDistinctId ?? traceId,
464
465
  traceId,
@@ -475,12 +476,13 @@ class WrappedCompletions extends AzureOpenAI.Chat.Completions {
475
476
  outputTokens: result.usage?.completion_tokens ?? 0,
476
477
  reasoningTokens: result.usage?.completion_tokens_details?.reasoning_tokens ?? 0,
477
478
  cacheReadInputTokens: result.usage?.prompt_tokens_details?.cached_tokens ?? 0
478
- }
479
+ },
480
+ captureImmediate: posthogCaptureImmediate
479
481
  });
480
482
  }
481
483
  return result;
482
- }, error => {
483
- sendEventToPosthog({
484
+ }, async error => {
485
+ await sendEventToPosthog({
484
486
  client: this.phClient,
485
487
  distinctId: posthogDistinctId ?? traceId,
486
488
  traceId,
@@ -497,7 +499,8 @@ class WrappedCompletions extends AzureOpenAI.Chat.Completions {
497
499
  outputTokens: 0
498
500
  },
499
501
  isError: true,
500
- error: JSON.stringify(error)
502
+ error: JSON.stringify(error),
503
+ captureImmediate: posthogCaptureImmediate
501
504
  });
502
505
  throw error;
503
506
  });
@@ -596,7 +599,9 @@ const mapVercelPrompt = prompt => {
596
599
  // Trim the inputs array until its JSON size fits within MAX_OUTPUT_SIZE
597
600
  let serialized = JSON.stringify(inputs);
598
601
  let removedCount = 0;
599
- while (Buffer.byteLength(serialized, 'utf8') > MAX_OUTPUT_SIZE && inputs.length > 0) {
602
+ // We need to keep track of the initial size of the inputs array because we're going to be mutating it
603
+ let initialSize = inputs.length;
604
+ for (let i = 0; i < initialSize && Buffer.byteLength(serialized, 'utf8') > MAX_OUTPUT_SIZE; i++) {
600
605
  inputs.shift();
601
606
  removedCount++;
602
607
  serialized = JSON.stringify(inputs);
@@ -604,7 +609,7 @@ const mapVercelPrompt = prompt => {
604
609
  if (removedCount > 0) {
605
610
  // Add one placeholder to indicate how many were removed
606
611
  inputs.unshift({
607
- role: 'assistant',
612
+ role: 'posthog',
608
613
  content: `[${removedCount} message${removedCount === 1 ? '' : 's'} removed due to size limit]`
609
614
  });
610
615
  }
@@ -710,7 +715,7 @@ const createInstrumentationMiddleware = (phClient, model, options) => {
710
715
  cacheCreationInputTokens: providerMetadata.anthropic.cacheCreationInputTokens
711
716
  } : {})
712
717
  };
713
- sendEventToPosthog({
718
+ await sendEventToPosthog({
714
719
  client: phClient,
715
720
  distinctId: options.posthogDistinctId,
716
721
  traceId: options.posthogTraceId,
@@ -730,12 +735,12 @@ const createInstrumentationMiddleware = (phClient, model, options) => {
730
735
  outputTokens: result.usage.completionTokens,
731
736
  ...additionalTokenValues
732
737
  },
733
- fullDebug: options.fullDebug
738
+ captureImmediate: options.posthogCaptureImmediate
734
739
  });
735
740
  return result;
736
741
  } catch (error) {
737
742
  const modelId = model.modelId;
738
- sendEventToPosthog({
743
+ await sendEventToPosthog({
739
744
  client: phClient,
740
745
  distinctId: options.posthogDistinctId,
741
746
  traceId: options.posthogTraceId,
@@ -753,7 +758,7 @@ const createInstrumentationMiddleware = (phClient, model, options) => {
753
758
  },
754
759
  isError: true,
755
760
  error: truncate(JSON.stringify(error)),
756
- fullDebug: options.fullDebug
761
+ captureImmediate: options.posthogCaptureImmediate
757
762
  });
758
763
  throw error;
759
764
  }
@@ -802,9 +807,9 @@ const createInstrumentationMiddleware = (phClient, model, options) => {
802
807
  }
803
808
  controller.enqueue(chunk);
804
809
  },
805
- flush() {
810
+ flush: async () => {
806
811
  const latency = (Date.now() - startTime) / 1000;
807
- sendEventToPosthog({
812
+ await sendEventToPosthog({
808
813
  client: phClient,
809
814
  distinctId: options.posthogDistinctId,
810
815
  traceId: options.posthogTraceId,
@@ -820,7 +825,7 @@ const createInstrumentationMiddleware = (phClient, model, options) => {
820
825
  params: mergedParams,
821
826
  httpStatus: 200,
822
827
  usage,
823
- fullDebug: options.fullDebug
828
+ captureImmediate: options.posthogCaptureImmediate
824
829
  });
825
830
  }
826
831
  });
@@ -829,7 +834,7 @@ const createInstrumentationMiddleware = (phClient, model, options) => {
829
834
  ...rest
830
835
  };
831
836
  } catch (error) {
832
- sendEventToPosthog({
837
+ await sendEventToPosthog({
833
838
  client: phClient,
834
839
  distinctId: options.posthogDistinctId,
835
840
  traceId: options.posthogTraceId,
@@ -847,7 +852,7 @@ const createInstrumentationMiddleware = (phClient, model, options) => {
847
852
  },
848
853
  isError: true,
849
854
  error: truncate(JSON.stringify(error)),
850
- fullDebug: options.fullDebug
855
+ captureImmediate: options.posthogCaptureImmediate
851
856
  });
852
857
  throw error;
853
858
  }
@@ -893,6 +898,7 @@ class WrappedMessages extends AnthropicOriginal.Messages {
893
898
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
894
899
  posthogPrivacyMode = false,
895
900
  posthogGroups,
901
+ posthogCaptureImmediate,
896
902
  ...anthropicParams
897
903
  } = body;
898
904
  const traceId = posthogTraceId ?? v4();
@@ -928,7 +934,7 @@ class WrappedMessages extends AnthropicOriginal.Messages {
928
934
  }
929
935
  }
930
936
  const latency = (Date.now() - startTime) / 1000;
931
- sendEventToPosthog({
937
+ await sendEventToPosthog({
932
938
  client: this.phClient,
933
939
  distinctId: posthogDistinctId ?? traceId,
934
940
  traceId,
@@ -943,11 +949,12 @@ class WrappedMessages extends AnthropicOriginal.Messages {
943
949
  baseURL: this.baseURL ?? '',
944
950
  params: body,
945
951
  httpStatus: 200,
946
- usage
952
+ usage,
953
+ captureImmediate: posthogCaptureImmediate
947
954
  });
948
955
  } catch (error) {
949
956
  // error handling
950
- sendEventToPosthog({
957
+ await sendEventToPosthog({
951
958
  client: this.phClient,
952
959
  distinctId: posthogDistinctId ?? traceId,
953
960
  traceId,
@@ -964,7 +971,8 @@ class WrappedMessages extends AnthropicOriginal.Messages {
964
971
  outputTokens: 0
965
972
  },
966
973
  isError: true,
967
- error: JSON.stringify(error)
974
+ error: JSON.stringify(error),
975
+ captureImmediate: posthogCaptureImmediate
968
976
  });
969
977
  }
970
978
  })();
@@ -974,10 +982,10 @@ class WrappedMessages extends AnthropicOriginal.Messages {
974
982
  return value;
975
983
  });
976
984
  } else {
977
- const wrappedPromise = parentPromise.then(result => {
985
+ const wrappedPromise = parentPromise.then(async result => {
978
986
  if ('content' in result) {
979
987
  const latency = (Date.now() - startTime) / 1000;
980
- sendEventToPosthog({
988
+ await sendEventToPosthog({
981
989
  client: this.phClient,
982
990
  distinctId: posthogDistinctId ?? traceId,
983
991
  traceId,
@@ -994,12 +1002,13 @@ class WrappedMessages extends AnthropicOriginal.Messages {
994
1002
  outputTokens: result.usage.output_tokens ?? 0,
995
1003
  cacheCreationInputTokens: result.usage.cache_creation_input_tokens ?? 0,
996
1004
  cacheReadInputTokens: result.usage.cache_read_input_tokens ?? 0
997
- }
1005
+ },
1006
+ captureImmediate: posthogCaptureImmediate
998
1007
  });
999
1008
  }
1000
1009
  return result;
1001
- }, error => {
1002
- sendEventToPosthog({
1010
+ }, async error => {
1011
+ await sendEventToPosthog({
1003
1012
  client: this.phClient,
1004
1013
  distinctId: posthogDistinctId ?? traceId,
1005
1014
  traceId,
@@ -1016,7 +1025,8 @@ class WrappedMessages extends AnthropicOriginal.Messages {
1016
1025
  outputTokens: 0
1017
1026
  },
1018
1027
  isError: true,
1019
- error: JSON.stringify(error)
1028
+ error: JSON.stringify(error),
1029
+ captureImmediate: posthogCaptureImmediate
1020
1030
  });
1021
1031
  throw error;
1022
1032
  });