Skip to content

feat(deepseek): add DeepSeek instrumentation with R1 reasoning_content support#4226

Open
CaringNihilistic wants to merge 2 commits into
traceloop:mainfrom
CaringNihilistic:feat/deepseek-instrumentation-2597
Open

feat(deepseek): add DeepSeek instrumentation with R1 reasoning_content support#4226
CaringNihilistic wants to merge 2 commits into
traceloop:mainfrom
CaringNihilistic:feat/deepseek-instrumentation-2597

Conversation

@CaringNihilistic

@CaringNihilistic CaringNihilistic commented Jun 10, 2026

Copy link
Copy Markdown

What

Adds opentelemetry-instrumentation-deepseek package for tracing DeepSeek API calls.

Why

Closes #2597. DeepSeek has no native tracing support in OpenLLMetry. Users calling
DeepSeek-R1 also lose visibility into reasoning_content (chain-of-thought output)
which is not captured by the existing OpenAI instrumentation.

How

  • Patches openai.resources.chat.completions (DeepSeek is OpenAI-compatible)
  • Only activates when base_url contains "deepseek" via _is_deepseek_client()
  • Captures reasoning_content as gen_ai.deepseek.reasoning_content span attribute
  • Accumulates reasoning_content across streaming chunks
  • Follows the Groq instrumentation structure exactly

Testing

121 tests passing, ruff clean.

Checklist

  • I have added tests that cover my changes.
  • PR name follows conventional commits format.
  • If adding a new instrumentation, I've added screenshots from some observability platform showing the change.

Summary by CodeRabbit

  • New Features
    • Added OpenTelemetry instrumentation for DeepSeek (sync/async, streaming, tool calls) with spans, metrics, and event emission, including DeepSeek-R1 reasoning content capture and prompt-content privacy controls.
  • Documentation
    • Added DeepSeek instrumentation README covering installation, supported OpenAI-compatible methods, and prompt-content logging configuration.
  • Tests
    • Added extensive unit/integration coverage for instrumentation behavior, event emission, span utilities, finish-reason mapping, and wrapper/edge cases.
  • Chores
    • Updated package/project configuration, pinned Python 3.10, bumped instrumentation version, and wired local editable dependencies for the sample app and SDK.

@CLAassistant

CLAassistant commented Jun 10, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9862dd61-ba78-4aa2-af92-8a2bdbed25ca

📥 Commits

Reviewing files that changed from the base of the PR and between 4ec6149 and 190367e.

📒 Files selected for processing (1)
  • packages/opentelemetry-instrumentation-deepseek/opentelemetry/instrumentation/deepseek/span_utils.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/opentelemetry-instrumentation-deepseek/opentelemetry/instrumentation/deepseek/span_utils.py

📝 Walkthrough

Walkthrough

Adds a new opentelemetry-instrumentation-deepseek package that wraps OpenAI chat completion calls (sync/async) for DeepSeek, capturing spans, streaming accumulation, token metrics, and optional structured event emission; includes utilities, span attribute builders, tests, packaging, and integration entries.

Changes

DeepSeek OpenTelemetry Instrumentation

