DSPy (tracing)
DSPy 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 and grab an API key .
Paste this into your coding agent:
Help me set up Respan tracing with the DSPy integration.
Follow https://www.respan.ai/docs/integrations/dspy.mdSetup
Install packages
pip install respan-ai respan-instrumentation-dspy dspySet environment variables
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
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 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.
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.
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.
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.
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
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)