Staso Docs
Python SDK

Manual Spans

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

import staso as st

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 values: "llm", "tool", "chain", "retriever", "agent", "custom".

When to use manual spans vs decorators

Use decorators whenUse manual spans when
You want to trace a whole function.You need to trace only a block inside a function.
Inputs and outputs match what you want to record.You want to attach metadata that isn't a function argument.
The call site is stable.Tracing is conditional — only if a feature flag is on, for example.
One unit of work per function.You need to update span fields mid-execution.

Decorators and manual spans compose freely. A decorated function can open manual spans inside itself.

Span methods

The object yielded by with st.span(...) as s exposes a small set of user-facing methods and fields.

Method / fieldPurpose
s.metadata[key] = valueAttach arbitrary JSON-serializable metadata.
s.input = {...}Record a custom input payload.
s.output = {...}Record a custom output payload.
s.annotate(name, value=..., data_type=..., comment=...)Attach a score or label to this span's trace. See annotations.
s.set_status(status, description=None)Force the span status (ok, error, timeout).
s.record_exception(exc)Mark the span as errored and record the exception message.

Exceptions raised inside the with block are automatically recorded — you only need record_exception if you are catching the exception and still want it logged on the span.

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

See also