@posthog/ai 4.2.0 → 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,74 +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', properties);
170
- }
171
- client.capture({
172
- distinctId: distinctId ?? traceId,
173
- event: '$ai_generation',
174
- properties,
175
- groups: params.posthogGroups
176
- });
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);
177
178
  }
178
179
  };
179
180
 
@@ -208,6 +209,7 @@ class WrappedCompletions$1 extends OpenAIOrignal.Chat.Completions {
208
209
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
209
210
  posthogPrivacyMode = false,
210
211
  posthogGroups,
212
+ posthogCaptureImmediate,
211
213
  ...openAIParams
212
214
  } = body;
213
215
  const traceId = posthogTraceId ?? v4();
@@ -237,7 +239,7 @@ class WrappedCompletions$1 extends OpenAIOrignal.Chat.Completions {
237
239
  }
238
240
  }
239
241
  const latency = (Date.now() - startTime) / 1000;
240
- sendEventToPosthog({
242
+ await sendEventToPosthog({
241
243
  client: this.phClient,
242
244
  distinctId: posthogDistinctId ?? traceId,
243
245
  traceId,
@@ -252,10 +254,11 @@ class WrappedCompletions$1 extends OpenAIOrignal.Chat.Completions {
252
254
  baseURL: this.baseURL ?? '',
253
255
  params: body,
254
256
  httpStatus: 200,
255
- usage
257
+ usage,
258
+ captureImmediate: posthogCaptureImmediate
256
259
  });
257
260
  } catch (error) {
258
- sendEventToPosthog({
261
+ await sendEventToPosthog({
259
262
  client: this.phClient,
260
263
  distinctId: posthogDistinctId ?? traceId,
261
264
  traceId,
@@ -272,7 +275,8 @@ class WrappedCompletions$1 extends OpenAIOrignal.Chat.Completions {
272
275
  outputTokens: 0
273
276
  },
274
277
  isError: true,
275
- error: JSON.stringify(error)
278
+ error: JSON.stringify(error),
279
+ captureImmediate: posthogCaptureImmediate
276
280
  });
277
281
  }
278
282
  })();
@@ -282,10 +286,10 @@ class WrappedCompletions$1 extends OpenAIOrignal.Chat.Completions {
282
286
  return value;
283
287
  });
284
288
  } else {
285
- const wrappedPromise = parentPromise.then(result => {
289
+ const wrappedPromise = parentPromise.then(async result => {
286
290
  if ('choices' in result) {
287
291
  const latency = (Date.now() - startTime) / 1000;
288
- sendEventToPosthog({
292
+ await sendEventToPosthog({
289
293
  client: this.phClient,
290
294
  distinctId: posthogDistinctId ?? traceId,
291
295
  traceId,
@@ -302,12 +306,13 @@ class WrappedCompletions$1 extends OpenAIOrignal.Chat.Completions {
302
306
  outputTokens: result.usage?.completion_tokens ?? 0,
303
307
  reasoningTokens: result.usage?.completion_tokens_details?.reasoning_tokens ?? 0,
304
308
  cacheReadInputTokens: result.usage?.prompt_tokens_details?.cached_tokens ?? 0
305
- }
309
+ },
310
+ captureImmediate: posthogCaptureImmediate
306
311
  });
307
312
  }
308
313
  return result;
309
- }, error => {
310
- sendEventToPosthog({
314
+ }, async error => {
315
+ await sendEventToPosthog({
311
316
  client: this.phClient,
312
317
  distinctId: posthogDistinctId ?? traceId,
313
318
  traceId,
@@ -324,7 +329,8 @@ class WrappedCompletions$1 extends OpenAIOrignal.Chat.Completions {
324
329
  outputTokens: 0
325
330
  },
326
331
  isError: true,
327
- error: JSON.stringify(error)
332
+ error: JSON.stringify(error),
333
+ captureImmediate: posthogCaptureImmediate
328
334
  });
329
335
  throw error;
330
336
  });
@@ -364,6 +370,7 @@ class WrappedCompletions extends AzureOpenAI.Chat.Completions {
364
370
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
365
371
  posthogPrivacyMode = false,
366
372
  posthogGroups,
373
+ posthogCaptureImmediate,
367
374
  ...openAIParams
368
375
  } = body;
369
376
  const traceId = posthogTraceId ?? v4();
@@ -397,7 +404,7 @@ class WrappedCompletions extends AzureOpenAI.Chat.Completions {
397
404
  }
398
405
  }
399
406
  const latency = (Date.now() - startTime) / 1000;
400
- sendEventToPosthog({
407
+ await sendEventToPosthog({
401
408
  client: this.phClient,
402
409
  distinctId: posthogDistinctId ?? traceId,
403
410
  traceId,
@@ -412,11 +419,12 @@ class WrappedCompletions extends AzureOpenAI.Chat.Completions {
412
419
  baseURL: this.baseURL ?? '',
413
420
  params: body,
414
421
  httpStatus: 200,
415
- usage
422
+ usage,
423
+ captureImmediate: posthogCaptureImmediate
416
424
  });
417
425
  } catch (error) {
418
426
  // error handling
419
- sendEventToPosthog({
427
+ await sendEventToPosthog({
420
428
  client: this.phClient,
421
429
  distinctId: posthogDistinctId ?? traceId,
422
430
  traceId,
@@ -433,7 +441,8 @@ class WrappedCompletions extends AzureOpenAI.Chat.Completions {
433
441
  outputTokens: 0
434
442
  },
435
443
  isError: true,
436
- error: JSON.stringify(error)
444
+ error: JSON.stringify(error),
445
+ captureImmediate: posthogCaptureImmediate
437
446
  });
438
447
  }
439
448
  })();
@@ -443,14 +452,14 @@ class WrappedCompletions extends AzureOpenAI.Chat.Completions {
443
452
  return value;
444
453
  });
445
454
  } else {
446
- const wrappedPromise = parentPromise.then(result => {
455
+ const wrappedPromise = parentPromise.then(async result => {
447
456
  if ('choices' in result) {
448
457
  const latency = (Date.now() - startTime) / 1000;
449
458
  let model = openAIParams.model;
450
459
  if (result.model != model) {
451
460
  model = result.model;
452
461
  }
453
- sendEventToPosthog({
462
+ await sendEventToPosthog({
454
463
  client: this.phClient,
455
464
  distinctId: posthogDistinctId ?? traceId,
456
465
  traceId,
@@ -467,12 +476,13 @@ class WrappedCompletions extends AzureOpenAI.Chat.Completions {
467
476
  outputTokens: result.usage?.completion_tokens ?? 0,
468
477
  reasoningTokens: result.usage?.completion_tokens_details?.reasoning_tokens ?? 0,
469
478
  cacheReadInputTokens: result.usage?.prompt_tokens_details?.cached_tokens ?? 0
470
- }
479
+ },
480
+ captureImmediate: posthogCaptureImmediate
471
481
  });
472
482
  }
473
483
  return result;
474
- }, error => {
475
- sendEventToPosthog({
484
+ }, async error => {
485
+ await sendEventToPosthog({
476
486
  client: this.phClient,
477
487
  distinctId: posthogDistinctId ?? traceId,
478
488
  traceId,
@@ -489,7 +499,8 @@ class WrappedCompletions extends AzureOpenAI.Chat.Completions {
489
499
  outputTokens: 0
490
500
  },
491
501
  isError: true,
492
- error: JSON.stringify(error)
502
+ error: JSON.stringify(error),
503
+ captureImmediate: posthogCaptureImmediate
493
504
  });
494
505
  throw error;
495
506
  });
@@ -587,18 +598,23 @@ const mapVercelPrompt = prompt => {
587
598
  try {
588
599
  // Trim the inputs array until its JSON size fits within MAX_OUTPUT_SIZE
589
600
  let serialized = JSON.stringify(inputs);
590
- while (Buffer.byteLength(serialized, 'utf8') > MAX_OUTPUT_SIZE && inputs.length > 0) {
591
- // Remove oldest message
601
+ let removedCount = 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++) {
592
605
  inputs.shift();
593
- // add blank message to beginning of array
606
+ removedCount++;
607
+ serialized = JSON.stringify(inputs);
608
+ }
609
+ if (removedCount > 0) {
610
+ // Add one placeholder to indicate how many were removed
594
611
  inputs.unshift({
595
- role: 'assistant',
596
- content: '[removed message due to size limit]'
612
+ role: 'posthog',
613
+ content: `[${removedCount} message${removedCount === 1 ? '' : 's'} removed due to size limit]`
597
614
  });
598
- serialized = JSON.stringify(inputs);
599
615
  }
600
616
  } catch (error) {
601
- console.error('Error stringifying inputs');
617
+ console.error('Error stringifying inputs', error);
602
618
  return [{
603
619
  role: 'posthog',
604
620
  content: 'An error occurred while processing your request. Please try again.'
@@ -699,7 +715,7 @@ const createInstrumentationMiddleware = (phClient, model, options) => {
699
715
  cacheCreationInputTokens: providerMetadata.anthropic.cacheCreationInputTokens
700
716
  } : {})
701
717
  };
702
- sendEventToPosthog({
718
+ await sendEventToPosthog({
703
719
  client: phClient,
704
720
  distinctId: options.posthogDistinctId,
705
721
  traceId: options.posthogTraceId,
@@ -719,12 +735,12 @@ const createInstrumentationMiddleware = (phClient, model, options) => {
719
735
  outputTokens: result.usage.completionTokens,
720
736
  ...additionalTokenValues
721
737
  },
722
- fullDebug: options.fullDebug
738
+ captureImmediate: options.posthogCaptureImmediate
723
739
  });
724
740
  return result;
725
741
  } catch (error) {
726
742
  const modelId = model.modelId;
727
- sendEventToPosthog({
743
+ await sendEventToPosthog({
728
744
  client: phClient,
729
745
  distinctId: options.posthogDistinctId,
730
746
  traceId: options.posthogTraceId,
@@ -742,7 +758,7 @@ const createInstrumentationMiddleware = (phClient, model, options) => {
742
758
  },
743
759
  isError: true,
744
760
  error: truncate(JSON.stringify(error)),
745
- fullDebug: options.fullDebug
761
+ captureImmediate: options.posthogCaptureImmediate
746
762
  });
747
763
  throw error;
748
764
  }
@@ -791,9 +807,9 @@ const createInstrumentationMiddleware = (phClient, model, options) => {
791
807
  }
792
808
  controller.enqueue(chunk);
793
809
  },
794
- flush() {
810
+ flush: async () => {
795
811
  const latency = (Date.now() - startTime) / 1000;
796
- sendEventToPosthog({
812
+ await sendEventToPosthog({
797
813
  client: phClient,
798
814
  distinctId: options.posthogDistinctId,
799
815
  traceId: options.posthogTraceId,
@@ -809,7 +825,7 @@ const createInstrumentationMiddleware = (phClient, model, options) => {
809
825
  params: mergedParams,
810
826
  httpStatus: 200,
811
827
  usage,
812
- fullDebug: options.fullDebug
828
+ captureImmediate: options.posthogCaptureImmediate
813
829
  });
814
830
  }
815
831
  });
@@ -818,7 +834,7 @@ const createInstrumentationMiddleware = (phClient, model, options) => {
818
834
  ...rest
819
835
  };
820
836
  } catch (error) {
821
- sendEventToPosthog({
837
+ await sendEventToPosthog({
822
838
  client: phClient,
823
839
  distinctId: options.posthogDistinctId,
824
840
  traceId: options.posthogTraceId,
@@ -836,7 +852,7 @@ const createInstrumentationMiddleware = (phClient, model, options) => {
836
852
  },
837
853
  isError: true,
838
854
  error: truncate(JSON.stringify(error)),
839
- fullDebug: options.fullDebug
855
+ captureImmediate: options.posthogCaptureImmediate
840
856
  });
841
857
  throw error;
842
858
  }
@@ -882,6 +898,7 @@ class WrappedMessages extends AnthropicOriginal.Messages {
882
898
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
883
899
  posthogPrivacyMode = false,
884
900
  posthogGroups,
901
+ posthogCaptureImmediate,
885
902
  ...anthropicParams
886
903
  } = body;
887
904
  const traceId = posthogTraceId ?? v4();
@@ -917,7 +934,7 @@ class WrappedMessages extends AnthropicOriginal.Messages {
917
934
  }
918
935
  }
919
936
  const latency = (Date.now() - startTime) / 1000;
920
- sendEventToPosthog({
937
+ await sendEventToPosthog({
921
938
  client: this.phClient,
922
939
  distinctId: posthogDistinctId ?? traceId,
923
940
  traceId,
@@ -932,11 +949,12 @@ class WrappedMessages extends AnthropicOriginal.Messages {
932
949
  baseURL: this.baseURL ?? '',
933
950
  params: body,
934
951
  httpStatus: 200,
935
- usage
952
+ usage,
953
+ captureImmediate: posthogCaptureImmediate
936
954
  });
937
955
  } catch (error) {
938
956
  // error handling
939
- sendEventToPosthog({
957
+ await sendEventToPosthog({
940
958
  client: this.phClient,
941
959
  distinctId: posthogDistinctId ?? traceId,
942
960
  traceId,
@@ -953,7 +971,8 @@ class WrappedMessages extends AnthropicOriginal.Messages {
953
971
  outputTokens: 0
954
972
  },
955
973
  isError: true,
956
- error: JSON.stringify(error)
974
+ error: JSON.stringify(error),
975
+ captureImmediate: posthogCaptureImmediate
957
976
  });
958
977
  }
959
978
  })();
@@ -963,10 +982,10 @@ class WrappedMessages extends AnthropicOriginal.Messages {
963
982
  return value;
964
983
  });
965
984
  } else {
966
- const wrappedPromise = parentPromise.then(result => {
985
+ const wrappedPromise = parentPromise.then(async result => {
967
986
  if ('content' in result) {
968
987
  const latency = (Date.now() - startTime) / 1000;
969
- sendEventToPosthog({
988
+ await sendEventToPosthog({
970
989
  client: this.phClient,
971
990
  distinctId: posthogDistinctId ?? traceId,
972
991
  traceId,
@@ -983,12 +1002,13 @@ class WrappedMessages extends AnthropicOriginal.Messages {
983
1002
  outputTokens: result.usage.output_tokens ?? 0,
984
1003
  cacheCreationInputTokens: result.usage.cache_creation_input_tokens ?? 0,
985
1004
  cacheReadInputTokens: result.usage.cache_read_input_tokens ?? 0
986
- }
1005
+ },
1006
+ captureImmediate: posthogCaptureImmediate
987
1007
  });
988
1008
  }
989
1009
  return result;
990
- }, error => {
991
- sendEventToPosthog({
1010
+ }, async error => {
1011
+ await sendEventToPosthog({
992
1012
  client: this.phClient,
993
1013
  distinctId: posthogDistinctId ?? traceId,
994
1014
  traceId,
@@ -1005,7 +1025,8 @@ class WrappedMessages extends AnthropicOriginal.Messages {
1005
1025
  outputTokens: 0
1006
1026
  },
1007
1027
  isError: true,
1008
- error: JSON.stringify(error)
1028
+ error: JSON.stringify(error),
1029
+ captureImmediate: posthogCaptureImmediate
1009
1030
  });
1010
1031
  throw error;
1011
1032
  });