# Tracing Your Code

Three decorators. That's the whole API.

Each one creates a **span** — a unit of work with a name, timing, status, and optional inputs/outputs. Spans nest automatically: a `@tool` called inside an `@agent` shows up as a child in the trace tree.

## `@st.agent`

Your agent's entry-point.

```python
@st.agent(name="support-agent", tags=["tier-1", "billing"])
def handle_request(user_message: str) -> str:
    ...
```

| Parameter | Type | Default |
|-----------|------|---------|
| `name` | `str` | Function name |
| `tags` | `list[str]` | `None` |

## `@st.tool`

Any tool or function your agent calls. Automatically captures **arguments and return value** — you'll see them on the dashboard.

```python
@st.tool(name="search_kb")
def search_knowledge_base(query: str, limit: int = 10) -> str:
    return db.search(query, limit=limit)
```

Dashboard shows:
- **Input**: `{"query": "reset password", "limit": 10}`
- **Output**: `"To reset your password, go to Settings > ..."`

| Parameter | Type | Default |
|-----------|------|---------|
| `name` | `str` | Function name |
| `tags` | `list[str]` | `None` |

## `@st.trace`

For anything that doesn't fit `@agent` or `@tool` — chains, retrievers, preprocessing.

```python
@st.trace(name="classify_intent", kind="CHAIN")
def classify_intent(message: str) -> str:
    ...

@st.trace(name="fetch_context", kind="RETRIEVER")
def fetch_context(user_id: str) -> dict:
    ...
```

| Parameter | Type | Default |
|-----------|------|---------|
| `name` | `str` | Function name |
| `kind` | `str` | `"CHAIN"` |
| `tags` | `list[str]` | `None` |

Span kinds: `AGENT`, `TOOL`, `CHAIN`, `LLM`, `RETRIEVER`, `EMBEDDING`

`@st.trace` does **not** capture inputs/outputs automatically (unlike `@st.tool`). Use it for lightweight spans.

## Async

All decorators work with async functions. No changes needed:

```python
@st.tool(name="search")
async def search(query: str) -> str:
    return await db.async_search(query)
```

## Nesting

```python
@st.agent(name="my-agent")
def run(query: str) -> str:
    context = search(query)
    intent = classify(query)
    return respond(context, intent)

@st.tool(name="search")
def search(query: str) -> str: ...

@st.trace(name="classify", kind="CHAIN")
def classify(query: str) -> str: ...
```

Dashboard:

```
my-agent (agent)
├── search (tool)
└── classify (chain)
```

## Errors

Decorated functions that raise exceptions: the SDK records the error on the span and **re-raises it**. Your error handling is never affected.

```python
@st.tool(name="api_call")
def call_external_api() -> str:
    raise ConnectionError("timeout")

try:
    call_external_api()  # Span shows error, exception still propagates
except ConnectionError:
    handle_failure()
```