Layer / File(s) Summary
Configuration and type contracts
opentelemetry/instrumentation/deepseek/config.py, opentelemetry/instrumentation/deepseek/event_models.py
Config class centralizes instrumentation settings (exception logger, metrics attributes, legacy-attribute mode), and MessageEvent/ChoiceEvent dataclasses define event payload shapes with tool-call support.
Utility helpers
opentelemetry/instrumentation/deepseek/utils.py
Core helpers: should_send_prompts() and set_span_attribute() for conditional tracing, dont_throw decorator for safe exception handling, metrics attribute builders, and Pydantic v1/v2 model normalization via model_as_dict().
Event emission system
opentelemetry/instrumentation/deepseek/event_emitter.py
Emits OpenTelemetry log records from request/response: emit_message_events() and emit_choice_events() dispatch to role-aware internal handlers that shape payloads, remove redacted fields based on tracing configuration, and emit structured events.
Span attribute utilities
opentelemetry/instrumentation/deepseek/span_utils.py
Helper functions that populate span attributes from request/response: set_input_attributes(), set_model_input_attributes(), set_streaming_response_attributes(), set_model_streaming_response_attributes(), set_model_response_attributes(), and set_response_attributes() handle content/tool-call conversion, token metrics, and DeepSeek-R1 reasoning content.
Core instrumentation and wrapper
opentelemetry/instrumentation/deepseek/__init__.py
Main instrumentation entry point: DeepSeekInstrumentor wraps OpenAI chat completion create() methods (sync/async), detects DeepSeek clients by base URL, manages span lifecycle, accumulates streaming chunks, routes to event or span-attribute paths, records token/operation duration metrics, and handles suppression contexts.
Package configuration and documentation
pyproject.toml, project.json, poetry.toml, version.py, README.md
Package metadata, Python 3.10 requirement, dependencies, build targets, entry point registration for deepseek instrumentor, and README documenting installation, usage, reasoning content capture, and content-tracing privacy controls.
Test infrastructure and fixtures
tests/traces/conftest.py
Pytest fixtures provisioning in-memory OTel components (span/log exporters and metric readers), DeepSeek and OpenAI SDK clients, and multiple instrumentation modes (legacy attributes, event emission enabled/disabled).
Event emitter unit tests
tests/traces/test_event_emitter.py
Validates event emission: role-aware event naming, tool-call inclusion/omission, and content/argument redaction when TRACELOOP_TRACE_CONTENT is disabled.
Finish reason mapping tests
tests/traces/test_finish_reasons.py
Unit tests for DeepSeek finish-reason normalization (tool_callstool_call) and extraction from response choices.
Instrumentation initialization tests
tests/traces/test_init.py
Comprehensive test coverage for client detection, streaming chunk accumulation, tool-call assembly, span processors (sync/async), wrapper suppression/passthrough, duration metrics, and error handling.
Span utility function tests
tests/traces/test_span_utils.py
Tests for content/tool-call-to-parts conversion, input/output message serialization, tool-definition recording, token histogram metrics, and reasoning-content attribute mapping.
Utility function tests
tests/traces/test_utils.py
Unit tests for dont_throw exception suppression, error metrics attributes, and Pydantic model conversion across versions.
Ecosystem integration
.cz.toml, .python-version, packages/sample-app/pyproject.toml, packages/traceloop-sdk/pyproject.toml
Version bump automation configuration, Python 3.10 pinning, and dependency/source integration in sample-app and traceloop-sdk packages.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~70 minutes

Possibly related PRs

  • traceloop/openllmetry#4133: The use_legacy_attributes flag introduced in this PR's DeepSeekInstrumentor directly overlaps with configuration propagation for the legacy-vs-events mode through Traceloop.init() and instrumentor constructors.

Suggested reviewers

  • doronkopit5
  • galzilber
  • max-deygin-traceloop
  • netanel-tl

Poem

🐰 A rabbit nibbles through the stack,
tracing chats along the track,
Spans and events in tidy rows,
reasoning content gently flows,
Hooray for DeepSeek telemetry — hop, hop, glow!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.92% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main addition: introducing DeepSeek instrumentation with R1 reasoning_content support.
Linked Issues check ✅ Passed The PR successfully implements all coding objectives from issue #2597: DeepSeek observability support with reasoning_content capture matching OpenAI feature parity.
Out of Scope Changes check ✅ Passed All changes directly support DeepSeek instrumentation. Minor configuration updates to sample-app and traceloop-sdk dependencies are necessary integration points.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/opentelemetry-instrumentation-deepseek/README.md (1)

4-4: ⚡ Quick win

Add alt text to the badge image for accessibility.

The PyPI badge image is missing alt text. Consider adding descriptive alt text for screen readers and accessibility compliance.

