Skip to content

[opentelemetry-instrumentation-genai-dspy] Add initial setup / boilerplate - #439

Merged
aabmass merged 8 commits into
open-telemetry:mainfrom
DylanRussell:instrument_dsp
Aug 21, 2026
Merged

[opentelemetry-instrumentation-genai-dspy] Add initial setup / boilerplate#439
aabmass merged 8 commits into
open-telemetry:mainfrom
DylanRussell:instrument_dsp

Conversation

@DylanRussell

Copy link
Copy Markdown
Contributor

Description

Initial boilerplate / setup for the dspy instrumentation.

dspy is a bit of a tricky case. IMO the only spans this instrumentation should write (that are good matches for sem convs) are execute_tool and inference spans.

Open inference instrumentation wraps the various dspy primitives (Module, Predict, Retrieve, Adapter) and create spans (which aren't in our sem convs..) for each one. What these primitives do doesn't align with any existing sem conv. Wrapping them in spans is interesting and makes some sense, as it describes what the framework is doing internally..

There is 1 built in Module ReAct (https://dspy.ai/diving-deeper/react/) which triggers what they call an "agentic loop".. so probably that one could be an invoke agent span..

Also dspy is yet another python library that wraps inference calls (adk, agno, and lots of other libraries do this too), so we have the same issue of double instrumentation when the model is also instrumented. The way I think we should resolve this is for the dspy instrumentation to check if the corresponding model instrumentation is installed and active. If it is, don't instrument the model call.

We can eventually add an env var that allows the user to choose which span they want (or both), but I assume if they have the model level instrumentation enabled they prefer to have the span from that.

Type of change

  • New feature (non-breaking change which adds functionality)

How has this been tested?

Unit tests

Checklist

  • Followed the style guidelines of this project
  • Changelog updated if the change requires an entry
  • Unit tests added
  • Documentation updated

Copilot AI lite review requested due to automatic review settings August 21, 2026 14:11
@DylanRussell
DylanRussell requested a review from a team as a code owner August 21, 2026 14:11
Assisted-by: Antigravity

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an initial “skeleton” instrumentation package for the dspy framework under instrumentation/, wiring it into the monorepo workspace, CI/test matrix, and documentation. The package currently provides the DSPyInstrumentor lifecycle and shared util-genai handler setup, with DSPy-specific patching intended for follow-up work.

Changes:

  • Introduces the new opentelemetry-instrumentation-genai-dspy package scaffold (entry point, versioning, README, licensing/notice, changelog fragment).
  • Adds minimal unit tests and test configuration for instrumentor lifecycle.
  • Wires the new package into the repo (root deps, pyright include/exclude, README table, tox envs, uv lock).

Reviewed changes

Copilot reviewed 18 out of 19 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
uv.lock Adds the new package to the workspace lock and resolves new dependencies introduced by DSPy.
tox.ini Adds oldest/latest test env wiring for the new DSPy instrumentation package.
README.md Registers DSPy instrumentation in the top-level instrumentation table.
pyproject.toml Adds the new package to workspace deps and to the root pyright include/exclude lists.
instrumentation/opentelemetry-instrumentation-genai-dspy/tests/test_instrumentor.py Adds unit tests for instrument/uninstrument lifecycle and dependency declaration.
instrumentation/opentelemetry-instrumentation-genai-dspy/tests/requirements.oldest.txt Declares oldest-factor behavior (no additional pins).
instrumentation/opentelemetry-instrumentation-genai-dspy/tests/requirements.latest.txt Declares latest-factor deps for DSPy tests (including editable installs).
instrumentation/opentelemetry-instrumentation-genai-dspy/tests/conftest.py Adds shared-provider instrumentation fixture via opentelemetry.test_util_genai.
instrumentation/opentelemetry-instrumentation-genai-dspy/tests/init.py Establishes test package module.
instrumentation/opentelemetry-instrumentation-genai-dspy/src/opentelemetry/instrumentation/genai/dspy/version.py Defines the package version.
instrumentation/opentelemetry-instrumentation-genai-dspy/src/opentelemetry/instrumentation/genai/dspy/package.py Declares the dspy instrumentation dependency tuple.
instrumentation/opentelemetry-instrumentation-genai-dspy/src/opentelemetry/instrumentation/genai/dspy/init.py Implements DSPyInstrumentor skeleton with completion hook wiring and handler construction.
instrumentation/opentelemetry-instrumentation-genai-dspy/README.rst Adds package-level documentation and usage guidance.
instrumentation/opentelemetry-instrumentation-genai-dspy/pyproject.toml Defines packaging metadata, deps, entry point, and towncrier config for the new package.
instrumentation/opentelemetry-instrumentation-genai-dspy/NOTICE Adds third-party notice text for attribution.
instrumentation/opentelemetry-instrumentation-genai-dspy/LICENSE Adds the Apache-2.0 license file to the package directory.
instrumentation/opentelemetry-instrumentation-genai-dspy/CHANGELOG.md Adds the towncrier-managed changelog stub for the package.
instrumentation/opentelemetry-instrumentation-genai-dspy/.changelog/439.added Adds a towncrier fragment describing the initial package setup.
instrumentation/opentelemetry-instrumentation-genai-dspy/.changelog/.gitignore Keeps the changelog directory structure tracked.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread instrumentation/opentelemetry-instrumentation-genai-dspy/README.rst Outdated
Comment thread tox.ini
@opentelemetry-pr-dashboard

opentelemetry-pr-dashboard Bot commented Aug 21, 2026

Copy link
Copy Markdown

Pull request dashboard status

Merged · refreshed 2026-08-21 22:17 UTC

Status above doesn't look right?
  • Anything look wrong? Report it with what you expected; it helps us improve the dashboard.

@aabmass

aabmass commented Aug 21, 2026

Copy link
Copy Markdown
Member

There is 1 built in Module ReAct (https://dspy.ai/diving-deeper/react/) which triggers what they call an "agentic loop".. so probably that one could be an invoke agent span..

That sounds like a good fit. Earlier versions of LangGraph also had ReAct as the only canned agent loop you could use.

Also dspy is yet another python library that wraps inference calls (adk, agno, and lots of other libraries do this too), so we have the same issue of double instrumentation when the model is also instrumented. The way I think we should resolve this is for the dspy instrumentation to check if the corresponding model instrumentation is installed and active. If it is, don't instrument the model call.

Were you able to check dspy with popular providers (say OpenAI, Anthropic, Gemini) uses the client libraries we already instrumented under the hood?

Comment thread .github/workflows/changelog.yml
@DylanRussell

DylanRussell commented Aug 21, 2026

Copy link
Copy Markdown
Contributor Author

Ah interesting it actually just uses litellm for this.. which apparently has native otel instrumentation.. So maybe this instrumentation should just emit execute_tool and invoke_agent spans and recommend the native litellm instrumentation for inference spans...

agno actually uses the SDKs, but I think optionally allows litellm or possibly it did but recently disabled that option due to the fact that it was recently compromised on pypy: https://github.com/agno-agi/agno/blob/7b8e5308e9c88e30153dfb0619c7b362bed272a2/libs/agno/pyproject.toml#L252

@eternalcuriouslearner

Copy link
Copy Markdown
Contributor

There is 1 built in Module ReAct (https://dspy.ai/diving-deeper/react/) which triggers what they call an "agentic loop".. so probably that one could be an invoke agent span..

That sounds like a good fit. Earlier versions of LangGraph also had ReAct as the only canned agent loop you could use.

Also dspy is yet another python library that wraps inference calls (adk, agno, and lots of other libraries do this too), so we have the same issue of double instrumentation when the model is also instrumented. The way I think we should resolve this is for the dspy instrumentation to check if the corresponding model instrumentation is installed and active. If it is, don't instrument the model call.

Were you able to check dspy with popular providers (say OpenAI, Anthropic, Gemini) uses the client libraries we already instrumented under the hood?

Seconding this—I’m trying to instrument LlamaIndex’s ReActAgent as an invoke_agent operation. CrewAI, LlamaIndex, and DSPy appear to use similar agent architectures, such as ReAct.

@DylanRussell

Copy link
Copy Markdown
Contributor Author

I think if we are going to the trouble of instrumenting this package we should go ahead and instrument the model call, it's not much work and we can also support disabling it if the user wants to use litellm's model span instead..

I tried out litellm's gen ai otel instrumentation, it's OK.. They don't support the completion hook stuff, and there are a bunch of minor issues with what they produce.. Here's the list of attribute keys from their span

[ "gen_ai.cost.discount_amount", "gen_ai.cost.discount_percent", "gen_ai.cost.input_cost", "gen_ai.cost.margin_fixed_amount", "gen_ai.cost.margin_percent", "gen_ai.cost.margin_total_amount", "gen_ai.cost.original_cost", "gen_ai.cost.output_cost", "gen_ai.cost.reasoning_cost", "gen_ai.cost.tool_usage_cost", "gen_ai.cost.total_cost", "gen_ai.input.messages", "gen_ai.operation.name", "gen_ai.output.messages", "gen_ai.request.model", "gen_ai.response.finish_reasons", "gen_ai.response.id", "gen_ai.response.model", "gen_ai.system", "gen_ai.usage.input_tokens", "gen_ai.usage.output_tokens", "gen_ai.usage.total_tokens", "hidden_params", "litellm.call_id", "litellm.provider.model", "llm.is_streaming", "llm.request.type", "metadata.applied_guardrails", "metadata.cold_storage_object_key", "metadata.mcp_tool_call_metadata", "metadata.prompt_management_metadata", "metadata.requester_custom_headers", "metadata.requester_ip_address", "metadata.requester_metadata", "metadata.routing_decision", "metadata.spend_logs_metadata", "metadata.team_alias", "metadata.team_id", "metadata.usage_object", "metadata.user_agent", "metadata.user_api_key_alias", "metadata.user_api_key_auth_metadata", "metadata.user_api_key_budget_reset_at", "metadata.user_api_key_end_user_id", "metadata.user_api_key_hash", "metadata.user_api_key_max_budget", "metadata.user_api_key_org_alias", "metadata.user_api_key_org_id", "metadata.user_api_key_project_alias", "metadata.user_api_key_project_id", "metadata.user_api_key_request_route", "metadata.user_api_key_spend", "metadata.user_api_key_team_alias", "metadata.user_api_key_team_id", "metadata.user_api_key_team_max_budget", "metadata.user_api_key_team_spend", "metadata.user_api_key_user_email", "metadata.user_api_key_user_id", "metadata.user_api_key_user_max_budget", "metadata.user_api_key_user_spend", "metadata.vector_store_request_metadata" ]

@aabmass

aabmass commented Aug 21, 2026

Copy link
Copy Markdown
Member

I think if we are going to the trouble of instrumenting this package we should go ahead and instrument the model call, it's not much work and we can also support disabling it if the user wants to use litellm's model span instead..

SGTM, maybe drop that in the issue. I'm merging this one

@aabmass
aabmass added this pull request to the merge queue Aug 21, 2026
Merged via the queue into open-telemetry:main with commit c4e3164 Aug 21, 2026
61 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants