Skip to main content
Tracing helps you understand the flow of operations in your LLM application by grouping related calls together.

Trace Hierarchy

Muxx uses a hierarchical tracing model:
Trace: "process-document"
|-- Span: "parse-document"
|-- Span: "extract-entities"
|   |-- Generation: GPT-4o call
|-- Span: "summarize"
|   |-- Generation: GPT-4o call
|-- Span: "format-output"

Creating Traces

With Decorators

from muxx import trace

@trace("my-operation")
def my_function():
    # All LLM calls inside are grouped under this trace
    pass

With Context Manager

from muxx import Muxx

muxx = Muxx()

with muxx.trace("my-operation"):
    # All LLM calls inside are grouped under this trace
    pass

Manual Trace Management

from muxx import Muxx

muxx = Muxx()

trace = muxx.start_trace("my-operation")
try:
    # Your code here
    pass
finally:
    trace.end()

Creating Spans

Spans represent individual operations within a trace.

With Decorators

from muxx import trace, span

@trace("document-processing")
def process_document(doc: str):

    @span("extraction")
    def extract():
        # LLM call for extraction
        pass

    @span("summarization")
    def summarize(data):
        # LLM call for summarization
        pass

    data = extract()
    return summarize(data)

With Context Manager

from muxx import Muxx

muxx = Muxx()

with muxx.trace("document-processing"):
    with muxx.span("extraction"):
        # Extraction logic
        pass

    with muxx.span("summarization"):
        # Summarization logic
        pass

Nested Spans

Spans can be nested to represent sub-operations:
from muxx import trace, span

@trace("complex-operation")
def complex_operation():

    @span("step-1")
    def step_one():

        @span("step-1a")
        def sub_step():
            # Nested operation
            pass

        sub_step()

    step_one()

Adding Metadata

To Traces

@trace("user-request", metadata={
    "user_id": "user_123",
    "session_id": "sess_abc",
    "feature": "chat"
})
def handle_request():
    pass

To Spans

@span("llm-call", metadata={
    "model": "gpt-4o",
    "purpose": "summarization"
})
def make_llm_call():
    pass

Trace Context

Access the current trace context:
from muxx import get_current_trace, get_current_span

@trace("my-trace")
def my_function():
    current_trace = get_current_trace()
    print(f"Trace ID: {current_trace.id}")

    with span("my-span"):
        current_span = get_current_span()
        print(f"Span ID: {current_span.id}")

Async Tracing

Tracing works seamlessly with async code:
import asyncio
from muxx import trace, span

@trace("async-operation")
async def async_operation():

    @span("async-step")
    async def async_step():
        await asyncio.sleep(1)

    await async_step()

Best Practices

Use names like user-signup, document-upload, chat-message rather than technical names.
Each span should represent one logical operation. Don’t create spans that are too broad or too narrow.
Include user IDs, feature flags, and other context that helps with debugging and analytics.
Traces and spans automatically capture errors, but you can add additional context.