@posthog/ai 7.2.1 → 7.2.2

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.
package/dist/index.d.ts CHANGED
@@ -206,6 +206,7 @@ declare class WrappedModels {
206
206
  constructor(client: GoogleGenAI, phClient: PostHog);
207
207
  generateContent(params: GenerateContentParameters & MonitoringParams): Promise<GenerateContentResponse>;
208
208
  generateContentStream(params: GenerateContentParameters & MonitoringParams): AsyncGenerator<GenerateContentResponse, void, unknown>;
209
+ private formatPartsAsContentBlocks;
209
210
  private formatInput;
210
211
  private extractSystemInstruction;
211
212
  private formatInputForPostHog;
package/dist/index.mjs CHANGED
@@ -6,7 +6,7 @@ import { wrapLanguageModel } from 'ai';
6
6
  import AnthropicOriginal from '@anthropic-ai/sdk';
7
7
  import { GoogleGenAI } from '@google/genai';
8
8
 
9
- var version = "7.2.1";
9
+ var version = "7.2.2";
10
10
 
11
11
  // Type guards for safer type checking
12
12
  const isString = value => {
@@ -113,6 +113,13 @@ const formatResponseOpenAI = response => {
113
113
  });
114
114
  }
115
115
  }
116
+ // Handle audio output (gpt-4o-audio-preview)
117
+ if (choice.message.audio) {
118
+ content.push({
119
+ type: 'audio',
120
+ ...choice.message.audio
121
+ });
122
+ }
116
123
  }
117
124
  if (content.length > 0) {
118
125
  output.push({
@@ -194,6 +201,19 @@ const formatResponseGemini = response => {
194
201
  arguments: part.functionCall.args
195
202
  }
196
203
  });
204
+ } else if (part.inlineData) {
205
+ // Handle audio/media inline data
206
+ const mimeType = part.inlineData.mimeType || 'audio/pcm';
207
+ let data = part.inlineData.data;
208
+ // Handle binary data (Buffer/Uint8Array -> base64)
209
+ if (data instanceof Uint8Array || Buffer.isBuffer(data)) {
210
+ data = Buffer.from(data).toString('base64');
211
+ }
212
+ content.push({
213
+ type: 'audio',
214
+ mime_type: mimeType,
215
+ data: data
216
+ });
197
217
  }
198
218
  }
