@posthog/ai 8.9.0 → 8.9.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.
@@ -418,7 +418,7 @@ function addDefaults(params) {
418
418
  };
419
419
  }
420
420
 
421
- var version = "8.9.0";
421
+ var version = "8.9.2";
422
422
 
423
423
  const DEFAULT_MAX_DEPTH = 3;
424
424
  const MAX_STACK_LINES = 20;
@@ -575,15 +575,22 @@ const captureAiGeneration = async (client, options) => {
575
575
  };
576
576
  }
577
577
  httpStatus = httpStatus ?? 200;
578
- let costOverrideData = {};
578
+
579
+ // A configured price applies only to a count the provider reported, so a call with no
580
+ // reported usage sends no cost instead of asserting $0. $ai_total_cost_usd sums the sides
581
+ // that were priced, which makes it the cost of the known side alone when the other side
582
+ // went unreported: a lower bound on the true total, not an assertion of it.
583
+ const costOverrideData = {};
579
584
  if (options.costOverride) {
580
- const inputCostUSD = (options.costOverride.inputCost ?? 0) * (usage.inputTokens ?? 0);
581
- const outputCostUSD = (options.costOverride.outputCost ?? 0) * (usage.outputTokens ?? 0);
582
- costOverrideData = {
583
- $ai_input_cost_usd: inputCostUSD,
584
- $ai_output_cost_usd: outputCostUSD,
585
- $ai_total_cost_usd: inputCostUSD + outputCostUSD
586
- };
585
+ if (usage.inputTokens !== undefined) {
586
+ costOverrideData.$ai_input_cost_usd = (options.costOverride.inputCost ?? 0) * usage.inputTokens;
587
+ }
588
+ if (usage.outputTokens !== undefined) {
589
+ costOverrideData.$ai_output_cost_usd = (options.costOverride.outputCost ?? 0) * usage.outputTokens;
590
+ }
591
+ if (Object.keys(costOverrideData).length > 0) {
592
+ costOverrideData.$ai_total_cost_usd = (costOverrideData.$ai_input_cost_usd ?? 0) + (costOverrideData.$ai_output_cost_usd ?? 0);
593
+ }
587
594
  }
588
595
 
589
596
  // The caller's own token counts override the SDK-derived ones further down, via the
@@ -627,7 +634,9 @@ const captureAiGeneration = async (client, options) => {
627
634
  $ai_input: safeInput,
628
635
  $ai_output_choices: safeOutput,
629
636
  $ai_http_status: httpStatus,
630
- $ai_input_tokens: usage.inputTokens ?? 0,
637
+ ...(usage.inputTokens !== undefined ? {
638
+ $ai_input_tokens: usage.inputTokens
639
+ } : {}),
631
640
  ...(usage.outputTokens !== undefined ? {
632
641
  $ai_output_tokens: usage.outputTokens
633
642
  } : {}),
@@ -745,10 +754,7 @@ class WrappedModels {
745
754
  latency,
746
755
  baseURL: 'https://generativelanguage.googleapis.com',
747
756
  modelParameters: getModelParams(params),
748
- usage: {
749
- inputTokens: 0,
750
- outputTokens: 0
751
- },
757
+ usage: {},
752
758
  error
753
759
  });
754
760
  throw error;
@@ -764,11 +770,10 @@ class WrappedModels {
764
770
  let firstTokenTime;
765
771
  let stopReason;
766
772
  let usage = {
767
- inputTokens: 0,
768
- outputTokens: 0,
769
773
  webSearchCount: 0,
770
774
  rawUsage: undefined
771
775
  };
776
+ let errored = false;
772
777
  try {
773
778
  const stream = await this.client.models.generateContentStream(geminiParams);
774
779
  for await (const chunk of stream) {
@@ -851,35 +856,8 @@ class WrappedModels {
851
856
  }
852
857
  yield chunk;
853
858
  }
854
- const latency = (Date.now() - startTime) / 1000;
855
- const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
856
- const availableTools = extractAvailableToolCalls('gemini', geminiParams);
857
-
858
- // Format output similar to formatResponseGemini
859
- const output = accumulatedContent.length > 0 ? [{
860
- role: 'assistant',
861
- content: accumulatedContent
862
- }] : [];
863
- await captureAiGeneration(this.phClient, {
864
- ...posthogParams,
865
- model: geminiParams.model,
866
- provider: 'gemini',
867
- input: this.formatInputForPostHog(geminiParams),
868
- output,
869
- latency,
870
- timeToFirstToken,
871
- baseURL: 'https://generativelanguage.googleapis.com',
872
- modelParameters: getModelParams(params),
873
- httpStatus: 200,
874
- usage: {
875
- ...usage,
876
- webSearchCount: usage.webSearchCount,
877
- rawUsage: usage.rawUsage
878
- },
879
- stopReason,
880
- tools: availableTools
881
- });
882
859
  } catch (error) {
860
+ errored = true;
883
861
  const latency = (Date.now() - startTime) / 1000;
884
862
  await captureAiGeneration(this.phClient, {
885
863
  ...posthogParams,
@@ -890,13 +868,44 @@ class WrappedModels {
890
868
  latency,
891
869
  baseURL: 'https://generativelanguage.googleapis.com',
892
870
  modelParameters: getModelParams(params),
893
- usage: {
894
- inputTokens: 0,
895
- outputTokens: 0
896
- },
871
+ usage,
897
872
  error
898
873
  });
899
874
  throw error;
875
+ } finally {
876
+ // A consumer that stops iterating resumes the pending yield as a return,
877
+ // skipping both the loop tail and the catch. Only a finally runs then, so
878
+ // the success capture lives here to cover completion and cancellation.
879
+ if (!errored) {
880
+ const latency = (Date.now() - startTime) / 1000;
881
+ const timeToFirstToken = firstTokenTime !== undefined ? (firstTokenTime - startTime) / 1000 : undefined;
882
+ const availableTools = extractAvailableToolCalls('gemini', geminiParams);
883
+
884
+ // Format output similar to formatResponseGemini
885
+ const output = accumulatedContent.length > 0 ? [{
886
+ role: 'assistant',
887
+ content: accumulatedContent
888
+ }] : [];
889
+ await captureAiGeneration(this.phClient, {
890
+ ...posthogParams,
891
+ model: geminiParams.model,
892
+ provider: 'gemini',
893
+ input: this.formatInputForPostHog(geminiParams),
894
+ output,
895
+ latency,
896
+ timeToFirstToken,
897
+ baseURL: 'https://generativelanguage.googleapis.com',
898
+ modelParameters: getModelParams(params),
899
+ httpStatus: 200,
900
+ usage: {
901
+ ...usage,
902
+ webSearchCount: usage.webSearchCount,
903
+ rawUsage: usage.rawUsage
904
+ },
905
+ stopReason,
906
+ tools: availableTools
907
+ });
908
+ }
900
909
  }
901
910
  }
902
911
  async embedContent(params) {
@@ -937,9 +946,7 @@ class WrappedModels {
937
946
  latency,
938
947
  baseURL: 'https://generativelanguage.googleapis.com',
939
948
  modelParameters: getModelParams(params),
940
- usage: {
941
- inputTokens: 0
942
- },
949
+ usage: {},
943
950
  error
944
951
  });
945
952
  throw error;