Skip to content

fix(instrumentation-qdrant): stop wrapping methods that qdrant-client 1.16 removed#4041

Open
saivedant169 wants to merge 2 commits intotraceloop:mainfrom
saivedant169:fix/qdrant-client-1.16-compat
Open

fix(instrumentation-qdrant): stop wrapping methods that qdrant-client 1.16 removed#4041
saivedant169 wants to merge 2 commits intotraceloop:mainfrom
saivedant169:fix/qdrant-client-1.16-compat

Conversation

@saivedant169
Copy link
Copy Markdown
Contributor

@saivedant169 saivedant169 commented Apr 21, 2026

Closes #3492

  • I have added tests that cover my changes.
  • If adding a new instrumentation or changing an existing one, I've added screenshots from some observability platform showing the change.
  • PR name follows conventional commits format: feat(instrumentation): ... or fix(instrumentation): ....
  • (If applicable) I have updated the documentation accordingly.

What

On qdrant-client 1.16 the client dropped a handful of legacy methods in favour of the newer query / query_points APIs. The instrumentor was still trying to wrap them up front through wrap_function_wrapper, which meant importing QdrantInstrumentor().instrument() on a modern client raised:

AttributeError: type object 'QdrantClient' has no attribute 'upload_records'

The methods that are no longer on the client are search, search_batch, search_groups, recommend, recommend_batch, recommend_groups, discover, discover_batch and upload_records.

Changes

  • Remove the nine dead entries from qdrant_client_methods.json and async_qdrant_client_methods.json so we no longer ask wrapt to resolve attributes that are not there.
  • Prune the matching branches in wrapper.py so the dispatch only handles methods we still wrap.
  • Bump _instruments from qdrant-client >= 1.7 to qdrant-client >= 1.10 so the declared floor actually matches what we wrap (query_points lands in 1.10), and bump the test dependency accordingly.
  • Add a regression test that runs a full instrument / uninstrument cycle against whichever qdrant-client is installed, so if the wrapped method list ever drifts out of sync again we notice it in CI.

How I checked it

Installed qdrant-client==1.16.1 in a clean venv together with this branch and ran:

uv run pytest tests/ -v

All 5 tests pass, including the new regression test. uv run ruff check is also clean on the package.

Before the fix, the same setup fails immediately with the AttributeError above at QdrantInstrumentor().instrument() time.

AI disclosure

Took help of an AI tool for the diff review and to draft this description. The design call, reproduction and test were done by hand.

Summary by CodeRabbit

  • Chores

    • Raised minimum required qdrant-client version to 1.10 or higher.
    • Refined span instrumentation to focus on core query and batch operations.
  • Bug Fixes

    • Reduced instrumentation surface to avoid incorrect spans for less-common methods.
  • Tests

    • Added a regression test to ensure instrument/uninstrument works across qdrant-client versions.

… 1.16 removed

On qdrant-client 1.16 a set of legacy client methods (search, search_batch,
search_groups, recommend, recommend_batch, recommend_groups, discover,
discover_batch and upload_records) were dropped in favour of the newer
query / query_points APIs. The instrumentor still tried to wrap them at
import time, which blew up with an AttributeError before user code had
a chance to run.

Drop those entries from the wrapped method tables and the wrapper's
method dispatch, bump the minimum supported qdrant-client version to
1.10 so the floor matches what we actually wrap, and add a small
regression test that exercises a full instrument/uninstrument cycle
against the installed client.

Closes traceloop#3492
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 21, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a86f2ea4-553c-489f-ba88-3865f079b0d6

📥 Commits

Reviewing files that changed from the base of the PR and between 2438d81 and 76380ff.

📒 Files selected for processing (3)
  • packages/opentelemetry-instrumentation-qdrant/opentelemetry/instrumentation/qdrant/async_qdrant_client_methods.json
  • packages/opentelemetry-instrumentation-qdrant/pyproject.toml
  • packages/opentelemetry-instrumentation-qdrant/tests/test_qdrant_instrumentation.py
✅ Files skipped from review due to trivial changes (1)
  • packages/opentelemetry-instrumentation-qdrant/pyproject.toml
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/opentelemetry-instrumentation-qdrant/tests/test_qdrant_instrumentation.py