199
219
  if (content.length > 0) {
@@ -598,6 +618,13 @@ function formatOpenAIResponsesInput(input, instructions) {
598
618
 
599
619
  const REDACTED_IMAGE_PLACEHOLDER = '[base64 image redacted]';
600
620
  // ============================================
621
+ // Multimodal Feature Toggle
622
+ // ============================================
623
+ const isMultimodalEnabled = () => {
624
+ const val = process.env._INTERNAL_LLMA_MULTIMODAL || '';
625
+ return val.toLowerCase() === 'true' || val === '1' || val.toLowerCase() === 'yes';
626
+ };
627
+ // ============================================
601
628
  // Base64 Detection Helpers
602
629
  // ============================================
603
630
  const isBase64DataUrl = str => {
@@ -622,6 +649,7 @@ const isRawBase64 = str => {
622
649
  return str.length > 20 && /^[A-Za-z0-9+/]+=*$/.test(str);
623
650
  };
624
651
  function redactBase64DataUrl(str) {
652
+ if (isMultimodalEnabled()) return str;
625
653
  if (!isString(str)) return str;
626
654
  // Check for data URL format
627
655
  if (isBase64DataUrl(str)) {
@@ -672,12 +700,21 @@ const sanitizeOpenAIImage = item => {
672
700
  }
673
701
  };
674
702
  }
703
+ // Handle audio format
704
+ if (item.type === 'audio' && 'data' in item) {
705
+ if (isMultimodalEnabled()) return item;
706
+ return {
707
+ ...item,
708
+ data: REDACTED_IMAGE_PLACEHOLDER
709
+ };
710
+ }
675
711
  return item;
676
712
  };
677
713
  const sanitizeAnthropicImage = item => {
714
+ if (isMultimodalEnabled()) return item;
678
715
  if (!isObject(item)) return item;
679
- // Handle Anthropic's image format
680
- if (item.type === 'image' && 'source' in item && isObject(item.source) && item.source.type === 'base64' && 'data' in item.source) {
716
+ // Handle Anthropic's image and document formats (same structure, different type field)
717
+ if ((item.type === 'image' || item.type === 'document') && 'source' in item && isObject(item.source) && item.source.type === 'base64' && 'data' in item.source) {
681
718
  return {
682
719
  ...item,
683
720
  source: {
@@ -689,8 +726,9 @@ const sanitizeAnthropicImage = item => {
689
726
  return item;
690
727
  };
691
728
  const sanitizeGeminiPart = part => {
729
+ if (isMultimodalEnabled()) return part;
692
730
  if (!isObject(part)) return part;
693
- // Handle Gemini's inline data format
731
+ // Handle Gemini's inline data format (images, audio, PDFs all use inlineData)
694
732
  if ('inlineData' in part && isObject(part.inlineData) && 'data' in part.inlineData) {
695
733
  return {
696
734
  ...part,
@@ -735,6 +773,7 @@ const sanitizeLangChainImage = item => {
735
773
  }
736
774
  // Anthropic style
737
775
  if (item.type === 'image' && 'source' in item && isObject(item.source) && 'data' in item.source) {
776
+ if (isMultimodalEnabled()) return item;
738
777
  return {
739
778
  ...item,
740
779
  source: {
@@ -2790,6 +2829,39 @@ class WrappedModels {
2790
2829
  throw error;
2791
2830
  }
2792
2831
  }
2832
+ formatPartsAsContentBlocks(parts) {
2833
+ const blocks = [];
2834
+ for (const part of parts) {
2835
+ // Handle dict/object with text field
2836
+ if (part && typeof part === 'object' && 'text' in part && part.text) {
2837
+ blocks.push({
2838
+ type: 'text',
2839
+ text: String(part.text)
2840
+ });
2841
+ }
2842
+ // Handle string parts
2843
+ else if (typeof part === 'string') {
2844
+ blocks.push({
2845
+ type: 'text',
2846
+ text: part
2847
+ });
2848
+ }
2849
+ // Handle inlineData (images, audio, PDFs)
2850
+ else if (part && typeof part === 'object' && 'inlineData' in part) {
2851
+ const inlineData = part.inlineData;
2852
+ const mimeType = inlineData.mimeType || inlineData.mime_type || '';
2853
+ const contentType = mimeType.startsWith('image/') ? 'image' : 'document';
2854
+ blocks.push({
2855
+ type: contentType,
2856
+ inline_data: {
2857
+ data: inlineData.data,
2858
+ mime_type: mimeType
2859
+ }
2860
+ });
2861
+ }
2862
+ }
2863
+ return blocks;
2864
+ }
2793
2865
  formatInput(contents) {
2794
2866
  if (typeof contents === 'string') {
2795
2867
  return [{
@@ -2814,20 +2886,24 @@ class WrappedModels {
2814
2886
  };
2815
2887
  }
2816
2888
  if ('content' in obj && obj.content) {
2889
+ // If content is a list, format it as content blocks
2890
+ if (Array.isArray(obj.content)) {
2891
+ const contentBlocks = this.formatPartsAsContentBlocks(obj.content);
2892
+ return {
2893
+ role: isString(obj.role) ? obj.role : 'user',
2894
+ content: contentBlocks
2895
+ };
2896
+ }
2817
2897
  return {
2818
2898
  role: isString(obj.role) ? obj.role : 'user',
2819
2899
  content: obj.content
2820
2900
  };
2821
2901
  }
2822
2902
  if ('parts' in obj && Array.isArray(obj.parts)) {
2903
+ const contentBlocks = this.formatPartsAsContentBlocks(obj.parts);
2823
2904
  return {
2824
2905
  role: isString(obj.role) ? obj.role : 'user',
2825
- content: obj.parts.map(part => {
2826
- if (part && typeof part === 'object' && 'text' in part) {
2827
- return part.text;
2828
- }
2829
- return part;
2830
- })
2906
+ content: contentBlocks
2831
2907
  };
2832
2908
  }
2833
2909
  }
@@ -3405,7 +3481,7 @@ var BaseCallbackHandler = class extends BaseCallbackHandlerMethodsClass {
3405
3481
  }
3406
3482
  static fromMethods(methods) {
3407
3483
  class Handler extends BaseCallbackHandler {
3408
- name = uuid.v4();
3484
+ name = uuid.v7();
3409
3485
  constructor() {
3410
3486
  super();
3411
3487
  Object.assign(this, methods);