@posthog/ai 7.19.7 → 7.20.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.
@@ -699,7 +699,7 @@ var BaseCallbackHandler = class extends BaseCallbackHandlerMethodsClass {
699
699
  }
700
700
  };
701
701
 
702
- var version = "7.19.7";
702
+ var version = "7.20.0";
703
703
 
704
704
  const DEFAULT_MAX_DEPTH = 3;
705
705
  const MAX_STACK_LINES = 20;
@@ -677,7 +677,7 @@ var BaseCallbackHandler = class extends BaseCallbackHandlerMethodsClass {
677
677
  }
678
678
  };
679
679
 
680
- var version = "7.19.7";
680
+ var version = "7.20.0";
681
681
 
682
682
  const DEFAULT_MAX_DEPTH = 3;
683
683
  const MAX_STACK_LINES = 20;
@@ -534,7 +534,7 @@ function formatOpenAIResponsesInput(input, instructions) {
534
534
  return messages;
535
535
  }
536
536
 
537
- var version = "7.19.7";
537
+ var version = "7.20.0";
538
538
 
539
539
  const DEFAULT_MAX_DEPTH = 3;
540
540
  const MAX_STACK_LINES = 20;
@@ -701,6 +701,12 @@ const captureAiGeneration = async (client, options) => {
701
701
  ...(options.tools ? {
702
702
  $ai_tools: options.tools
703
703
  } : {}),
704
+ ...(options.completionId ? {
705
+ $ai_completion_id: options.completionId
706
+ } : {}),
707
+ ...(options.providerMetadata && Object.keys(options.providerMetadata).length > 0 ? {
708
+ $ai_provider_metadata: options.providerMetadata
709
+ } : {}),
704
710
  ...errorData,
705
711
  ...costOverrideData
706
712
  };
@@ -725,6 +731,34 @@ function isResponseTokenChunk(chunk) {
725
731
  return chunk.type === 'response.output_item.added' || chunk.type === 'response.content_part.added' || chunk.type === 'response.output_text.delta' || chunk.type === 'response.reasoning_text.delta' || chunk.type === 'response.reasoning_summary_text.delta' || chunk.type === 'response.audio.delta' || chunk.type === 'response.audio.transcript.delta' || chunk.type === 'response.refusal.delta';
726
732
  }
727
733
 
734
+ /**
735
+ * Reads the OpenAI SDK's `_request_id` field from a response object. The SDK
736
+ * attaches the `x-request-id` response header here, but it is not part of the
737
+ * public response types, so it has to be read through a cast. Used to populate
738
+ * `$ai_provider_metadata.request_id`.
739
+ */
740
+ function extractRequestId(result) {
741
+ return result?._request_id ?? undefined;
742
+ }
743
+
744
+ /**
745
+ * Assembles the `$ai_provider_metadata` blob for OpenAI / Azure OpenAI events.
746
+ * Provider-specific fields (system fingerprint, request id) live here rather
747
+ * than in the shared, provider-agnostic `$ai_*` namespace. Only keys with a
748
+ * truthy value are included, and `undefined` is returned when there is nothing
749
+ * to report so the property can be omitted from the event entirely.
750
+ */
751
+ function buildProviderMetadata(fields) {
752
+ const metadata = {};
753
+ if (fields.systemFingerprint) {
754
+ metadata.system_fingerprint = fields.systemFingerprint;
755
+ }
756
+ if (fields.requestId) {
757
+ metadata.request_id = fields.requestId;
758
+ }
759
+ return Object.keys(metadata).length > 0 ? metadata : undefined;
760
+ }
761
+
728
762
  const Chat = openai.OpenAI.Chat;
729
763
  const Completions = Chat.Completions;
730
764
  const Responses = openai.OpenAI.Responses;