📝 Walkthrough

Walkthrough

Updated Qdrant instrumentation: bumped declared minimum qdrant-client version and removed instrumentation/span mappings and span-attribute handling for several Qdrant client methods that no longer exist; added a regression test to ensure wrapped methods match the installed client surface.

Changes

Cohort / File(s) Summary
Version & Packaging
packages/opentelemetry-instrumentation-qdrant/opentelemetry/instrumentation/qdrant/__init__.py, packages/opentelemetry-instrumentation-qdrant/pyproject.toml
Bumped declared minimum qdrant-client in the instrumentation package to >= 1.10; adjusted test dependency range to >=1.10,<1.17.
Async method-to-span mappings
packages/opentelemetry-instrumentation-qdrant/opentelemetry/instrumentation/qdrant/async_qdrant_client_methods.json
Removed span mappings for upload_records, search, search_batch, search_groups, discover, discover_batch, recommend, recommend_batch, recommend_groups; added query_points and query_batch_points.
Sync method-to-span mappings
packages/opentelemetry-instrumentation-qdrant/opentelemetry/instrumentation/qdrant/qdrant_client_methods.json
Removed the same set of obsolete method-to-span mappings (upload/search/discover/recommend variants); kept existing mappings for other methods.
Span attribute wrapper logic
packages/opentelemetry-instrumentation-qdrant/opentelemetry/instrumentation/qdrant/wrapper.py
Removed attribute-setting branch for upload_records; narrowed search-related attribute handling to query/query_points and query_batch_points only (reduced coverage of prior search/discover/recommend methods).
Tests
packages/opentelemetry-instrumentation-qdrant/tests/test_qdrant_instrumentation.py
Added test_wrapped_methods_exist_on_client_surface() to assert that WRAPPED_METHODS correspond to the installed qdrant_client public API and to exercise instrument/uninstrument cycle when methods may be missing.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇🌿 I hopped through code with gentle paws,
Pruned old methods without a pause.
Bumped to one-ten, trimmed tangled threads,
Wrapped only what the client now sheds.
Quiet fields — no AttributeErrors now!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main fix: removing methods from instrumentation that were removed in qdrant-client 1.16.
Linked Issues check ✅ Passed The PR directly addresses #3492 by removing all methods that cause AttributeError (upload_records and search/recommend/discover variants), updating version requirements, and adding a regression test to prevent recurrence.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing qdrant-client 1.16 incompatibility: removing wrapped methods, updating version constraints, adding regression test, and refactoring wrapper dispatch logic.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ 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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/opentelemetry-instrumentation-qdrant/opentelemetry/instrumentation/qdrant/async_qdrant_client_methods.json (1)

1-72: ⚠️ Potential issue | 🟠 Major

Add query_points and query_batch_points to async configuration for parity with sync instrumentation.

The sync qdrant_client_methods.json includes both query_points and query_batch_points (with dedicated wrapper.py handling that remaps query_pointssearch and query_batch_pointssearch_batch), but the async version is missing these entries. Since AsyncQdrantClient exposes both methods and the wrapper already has the necessary attribute-setting logic in place (lines 58–61, 72–75, 112), async users who migrate to the recommended query_points/query_batch_points methods will currently receive no Qdrant spans for their primary read path.

Proposed fix
     {
         "object": "AsyncQdrantClient",
         "method": "query",
         "span_name": "qdrant.query"
     },
+    {
+        "object": "AsyncQdrantClient",
+        "method": "query_points",
+        "span_name": "qdrant.query_points"
+    },
     {
         "object": "AsyncQdrantClient",
         "method": "query_batch",
         "span_name": "qdrant.query_batch"
     },
