Skip to content

Commit e433db0

Browse files
committed
fix: Emit $ai_trace events for agent spans instead of skipping
Agent spans now create $ai_trace events so that child spans (tools, etc.) have a valid parent reference. Previously agent spans were skipped, which caused orphaned $ai_parent_id references in tool spans.
1 parent b4bd909 commit e433db0

File tree

2 files changed

+20
-8
lines changed

2 files changed

+20
-8
lines changed

posthog/ai/otel/exporter.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,10 +231,17 @@ def _span_to_event(self, span: ReadableSpan) -> Optional[Dict[str, Any]]:
231231
error_message,
232232
)
233233

234-
# Agent run span → skip (PostHog UI auto-creates trace wrapper from generation events)
235-
# The $ai_trace_id on generation events is sufficient for grouping
234+
# Agent run span → $ai_trace
236235
if self._is_agent_span(span_name, attrs):
237-
return None # Don't emit separate $ai_trace events
236+
return self._create_trace_event(
237+
span,
238+
attrs,
239+
trace_id,
240+
span_id,
241+
latency,
242+
is_error,
243+
error_message,
244+
)
238245

239246
# Tool execution span → $ai_span
240247
if self._is_tool_span(span_name, attrs):

posthog/test/ai/otel/test_exporter.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -234,9 +234,9 @@ def test_generation_event_with_model_parameters(self, mock_client):
234234

235235

236236
class TestAgentSpanHandling:
237-
"""Tests for agent span handling (should be skipped)."""
237+
"""Tests for agent span handling ($ai_trace events)."""
238238

239-
def test_agent_span_is_skipped(self, mock_client):
239+
def test_agent_span_creates_trace_event(self, mock_client):
240240
exporter = PostHogSpanExporter(mock_client, distinct_id="user_123")
241241

242242
span = create_mock_span(
@@ -245,16 +245,21 @@ def test_agent_span_is_skipped(self, mock_client):
245245

246246
exporter.export([span])
247247

248-
mock_client.capture.assert_not_called()
248+
mock_client.capture.assert_called_once()
249+
call_kwargs = mock_client.capture.call_args[1]
250+
assert call_kwargs["event"] == "$ai_trace"
251+
assert call_kwargs["properties"]["$ai_span_name"] == "TestAgent"
249252

250-
def test_invoke_agent_span_is_skipped(self, mock_client):
253+
def test_invoke_agent_span_creates_trace_event(self, mock_client):
251254
exporter = PostHogSpanExporter(mock_client, distinct_id="user_123")
252255

253256
span = create_mock_span(name="invoke_agent", attributes={})
254257

255258
exporter.export([span])
256259

257-
mock_client.capture.assert_not_called()
260+
mock_client.capture.assert_called_once()
261+
call_kwargs = mock_client.capture.call_args[1]
262+
assert call_kwargs["event"] == "$ai_trace"
258263

259264

260265
class TestToolSpanEventCreation:

0 commit comments

Comments
 (0)