@posthog/ai 8.6.3 → 8.6.5

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.
@@ -43,14 +43,16 @@ const FILE_FAMILY_TYPES = new Set(['file', 'input_file', 'document', 'media', 'f
43
43
  const KNOWN_AUDIO_FORMATS = new Set(['wav', 'mp3', 'ogg', 'flac', 'm4a', 'aac', 'webm']);
44
44
  class MediaTypeContext {
45
45
  static EMPTY = new MediaTypeContext(undefined, undefined);
46
- constructor(parent, key) {
46
+ constructor(parent, key, explicitMediaType) {
47
47
  this.parent = parent;
48
48
  this.key = key;
49
+ this.explicitMediaType = explicitMediaType;
49
50
  }
50
51
  inferMediaType() {
51
52
  return this.inferFromSiblingMime() ?? this.inferFromSiblingFormat() ?? this.inferFromParentType() ?? this.inferFromKey();
52
53
  }
53
54
  inferFromSiblingMime() {
55
+ if (this.explicitMediaType) return this.explicitMediaType;
54
56
  if (!this.parent) return undefined;
55
57
  for (const hint of MIME_HINT_KEYS) {
56
58
  const v = this.parent[hint];
@@ -85,7 +87,13 @@ class MediaTypeContext {
85
87
  if (key.includes('file') || key.includes('document')) return 'application/octet-stream';
86
88
  return undefined;
87
89
  }
90
+ hasExplicitBinaryMediaType() {
91
+ if (!this.explicitMediaType && (!this.parent || !this.key || !STRONG_CONTEXT_KEYS.has(this.key))) return false;
92
+ const mediaType = this.inferFromSiblingMime();
93
+ return mediaType !== undefined && !mediaType.toLowerCase().startsWith('text/');
94
+ }
88
95
  signalsBinary() {
96
+ if (this.explicitMediaType) return true;
89
97
  if (this.parent) {
90
98
  for (const hint of MIME_HINT_KEYS) {
91
99
  if (typeof this.parent[hint] === 'string') return true;
@@ -107,10 +115,10 @@ class BinaryContentRedactor {
107
115
  constructor(recognizer = new Base64Recognizer()) {
108
116
  this.recognizer = recognizer;
109
117
  }
110
- redact(value) {
118
+ redact(value, mediaType) {
111
119
  if (this.isMultimodalEnabled()) return value;
112
120
  this.visited = new WeakSet();
113
- return this.walk(value, MediaTypeContext.EMPTY);
121
+ return this.walk(value, mediaType ? new MediaTypeContext(undefined, undefined, mediaType) : MediaTypeContext.EMPTY);
114
122
  }
115
123
  walk(value, ctx) {
116
124
  if (value === null || value === undefined) return value;
@@ -134,8 +142,10 @@ class BinaryContentRedactor {
134
142
  return out;
135
143
  }
136
144
  redactString(value, ctx) {
137
- const minLength = ctx.signalsBinary() ? STRONG_CONTEXT_MIN_LENGTH : WEAK_CONTEXT_MIN_LENGTH;
138
- const recognition = this.recognizer.recognize(value, minLength);
145
+ const hasExplicitBinaryMediaType = ctx.hasExplicitBinaryMediaType();
146
+ const recognitionValue = hasExplicitBinaryMediaType ? value.replace(/[\r\n]/g, '') : value;
147
+ const minLength = hasExplicitBinaryMediaType ? Math.min(recognitionValue.length, STRONG_CONTEXT_MIN_LENGTH) : ctx.signalsBinary() ? STRONG_CONTEXT_MIN_LENGTH : WEAK_CONTEXT_MIN_LENGTH;
148
+ const recognition = this.recognizer.recognize(recognitionValue, minLength);
139
149
  switch (recognition.kind) {
140
150
  case 'data-url':
141
151
  return this.placeholderFor(recognition.mediaType);
@@ -300,7 +310,7 @@ function addDefaults(params) {
300
310
  };
301
311
  }
302
312
 
303
- var version = "8.6.3";
313
+ var version = "8.6.5";
304
314
 
305
315
  const DEFAULT_MAX_DEPTH = 3;
306
316
  const MAX_STACK_LINES = 20;
@@ -433,6 +443,8 @@ const captureAiGeneration = async (client, options) => {
433
443
  if (httpStatus === undefined) {
434
444
  if (typeof options.error === 'object' && 'status' in options.error && typeof options.error.status === 'number') {
435
445
  httpStatus = options.error.status;
446
+ } else if (typeof options.error === 'object' && 'statusCode' in options.error && typeof options.error.statusCode === 'number') {
447
+ httpStatus = options.error.statusCode;
436
448
  } else {
437
449
  httpStatus = 500;
438
450
  }
@@ -539,6 +551,77 @@ const captureAiGeneration = async (client, options) => {
539
551
  }
540
552
  };
541
553
 
554
+ function addRequestId(result, response, requestIdHeader) {
555
+ if (!result || typeof result !== 'object' || Array.isArray(result)) {
556
+ return result;
557
+ }
558
+ return Object.defineProperty(result, '_request_id', {
559
+ value: response.headers.get(requestIdHeader),
560
+ enumerable: false
561
+ });
562
+ }
563
+ function getResponsePropsPromise(parentPromise) {
564
+ const responsePromise = parentPromise.responsePromise;
565
+ if (!responsePromise || typeof responsePromise.then !== 'function') {
566
+ return undefined;
567
+ }
568
+ return responsePromise;
569
+ }
570
+ function decorateProviderPromise(wrappedPromise, responsePropsPromise, requestIdHeader, preserveThenUnwrap) {
571
+ const providerPromise = wrappedPromise;
572
+ if (responsePropsPromise) {
573
+ providerPromise.asResponse = async () => (await responsePropsPromise).response;
574
+ providerPromise.withResponse = async () => {
575
+ const [props, data] = await Promise.all([responsePropsPromise, wrappedPromise]);
576
+ return {
577
+ response: props.response,
578
+ data,
579
+ request_id: props.response.headers.get(requestIdHeader)
580
+ };
581
+ };
582
+ }
583
+ if (preserveThenUnwrap) {
584
+ providerPromise._thenUnwrap = transform => {
585
+ if (!responsePropsPromise) {
586
+ throw new Error('The provider promise response metadata is unavailable');
587
+ }
588
+ const transformedPromise = Promise.all([wrappedPromise, responsePropsPromise]).then(([data, props]) => addRequestId(transform(data, props), props.response, requestIdHeader));
589
+ return decorateProviderPromise(transformedPromise, responsePropsPromise, requestIdHeader, true);
590
+ };
591
+ }
592
+ return providerPromise;
593
+ }
594
+
595
+ /**
596
+ * Keep the provider SDK helpers on a promise whose resolved value is instrumented.
597
+ * OpenAI's parse helpers compose create calls through `_thenUnwrap`, while both
598
+ * OpenAI and Anthropic expose the raw response through `asResponse` and
599
+ * `withResponse`.
600
+ */
601
+
602
+ function preserveProviderPromise(parentPromise, wrappedPromise, options = {}) {
603
+ const responsePropsPromise = getResponsePropsPromise(parentPromise);
604
+ const preserveThenUnwrap = typeof parentPromise._thenUnwrap === 'function';
605
+ const providerPromise = decorateProviderPromise(wrappedPromise, responsePropsPromise, options.requestIdHeader ?? 'x-request-id', preserveThenUnwrap);
606
+ if (!responsePropsPromise) {
607
+ const asResponse = parentPromise.asResponse?.bind(parentPromise);
608
+ if (asResponse) {
609
+ providerPromise.asResponse = asResponse;
610
+ }
611
+ const withResponse = parentPromise.withResponse?.bind(parentPromise);
612
+ if (withResponse) {
613
+ providerPromise.withResponse = async () => {
614
+ const [response, data] = await Promise.all([withResponse(), wrappedPromise]);
615
+ return {
616
+ ...response,
617
+ data
618
+ };
619
+ };
620
+ }
621
+ }
622
+ return providerPromise;
623
+ }
624
+
542
625
  /**
543
626
  * Splits an SDK stream into a monitoring branch and a caller branch without
544
627
  * allowing either branch to read ahead of the other. Unlike the SDKs' `tee()`
@@ -776,9 +859,9 @@ class WrappedMessages extends AnthropicOriginal__default.default.Messages {
776
859
  posthogParams
777
860
  } = extractPosthogParams(body);
778
861
  const startTime = Date.now();
779
- const parentPromise = super.create(anthropicParams, options);
780
862
  if (anthropicParams.stream) {
781
- return parentPromise.then(value => {
863
+ const parentPromise = super.create(anthropicParams, options);
864
+ const wrappedPromise = parentPromise.then(value => {
782
865
  let accumulatedContent = '';
783
866
  const contentBlocks = [];
784
867
  const toolsInProgress = new Map();
@@ -955,7 +1038,11 @@ class WrappedMessages extends AnthropicOriginal__default.default.Messages {
955
1038
  }
956
1039
  return value;
957
1040
  });
1041
+ return preserveProviderPromise(parentPromise, wrappedPromise, {
1042
+ requestIdHeader: 'request-id'
1043
+ });
958
1044
  } else {
1045
+ const parentPromise = super.create(anthropicParams, options);
959
1046
  const wrappedPromise = parentPromise.then(async result => {
960
1047
  if ('content' in result) {
961
1048
  const latency = (Date.now() - startTime) / 1000;
@@ -1002,7 +1089,9 @@ class WrappedMessages extends AnthropicOriginal__default.default.Messages {
1002
1089
  });
1003
1090
  throw error;
1004
1091
  });
1005
- return wrappedPromise;
1092
+ return preserveProviderPromise(parentPromise, wrappedPromise, {
1093
+ requestIdHeader: 'request-id'
1094
+ });
1006
1095
  }
1007
1096
  }
1008
1097
  }