@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
@@ -1,11 +1,6 @@
1
1
  import { GoogleGenAI } from "@google/genai";
2
2
  import { v4 } from "uuid";
3
3
  import { toJsonSafeValue, uuidv7 } from "@posthog/core";
4
- //#region src/typeGuards.ts
5
- const isString = (value) => {
6
- return typeof value === "string";
7
- };
8
- //#endregion
9
4
  //#region src/captureAiEvent.ts
10
5
  /** @internal */
11
6
  function isFullAiCaptureEnabled(client) {
@@ -28,6 +23,14 @@ async function captureAiEventImmediate(client, event) {
28
23
  await client.captureImmediate(event);
29
24
  }
30
25
  //#endregion
26
+ //#region src/typeGuards.ts
27
+ const isString = (value) => {
28
+ return typeof value === "string";
29
+ };
30
+ const isObject = (value) => {
31
+ return value !== null && typeof value === "object" && !Array.isArray(value);
32
+ };
33
+ //#endregion
31
34
  //#region src/sanitization/base64_recognizer.ts
32
35
  const DATA_URL_PREFIX_RE = /^data:([^;,\s]+)(?:;[^;,\s]+)*;base64,/i;
33
36
  const BASE64_ALPHABET_RE = /^[A-Za-z0-9+/_=-]+$/;
@@ -171,17 +174,26 @@ var BinaryContentRedactor = class {
171
174
  this.visited = /* @__PURE__ */ new WeakSet();
172
175
  return this.walk(value, mediaType ? new MediaTypeContext(void 0, void 0, mediaType) : MediaTypeContext.EMPTY);
173
176
  }
177
+ /** Redact one leaf while a caller owns bounded, type-preserving traversal. @internal */
178
+ redactLeaf(value, context) {
179
+ return typeof value === "string" ? this.redactString(value, context) : this.placeholderFor(context.inferMediaType());
180
+ }
174
181
  walk(value, ctx) {
175
182
  if (value === null || value === void 0) return value;
176
- if (typeof value === "string") return this.redactString(value, ctx);
183
+ if (typeof value === "string") return this.redactLeaf(value, ctx);
177
184
  if (typeof value !== "object") return value;
178
- if (typeof Uint8Array !== "undefined" && value instanceof Uint8Array) return this.placeholderFor(ctx.inferMediaType());
185
+ if (typeof Uint8Array !== "undefined" && value instanceof Uint8Array) return this.redactLeaf(value, ctx);
179
186
  if (this.visited.has(value)) return null;
180
187
  this.visited.add(value);
181
188
  if (Array.isArray(value)) return value.map((item) => this.walk(item, ctx));
182
189
  const obj = value;
183
190
  const out = {};
184
- for (const k of Object.keys(obj)) out[k] = this.walk(obj[k], new MediaTypeContext(obj, k));
191
+ for (const k of Object.keys(obj)) Object.defineProperty(out, k, {
192
+ value: this.walk(obj[k], new MediaTypeContext(obj, k)),
193
+ enumerable: true,
194
+ writable: true,
195
+ configurable: true
196
+ });
185
197
  return out;
186
198
  }
187
199
  redactString(value, ctx) {
@@ -203,11 +215,11 @@ var BinaryContentRedactor = class {
203
215
  };
204
216
  //#endregion
205
217
  //#region src/sanitization.ts
206
- const redactor = new BinaryContentRedactor();
218
+ const redactor$1 = new BinaryContentRedactor();
207
219
  function redactBase64DataUrl(str, mediaType) {
208
- return redactor.redact(str, mediaType);
220
+ return redactor$1.redact(str, mediaType);
209
221
  }
210
- const sanitize = (data, client) => isFullAiCaptureEnabled(client) ? data : redactor.redact(data);
222
+ const sanitize = (data, client) => isFullAiCaptureEnabled(client) ? data : redactor$1.redact(data);
211
223
  const sanitizeGemini = (data, client) => sanitize(data, client);
212
224
  //#endregion
213
225
  //#region src/utils.ts
@@ -352,8 +364,9 @@ const formatResponseGemini = (response, client) => {
352
364
  });
353
365
  return output;
354
366
  };
367
+ const isPrivacyModeEnabled = (client, privacyMode) => Boolean(client.privacy_mode || privacyMode);
355
368
  const withPrivacyMode = (client, privacyMode, input) => {
356
- return client.privacy_mode || privacyMode ? null : input;
369
+ return isPrivacyModeEnabled(client, privacyMode) ? null : input;
357
370
  };
