@posthog/ai 8.6.1 → 8.6.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.
@@ -1,6 +1,6 @@
1
1
  import { GoogleGenAI } from '@google/genai';
2
2
  import { v4 } from 'uuid';
3
- import { uuidv7 } from '@posthog/core';
3
+ import { toJsonSafeValue, uuidv7 } from '@posthog/core';
4
4
 
5
5
  // Type guards for safer type checking
6
6
 
@@ -380,7 +380,7 @@ function addDefaults(params) {
380
380
  };
381
381
  }
382
382
 
383
- var version = "8.6.1";
383
+ var version = "8.6.3";
384
384
 
385
385
  const DEFAULT_MAX_DEPTH = 3;
386
386
  const MAX_STACK_LINES = 20;
@@ -491,120 +491,131 @@ const warnIfPostHogAiGateway = baseURL => {
491
491
  * so callers can re-throw the original error reference safely.
492
492
  */
493
493
  const captureAiGeneration = async (client, options) => {
494
- if (!client.capture) {
495
- return;
496
- }
497
- warnIfPostHogAiGateway(options.baseURL);
498
- const traceId = options.traceId ?? v4();
499
- const eventType = options.eventType ?? AIEvent.Generation;
500
- const privacyMode = options.privacyMode ?? false;
501
- const usage = options.usage ?? {};
502
- const safeInput = sanitizeValues(options.input);
503
- const safeOutput = sanitizeValues(options.output);
504
- let httpStatus = options.httpStatus;
505
- let errorData = {};
506
- if (options.error) {
507
- if (httpStatus === undefined) {
508
- if (typeof options.error === 'object' && 'status' in options.error && typeof options.error.status === 'number') {
509
- httpStatus = options.error.status;
510
- } else {
511
- httpStatus = 500;
512
- }
494
+ try {
495
+ if (!client.capture) {
496
+ return;
513
497
  }
514
- let exceptionId;
515
- if (client.options?.enableExceptionAutocapture) {
516
- exceptionId = uuidv7();
517
- client.captureException(options.error, undefined, {
518
- $ai_trace_id: traceId
519
- }, exceptionId);
520
- if (typeof options.error === 'object') {
521
- options.error.__posthog_previously_captured_error = true;
498
+ warnIfPostHogAiGateway(options.baseURL);
499
+ const traceId = options.traceId ?? v4();
500
+ const eventType = options.eventType ?? AIEvent.Generation;
501
+ const privacyMode = options.privacyMode ?? false;
502
+ const usage = options.usage ?? {};
503
+
504
+ // Check privacy before reading or traversing input/output. Besides avoiding
505
+ // needless work, this ensures hostile getters/proxies cannot observe a value
506
+ // that the caller explicitly requested us to redact.
507
+ const shouldRedact = withPrivacyMode(client, privacyMode, false) === null;
508
+ const safeInput = shouldRedact ? null : toJsonSafeValue(options.input);
509
+ const safeOutput = shouldRedact ? null : toJsonSafeValue(options.output);
510
+ let httpStatus = options.httpStatus;
511
+ let errorData = {};
512
+ if (options.error) {
513
+ if (httpStatus === undefined) {
514
+ if (typeof options.error === 'object' && 'status' in options.error && typeof options.error.status === 'number') {
515
+ httpStatus = options.error.status;
516
+ } else {
517
+ httpStatus = 500;
518
+ }
519
+ }
520
+ let exceptionId;
521
+ if (client.options?.enableExceptionAutocapture) {
522
+ exceptionId = uuidv7();
523
+ client.captureException(options.error, undefined, {
524
+ $ai_trace_id: traceId
525
+ }, exceptionId);
526
+ if (typeof options.error === 'object') {
527
+ ;
528
+ options.error.__posthog_previously_captured_error = true;
529
+ }
522
530
  }
531
+ errorData = {
532
+ $ai_is_error: true,
533
+ $ai_error: stringifyError(options.error),
534
+ $exception_event_id: exceptionId
535
+ };
523
536
  }
524
- errorData = {
525
- $ai_is_error: true,
526
- $ai_error: stringifyError(options.error),
527
- $exception_event_id: exceptionId
537
+ httpStatus = httpStatus ?? 200;
538
+ let costOverrideData = {};
539
+ if (options.costOverride) {
540
+ const inputCostUSD = (options.costOverride.inputCost ?? 0) * (usage.inputTokens ?? 0);
541
+ const outputCostUSD = (options.costOverride.outputCost ?? 0) * (usage.outputTokens ?? 0);
542
+ costOverrideData = {
543
+ $ai_input_cost_usd: inputCostUSD,
544
+ $ai_output_cost_usd: outputCostUSD,
545
+ $ai_total_cost_usd: inputCostUSD + outputCostUSD
546
+ };
547
+ }
548
+ const additionalTokenValues = {
549
+ ...(usage.reasoningTokens ? {
550
+ $ai_reasoning_tokens: usage.reasoningTokens
551
+ } : {}),
552
+ ...(usage.cacheReadInputTokens ? {
553
+ $ai_cache_read_input_tokens: usage.cacheReadInputTokens
554
+ } : {}),
555
+ ...(usage.cacheCreationInputTokens ? {
556
+ $ai_cache_creation_input_tokens: usage.cacheCreationInputTokens
557
+ } : {}),
558
+ ...(usage.webSearchCount ? {
559
+ $ai_web_search_count: usage.webSearchCount
560
+ } : {}),
561
+ ...(usage.rawUsage ? {
562
+ $ai_usage: usage.rawUsage
563
+ } : {})
528
564
  };
529
- }
530
- httpStatus = httpStatus ?? 200;
531
- let costOverrideData = {};
532
- if (options.costOverride) {
533
- const inputCostUSD = (options.costOverride.inputCost ?? 0) * (usage.inputTokens ?? 0);
534
- const outputCostUSD = (options.costOverride.outputCost ?? 0) * (usage.outputTokens ?? 0);
535
- costOverrideData = {
536
- $ai_input_cost_usd: inputCostUSD,
537
- $ai_output_cost_usd: outputCostUSD,
538
- $ai_total_cost_usd: inputCostUSD + outputCostUSD
565
+ const properties = {
566
+ $ai_lib: 'posthog-ai',
567
+ $ai_lib_version: version,
568
+ $ai_provider: options.providerOverride ?? options.provider,
569
+ $ai_model: options.modelOverride ?? options.model,
570
+ $ai_model_parameters: options.modelParameters ?? {},
571
+ $ai_input: safeInput,
572
+ $ai_output_choices: safeOutput,
573
+ $ai_http_status: httpStatus,
574
+ $ai_input_tokens: usage.inputTokens ?? 0,
575
+ ...(usage.outputTokens !== undefined ? {
576
+ $ai_output_tokens: usage.outputTokens
577
+ } : {}),
578
+ ...additionalTokenValues,
579
+ $ai_latency: options.latency ?? 0,
580
+ ...(options.timeToFirstToken !== undefined ? {
581
+ $ai_time_to_first_token: options.timeToFirstToken
582
+ } : {}),
583
+ $ai_trace_id: traceId,
584
+ $ai_base_url: options.baseURL ?? '',
585
+ ...options.properties,
586
+ $ai_tokens_source: getTokensSource(options.properties),
587
+ ...(options.distinctId ? {} : {
588
+ $process_person_profile: false
589
+ }),
590
+ ...(options.stopReason ? {
591
+ $ai_stop_reason: options.stopReason
592
+ } : {}),
593
+ ...(options.tools ? {
594
+ $ai_tools: options.tools
595
+ } : {}),
596
+ ...(options.completionId ? {
597
+ $ai_completion_id: options.completionId
598
+ } : {}),
599
+ ...(options.providerMetadata && Object.keys(options.providerMetadata).length > 0 ? {
600
+ $ai_provider_metadata: options.providerMetadata
601
+ } : {}),
602
+ ...errorData,
603
+ ...costOverrideData
539
604
  };
540
- }
541
- const additionalTokenValues = {
542
- ...(usage.reasoningTokens ? {
543
- $ai_reasoning_tokens: usage.reasoningTokens
544
- } : {}),
545
- ...(usage.cacheReadInputTokens ? {
546
- $ai_cache_read_input_tokens: usage.cacheReadInputTokens
547
- } : {}),
548
- ...(usage.cacheCreationInputTokens ? {
549
- $ai_cache_creation_input_tokens: usage.cacheCreationInputTokens
550
- } : {}),
551
- ...(usage.webSearchCount ? {
552
- $ai_web_search_count: usage.webSearchCount
553
- } : {}),
554
- ...(usage.rawUsage ? {
555
- $ai_usage: usage.rawUsage
556
- } : {})
557
- };
558
- const properties = {
559
- $ai_lib: 'posthog-ai',
560
- $ai_lib_version: version,
561
- $ai_provider: options.providerOverride ?? options.provider,
562
- $ai_model: options.modelOverride ?? options.model,
563
- $ai_model_parameters: options.modelParameters ?? {},
564
- $ai_input: withPrivacyMode(client, privacyMode, safeInput),
565
- $ai_output_choices: withPrivacyMode(client, privacyMode, safeOutput),
566
- $ai_http_status: httpStatus,
567
- $ai_input_tokens: usage.inputTokens ?? 0,
568
- ...(usage.outputTokens !== undefined ? {
569
- $ai_output_tokens: usage.outputTokens
570
- } : {}),
571
- ...additionalTokenValues,
572
- $ai_latency: options.latency ?? 0,
573
- ...(options.timeToFirstToken !== undefined ? {
574
- $ai_time_to_first_token: options.timeToFirstToken
575
- } : {}),
576
- $ai_trace_id: traceId,
577
- $ai_base_url: options.baseURL ?? '',
578
- ...options.properties,
579
- $ai_tokens_source: getTokensSource(options.properties),
580
- ...(options.distinctId ? {} : {
581
- $process_person_profile: false
582
- }),
583
- ...(options.stopReason ? {
584
- $ai_stop_reason: options.stopReason
585
- } : {}),
586
- ...(options.tools ? {
587
- $ai_tools: options.tools
588
- } : {}),
589
- ...(options.completionId ? {
590
- $ai_completion_id: options.completionId
591
- } : {}),
592
- ...(options.providerMetadata && Object.keys(options.providerMetadata).length > 0 ? {
593
- $ai_provider_metadata: options.providerMetadata
594
- } : {}),
595
- ...errorData,
596
- ...costOverrideData
597
- };
598
- const event = {
599
- distinctId: options.distinctId ?? traceId,
600
- event: eventType,
601
- properties,
602
- groups: options.groups
603
- };
604
- if (options.captureImmediate) {
605
- await client.captureImmediate(event);
606
- } else {
607
- client.capture(event);
605
+ const event = {
606
+ distinctId: options.distinctId ?? traceId,
607
+ event: eventType,
608
+ properties,
609
+ groups: options.groups
610
+ };
611
+ if (options.captureImmediate) {
612
+ await client.captureImmediate(event);
613
+ } else {
614
+ client.capture(event);
615
+ }
616
+ } catch (error) {
617
+ // Telemetry failures must never affect the instrumented provider call.
618
+ console.warn('[PostHog AI] Failed to capture generation telemetry:', error);
608
619
  }
609
620
  };
610
621