@@ -777,6 +811,10 @@ class WrappedCompletions extends Completions {
777
811
  if ('tee' in value) {
778
812
  const [stream1, stream2] = value.tee();
779
813
  (async () => {
814
+ // Hoisted so the catch block can surface whatever was accumulated
815
+ // from the streamed chunks before the failure.
816
+ let completionIdFromResponse;
817
+ let systemFingerprintFromResponse;
780
818
  try {
781
819
  const contentBlocks = [];
782
820
  let accumulatedContent = '';
@@ -793,10 +831,16 @@ class WrappedCompletions extends Completions {
793
831
  const toolCallsInProgress = new Map();
794
832
  let rawUsageData;
795
833
  for await (const chunk of stream1) {
796
- // Extract model from chunk (Chat Completions chunks have model field)
834
+ // Extract model and completion metadata from chunk (Chat Completions chunks carry these fields)
797
835
  if (!modelFromResponse && chunk.model) {
798
836
  modelFromResponse = chunk.model;
799
837
  }
838
+ if (!completionIdFromResponse && chunk.id) {
839
+ completionIdFromResponse = chunk.id;
840
+ }
841
+ if (!systemFingerprintFromResponse && chunk.system_fingerprint) {
842
+ systemFingerprintFromResponse = chunk.system_fingerprint;
843
+ }
800
844
  const choice = chunk?.choices?.[0];
801
845
  if (choice?.finish_reason) {
802
846
  stopReason = choice.finish_reason;
@@ -918,7 +962,11 @@ class WrappedCompletions extends Completions {
918
962
  rawUsage: rawUsageData
919
963
  },
920
964
  stopReason,
921
- tools: availableTools
965
+ tools: availableTools,
966
+ completionId: completionIdFromResponse,
967
+ providerMetadata: buildProviderMetadata({
968
+ systemFingerprint: systemFingerprintFromResponse
969
+ })
922
970
  });
923
971
  } catch (error) {
924
972
  await captureAiGeneration(this.phClient, {
@@ -934,6 +982,13 @@ class WrappedCompletions extends Completions {
934
982
  inputTokens: 0,
935
983
  outputTokens: 0
936
984
  },
985
+ // If the stream fails mid-flight, surface whatever completion
986
+ // metadata the consumed chunks already provided so the error
987
+ // event can still be correlated to OpenAI's Logs dashboard.
988
+ completionId: completionIdFromResponse,
989
+ providerMetadata: buildProviderMetadata({
990
+ systemFingerprint: systemFingerprintFromResponse
991
+ }),
937
992
  error
938
993
  });
939
994
  throw error;
@@ -970,7 +1025,12 @@ class WrappedCompletions extends Completions {
970
1025
  rawUsage: result.usage
971
1026
  },
972
1027
  stopReason: result.choices[0]?.finish_reason ?? undefined,
973
- tools: availableTools
1028
+ tools: availableTools,
1029
+ completionId: result.id,
1030
+ providerMetadata: buildProviderMetadata({
1031
+ systemFingerprint: result.system_fingerprint,
1032
+ requestId: extractRequestId(result)
1033
+ })
974
1034
  });
975
1035
  }
976
1036
  return result;
@@ -1024,6 +1084,9 @@ class WrappedResponses extends Responses {
1024
1084
  if ('tee' in value && typeof value.tee === 'function') {
1025
1085
  const [stream1, stream2] = value.tee();
1026
1086
  (async () => {
1087
+ // Hoisted so the catch block can surface the completion ID that
1088
+ // was accumulated from the streamed chunks before the failure.
1089
+ let completionIdFromResponse;
1027
1090
  try {
1028
1091
  let finalContent = [];
1029
1092
  let modelFromResponse;
@@ -1041,10 +1104,13 @@ class WrappedResponses extends Responses {
1041
1104
  firstTokenTime = Date.now();
1042
1105
  }
1043
1106
  if ('response' in chunk && chunk.response) {
1044
- // Extract model from response object in chunk (for stored prompts)
1107
+ // Extract model and completion ID from the response object in the chunk (for stored prompts)
1045
1108
  if (!modelFromResponse && chunk.response.model) {
1046
1109
  modelFromResponse = chunk.response.model;
1047
1110
  }
1111
+ if (!completionIdFromResponse && chunk.response.id) {
1112
+ completionIdFromResponse = chunk.response.id;
1113
+ }
1048
1114
  const chunkWebSearchCount = calculateWebSearchCount(chunk.response);
1049
1115
  if (chunkWebSearchCount > 0 && chunkWebSearchCount > (usage.webSearchCount ?? 0)) {
1050
1116
  usage.webSearchCount = chunkWebSearchCount;
@@ -1090,7 +1156,8 @@ class WrappedResponses extends Responses {
1090
1156
  rawUsage: rawUsageData
1091
1157
  },
1092
1158
  stopReason,
1093
- tools: availableTools
1159
+ tools: availableTools,
1160
+ completionId: completionIdFromResponse
1094
1161
  });
1095
1162
  } catch (error) {
1096
1163
  await captureAiGeneration(this.phClient, {
@@ -1106,6 +1173,9 @@ class WrappedResponses extends Responses {
1106
1173
  inputTokens: 0,
1107
1174
  outputTokens: 0
1108
1175
  },
1176
+ // Surface the completion ID from any chunks consumed before
1177
+ // the stream failed so the error event remains correlatable.
1178
+ completionId: completionIdFromResponse,
1109
1179
  error
1110
1180
  });
1111
1181
  throw error;
@@ -1142,7 +1212,11 @@ class WrappedResponses extends Responses {
1142
1212
  rawUsage: result.usage
1143
1213
  },
1144
1214
  stopReason: result.status ?? undefined,
1145
- tools: availableTools
1215
+ tools: availableTools,
1216
+ completionId: result.id,
1217
+ providerMetadata: buildProviderMetadata({
1218
+ requestId: extractRequestId(result)
1219
+ })
1146
1220
  });
1147
1221
  }
1148
1222
  return result;
@@ -1200,7 +1274,11 @@ class WrappedResponses extends Responses {
1200
1274
  cacheReadInputTokens: result.usage?.input_tokens_details?.cached_tokens ?? 0,
1201
1275
  rawUsage: result.usage
1202
1276
  },
1203
- stopReason: result.status ?? undefined
1277
+ stopReason: result.status ?? undefined,
1278
+ completionId: result.id,
1279
+ providerMetadata: buildProviderMetadata({
1280
+ requestId: extractRequestId(result)
1281
+ })
1204
1282
  });
1205
1283
  return result;
1206
1284
  }, async error => {