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