+    {
+        "object": "AsyncQdrantClient",
+        "method": "query_batch_points",
+        "span_name": "qdrant.query_batch_points"
+    },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/opentelemetry-instrumentation-qdrant/opentelemetry/instrumentation/qdrant/async_qdrant_client_methods.json`
around lines 1 - 72, Add entries for "query_points" and "query_batch_points" to
the AsyncQdrantClient config JSON so async instrumentation matches sync
behavior: insert objects with "object": "AsyncQdrantClient", "method":
"query_points", "span_name": "qdrant.query_points" and "object":
"AsyncQdrantClient", "method": "query_batch_points", "span_name":
"qdrant.query_batch_points" into
opentelemetry/instrumentation/qdrant/async_qdrant_client_methods.json (the
wrapper logic in wrapper.py that remaps query_points→search and
query_batch_points→search_batch will pick these up).
🧹 Nitpick comments (2)
packages/opentelemetry-instrumentation-qdrant/pyproject.toml (1)

38-38: Test dependency range LGTM, but consider pinning an upper bound more conservatively.

qdrant-client>=1.10,<2 is consistent with the instrumentation floor and lets CI pick up 1.16. Note, however, that since this PR was motivated precisely by qdrant-client 1.16 removing public API, allowing the full <2 range in the test group means the next minor release that drops/renames more methods may silently break users without CI catching it until after release. Consider either capping to the currently verified version (e.g., <1.17) or wiring a periodic matrix test against the latest release so drifts are surfaced quickly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opentelemetry-instrumentation-qdrant/pyproject.toml` at line 38, The
test dependency range "qdrant-client>=1.10,<2" is too broad given recent
breaking changes; update the dependency spec in
packages/opentelemetry-instrumentation-qdrant's pyproject to a more conservative
upper bound (e.g., change "<2" to "<1.17") or alternatively add a CI matrix job
that periodically tests against the latest qdrant-client to detect API drift;
locate the dependency line containing "qdrant-client>=1.10,<2" and replace or
augment it accordingly.
packages/opentelemetry-instrumentation-qdrant/tests/test_qdrant_instrumentation.py (1)

141-141: Leading uninstrument() on a fresh instrumentor.

Calling uninstrument() before any instrument() relies on BaseInstrumentor tolerating that ordering (it currently does, via internal _is_instrumented_by_opentelemetry). If that's intentional as part of the drift check, fine; otherwise drop it to keep the cycle symmetric (instrument → uninstrument → instrument → uninstrument) and finish in the uninstrumented state so this test doesn't leave global wrappers installed for subsequent tests in the module.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/opentelemetry-instrumentation-qdrant/tests/test_qdrant_instrumentation.py`
at line 141, The test currently calls instrumentor.uninstrument() before any
instrument(), which relies on BaseInstrumentor tolerating that ordering via the
internal _is_instrumented_by_opentelemetry flag; remove that initial
instrumentor.uninstrument() call so the test follows a symmetric cycle
(instrument() → uninstrument() → instrument() → uninstrument()) and ends in the
uninstrumented state, or alternatively replace the leading call with an
assert/cleanup that ensures no global wrappers are installed without invoking
uninstrument() prematurely; locate the call to instrumentor.uninstrument() in
the test and remove or replace it accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@packages/opentelemetry-instrumentation-qdrant/tests/test_qdrant_instrumentation.py`:
- Around line 127-144: The test currently only calls
QdrantInstrumentor.instrument()/uninstrument() and so misses stale method names
in WRAPPED_METHODS; update the
test_instrument_does_not_fail_on_missing_client_methods to import
WRAPPED_METHODS from opentelemetry.instrumentation.qdrant and the real Qdrant
client class used by the instrumentor, then assert that for every (obj_name,
method_name) tuple in WRAPPED_METHODS the target client class has that attribute
(use hasattr on the client class or the specific attribute container referenced
by the tuple) so the test fails if WRAPPED_METHODS contains a nonexistent/typo'd
method; keep the existing instrument/uninstrument calls to preserve the
regression check.

---

Outside diff comments:
In
`@packages/opentelemetry-instrumentation-qdrant/opentelemetry/instrumentation/qdrant/async_qdrant_client_methods.json`:
- Around line 1-72: Add entries for "query_points" and "query_batch_points" to
the AsyncQdrantClient config JSON so async instrumentation matches sync
behavior: insert objects with "object": "AsyncQdrantClient", "method":
"query_points", "span_name": "qdrant.query_points" and "object":
"AsyncQdrantClient", "method": "query_batch_points", "span_name":
"qdrant.query_batch_points" into
opentelemetry/instrumentation/qdrant/async_qdrant_client_methods.json (the
wrapper logic in wrapper.py that remaps query_points→search and
query_batch_points→search_batch will pick these up).

