> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://respan.ai/docs/integrations/dspy/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://respan.ai/_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) ``` > Trace DSPy programs with Respan — native callback instrumentation, optional gateway routing, and full observability.