358
371
  function toSafeString(input) {
359
372
  if (input === void 0 || input === null) return "";
@@ -435,7 +448,7 @@ function addDefaults(params) {
435
448
  }
436
449
  //#endregion
437
450
  //#region package.json
438
- var version = "8.14.1";
451
+ var version = "8.14.3";
439
452
  //#endregion
440
453
  //#region src/serializeError.ts
441
454
  const DEFAULT_MAX_DEPTH = 3;
@@ -522,7 +535,7 @@ const captureAiGeneration = async (client, options) => {
522
535
  const eventType = options.eventType ?? "$ai_generation";
523
536
  const privacyMode = options.privacyMode ?? false;
524
537
  const usage = options.usage ?? {};
525
- const shouldRedact = withPrivacyMode(client, privacyMode, false) === null;
538
+ const shouldRedact = isPrivacyModeEnabled(client, privacyMode);
526
539
  const safeInput = shouldRedact ? null : toJsonSafeValue(options.input);
527
540
  const safeOutput = shouldRedact ? null : toJsonSafeValue(options.output);
528
541
  let httpStatus = options.httpStatus;
@@ -604,6 +617,89 @@ const captureAiGeneration = async (client, options) => {
604
617
  }
605
618
  };
606
619
  //#endregion
620
+ //#region src/toolResult.ts
621
+ const MAX_DEPTH = 20;
622
+ const MAX_ITEMS = 1e3;
623
+ const MAX_NODES = 1e4;
624
+ const TRUNCATED = "[Truncated]";
625
+ const UNSERIALIZABLE = "[Unserializable]";
626
+ const redactor = new BinaryContentRedactor();
627
+ const propertyIsEnumerable = Object.prototype.propertyIsEnumerable;
628
+ /** @internal Preserve tool-result structure while bounding each string value. */
629
+ function formatToolResult(content, client, maxBytes = 5e3) {
630
+ try {
631
+ if (isFullAiCaptureEnabled(client)) return toJsonSafeValue(content);
632
+ const ancestors = /* @__PURE__ */ new WeakSet();
633
+ let remainingNodes = MAX_NODES;
634
+ const convert = (value, depth, context) => {
635
+ if (remainingNodes <= 0) return TRUNCATED;
636
+ remainingNodes--;
637
+ try {
638
+ if (value === null || typeof value !== "object") {
639
+ const safe = toJsonSafeValue(value);
640
+ return typeof safe === "string" ? truncate(redactor.redactLeaf(safe, context), void 0, maxBytes) : safe;
641
+ }
642
+ if (depth >= MAX_DEPTH) return TRUNCATED;
643
+ if (typeof Uint8Array !== "undefined" && value instanceof Uint8Array) return redactor.redactLeaf(value, context);
644
+ if (ancestors.has(value)) return "[Circular]";
645
+ ancestors.add(value);
646
+ try {
647
+ if (value instanceof Date) {
648
+ const safe = toJsonSafeValue(value);
649
+ return typeof safe === "string" ? truncate(redactor.redactLeaf(safe, context), void 0, maxBytes) : safe;
650
+ }
651
+ let hasToJSONResult = false;
652
+ let toJSONResult;
653
+ try {
654
+ const toJSON = value.toJSON;
655
+ if (typeof toJSON === "function") {
656
+ toJSONResult = toJSON.call(value);
657
+ hasToJSONResult = true;
658
+ }
659
+ } catch {}
660
+ if (hasToJSONResult) return convert(toJSONResult, depth + 1, context);
661
+ if (Array.isArray(value)) {
662
+ const output = [];
663
+ const count = Math.min(value.length, MAX_ITEMS);
664
+ let index = 0;
665
+ for (; index < count && remainingNodes > 0; index++) output.push(convert(value[index], depth + 1, context));
666
+ if (value.length > index) output.push(TRUNCATED);
667
+ return output;
668
+ }
669
+ const input = value;
670
+ const output = {};
671
+ let count = 0;
672
+ let truncated = false;
673
+ for (const key in input) {
674
+ if (!propertyIsEnumerable.call(input, key)) break;
675
+ if (count >= MAX_ITEMS || remainingNodes <= 0) {
676
+ truncated = true;
677
+ break;
678
+ }
679
+ const converted = convert(input[key], depth + 1, new MediaTypeContext(input, key));
680
+ Object.defineProperty(output, key, {
681
+ value: converted,
682
+ enumerable: true,
683
+ writable: true,
684
+ configurable: true
685
+ });
686
+ count++;
687
+ }
688
+ if (truncated) output[TRUNCATED] = "Additional properties omitted";
689
+ return output;
690
+ } finally {
691
+ ancestors.delete(value);
692
+ }
693
+ } catch {
694
+ return UNSERIALIZABLE;
695
+ }
696
+ };
697
+ return convert(content, 0, MediaTypeContext.EMPTY);
698
+ } catch {
699
+ return UNSERIALIZABLE;
700
+ }
701
+ }
702
+ //#endregion
607
703
  //#region src/gemini/usage.ts
608
704
  /** Map Gemini usage metadata to PostHog's provider-agnostic token fields. */
609
705
  function mapGeminiUsage(metadata, additionalUsage = {}) {
@@ -620,6 +716,12 @@ function mapGeminiUsage(metadata, additionalUsage = {}) {
620
716
  //#endregion
621
717
  //#region src/gemini/index.ts
622
718
  const TOOL_RESULT_MAX_BYTES = 5e3;
719
+ function geminiFunctionResponseContent(response) {
720
+ return response.parts !== void 0 ? {
721
+ ...response.response !== void 0 && { response: response.response },
722
+ parts: response.parts
723
+ } : response.response ?? {};
724
+ }
623
725
  var PostHogGoogleGenAI = class {
624
726
  constructor(config) {
625
727
  const { posthog, ...geminiConfig } = config;
@@ -646,7 +748,7 @@ var WrappedModels = class {
646
748
  ...posthogParams,
647
749
  model: geminiParams.model,
648
750
  provider: "gemini",
649
- input: this.formatInputForPostHog(geminiParams),
751
+ input: this.formatInputForPostHog(geminiParams, posthogParams.privacyMode),
650
752
  output: formatResponseGemini(response, this.phClient),
651
753
  latency,
652
754
  baseURL: "https://generativelanguage.googleapis.com",
@@ -663,7 +765,7 @@ var WrappedModels = class {
663
765
  ...posthogParams,
664
766
  model: geminiParams.model,
665
767
  provider: "gemini",
666
- input: this.formatInputForPostHog(geminiParams),
768
+ input: this.formatInputForPostHog(geminiParams, posthogParams.privacyMode),
667
769
  output: [],
668
770
  latency,
669
771
  baseURL: "https://generativelanguage.googleapis.com",
@@ -730,7 +832,7 @@ var WrappedModels = class {
730
832
  ...posthogParams,
731
833
  model: geminiParams.model,
732
834
  provider: "gemini",
733
- input: this.formatInputForPostHog(geminiParams),
835
+ input: this.formatInputForPostHog(geminiParams, posthogParams.privacyMode),
734
836
  output: [],
735
837
  latency,
736
838
  baseURL: "https://generativelanguage.googleapis.com",
@@ -752,7 +854,7 @@ var WrappedModels = class {
752
854
  ...posthogParams,
753
855
  model: geminiParams.model,
754
856
  provider: "gemini",
755
- input: this.formatInputForPostHog(geminiParams),
857
+ input: this.formatInputForPostHog(geminiParams, posthogParams.privacyMode),
756
858
  output,
757
859
  latency,
758
860
  timeToFirstToken,
@@ -836,28 +938,16 @@ var WrappedModels = class {
836
938
  } else if (part && typeof part === "object" && "functionResponse" in part) {
837
939
  const functionResponse = part.functionResponse;
838
940
  if (functionResponse?.name) {
839
- const content = functionResponse.parts !== void 0 ? {
840
- ...functionResponse.response !== void 0 && { response: functionResponse.response },
841
- parts: functionResponse.parts
842
- } : functionResponse.response ?? {};
941
+ const content = geminiFunctionResponseContent(functionResponse);
843
942
  blocks.push({
844
943
  type: "tool_result",
845
944
  ...functionResponse.id != null && { tool_use_id: functionResponse.id },
846
- content: this.boundToolResult(content)
945
+ content
847
946
  });
848
947
  }
849
948
  }
850
949
  return blocks;
851
950
  }
852
- boundToolResult(content) {
853
- if (isFullAiCaptureEnabled(this.phClient)) return content;
854
- try {
855
- const serialized = JSON.stringify(content);
856
- return utf8ByteLength(serialized) > TOOL_RESULT_MAX_BYTES ? truncate(serialized, void 0, TOOL_RESULT_MAX_BYTES) : content;
857
- } catch {
858
- return "[Unserializable tool result]";
859
- }
860
- }
861
951
  formatInput(contents) {
862
952
  if (typeof contents === "string") return [{
863
953
  role: "user",
@@ -932,17 +1022,63 @@ var WrappedModels = class {
932
1022
  }
933
1023
  return null;
934
1024
  }
935
- formatInputForPostHog(params) {
936
- const sanitized = sanitizeGemini(params.contents, this.phClient);
937
- const messages = this.formatInput(sanitized);
938
- const systemInstruction = this.extractSystemInstruction(params);
939
- if (systemInstruction) {
940
- if (!messages.some((msg) => msg.role === "system")) return [{
1025
+ boundToolResult(content) {
1026
+ const normalized = formatToolResult(content, this.phClient, Infinity);
1027
+ if (isFullAiCaptureEnabled(this.phClient)) return normalized;
1028
+ const serialized = JSON.stringify(normalized);
1029
+ return serialized !== void 0 && utf8ByteLength(serialized) > TOOL_RESULT_MAX_BYTES ? truncate(serialized, void 0, TOOL_RESULT_MAX_BYTES) : normalized;
1030
+ }
1031
+ formatInputForPostHog(params, privacyMode) {
1032
+ try {
1033
+ if (isPrivacyModeEnabled(this.phClient, privacyMode)) return null;
1034
+ const preparePart = (part) => {
1035
+ if (!isObject(part) || !Object.hasOwn(part, "functionResponse") || !isObject(part.functionResponse)) return part;
1036
+ const response = part.functionResponse;
1037
+ const hasParts = response.parts !== void 0;
1038
+ const hasResponse = Object.hasOwn(response, "response");
1039
+ if (!hasParts && !hasResponse) return part;
1040
+ const formatted = this.boundToolResult(geminiFunctionResponseContent(response));
1041
+ const prepared = { ...response };
1042
+ if (hasParts && isObject(formatted)) {
1043
+ if (hasResponse) prepared.response = formatted.response;
1044
+ prepared.parts = formatted.parts;
1045
+ } else {
1046
+ prepared.response = formatted;
1047
+ if (hasParts) delete prepared.parts;
1048
+ }
1049
+ return {
1050
+ ...part,
1051
+ functionResponse: prepared
1052
+ };
1053
+ };
1054
+ const prepareContent = (content) => {
1055
+ const part = preparePart(content);
1056
+ if (!isObject(part)) return part;
1057
+ const originalParts = Object.hasOwn(part, "parts") ? part.parts : void 0;
1058
+ const originalContent = Object.hasOwn(part, "content") ? part.content : void 0;
1059
+ const parts = Array.isArray(originalParts) ? originalParts.map(preparePart) : void 0;
1060
+ const blocks = Array.isArray(originalContent) ? originalContent.map(preparePart) : void 0;
1061
+ const partsChanged = parts?.some((value, index) => value !== originalParts[index]);
1062
+ const contentChanged = blocks?.some((value, index) => value !== originalContent[index]);
1063
+ if (!partsChanged && !contentChanged) return part;
1064
+ return {
1065
+ ...part,
1066
+ ...partsChanged && { parts },
1067
+ ...contentChanged && { content: blocks }
1068
+ };
1069
+ };
1070
+ const contents = Array.isArray(params.contents) ? params.contents.map(prepareContent) : prepareContent(params.contents);
1071
+ const sanitized = sanitizeGemini(contents, this.phClient);
1072
+ const messages = this.formatInput(sanitized);
1073
+ const systemInstruction = this.extractSystemInstruction(params);
1074
+ if (systemInstruction && !messages.some((message) => message.role === "system")) return [{
941
1075
  role: "system",
942
1076
  content: systemInstruction
943
1077
  }, ...messages];
1078
+ return messages;
1079
+ } catch {
1080
+ return "[Unserializable]";
944
1081
  }
945
- return messages;
946
1082
  }
947
1083
  };
948
1084
  /**