@posthog/ai 7.16.15 → 7.17.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/dist/index.mjs CHANGED
@@ -5,8 +5,6 @@ import { uuidv7 } from '@posthog/core';
5
5
  import AnthropicOriginal from '@anthropic-ai/sdk';
6
6
  import { GoogleGenAI } from '@google/genai';
7
7
 
8
- var version = "7.16.15";
9
-
10
8
  // Type guards for safer type checking
11
9
  const isString = value => {
12
10
  return typeof value === 'string';
@@ -244,6 +242,13 @@ function getTokensSource(posthogProperties) {
244
242
  // limit large outputs by truncating to 200kb (approx 200k bytes)
245
243
  const MAX_OUTPUT_SIZE = 200000;
246
244
  const STRING_FORMAT = 'utf8';
245
+ // Reused across calls to avoid per-invocation allocation; truncate() runs
246
+ // hundreds of times for prompts with many parts.
247
+ const sharedTextEncoder = new TextEncoder();
248
+ const sharedTextDecoder = new TextDecoder(STRING_FORMAT, {
249
+ fatal: false
250
+ });
251
+ const utf8ByteLength = str => sharedTextEncoder.encode(str).byteLength;
247
252
  /**
248
253
  * Safely converts content to a string, preserving structure for objects/arrays.
249
254
  * - If content is already a string, returns it as-is
@@ -514,19 +519,15 @@ const truncate = input => {
514
519
  return '';
515
520
  }
516
521
  // Check if we need to truncate and ensure STRING_FORMAT is respected
517
- const encoder = new TextEncoder();
518
- const buffer = encoder.encode(str);
522
+ const buffer = sharedTextEncoder.encode(str);
519
523
  if (buffer.length <= MAX_OUTPUT_SIZE) {
520
524
  // Ensure STRING_FORMAT is respected
521
- return new TextDecoder(STRING_FORMAT).decode(buffer);
525
+ return sharedTextDecoder.decode(buffer);
522
526
  }
523
- // Truncate the buffer and ensure a valid string is returned
527
+ // Truncate the buffer and ensure a valid string is returned.
528
+ // fatal: false means we get U+FFFD at the end if truncation broke the encoding.
524
529
  const truncatedBuffer = buffer.slice(0, MAX_OUTPUT_SIZE);
525
- // fatal: false means we get U+FFFD at the end if truncation broke the encoding
526
- const decoder = new TextDecoder(STRING_FORMAT, {
527
- fatal: false
528
- });
529
- let truncatedStr = decoder.decode(truncatedBuffer);
530
+ let truncatedStr = sharedTextDecoder.decode(truncatedBuffer);
530
531
  if (truncatedStr.endsWith('\uFFFD')) {
531
532
  truncatedStr = truncatedStr.slice(0, -1);
532
533
  }
@@ -710,73 +711,107 @@ function addDefaults(params) {
710
711
  traceId: params.traceId ?? v4()
711
712
  };
712
713
  }
713
- const sendEventWithErrorToPosthog = async ({
714
- client,
715
- traceId,
716
- error,
717
- ...args
718
- }) => {
719
- const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
720
- const properties = {
721
- client,
722
- traceId,
723
- httpStatus,
724
- error: JSON.stringify(error),
725
- ...args
726
- };
727
- const enrichedError = error;
728
- if (client.options?.enableExceptionAutocapture) {
729
- // assign a uuid that can be used to link the trace and exception events
730
- const exceptionId = uuidv7();
731
- client.captureException(error, undefined, {
732
- $ai_trace_id: traceId
733
- }, exceptionId);
734
- enrichedError.__posthog_previously_captured_error = true;
735
- properties.exceptionId = exceptionId;
736
- }
737
- await sendEventToPosthog(properties);
738
- return enrichedError;
739
- };
740
- const sendEventToPosthog = async ({
741
- client,
742
- eventType = AIEvent.Generation,
743
- distinctId,
744
- traceId,
745
- model,
746
- provider,
747
- input,
748
- output,
749
- latency,
750
- timeToFirstToken,
751
- baseURL,
752
- params,
753
- httpStatus = 200,
754
- usage = {},
755
- error,
756
- exceptionId,
757
- stopReason,
758
- tools,
759
- captureImmediate = false
760
- }) => {
761
- if (!client.capture) {
762
- return Promise.resolve();
714
+ function formatOpenAIResponsesInput(input, instructions) {
715
+ const messages = [];
716
+ if (instructions) {
717
+ messages.push({
718
+ role: 'system',
719
+ content: instructions
720
+ });
763
721
  }
764
- // sanitize input and output for UTF-8 validity
765
- const safeInput = sanitizeValues(input);
766
- const safeOutput = sanitizeValues(output);
767
- const safeError = sanitizeValues(error);
722
+ if (Array.isArray(input)) {
723
+ for (const item of input) {
724
+ if (typeof item === 'string') {
725
+ messages.push({
726
+ role: 'user',
727
+ content: item
728
+ });
729
+ } else if (item && typeof item === 'object') {
730
+ const obj = item;
731
+ const role = isString(obj.role) ? obj.role : 'user';
732
+ // Handle content properly - preserve structure for objects/arrays
733
+ const content = obj.content ?? obj.text ?? item;
734
+ messages.push({
735
+ role,
736
+ content: toContentString(content)
737
+ });
738
+ } else {
739
+ messages.push({
740
+ role: 'user',
741
+ content: toContentString(item)
742
+ });
743
+ }
744
+ }
745
+ } else if (typeof input === 'string') {
746
+ messages.push({
747
+ role: 'user',
748
+ content: input
749
+ });
750
+ } else if (input) {
751
+ messages.push({
752
+ role: 'user',
753
+ content: toContentString(input)
754
+ });
755
+ }
756
+ return messages;
757
+ }
758
+
759
+ var version = "7.17.1";
760
+
761
+ /**
762
+ * Capture an `$ai_generation` (or `$ai_embedding`) event to PostHog.
763
+ *
764
+ * This is the canonical primitive that every `@posthog/ai` wrapper
765
+ * (`withTracing`, `OpenAI`, `Anthropic`, `GoogleGenAI`, …) funnels through, so
766
+ * external code can use it directly to instrument LLM calls made through
767
+ * arbitrary clients (Cloudflare Workers AI, custom HTTP, etc.) and get the
768
+ * same events the SDK wrappers produce.
769
+ *
770
+ * When `error` is set, the event is captured as an error. If the error is an
771
+ * object, it is mutated in place to set `__posthog_previously_captured_error`
772
+ * so callers can re-throw the original error reference safely.
773
+ */
774
+ const captureAiGeneration = async (client, options) => {
775
+ if (!client.capture) {
776
+ return;
777
+ }
778
+ const traceId = options.traceId ?? v4();
779
+ const eventType = options.eventType ?? AIEvent.Generation;
780
+ const privacyMode = options.privacyMode ?? false;
781
+ const usage = options.usage ?? {};
782
+ const safeInput = sanitizeValues(options.input);
783
+ const safeOutput = sanitizeValues(options.output);
784
+ let httpStatus = options.httpStatus;
768
785
  let errorData = {};
769
- if (error) {
786
+ if (options.error) {
787
+ if (httpStatus === undefined) {
788
+ if (typeof options.error === 'object' && 'status' in options.error && typeof options.error.status === 'number') {
789
+ httpStatus = options.error.status;
790
+ } else {
791
+ httpStatus = 500;
792
+ }
793
+ }
794
+ let exceptionId;
795
+ if (client.options?.enableExceptionAutocapture) {
796
+ exceptionId = uuidv7();
797
+ client.captureException(options.error, undefined, {
798
+ $ai_trace_id: traceId
799
+ }, exceptionId);
800
+ if (typeof options.error === 'object') {
801
+ options.error.__posthog_previously_captured_error = true;
802
+ }
803
+ }
770
804
  errorData = {
771
805
  $ai_is_error: true,
772
- $ai_error: safeError,
806
+ $ai_error: sanitizeValues(JSON.stringify(options.error)),
773
807
  $exception_event_id: exceptionId
774
808
  };
775
809
  }
810
+ httpStatus = httpStatus ?? 200;
776
811
  let costOverrideData = {};
777
- if (params.posthogCostOverride) {
778
- const inputCostUSD = (params.posthogCostOverride.inputCost ?? 0) * (usage.inputTokens ?? 0);
779
- const outputCostUSD = (params.posthogCostOverride.outputCost ?? 0) * (usage.outputTokens ?? 0);
812
+ if (options.costOverride) {
813
+ const inputCostUSD = (options.costOverride.inputCost ?? 0) * (usage.inputTokens ?? 0);
814
+ const outputCostUSD = (options.costOverride.outputCost ?? 0) * (usage.outputTokens ?? 0);
780
815
  costOverrideData = {
781
816
  $ai_input_cost_usd: inputCostUSD,
782
817
  $ai_output_cost_usd: outputCostUSD,
@@ -803,95 +838,49 @@ const sendEventToPosthog = async ({
803
838
  const properties = {
804
839
  $ai_lib: 'posthog-ai',
805
840
  $ai_lib_version: version,
806
- $ai_provider: params.posthogProviderOverride ?? provider,
807
- $ai_model: params.posthogModelOverride ?? model,
808
- $ai_model_parameters: getModelParams(params),
809
- $ai_input: withPrivacyMode(client, params.posthogPrivacyMode ?? false, safeInput),
810
- $ai_output_choices: withPrivacyMode(client, params.posthogPrivacyMode ?? false, safeOutput),
841
+ $ai_provider: options.providerOverride ?? options.provider,
842
+ $ai_model: options.modelOverride ?? options.model,
843
+ $ai_model_parameters: options.modelParameters ?? {},
844
+ $ai_input: withPrivacyMode(client, privacyMode, safeInput),
845
+ $ai_output_choices: withPrivacyMode(client, privacyMode, safeOutput),
811
846
  $ai_http_status: httpStatus,
812
847
  $ai_input_tokens: usage.inputTokens ?? 0,
813
848
  ...(usage.outputTokens !== undefined ? {
814
849
  $ai_output_tokens: usage.outputTokens
815
850
  } : {}),
816
851
  ...additionalTokenValues,
817
- $ai_latency: latency,
818
- ...(timeToFirstToken !== undefined ? {
819
- $ai_time_to_first_token: timeToFirstToken
852
+ $ai_latency: options.latency ?? 0,
853
+ ...(options.timeToFirstToken !== undefined ? {
854
+ $ai_time_to_first_token: options.timeToFirstToken
820
855
  } : {}),
821
856
  $ai_trace_id: traceId,
822
- $ai_base_url: baseURL,
823
- ...params.posthogProperties,
824
- $ai_tokens_source: getTokensSource(params.posthogProperties),
825
- ...(distinctId ? {} : {
857
+ $ai_base_url: options.baseURL ?? '',
858
+ ...options.properties,
859
+ $ai_tokens_source: getTokensSource(options.properties),
860
+ ...(options.distinctId ? {} : {
826
861
  $process_person_profile: false
827
862
  }),
828
- ...(stopReason ? {
829
- $ai_stop_reason: stopReason
863
+ ...(options.stopReason ? {
864
+ $ai_stop_reason: options.stopReason
830
865
  } : {}),
831
- ...(tools ? {
832
- $ai_tools: tools
866
+ ...(options.tools ? {
867
+ $ai_tools: options.tools
833
868
  } : {}),
834
869
  ...errorData,
835
870
  ...costOverrideData
836
871
  };
837
872
  const event = {
838
- distinctId: distinctId ?? traceId,
873
+ distinctId: options.distinctId ?? traceId,
839
874
  event: eventType,
840
875
  properties,
841
- groups: params.posthogGroups
876
+ groups: options.groups
842
877
  };
843
- if (captureImmediate) {
844
- // await capture promise to send single event in serverless environments
878
+ if (options.captureImmediate) {
845
879
  await client.captureImmediate(event);
846
880
  } else {
847
881
  client.capture(event);
848
882
  }
849
- return Promise.resolve();
850
883
  };
851
- function formatOpenAIResponsesInput(input, instructions) {
852
- const messages = [];
853
- if (instructions) {
854
- messages.push({
855
- role: 'system',
856
- content: instructions
857
- });
858
- }
859
- if (Array.isArray(input)) {
860
- for (const item of input) {
861
- if (typeof item === 'string') {
862
- messages.push({
863
- role: 'user',
864
- content: item
865
- });
866
- } else if (item && typeof item === 'object') {
867
- const obj = item;
868
- const role = isString(obj.role) ? obj.role : 'user';
869
- // Handle content properly - preserve structure for objects/arrays
870
- const content = obj.content ?? obj.text ?? item;
871
- messages.push({
872
- role,
873
- content: toContentString(content)
874
- });
875
- } else {
876
- messages.push({
877
- role: 'user',
878
- content: toContentString(item)
879
- });
880
- }
881
- }
882
- } else if (typeof input === 'string') {
883
- messages.push({
884
- role: 'user',
885
- content: input
886
- });
887
- } else if (input) {
888
- messages.push({
889
- role: 'user',
890
- content: toContentString(input)
891
- });
892
- }
893
- return messages;
894
- }
895
884
 
896
885
  /**
897
886
  * Checks if a ResponseStreamEvent chunk represents the first token/content from the model.
@@ -1060,8 +1049,7 @@ let WrappedCompletions$1 = class WrappedCompletions extends Completions {
1060
1049
  const latency = (Date.now() - startTime) / 1000;
1061
1050
  const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
1062
1051
  const availableTools = extractAvailableToolCalls('openai', openAIParams);
1063
- await sendEventToPosthog({
1064
- client: this.phClient,
1052
+ await captureAiGeneration(this.phClient, {
1065
1053
  ...posthogParams,
1066
1054
  model: openAIParams.model ?? modelFromResponse,
1067
1055
  provider: 'openai',
@@ -1070,7 +1058,7 @@ let WrappedCompletions$1 = class WrappedCompletions extends Completions {
1070
1058
  latency,
1071
1059
  timeToFirstToken,
1072
1060
  baseURL: this.baseURL,
1073
- params: body,
1061
+ modelParameters: getModelParams(body),
1074
1062
  httpStatus: 200,
1075
1063
  usage: {
1076
1064
  inputTokens: usage.inputTokens,
@@ -1084,8 +1072,7 @@ let WrappedCompletions$1 = class WrappedCompletions extends Completions {
1084
1072
  tools: availableTools
1085
1073
  });
1086
1074
  } catch (error) {
1087
- const enrichedError = await sendEventWithErrorToPosthog({
1088
- client: this.phClient,
1075
+ await captureAiGeneration(this.phClient, {
1089
1076
  ...posthogParams,
1090
1077
  model: openAIParams.model,
1091
1078
  provider: 'openai',
@@ -1093,14 +1080,14 @@ let WrappedCompletions$1 = class WrappedCompletions extends Completions {
1093
1080
  output: [],
1094
1081
  latency: 0,
1095
1082
  baseURL: this.baseURL,
1096
- params: body,
1083
+ modelParameters: getModelParams(body),
1097
1084
  usage: {
1098
1085
  inputTokens: 0,
1099
1086
  outputTokens: 0
1100
1087
  },
1101
1088
  error
1102
1089
  });
1103
- throw enrichedError;
1090
+ throw error;
1104
1091
  }
1105
1092
  })();
1106
1093
  // Return the other stream to the user
@@ -1114,8 +1101,7 @@ let WrappedCompletions$1 = class WrappedCompletions extends Completions {
1114
1101
  const latency = (Date.now() - startTime) / 1000;
1115
1102
  const availableTools = extractAvailableToolCalls('openai', openAIParams);
1116
1103
  const formattedOutput = formatResponseOpenAI(result);
1117
- await sendEventToPosthog({
1118
- client: this.phClient,
1104
+ await captureAiGeneration(this.phClient, {
1119
1105
  ...posthogParams,
1120
1106
  model: openAIParams.model ?? result.model,
1121
1107
  provider: 'openai',
@@ -1123,7 +1109,7 @@ let WrappedCompletions$1 = class WrappedCompletions extends Completions {
1123
1109
  output: formattedOutput,
1124
1110
  latency,
1125
1111
  baseURL: this.baseURL,
1126
- params: body,
1112
+ modelParameters: getModelParams(body),
1127
1113
  httpStatus: 200,
1128
1114
  usage: {
1129
1115
  inputTokens: result.usage?.prompt_tokens ?? 0,
@@ -1140,8 +1126,7 @@ let WrappedCompletions$1 = class WrappedCompletions extends Completions {
1140
1126
  return result;
1141
1127
  }, async error => {
1142
1128
  const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
1143
- await sendEventToPosthog({
1144
- client: this.phClient,
1129
+ await captureAiGeneration(this.phClient, {
1145
1130
  ...posthogParams,
1146
1131
  model: openAIParams.model,
1147
1132
  provider: 'openai',
@@ -1149,13 +1134,13 @@ let WrappedCompletions$1 = class WrappedCompletions extends Completions {
1149
1134
  output: [],
1150
1135
  latency: 0,
1151
1136
  baseURL: this.baseURL,
1152
- params: body,
1137
+ modelParameters: getModelParams(body),
1153
1138
  httpStatus,
1154
1139
  usage: {
1155
1140
  inputTokens: 0,
1156
1141
  outputTokens: 0
1157
1142
  },
1158
- error: JSON.stringify(error)
1143
+ error
1159
1144
  });
1160
1145
  throw error;
1161
1146
  });
@@ -1228,8 +1213,7 @@ let WrappedResponses$1 = class WrappedResponses extends Responses {
1228
1213
  const latency = (Date.now() - startTime) / 1000;
1229
1214
  const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
1230
1215
  const availableTools = extractAvailableToolCalls('openai', openAIParams);
1231
- await sendEventToPosthog({
1232
- client: this.phClient,
1216
+ await captureAiGeneration(this.phClient, {
1233
1217
  ...posthogParams,
1234
1218
  model: openAIParams.model ?? modelFromResponse,
1235
1219
  provider: 'openai',
@@ -1238,7 +1222,7 @@ let WrappedResponses$1 = class WrappedResponses extends Responses {
1238
1222
  latency,
1239
1223
  timeToFirstToken,
1240
1224
  baseURL: this.baseURL,
1241
- params: body,
1225
+ modelParameters: getModelParams(body),
1242
1226
  httpStatus: 200,
1243
1227
  usage: {
1244
1228
  inputTokens: usage.inputTokens,
@@ -1252,8 +1236,7 @@ let WrappedResponses$1 = class WrappedResponses extends Responses {
1252
1236
  tools: availableTools
1253
1237
  });
1254
1238
  } catch (error) {
1255
- const enrichedError = await sendEventWithErrorToPosthog({
1256
- client: this.phClient,
1239
+ await captureAiGeneration(this.phClient, {
1257
1240
  ...posthogParams,
1258
1241
  model: openAIParams.model,
1259
1242
  provider: 'openai',
@@ -1261,14 +1244,14 @@ let WrappedResponses$1 = class WrappedResponses extends Responses {
1261
1244
  output: [],
1262
1245
  latency: 0,
1263
1246
  baseURL: this.baseURL,
1264
- params: body,
1247
+ modelParameters: getModelParams(body),
1265
1248
  usage: {
1266
1249
  inputTokens: 0,
1267
1250
  outputTokens: 0
1268
1251
  },
1269
- error: error
1252
+ error
1270
1253
  });
1271
- throw enrichedError;
1254
+ throw error;
1272
1255
  }
1273
1256
  })();
1274
1257
  return stream2;
@@ -1283,8 +1266,7 @@ let WrappedResponses$1 = class WrappedResponses extends Responses {
1283
1266
  const formattedOutput = formatResponseOpenAI({
1284
1267
  output: result.output
1285
1268
  });
1286
- await sendEventToPosthog({
1287
- client: this.phClient,
1269
+ await captureAiGeneration(this.phClient, {
1288
1270
  ...posthogParams,
1289
1271
  model: openAIParams.model ?? result.model,
1290
1272
  provider: 'openai',
@@ -1292,7 +1274,7 @@ let WrappedResponses$1 = class WrappedResponses extends Responses {
1292
1274
  output: formattedOutput,
1293
1275
  latency,
1294
1276
  baseURL: this.baseURL,
1295
- params: body,
1277
+ modelParameters: getModelParams(body),
1296
1278
  httpStatus: 200,
1297
1279
  usage: {
1298
1280
  inputTokens: result.usage?.input_tokens ?? 0,
@@ -1309,8 +1291,7 @@ let WrappedResponses$1 = class WrappedResponses extends Responses {
1309
1291
  return result;
1310
1292
  }, async error => {
1311
1293
  const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
1312
- await sendEventToPosthog({
1313
- client: this.phClient,
1294
+ await captureAiGeneration(this.phClient, {
1314
1295
  ...posthogParams,
1315
1296
  model: openAIParams.model,
1316
1297
  provider: 'openai',
@@ -1318,13 +1299,13 @@ let WrappedResponses$1 = class WrappedResponses extends Responses {
1318
1299
  output: [],
1319
1300
  latency: 0,
1320
1301
  baseURL: this.baseURL,
1321
- params: body,
1302
+ modelParameters: getModelParams(body),
1322
1303
  httpStatus,
1323
1304
  usage: {
1324
1305
  inputTokens: 0,
1325
1306
  outputTokens: 0
1326
1307
  },
1327
- error: JSON.stringify(error)
1308
+ error
1328
1309
  });
1329
1310
  throw error;
1330
1311
  });
@@ -1345,8 +1326,7 @@ let WrappedResponses$1 = class WrappedResponses extends Responses {
1345
1326
  const parentPromise = super.parse(openAIParams, options);
1346
1327
  const wrappedPromise = parentPromise.then(async result => {
1347
1328
  const latency = (Date.now() - startTime) / 1000;
1348
- await sendEventToPosthog({
1349
- client: this.phClient,
1329
+ await captureAiGeneration(this.phClient, {
1350
1330
  ...posthogParams,
1351
1331
  model: openAIParams.model ?? result.model,
1352
1332
  provider: 'openai',
@@ -1354,7 +1334,7 @@ let WrappedResponses$1 = class WrappedResponses extends Responses {
1354
1334
  output: result.output,
1355
1335
  latency,
1356
1336
  baseURL: this.baseURL,
1357
- params: body,
1337
+ modelParameters: getModelParams(body),
1358
1338
  httpStatus: 200,
1359
1339
  usage: {
1360
1340
  inputTokens: result.usage?.input_tokens ?? 0,
@@ -1367,8 +1347,7 @@ let WrappedResponses$1 = class WrappedResponses extends Responses {
1367
1347
  });
1368
1348
  return result;
1369
1349
  }, async error => {
1370
- const enrichedError = await sendEventWithErrorToPosthog({
1371
- client: this.phClient,
1350
+ await captureAiGeneration(this.phClient, {
1372
1351
  ...posthogParams,
1373
1352
  model: openAIParams.model,
1374
1353
  provider: 'openai',
@@ -1376,14 +1355,14 @@ let WrappedResponses$1 = class WrappedResponses extends Responses {
1376
1355
  output: [],
1377
1356
  latency: 0,
1378
1357
  baseURL: this.baseURL,
1379
- params: body,
1358
+ modelParameters: getModelParams(body),
1380
1359
  usage: {
1381
1360
  inputTokens: 0,
1382
1361
  outputTokens: 0
1383
1362
  },
1384
- error: JSON.stringify(error)
1363
+ error
1385
1364
  });
1386
- throw enrichedError;
1365
+ throw error;
1387
1366
  });
1388
1367
  return wrappedPromise;
1389
1368
  } finally {
@@ -1407,8 +1386,7 @@ let WrappedEmbeddings$1 = class WrappedEmbeddings extends Embeddings {
1407
1386
  const parentPromise = super.create(openAIParams, options);
1408
1387
  const wrappedPromise = parentPromise.then(async result => {
1409
1388
  const latency = (Date.now() - startTime) / 1000;
1410
- await sendEventToPosthog({
1411
- client: this.phClient,
1389
+ await captureAiGeneration(this.phClient, {
1412
1390
  ...posthogParams,
1413
1391
  eventType: AIEvent.Embedding,
1414
1392
  model: openAIParams.model,
@@ -1418,7 +1396,7 @@ let WrappedEmbeddings$1 = class WrappedEmbeddings extends Embeddings {
1418
1396
  // Embeddings don't have output content
1419
1397
  latency,
1420
1398
  baseURL: this.baseURL,
1421
- params: body,
1399
+ modelParameters: getModelParams(body),
1422
1400
  httpStatus: 200,
1423
1401
  usage: {
1424
1402
  inputTokens: result.usage?.prompt_tokens ?? 0,
@@ -1428,8 +1406,7 @@ let WrappedEmbeddings$1 = class WrappedEmbeddings extends Embeddings {
1428
1406
  return result;
1429
1407
  }, async error => {
1430
1408
  const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
1431
- await sendEventToPosthog({
1432
- client: this.phClient,
1409
+ await captureAiGeneration(this.phClient, {
1433
1410
  eventType: AIEvent.Embedding,
1434
1411
  ...posthogParams,
1435
1412
  model: openAIParams.model,
@@ -1439,12 +1416,12 @@ let WrappedEmbeddings$1 = class WrappedEmbeddings extends Embeddings {
1439
1416
  // Embeddings don't have output content
1440
1417
  latency: 0,
1441
1418
  baseURL: this.baseURL,
1442
- params: body,
1419
+ modelParameters: getModelParams(body),
1443
1420
  httpStatus,
1444
1421
  usage: {
1445
1422
  inputTokens: 0
1446
1423
  },
1447
- error: JSON.stringify(error)
1424
+ error
1448
1425
  });
1449
1426
  throw error;
1450
1427
  });
@@ -1503,8 +1480,7 @@ class WrappedTranscriptions extends Transcriptions {
1503
1480
  const latency = (Date.now() - startTime) / 1000;
1504
1481
  const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
1505
1482
  const availableTools = extractAvailableToolCalls('openai', openAIParams);
1506
- await sendEventToPosthog({
1507
- client: this.phClient,
1483
+ await captureAiGeneration(this.phClient, {
1508
1484
  ...posthogParams,
1509
1485
  model: openAIParams.model,
1510
1486
  provider: 'openai',
@@ -1513,14 +1489,13 @@ class WrappedTranscriptions extends Transcriptions {
1513
1489
  latency,
1514
1490
  timeToFirstToken,
1515
1491
  baseURL: this.baseURL,
1516
- params: body,
1492
+ modelParameters: getModelParams(body),
1517
1493
  httpStatus: 200,
1518
1494
  usage,
1519
1495
  tools: availableTools
1520
1496
  });
1521
1497
  } catch (error) {
1522
- const enrichedError = await sendEventWithErrorToPosthog({
1523
- client: this.phClient,
1498
+ await captureAiGeneration(this.phClient, {
1524
1499
  ...posthogParams,
1525
1500
  model: openAIParams.model,
1526
1501
  provider: 'openai',
@@ -1528,14 +1503,14 @@ class WrappedTranscriptions extends Transcriptions {
1528
1503
  output: [],
1529
1504
  latency: 0,
1530
1505
  baseURL: this.baseURL,
1531
- params: body,
1506
+ modelParameters: getModelParams(body),
1532
1507
  usage: {
1533
1508
  inputTokens: 0,
1534
1509
  outputTokens: 0
1535
1510
  },
1536
- error: error
1511
+ error
1537
1512
  });
1538
- throw enrichedError;
1513
+ throw error;
1539
1514
  }
1540
1515
  })();
1541
1516
  return stream2;
@@ -1546,8 +1521,7 @@ class WrappedTranscriptions extends Transcriptions {
1546
1521
  const wrappedPromise = parentPromise.then(async result => {
1547
1522
  if ('text' in result) {
1548
1523
  const latency = (Date.now() - startTime) / 1000;
1549
- await sendEventToPosthog({
1550
- client: this.phClient,
1524
+ await captureAiGeneration(this.phClient, {
1551
1525
  ...posthogParams,
1552
1526
  model: openAIParams.model,
1553
1527
  provider: 'openai',
@@ -1555,7 +1529,7 @@ class WrappedTranscriptions extends Transcriptions {
1555
1529
  output: result.text,
1556
1530
  latency,
1557
1531
  baseURL: this.baseURL,
1558
- params: body,
1532
+ modelParameters: getModelParams(body),
1559
1533
  httpStatus: 200,
1560
1534
  usage: {
1561
1535
  inputTokens: result.usage?.type === 'tokens' ? result.usage.input_tokens ?? 0 : 0,
@@ -1566,8 +1540,7 @@ class WrappedTranscriptions extends Transcriptions {
1566
1540
  return result;
1567
1541
  }
1568
1542
  }, async error => {
1569
- const enrichedError = await sendEventWithErrorToPosthog({
1570
- client: this.phClient,
1543
+ await captureAiGeneration(this.phClient, {
1571
1544
  ...posthogParams,
1572
1545
  model: openAIParams.model,
1573
1546
  provider: 'openai',
@@ -1575,14 +1548,14 @@ class WrappedTranscriptions extends Transcriptions {
1575
1548
  output: [],
1576
1549
  latency: 0,
1577
1550
  baseURL: this.baseURL,
1578
- params: body,
1551
+ modelParameters: getModelParams(body),
1579
1552
  usage: {
1580
1553
  inputTokens: 0,
1581
1554
  outputTokens: 0
1582
1555
  },
1583
- error: error
1556
+ error
1584
1557
  });
1585
- throw enrichedError;
1558
+ throw error;
1586
1559
  });
1587
1560
  return wrappedPromise;
1588
1561
  }
@@ -1727,8 +1700,7 @@ class WrappedCompletions extends AzureOpenAI.Chat.Completions {
1727
1700
  }];
1728
1701
  const latency = (Date.now() - startTime) / 1000;
1729
1702
  const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
1730
- await sendEventToPosthog({
1731
- client: this.phClient,
1703
+ await captureAiGeneration(this.phClient, {
1732
1704
  ...posthogParams,
1733
1705
  model: openAIParams.model ?? modelFromResponse,
1734
1706
  provider: 'azure',
@@ -1737,13 +1709,12 @@ class WrappedCompletions extends AzureOpenAI.Chat.Completions {
1737
1709
  latency,
1738
1710
  timeToFirstToken,
1739
1711
  baseURL: this.baseURL,
1740
- params: body,
1712
+ modelParameters: getModelParams(body),
1741
1713
  httpStatus: 200,
1742
1714
  usage
1743
1715
  });
1744
1716
  } catch (error) {
1745
- const enrichedError = await sendEventWithErrorToPosthog({
1746
- client: this.phClient,
1717
+ await captureAiGeneration(this.phClient, {
1747
1718
  ...posthogParams,
1748
1719
  model: openAIParams.model,
1749
1720
  provider: 'azure',
@@ -1751,14 +1722,14 @@ class WrappedCompletions extends AzureOpenAI.Chat.Completions {
1751
1722
  output: [],
1752
1723
  latency: 0,
1753
1724
  baseURL: this.baseURL,
1754
- params: body,
1725
+ modelParameters: getModelParams(body),
1755
1726
  usage: {
1756
1727
  inputTokens: 0,
1757
1728
  outputTokens: 0
1758
1729
  },
1759
1730
  error: error
1760
1731
  });
1761
- throw enrichedError;
1732
+ throw error;
1762
1733
  }
1763
1734
  })();
1764
1735
  // Return the other stream to the user
@@ -1770,8 +1741,7 @@ class WrappedCompletions extends AzureOpenAI.Chat.Completions {
1770
1741
  const wrappedPromise = parentPromise.then(async result => {
1771
1742
  if ('choices' in result) {
1772
1743
  const latency = (Date.now() - startTime) / 1000;
1773
- await sendEventToPosthog({
1774
- client: this.phClient,
1744
+ await captureAiGeneration(this.phClient, {
1775
1745
  ...posthogParams,
1776
1746
  model: openAIParams.model ?? result.model,
1777
1747
  provider: 'azure',
@@ -1779,7 +1749,7 @@ class WrappedCompletions extends AzureOpenAI.Chat.Completions {
1779
1749
  output: formatResponseOpenAI(result),
1780
1750
  latency,
1781
1751
  baseURL: this.baseURL,
1782
- params: body,
1752
+ modelParameters: getModelParams(body),
1783
1753
  httpStatus: 200,
1784
1754
  usage: {
1785
1755
  inputTokens: result.usage?.prompt_tokens ?? 0,
@@ -1792,8 +1762,7 @@ class WrappedCompletions extends AzureOpenAI.Chat.Completions {
1792
1762
  return result;
1793
1763
  }, async error => {
1794
1764
  const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
1795
- await sendEventToPosthog({
1796
- client: this.phClient,
1765
+ await captureAiGeneration(this.phClient, {
1797
1766
  ...posthogParams,
1798
1767
  model: openAIParams.model,
1799
1768
  provider: 'azure',
@@ -1801,13 +1770,13 @@ class WrappedCompletions extends AzureOpenAI.Chat.Completions {
1801
1770
  output: [],
1802
1771
  latency: 0,
1803
1772
  baseURL: this.baseURL,
1804
- params: body,
1773
+ modelParameters: getModelParams(body),
1805
1774
  httpStatus,
1806
1775
  usage: {
1807
1776
  inputTokens: 0,
1808
1777
  outputTokens: 0
1809
1778
  },
1810
- error: JSON.stringify(error)
1779
+ error
1811
1780
  });
1812
1781
  throw error;
1813
1782
  });
@@ -1867,8 +1836,7 @@ class WrappedResponses extends AzureOpenAI.Responses {
1867
1836
  }
1868
1837
  const latency = (Date.now() - startTime) / 1000;
1869
1838
  const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
1870
- await sendEventToPosthog({
1871
- client: this.phClient,
1839
+ await captureAiGeneration(this.phClient, {
1872
1840
  ...posthogParams,
1873
1841
  model: openAIParams.model ?? modelFromResponse,
1874
1842
  provider: 'azure',
@@ -1877,13 +1845,12 @@ class WrappedResponses extends AzureOpenAI.Responses {
1877
1845
  latency,
1878
1846
  timeToFirstToken,
1879
1847
  baseURL: this.baseURL,
1880
- params: body,
1848
+ modelParameters: getModelParams(body),
1881
1849
  httpStatus: 200,
1882
1850
  usage
1883
1851
  });
1884
1852
  } catch (error) {
1885
- const enrichedError = await sendEventWithErrorToPosthog({
1886
- client: this.phClient,
1853
+ await captureAiGeneration(this.phClient, {
1887
1854
  ...posthogParams,
1888
1855
  model: openAIParams.model,
1889
1856
  provider: 'azure',
@@ -1891,14 +1858,14 @@ class WrappedResponses extends AzureOpenAI.Responses {
1891
1858
  output: [],
1892
1859
  latency: 0,
1893
1860
  baseURL: this.baseURL,
1894
- params: body,
1861
+ modelParameters: getModelParams(body),
1895
1862
  usage: {
1896
1863
  inputTokens: 0,
1897
1864
  outputTokens: 0
1898
1865
  },
1899
1866
  error: error
1900
1867
  });
1901
- throw enrichedError;
1868
+ throw error;
1902
1869
  }
1903
1870
  })();
1904
1871
  return stream2;
@@ -1909,8 +1876,7 @@ class WrappedResponses extends AzureOpenAI.Responses {
1909
1876
  const wrappedPromise = parentPromise.then(async result => {
1910
1877
  if ('output' in result) {
1911
1878
  const latency = (Date.now() - startTime) / 1000;
1912
- await sendEventToPosthog({
1913
- client: this.phClient,
1879
+ await captureAiGeneration(this.phClient, {
1914
1880
  ...posthogParams,
1915
1881
  model: openAIParams.model ?? result.model,
1916
1882
  provider: 'azure',
@@ -1918,7 +1884,7 @@ class WrappedResponses extends AzureOpenAI.Responses {
1918
1884
  output: result.output,
1919
1885
  latency,
1920
1886
  baseURL: this.baseURL,
1921
- params: body,
1887
+ modelParameters: getModelParams(body),
1922
1888
  httpStatus: 200,
1923
1889
  usage: {
1924
1890
  inputTokens: result.usage?.input_tokens ?? 0,
@@ -1931,8 +1897,7 @@ class WrappedResponses extends AzureOpenAI.Responses {
1931
1897
  return result;
1932
1898
  }, async error => {
1933
1899
  const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
1934
- await sendEventToPosthog({
1935
- client: this.phClient,
1900
+ await captureAiGeneration(this.phClient, {
1936
1901
  ...posthogParams,
1937
1902
  model: openAIParams.model,
1938
1903
  provider: 'azure',
@@ -1940,13 +1905,13 @@ class WrappedResponses extends AzureOpenAI.Responses {
1940
1905
  output: [],
1941
1906
  latency: 0,
1942
1907
  baseURL: this.baseURL,
1943
- params: body,
1908
+ modelParameters: getModelParams(body),
1944
1909
  httpStatus,
1945
1910
  usage: {
1946
1911
  inputTokens: 0,
1947
1912
  outputTokens: 0
1948
1913
  },
1949
- error: JSON.stringify(error)
1914
+ error
1950
1915
  });
1951
1916
  throw error;
1952
1917
  });
@@ -1962,8 +1927,7 @@ class WrappedResponses extends AzureOpenAI.Responses {
1962
1927
  const parentPromise = super.parse(openAIParams, options);
1963
1928
  const wrappedPromise = parentPromise.then(async result => {
1964
1929
  const latency = (Date.now() - startTime) / 1000;
1965
- await sendEventToPosthog({
1966
- client: this.phClient,
1930
+ await captureAiGeneration(this.phClient, {
1967
1931
  ...posthogParams,
1968
1932
  model: openAIParams.model ?? result.model,
1969
1933
  provider: 'azure',
@@ -1971,7 +1935,7 @@ class WrappedResponses extends AzureOpenAI.Responses {
1971
1935
  output: result.output,
1972
1936
  latency,
1973
1937
  baseURL: this.baseURL,
1974
- params: body,
1938
+ modelParameters: getModelParams(body),
1975
1939
  httpStatus: 200,
1976
1940
  usage: {
1977
1941
  inputTokens: result.usage?.input_tokens ?? 0,
@@ -1982,8 +1946,7 @@ class WrappedResponses extends AzureOpenAI.Responses {
1982
1946
  });
1983
1947
  return result;
1984
1948
  }, async error => {
1985
- await sendEventToPosthog({
1986
- client: this.phClient,
1949
+ await captureAiGeneration(this.phClient, {
1987
1950
  ...posthogParams,
1988
1951
  model: openAIParams.model,
1989
1952
  provider: 'azure',
@@ -1991,13 +1954,13 @@ class WrappedResponses extends AzureOpenAI.Responses {
1991
1954
  output: [],
1992
1955
  latency: 0,
1993
1956
  baseURL: this.baseURL,
1994
- params: body,
1957
+ modelParameters: getModelParams(body),
1995
1958
  httpStatus: error?.status ? error.status : 500,
1996
1959
  usage: {
1997
1960
  inputTokens: 0,
1998
1961
  outputTokens: 0
1999
1962
  },
2000
- error: JSON.stringify(error)
1963
+ error
2001
1964
  });
2002
1965
  throw error;
2003
1966
  });
@@ -2019,8 +1982,7 @@ class WrappedEmbeddings extends AzureOpenAI.Embeddings {
2019
1982
  const parentPromise = super.create(openAIParams, options);
2020
1983
  const wrappedPromise = parentPromise.then(async result => {
2021
1984
  const latency = (Date.now() - startTime) / 1000;
2022
- await sendEventToPosthog({
2023
- client: this.phClient,
1985
+ await captureAiGeneration(this.phClient, {
2024
1986
  eventType: AIEvent.Embedding,
2025
1987
  ...posthogParams,
2026
1988
  model: openAIParams.model,
@@ -2030,7 +1992,7 @@ class WrappedEmbeddings extends AzureOpenAI.Embeddings {
2030
1992
  // Embeddings don't have output content
2031
1993
  latency,
2032
1994
  baseURL: this.baseURL,
2033
- params: body,
1995
+ modelParameters: getModelParams(body),
2034
1996
  httpStatus: 200,
2035
1997
  usage: {
2036
1998
  inputTokens: result.usage?.prompt_tokens ?? 0
@@ -2039,8 +2001,7 @@ class WrappedEmbeddings extends AzureOpenAI.Embeddings {
2039
2001
  return result;
2040
2002
  }, async error => {
2041
2003
  const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
2042
- await sendEventToPosthog({
2043
- client: this.phClient,
2004
+ await captureAiGeneration(this.phClient, {
2044
2005
  eventType: AIEvent.Embedding,
2045
2006
  ...posthogParams,
2046
2007
  model: openAIParams.model,
@@ -2049,12 +2010,12 @@ class WrappedEmbeddings extends AzureOpenAI.Embeddings {
2049
2010
  output: null,
2050
2011
  latency: 0,
2051
2012
  baseURL: this.baseURL,
2052
- params: body,
2013
+ modelParameters: getModelParams(body),
2053
2014
  httpStatus,
2054
2015
  usage: {
2055
2016
  inputTokens: 0
2056
2017
  },
2057
- error: JSON.stringify(error)
2018
+ error
2058
2019
  });
2059
2020
  throw error;
2060
2021
  });
@@ -2153,18 +2114,26 @@ const mapVercelPrompt = messages => {
2153
2114
  };
2154
2115
  });
2155
2116
  try {
2156
- // Trim the inputs array until its JSON size fits within MAX_OUTPUT_SIZE
2157
- const encoder = new TextEncoder();
2158
- let serialized = JSON.stringify(inputs);
2117
+ // Trim the inputs array until its serialized JSON size fits within MAX_OUTPUT_SIZE.
2118
+ // Pre-compute each message's byte size once so we can shift by accumulated budget
2119
+ // in a single linear pass, instead of re-stringifying the whole array per iteration.
2120
+ const messageSizes = inputs.map(m => utf8ByteLength(JSON.stringify(m)));
2121
+ // Account for the surrounding `[` `]` plus a comma between each pair of elements.
2122
+ let totalBytes = 2 + Math.max(0, messageSizes.length - 1);
2123
+ for (const size of messageSizes) {
2124
+ totalBytes += size;
2125
+ }
2159
2126
  let removedCount = 0;
2160
- // We need to keep track of the initial size of the inputs array because we're going to be mutating it
2161
- const initialSize = inputs.length;
2162
- for (let i = 0; i < initialSize && encoder.encode(serialized).byteLength > MAX_OUTPUT_SIZE; i++) {
2163
- inputs.shift();
2127
+ while (totalBytes > MAX_OUTPUT_SIZE && removedCount < messageSizes.length) {
2128
+ totalBytes -= messageSizes[removedCount];
2129
+ // Each removed message past the first also drops the comma that joined it.
2130
+ if (removedCount < messageSizes.length - 1) {
2131
+ totalBytes -= 1;
2132
+ }
2164
2133
  removedCount++;
2165
- serialized = JSON.stringify(inputs);
2166
2134
  }
2167
2135
  if (removedCount > 0) {
2136
+ inputs.splice(0, removedCount);
2168
2137
  // Add one placeholder to indicate how many were removed
2169
2138
  inputs.unshift({
2170
2139
  role: 'posthog',
@@ -2383,6 +2352,18 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
2383
2352
  $ai_framework_version: model.specificationVersion === 'v3' ? '6' : '5'
2384
2353
  }
2385
2354
  };
2355
+ // Shared `captureAiGeneration` options for every call site in this wrapper.
2356
+ const baseOptions = {
2357
+ distinctId: mergedOptions.posthogDistinctId,
2358
+ traceId,
2359
+ properties: mergedOptions.posthogProperties,
2360
+ groups: mergedOptions.posthogGroups,
2361
+ privacyMode: mergedOptions.posthogPrivacyMode,
2362
+ modelOverride: mergedOptions.posthogModelOverride,
2363
+ providerOverride: mergedOptions.posthogProviderOverride,
2364
+ costOverride: mergedOptions.posthogCostOverride,
2365
+ captureImmediate: mergedOptions.posthogCaptureImmediate
2366
+ };
2386
2367
  // Create wrapped model using Object.create to preserve the prototype chain
2387
2368
  // This automatically inherits all properties (including getters) from the model
2388
2369
  const wrappedModel = Object.create(model, {
@@ -2435,46 +2416,40 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
2435
2416
  // Extract finish reason - V2 returns a string, V3 returns an object with .unified
2436
2417
  const rawFinishReason = result.finishReason;
2437
2418
  const finishReasonStr = typeof rawFinishReason === 'string' ? rawFinishReason : rawFinishReason && typeof rawFinishReason === 'object' && 'unified' in rawFinishReason ? String(rawFinishReason.unified) : undefined;
2438
- await sendEventToPosthog({
2439
- client: phClient,
2440
- distinctId: mergedOptions.posthogDistinctId,
2441
- traceId: mergedOptions.posthogTraceId ?? v4(),
2419
+ await captureAiGeneration(phClient, {
2420
+ ...baseOptions,
2442
2421
  model: modelId,
2443
2422
  provider: provider,
2444
2423
  input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt),
2445
2424
  output: content,
2446
2425
  latency,
2447
2426
  baseURL,
2448
- params: mergedParams,
2427
+ modelParameters: getModelParams(mergedParams),
2449
2428
  httpStatus: 200,
2450
2429
  usage,
2451
2430
  stopReason: finishReasonStr,
2452
- tools: availableTools,
2453
- captureImmediate: mergedOptions.posthogCaptureImmediate
2431
+ tools: availableTools
2454
2432
  });
2455
2433
  return result;
2456
2434
  } catch (error) {
2457
2435
  const modelId = model.modelId;
2458
- const enrichedError = await sendEventWithErrorToPosthog({
2459
- client: phClient,
2460
- distinctId: mergedOptions.posthogDistinctId,
2461
- traceId: mergedOptions.posthogTraceId ?? v4(),
2436
+ await captureAiGeneration(phClient, {
2437
+ ...baseOptions,
2462
2438
  model: modelId,
2463
2439
  provider: model.provider,
2464
2440
  input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt),
2465
2441
  output: [],
2466
2442
  latency: 0,
2467
2443
  baseURL: '',
2468
- params: mergedParams,
2444
+ modelParameters: getModelParams(mergedParams),
2469
2445
  usage: {
2470
2446
  inputTokens: 0,
2471
2447
  outputTokens: 0
2472
2448
  },
2473
2449
  error: error,
2474
- tools: availableTools,
2475
- captureImmediate: mergedOptions.posthogCaptureImmediate
2450
+ tools: availableTools
2476
2451
  });
2477
- throw enrichedError;
2452
+ throw error;
2478
2453
  }
2479
2454
  },
2480
2455
  writable: true,
@@ -2620,10 +2595,8 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
2620
2595
  }
2621
2596
  };
2622
2597
  adjustAnthropicV3CacheTokens(model, modelId, provider, finalUsage);
2623
- await sendEventToPosthog({
2624
- client: phClient,
2625
- distinctId: mergedOptions.posthogDistinctId,
2626
- traceId: mergedOptions.posthogTraceId ?? v4(),
2598
+ await captureAiGeneration(phClient, {
2599
+ ...baseOptions,
2627
2600
  model: modelId,
2628
2601
  provider: provider,
2629
2602
  input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt),
@@ -2631,12 +2604,11 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
2631
2604
  latency,
2632
2605
  timeToFirstToken,
2633
2606
  baseURL,
2634
- params: mergedParams,
2607
+ modelParameters: getModelParams(mergedParams),
2635
2608
  httpStatus: 200,
2636
2609
  usage: finalUsage,
2637
2610
  stopReason,
2638
- tools: availableTools,
2639
- captureImmediate: mergedOptions.posthogCaptureImmediate
2611
+ tools: availableTools
2640
2612
  });
2641
2613
  }
2642
2614
  });
@@ -2645,26 +2617,23 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
2645
2617
  ...rest
2646
2618
  };
2647
2619
  } catch (error) {
2648
- const enrichedError = await sendEventWithErrorToPosthog({
2649
- client: phClient,
2650
- distinctId: mergedOptions.posthogDistinctId,
2651
- traceId: mergedOptions.posthogTraceId ?? v4(),
2620
+ await captureAiGeneration(phClient, {
2621
+ ...baseOptions,
2652
2622
  model: modelId,
2653
2623
  provider: provider,
2654
2624
  input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt),
2655
2625
  output: [],
2656
2626
  latency: 0,
2657
2627
  baseURL: '',
2658
- params: mergedParams,
2628
+ modelParameters: getModelParams(mergedParams),
2659
2629
  usage: {
2660
2630
  inputTokens: 0,
2661
2631
  outputTokens: 0
2662
2632
  },
2663
2633
  error: error,
2664
- tools: availableTools,
2665
- captureImmediate: mergedOptions.posthogCaptureImmediate
2634
+ tools: availableTools
2666
2635
  });
2667
- throw enrichedError;
2636
+ throw error;
2668
2637
  }
2669
2638
  },
2670
2639
  writable: true,
@@ -2829,8 +2798,7 @@ class WrappedMessages extends AnthropicOriginal.Messages {
2829
2798
  text: accumulatedContent
2830
2799
  }]
2831
2800
  }];
2832
- await sendEventToPosthog({
2833
- client: this.phClient,
2801
+ await captureAiGeneration(this.phClient, {
2834
2802
  ...posthogParams,
2835
2803
  model: anthropicParams.model,
2836
2804
  provider: 'anthropic',
@@ -2839,15 +2807,14 @@ class WrappedMessages extends AnthropicOriginal.Messages {
2839
2807
  latency,
2840
2808
  timeToFirstToken,
2841
2809
  baseURL: this.baseURL,
2842
- params: body,
2810
+ modelParameters: getModelParams(body),
2843
2811
  httpStatus: 200,
2844
2812
  usage,
2845
2813
  stopReason,
2846
2814
  tools: availableTools
2847
2815
  });
2848
2816
  } catch (error) {
2849
- const enrichedError = await sendEventWithErrorToPosthog({
2850
- client: this.phClient,
2817
+ await captureAiGeneration(this.phClient, {
2851
2818
  ...posthogParams,
2852
2819
  model: anthropicParams.model,
2853
2820
  provider: 'anthropic',
@@ -2855,14 +2822,14 @@ class WrappedMessages extends AnthropicOriginal.Messages {
2855
2822
  output: [],
2856
2823
  latency: 0,
2857
2824
  baseURL: this.baseURL,
2858
- params: body,
2825
+ modelParameters: getModelParams(body),
2859
2826
  usage: {
2860
2827
  inputTokens: 0,
2861
2828
  outputTokens: 0
2862
2829
  },
2863
2830
  error: error
2864
2831
  });
2865
- throw enrichedError;
2832
+ throw error;
2866
2833
  }
2867
2834
  })();
2868
2835
  // Return the other stream to the user
@@ -2875,8 +2842,7 @@ class WrappedMessages extends AnthropicOriginal.Messages {
2875
2842
  if ('content' in result) {
2876
2843
  const latency = (Date.now() - startTime) / 1000;
2877
2844
  const availableTools = extractAvailableToolCalls('anthropic', anthropicParams);
2878
- await sendEventToPosthog({
2879
- client: this.phClient,
2845
+ await captureAiGeneration(this.phClient, {
2880
2846
  ...posthogParams,
2881
2847
  model: anthropicParams.model,
2882
2848
  provider: 'anthropic',
@@ -2884,7 +2850,7 @@ class WrappedMessages extends AnthropicOriginal.Messages {
2884
2850
  output: formatResponseAnthropic(result),
2885
2851
  latency,
2886
2852
  baseURL: this.baseURL,
2887
- params: body,
2853
+ modelParameters: getModelParams(body),
2888
2854
  httpStatus: 200,
2889
2855
  usage: {
2890
2856
  inputTokens: result.usage.input_tokens ?? 0,
@@ -2900,8 +2866,7 @@ class WrappedMessages extends AnthropicOriginal.Messages {
2900
2866
  }
2901
2867
  return result;
2902
2868
  }, async error => {
2903
- await sendEventToPosthog({
2904
- client: this.phClient,
2869
+ await captureAiGeneration(this.phClient, {
2905
2870
  ...posthogParams,
2906
2871
  model: anthropicParams.model,
2907
2872
  provider: 'anthropic',
@@ -2909,13 +2874,13 @@ class WrappedMessages extends AnthropicOriginal.Messages {
2909
2874
  output: [],
2910
2875
  latency: 0,
2911
2876
  baseURL: this.baseURL,
2912
- params: body,
2877
+ modelParameters: getModelParams(body),
2913
2878
  httpStatus: error?.status ? error.status : 500,
2914
2879
  usage: {
2915
2880
  inputTokens: 0,
2916
2881
  outputTokens: 0
2917
2882
  },
2918
- error: JSON.stringify(error)
2883
+ error: error
2919
2884
  });
2920
2885
  throw error;
2921
2886
  });
@@ -2952,8 +2917,7 @@ class WrappedModels {
2952
2917
  const availableTools = extractAvailableToolCalls('gemini', geminiParams);
2953
2918
  const metadata = response.usageMetadata;
2954
2919
  const finishReason = response.candidates?.[0]?.finishReason;
2955
- await sendEventToPosthog({
2956
- client: this.phClient,
2920
+ await captureAiGeneration(this.phClient, {
2957
2921
  ...posthogParams,
2958
2922
  model: geminiParams.model,
2959
2923
  provider: 'gemini',
@@ -2961,7 +2925,7 @@ class WrappedModels {
2961
2925
  output: formatResponseGemini(response),
2962
2926
  latency,
2963
2927
  baseURL: 'https://generativelanguage.googleapis.com',
2964
- params: params,
2928
+ modelParameters: getModelParams(params),
2965
2929
  httpStatus: 200,
2966
2930
  usage: {
2967
2931
  inputTokens: metadata?.promptTokenCount ?? 0,
@@ -2977,8 +2941,7 @@ class WrappedModels {
2977
2941
  return response;
2978
2942
  } catch (error) {
2979
2943
  const latency = (Date.now() - startTime) / 1000;
2980
- const enrichedError = await sendEventWithErrorToPosthog({
2981
- client: this.phClient,
2944
+ await captureAiGeneration(this.phClient, {
2982
2945
  ...posthogParams,
2983
2946
  model: geminiParams.model,
2984
2947
  provider: 'gemini',
@@ -2986,14 +2949,14 @@ class WrappedModels {
2986
2949
  output: [],
2987
2950
  latency,
2988
2951
  baseURL: 'https://generativelanguage.googleapis.com',
2989
- params: params,
2952
+ modelParameters: getModelParams(params),
2990
2953
  usage: {
2991
2954
  inputTokens: 0,
2992
2955
  outputTokens: 0
2993
2956
  },
2994
- error: error
2957
+ error
2995
2958
  });
2996
- throw enrichedError;
2959
+ throw error;
2997
2960
  }
2998
2961
  }
2999
2962
  async *generateContentStream(params) {
@@ -3092,8 +3055,7 @@ class WrappedModels {
3092
3055
  role: 'assistant',
3093
3056
  content: accumulatedContent
3094
3057
  }] : [];
3095
- await sendEventToPosthog({
3096
- client: this.phClient,
3058
+ await captureAiGeneration(this.phClient, {
3097
3059
  ...posthogParams,
3098
3060
  model: geminiParams.model,
3099
3061
  provider: 'gemini',
@@ -3102,7 +3064,7 @@ class WrappedModels {
3102
3064
  latency,
3103
3065
  timeToFirstToken,
3104
3066
  baseURL: 'https://generativelanguage.googleapis.com',
3105
- params: params,
3067
+ modelParameters: getModelParams(params),
3106
3068
  httpStatus: 200,
3107
3069
  usage: {
3108
3070
  ...usage,
@@ -3114,8 +3076,7 @@ class WrappedModels {
3114
3076
  });
3115
3077
  } catch (error) {
3116
3078
  const latency = (Date.now() - startTime) / 1000;
3117
- const enrichedError = await sendEventWithErrorToPosthog({
3118
- client: this.phClient,
3079
+ await captureAiGeneration(this.phClient, {
3119
3080
  ...posthogParams,
3120
3081
  model: geminiParams.model,
3121
3082
  provider: 'gemini',
@@ -3123,14 +3084,14 @@ class WrappedModels {
3123
3084
  output: [],
3124
3085
  latency,
3125
3086
  baseURL: 'https://generativelanguage.googleapis.com',
3126
- params: params,
3087
+ modelParameters: getModelParams(params),
3127
3088
  usage: {
3128
3089
  inputTokens: 0,
3129
3090
  outputTokens: 0
3130
3091
  },
3131
- error: error
3092
+ error
3132
3093
  });
3133
- throw enrichedError;
3094
+ throw error;
3134
3095
  }
3135
3096
  }
3136
3097
  async embedContent(params) {
@@ -3143,8 +3104,7 @@ class WrappedModels {
3143
3104
  const response = await this.client.models.embedContent(geminiParams);
3144
3105
  const latency = (Date.now() - startTime) / 1000;
3145
3106
  const inputTokens = extractEmbeddingTokenCount(response);
3146
- await sendEventToPosthog({
3147
- client: this.phClient,
3107
+ await captureAiGeneration(this.phClient, {
3148
3108
  ...posthogParams,
3149
3109
  eventType: AIEvent.Embedding,
3150
3110
  model: geminiParams.model,
@@ -3153,7 +3113,7 @@ class WrappedModels {
3153
3113
  output: null,
3154
3114
  latency,
3155
3115
  baseURL: 'https://generativelanguage.googleapis.com',
3156
- params: params,
3116
+ modelParameters: getModelParams(params),
3157
3117
  httpStatus: 200,
3158
3118
  usage: {
3159
3119
  inputTokens
@@ -3162,8 +3122,7 @@ class WrappedModels {
3162
3122
  return response;
3163
3123
  } catch (error) {
3164
3124
  const latency = (Date.now() - startTime) / 1000;
3165
- const enrichedError = await sendEventWithErrorToPosthog({
3166
- client: this.phClient,
3125
+ await captureAiGeneration(this.phClient, {
3167
3126
  ...posthogParams,
3168
3127
  eventType: AIEvent.Embedding,
3169
3128
  model: geminiParams.model,
@@ -3172,13 +3131,13 @@ class WrappedModels {
3172
3131
  output: null,
3173
3132
  latency,
3174
3133
  baseURL: 'https://generativelanguage.googleapis.com',
3175
- params: params,
3134
+ modelParameters: getModelParams(params),
3176
3135
  usage: {
3177
3136
  inputTokens: 0
3178
3137
  },
3179
- error: error
3138
+ error
3180
3139
  });
3181
- throw enrichedError;
3140
+ throw error;
3182
3141
  }
3183
3142
  }
3184
3143
  formatPartsAsContentBlocks(parts) {
@@ -4714,5 +4673,5 @@ class Prompts {
4714
4673
  }
4715
4674
  }
4716
4675
 
4717
- export { PostHogAnthropic as Anthropic, PostHogAzureOpenAI as AzureOpenAI, PostHogGoogleGenAI as GoogleGenAI, LangChainCallbackHandler, PostHogOpenAI as OpenAI, Prompts, wrapVercelLanguageModel as withTracing };
4676
+ export { AIEvent, PostHogAnthropic as Anthropic, PostHogAzureOpenAI as AzureOpenAI, PostHogGoogleGenAI as GoogleGenAI, LangChainCallbackHandler, PostHogOpenAI as OpenAI, Prompts, captureAiGeneration, wrapVercelLanguageModel as withTracing };
4718
4677
  //# sourceMappingURL=index.mjs.map