Skip to content

spike: gen_ai.conversation_root — native + OTel ingest paths#89

Draft
adityamehra wants to merge 3 commits into
mainfrom
spike/gen-ai-conversation-root
Draft

spike: gen_ai.conversation_root — native + OTel ingest paths#89
adityamehra wants to merge 3 commits into
mainfrom
spike/gen-ai-conversation-root

Conversation

@adityamehra

Copy link
Copy Markdown
Member

Summary

  • OTel path (src/splunk_ao/otel.py): Added GEN_AI_CONVERSATION_ROOT = "gen_ai.conversation_root" constant and root-detection in start_splunk_ao_span(). Root status is sampled before entering the new span context, so a WorkflowSpan/AgentSpan with no valid OTel parent automatically receives gen_ai.conversation_root = True.
  • Native path (src/splunk_ao/schema/logged.py, src/splunk_ao/logger/logger.py): LoggedWorkflowSpan and LoggedAgentSpan gain a conversation_root: bool | None field. add_workflow_span() / add_agent_span() auto-detect the root by checking whether current_parent() is a LoggedTrace; if so, conversation_root=True is set on the span and mirrored into user_metadata["gen_ai.conversation_root"] as a bridge until the backend schema is updated.
  • Example (examples/agent/langchain-agent-http/): FastAPI HTTP wrapper around the LangChain agent example (mirrors PR #236 scenarios). Verified end-to-end against rc0.

Reference

  • splunk-otel-python-contrib PR #236opentelemetry-util-genai reference implementation
  • Root detection heuristic: a Workflow/AgentInvocation span is the conversation root if it has no immediate GenAI parent (OTel: no valid parent SpanContext; native: parent is LoggedTrace)

What this spike establishes

Path Detection Attribute written
OTel (start_splunk_ao_span) No valid OTel parent before entering span gen_ai.conversation_root = True (OTel attribute)
Native (add_workflow_span) current_parent() is LoggedTrace LoggedWorkflowSpan.conversation_root = True + user_metadata["gen_ai.conversation_root"] = "true"
Native (add_agent_span) current_parent() is LoggedTrace LoggedAgentSpan.conversation_root = True + user_metadata["gen_ai.conversation_root"] = "true"

Test plan

  • Confirm root WorkflowSpan (parent = LoggedTrace) → conversation_root=True ✓ (verified locally)
  • Confirm nested WorkflowSpan (parent = another span) → conversation_root=None ✓ (verified locally)
  • Confirm OTel root span → gen_ai.conversation_root=True attribute ✓ (verified via in-memory exporter)
  • Confirm OTel nested span → no attribute ✓ (verified via in-memory exporter)
  • End-to-end traces reach rc0 console with gen_ai.conversation_root in user_metadata ✓ (manual run against rc0)
  • Add unit tests for both paths before merging full implementation
  • Confirm backend can consume/index the user_metadata bridge value
  • Align with galileo-core schema update once backend schema ships the proper field

TODOs before full implementation

  • Unit tests (tests/) covering root vs nested detection for both paths
  • Handler verification (SplunkAOCallback, LangGraph, CrewAI, etc.) — each framework callback's first GenAI span should be auto-detected as root
  • Backend schema change to accept conversation_root as a first-class field (currently bridged via user_metadata)
  • Remove user_metadata bridge once backend schema is updated

Made with Cursor

adityamehra and others added 3 commits July 16, 2026 11:20
…t paths

Implements initial `gen_ai.conversation_root` detection for both the OTel
and native (SplunkAOLogger) span ingestion routes, mirroring the approach
from splunk-otel-python-contrib PR #236.

OTel path (src/splunk_ao/otel.py):
- Add GEN_AI_CONVERSATION_ROOT = "gen_ai.conversation_root" constant.
- In start_splunk_ao_span(), capture whether the caller has a valid OTel
  parent span *before* entering the new span context.  If no parent exists
  and the span is a WorkflowSpan or AgentSpan, set the attribute to True.

Native path (src/splunk_ao/schema/logged.py, src/splunk_ao/logger/logger.py):
- Add conversation_root: bool | None field to LoggedWorkflowSpan and
  LoggedAgentSpan for SDK-level introspection.
- In add_workflow_span() and add_agent_span(), auto-detect the root: if the
  current parent is a LoggedTrace (not another GenAI span), set
  conversation_root=True and mirror it into user_metadata["gen_ai.conversation_root"]
  so it reaches the backend without requiring a schema migration.

Example (examples/agent/langchain-agent-http/):
- FastAPI HTTP wrapper around the langchain-agent example.
- POST /invoke: single agent call (HTTP span → root WorkflowSpan).
- POST /invoke/nested: two sequential agent calls (each trace gets its own root).
- Verified end-to-end against rc0 (splunk-ao-rc0 env vars); both routes send
  traces with conversation_root correctly set.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant