Skip to content

Commit ed8a8d4

Browse files
committed
feat!: AI wrappers and MCP send personless as a per-event option
1 parent 4120a8b commit ed8a8d4

22 files changed

Lines changed: 221 additions & 120 deletions

‎posthog/ai/claude_agent_sdk/client.py‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,14 +242,12 @@ async def disconnect(self) -> None:
242242
**self._extra_props,
243243
}
244244

245-
if resolved_id is None:
246-
properties["$process_person_profile"] = False
247-
248245
self._processor._capture_event(
249246
"$ai_trace",
250247
properties,
251248
resolved_id or self._trace_id,
252249
self._groups,
250+
personless=resolved_id is None,
253251
)
254252

255253
try:

‎posthog/ai/claude_agent_sdk/processor.py‎

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ def _capture_event(
192192
properties: Dict[str, Any],
193193
distinct_id: Optional[str] = None,
194194
groups: Optional[Dict[str, Any]] = None,
195+
personless: bool = False,
195196
) -> None:
196197
_capture_processor_event(
197198
self._client,
@@ -200,6 +201,7 @@ def _capture_event(
200201
default_properties=self._properties,
201202
distinct_id=distinct_id,
202203
groups=groups if groups is not None else self._groups,
204+
personless=personless,
203205
)
204206

205207
async def query(
@@ -449,11 +451,12 @@ def _emit_generation(
449451
if gen.stop_reason is not None:
450452
properties["$ai_stop_reason"] = gen.stop_reason
451453

452-
if resolved_id is None:
453-
properties["$process_person_profile"] = False
454-
455454
self._capture_event(
456-
"$ai_generation", properties, resolved_id or trace_id, groups
455+
"$ai_generation",
456+
properties,
457+
resolved_id or trace_id,
458+
groups,
459+
personless=resolved_id is None,
457460
)
458461

459462
def _emit_generation_from_result(
@@ -522,11 +525,12 @@ def _emit_generation_from_result(
522525
if result.total_cost_usd is not None:
523526
properties["$ai_total_cost_usd"] = result.total_cost_usd
524527

525-
if resolved_id is None:
526-
properties["$process_person_profile"] = False
527-
528528
self._capture_event(
529-
"$ai_generation", properties, resolved_id or trace_id, groups
529+
"$ai_generation",
530+
properties,
531+
resolved_id or trace_id,
532+
groups,
533+
personless=resolved_id is None,
530534
)
531535

532536
def _emit_tool_span(
@@ -557,10 +561,13 @@ def _emit_tool_span(
557561
_ensure_serializable(block.input), self._client
558562
)
559563

560-
if resolved_id is None:
561-
properties["$process_person_profile"] = False
562-
563-
self._capture_event("$ai_span", properties, resolved_id or trace_id, groups)
564+
self._capture_event(
565+
"$ai_span",
566+
properties,
567+
resolved_id or trace_id,
568+
groups,
569+
personless=resolved_id is None,
570+
)
564571

565572
def _emit_trace(
566573
self,
@@ -592,10 +599,13 @@ def _emit_trace(
592599
if result.total_cost_usd is not None:
593600
properties["$ai_total_cost_usd"] = result.total_cost_usd
594601

595-
if resolved_id is None:
596-
properties["$process_person_profile"] = False
597-
598-
self._capture_event("$ai_trace", properties, resolved_id or trace_id, groups)
602+
self._capture_event(
603+
"$ai_trace",
604+
properties,
605+
resolved_id or trace_id,
606+
groups,
607+
personless=resolved_id is None,
608+
)
599609

600610
# Flush to ensure events are sent before process exits
601611
try:

‎posthog/ai/gemini/_shared.py‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -266,13 +266,11 @@ def _capture_embedding_outcome(
266266
event_properties["$ai_is_error"] = True
267267
event_properties["$ai_error"] = str(error)
268268

269-
if distinct_id is None:
270-
event_properties["$process_person_profile"] = False
271-
272269
_capture_ai_event(
273270
self._ph_client,
274271
"$ai_embedding",
275272
distinct_id=distinct_id or trace_id,
276273
properties=event_properties,
277274
groups=groups,
275+
personless=distinct_id is None,
278276
)

‎posthog/ai/langchain/callbacks.py‎

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -577,15 +577,13 @@ def _capture_trace_or_span(
577577
finalize_ai_content(outputs, self._ph_client),
578578
)
579579

580-
if self._distinct_id is None:
581-
event_properties["$process_person_profile"] = False
582-
583580
_capture_ai_event(
584581
self._ph_client,
585582
event_name,
586583
distinct_id=self._distinct_id or run_id,
587584
properties=event_properties,
588585
groups=self._groups,
586+
personless=self._distinct_id is None,
589587
)
590588

591589
def _pop_run_and_capture_generation(
@@ -662,9 +660,6 @@ def _capture_generation(
662660
if self._properties:
663661
event_properties.update(self._properties)
664662

665-
if self._distinct_id is None:
666-
event_properties["$process_person_profile"] = False
667-
668663
if isinstance(output, BaseException):
669664
event_properties["$ai_http_status"] = _get_http_status(output)
670665
event_properties["$ai_error"] = _stringify_exception(output)
@@ -737,6 +732,7 @@ def _capture_generation(
737732
distinct_id=self._distinct_id or trace_id,
738733
properties=event_properties,
739734
groups=self._groups,
735+
personless=self._distinct_id is None,
740736
)
741737

742738
def _log_debug_event(

‎posthog/ai/openai/_embeddings.py‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,12 @@ def _capture_embedding_event(
3838
**(properties or {}),
3939
}
4040

41-
if distinct_id is None:
42-
event_properties["$process_person_profile"] = False
43-
4441
if hasattr(posthog_client, "capture"):
4542
_capture_ai_event(
4643
posthog_client,
4744
"$ai_embedding",
4845
distinct_id=distinct_id or trace_id,
4946
properties=event_properties,
5047
groups=groups,
48+
personless=distinct_id is None,
5149
)

‎posthog/ai/openai_agents/processor.py‎

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -159,14 +159,18 @@ def _capture_event(
159159
properties: Dict[str, Any],
160160
distinct_id: Optional[str] = None,
161161
) -> None:
162-
"""Capture an event without allowing telemetry failures to escape."""
162+
"""Capture an event without allowing telemetry failures to escape.
163+
164+
Without a distinct ID, the event is personless and uses its trace ID.
165+
"""
163166
_capture_processor_event(
164167
self._client,
165168
event,
166169
properties,
167170
default_properties=self._properties,
168-
distinct_id=distinct_id,
171+
distinct_id=distinct_id or properties.get("$ai_trace_id"),
169172
groups=self._groups,
173+
personless=distinct_id is None,
170174
)
171175

172176
def on_trace_start(self, trace: Trace) -> None:
@@ -228,12 +232,9 @@ def on_trace_end(self, trace: Trace) -> None:
228232
if metadata:
229233
properties["$ai_trace_metadata"] = _ensure_serializable(metadata)
230234

231-
if distinct_id is None:
232-
properties["$process_person_profile"] = False
233-
234235
self._capture_event(
235236
event="$ai_trace",
236-
distinct_id=distinct_id or trace_id,
237+
distinct_id=distinct_id,
237238
properties=properties,
238239
)
239240
except Exception as e:
@@ -267,8 +268,8 @@ def on_span_end(self, span: Span[Any]) -> None:
267268
latency = (ended - started) if (started and ended) else 0
268269

269270
# Get user-provided distinct ID from trace metadata (resolved at trace start).
270-
# None means no user-provided ID — use trace_id as fallback in personless mode,
271-
# matching the langchain/openai pattern: `distinct_id or trace_id`.
271+
# None means no user-provided ID: _capture_event falls back to the
272+
# trace ID and makes the event personless.
272273
trace_info = self._trace_metadata.get(trace_id, {})
273274
distinct_id = trace_info.get("distinct_id") or self._get_distinct_id(None)
274275

@@ -317,11 +318,6 @@ def on_span_end(self, span: Span[Any]) -> None:
317318
"$ai_error_type": error_type,
318319
}
319320

320-
# Personless mode: no user-provided distinct_id, fallback to trace_id
321-
if distinct_id is None:
322-
error_properties["$process_person_profile"] = False
323-
distinct_id = trace_id
324-
325321
# Dispatch based on span data type
326322
if isinstance(span_data, GenerationSpanData):
327323
self._handle_generation_span(
@@ -471,7 +467,7 @@ def _handle_generation_span(
471467
span_id: str,
472468
parent_id: Optional[str],
473469
latency: float,
474-
distinct_id: str,
470+
distinct_id: Optional[str],
475471
group_id: Optional[str],
476472
error_properties: Dict[str, Any],
477473
) -> None:
@@ -554,7 +550,7 @@ def _handle_function_span(
554550
span_id: str,
555551
parent_id: Optional[str],
556552
latency: float,
557-
distinct_id: str,
553+
distinct_id: Optional[str],
558554
group_id: Optional[str],
559555
error_properties: Dict[str, Any],
560556
) -> None:
@@ -587,7 +583,7 @@ def _handle_agent_span(
587583
span_id: str,
588584
parent_id: Optional[str],
589585
latency: float,
590-
distinct_id: str,
586+
distinct_id: Optional[str],
591587
group_id: Optional[str],
592588
error_properties: Dict[str, Any],
593589
) -> None:
@@ -616,7 +612,7 @@ def _handle_handoff_span(
616612
span_id: str,
617613
parent_id: Optional[str],
618614
latency: float,
619-
distinct_id: str,
615+
distinct_id: Optional[str],
620616
group_id: Optional[str],
621617
error_properties: Dict[str, Any],
622618
) -> None:
@@ -640,7 +636,7 @@ def _handle_guardrail_span(
640636
span_id: str,
641637
parent_id: Optional[str],
642638
latency: float,
643-
distinct_id: str,
639+
distinct_id: Optional[str],
644640
group_id: Optional[str],
645641
error_properties: Dict[str, Any],
646642
) -> None:
@@ -663,7 +659,7 @@ def _handle_response_span(
663659
span_id: str,
664660
parent_id: Optional[str],
665661
latency: float,
666-
distinct_id: str,
662+
distinct_id: Optional[str],
667663
group_id: Optional[str],
668664
error_properties: Dict[str, Any],
669665
) -> None:
@@ -732,7 +728,7 @@ def _handle_custom_span(
732728
span_id: str,
733729
parent_id: Optional[str],
734730
latency: float,
735-
distinct_id: str,
731+
distinct_id: Optional[str],
736732
group_id: Optional[str],
737733
error_properties: Dict[str, Any],
738734
) -> None:
@@ -757,7 +753,7 @@ def _handle_audio_span(
757753
span_id: str,
758754
parent_id: Optional[str],
759755
latency: float,
760-
distinct_id: str,
756+
distinct_id: Optional[str],
761757
group_id: Optional[str],
762758
error_properties: Dict[str, Any],
763759
) -> None:
@@ -832,7 +828,7 @@ def _handle_mcp_span(
832828
span_id: str,
833829
parent_id: Optional[str],
834830
latency: float,
835-
distinct_id: str,
831+
distinct_id: Optional[str],
836832
group_id: Optional[str],
837833
error_properties: Dict[str, Any],
838834
) -> None:
@@ -856,7 +852,7 @@ def _handle_generic_span(
856852
span_id: str,
857853
parent_id: Optional[str],
858854
latency: float,
859-
distinct_id: str,
855+
distinct_id: Optional[str],
860856
group_id: Optional[str],
861857
error_properties: Dict[str, Any],
862858
) -> None:

0 commit comments

Comments
 (0)