@posthog/ai 7.4.0 → 7.4.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.
@@ -7,7 +7,7 @@ var buffer = require('buffer');
7
7
  var uuid = require('uuid');
8
8
  var core = require('@posthog/core');
9
9
 
10
- var version = "7.4.0";
10
+ var version = "7.4.2";
11
11
 
12
12
  // Type guards for safer type checking
13
13
 
@@ -3,7 +3,7 @@ import { Buffer } from 'buffer';
3
3
  import { v4 } from 'uuid';
4
4
  import { uuidv7 } from '@posthog/core';
5
5
 
6
- var version = "7.4.0";
6
+ var version = "7.4.2";
7
7
 
8
8
  // Type guards for safer type checking
9
9
 
@@ -4,7 +4,7 @@ var uuid = require('uuid');
4
4
  var buffer = require('buffer');
5
5
  var core = require('@posthog/core');
6
6
 
7
- var version = "7.4.0";
7
+ var version = "7.4.2";
8
8
 
9
9
  // Type guards for safer type checking
10
10
 
@@ -413,6 +413,13 @@ function redactBase64DataUrl(str) {
413
413
  return str;
414
414
  }
415
415
 
416
+ // Union types for dual version support
417
+
418
+ // Type guards
419
+ function isV3Model(model) {
420
+ return model.specificationVersion === 'v3';
421
+ }
422
+
416
423
  // Content types for the output array
417
424
 
