Staso Docs
Python SDK

Manual spans

st.span is for when decorators are too coarse — sub-steps, conditional tracing, anything you need to mutate while it runs.

with st.span("embed-query", kind="retriever") as s:
    s.metadata["query"] = query
    result = embed(query)
    s.metadata["hit_count"] = len(result)

Signature

st.span(name: str, *, kind: str | SpanKind = SpanKind.CHAIN) -> ContextManager[Span]

Valid kind: "llm", "tool", "chain", "retriever", "agent", "custom".

Methods

Purpose
s.metadata[key] = valueAttach metadata.
s.input = {...}Record a custom input payload.
s.output = {...}Record a custom output payload.
s.annotate(name, value=..., data_type=...)Attach a score or label. See annotations.
s.set_status(status, description=None)Force ok, error, or timeout.
s.record_exception(exc)Mark errored and record the exception.

Exceptions raised inside the with block are recorded automatically. Use record_exception only when you catch the exception and still want it logged.

with st.span("call-stripe", kind="tool") as s:
    try:
        charge = stripe.charge(...)
        s.output = {"charge_id": charge.id}
    except stripe.error.CardError as exc:
        s.record_exception(exc)
        s.set_status("error", "card declined")
        raise

Decorators vs manual

DecoratorsManual spans
Whole function.Block inside a function.
Inputs/outputs match arguments.Mutate fields mid-execution.
One unit per function.Conditional tracing.

They compose freely.

Next