♿ Proposed fix
-    <img src="https://badge.fury.io/py/opentelemetry-instrumentation-deepseek.svg">
+    <img src="https://badge.fury.io/py/opentelemetry-instrumentation-deepseek.svg" alt="PyPI version">
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/opentelemetry-instrumentation-deepseek/README.md` at line 4, The
README's PyPI badge <img> tag is missing alt text; update the <img> element in
packages/opentelemetry-instrumentation-deepseek/README.md to include a
descriptive alt attribute (e.g., alt="PyPI package:
opentelemetry-instrumentation-deepseek") so screen readers and accessibility
tools can convey the badge meaning.
packages/opentelemetry-instrumentation-deepseek/tests/traces/conftest.py (1)

82-82: 💤 Low value

Optional: Remove redundant reader fixture parameter.

The reader parameter is redundant since the meter_provider fixture (lines 57-62) already declares reader as a dependency. Pytest will automatically ensure reader is initialized before meter_provider. Removing it would simplify the fixture signatures without changing behavior.

♻️ Simplified fixture signatures
-def instrument_legacy(reader, tracer_provider, meter_provider):
+def instrument_legacy(tracer_provider, meter_provider):
-def instrument_with_content(reader, tracer_provider, logger_provider, meter_provider):
+def instrument_with_content(tracer_provider, logger_provider, meter_provider):
-def instrument_with_no_content(reader, tracer_provider, logger_provider, meter_provider):
+def instrument_with_no_content(tracer_provider, logger_provider, meter_provider):

Also applies to: 95-95, 114-114

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/opentelemetry-instrumentation-deepseek/tests/traces/conftest.py` at
line 82, Remove the redundant reader fixture parameter from the affected fixture
functions; specifically, update instrument_legacy(reader, tracer_provider,
meter_provider) and the other two fixture definitions referenced (the fixtures
at the other noted locations) to drop the reader parameter since meter_provider
already depends on reader and pytest will initialize it automatically; adjust
the function signatures (e.g., instrument_legacy and the two other fixture
function names shown in the diff) and any internal references to the removed
parameter so the fixtures continue to use tracer_provider and meter_provider
only.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/opentelemetry-instrumentation-deepseek/opentelemetry/instrumentation/deepseek/event_models.py`:
- Around line 18-22: The TypedDict field `role` in CompletionMessage is
incorrectly given a default (`role: str = "assistant"`); remove the default so
the key is a required string (`role: str`) because codepaths pass it explicitly,
or if you intend it optional switch to `role: NotRequired[str]` (importing
NotRequired from typing_extensions for compatibility). Update the
CompletionMessage definition accordingly to avoid TypedDict/typing-tool issues
(refer to the CompletionMessage TypedDict in event_models.py).

---

Nitpick comments:
In `@packages/opentelemetry-instrumentation-deepseek/README.md`:
- Line 4: The README's PyPI badge <img> tag is missing alt text; update the
<img> element in packages/opentelemetry-instrumentation-deepseek/README.md to
include a descriptive alt attribute (e.g., alt="PyPI package:
opentelemetry-instrumentation-deepseek") so screen readers and accessibility
tools can convey the badge meaning.

In `@packages/opentelemetry-instrumentation-deepseek/tests/traces/conftest.py`:
- Line 82: Remove the redundant reader fixture parameter from the affected
fixture functions; specifically, update instrument_legacy(reader,
tracer_provider, meter_provider) and the other two fixture definitions
referenced (the fixtures at the other noted locations) to drop the reader
parameter since meter_provider already depends on reader and pytest will
initialize it automatically; adjust the function signatures (e.g.,
instrument_legacy and the two other fixture function names shown in the diff)
and any internal references to the removed parameter so the fixtures continue to
use tracer_provider and meter_provider only.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 081db5d1-e617-42c5-b0c2-8bdaca29d911

📥 Commits

Reviewing files that changed from the base of the PR and between 4643b88 and 74c1cf7.

⛔ Files ignored due to path filters (3)
  • packages/opentelemetry-instrumentation-deepseek/uv.lock is excluded by !**/*.lock
  • packages/sample-app/uv.lock is excluded by !**/*.lock
  • packages/traceloop-sdk/uv.lock is excluded by !**/*.lock
📒 Files selected for processing (23)
  • .cz.toml
  • packages/opentelemetry-instrumentation-deepseek/.python-version
  • packages/opentelemetry-instrumentation-deepseek/README.md
  • packages/opentelemetry-instrumentation-deepseek/opentelemetry/instrumentation/deepseek/__init__.py
  • packages/opentelemetry-instrumentation-deepseek/opentelemetry/instrumentation/deepseek/config.py
  • packages/opentelemetry-instrumentation-deepseek/opentelemetry/instrumentation/deepseek/event_emitter.py
  • packages/opentelemetry-instrumentation-deepseek/opentelemetry/instrumentation/deepseek/event_models.py
  • packages/opentelemetry-instrumentation-deepseek/opentelemetry/instrumentation/deepseek/span_utils.py
  • packages/opentelemetry-instrumentation-deepseek/opentelemetry/instrumentation/deepseek/utils.py
  • packages/opentelemetry-instrumentation-deepseek/opentelemetry/instrumentation/deepseek/version.py
  • packages/opentelemetry-instrumentation-deepseek/poetry.toml
  • packages/opentelemetry-instrumentation-deepseek/project.json
  • packages/opentelemetry-instrumentation-deepseek/pyproject.toml
  • packages/opentelemetry-instrumentation-deepseek/tests/__init__.py
  • packages/opentelemetry-instrumentation-deepseek/tests/data/.gitkeep
  • packages/opentelemetry-instrumentation-deepseek/tests/traces/conftest.py
  • packages/opentelemetry-instrumentation-deepseek/tests/traces/test_event_emitter.py
  • packages/opentelemetry-instrumentation-deepseek/tests/traces/test_finish_reasons.py
  • packages/opentelemetry-instrumentation-deepseek/tests/traces/test_init.py
  • packages/opentelemetry-instrumentation-deepseek/tests/traces/test_span_utils.py
  • packages/opentelemetry-instrumentation-deepseek/tests/traces/test_utils.py
  • packages/sample-app/pyproject.toml
  • packages/traceloop-sdk/pyproject.toml

…t support

Adds opentelemetry-instrumentation-deepseek package for tracing DeepSeek API
calls. Closes traceloop#2597.

DeepSeek has no native tracing support in OpenLLMetry. Users calling
DeepSeek-R1 also lose visibility into reasoning_content (chain-of-thought
output) which is not captured by the existing OpenAI instrumentation.

- Patches openai.resources.chat.completions (DeepSeek is OpenAI-compatible)
- Only activates when base_url contains "deepseek" via _is_deepseek_client()
- Captures reasoning_content as gen_ai.deepseek.reasoning_content span attribute
- Accumulates reasoning_content across streaming chunks
- Follows the Groq instrumentation structure exactly
@CaringNihilistic CaringNihilistic force-pushed the feat/deepseek-instrumentation-2597 branch from abef5e8 to 4ec6149 Compare June 17, 2026 20:29

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/opentelemetry-instrumentation-deepseek/opentelemetry/instrumentation/deepseek/span_utils.py`:
- Around line 259-308: The set_response_attributes function lacks exception
handling protection and can fail during execution, particularly when
json.dumps() is called on line 305 if the message content contains
non-JSON-serializable data. Add the `@dont_throw` decorator to the
set_response_attributes function definition to ensure that any exceptions raised
during execution are caught and logged without crashing the instrumentation,
similar to how set_model_response_attributes is protected.
- Around line 175-194: The `set_streaming_response_attributes` function is
missing the `@dont_throw` decorator which prevents exceptions from propagating
to user code. The `json.dumps([message])` call can raise exceptions if the
message contains non-serializable content, and without the decorator these will
crash the user's application. Add the `@dont_throw` decorator above the function
definition, matching the pattern used in the related functions
`set_input_attributes` and `set_model_input_attributes`, to ensure any
exceptions during serialization or span attribute operations are caught and
suppressed rather than propagated.
- Around line 197-210: The function set_model_streaming_response_attributes
lacks the `@dont_throw` decorator that is used for consistency and safety across
other instrumentation functions. Add the `@dont_throw` decorator above the
function definition to ensure that any unexpected errors (such as AttributeError
when accessing usage.completion_tokens, usage.prompt_tokens, or
usage.total_tokens) are caught and handled gracefully, maintaining
instrumentation resilience without interrupting the main application flow.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9ecf1e1f-28ee-43fe-b1a6-5abebe1e58e9