418
425
  const mapVercelParams = params => {
@@ -641,6 +648,20 @@ const extractAdditionalTokenValues = providerMetadata => {
641
648
  return {};
642
649
  };
643
650
 
651
+ // For Anthropic providers in V3, inputTokens.total is the sum of all tokens (uncached + cache read + cache write).
652
+ // Our cost calculation expects inputTokens to be only the uncached portion for Anthropic.
653
+ // This helper subtracts cache tokens from inputTokens for Anthropic V3 models.
654
+ const adjustAnthropicV3CacheTokens = (model, provider, usage) => {
655
+ if (isV3Model(model) && provider.toLowerCase().includes('anthropic')) {
656
+ const cacheReadTokens = usage.cacheReadInputTokens || 0;
657
+ const cacheWriteTokens = usage.cacheCreationInputTokens || 0;
658
+ const cacheTokens = cacheReadTokens + cacheWriteTokens;
659
+ if (usage.inputTokens && cacheTokens > 0) {
660
+ usage.inputTokens = Math.max(usage.inputTokens - cacheTokens, 0);
661
+ }
662
+ }
663
+ };
664
+
644
665
  // Helper to extract numeric token value from V2 (number) or V3 (object with .total) usage formats
645
666
  const extractTokenCount = value => {
646
667
  if (typeof value === 'number') {
@@ -695,240 +716,252 @@ const wrapVercelLanguageModel = (model, phClient, options) => {
695
716
  }
696
717
  };
697
718
 
698
- // Create wrapped model that preserves the original type
699
- const wrappedModel = {
700
- ...model,
701
- doGenerate: async params => {
702
- const startTime = Date.now();
703
- const mergedParams = {
704
- ...mergedOptions,
705
- ...mapVercelParams(params)
706
- };
707
- const availableTools = extractAvailableToolCalls('vercel', params);
708
- try {
709
- const result = await model.doGenerate(params);
710
- const modelId = mergedOptions.posthogModelOverride ?? (result.response?.modelId ? result.response.modelId : model.modelId);
711
- const provider = mergedOptions.posthogProviderOverride ?? extractProvider(model);
712
- const baseURL = ''; // cannot currently get baseURL from vercel
713
- const content = mapVercelOutput(result.content);
714
- const latency = (Date.now() - startTime) / 1000;
715
- const providerMetadata = result.providerMetadata;
716
- const additionalTokenValues = extractAdditionalTokenValues(providerMetadata);
717
- const webSearchCount = extractWebSearchCount(providerMetadata, result.usage);
718
-
719
- // V2 usage has simple numbers, V3 has objects with .total - normalize both
720
- const usageObj = result.usage;
721
- const usage = {
722
- inputTokens: extractTokenCount(result.usage.inputTokens),
723
- outputTokens: extractTokenCount(result.usage.outputTokens),
724
- reasoningTokens: extractReasoningTokens(usageObj),
725
- cacheReadInputTokens: extractCacheReadTokens(usageObj),
726
- webSearchCount,
727
- ...additionalTokenValues
719
+ // Create wrapped model using Object.create to preserve the prototype chain
720
+ // This automatically inherits all properties (including getters) from the model
721
+ const wrappedModel = Object.create(model, {
722
+ doGenerate: {
723
+ value: async params => {
724
+ const startTime = Date.now();
725
+ const mergedParams = {
726
+ ...mergedOptions,
727
+ ...mapVercelParams(params)
728
728
  };
729
- await sendEventToPosthog({
730
- client: phClient,
731
- distinctId: mergedOptions.posthogDistinctId,
732
- traceId: mergedOptions.posthogTraceId ?? uuid.v4(),
733
- model: modelId,
734
- provider: provider,
735
- input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt),
736
- output: content,
737
- latency,
738
- baseURL,
739
- params: mergedParams,
740
- httpStatus: 200,
741
- usage,
742
- tools: availableTools,
743
- captureImmediate: mergedOptions.posthogCaptureImmediate
744
- });
745
- return result;
746
- } catch (error) {
747
- const modelId = model.modelId;
748
- const enrichedError = await sendEventWithErrorToPosthog({
749
- client: phClient,
750
- distinctId: mergedOptions.posthogDistinctId,
751
- traceId: mergedOptions.posthogTraceId ?? uuid.v4(),
752
- model: modelId,
753
- provider: model.provider,
754
- input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt),
755
- output: [],
756
- latency: 0,
757
- baseURL: '',
758
- params: mergedParams,
759
- usage: {
760
- inputTokens: 0,
761
- outputTokens: 0
762
- },
763
- error: error,
764
- tools: availableTools,
765
- captureImmediate: mergedOptions.posthogCaptureImmediate
766
- });
767
- throw enrichedError;
768
- }
729
+ const availableTools = extractAvailableToolCalls('vercel', params);
730
+ try {
731
+ const result = await model.doGenerate(params);
732
+ const modelId = mergedOptions.posthogModelOverride ?? (result.response?.modelId ? result.response.modelId : model.modelId);
733
+ const provider = mergedOptions.posthogProviderOverride ?? extractProvider(model);
734
+ const baseURL = ''; // cannot currently get baseURL from vercel
735
+ const content = mapVercelOutput(result.content);
736
+ const latency = (Date.now() - startTime) / 1000;
737
+ const providerMetadata = result.providerMetadata;
738
+ const additionalTokenValues = extractAdditionalTokenValues(providerMetadata);
739
+ const webSearchCount = extractWebSearchCount(providerMetadata, result.usage);
740
+
741
+ // V2 usage has simple numbers, V3 has objects with .total - normalize both
742
+ const usageObj = result.usage;
743
+ const usage = {
744
+ inputTokens: extractTokenCount(result.usage.inputTokens),
745
+ outputTokens: extractTokenCount(result.usage.outputTokens),
746
+ reasoningTokens: extractReasoningTokens(usageObj),
747
+ cacheReadInputTokens: extractCacheReadTokens(usageObj),
748
+ webSearchCount,
749
+ ...additionalTokenValues
750
+ };
751
+ adjustAnthropicV3CacheTokens(model, provider, usage);
752
+ await sendEventToPosthog({
753
+ client: phClient,
754
+ distinctId: mergedOptions.posthogDistinctId,
755
+ traceId: mergedOptions.posthogTraceId ?? uuid.v4(),
756
+ model: modelId,
757
+ provider: provider,
758
+ input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt),
759
+ output: content,
760
+ latency,
761
+ baseURL,
762
+ params: mergedParams,
763
+ httpStatus: 200,
764
+ usage,
765
+ tools: availableTools,
766
+ captureImmediate: mergedOptions.posthogCaptureImmediate
767
+ });
768
+ return result;
769
+ } catch (error) {
770
+ const modelId = model.modelId;
771
+ const enrichedError = await sendEventWithErrorToPosthog({
772
+ client: phClient,
773
+ distinctId: mergedOptions.posthogDistinctId,
774
+ traceId: mergedOptions.posthogTraceId ?? uuid.v4(),
775
+ model: modelId,
776
+ provider: model.provider,
777
+ input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt),
778
+ output: [],
779
+ latency: 0,
780
+ baseURL: '',
781
+ params: mergedParams,
782
+ usage: {
783
+ inputTokens: 0,
784
+ outputTokens: 0
785
+ },
786
+ error: error,
787
+ tools: availableTools,
788
+ captureImmediate: mergedOptions.posthogCaptureImmediate
789
+ });
790
+ throw enrichedError;
791
+ }
792
+ },
793
+ writable: true,
794
+ configurable: true,
795
+ enumerable: false
769
796
  },
770
- doStream: async params => {
771
- const startTime = Date.now();
772
- let generatedText = '';
773
- let reasoningText = '';
774
- let usage = {};
775
- let providerMetadata = undefined;
776
- const mergedParams = {
777
- ...mergedOptions,
778
- ...mapVercelParams(params)
779
- };
780
- const modelId = mergedOptions.posthogModelOverride ?? model.modelId;
781
- const provider = mergedOptions.posthogProviderOverride ?? extractProvider(model);
782
- const availableTools = extractAvailableToolCalls('vercel', params);
783
- const baseURL = ''; // cannot currently get baseURL from vercel
784
-
785
- // Map to track in-progress tool calls
786
- const toolCallsInProgress = new Map();
787
- try {
788
- const {
789
- stream,
790
- ...rest
791
- } = await model.doStream(params);
792
- const transformStream = new TransformStream({
793
- transform(chunk, controller) {
794
- // Handle streaming patterns - compatible with both V2 and V3
795
- if (chunk.type === 'text-delta') {
796
- generatedText += chunk.delta;
797
- }
798
- if (chunk.type === 'reasoning-delta') {
799
- reasoningText += chunk.delta;
800
- }
797
+ doStream: {
798
+ value: async params => {
799
+ const startTime = Date.now();
800
+ let generatedText = '';
801
+ let reasoningText = '';
802
+ let usage = {};
803
+ let providerMetadata = undefined;
804
+ const mergedParams = {
805
+ ...mergedOptions,
806
+ ...mapVercelParams(params)
807
+ };
808
+ const modelId = mergedOptions.posthogModelOverride ?? model.modelId;
809
+ const provider = mergedOptions.posthogProviderOverride ?? extractProvider(model);
810
+ const availableTools = extractAvailableToolCalls('vercel', params);
811
+ const baseURL = ''; // cannot currently get baseURL from vercel
801
812
 
802
- // Handle tool call chunks
803
- if (chunk.type === 'tool-input-start') {
804
- // Initialize a new tool call
805
- toolCallsInProgress.set(chunk.id, {
806
- toolCallId: chunk.id,
807
- toolName: chunk.toolName,
808
- input: ''
809
- });
810
- }
811
- if (chunk.type === 'tool-input-delta') {
812
- // Accumulate tool call arguments
813
- const toolCall = toolCallsInProgress.get(chunk.id);
814
- if (toolCall) {
815
- toolCall.input += chunk.delta;
813
+ // Map to track in-progress tool calls
814
+ const toolCallsInProgress = new Map();
815
+ try {
816
+ const {
817
+ stream,
818
+ ...rest
819
+ } = await model.doStream(params);
820
+ const transformStream = new TransformStream({
821
+ transform(chunk, controller) {
822
+ // Handle streaming patterns - compatible with both V2 and V3
823
+ if (chunk.type === 'text-delta') {
824
+ generatedText += chunk.delta;
825
+ }
826
+ if (chunk.type === 'reasoning-delta') {
827
+ reasoningText += chunk.delta;
816
828
  }
817
- }
818
- if (chunk.type === 'tool-input-end') {
819
- // Tool call is complete, keep it in the map for final processing
820
- }
821
- if (chunk.type === 'tool-call') {
822
- // Direct tool call chunk (complete tool call)
823
- toolCallsInProgress.set(chunk.toolCallId, {
824
- toolCallId: chunk.toolCallId,
825
- toolName: chunk.toolName,
826
- input: chunk.input
827
- });
828
- }
829
- if (chunk.type === 'finish') {
830
- providerMetadata = chunk.providerMetadata;
831
- const additionalTokenValues = extractAdditionalTokenValues(providerMetadata);
832
- const chunkUsage = chunk.usage || {};
833
- usage = {
834
- inputTokens: extractTokenCount(chunk.usage?.inputTokens),
835
- outputTokens: extractTokenCount(chunk.usage?.outputTokens),
836
- reasoningTokens: extractReasoningTokens(chunkUsage),
837
- cacheReadInputTokens: extractCacheReadTokens(chunkUsage),
838
- ...additionalTokenValues
839
- };
840
- }
841
- controller.enqueue(chunk);
842
- },
843
- flush: async () => {
844
- const latency = (Date.now() - startTime) / 1000;
845
- // Build content array similar to mapVercelOutput structure
846
- const content = [];
847
- if (reasoningText) {
848
- content.push({
849
- type: 'reasoning',
850
- text: truncate(reasoningText)
851
- });
852
- }
853
- if (generatedText) {
854
- content.push({
855
- type: 'text',
856
- text: truncate(generatedText)
857
- });
858
- }
859
829
 
860
- // Add completed tool calls to content
861
- for (const toolCall of toolCallsInProgress.values()) {
862
- if (toolCall.toolName) {
830
+ // Handle tool call chunks
831
+ if (chunk.type === 'tool-input-start') {
832
+ // Initialize a new tool call
833
+ toolCallsInProgress.set(chunk.id, {
834
+ toolCallId: chunk.id,
835
+ toolName: chunk.toolName,
836
+ input: ''
837
+ });
838
+ }
839
+ if (chunk.type === 'tool-input-delta') {
840
+ // Accumulate tool call arguments
841
+ const toolCall = toolCallsInProgress.get(chunk.id);
842
+ if (toolCall) {
843
+ toolCall.input += chunk.delta;
844
+ }
845
+ }
846
+ if (chunk.type === 'tool-input-end') {
847
+ // Tool call is complete, keep it in the map for final processing
848
+ }
849
+ if (chunk.type === 'tool-call') {
850
+ // Direct tool call chunk (complete tool call)
851
+ toolCallsInProgress.set(chunk.toolCallId, {
852
+ toolCallId: chunk.toolCallId,
853
+ toolName: chunk.toolName,
854
+ input: chunk.input
855
+ });
856
+ }
857
+ if (chunk.type === 'finish') {
858
+ providerMetadata = chunk.providerMetadata;
859
+ const additionalTokenValues = extractAdditionalTokenValues(providerMetadata);
860
+ const chunkUsage = chunk.usage || {};
861
+ usage = {
862
+ inputTokens: extractTokenCount(chunk.usage?.inputTokens),
863
+ outputTokens: extractTokenCount(chunk.usage?.outputTokens),
864
+ reasoningTokens: extractReasoningTokens(chunkUsage),
865
+ cacheReadInputTokens: extractCacheReadTokens(chunkUsage),
866
+ ...additionalTokenValues
867
+ };
868
+ }
869
+ controller.enqueue(chunk);
870
+ },
871
+ flush: async () => {
872
+ const latency = (Date.now() - startTime) / 1000;
873
+ // Build content array similar to mapVercelOutput structure
874
+ const content = [];
875
+ if (reasoningText) {
863
876
  content.push({
864
- type: 'tool-call',
865
- id: toolCall.toolCallId,
866
- function: {
867
- name: toolCall.toolName,
868
- arguments: toolCall.input
869
- }
877
+ type: 'reasoning',
878
+ text: truncate(reasoningText)
879
+ });
880
+ }
881
+ if (generatedText) {
882
+ content.push({
883
+ type: 'text',
884
+ text: truncate(generatedText)
870
885
  });
871
886
  }
872
- }
873
887
 
874
- // Structure output like mapVercelOutput does
875
- const output = content.length > 0 ? [{
876
- role: 'assistant',
877
- content: content.length === 1 && content[0].type === 'text' ? content[0].text : content
878
- }] : [];
879
- const webSearchCount = extractWebSearchCount(providerMetadata, usage);
880
-
881
- // Update usage with web search count
882
- const finalUsage = {
883
- ...usage,
884
- webSearchCount
885
- };
886
- await sendEventToPosthog({
887
- client: phClient,
888
- distinctId: mergedOptions.posthogDistinctId,
889
- traceId: mergedOptions.posthogTraceId ?? uuid.v4(),
890
- model: modelId,
891
- provider: provider,
892
- input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt),
893
- output: output,
894
- latency,
895
- baseURL,
896
- params: mergedParams,
897
- httpStatus: 200,
898
- usage: finalUsage,
899
- tools: availableTools,
900
- captureImmediate: mergedOptions.posthogCaptureImmediate
901
- });
902
- }
903
- });
904
- return {
905
- stream: stream.pipeThrough(transformStream),
906
- ...rest
907
- };
908
- } catch (error) {
909
- const enrichedError = await sendEventWithErrorToPosthog({
910
- client: phClient,
911
- distinctId: mergedOptions.posthogDistinctId,
912
- traceId: mergedOptions.posthogTraceId ?? uuid.v4(),
913
- model: modelId,
914
- provider: provider,
915
- input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt),
916
- output: [],
917
- latency: 0,
918
- baseURL: '',
919
- params: mergedParams,
920
- usage: {
921
- inputTokens: 0,
922
- outputTokens: 0
923
- },
924
- error: error,
925
- tools: availableTools,
926
- captureImmediate: mergedOptions.posthogCaptureImmediate
927
- });
928
- throw enrichedError;
929
- }
888
+ // Add completed tool calls to content
889
+ for (const toolCall of toolCallsInProgress.values()) {
890
+ if (toolCall.toolName) {
891
+ content.push({
892
+ type: 'tool-call',
893
+ id: toolCall.toolCallId,
894
+ function: {
895
+ name: toolCall.toolName,
896
+ arguments: toolCall.input
897
+ }
898
+ });
899
+ }
900
+ }
901
+
902
+ // Structure output like mapVercelOutput does
903
+ const output = content.length > 0 ? [{
904
+ role: 'assistant',
905
+ content: content.length === 1 && content[0].type === 'text' ? content[0].text : content
906
+ }] : [];
907
+ const webSearchCount = extractWebSearchCount(providerMetadata, usage);
908
+
909
+ // Update usage with web search count
910
+ const finalUsage = {
911
+ ...usage,
912
+ webSearchCount
913
+ };
914
+ adjustAnthropicV3CacheTokens(model, provider, finalUsage);
915
+ await sendEventToPosthog({
916
+ client: phClient,
917
+ distinctId: mergedOptions.posthogDistinctId,
918
+ traceId: mergedOptions.posthogTraceId ?? uuid.v4(),
919
+ model: modelId,
920
+ provider: provider,
921
+ input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt),
922
+ output: output,
923
+ latency,
924
+ baseURL,
925
+ params: mergedParams,
926
+ httpStatus: 200,
927
+ usage: finalUsage,
928
+ tools: availableTools,
929
+ captureImmediate: mergedOptions.posthogCaptureImmediate
930
+ });
931
+ }
932
+ });
933
+ return {
934
+ stream: stream.pipeThrough(transformStream),
935
+ ...rest
936
+ };
937
+ } catch (error) {
938
+ const enrichedError = await sendEventWithErrorToPosthog({
939
+ client: phClient,
940
+ distinctId: mergedOptions.posthogDistinctId,
941
+ traceId: mergedOptions.posthogTraceId ?? uuid.v4(),
942
+ model: modelId,
943
+ provider: provider,
944
+ input: mergedOptions.posthogPrivacyMode ? '' : mapVercelPrompt(params.prompt),
945
+ output: [],
946
+ latency: 0,
947
+ baseURL: '',
948
+ params: mergedParams,
949
+ usage: {
950
+ inputTokens: 0,
951
+ outputTokens: 0
952
+ },
953
+ error: error,
954
+ tools: availableTools,
955
+ captureImmediate: mergedOptions.posthogCaptureImmediate
956
+ });
957
+ throw enrichedError;
958
+ }
959
+ },
960
+ writable: true,
961
+ configurable: true,
962
+ enumerable: false
930
963
  }
931
- };
964
+ });
932
965
  return wrappedModel;
933
966
  };
934
967