> ## Documentation Index
> Fetch the complete documentation index at: https://docs.021labs.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# @observe

> Decorator to create a trace context

## Signature

```python theme={null}
def observe(
    name: str | None = None,
    *,
    tags: list[str] | None = None,
    metadata: dict | None = None,
    user_id: str | Callable[[], str | None] | None = None,
    session_id: str | Callable[[], str | None] | None = None,
    version: str | None = None,
    trace_id: str | Callable[[], str | None] | None = None,
    capture_input: bool = False,
    capture_output: bool = False,
) -> Callable
```

## Parameters

<ParamField path="name" type="string" default="function name">
  Name for the trace. Defaults to the decorated function's name if not provided.

  Example: `"chat-completion"`, `"customer-support"`, `"process-order"`
</ParamField>

<ParamField path="tags" type="list[string]" default="[]">
  Tags to attach to all spans in this trace.

  Example: `["production", "high-priority"]`
</ParamField>

<ParamField path="metadata" type="dict" default="{}">
  Custom key-value pairs to attach to the trace.

  Example: `{"order_id": "123", "plan": "enterprise"}`
</ParamField>

<ParamField path="user_id" type="string | Callable[[], str | None]" default="None">
  User identifier. Can be a static string or a zero-argument callable that returns a string.

  Example: `"user_123"` or `lambda: get_current_user_id()`
</ParamField>

<ParamField path="session_id" type="string | Callable[[], str | None]" default="None">
  Session identifier for grouping related traces. Accepts a string or callable.
</ParamField>

<ParamField path="version" type="string" default="None">
  Version string stored in trace metadata (e.g., model version, app version).

  Example: `"1.0.0"`, `"claude-sonnet-4-6"`
</ParamField>

<ParamField path="trace_id" type="string | Callable[[], str | None]" default="auto-generated">
  Explicit trace ID. If not provided, a UUID is generated. Accepts a string or callable.

  Use this to correlate traces across services.
</ParamField>

<ParamField path="capture_input" type="boolean" default={false}>
  If `True`, serialize the function's arguments and store them in `trace.metadata["input"]`.
</ParamField>

<ParamField path="capture_output" type="boolean" default={false}>
  If `True`, serialize the function's return value and store it in `trace.metadata["output"]`.
</ParamField>

## Examples

### Basic

```python theme={null}
from rdk import observe

@observe()
def chat(message: str) -> str:
    # trace name defaults to "chat"
    ...

@observe(name="customer-support")
def handle_request(message: str) -> str:
    ...
```

### With metadata

```python theme={null}
from rdk import observe

@observe(
    name="chat-pipeline",
    tags=["production", "claude"],
    metadata={"team": "ml-platform"},
    version="2.1.0",
)
def pipeline(question: str) -> str:
    ...
```

### Dynamic user and session

```python theme={null}
from rdk import observe

@observe(
    name="chat",
    user_id=lambda: get_current_user_id(),    # Callable — evaluated at call time
    session_id=lambda: get_session_id(),
)
def chat(message: str) -> str:
    ...
```

### Capture inputs and outputs

```python theme={null}
from rdk import observe

@observe(name="summarize", capture_input=True, capture_output=True)
def summarize(text: str) -> str:
    # Arguments stored in trace.metadata["input"]
    # Return value stored in trace.metadata["output"]
    ...
```

### Sync and async

Works with both sync and async functions:

```python theme={null}
from rdk import observe

@observe(name="sync-chat")
def sync_chat(message: str) -> str:
    ...

@observe(name="async-chat")
async def async_chat(message: str) -> str:
    ...
```

## Nesting

Nested `@observe` decorators skip the inner trace — only the outermost creates a trace context. All LLM calls within nested functions are attributed to the outermost trace.

```python theme={null}
@observe(name="outer")
def outer():
    inner()  # No new trace created; spans go to "outer"

@observe(name="inner")
def inner():
    ...
```

## Context manager alternative

If decorators aren't convenient (notebooks, scripts), use `trace()`:

```python theme={null}
from rdk import trace

with trace("my-operation", tags=["dev"]) as t:
    print(f"Trace ID: {t.id}")
    # LLM calls here are captured
```

See [trace()](/api-reference/trace) for details.

## See Also

* [trace()](/api-reference/trace) — Context manager alternative
* [span()](/api-reference/span) — Create manual spans within a trace
* [Custom Metadata](/features/custom-metadata) — Adding custom data to traces
