@posthog/ai 4.0.1 → 4.2.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.
package/lib/index.cjs.js CHANGED
@@ -32,6 +32,9 @@ var OpenAIOrignal__default = /*#__PURE__*/_interopDefaultLegacy(OpenAIOrignal);
32
32
  var uuid__namespace = /*#__PURE__*/_interopNamespace(uuid);
33
33
  var AnthropicOriginal__default = /*#__PURE__*/_interopDefaultLegacy(AnthropicOriginal);
34
34
 
35
+ // limit large outputs by truncating to 200kb (approx 200k bytes)
36
+ const MAX_OUTPUT_SIZE = 200000;
37
+ const STRING_FORMAT = 'utf8';
35
38
  const getModelParams = params => {
36
39
  if (!params) {
37
40
  return {};
@@ -87,13 +90,26 @@ const mergeSystemPrompt = (params, provider) => {
87
90
  const withPrivacyMode = (client, privacyMode, input) => {
88
91
  return client.privacy_mode || privacyMode ? null : input;
89
92
  };
93
+ const truncate = str => {
94
+ try {
95
+ const buffer$1 = buffer.Buffer.from(str, STRING_FORMAT);
96
+ if (buffer$1.length <= MAX_OUTPUT_SIZE) {
97
+ return str;
98
+ }
99
+ const truncatedBuffer = buffer$1.slice(0, MAX_OUTPUT_SIZE);
100
+ return `${truncatedBuffer.toString(STRING_FORMAT)}... [truncated]`;
101
+ } catch (error) {
102
+ console.error('Error truncating, likely not a string');
103
+ return str;
104
+ }
105
+ };
90
106
  function sanitizeValues(obj) {
91
107
  if (obj === undefined || obj === null) {
92
108
  return obj;
93
109
  }
94
110
  const jsonSafe = JSON.parse(JSON.stringify(obj));
95
111
  if (typeof jsonSafe === 'string') {
96
- return buffer.Buffer.from(jsonSafe, 'utf8').toString('utf8');
112
+ return buffer.Buffer.from(jsonSafe, STRING_FORMAT).toString(STRING_FORMAT);
97
113
  } else if (Array.isArray(jsonSafe)) {
98
114
  return jsonSafe.map(sanitizeValues);
99
115
  } else if (jsonSafe && typeof jsonSafe === 'object') {
@@ -116,7 +132,8 @@ const sendEventToPosthog = ({
116
132
  usage = {},
117
133
  isError = false,
118
134
  error,
119
- tools
135
+ tools,
136
+ fullDebug = false
120
137
  }) => {
121
138
  if (client.capture) {
122
139
  // sanitize input and output for UTF-8 validity
@@ -151,32 +168,37 @@ const sendEventToPosthog = ({
151
168
  $ai_cache_creation_input_tokens: usage.cacheCreationInputTokens
152
169
  } : {})
153
170
  };
171
+ const properties = {
172
+ $ai_provider: params.posthogProviderOverride ?? provider,
173
+ $ai_model: params.posthogModelOverride ?? model,
174
+ $ai_model_parameters: getModelParams(params),
175
+ $ai_input: withPrivacyMode(client, params.posthogPrivacyMode ?? false, safeInput),
176
+ $ai_output_choices: withPrivacyMode(client, params.posthogPrivacyMode ?? false, safeOutput),
177
+ $ai_http_status: httpStatus,
178
+ $ai_input_tokens: usage.inputTokens ?? 0,
179
+ $ai_output_tokens: usage.outputTokens ?? 0,
180
+ ...additionalTokenValues,
181
+ $ai_latency: latency,
182
+ $ai_trace_id: traceId,
183
+ $ai_base_url: baseURL,
184
+ ...params.posthogProperties,
185
+ ...(distinctId ? {} : {
186
+ $process_person_profile: false
187
+ }),
188
+ ...(tools ? {
189
+ $ai_tools: tools
190
+ } : {}),
191
+ ...errorData,
192
+ ...costOverrideData
193
+ };
194
+ if (fullDebug) {
195
+ // @ts-ignore
196
+ console.log('Sending event to PostHog', properties);
197
+ }
154
198
  client.capture({
155
199
  distinctId: distinctId ?? traceId,
156
200
  event: '$ai_generation',
157
- properties: {
158
- $ai_provider: params.posthogProviderOverride ?? provider,
159
- $ai_model: params.posthogModelOverride ?? model,
160
- $ai_model_parameters: getModelParams(params),
161
- $ai_input: withPrivacyMode(client, params.posthogPrivacyMode ?? false, safeInput),
162
- $ai_output_choices: withPrivacyMode(client, params.posthogPrivacyMode ?? false, safeOutput),
163
- $ai_http_status: httpStatus,
164
- $ai_input_tokens: usage.inputTokens ?? 0,
165
- $ai_output_tokens: usage.outputTokens ?? 0,
166
- ...additionalTokenValues,
167
- $ai_latency: latency,
168
- $ai_trace_id: traceId,
169
- $ai_base_url: baseURL,
170
- ...params.posthogProperties,
171
- ...(distinctId ? {} : {
172
- $process_person_profile: false
173
- }),
174
- ...(tools ? {
175
- $ai_tools: tools
176
- } : {}),
177
- ...errorData,
178
- ...costOverrideData
179
- },
201
+ properties,
180
202
  groups: params.posthogGroups
181
203
  });
182
204
  }
@@ -515,14 +537,27 @@ const mapVercelParams = params => {
515
537
  };
516
538
  };
517
539
  const mapVercelPrompt = prompt => {
518
- return prompt.map(p => {
540
+ // normalize single inputs into an array of messages
541
+ let promptsArray;
542
+ if (typeof prompt === 'string') {
543
+ promptsArray = [{
544
+ role: 'user',
545
+ content: prompt
546
+ }];
547
+ } else if (!Array.isArray(prompt)) {
548
+ promptsArray = [prompt];
549
+ } else {
550
+ promptsArray = prompt;
551
+ }
552
+ // Map and truncate individual content
553
+ const inputs = promptsArray.map(p => {
519
554
  let content = {};
520
555
  if (Array.isArray(p.content)) {
521
556
  content = p.content.map(c => {
522
557
  if (c.type === 'text') {
523
558
  return {
524
559
  type: 'text',
525
- content: c.text
560
+ content: truncate(c.text)
526
561
  };
527
562
  } else if (c.type === 'image') {
528
563
  return {
@@ -568,7 +603,7 @@ const mapVercelPrompt = prompt => {
568
603
  } else {
569
604
  content = {
570
605
  type: 'text',
571
- text: p.content
606
+ text: truncate(p.content)
572
607
  };
573
608
  }
574
609
  return {
@@ -576,48 +611,85 @@ const mapVercelPrompt = prompt => {
576
611
  content
577
612
  };
578
613
  });
614
+ try {
615
+ // Trim the inputs array until its JSON size fits within MAX_OUTPUT_SIZE
616
+ let serialized = JSON.stringify(inputs);
617
+ while (buffer.Buffer.byteLength(serialized, 'utf8') > MAX_OUTPUT_SIZE && inputs.length > 0) {
618
+ // Remove oldest message
619
+ inputs.shift();
620
+ // add blank message to beginning of array
621
+ inputs.unshift({
622
+ role: 'assistant',
623
+ content: '[removed message due to size limit]'
624
+ });
625
+ serialized = JSON.stringify(inputs);
626
+ }
627
+ } catch (error) {
628
+ console.error('Error stringifying inputs');
629
+ return [{
630
+ role: 'posthog',
631
+ content: 'An error occurred while processing your request. Please try again.'
632
+ }];
633
+ }
634
+ return inputs;
579
635
  };
580
636
  const mapVercelOutput = result => {
637
+ // normalize string results to object
638
+ const normalizedResult = typeof result === 'string' ? {
639
+ text: result
640
+ } : result;
581
641
  const output = {
582
- ...(result.text ? {
583
- text: result.text
642
+ ...(normalizedResult.text ? {
643
+ text: normalizedResult.text
584
644
  } : {}),
585
- ...(result.object ? {
586
- object: result.object
645
+ ...(normalizedResult.object ? {
646
+ object: normalizedResult.object
587
647
  } : {}),
588
- ...(result.reasoning ? {
589
- reasoning: result.reasoning
648
+ ...(normalizedResult.reasoning ? {
649
+ reasoning: normalizedResult.reasoning
590
650
  } : {}),
591
- ...(result.response ? {
592
- response: result.response
651
+ ...(normalizedResult.response ? {
652
+ response: normalizedResult.response
593
653
  } : {}),
594
- ...(result.finishReason ? {
595
- finishReason: result.finishReason
654
+ ...(normalizedResult.finishReason ? {
655
+ finishReason: normalizedResult.finishReason
596
656
  } : {}),
597
- ...(result.usage ? {
598
- usage: result.usage
657
+ ...(normalizedResult.usage ? {
658
+ usage: normalizedResult.usage
599
659
  } : {}),
600
- ...(result.warnings ? {
601
- warnings: result.warnings
660
+ ...(normalizedResult.warnings ? {
661
+ warnings: normalizedResult.warnings
602
662
  } : {}),
603
- ...(result.providerMetadata ? {
604
- toolCalls: result.providerMetadata
663
+ ...(normalizedResult.providerMetadata ? {
664
+ toolCalls: normalizedResult.providerMetadata
665
+ } : {}),
666
+ ...(normalizedResult.files ? {
667
+ files: normalizedResult.files.map(file => ({
668
+ name: file.name,
669
+ size: file.size,
670
+ type: file.type
671
+ }))
605
672
  } : {})
606
673
  };
607
- // if text and no object or reasoning, return text
608
674
  if (output.text && !output.object && !output.reasoning) {
609
675
  return [{
610
- content: output.text,
676
+ content: truncate(output.text),
677
+ role: 'assistant'
678
+ }];
679
+ }
680
+ // otherwise stringify and truncate
681
+ try {
682
+ const jsonOutput = JSON.stringify(output);
683
+ return [{
684
+ content: truncate(jsonOutput),
611
685
  role: 'assistant'
612
686
  }];
687
+ } catch (error) {
688
+ console.error('Error stringifying output');
689
+ return [];
613
690
  }
614
- return [{
615
- content: JSON.stringify(output),
616
- role: 'assistant'
617
- }];
618
691
  };
619
692
  const extractProvider = model => {
620
- // vercel provider is in the format of provider.endpoint
621
693
  const provider = model.provider.toLowerCase();
622
694
  const providerName = provider.split('.')[0];
623
695
  return providerName;
@@ -673,7 +745,8 @@ const createInstrumentationMiddleware = (phClient, model, options) => {
673
745
  inputTokens: result.usage.promptTokens,
674
746
  outputTokens: result.usage.completionTokens,
675
747
  ...additionalTokenValues
676
- }
748
+ },
749
+ fullDebug: options.fullDebug
677
750
  });
678
751
  return result;
679
752
  } catch (error) {
@@ -695,7 +768,8 @@ const createInstrumentationMiddleware = (phClient, model, options) => {
695
768
  outputTokens: 0
696
769
  },
697
770
  isError: true,
698
- error: JSON.stringify(error)
771
+ error: truncate(JSON.stringify(error)),
772
+ fullDebug: options.fullDebug
699
773
  });
700
774
  throw error;
701
775
  }
@@ -761,7 +835,8 @@ const createInstrumentationMiddleware = (phClient, model, options) => {
761
835
  baseURL,
762
836
  params: mergedParams,
763
837
  httpStatus: 200,
764
- usage
838
+ usage,
839
+ fullDebug: options.fullDebug
765
840
  });
766
841
  }
767
842
  });
@@ -787,7 +862,8 @@ const createInstrumentationMiddleware = (phClient, model, options) => {
787
862
  outputTokens: 0
788
863
  },
789
864
  isError: true,
790
- error: JSON.stringify(error)
865
+ error: truncate(JSON.stringify(error)),
866
+ fullDebug: options.fullDebug
791
867
  });
792
868
  throw error;
793
869
  }