Skip to content

new: support multimodal embeddings#908

Merged
joein merged 6 commits into
devfrom
fastembed-0.6.0
Apr 15, 2025
Merged

new: support multimodal embeddings#908
joein merged 6 commits into
devfrom
fastembed-0.6.0

Conversation

@joein

@joein joein commented Feb 24, 2025

Copy link
Copy Markdown
Member

No description provided.

@coderabbitai

coderabbitai Bot commented Feb 24, 2025

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

The changes introduce support for a new embedding model type, LateInteractionMultimodalEmbedding, within the embedding infrastructure. The Embedder class is extended with a new cache for these multimodal models and a method to initialize or retrieve them. The main embedding method is refactored to delegate to specialized private methods based on the model type and input modality, supporting dense text, sparse text, late interaction text, late interaction multimodal text and image, and dense images. Error handling is updated for unsupported models, and type annotations are kept consistent.

In the model embedder, batching logic is improved to distinguish between text and image data for multimodal models, and model validation is streamlined. Explicit error handling is added for unsupported inference object usage with the new multimodal models. The common fastembed module now includes the new model type and its supported models dictionary. Testing is expanded with a new fixture and test function to mock and verify multimodal embedding behavior, including batching, vector shapes, and embedding values.

Suggested reviewers

  • generall

Tip

⚡💬 Agentic Chat (Pro Plan, General Availability)
  • We're introducing multi-step agentic chat in review comments and issue comments, within and outside of PR's. This feature enhances review and issue discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments and add commits to existing pull requests.
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@netlify

netlify Bot commented Feb 24, 2025

Copy link
Copy Markdown

Deploy Preview for poetic-froyo-8baba7 ready!

Name Link
🔨 Latest commit f5037bc
🔍 Latest deploy log https://app.netlify.com/sites/poetic-froyo-8baba7/deploys/67fe6a636f6e590008934911
😎 Deploy Preview https://deploy-preview-908--poetic-froyo-8baba7.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@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: 0

🧹 Nitpick comments (6)
pyproject.toml (1)

30-30: Consider pinning the Git dependency to a specific commit or tag.
Relying on the HEAD of a repository can introduce unpredictability if changes occur upstream. Pinning ensures consistent builds across environments.

tests/embed_tests/test_local_inference.py (2)

