Skip to content

Distributed Tracing#

End-to-end tracing for multi-agent workflows with automatic context propagation.

Start a New Trace#

from openintent import TracingContext

# Create a root tracing context
ctx = TracingContext.new_root()
print(f"Trace ID: {ctx.trace_id}")  # 128-bit hex

Log Events with Trace Context#

from openintent import OpenIntentClient

client = OpenIntentClient(
    base_url="http://localhost:8000",
    agent_id="coordinator"
)

# Log an event with tracing fields
event = client.log_event(
    intent_id="intent-123",
    event_type="tool_invocation",
    payload={
        "tool_name": "web_search",
        "arguments": {"query": "latest data"},
        "result": {"urls": ["..."]},
        "duration_ms": 342.5
    },
    trace_id=ctx.trace_id,
    parent_event_id=None,  # Root span
)

Propagate Context Through Tool Handlers#

from openintent import Agent, TracingContext, on_assignment, ToolDef

# Define a tool that accepts tracing context
async def delegate_to_researcher(query: str, tracing: TracingContext = None):
    """This tool delegates work to another agent, passing trace context."""
    researcher = ResearchAgent(tracing_context=tracing)
    return await researcher.run(query)

# Normal tools work without changes
def calculator(expression: str):
    return {"result": eval(expression)}

@Agent("coordinator", tools=[
    ToolDef(
        name="delegate_research",
        description="Delegate research to specialist",
        parameters={"query": {"type": "string"}},
        handler=delegate_to_researcher,
    ),
    ToolDef(
        name="calculate",
        description="Evaluate expression",
        parameters={"expression": {"type": "string"}},
        handler=calculator,
    ),
])
class CoordinatorAgent:
    @on_assignment
    async def handle(self, intent):
        return await self.think("Research and calculate results")

Query a Complete Trace#

# Retrieve all events across all intents sharing a trace ID
events = client.list_events(
    trace_id="4bf92f3577b34da6a3ce929d0e0e4736"
)

# Build the call tree
roots = [e for e in events if e.parent_event_id is None]

def print_tree(event, indent=0):
    children = [e for e in events if e.parent_event_id == event.id]
    prefix = "  " * indent
    print(f"{prefix}{event.event_type}: {event.payload.get('tool_name', '?')}")
    for child in children:
        print_tree(child, indent + 1)

for root in roots:
    print_tree(root)

Trace Tree Visualization#

Trace: 4bf92f3577b34da6a3ce929d0e0e4736

User Request
└── Coordinator Agent
    ├── tool_invocation: web_search          (evt_001, parent: null)
    ├── tool_invocation: delegate_research   (evt_002, parent: evt_001)
    │   └── Research Agent
    │       ├── tool_invocation: fetch_data  (evt_003, parent: evt_002)
    │       └── tool_invocation: summarize   (evt_004, parent: evt_003)
    └── tool_invocation: format_response     (evt_005, parent: evt_004)

TracingContext API#

from openintent import TracingContext

# Create new root trace
ctx = TracingContext.new_root()

# Create child context after emitting an event
child = ctx.child(event_id="evt_001")

# Serialize for transport
data = ctx.to_dict()  # {"trace_id": "...", "parent_event_id": "..."}

# Deserialize (returns None if trace_id missing)
restored = TracingContext.from_dict(data)