@posthog/ai 8.14.1 → 8.14.3

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.
Files changed (48) hide show
  1. package/README.md +35 -1
  2. package/dist/adk/index.cjs +16 -6
  3. package/dist/adk/index.cjs.map +1 -1
  4. package/dist/adk/index.mjs +16 -6
  5. package/dist/adk/index.mjs.map +1 -1
  6. package/dist/anthropic/index.cjs +156 -15
  7. package/dist/anthropic/index.cjs.map +1 -1
  8. package/dist/anthropic/index.d.ts +1 -0
  9. package/dist/anthropic/index.mjs +156 -15
  10. package/dist/anthropic/index.mjs.map +1 -1
  11. package/dist/claude-agent-sdk/index.cjs +111 -37
  12. package/dist/claude-agent-sdk/index.cjs.map +1 -1
  13. package/dist/claude-agent-sdk/index.mjs +111 -37
  14. package/dist/claude-agent-sdk/index.mjs.map +1 -1
  15. package/dist/gemini/index.cjs +175 -39
  16. package/dist/gemini/index.cjs.map +1 -1
  17. package/dist/gemini/index.d.ts +1 -1
  18. package/dist/gemini/index.mjs +175 -39
  19. package/dist/gemini/index.mjs.map +1 -1
  20. package/dist/index.cjs +15 -8
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.mjs +15 -8
  23. package/dist/index.mjs.map +1 -1
  24. package/dist/langchain/index.cjs +15 -5
  25. package/dist/langchain/index.cjs.map +1 -1
  26. package/dist/langchain/index.mjs +15 -5
  27. package/dist/langchain/index.mjs.map +1 -1
  28. package/dist/langchain/middleware/index.cjs +15 -5
  29. package/dist/langchain/middleware/index.cjs.map +1 -1
  30. package/dist/langchain/middleware/index.mjs +15 -5
  31. package/dist/langchain/middleware/index.mjs.map +1 -1
  32. package/dist/openai/index.cjs +181 -21
  33. package/dist/openai/index.cjs.map +1 -1
  34. package/dist/openai/index.mjs +181 -21
  35. package/dist/openai/index.mjs.map +1 -1
  36. package/dist/openai-agents/index.cjs +15 -5
  37. package/dist/openai-agents/index.cjs.map +1 -1
  38. package/dist/openai-agents/index.mjs +15 -5
  39. package/dist/openai-agents/index.mjs.map +1 -1
  40. package/dist/otel/index.cjs +12 -3
  41. package/dist/otel/index.cjs.map +1 -1
  42. package/dist/otel/index.mjs +12 -3
  43. package/dist/otel/index.mjs.map +1 -1
  44. package/dist/vercel/index.cjs +15 -8
  45. package/dist/vercel/index.cjs.map +1 -1
  46. package/dist/vercel/index.mjs +15 -8
  47. package/dist/vercel/index.mjs.map +1 -1
  48. package/package.json +4 -4
@@ -5,11 +5,6 @@ Object.defineProperties(exports, {
5
5
  let _google_genai = require("@google/genai");
6
6
  let uuid = require("uuid");
7
7
  let _posthog_core = require("@posthog/core");
8
- //#region src/typeGuards.ts
9
- const isString = (value) => {
10
- return typeof value === "string";
11
- };
12
- //#endregion
13
8
  //#region src/captureAiEvent.ts
14
9
  /** @internal */
15
10
  function isFullAiCaptureEnabled(client) {
@@ -32,6 +27,14 @@ async function captureAiEventImmediate(client, event) {
32
27
  await client.captureImmediate(event);
33
28
  }
34
29
  //#endregion
30
+ //#region src/typeGuards.ts
31
+ const isString = (value) => {
32
+ return typeof value === "string";
33
+ };
34
+ const isObject = (value) => {
35
+ return value !== null && typeof value === "object" && !Array.isArray(value);
36
+ };
37
+ //#endregion
35
38
  //#region src/sanitization/base64_recognizer.ts
36
39
  const DATA_URL_PREFIX_RE = /^data:([^;,\s]+)(?:;[^;,\s]+)*;base64,/i;
37
40
  const BASE64_ALPHABET_RE = /^[A-Za-z0-9+/_=-]+$/;
@@ -175,17 +178,26 @@ var BinaryContentRedactor = class {
175
178
  this.visited = /* @__PURE__ */ new WeakSet();
176
179
  return this.walk(value, mediaType ? new MediaTypeContext(void 0, void 0, mediaType) : MediaTypeContext.EMPTY);
177
180
  }
181
+ /** Redact one leaf while a caller owns bounded, type-preserving traversal. @internal */
182
+ redactLeaf(value, context) {
183
+ return typeof value === "string" ? this.redactString(value, context) : this.placeholderFor(context.inferMediaType());
184
+ }
178
185
  walk(value, ctx) {
179
186
  if (value === null || value === void 0) return value;
180
- if (typeof value === "string") return this.redactString(value, ctx);
187
+ if (typeof value === "string") return this.redactLeaf(value, ctx);
181
188
  if (typeof value !== "object") return value;
182
- if (typeof Uint8Array !== "undefined" && value instanceof Uint8Array) return this.placeholderFor(ctx.inferMediaType());
189
+ if (typeof Uint8Array !== "undefined" && value instanceof Uint8Array) return this.redactLeaf(value, ctx);
183
190
  if (this.visited.has(value)) return null;
184
191
  this.visited.add(value);
185
192
  if (Array.isArray(value)) return value.map((item) => this.walk(item, ctx));
186
193
  const obj = value;
187
194
  const out = {};
188
- for (const k of Object.keys(obj)) out[k] = this.walk(obj[k], new MediaTypeContext(obj, k));
195
+ for (const k of Object.keys(obj)) Object.defineProperty(out, k, {
196
+ value: this.walk(obj[k], new MediaTypeContext(obj, k)),
197
+ enumerable: true,
198
+ writable: true,
199
+ configurable: true
200
+ });
189
201
  return out;
190
202
  }
191
203
  redactString(value, ctx) {
@@ -207,11 +219,11 @@ var BinaryContentRedactor = class {
207
219
  };
208
220
  //#endregion
209
221
  //#region src/sanitization.ts
210
- const redactor = new BinaryContentRedactor();
222
+ const redactor$1 = new BinaryContentRedactor();
211
223
  function redactBase64DataUrl(str, mediaType) {
212
- return redactor.redact(str, mediaType);
224
+ return redactor$1.redact(str, mediaType);
213
225
  }
214
- const sanitize = (data, client) => isFullAiCaptureEnabled(client) ? data : redactor.redact(data);
226
+ const sanitize = (data, client) => isFullAiCaptureEnabled(client) ? data : redactor$1.redact(data);
215
227
  const sanitizeGemini = (data, client) => sanitize(data, client);
216
228
  //#endregion
217
229
  //#region src/utils.ts
@@ -356,8 +368,9 @@ const formatResponseGemini = (response, client) => {
356
368
  });
357
369
  return output;
358
370
  };
371
+ const isPrivacyModeEnabled = (client, privacyMode) => Boolean(client.privacy_mode || privacyMode);
359
372
  const withPrivacyMode = (client, privacyMode, input) => {
360
- return client.privacy_mode || privacyMode ? null : input;
373
+ return isPrivacyModeEnabled(client, privacyMode) ? null : input;
361
374
  };
362
375
  function toSafeString(input) {
363
376
  if (input === void 0 || input === null) return "";
@@ -439,7 +452,7 @@ function addDefaults(params) {
439
452
  }
440
453
  //#endregion
441
454
  //#region package.json
442
- var version = "8.14.1";
455
+ var version = "8.14.3";
443
456
  //#endregion
444
457
  //#region src/serializeError.ts
445
458
  const DEFAULT_MAX_DEPTH = 3;
@@ -526,7 +539,7 @@ const captureAiGeneration = async (client, options) => {
526
539
  const eventType = options.eventType ?? "$ai_generation";
527
540
  const privacyMode = options.privacyMode ?? false;
528
541
  const usage = options.usage ?? {};
529
- const shouldRedact = withPrivacyMode(client, privacyMode, false) === null;
542
+ const shouldRedact = isPrivacyModeEnabled(client, privacyMode);
530
543
  const safeInput = shouldRedact ? null : (0, _posthog_core.toJsonSafeValue)(options.input);
531
544
  const safeOutput = shouldRedact ? null : (0, _posthog_core.toJsonSafeValue)(options.output);
532
545
  let httpStatus = options.httpStatus;
@@ -608,6 +621,89 @@ const captureAiGeneration = async (client, options) => {
608
621
  }
609
622
  };
610
623
  //#endregion
624
+ //#region src/toolResult.ts
625
+ const MAX_DEPTH = 20;
626
+ const MAX_ITEMS = 1e3;
627
+ const MAX_NODES = 1e4;
628
+ const TRUNCATED = "[Truncated]";
629
+ const UNSERIALIZABLE = "[Unserializable]";
630
+ const redactor = new BinaryContentRedactor();
631
+ const propertyIsEnumerable = Object.prototype.propertyIsEnumerable;
632
+ /** @internal Preserve tool-result structure while bounding each string value. */
633
+ function formatToolResult(content, client, maxBytes = 5e3) {
634
+ try {
635
+ if (isFullAiCaptureEnabled(client)) return (0, _posthog_core.toJsonSafeValue)(content);
636
+ const ancestors = /* @__PURE__ */ new WeakSet();
637
+ let remainingNodes = MAX_NODES;
638
+ const convert = (value, depth, context) => {
639
+ if (remainingNodes <= 0) return TRUNCATED;
640
+ remainingNodes--;
641
+ try {
642
+ if (value === null || typeof value !== "object") {
643
+ const safe = (0, _posthog_core.toJsonSafeValue)(value);
644
+ return typeof safe === "string" ? truncate(redactor.redactLeaf(safe, context), void 0, maxBytes) : safe;
645
+ }
646
+ if (depth >= MAX_DEPTH) return TRUNCATED;
647
+ if (typeof Uint8Array !== "undefined" && value instanceof Uint8Array) return redactor.redactLeaf(value, context);
648
+ if (ancestors.has(value)) return "[Circular]";
649
+ ancestors.add(value);
650
+ try {
651
+ if (value instanceof Date) {
652
+ const safe = (0, _posthog_core.toJsonSafeValue)(value);
653
+ return typeof safe === "string" ? truncate(redactor.redactLeaf(safe, context), void 0, maxBytes) : safe;
654
+ }
655
+ let hasToJSONResult = false;
656
+ let toJSONResult;
657
+ try {
658
+ const toJSON = value.toJSON;
659
+ if (typeof toJSON === "function") {
660
+ toJSONResult = toJSON.call(value);
661
+ hasToJSONResult = true;
662
+ }
663
+ } catch {}
664
+ if (hasToJSONResult) return convert(toJSONResult, depth + 1, context);
665
+ if (Array.isArray(value)) {
666
+ const output = [];
667
+ const count = Math.min(value.length, MAX_ITEMS);
668
+ let index = 0;
669
+ for (; index < count && remainingNodes > 0; index++) output.push(convert(value[index], depth + 1, context));
670
+ if (value.length > index) output.push(TRUNCATED);
671
+ return output;
672
+ }
673
+ const input = value;
674
+ const output = {};
675
+ let count = 0;
676
+ let truncated = false;
677
+ for (const key in input) {
678
+ if (!propertyIsEnumerable.call(input, key)) break;
679
+ if (count >= MAX_ITEMS || remainingNodes <= 0) {
680
+ truncated = true;
681
+ break;
682
+ }
683
+ const converted = convert(input[key], depth + 1, new MediaTypeContext(input, key));
684
+ Object.defineProperty(output, key, {
685
+ value: converted,
686
+ enumerable: true,
687
+ writable: true,
688
+ configurable: true
689
+ });
690
+ count++;
691
+ }
692
+ if (truncated) output[TRUNCATED] = "Additional properties omitted";
693
+ return output;
694
+ } finally {
695
+ ancestors.delete(value);
696
+ }
697
+ } catch {
698
+ return UNSERIALIZABLE;
699
+ }
700
+ };
701
+ return convert(content, 0, MediaTypeContext.EMPTY);
702
+ } catch {
703
+ return UNSERIALIZABLE;
704
+ }
705
+ }
706
+ //#endregion
611
707
  //#region src/gemini/usage.ts
612
708
  /** Map Gemini usage metadata to PostHog's provider-agnostic token fields. */
613
709
  function mapGeminiUsage(metadata, additionalUsage = {}) {
@@ -624,6 +720,12 @@ function mapGeminiUsage(metadata, additionalUsage = {}) {
624
720
  //#endregion
625
721
  //#region src/gemini/index.ts
626
722
  const TOOL_RESULT_MAX_BYTES = 5e3;
723
+ function geminiFunctionResponseContent(response) {
724
+ return response.parts !== void 0 ? {
725
+ ...response.response !== void 0 && { response: response.response },
726
+ parts: response.parts
727
+ } : response.response ?? {};
728
+ }
627
729
  var PostHogGoogleGenAI = class {
628
730
  constructor(config) {
629
731
  const { posthog, ...geminiConfig } = config;
@@ -650,7 +752,7 @@ var WrappedModels = class {
650
752
  ...posthogParams,
651
753
  model: geminiParams.model,
652
754
  provider: "gemini",
653
- input: this.formatInputForPostHog(geminiParams),
755
+ input: this.formatInputForPostHog(geminiParams, posthogParams.privacyMode),
654
756
  output: formatResponseGemini(response, this.phClient),
655
757
  latency,
656
758
  baseURL: "https://generativelanguage.googleapis.com",
@@ -667,7 +769,7 @@ var WrappedModels = class {
667
769
  ...posthogParams,
668
770
  model: geminiParams.model,
669
771
  provider: "gemini",
670
- input: this.formatInputForPostHog(geminiParams),
772
+ input: this.formatInputForPostHog(geminiParams, posthogParams.privacyMode),
671
773
  output: [],
672
774
  latency,
673
775
  baseURL: "https://generativelanguage.googleapis.com",
@@ -734,7 +836,7 @@ var WrappedModels = class {
734
836
  ...posthogParams,
735
837
  model: geminiParams.model,
736
838
  provider: "gemini",
737
- input: this.formatInputForPostHog(geminiParams),
839
+ input: this.formatInputForPostHog(geminiParams, posthogParams.privacyMode),
738
840
  output: [],
739
841
  latency,
740
842
  baseURL: "https://generativelanguage.googleapis.com",
@@ -756,7 +858,7 @@ var WrappedModels = class {
756
858
  ...posthogParams,
757
859
  model: geminiParams.model,
758
860
  provider: "gemini",
759
- input: this.formatInputForPostHog(geminiParams),
861
+ input: this.formatInputForPostHog(geminiParams, posthogParams.privacyMode),
760
862
  output,
761
863
  latency,
762
864
  timeToFirstToken,
@@ -840,28 +942,16 @@ var WrappedModels = class {
840
942
  } else if (part && typeof part === "object" && "functionResponse" in part) {
841
943
  const functionResponse = part.functionResponse;
842
944
  if (functionResponse?.name) {
843
- const content = functionResponse.parts !== void 0 ? {
844
- ...functionResponse.response !== void 0 && { response: functionResponse.response },
845
- parts: functionResponse.parts
846
- } : functionResponse.response ?? {};
945
+ const content = geminiFunctionResponseContent(functionResponse);
847
946
  blocks.push({
848
947
  type: "tool_result",
849
948
  ...functionResponse.id != null && { tool_use_id: functionResponse.id },
850
- content: this.boundToolResult(content)
949
+ content
851
950
  });
852
951
  }
853
952
  }
854
953
  return blocks;
855
954
  }
856
- boundToolResult(content) {
857
- if (isFullAiCaptureEnabled(this.phClient)) return content;
858
- try {
859
- const serialized = JSON.stringify(content);
860
- return utf8ByteLength(serialized) > TOOL_RESULT_MAX_BYTES ? truncate(serialized, void 0, TOOL_RESULT_MAX_BYTES) : content;
861
- } catch {
862
- return "[Unserializable tool result]";
863
- }
864
- }
865
955
  formatInput(contents) {
866
956
  if (typeof contents === "string") return [{
867
957
  role: "user",
@@ -936,17 +1026,63 @@ var WrappedModels = class {
936
1026
  }
937
1027
  return null;
938
1028
  }
939
- formatInputForPostHog(params) {
940
- const sanitized = sanitizeGemini(params.contents, this.phClient);
941
- const messages = this.formatInput(sanitized);
942
- const systemInstruction = this.extractSystemInstruction(params);
943
- if (systemInstruction) {
944
- if (!messages.some((msg) => msg.role === "system")) return [{
1029
+ boundToolResult(content) {
1030
+ const normalized = formatToolResult(content, this.phClient, Infinity);
1031
+ if (isFullAiCaptureEnabled(this.phClient)) return normalized;
1032
+ const serialized = JSON.stringify(normalized);
1033
+ return serialized !== void 0 && utf8ByteLength(serialized) > TOOL_RESULT_MAX_BYTES ? truncate(serialized, void 0, TOOL_RESULT_MAX_BYTES) : normalized;
1034
+ }
1035
+ formatInputForPostHog(params, privacyMode) {
1036
+ try {
1037
+ if (isPrivacyModeEnabled(this.phClient, privacyMode)) return null;
1038
+ const preparePart = (part) => {
1039
+ if (!isObject(part) || !Object.hasOwn(part, "functionResponse") || !isObject(part.functionResponse)) return part;
1040
+ const response = part.functionResponse;
1041
+ const hasParts = response.parts !== void 0;
1042
+ const hasResponse = Object.hasOwn(response, "response");
1043
+ if (!hasParts && !hasResponse) return part;
1044
+ const formatted = this.boundToolResult(geminiFunctionResponseContent(response));
1045
+ const prepared = { ...response };
1046
+ if (hasParts && isObject(formatted)) {
1047
+ if (hasResponse) prepared.response = formatted.response;
1048
+ prepared.parts = formatted.parts;
1049
+ } else {
1050
+ prepared.response = formatted;
1051
+ if (hasParts) delete prepared.parts;
1052
+ }
1053
+ return {
1054
+ ...part,
1055
+ functionResponse: prepared
1056
+ };
1057
+ };
1058
+ const prepareContent = (content) => {
1059
+ const part = preparePart(content);
1060
+ if (!isObject(part)) return part;
1061
+ const originalParts = Object.hasOwn(part, "parts") ? part.parts : void 0;
1062
+ const originalContent = Object.hasOwn(part, "content") ? part.content : void 0;
1063
+ const parts = Array.isArray(originalParts) ? originalParts.map(preparePart) : void 0;
1064
+ const blocks = Array.isArray(originalContent) ? originalContent.map(preparePart) : void 0;
1065
+ const partsChanged = parts?.some((value, index) => value !== originalParts[index]);
1066
+ const contentChanged = blocks?.some((value, index) => value !== originalContent[index]);
1067
+ if (!partsChanged && !contentChanged) return part;
1068
+ return {
1069
+ ...part,
1070
+ ...partsChanged && { parts },
1071
+ ...contentChanged && { content: blocks }
1072
+ };
1073
+ };
1074
+ const contents = Array.isArray(params.contents) ? params.contents.map(prepareContent) : prepareContent(params.contents);
1075
+ const sanitized = sanitizeGemini(contents, this.phClient);
1076
+ const messages = this.formatInput(sanitized);
1077
+ const systemInstruction = this.extractSystemInstruction(params);
1078
+ if (systemInstruction && !messages.some((message) => message.role === "system")) return [{
945
1079
  role: "system",
946
1080
  content: systemInstruction
947
1081
  }, ...messages];
1082
+ return messages;
1083
+ } catch {
1084
+ return "[Unserializable]";
948
1085
  }
949
- return messages;
950
1086
  }
951
1087
  };
952
1088
  /**