1719-1722: Consider suppressing or replacing the unused import.
Static analysis flags import fastembed as unused. You can either suppress it (e.g., # noqa: F401) or use importlib.util.find_spec('fastembed') for availability checks and skip.

🧰 Tools
🪛 Ruff (0.8.2)

1722-1722: fastembed imported but unused; consider using importlib.util.find_spec to test for availability

(F401)


1726-1753: Mock class is well-structured.
The counters for text and image calls facilitate easy assertions. If reused in multiple tests, consider resetting them in a setup to avoid cross-test pollution.

qdrant_client/embed/embedder.py (1)

158-190: Constructor follows established pattern.
get_or_init_late_interaction_multimodal_model mirrors existing get_or_init_* methods. Consider supporting the deprecated flag if needed for future or backward compatibility.

qdrant_client/embed/model_embedder.py (2)

314-317: Improve type checking comment clarity.

The comment about IDE type checker behavior could be more clear.

-        if not isinstance(
-            data, get_args(INFERENCE_OBJECT_TYPES)
-        ):  # ide type checker ignores `not` and scolds
-            return data  # type: ignore
+        # Some IDEs incorrectly flag type issues with negated isinstance checks
+        if not isinstance(data, get_args(INFERENCE_OBJECT_TYPES)):
+            return data  # type: ignore

384-390: Ensure proper error handling for multimodal inference objects.

The code correctly validates supported models and raises an appropriate error for unsupported InferenceObject usage with multimodal models. However, consider adding more descriptive error messages to guide users.

-            raise ValueError(f"{model_name} does not support `InferenceObject` interface")
+            raise ValueError(
+                f"{model_name} does not support `InferenceObject` interface. "
+                "Use Document or Image objects directly for multimodal embeddings."
+            )

Also applies to: 428-438

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ce4c51d and 9386868.

⛔ Files ignored due to path filters (1)
  • poetry.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • pyproject.toml (1 hunks)
  • qdrant_client/embed/embedder.py (5 hunks)
  • qdrant_client/embed/model_embedder.py (7 hunks)
  • qdrant_client/fastembed_common.py (2 hunks)
  • tests/embed_tests/test_local_inference.py (3 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
tests/embed_tests/test_local_inference.py

1722-1722: fastembed imported but unused; consider using importlib.util.find_spec to test for availability

(F401)

⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: Redirect rules - poetic-froyo-8baba7
  • GitHub Check: Header rules - poetic-froyo-8baba7
  • GitHub Check: Pages changed - poetic-froyo-8baba7
  • GitHub Check: Python 3.13.x on ubuntu-latest test
  • GitHub Check: Python 3.12.x on ubuntu-latest test
  • GitHub Check: Python 3.11.x on ubuntu-latest test
  • GitHub Check: Python 3.10.x on ubuntu-latest test
  • GitHub Check: Python 3.9.x on ubuntu-latest test
  • GitHub Check: Python 3.13 test
🔇 Additional comments (14)
qdrant_client/fastembed_common.py (3)

14-14: Confirm the availability of LateInteractionMultimodalEmbedding.
The import is correct, but please verify that the newly switched Git-based dependency reliably provides this attribute.


23-23: Fallback assignment looks good.
Setting LateInteractionMultimodalEmbedding = None for the import failure scenario aligns with how other classes are handled in the except block.


81-88: Dictionary declaration is consistent with other embedding model dictionaries.
The _LATE_INTERACTION_MULTIMODAL_EMBEDDING_MODELS structure matches the existing pattern and should remain maintainable.

tests/embed_tests/test_local_inference.py (3)

6-6: Direct embedder import is straightforward.
The explicit import from qdrant_client.embed.embedder is clear and avoids any confusion about which functions are being tested.


1708-1717: Batch size checks appear consistent.
Ensuring that param_storage["batch_size"] matches inference_batch_size after each embedding call confirms correct option propagation. No issues found.


1755-1818: Test coverage for multimodal embedding is effective.
The assertions for text and image calls, as well as final scroll checks, confirm that embeddings are applied and stored properly. This test is thorough for verifying the new multimodal logic.

qdrant_client/embed/embedder.py (5)

11-11: Import statement is consistent with other imports.
Including LateInteractionMultimodalEmbedding maintains parity with the newly supported multimodal embedding type.


20-20: Importing _LATE_INTERACTION_MULTIMODAL_EMBEDDING_MODELS is logical.
This keeps the embedder aligned with the newly declared dictionary in fastembed_common.py.


45-47: New dictionary for multimodal embedding models is consistent.
Using defaultdict(list) mirrors the approach taken for other embedding model caches, helping to keep the embedder interface uniform.


236-265: Multimodal check in embed is well-structured.
Raising an error if both texts and images are provided prevents conflicting usage, and the new branch is integrated cleanly among other embedding checks.


266-375: Implementation of _embed_late_interaction_multimodal is robust.
It correctly enforces exclusive text-versus-image input. Additionally, reusing the shared pattern for model retrieval maintains code consistency.

qdrant_client/embed/model_embedder.py (3)

13-13: LGTM! Import changes align with multimodal support.

The addition of NumericVectorStruct and _LATE_INTERACTION_MULTIMODAL_EMBEDDING_MODELS imports support the new multimodal embedding functionality.

Also applies to: 21-21


283-296: Return type change improves type safety.

The return type change from models.VectorStruct to NumericVectorStruct provides better type safety and aligns with the actual return values in the implementation.


334-396:

Details

❓ Verification inconclusive

Verify the handling of mixed text and image inputs.

The embed method has been updated to handle both text and image inputs by tracking their types separately. However, we should verify that this works correctly with mixed inputs.


🏁 Script executed:

#!/bin/bash
# Search for test cases that verify mixed text/image input handling
rg -A 5 "def test.*mixed.*input" tests/
ast-grep --pattern 'def test_$_($_) {
  $$$
  texts = $$$
  images = $$$
  $$$
}'

Length of output: 125


Mixed Input Handling Requires Manual Verification
The updated embed method distinguishes between text and image inputs by grouping them based on type. However, our automated search did not yield any test cases specifically verifying mixed input handling. Please manually verify that the logic preserves the ordering and correctly processes a batch with both text and image objects. If possible, consider adding test cases to cover this mixed scenario.

@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

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9386868 and 940088d.

⛔ Files ignored due to path filters (1)
  • poetry.lock is excluded by !**/*.lock
📒 Files selected for processing (2)
  • pyproject.toml (1 hunks)
  • qdrant_client/embed/embedder.py (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • pyproject.toml
🧰 Additional context used
🧬 Code Definitions (1)
qdrant_client/embed/embedder.py (2)
qdrant_client/embed/model_embedder.py (1)
  • embed (334-381)
tests/embed_tests/test_local_inference.py (2)
  • embed_text (1737-1740)
  • embed_image (1742-1745)
🪛 GitHub Actions: type-checkers
qdrant_client/embed/embedder.py

[error] 258-258: Argument 1 to '_embed_dense_image' of 'Embedder' has incompatible type 'list[Any] | None'; expected 'list[Any]'

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Redirect rules - poetic-froyo-8baba7
  • GitHub Check: Header rules - poetic-froyo-8baba7
  • GitHub Check: Pages changed - poetic-froyo-8baba7
🔇 Additional comments (8)
qdrant_client/embed/embedder.py (8)

10-11: LGTM on the new imports.

The code correctly imports the new LateInteractionMultimodalEmbedding class and the _LATE_INTERACTION_MULTIMODAL_EMBEDDING_MODELS constant needed for the multimodal embedding support.

Also applies to: 20-20


45-47: LGTM on the new instance variable.

The new dictionary to store instances of multimodal embedding models follows the same pattern as other model dictionaries in the class, maintaining consistency.


158-190: LGTM on the new model initialization method.

The get_or_init_late_interaction_multimodal_model method follows the same pattern as other model initialization methods in the class, maintaining consistency and reusing the same approach for model caching and initialization.


268-294: LGTM on the extracted text embedding method.

The _embed_dense_text method extracts the dense text embedding logic from the main embed method, improving code organization and readability.


295-324: LGTM on the extracted sparse text embedding method.

The _embed_sparse_text method extracts the sparse text embedding logic from the main embed method, improving code organization and readability.


326-346: LGTM on the extracted late interaction text embedding method.

The _embed_late_interaction_text method extracts the late interaction text embedding logic from the main embed method, improving code organization.


348-376: LGTM on the new multimodal embedding method.

The _embed_late_interaction_multimodal method correctly handles the new multimodal embedding functionality, following the same pattern as other embedding methods. It properly checks that only one input type (text or images) is provided.


378-390: LGTM on the extracted image embedding method.

The _embed_dense_image method extracts the image embedding logic from the main embed method, improving code organization.

Comment thread qdrant_client/embed/embedder.py

@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

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 940088d and 45a0bfd.

📒 Files selected for processing (1)
  • qdrant_client/embed/embedder.py (5 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
qdrant_client/embed/embedder.py (4)
qdrant_client/embed/model_embedder.py (1)
  • embed (334-381)
qdrant_client/async_qdrant_fastembed.py (1)
  • query (580-654)
qdrant_client/qdrant_fastembed.py (1)
  • query (626-710)
tests/embed_tests/test_local_inference.py (2)
  • embed_text (1737-1740)
  • embed_image (1742-1745)
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Redirect rules - poetic-froyo-8baba7
  • GitHub Check: Header rules - poetic-froyo-8baba7
  • GitHub Check: Pages changed - poetic-froyo-8baba7
  • GitHub Check: Python 3.13 test
🔇 Additional comments (6)
qdrant_client/embed/embedder.py (6)

11-11: Good addition of the new embedding types

The imports have been correctly expanded to include the new LateInteractionMultimodalEmbedding class and the _LATE_INTERACTION_MULTIMODAL_EMBEDDING_MODELS constant. This enables the multimodal embedding functionality.

Also applies to: 20-20


45-47: Consistent implementation of model dictionary for multimodal embeddings

The instance variable for storing multimodal embedding models follows the same pattern as the other model dictionaries in the class, which is good for consistency and maintainability.


158-190: Well-structured initialization method for multimodal embeddings

The get_or_init_late_interaction_multimodal_model method correctly implements the model initialization pattern consistent with other similar methods in the class. The validation of supported models and caching mechanism is properly implemented.


236-268: Improved code organization with specialized embedding methods

Breaking down the embedding logic into specialized private methods improves code organization and readability. The branching logic for different model types is clear and well-structured.

The assertion on line 258 correctly ensures type safety for the images parameter, addressing the issue that was previously flagged in the pipeline.


381-393: Good implementation of image embedding method

The _embed_dense_image method is well-implemented and follows the same pattern as the other embedding methods, maintaining consistency throughout the codebase.


271-349: Excellent refactoring of embedding methods

The extraction of embedding logic into separate methods for each embedding type is a significant improvement. This approach:

  1. Makes the code more modular and easier to maintain
  2. Improves readability by grouping related functionality
  3. Facilitates testing each embedding type in isolation
  4. Simplifies future extensions to support additional embedding types

This is a great example of the Single Responsibility Principle in action.

Comment thread qdrant_client/embed/embedder.py Outdated
@joein joein force-pushed the fastembed-0.6.0 branch from 121d335 to 5095688 Compare April 7, 2025 14:22

@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: 0

🧹 Nitpick comments (1)
qdrant_client/embed/embedder.py (1)

257-259: Good mypy type checking assistance

The assertion helps the static type checker understand that images is definitely not None in this code path, preventing false positive errors.

Consider adding an explanatory message to the assertion for better debugging:

-            assert (
-                images is not None
-            )  # just to satisfy mypy which can't infer it from the previous conditions
+            assert images is not None, "Images must be provided at this point - this assertion helps mypy"
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 45a0bfd and 121d335.

📒 Files selected for processing (1)
  • qdrant_client/embed/embedder.py (5 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
qdrant_client/embed/embedder.py (2)
qdrant_client/http/models/models.py (1)
  • SparseVector (2580-2586)
tests/embed_tests/test_local_inference.py (2)
  • embed_text (1737-1740)
  • embed_image (1742-1745)
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Redirect rules - poetic-froyo-8baba7
  • GitHub Check: Header rules - poetic-froyo-8baba7
  • GitHub Check: Pages changed - poetic-froyo-8baba7
  • GitHub Check: Python 3.13 test
🔇 Additional comments (7)
qdrant_client/embed/embedder.py (7)

11-11: Well-organized import additions for multimodal support

The imports for LateInteractionMultimodalEmbedding and _LATE_INTERACTION_MULTIMODAL_EMBEDDING_MODELS are correctly added to support the new multimodal embedding functionality.

Also applies to: 20-20


45-47: Good implementation of multimodal embeddings storage

The dictionary for storing multimodal embedding models follows the same pattern as the other model dictionaries, maintaining consistency throughout the codebase.


158-191: LGTM: get_or_init_late_interaction_multimodal_model implementation

The implementation follows the established pattern of other model initialization methods in the class:

  1. Validates the model name against supported models
  2. Creates an options dictionary with sensible defaults
  3. Checks for existing model instances before creating new ones
  4. Properly stores and returns the model instance

This approach maintains consistency and prevents duplicate model instantiation.


233-269: Excellent refactoring of the embed method

The embed method has been significantly improved by:

  1. Using a clear validation check at the beginning
  2. Breaking down embedding logic into specialized private methods
  3. Adding proper type hints and assertions for static type checking
  4. Extending support for multimodal embeddings

The code now better handles the different embedding scenarios while maintaining clean separation of concerns.


351-367: Appropriately implemented multimodal text embedding method

The implementation properly handles embedding text inputs for multimodal models by:

  1. Getting or initializing the appropriate model
  2. Using a specialized embed_text method rather than the generic embed
  3. Correctly converting the embeddings to the expected format

368-382: Appropriately implemented multimodal image embedding method

The implementation correctly handles embedding image inputs for multimodal models by:

  1. Getting or initializing the appropriate model
  2. Using a specialized embed_image method
  3. Properly converting the embeddings to the expected format

271-297: Well-structured private helper methods

The refactoring into specialized private helper methods is excellent and follows good software engineering practices:

  1. Each method has a single responsibility
  2. Consistent patterns are maintained across all methods
  3. Type annotations are properly used
  4. The code is more maintainable and easier to understand

This approach will make it easier to add more embedding types in the future.

Also applies to: 298-328, 329-350, 383-395

@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: 0

🧹 Nitpick comments (3)
tests/embed_tests/test_local_inference.py (3)

1722-1725: Consider using importlib to check for the availability of fastembed.

The static analyzer flagged that fastembed is imported but not used directly in the code. Consider using importlib.util.find_spec for a cleaner way to check if a module is available.

-    try:
-        import fastembed
-    except ImportError:
-        pytest.skip("FastEmbed is not installed, skipping")
+    import importlib.util
+    if importlib.util.find_spec("fastembed") is None:
+        pytest.skip("FastEmbed is not installed, skipping")
🧰 Tools
🪛 Ruff (0.8.2)

1722-1722: fastembed imported but unused; consider using importlib.util.find_spec to test for availability

(F401)


1726-1746: Good mock design with tracking counters.

The mock implementation properly tracks call counts and provides predefined embeddings. However, using class variables for tracking calls could potentially lead to issues if multiple tests use this fixture and run in parallel or if tests depend on the order of execution.

 class LateInteractionMultimodalEmbeddingMock:
-    text_calls = 0
-    image_calls = 0
     num_image_columns = 10
     num_text_columns = 20
     text_embedding = np.array([[0.1, 0.2, 0.3]] * num_text_columns)
     image_embedding = np.array([[0.1, 0.2, 0.3]] * num_image_columns)

     def __init__(self, *args, **kwargs):
-        pass
+        self.text_calls = 0
+        self.image_calls = 0

     def embed_text(self, documents, *args, **kwargs):
-        LateInteractionMultimodalEmbeddingMock.text_calls += 1
+        self.text_calls += 1
         for _ in documents:
             yield self.text_embedding

     def embed_image(self, images, *args, **kwargs):
-        LateInteractionMultimodalEmbeddingMock.image_calls += 1
+        self.image_calls += 1
         for _ in images:
             yield self.image_embedding

1755-1817: Well-structured test for multimodal embedding.

The test thoroughly validates the multimodal embedding functionality by checking:

  1. Batching behavior (via call counts)
  2. Result shapes and dimensions
  3. Expected embedding values

The vectors configuration with multivector_config matches the expected multimodal embedding architecture. However, the test assertions currently rely on class variables, which should be adjusted if you change to instance variables as suggested above.

If you change to instance variables in the mock:

-    assert mock_cls.text_calls == 1  # assert that text records were assembled into batches
-    assert mock_cls.image_calls == 1
+    # Get the mock instance from the embedder
+    mock_instance = local_client._model_embedder.embedder.late_interaction_embedding_models["Qdrant/colpali-v1.3-fp16"][0]
+    assert mock_instance.text_calls == 1  # assert that text records were assembled into batches
+    assert mock_instance.image_calls == 1
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 121d335 and 5095688.

⛔ Files ignored due to path filters (1)
  • poetry.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • pyproject.toml (1 hunks)
  • qdrant_client/embed/embedder.py (4 hunks)
  • qdrant_client/embed/model_embedder.py (7 hunks)
  • qdrant_client/fastembed_common.py (2 hunks)
  • tests/embed_tests/test_local_inference.py (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • pyproject.toml
  • qdrant_client/fastembed_common.py
  • qdrant_client/embed/embedder.py
  • qdrant_client/embed/model_embedder.py
🧰 Additional context used
🧬 Code Definitions (1)
tests/embed_tests/test_local_inference.py (12)
qdrant_client/embed/model_embedder.py (1)
  • embed (334-381)
qdrant_client/embed/embedder.py (1)
  • embed (225-270)
tests/congruence_tests/test_discovery.py (1)
  • local_client (38-42)
tests/congruence_tests/test_facet.py (1)
  • local_client (35-38)
tests/congruence_tests/test_search_distance_matrix.py (1)
  • local_client (27-30)
tests/congruence_tests/test_sparse_discovery.py (1)
  • local_client (37-48)
qdrant_client/local/qdrant_local.py (3)
  • upsert (773-778)
  • create_collection (988-1039)
  • scroll (730-749)
qdrant_client/local/local_collection.py (2)
  • upsert (2292-2328)
  • scroll (1870-1904)
qdrant_client/async_qdrant_client.py (3)
  • upsert (1513-1583)
  • create_collection (2232-2319)
  • scroll (1354-1421)
qdrant_client/qdrant_remote.py (3)
  • upsert (1840-1918)
  • create_collection (2703-2822)
  • scroll (1648-1736)
qdrant_client/qdrant_client.py (4)
  • upsert (1561-1638)
  • QdrantClient (28-3163)
  • create_collection (2311-2399)
  • scroll (1399-1467)
qdrant_client/async_qdrant_remote.py (3)
  • upsert (1616-1685)
  • create_collection (2446-2548)
  • scroll (1443-1519)
🪛 Ruff (0.8.2)
tests/embed_tests/test_local_inference.py

1722-1722: fastembed imported but unused; consider using importlib.util.find_spec to test for availability

(F401)

⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: Redirect rules - poetic-froyo-8baba7
  • GitHub Check: Header rules - poetic-froyo-8baba7
  • GitHub Check: Pages changed - poetic-froyo-8baba7
  • GitHub Check: Python 3.13.x on ubuntu-latest test
  • GitHub Check: Python 3.12.x on ubuntu-latest test
  • GitHub Check: Python 3.11.x on ubuntu-latest test
  • GitHub Check: Python 3.10.x on ubuntu-latest test
  • GitHub Check: Python 3.9.x on ubuntu-latest test
  • GitHub Check: Python 3.13 test
🔇 Additional comments (2)
tests/embed_tests/test_local_inference.py (2)

1718-1753: LGTM! Well-structured fixture for mocking multimodal embeddings.

The fixture properly mocks the LateInteractionMultimodalEmbedding class with tracking capabilities for both text and image embeddings, and correctly restores the original class after the test. The mock implementation provides fixed embedding outputs which enables deterministic testing.

🧰 Tools
🪛 Ruff (0.8.2)

1722-1722: fastembed imported but unused; consider using importlib.util.find_spec to test for availability

(F401)


1708-1708: Code formatting change to single-line upsert.

This is a minor formatting change that consolidates the upsert method call into a single line, which improves readability.

@joein joein force-pushed the fastembed-0.6.0 branch from 53a8379 to 135bdd3 Compare April 7, 2025 15:58

@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: 0

🧹 Nitpick comments (5)
tests/embed_tests/test_local_inference.py (3)

1722-1725: Consider using importlib.util.find_spec for package availability check

The static analysis tool flagged the fastembed import as unused. While your approach works, using importlib.util.find_spec is the recommended way to check for package availability.

-    try:
-        import fastembed
-    except ImportError:
-        pytest.skip("FastEmbed is not installed, skipping")
+    from importlib.util import find_spec
+    if find_spec("fastembed") is None:
+        pytest.skip("FastEmbed is not installed, skipping")
🧰 Tools
🪛 Ruff (0.8.2)

1722-1722: fastembed imported but unused; consider using importlib.util.find_spec to test for availability

(F401)


1726-1746: Consider resetting class variables between tests

The mock class uses class variables to track call counts, which could lead to test failures if multiple tests run sequentially or in parallel. Consider adding a reset method or using instance variables instead.

class LateInteractionMultimodalEmbeddingMock:
-    text_calls = 0
-    image_calls = 0
     num_image_columns = 10
     num_text_columns = 20
     text_embedding = np.array([[0.1, 0.2, 0.3]] * num_text_columns)
     image_embedding = np.array([[0.1, 0.2, 0.3]] * num_image_columns)

     def __init__(self, *args, **kwargs):
-        pass
+        self.text_calls = 0
+        self.image_calls = 0

     def embed_text(self, documents, *args, **kwargs):
-        LateInteractionMultimodalEmbeddingMock.text_calls += 1
+        self.text_calls += 1
         for _ in documents:
             yield self.text_embedding

     def embed_image(self, images, *args, **kwargs):
-        LateInteractionMultimodalEmbeddingMock.image_calls += 1
+        self.image_calls += 1
         for _ in images:
             yield self.image_embedding

1801-1802: Update assertion to handle instance variables

If you implement the suggested change to use instance variables in the mock, you'll need to update these assertions as well.

-    assert mock_cls.text_calls == 1  # assert that text records were assembled into batches
-    assert mock_cls.image_calls == 1
+    # Get the instance used inside the embedder
+    mock_instance = local_client._model_embedder.embedder.late_interaction_multimodal_embedding_models["Qdrant/colpali-v1.3-fp16"][0].model
+    assert mock_instance.text_calls == 1  # assert that text records were assembled into batches
+    assert mock_instance.image_calls == 1
qdrant_client/embed/embedder.py (2)

258-261: Consider simplifying the assertion for type checking

The assertion is used solely for type checking purposes. Consider using a simpler check or relying on the conditional logic.

-            assert (
-                images is not None
-            )  # just to satisfy mypy which can't infer it from the previous conditions
+            # At this point, images must be not None due to the previous conditions

352-383: Consider adding is_query parameter for consistency

Unlike the other embedding methods, the multimodal methods don't have an is_query parameter. If the multimodal embedding doesn't need different behavior for queries vs. documents, it would be good to add a comment explaining this.

def _embed_late_interaction_multimodal_text(
    self,
    texts: list[str],
    model_name: str,
    options: Optional[dict[str, Any]],
    batch_size: int,
+    is_query: bool = False,  # Unused, but added for API consistency
) -> list[list[list[float]]]:
+    # Multimodal embeddings don't have different behavior for queries
    embedding_model_inst = self.get_or_init_late_interaction_multimodal_model(
        model_name=model_name, **options or {}
    )
    return [
        embedding.tolist()
        for embedding in embedding_model_inst.embed_text(
            documents=texts, batch_size=batch_size
        )
    ]
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 53a8379 and 135bdd3.

⛔ Files ignored due to path filters (1)
  • poetry.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • pyproject.toml (1 hunks)
  • qdrant_client/embed/embedder.py (4 hunks)
  • qdrant_client/embed/model_embedder.py (7 hunks)
  • qdrant_client/fastembed_common.py (2 hunks)
  • tests/embed_tests/test_local_inference.py (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • pyproject.toml
  • qdrant_client/fastembed_common.py
  • qdrant_client/embed/model_embedder.py
🧰 Additional context used
🧬 Code Definitions (1)
tests/embed_tests/test_local_inference.py (12)
qdrant_client/embed/model_embedder.py (1)
  • embed (334-382)
qdrant_client/embed/embedder.py (1)
  • embed (225-270)
tests/congruence_tests/test_discovery.py (1)
  • local_client (38-42)
tests/congruence_tests/test_facet.py (1)
  • local_client (35-38)
tests/congruence_tests/test_search_distance_matrix.py (1)
  • local_client (27-30)
tests/congruence_tests/test_sparse_discovery.py (1)
  • local_client (37-48)
qdrant_client/local/qdrant_local.py (3)
  • upsert (773-778)
  • create_collection (988-1039)
  • scroll (730-749)
qdrant_client/local/local_collection.py (2)
  • upsert (2292-2328)
  • scroll (1870-1904)
qdrant_client/async_qdrant_client.py (3)
  • upsert (1513-1583)
  • create_collection (2232-2319)
  • scroll (1354-1421)
qdrant_client/async_qdrant_remote.py (3)
  • upsert (1616-1685)
  • create_collection (2446-2548)
  • scroll (1443-1519)
qdrant_client/qdrant_client.py (4)
  • upsert (1561-1638)
  • QdrantClient (28-3163)
  • create_collection (2311-2399)
  • scroll (1399-1467)
qdrant_client/qdrant_remote.py (3)
  • upsert (1840-1918)
  • create_collection (2703-2822)
  • scroll (1648-1736)
🪛 Ruff (0.8.2)
tests/embed_tests/test_local_inference.py

1722-1722: fastembed imported but unused; consider using importlib.util.find_spec to test for availability

(F401)

⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: Redirect rules - poetic-froyo-8baba7
  • GitHub Check: Header rules - poetic-froyo-8baba7
  • GitHub Check: Pages changed - poetic-froyo-8baba7
  • GitHub Check: Python 3.13.x on ubuntu-latest test
  • GitHub Check: Python 3.12.x on ubuntu-latest test
  • GitHub Check: Python 3.11.x on ubuntu-latest test
  • GitHub Check: Python 3.10.x on ubuntu-latest test
  • GitHub Check: Python 3.9.x on ubuntu-latest test
  • GitHub Check: Python 3.13 test
🔇 Additional comments (7)
tests/embed_tests/test_local_inference.py (3)

1719-1752: Good test fixture implementation for multimodal embedding

The mock class properly simulates the behavior of the LateInteractionMultimodalEmbedding class by tracking calls to text and image embedding methods and returning consistent embeddings.

🧰 Tools
🪛 Ruff (0.8.2)

1722-1722: fastembed imported but unused; consider using importlib.util.find_spec to test for availability

(F401)


1755-1817: Comprehensive test for multimodal embedding functionality

The test effectively validates the integration of multimodal embedding by:

  1. Creating points with both text and image vectors
  2. Verifying embedding method calls
  3. Checking embedding shapes and values
  4. Testing with points containing only text vectors

This provides good coverage for the new functionality.


1708-1708: LGTM - Minor method call format fix

The simplified method call format improves readability.

qdrant_client/embed/embedder.py (4)

46-48: Appropriate addition of multimodal embedding model storage

The dictionary for storing multimodal embedding model instances follows the same pattern as the other model types, maintaining consistency in the codebase.


159-192: Well-implemented model initialization method

The get_or_init_late_interaction_multimodal_model method follows the established pattern of the other model initialization methods, checking for existing instances with matching options before creating new ones, which is efficient.


237-269: Good refactoring of embedding logic

The refactoring of the embed method improves code organization by separating different embedding types into specialized private methods while maintaining the same interface.


272-351: Well-structured private methods for different embedding types

The extracted private methods for different embedding types follow a consistent pattern and preserve the original functionality, making the code more modular and easier to maintain.

@joein joein force-pushed the fastembed-0.6.0 branch from 135bdd3 to f5037bc Compare April 15, 2025 14:17

@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: 0

🧹 Nitpick comments (5)
qdrant_client/fastembed_common.py (1)

16-16: Remove unused _MultitaskTextEmbedding import
Ruff flags this import as unused. Consider removing if not needed.

-    from fastembed.text.multitask_embedding import JinaEmbeddingV3 as _MultitaskTextEmbedding
🧰 Tools
🪛 Ruff (0.8.2)

16-16: fastembed.text.multitask_embedding.JinaEmbeddingV3 imported but unused; consider using importlib.util.find_spec to test for availability

(F401)

tests/embed_tests/test_local_inference.py (1)

1718-1752: Mock fixture for LateInteractionMultimodalEmbedding
Providing a mock class is a good approach for focused testing. However, the import of fastembed in line 1721 is not otherwise referenced, prompting Ruff to flag it as unused. Also, note that the static counters (text_calls and image_calls) might introduce concurrency issues if tests run in parallel.

Consider removing or conditionally importing fastembed using importlib.util.find_spec, and either resetting the static counters or using instance-level counters if concurrency becomes a concern:

-        import fastembed
+        # Optionally use importlib.util.find_spec to check for module availability if needed
🧰 Tools
🪛 Ruff (0.8.2)

1721-1721: fastembed imported but unused; consider using importlib.util.find_spec to test for availability

(F401)

qdrant_client/embed/embedder.py (3)

157-189: Well-implemented model initialization method for multimodal embeddings.

This method follows the established pattern from other model initialization methods:

  1. Validates the model name against supported models
  2. Creates a consistent options dictionary
  3. Checks for an existing model in cache
  4. Creates and caches a new model instance when needed

Consider adding a docstring to explain the purpose and behavior of this method, especially since it handles a new model type.


341-356: New method for multimodal text embedding correctly implemented.

The implementation properly initializes the multimodal model and calls the appropriate embed_text method. The return type annotation correctly indicates the expected structure of embeddings.

This method doesn't handle the is_query parameter unlike the other text embedding methods. If this is by design, consider adding a comment explaining this difference.


270-385: Good separation of embedding logic into specialized methods.

Breaking down the embedding logic into specialized private methods improves code organization and readability. Each method follows a consistent pattern and handles its specific embedding scenario appropriately.

Consider adding docstrings to these private methods to document their purpose, parameters, and return values. This would be especially helpful for understanding the differences between the various embedding types.

 def _embed_dense_text(
     self,
     texts: list[str],
     model_name: str,
     options: Optional[dict[str, Any]],
     is_query: bool,
     batch_size: int,
 ) -> list[list[float]]:
+    """
+    Embed text documents using a dense embedding model.
+    
+    Args:
+        texts: List of text documents to embed
+        model_name: Name of the embedding model to use
+        options: Optional model initialization options
+        is_query: Whether the documents are queries (for retrieval)
+        batch_size: Batch size for embedding
+        
+    Returns:
+        List of dense vector embeddings
+    """
     embedding_model_inst = self.get_or_init_model(model_name=model_name, **options or {})

     if not is_query:

Similar docstrings should be added to all other private embedding methods.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 135bdd3 and f5037bc.

⛔ Files ignored due to path filters (1)
  • poetry.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • qdrant_client/embed/embedder.py (4 hunks)
  • qdrant_client/embed/model_embedder.py (7 hunks)
  • qdrant_client/fastembed_common.py (2 hunks)
  • tests/embed_tests/test_local_inference.py (3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
qdrant_client/embed/embedder.py (3)
qdrant_client/embed/model_embedder.py (1)
  • embed (334-382)
qdrant_client/http/models/models.py (1)
  • SparseVector (2687-2693)
tests/embed_tests/test_local_inference.py (2)
  • embed_text (1736-1739)
  • embed_image (1741-1744)
qdrant_client/fastembed_common.py (1)
qdrant_client/http/models/models.py (1)
  • Distance (650-661)
🪛 Ruff (0.8.2)
qdrant_client/fastembed_common.py

16-16: fastembed.text.multitask_embedding.JinaEmbeddingV3 imported but unused; consider using importlib.util.find_spec to test for availability

(F401)

tests/embed_tests/test_local_inference.py

1721-1721: fastembed imported but unused; consider using importlib.util.find_spec to test for availability

(F401)

⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: Redirect rules - poetic-froyo-8baba7
  • GitHub Check: Header rules - poetic-froyo-8baba7
  • GitHub Check: Pages changed - poetic-froyo-8baba7
  • GitHub Check: Python 3.13.x on ubuntu-latest test
  • GitHub Check: Python 3.12.x on ubuntu-latest test
  • GitHub Check: Python 3.11.x on ubuntu-latest test
  • GitHub Check: Python 3.10.x on ubuntu-latest test
  • GitHub Check: Python 3.9.x on ubuntu-latest test
🔇 Additional comments (18)
qdrant_client/fastembed_common.py (3)

14-14: Added import for LateInteractionMultimodalEmbedding
The new import enhances the file to recognize and support multimodal embedding.


23-23: Consistent fallback on import error
Assigning LateInteractionMultimodalEmbedding = None mirrors the existing pattern for optional imports and maintains graceful handling when the library is unavailable.


71-78: Initialization of _LATE_INTERACTION_MULTIMODAL_EMBEDDING_MODELS
This dictionary correctly mirrors the pattern used by other embedding model dictionaries and appropriately defaults to {} when the class is unavailable.

tests/embed_tests/test_local_inference.py (2)

6-6: New import of qdrant_client.embed.embedder
This import is necessary for mocking and testing the new multimodal embedding behavior.


1754-1817: New test_embed_multimodal test coverage
This test thoroughly validates text and image embeddings for the new multimodal embedding model. The logic to verify vector shapes and calls is appropriate and aids in confirming correct behavior and ordering.

qdrant_client/embed/model_embedder.py (8)

13-13: Additional imports for numeric vector types
Importing NumericVector and NumericVectorStruct clarifies the codebase’s type usage.


21-21: Added _LATE_INTERACTION_MULTIMODAL_EMBEDDING_MODELS to imports
Extends the embedding logic to handle late interaction multimodal support in the model embedder.


284-285: Return type changed to NumericVectorStruct
Switching from models.VectorStruct to NumericVectorStruct provides a more precise type signature and aligns with the rest of the numeric vector workflow.


314-316: Explicit check for inference object types
Guarding against non-inference objects here prevents accidental attempts to drain non-inference embeddings.


335-339: Refined batching logic and docstring
Separating text and image data in the same method is clearly explained in the docstring. The approach efficiently handles multimodal objects while preserving ordering.


386-390: Addition of _LATE_INTERACTION_MULTIMODAL_EMBEDDING_MODELS to supported sets
This ensures that the model embedder recognizes the new multimodal models as valid.


431-433: Extended model sets for InferenceObject resolution
Unpacking the new model references in the conditional broadens the coverage for recognized inference objects.


437-438: Explicit error for Late Interaction Multimodal + InferenceObject
Raising an exception for unsupported scenarios avoids silent failures and communicates expected usage constraints.

qdrant_client/embed/embedder.py (5)

8-19: Imports added for multimodal embedding support.

The new imports for LateInteractionMultimodalEmbedding and _LATE_INTERACTION_MULTIMODAL_EMBEDDING_MODELS are correctly included to support the multimodal embedding functionality.


44-46: Added cache for multimodal embedding models.

The new cache implementation is well-structured and follows the same pattern as the existing model caches, using the appropriate type annotations.


358-371: New method for multimodal image embedding correctly implemented.

The implementation correctly handles multimodal image embedding by initializing the appropriate model and calling the embed_image method. The return value is properly formatted as expected.


256-258: Good type checking support with assert statement.

The assert statement helps mypy understand that images cannot be None at this point, avoiding a type error. This shows good attention to static type checking requirements.


235-268:

Details

❓ Verification inconclusive

Good refactoring of the embed method with clear delegation.

The embed method has been nicely refactored to delegate to specialized private methods based on model type and input modality. This improves code organization and maintainability.

Note that the is_query parameter isn't passed to the multimodal embedding methods. Is this intentional? If multimodal models don't support query mode, consider adding a comment to clarify this design decision.


🏁 Script executed:

#!/bin/bash
# Check for query_embed or similar methods in LateInteractionMultimodalEmbedding

# Look for class definition and methods
ast-grep --pattern $'class LateInteractionMultimodalEmbedding {
  $$$
}'

# Specifically look for query embedding methods
ast-grep --pattern $'def query_embed($$$) {
  $$$
}'

# Look for usages or tests of is_query with multimodal embeddings
rg -A 5 "is_query.*multimodal" -g "*.py"

Length of output: 170


Action Required: Verify Multimodal Query Behavior

Good refactoring overall—the delegation to specialized methods is clean. However, the is_query parameter isn’t forwarded when processing multimodal inputs. Our initial verification (searching for query-specific implementations in the multimodal embedding methods) did not reveal any handling of is_query. Please verify that this omission is intentional (i.e. multimodal models don’t support query mode) and, if so, add a comment clarifying the design decision. If query mode should be supported, then the methods might need updating.

@joein joein merged commit a0bbada into dev Apr 15, 2025
joein added a commit that referenced this pull request Apr 22, 2025
* new: support multimodal embeddings

* fix: fix embed when model name is present in both text and image supported models

* fix: fix type checkers

* refactor: split multimodal embed into embed text and embed image

* chore: comment batch separation

* chore: rollback formatting
@coderabbitai coderabbitai Bot mentioned this pull request Aug 14, 2025
2 tasks
@coderabbitai coderabbitai Bot mentioned this pull request Oct 23, 2025
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.

2 participants