---

Nitpick comments:
In `@packages/opentelemetry-instrumentation-qdrant/pyproject.toml`:
- Line 38: The test dependency range "qdrant-client>=1.10,<2" is too broad given
recent breaking changes; update the dependency spec in
packages/opentelemetry-instrumentation-qdrant's pyproject to a more conservative
upper bound (e.g., change "<2" to "<1.17") or alternatively add a CI matrix job
that periodically tests against the latest qdrant-client to detect API drift;
locate the dependency line containing "qdrant-client>=1.10,<2" and replace or
augment it accordingly.

In
`@packages/opentelemetry-instrumentation-qdrant/tests/test_qdrant_instrumentation.py`:
- Line 141: The test currently calls instrumentor.uninstrument() before any
instrument(), which relies on BaseInstrumentor tolerating that ordering via the
internal _is_instrumented_by_opentelemetry flag; remove that initial
instrumentor.uninstrument() call so the test follows a symmetric cycle
(instrument() → uninstrument() → instrument() → uninstrument()) and ends in the
uninstrumented state, or alternatively replace the leading call with an
assert/cleanup that ensures no global wrappers are installed without invoking
uninstrument() prematurely; locate the call to instrumentor.uninstrument() in
the test and remove or replace it accordingly.
🪄 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: fdaabf1b-1b6b-4210-b0fb-e7651e8518ac

📥 Commits

Reviewing files that changed from the base of the PR and between 3efa0eb and 2438d81.

📒 Files selected for processing (6)
  • packages/opentelemetry-instrumentation-qdrant/opentelemetry/instrumentation/qdrant/__init__.py
  • packages/opentelemetry-instrumentation-qdrant/opentelemetry/instrumentation/qdrant/async_qdrant_client_methods.json
  • packages/opentelemetry-instrumentation-qdrant/opentelemetry/instrumentation/qdrant/qdrant_client_methods.json
  • packages/opentelemetry-instrumentation-qdrant/opentelemetry/instrumentation/qdrant/wrapper.py
  • packages/opentelemetry-instrumentation-qdrant/pyproject.toml
  • packages/opentelemetry-instrumentation-qdrant/tests/test_qdrant_instrumentation.py

Pick up four items flagged in review:

- Add query_points and query_batch_points entries to the async method
  list so AsyncQdrantClient users keep getting spans on the recommended
  read path. The sync list already had both, the async list did not.
- Tighten the qdrant-client test dependency from <2 to <1.17 so the next
  breaking minor cannot slip into CI silently. We can lift the cap in a
  follow up once a new release has been verified.
- Rework the regression test so it walks WRAPPED_METHODS against the
  installed qdrant-client and fails with a useful message if any
  (object, method) entry is not resolvable. The hasattr guards in
  _instrument / _uninstrument were swallowing drift quietly, so the old
  test only caught a truly unhandled exception.
- Drop the leading uninstrument() on a fresh instrumentor and end the
  cycle uninstrumented so the test does not leave global wrappers in
  place for anything that runs after it.
@saivedant169
Copy link
Copy Markdown
Contributor Author

Thanks for the thorough pass. Pushed 76380ff picking up all four items:

  • Added query_points and query_batch_points to the async method list so AsyncQdrantClient users get spans on the recommended read path. Good catch on the sync/async drift.
  • Tightened the qdrant-client test dependency from <2 to <1.17 so the next breaking minor cannot slip in silently.
  • Reworked the regression test to walk WRAPPED_METHODS against the installed client and fail with a useful message if any entry no longer resolves. The old version really was just an exception sink.
  • Dropped the leading uninstrument() on a fresh instrumentor and made the cycle symmetric so the test ends uninstrumented.

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.

🐛 Bug Report: opentelemetry-instrumentation-qdrant is incompatible with qdrant-client version 1.16.1

1 participant