@posthog/ai 8.6.0 → 8.6.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.
@@ -215,7 +215,7 @@ function sanitizeValues(obj) {
215
215
  return jsonSafe;
216
216
  }
217
217
 
218
- var version = "8.6.0";
218
+ var version = "8.6.2";
219
219
 
220
220
  const DEFAULT_MAX_DEPTH = 3;
221
221
  const MAX_STACK_LINES = 20;
@@ -306,6 +306,13 @@ const warnIfPostHogAiGateway = baseURL => {
306
306
  console.warn('[PostHog] The PostHog AI wrapper is pointed at the PostHog AI Gateway. ' + 'Both capture $ai_generation, so every call is double-counted and double-billed. ' + `Use one or the other — see ${GATEWAY_DOCS_URL}.`);
307
307
  };
308
308
 
309
+ // Mirror LangGraph's isGraphBubbleUp guard without adding LangGraph as a dependency. Every
310
+ // LangGraph control-flow exception (GraphInterrupt, NodeInterrupt, ParentCommand, GraphDrained,
311
+ // and future subclasses) exposes a prototype getter `is_bubble_up` that returns true, which the
312
+ // LangGraph runtime itself uses to distinguish control flow from real failures. The getter reads
313
+ // as undefined on ordinary Errors and works across duplicated LangGraph package copies.
314
+ const isLangGraphControlFlow = error => error.is_bubble_up === true;
315
+
309
316
  /** A run may either be a Span or a Generation */
310
317
 
311
318
  /** Storage for run metadata */
@@ -557,8 +564,20 @@ class LangChainCallbackHandler extends BaseCallbackHandler {
557
564
  eventProperties['$process_person_profile'] = false;
558
565
  }
559
566
  if (outputs instanceof Error) {
560
- eventProperties['$ai_error'] = stringifyError(outputs);
561
- eventProperties['$ai_is_error'] = true;
567
+ if (isLangGraphControlFlow(outputs)) {
568
+ // GraphInterrupt carries the pending interrupts (e.g. the question posed to a human).
569
+ // Surface them under the same `__interrupt__` key LangGraph hands back to the caller,
570
+ // so an interrupted span stays distinguishable from a node that returned nothing.
571
+ const interrupts = outputs.interrupts;
572
+ if (interrupts !== undefined) {
573
+ eventProperties['$ai_output_state'] = withPrivacyMode(this.client, this.privacyMode, sanitizeLangChain({
574
+ __interrupt__: interrupts
575
+ }));
576
+ }
577
+ } else {
578
+ eventProperties['$ai_error'] = stringifyError(outputs);
579
+ eventProperties['$ai_is_error'] = true;
580
+ }
562
581
  } else if (outputs !== undefined) {
563
582
  eventProperties['$ai_output_state'] = withPrivacyMode(this.client, this.privacyMode, sanitizeLangChain(outputs));
564
583
  }
@@ -783,27 +802,10 @@ class LangChainCallbackHandler extends BaseCallbackHandler {
783
802
  return undefined;
784
803
  }
785
804
  const gen = lastGeneration[0];
786
-
787
- // Check generationInfo for finish_reason (OpenAI format)
788
- if (gen.generationInfo?.finish_reason) {
789
- return String(gen.generationInfo.finish_reason);
790
- }
791
-
792
- // Check generationInfo for response_metadata.stop_reason (Anthropic format)
793
- if (gen.generationInfo?.response_metadata?.stop_reason) {
794
- return String(gen.generationInfo.response_metadata.stop_reason);
795
- }
796
-
797
- // Check message response_metadata for finish_reason (common LangChain format)
798
- if (gen.generationInfo?.response_metadata?.finish_reason) {
799
- return String(gen.generationInfo.response_metadata.finish_reason);
800
- }
801
-
802
- // Check for stop_reason directly in generationInfo
803
- if (gen.generationInfo?.stop_reason) {
804
- return String(gen.generationInfo.stop_reason);
805
- }
806
- return undefined;
805
+ const messageResponseMetadata = gen.message?.response_metadata;
806
+ const generationResponseMetadata = gen.generationInfo?.response_metadata;
807
+ const stopReason = messageResponseMetadata?.finish_reason || messageResponseMetadata?.stop_reason || gen.generationInfo?.finish_reason || generationResponseMetadata?.stop_reason || generationResponseMetadata?.finish_reason || gen.generationInfo?.stop_reason;
808
+ return stopReason != null ? String(stopReason) : undefined;
807
809
  }
808
810
  _parseUsageModel(usage, provider, model) {
809
811
  const conversionList = [['promptTokens', 'input'], ['completionTokens', 'output'], ['input_tokens', 'input'], ['output_tokens', 'output'], ['prompt_token_count', 'input'], ['candidates_token_count', 'output'], ['inputTokenCount', 'input'], ['outputTokenCount', 'output'], ['input_token_count', 'input'], ['generated_token_count', 'output']];
@@ -921,14 +923,11 @@ class LangChainCallbackHandler extends BaseCallbackHandler {
921
923
  if (llmUsage[0] === 0 && llmUsage[1] === 0 && response.generations) {
922
924
  for (const generation of response.generations) {
923
925
  for (const genChunk of generation) {
924
- // Check other paths for usage information
925
- if (genChunk.generationInfo?.usage_metadata) {
926
- llmUsage = this._parseUsageModel(genChunk.generationInfo.usage_metadata, provider, model);
927
- return llmUsage;
928
- }
929
- const messageChunk = genChunk.generationInfo ?? {};
930
- const responseMetadata = messageChunk.response_metadata ?? {};
931
- const chunkUsage = responseMetadata['usage'] ?? responseMetadata['amazon-bedrock-invocationMetrics'] ?? messageChunk.usage_metadata;
926
+ const message = genChunk.message ?? {};
927
+ const messageResponseMetadata = message.response_metadata ?? {};
928
+ const generationInfo = genChunk.generationInfo ?? {};
929
+ const generationResponseMetadata = generationInfo.response_metadata ?? {};
930
+ const chunkUsage = message.usage_metadata ?? messageResponseMetadata['usage'] ?? messageResponseMetadata['amazon-bedrock-invocationMetrics'] ?? generationInfo.usage_metadata ?? generationResponseMetadata['usage'] ?? generationResponseMetadata['amazon-bedrock-invocationMetrics'];
932
931
  if (chunkUsage) {
933
932
  llmUsage = this._parseUsageModel(chunkUsage, provider, model);
934
933
  return llmUsage;