📥 Commits

Reviewing files that changed from the base of the PR and between 7d09881 and 4ec6149.

⛔ Files ignored due to path filters (3)
  • packages/opentelemetry-instrumentation-deepseek/uv.lock is excluded by !**/*.lock
  • packages/sample-app/uv.lock is excluded by !**/*.lock
  • packages/traceloop-sdk/uv.lock is excluded by !**/*.lock
📒 Files selected for processing (23)
  • .cz.toml
  • packages/opentelemetry-instrumentation-deepseek/.python-version
  • packages/opentelemetry-instrumentation-deepseek/README.md
  • packages/opentelemetry-instrumentation-deepseek/opentelemetry/instrumentation/deepseek/__init__.py
  • packages/opentelemetry-instrumentation-deepseek/opentelemetry/instrumentation/deepseek/config.py
  • packages/opentelemetry-instrumentation-deepseek/opentelemetry/instrumentation/deepseek/event_emitter.py
  • packages/opentelemetry-instrumentation-deepseek/opentelemetry/instrumentation/deepseek/event_models.py
  • packages/opentelemetry-instrumentation-deepseek/opentelemetry/instrumentation/deepseek/span_utils.py
  • packages/opentelemetry-instrumentation-deepseek/opentelemetry/instrumentation/deepseek/utils.py
  • packages/opentelemetry-instrumentation-deepseek/opentelemetry/instrumentation/deepseek/version.py
  • packages/opentelemetry-instrumentation-deepseek/poetry.toml
  • packages/opentelemetry-instrumentation-deepseek/project.json
  • packages/opentelemetry-instrumentation-deepseek/pyproject.toml
  • packages/opentelemetry-instrumentation-deepseek/tests/__init__.py
  • packages/opentelemetry-instrumentation-deepseek/tests/data/.gitkeep
  • packages/opentelemetry-instrumentation-deepseek/tests/traces/conftest.py
  • packages/opentelemetry-instrumentation-deepseek/tests/traces/test_event_emitter.py
  • packages/opentelemetry-instrumentation-deepseek/tests/traces/test_finish_reasons.py
  • packages/opentelemetry-instrumentation-deepseek/tests/traces/test_init.py
  • packages/opentelemetry-instrumentation-deepseek/tests/traces/test_span_utils.py
  • packages/opentelemetry-instrumentation-deepseek/tests/traces/test_utils.py
  • packages/sample-app/pyproject.toml
  • packages/traceloop-sdk/pyproject.toml
✅ Files skipped from review due to trivial changes (4)
  • packages/opentelemetry-instrumentation-deepseek/.python-version
  • packages/opentelemetry-instrumentation-deepseek/opentelemetry/instrumentation/deepseek/version.py
  • .cz.toml
  • packages/opentelemetry-instrumentation-deepseek/project.json
🚧 Files skipped from review as they are similar to previous changes (16)
  • packages/opentelemetry-instrumentation-deepseek/README.md
  • packages/traceloop-sdk/pyproject.toml
  • packages/sample-app/pyproject.toml
  • packages/opentelemetry-instrumentation-deepseek/poetry.toml
  • packages/opentelemetry-instrumentation-deepseek/opentelemetry/instrumentation/deepseek/config.py
  • packages/opentelemetry-instrumentation-deepseek/tests/traces/test_finish_reasons.py
  • packages/opentelemetry-instrumentation-deepseek/opentelemetry/instrumentation/deepseek/event_models.py
  • packages/opentelemetry-instrumentation-deepseek/tests/traces/test_utils.py
  • packages/opentelemetry-instrumentation-deepseek/pyproject.toml
  • packages/opentelemetry-instrumentation-deepseek/tests/traces/test_span_utils.py
  • packages/opentelemetry-instrumentation-deepseek/tests/traces/test_event_emitter.py
  • packages/opentelemetry-instrumentation-deepseek/tests/traces/conftest.py
  • packages/opentelemetry-instrumentation-deepseek/opentelemetry/instrumentation/deepseek/utils.py
  • packages/opentelemetry-instrumentation-deepseek/opentelemetry/instrumentation/deepseek/init.py
  • packages/opentelemetry-instrumentation-deepseek/opentelemetry/instrumentation/deepseek/event_emitter.py
  • packages/opentelemetry-instrumentation-deepseek/tests/traces/test_init.py

@CaringNihilistic

Copy link
Copy Markdown
Author

Hi team, CLA is now signed by all committers. Could a maintainer
please approve the workflow run so the checks can execute?
Happy to address any feedback. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🚀 Feature: DeepSeek Observability Support

2 participants