> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://respan.ai/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://respan.ai/docs/_mcp/server.

# DSPy (tracing)

> Trace DSPy programs with Respan — native callback instrumentation, optional gateway routing, and full observability.

[DSPy](https://dspy.ai/) is a framework for programming language model systems with signatures, modules, tools, agents, and evaluation loops. Respan's DSPy integration registers a native DSPy callback and sends module, LLM, adapter, tool, and evaluation spans through `respan-tracing`.

#### Set up Respan

Create an account at [platform.respan.ai](https://platform.respan.ai) and grab an [API key](https://platform.respan.ai/platform/gateway/api-keys).

Run `npx @respan/cli setup` to set up with your coding agent.

#### Use Respan Gateway

See [DSPy gateway setup](/docs/integrations/gateway/ds-py) to route this integration through the Respan gateway.

#### Example projects

* Example repo root: `respan-example-projects/python/tracing/dspy`

## Setup

#### Install packages

```bash
pip install respan-ai respan-instrumentation-dspy dspy
```

#### Set environment variables

```bash
export RESPAN_API_KEY="YOUR_RESPAN_API_KEY"
export OPENAI_API_KEY="YOUR_OPENAI_API_KEY"
export RESPAN_DSPY_MODEL="openai/gpt-4o-mini"
```

`RESPAN_API_KEY` exports traces to Respan. `OPENAI_API_KEY` is used by DSPy's underlying OpenAI-compatible model client in this tracing-only setup.

#### Initialize and run

```python
import os

import dspy
from respan import Respan
from respan_instrumentation_dspy import DSPyInstrumentor

respan_api_key = os.environ["RESPAN_API_KEY"]
openai_api_key = os.environ["OPENAI_API_KEY"]
model = os.getenv("RESPAN_DSPY_MODEL", "openai/gpt-4o-mini")

respan = Respan(
    api_key=respan_api_key,
    app_name="dspy-quickstart",
    instrumentations=[DSPyInstrumentor()],
)

dspy.configure(
    lm=dspy.LM(
        model,
        api_key=openai_api_key,
        cache=False,
        temperature=0.1,
    )
)

class QA(dspy.Signature):
    """Answer the question with one concise sentence."""

    question: str = dspy.InputField()
    answer: str = dspy.OutputField()

predict = dspy.Predict(QA)
prediction = predict(question="What does DSPy help developers build?")

print(prediction.answer)
```

#### View your trace

Open the [Traces page](https://platform.respan.ai/platform/traces) to see your DSPy program with module spans, adapter formatting/parsing spans, LLM calls, tool calls, and evaluation spans.

## Configuration

### Respan

| Parameter             | Type           | Default | Description                                                            |
| --------------------- | -------------- | ------- | ---------------------------------------------------------------------- |
| `api_key`             | `str \| None`  | `None`  | Falls back to `RESPAN_API_KEY` env var.                                |
| `base_url`            | `str \| None`  | `None`  | Falls back to `RESPAN_BASE_URL` env var.                               |
| `app_name`            | `str \| None`  | `None`  | Service name shown on exported DSPy spans.                             |
| `instrumentations`    | `list`         | `[]`    | Plugin instrumentations to activate, for example `DSPyInstrumentor()`. |
| `customer_identifier` | `str \| None`  | `None`  | Default customer identifier for all spans.                             |
| `metadata`            | `dict \| None` | `None`  | Default metadata attached to all spans.                                |
| `environment`         | `str \| None`  | `None`  | Environment tag, for example `"production"`.                           |

### DSPyInstrumentor

| Parameter         | Type          | Default | Description                                                                                                               |
| ----------------- | ------------- | ------- | ------------------------------------------------------------------------------------------------------------------------- |
| `target`          | `Any \| None` | `None`  | Optional DSPy object to instrument. If omitted, the callback is registered globally with `dspy.configure(callbacks=...)`. |
| `include_content` | `bool`        | `True`  | Capture span inputs and outputs. Set to `False` to omit prompt, completion, tool input, and tool output content.          |

## What gets traced

| DSPy operation                                          | Respan span                         | Notes                                                                                                       |
| ------------------------------------------------------- | ----------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `dspy.Predict`, `dspy.ChainOfThought`, and most modules | `dspy.module`                       | Captures module input and prediction output.                                                                |
| `dspy.ReAct`                                            | `dspy.module` with `agent` log type | Captures the agent trajectory and final answer.                                                             |
| `dspy.LM` calls                                         | `dspy.lm`                           | Captures chat messages, completion content, model, provider, and token usage when DSPy/LiteLLM provides it. |
| `dspy.Tool` calls                                       | `dspy.tool`                         | Captures `{name, arguments}` as input and the tool result as output.                                        |
| DSPy adapters                                           | `dspy.adapter`                      | Captures prompt formatting and completion parsing.                                                          |
| `dspy.Evaluate`                                         | `dspy.evaluate`                     | Captures evaluator inputs, score, and summarized results.                                                   |

## Attributes

### In Respan()

Set defaults at initialization — these apply to all spans.

```python
from respan import Respan
from respan_instrumentation_dspy import DSPyInstrumentor

respan = Respan(
    app_name="dspy-api",
    instrumentations=[DSPyInstrumentor()],
    customer_identifier="user_123",
    metadata={"service": "dspy-api", "version": "1.0.0"},
)
```

### With propagate\_attributes

Override per-request using a context scope.

```python
import dspy
from respan import Respan
from respan_instrumentation_dspy import DSPyInstrumentor

respan = Respan(instrumentations=[DSPyInstrumentor()])

class QA(dspy.Signature):
    question: str = dspy.InputField()
    answer: str = dspy.OutputField()

predict = dspy.ChainOfThought(QA)

def handle_request(user_id: str, question: str):
    with respan.propagate_attributes(
        customer_identifier=user_id,
        thread_identifier="conv_abc_123",
        trace_group_identifier="dspy-support-request",
        metadata={"plan": "pro"},
    ):
        result = predict(question=question)
        print(result.answer)
```

| Attribute                | Type   | Description                                           |
| ------------------------ | ------ | ----------------------------------------------------- |
| `customer_identifier`    | `str`  | Identifies the end user in Respan analytics.          |
| `thread_identifier`      | `str`  | Groups related DSPy calls into a conversation.        |
| `trace_group_identifier` | `str`  | Groups related traces for search and filtering.       |
| `metadata`               | `dict` | Custom key-value pairs. Merged with default metadata. |

## Decorators (optional)

Decorators are not required. DSPy module calls, LLM calls, adapter work, tool calls, and evaluation calls are auto-traced by the instrumentor. Use Respan workflow spans when you want a recognizable root span around a full DSPy script or request.

```python
import json

import dspy
from opentelemetry.semconv_ai import SpanAttributes
from respan import Respan
from respan_instrumentation_dspy import DSPyInstrumentor

respan = Respan(
    app_name="dspy-support",
    instrumentations=[DSPyInstrumentor()],
)

class QA(dspy.Signature):
    question: str = dspy.InputField()
    answer: str = dspy.OutputField()

answerer = dspy.ChainOfThought(QA)

def support_workflow(question: str):
    client = respan.telemetry.get_client()

    with respan.propagate_attributes(
        trace_group_identifier="support-workflow-001",
        metadata={"workflow": "support_qa"},
    ):
        with client.start_span("dspy_support.workflow", kind="workflow") as span:
            span.set_attribute(
                SpanAttributes.TRACELOOP_ENTITY_INPUT,
                json.dumps({"question": question}),
            )
            prediction = answerer(question=question)
            span.set_attribute(
                SpanAttributes.TRACELOOP_ENTITY_OUTPUT,
                json.dumps({"answer": prediction.answer}),
            )
            return prediction.answer

print(support_workflow("Why is tracing useful for DSPy programs?"))
```

## Examples

### Tool calls

Tool calls are captured as `dspy.tool` spans with JSON input shaped as `{name, arguments}` and the returned tool result as output.

```python
import dspy

def lookup_order_status(order_id: str) -> str:
    statuses = {
        "ord-1001": "ord-1001 is shipped and arriving tomorrow.",
        "ord-1002": "ord-1002 is waiting for carrier pickup.",
    }
    return statuses.get(order_id, f"No status found for {order_id}.")

tool = dspy.Tool(
    lookup_order_status,
    name="lookup_order_status",
    desc="Look up the shipping status for an order id.",
)

status = tool(order_id="ord-1001")
print(status)
```

### ReAct with a tool

```python
import dspy

class CityQuestion(dspy.Signature):
    """Answer the user's city question with one sentence."""

    question: str = dspy.InputField()
    answer: str = dspy.OutputField()

def lookup_city_fact(city: str) -> str:
    facts = {
        "tokyo": "Tokyo has one of the world's busiest rail networks.",
        "paris": "Paris is known for the Louvre and the Eiffel Tower.",
    }
    return facts.get(city.lower(), f"No stored fact for {city}.")

agent = dspy.ReAct(CityQuestion, tools=[lookup_city_fact], max_iters=3)
prediction = agent(
    question=(
        "Use lookup_city_fact for Tokyo, then answer with the fact in "
        "one sentence."
    )
)
print(prediction.answer)
```