@posthog/ai 7.20.14 → 8.0.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.
@@ -534,7 +534,7 @@ function formatOpenAIResponsesInput(input, instructions) {
534
534
  return messages;
535
535
  }
536
536
 
537
- var version = "7.20.14";
537
+ var version = "8.0.0";
538
538
 
539
539
  const DEFAULT_MAX_DEPTH = 3;
540
540
  const MAX_STACK_LINES = 20;
@@ -759,6 +759,533 @@ function buildProviderMetadata(fields) {
759
759
  return Object.keys(metadata).length > 0 ? metadata : undefined;
760
760
  }
761
761
 
762
+ class PostHogAzureOpenAI extends openai.AzureOpenAI {
763
+ constructor(config) {
764
+ const {
765
+ posthog,
766
+ ...openAIConfig
767
+ } = config;
768
+ super(openAIConfig);
769
+ this.phClient = posthog;
770
+ this.chat = new WrappedChat$1(this, this.phClient);
771
+ this.embeddings = new WrappedEmbeddings$1(this, this.phClient);
772
+ }
773
+ }
774
+ let WrappedChat$1 = class WrappedChat extends openai.AzureOpenAI.Chat {
775
+ constructor(parentClient, phClient) {
776
+ super(parentClient);
777
+ this.completions = new WrappedCompletions$1(parentClient, phClient);
778
+ }
779
+ };
780
+ let WrappedCompletions$1 = class WrappedCompletions extends openai.AzureOpenAI.Chat.Completions {
781
+ constructor(client, phClient) {
782
+ super(client);
783
+ this.phClient = phClient;
784
+ this.baseURL = client.baseURL;
785
+ }
786
+
787
+ // --- Overload #1: Non-streaming
788
+
789
+ // --- Overload #2: Streaming
790
+
791
+ // --- Overload #3: Generic base
792
+
793
+ // --- Implementation Signature
794
+ create(body, options) {
795
+ const {
796
+ providerParams: openAIParams,
797
+ posthogParams
798
+ } = extractPosthogParams(body);
799
+ const startTime = Date.now();
800
+ const parentPromise = super.create(openAIParams, options);
801
+ if (openAIParams.stream) {
802
+ return parentPromise.then(value => {
803
+ if ('tee' in value) {
804
+ const [stream1, stream2] = value.tee();
805
+ (async () => {
806
+ // Hoisted so the catch block can surface whatever was accumulated
807
+ // from the streamed chunks before the failure.
808
+ let completionIdFromResponse;
809
+ let systemFingerprintFromResponse;
810
+ try {
811
+ const contentBlocks = [];
812
+ let accumulatedContent = '';
813
+ let modelFromResponse;
814
+ let firstTokenTime;
815
+ let usage = {
816
+ inputTokens: 0,
817
+ outputTokens: 0
818
+ };
819
+
820
+ // Map to track in-progress tool calls
821
+ const toolCallsInProgress = new Map();
822
+ for await (const chunk of stream1) {
823
+ // Extract model and completion metadata from chunk (Chat Completions chunks carry these fields)
824
+ if (!modelFromResponse && chunk.model) {
825
+ modelFromResponse = chunk.model;
826
+ }
827
+ if (!completionIdFromResponse && chunk.id) {
828
+ completionIdFromResponse = chunk.id;
829
+ }
830
+ if (!systemFingerprintFromResponse && chunk.system_fingerprint) {
831
+ systemFingerprintFromResponse = chunk.system_fingerprint;
832
+ }
833
+ const choice = chunk?.choices?.[0];
834
+
835
+ // Handle text content
836
+ const deltaContent = choice?.delta?.content;
837
+ if (deltaContent) {
838
+ if (firstTokenTime === undefined) {
839
+ firstTokenTime = Date.now();
840
+ }
841
+ accumulatedContent += deltaContent;
842
+ }
843
+
844
+ // Handle tool calls
845
+ const deltaToolCalls = choice?.delta?.tool_calls;
846
+ if (deltaToolCalls && Array.isArray(deltaToolCalls)) {
847
+ if (firstTokenTime === undefined) {
848
+ firstTokenTime = Date.now();
849
+ }
850
+ for (const toolCall of deltaToolCalls) {
851
+ const index = toolCall.index;
852
+ if (index !== undefined) {
853
+ if (!toolCallsInProgress.has(index)) {
854
+ // New tool call
855
+ toolCallsInProgress.set(index, {
856
+ id: toolCall.id || '',
857
+ name: toolCall.function?.name || '',
858
+ arguments: ''
859
+ });
860
+ }
861
+ const inProgressCall = toolCallsInProgress.get(index);
862
+ if (inProgressCall) {
863
+ // Update tool call data
864
+ if (toolCall.id) {
865
+ inProgressCall.id = toolCall.id;
866
+ }
867
+ if (toolCall.function?.name) {
868
+ inProgressCall.name = toolCall.function.name;
869
+ }
870
+ if (toolCall.function?.arguments) {
871
+ inProgressCall.arguments += toolCall.function.arguments;
872
+ }
873
+ }
874
+ }
875
+ }
876
+ }
877
+
878
+ // Handle usage information
879
+ if (chunk.usage) {
880
+ usage = {
881
+ inputTokens: chunk.usage.prompt_tokens ?? 0,
882
+ outputTokens: chunk.usage.completion_tokens ?? 0,
883
+ reasoningTokens: chunk.usage.completion_tokens_details?.reasoning_tokens ?? 0,
884
+ cacheReadInputTokens: chunk.usage.prompt_tokens_details?.cached_tokens ?? 0
885
+ };
886
+ }
887
+ }
888
+
889
+ // Build final content blocks
890
+ if (accumulatedContent) {
891
+ contentBlocks.push({
892
+ type: 'text',
893
+ text: accumulatedContent
894
+ });
895
+ }
896
+
897
+ // Add completed tool calls to content blocks
898
+ for (const toolCall of toolCallsInProgress.values()) {
899
+ if (toolCall.name) {
900
+ contentBlocks.push({
901
+ type: 'function',
902
+ id: toolCall.id,
903
+ function: {
904
+ name: toolCall.name,
905
+ arguments: toolCall.arguments
906
+ }
907
+ });
908
+ }
909
+ }
910
+
911
+ // Format output to match non-streaming version
912
+ const formattedOutput = contentBlocks.length > 0 ? [{
913
+ role: 'assistant',
914
+ content: contentBlocks
915
+ }] : [{
916
+ role: 'assistant',
917
+ content: [{
918
+ type: 'text',
919
+ text: ''
920
+ }]
921
+ }];
922
+ const latency = (Date.now() - startTime) / 1000;
923
+ const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
924
+ await captureAiGeneration(this.phClient, {
925
+ ...posthogParams,
926
+ model: openAIParams.model ?? modelFromResponse,
927
+ provider: 'azure',
928
+ input: sanitizeOpenAI(openAIParams.messages),
929
+ output: formattedOutput,
930
+ latency,
931
+ timeToFirstToken,
932
+ baseURL: this.baseURL,
933
+ modelParameters: getModelParams(body),
934
+ httpStatus: 200,
935
+ usage,
936
+ completionId: completionIdFromResponse,
937
+ providerMetadata: buildProviderMetadata({
938
+ systemFingerprint: systemFingerprintFromResponse
939
+ })
940
+ });
941
+ } catch (error) {
942
+ await captureAiGeneration(this.phClient, {
943
+ ...posthogParams,
944
+ model: openAIParams.model,
945
+ provider: 'azure',
946
+ input: sanitizeOpenAI(openAIParams.messages),
947
+ output: [],
948
+ latency: 0,
949
+ baseURL: this.baseURL,
950
+ modelParameters: getModelParams(body),
951
+ usage: {
952
+ inputTokens: 0,
953
+ outputTokens: 0
954
+ },
955
+ // If the stream fails mid-flight, surface whatever completion
956
+ // metadata the consumed chunks already provided so the error
957
+ // event can still be correlated to OpenAI's Logs dashboard.
958
+ completionId: completionIdFromResponse,
959
+ providerMetadata: buildProviderMetadata({
960
+ systemFingerprint: systemFingerprintFromResponse
961
+ }),
962
+ error: error
963
+ });
964
+ throw error;
965
+ }
966
+ })();
967
+
968
+ // Return the other stream to the user
969
+ return stream2;
970
+ }
971
+ return value;
972
+ });
973
+ } else {
974
+ const wrappedPromise = parentPromise.then(async result => {
975
+ if ('choices' in result) {
976
+ const latency = (Date.now() - startTime) / 1000;
977
+ await captureAiGeneration(this.phClient, {
978
+ ...posthogParams,
979
+ model: openAIParams.model ?? result.model,
980
+ provider: 'azure',
981
+ input: openAIParams.messages,
982
+ output: formatResponseOpenAI(result),
983
+ latency,
984
+ baseURL: this.baseURL,
985
+ modelParameters: getModelParams(body),
986
+ httpStatus: 200,
987
+ usage: {
988
+ inputTokens: result.usage?.prompt_tokens ?? 0,
989
+ outputTokens: result.usage?.completion_tokens ?? 0,
990
+ reasoningTokens: result.usage?.completion_tokens_details?.reasoning_tokens ?? 0,
991
+ cacheReadInputTokens: result.usage?.prompt_tokens_details?.cached_tokens ?? 0
992
+ },
993
+ completionId: result.id,
994
+ providerMetadata: buildProviderMetadata({
995
+ systemFingerprint: result.system_fingerprint,
996
+ requestId: extractRequestId(result)
997
+ })
998
+ });
999
+ }
1000
+ return result;
1001
+ }, async error => {
1002
+ const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
1003
+ await captureAiGeneration(this.phClient, {
1004
+ ...posthogParams,
1005
+ model: openAIParams.model,
1006
+ provider: 'azure',
1007
+ input: openAIParams.messages,
1008
+ output: [],
1009
+ latency: 0,
1010
+ baseURL: this.baseURL,
1011
+ modelParameters: getModelParams(body),
1012
+ httpStatus,
1013
+ usage: {
1014
+ inputTokens: 0,
1015
+ outputTokens: 0
1016
+ },
1017
+ error
1018
+ });
1019
+ throw error;
1020
+ });
1021
+ return wrappedPromise;
1022
+ }
1023
+ }
1024
+ };
1025
+ let WrappedResponses$1 = class WrappedResponses extends openai.AzureOpenAI.Responses {
1026
+ constructor(client, phClient) {
1027
+ super(client);
1028
+ this.phClient = phClient;
1029
+ this.baseURL = client.baseURL;
1030
+ }
1031
+
1032
+ // --- Overload #1: Non-streaming
1033
+
1034
+ // --- Overload #2: Streaming
1035
+
1036
+ // --- Overload #3: Generic base
1037
+
1038
+ // --- Implementation Signature
1039
+ create(body, options) {
1040
+ const {
1041
+ providerParams: openAIParams,
1042
+ posthogParams
1043
+ } = extractPosthogParams(body);
1044
+ const startTime = Date.now();
1045
+ const parentPromise = super.create(openAIParams, options);
1046
+ if (openAIParams.stream) {
1047
+ return parentPromise.then(value => {
1048
+ if ('tee' in value && typeof value.tee === 'function') {
1049
+ const [stream1, stream2] = value.tee();
1050
+ (async () => {
1051
+ // Hoisted so the catch block can surface the completion ID that
1052
+ // was accumulated from the streamed chunks before the failure.
1053
+ let completionIdFromResponse;
1054
+ try {
1055
+ let finalContent = [];
1056
+ let modelFromResponse;
1057
+ let firstTokenTime;
1058
+ let usage = {
1059
+ inputTokens: 0,
1060
+ outputTokens: 0
1061
+ };
1062
+ for await (const chunk of stream1) {
1063
+ // Track first token time on content delta events
1064
+ if (firstTokenTime === undefined && isResponseTokenChunk(chunk)) {
1065
+ firstTokenTime = Date.now();
1066
+ }
1067
+ if ('response' in chunk && chunk.response) {
1068
+ // Extract model and completion ID from the response object in the chunk (for stored prompts)
1069
+ if (!modelFromResponse && chunk.response.model) {
1070
+ modelFromResponse = chunk.response.model;
1071
+ }
1072
+ if (!completionIdFromResponse && chunk.response.id) {
1073
+ completionIdFromResponse = chunk.response.id;
1074
+ }
1075
+ }
1076
+ if (chunk.type === 'response.completed' && 'response' in chunk && chunk.response?.output && chunk.response.output.length > 0) {
1077
+ finalContent = chunk.response.output;
1078
+ }
1079
+ if ('usage' in chunk && chunk.usage) {
1080
+ usage = {
1081
+ inputTokens: chunk.usage.input_tokens ?? 0,
1082
+ outputTokens: chunk.usage.output_tokens ?? 0,
1083
+ reasoningTokens: chunk.usage.output_tokens_details?.reasoning_tokens ?? 0,
1084
+ cacheReadInputTokens: chunk.usage.input_tokens_details?.cached_tokens ?? 0
1085
+ };
1086
+ }
1087
+ }
1088
+ const latency = (Date.now() - startTime) / 1000;
1089
+ const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
1090
+ await captureAiGeneration(this.phClient, {
1091
+ ...posthogParams,
1092
+ model: openAIParams.model ?? modelFromResponse,
1093
+ provider: 'azure',
1094
+ input: formatOpenAIResponsesInput(openAIParams.input, openAIParams.instructions),
1095
+ output: finalContent,
1096
+ latency,
1097
+ timeToFirstToken,
1098
+ baseURL: this.baseURL,
1099
+ modelParameters: getModelParams(body),
1100
+ httpStatus: 200,
1101
+ usage,
1102
+ completionId: completionIdFromResponse
1103
+ });
1104
+ } catch (error) {
1105
+ await captureAiGeneration(this.phClient, {
1106
+ ...posthogParams,
1107
+ model: openAIParams.model,
1108
+ provider: 'azure',
1109
+ input: formatOpenAIResponsesInput(openAIParams.input, openAIParams.instructions),
1110
+ output: [],
1111
+ latency: 0,
1112
+ baseURL: this.baseURL,
1113
+ modelParameters: getModelParams(body),
1114
+ usage: {
1115
+ inputTokens: 0,
1116
+ outputTokens: 0
1117
+ },
1118
+ // Surface the completion ID from any chunks consumed before
1119
+ // the stream failed so the error event remains correlatable.
1120
+ completionId: completionIdFromResponse,
1121
+ error: error
1122
+ });
1123
+ throw error;
1124
+ }
1125
+ })();
1126
+ return stream2;
1127
+ }
1128
+ return value;
1129
+ });
1130
+ } else {
1131
+ const wrappedPromise = parentPromise.then(async result => {
1132
+ if ('output' in result) {
1133
+ const latency = (Date.now() - startTime) / 1000;
1134
+ await captureAiGeneration(this.phClient, {
1135
+ ...posthogParams,
1136
+ model: openAIParams.model ?? result.model,
1137
+ provider: 'azure',
1138
+ input: formatOpenAIResponsesInput(openAIParams.input, openAIParams.instructions),
1139
+ output: result.output,
1140
+ latency,
1141
+ baseURL: this.baseURL,
1142
+ modelParameters: getModelParams(body),
1143
+ httpStatus: 200,
1144
+ usage: {
1145
+ inputTokens: result.usage?.input_tokens ?? 0,
1146
+ outputTokens: result.usage?.output_tokens ?? 0,
1147
+ reasoningTokens: result.usage?.output_tokens_details?.reasoning_tokens ?? 0,
1148
+ cacheReadInputTokens: result.usage?.input_tokens_details?.cached_tokens ?? 0
1149
+ },
1150
+ completionId: result.id,
1151
+ providerMetadata: buildProviderMetadata({
1152
+ requestId: extractRequestId(result)
1153
+ })
1154
+ });
1155
+ }
1156
+ return result;
1157
+ }, async error => {
1158
+ const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
1159
+ await captureAiGeneration(this.phClient, {
1160
+ ...posthogParams,
1161
+ model: openAIParams.model,
1162
+ provider: 'azure',
1163
+ input: formatOpenAIResponsesInput(openAIParams.input, openAIParams.instructions),
1164
+ output: [],
1165
+ latency: 0,
1166
+ baseURL: this.baseURL,
1167
+ modelParameters: getModelParams(body),
1168
+ httpStatus,
1169
+ usage: {
1170
+ inputTokens: 0,
1171
+ outputTokens: 0
1172
+ },
1173
+ error
1174
+ });
1175
+ throw error;
1176
+ });
1177
+ return wrappedPromise;
1178
+ }
1179
+ }
1180
+ parse(body, options) {
1181
+ const {
1182
+ providerParams: openAIParams,
1183
+ posthogParams
1184
+ } = extractPosthogParams(body);
1185
+ const startTime = Date.now();
1186
+ const parentPromise = super.parse(openAIParams, options);
1187
+ const wrappedPromise = parentPromise.then(async result => {
1188
+ const latency = (Date.now() - startTime) / 1000;
1189
+ await captureAiGeneration(this.phClient, {
1190
+ ...posthogParams,
1191
+ model: openAIParams.model ?? result.model,
1192
+ provider: 'azure',
1193
+ input: formatOpenAIResponsesInput(openAIParams.input, openAIParams.instructions),
1194
+ output: result.output,
1195
+ latency,
1196
+ baseURL: this.baseURL,
1197
+ modelParameters: getModelParams(body),
1198
+ httpStatus: 200,
1199
+ usage: {
1200
+ inputTokens: result.usage?.input_tokens ?? 0,
1201
+ outputTokens: result.usage?.output_tokens ?? 0,
1202
+ reasoningTokens: result.usage?.output_tokens_details?.reasoning_tokens ?? 0,
1203
+ cacheReadInputTokens: result.usage?.input_tokens_details?.cached_tokens ?? 0
1204
+ },
1205
+ completionId: result.id,
1206
+ providerMetadata: buildProviderMetadata({
1207
+ requestId: extractRequestId(result)
1208
+ })
1209
+ });
1210
+ return result;
1211
+ }, async error => {
1212
+ await captureAiGeneration(this.phClient, {
1213
+ ...posthogParams,
1214
+ model: openAIParams.model,
1215
+ provider: 'azure',
1216
+ input: formatOpenAIResponsesInput(openAIParams.input, openAIParams.instructions),
1217
+ output: [],
1218
+ latency: 0,
1219
+ baseURL: this.baseURL,
1220
+ modelParameters: getModelParams(body),
1221
+ httpStatus: error?.status ? error.status : 500,
1222
+ usage: {
1223
+ inputTokens: 0,
1224
+ outputTokens: 0
1225
+ },
1226
+ error
1227
+ });
1228
+ throw error;
1229
+ });
1230
+ return wrappedPromise;
1231
+ }
1232
+ };
1233
+ let WrappedEmbeddings$1 = class WrappedEmbeddings extends openai.AzureOpenAI.Embeddings {
1234
+ constructor(client, phClient) {
1235
+ super(client);
1236
+ this.phClient = phClient;
1237
+ this.baseURL = client.baseURL;
1238
+ }
1239
+ create(body, options) {
1240
+ const {
1241
+ providerParams: openAIParams,
1242
+ posthogParams
1243
+ } = extractPosthogParams(body);
1244
+ const startTime = Date.now();
1245
+ const parentPromise = super.create(openAIParams, options);
1246
+ const wrappedPromise = parentPromise.then(async result => {
1247
+ const latency = (Date.now() - startTime) / 1000;
1248
+ await captureAiGeneration(this.phClient, {
1249
+ eventType: AIEvent.Embedding,
1250
+ ...posthogParams,
1251
+ model: openAIParams.model,
1252
+ provider: 'azure',
1253
+ input: withPrivacyMode(this.phClient, posthogParams.privacyMode, openAIParams.input),
1254
+ output: null,
1255
+ // Embeddings don't have output content
1256
+ latency,
1257
+ baseURL: this.baseURL,
1258
+ modelParameters: getModelParams(body),
1259
+ httpStatus: 200,
1260
+ usage: {
1261
+ inputTokens: result.usage?.prompt_tokens ?? 0
1262
+ }
1263
+ });
1264
+ return result;
1265
+ }, async error => {
1266
+ const httpStatus = error && typeof error === 'object' && 'status' in error ? error.status ?? 500 : 500;
1267
+ await captureAiGeneration(this.phClient, {
1268
+ eventType: AIEvent.Embedding,
1269
+ ...posthogParams,
1270
+ model: openAIParams.model,
1271
+ provider: 'azure',
1272
+ input: withPrivacyMode(this.phClient, posthogParams.privacyMode, openAIParams.input),
1273
+ output: null,
1274
+ latency: 0,
1275
+ baseURL: this.baseURL,
1276
+ modelParameters: getModelParams(body),
1277
+ httpStatus,
1278
+ usage: {
1279
+ inputTokens: 0
1280
+ },
1281
+ error
1282
+ });
1283
+ throw error;
1284
+ });
1285
+ return wrappedPromise;
1286
+ }
1287
+ };
1288
+
762
1289
  const Chat = openai.OpenAI.Chat;
763
1290
  const Completions = Chat.Completions;
764
1291
  const Responses = openai.OpenAI.Responses;
@@ -1542,6 +2069,7 @@ class WrappedTranscriptions extends Transcriptions {
1542
2069
  }
1543
2070
  }
1544
2071
 
2072
+ exports.AzureOpenAI = PostHogAzureOpenAI;
1545
2073
  exports.OpenAI = PostHogOpenAI;
1546
2074
  exports.PostHogOpenAI = PostHogOpenAI;
1547
2075
  exports.WrappedAudio = WrappedAudio;