> ## Documentation Index
> Fetch the complete documentation index at: https://moonshotfactory.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Tracing

> Record agent traces using the decorator, context manager, or manual API.

The AgentVista SDK gives you three ways to trace an agent. All three produce the same trace format in the AgentVista dashboard — choose whichever fits your codebase.

<Tabs>
  <Tab title="Decorator">
    ## Decorator

    `@agentvista.trace_agent` wraps an existing function without changing its
    signature or return value. The function name becomes the agent name in the
    dashboard.

    ```python theme={null}
    import agentvista

    agentvista.init(api_key="av_xxxxx")

    @agentvista.trace_agent
    def qualify_lead(lead: dict) -> dict:
        # your existing agent code — unchanged
        result = call_llm(lead)
        return result
    ```

    You can also pass a custom name:

    ```python theme={null}
    @agentvista.trace_agent("lead-qualifier")
    def qualify_lead(lead: dict) -> dict:
        ...
    ```

    When the decorated function raises an exception, the trace is automatically
    marked as `failed` and the exception message is recorded. The exception
    still propagates normally to the caller.
  </Tab>

  <Tab title="Context manager">
    ## Context manager

    `agentvista.run()` gives you explicit control over the trace. Use it when
    you need to attach a success signal or a custom outcome label.

    ```python theme={null}
    import agentvista

    agentvista.init(api_key="av_xxxxx")

    with agentvista.run("lead-qualifier") as r:
        result = call_llm(lead)
        r.set_outcome(success=True, outcome="qualified")
    ```

    ### Signalling failure

    Call `r.set_error()` to mark the trace as failed with a descriptive message:

    ```python theme={null}
    with agentvista.run("lead-qualifier") as r:
        try:
            result = call_llm(lead)
            r.set_outcome(success=True, outcome="qualified")
        except ValueError as e:
            r.set_error(str(e))
    ```

    If an unhandled exception propagates out of the `with` block, the trace is
    automatically marked as `failed` and the exception message is recorded. The
    exception is not suppressed.

    ### Methods on `TracedRun`

    | Method                                 | Description                                                                                  |
    | -------------------------------------- | -------------------------------------------------------------------------------------------- |
    | `r.set_outcome(success, outcome=None)` | Signal success or failure with an optional outcome label (e.g. `"qualified"`, `"rejected"`). |
    | `r.set_error(message)`                 | Mark the trace as failed with a custom message. Sets `success=False`.                        |
  </Tab>

  <Tab title="Manual">
    ## Manual

    `agentvista.record()` is the v1 API. It creates a single-span trace in one
    call. Use it when you want the smallest possible footprint, or when
    migrating from v1.

    ```python theme={null}
    import agentvista

    agentvista.init(api_key="av_xxxxx")

    agentvista.record(
        agent="lead-qualifier",
        success=True,
        outcome="qualified",
        cost=0.043,
        model="claude-sonnet-4-6",
        input_tokens=512,
        output_tokens=128,
        total_tokens=640,
    )
    ```

    `record()` is non-blocking and returns in under 0.1 ms. The event is
    buffered and sent asynchronously.

    ### Parameters

    | Parameter       | Type            | Description                                                        |
    | --------------- | --------------- | ------------------------------------------------------------------ |
    | `agent`         | `str`           | Agent name. Required.                                              |
    | `success`       | `bool \| None`  | Whether the agent achieved its business goal.                      |
    | `outcome`       | `str \| None`   | Outcome label (e.g. `"qualified"`, `"escalated"`).                 |
    | `cost`          | `float \| None` | Cost in USD.                                                       |
    | `model`         | `str \| None`   | LLM model used.                                                    |
    | `input_tokens`  | `int \| None`   | Input token count.                                                 |
    | `output_tokens` | `int \| None`   | Output token count.                                                |
    | `total_tokens`  | `int \| None`   | Total token count.                                                 |
    | `duration_ms`   | `int \| None`   | Execution duration in milliseconds.                                |
    | `error_message` | `str \| None`   | Error message if the agent failed.                                 |
    | `metadata`      | `dict \| None`  | Arbitrary JSON metadata.                                           |
    | `status`        | `str`           | Run status: `"completed"` (default), `"failed"`, or `"timed_out"`. |
  </Tab>
</Tabs>

***

## Child spans

Inside any active trace — whether started by `@trace_agent` or `agentvista.run()` — you can create child spans to break down the work into named steps.

```python theme={null}
import agentvista

agentvista.init(api_key="av_xxxxx")

@agentvista.trace_agent("lead-qualifier")
def qualify_lead(lead: dict) -> dict:
    with agentvista.span("fetch-crm-data", span_type="tool"):
        crm_data = fetch_from_crm(lead["id"])

    with agentvista.span("score-lead", span_type="llm"):
        score = call_llm(lead, crm_data)

    return score
```

Spans nest automatically: each `span()` call detects the active trace context and links itself as a child of the current span.

`agentvista.span()` is a no-op when called outside an active trace, so you can safely leave span instrumentation in place even if `init()` has not been called.

### Span types

Pass `span_type` to categorize the work each span represents. The AgentVista dashboard uses span types for filtering and visualization.

| `span_type` | Use for                                                      |
| ----------- | ------------------------------------------------------------ |
| `"agent"`   | Sub-agent or nested orchestration step                       |
| `"llm"`     | A call to an LLM API                                         |
| `"tool"`    | An external tool invocation (API call, file I/O, web search) |
| `"http"`    | An outbound HTTP request                                     |
| `"db"`      | A database query                                             |
| `"custom"`  | Anything else (default when `span_type` is omitted)          |

***

## Distributed tracing

When an agent calls another service, you can propagate the current trace across the network boundary using [W3C Trace Context](https://www.w3.org/TR/trace-context/) headers.

### Injecting a traceparent header

Call `agentvista.inject_traceparent(headers)` to add a `traceparent` header to an outgoing request. The function mutates and returns the headers dict.

```python theme={null}
import httpx
import agentvista

with agentvista.run("orchestrator") as r:
    headers = {}
    agentvista.inject_traceparent(headers)
    # headers now contains: {"traceparent": "00-<trace_id>-<span_id>-01"}

    response = httpx.post(
        "https://downstream-service/process",
        headers=headers,
        json={"data": "..."},
    )
```

`inject_traceparent()` is a no-op when called outside an active trace.

### Extracting a traceparent header

In the downstream service, pass the incoming `traceparent` to `agentvista.run()` via `incoming_traceparent`. AgentVista will continue the same logical trace so all downstream spans appear in the same unified view.

```python theme={null}
import agentvista

# In a FastAPI or similar framework:
def process(request):
    incoming = request.headers.get("traceparent")

    with agentvista.run("downstream-agent", incoming_traceparent=incoming) as r:
        result = do_work()
        r.set_outcome(success=True)
```

You can also parse the header directly with `agentvista.extract_traceparent(headers)`, which returns `(trace_id, parent_span_id)` or `None` if the header is missing or malformed.

***

## Performance

* `record()` returns in **under 0.1 ms** — there is no synchronous network call on the hot path.
* Events are batched in memory and flushed to AgentVista every **2 seconds** by a background thread.
* Network failures are silently dropped. Your agents never crash due to observability errors.
* Tracing state uses `contextvars.ContextVar`, which is both async-safe and thread-safe — safe to use in FastAPI, asyncio, and threaded servers without any extra configuration.
