diff --git a/meilisearch_python_sdk/_client.py b/meilisearch_python_sdk/_client.py index d5de75fc..dd570b39 100644 --- a/meilisearch_python_sdk/_client.py +++ b/meilisearch_python_sdk/_client.py @@ -623,15 +623,18 @@ async def multi_search( >>> search_results = await client.search(queries) """ url = "multi-search" - if federation: - processed_queries = [] - for query in queries: - q = query.model_dump(by_alias=True) + processed_queries = [] + for query in queries: + q = query.model_dump(by_alias=True) + + if query.retrieve_vectors is None: + del q["retrieveVectors"] + + if federation: del q["limit"] del q["offset"] - processed_queries.append(q) - else: - processed_queries = [x.model_dump(by_alias=True) for x in queries] + + processed_queries.append(q) if federation: federation_payload = federation.model_dump(by_alias=True) @@ -1479,15 +1482,18 @@ def multi_search( >>> search_results = client.search(queries) """ url = "multi-search" - if federation: - processed_queries = [] - for query in queries: - q = query.model_dump(by_alias=True) + processed_queries = [] + for query in queries: + q = query.model_dump(by_alias=True) + + if query.retrieve_vectors is None: + del q["retrieveVectors"] + + if federation: del q["limit"] del q["offset"] - processed_queries.append(q) - else: - processed_queries = [x.model_dump(by_alias=True) for x in queries] + + processed_queries.append(q) if federation: federation_payload = federation.model_dump(by_alias=True) diff --git a/meilisearch_python_sdk/index.py b/meilisearch_python_sdk/index.py index f8138ed6..f3e14775 100644 --- a/meilisearch_python_sdk/index.py +++ b/meilisearch_python_sdk/index.py @@ -721,6 +721,7 @@ async def search( vector: list[float] | None = None, hybrid: Hybrid | None = None, locales: list[str] | None = None, + retrieve_vectors: bool | None = None, ) -> SearchResults: """Search the index. @@ -781,6 +782,7 @@ async def search( with caution. locales: Specifies the languages for the search. This parameter can only be used with Milisearch >= v1.10.0. Defaults to None letting the Meilisearch pick. + retrieve_vectors: Return document vector data with search result. Returns: Results of the search @@ -824,6 +826,7 @@ async def search( hybrid=hybrid, ranking_score_threshold=ranking_score_threshold, locales=locales, + retrieve_vectors=retrieve_vectors, ) search_url = f"{self._base_url_with_uid}/search" @@ -986,6 +989,7 @@ async def facet_search( ranking_score_threshold: float | None = None, vector: list[float] | None = None, locales: list[str] | None = None, + retrieve_vectors: bool | None = None, ) -> FacetSearchResults: """Search the index. @@ -1038,6 +1042,7 @@ async def facet_search( with caution. locales: Specifies the languages for the search. This parameter can only be used with Milisearch >= v1.10.0. Defaults to None letting the Meilisearch pick. + retrieve_vectors: Return document vector data with search result. Returns: Results of the search @@ -1085,6 +1090,7 @@ async def facet_search( ranking_score_threshold=ranking_score_threshold, vector=vector, locales=locales, + retrieve_vectors=retrieve_vectors, ) search_url = f"{self._base_url_with_uid}/facet-search" @@ -5085,6 +5091,7 @@ def search( vector: list[float] | None = None, hybrid: Hybrid | None = None, locales: list[str] | None = None, + retrieve_vectors: bool | None = None, ) -> SearchResults: """Search the index. @@ -5145,6 +5152,7 @@ def search( with caution. locales: Specifies the languages for the search. This parameter can only be used with Milisearch >= v1.10.0. Defaults to None letting the Meilisearch pick. + retrieve_vectors: Return document vector data with search result. Returns: Results of the search @@ -5188,6 +5196,7 @@ def search( hybrid=hybrid, ranking_score_threshold=ranking_score_threshold, locales=locales, + retrieve_vectors=retrieve_vectors, ) if self._pre_search_plugins: @@ -5256,6 +5265,7 @@ def facet_search( ranking_score_threshold: float | None = None, vector: list[float] | None = None, locales: list[str] | None = None, + retrieve_vectors: bool | None = None, ) -> FacetSearchResults: """Search the index. @@ -5308,6 +5318,7 @@ def facet_search( with caution. locales: Specifies the languages for the search. This parameter can only be used with Milisearch >= v1.10.0. Defaults to None letting the Meilisearch pick. + retrieve_vectors: Return document vector data with search result. Returns: Results of the search @@ -5355,6 +5366,7 @@ def facet_search( ranking_score_threshold=ranking_score_threshold, vector=vector, locales=locales, + retrieve_vectors=retrieve_vectors, ) if self._pre_facet_search_plugins: @@ -8262,6 +8274,7 @@ def _process_search_parameters( vector: list[float] | None = None, hybrid: Hybrid | None = None, locales: list[str] | None = None, + retrieve_vectors: bool | None = None, ) -> JsonDict: if attributes_to_retrieve is None: attributes_to_retrieve = ["*"] @@ -8310,6 +8323,9 @@ def _process_search_parameters( if locales: body["locales"] = locales + if retrieve_vectors is not None: + body["retrieveVectors"] = retrieve_vectors + return body diff --git a/meilisearch_python_sdk/models/search.py b/meilisearch_python_sdk/models/search.py index d50ac97b..a882e0dd 100644 --- a/meilisearch_python_sdk/models/search.py +++ b/meilisearch_python_sdk/models/search.py @@ -70,6 +70,7 @@ class SearchParams(CamelBase): vector: list[float] | None = None hybrid: Hybrid | None = None locales: list[str] | None = None + retrieve_vectors: bool | None = None @field_validator("ranking_score_threshold", mode="before") # type: ignore[attr-defined] @classmethod diff --git a/meilisearch_python_sdk/models/settings.py b/meilisearch_python_sdk/models/settings.py index 91c40434..5db8c194 100644 --- a/meilisearch_python_sdk/models/settings.py +++ b/meilisearch_python_sdk/models/settings.py @@ -56,6 +56,7 @@ class OpenAiEmbedder(CamelBase): document_template: str | None = None document_template_max_bytes: int | None = None distribution: Distribution | None = None + binary_quantized: bool | None = None class HuggingFaceEmbedder(CamelBase): @@ -65,6 +66,8 @@ class HuggingFaceEmbedder(CamelBase): document_template: str | None = None document_template_max_bytes: int | None = None distribution: Distribution | None = None + dimensions: int | None = None + binary_quantized: bool | None = None class OllamaEmbedder(CamelBase): @@ -76,6 +79,7 @@ class OllamaEmbedder(CamelBase): document_template: str | None = None document_template_max_bytes: int | None = None distribution: Distribution | None = None + binary_quantized: bool | None = None class RestEmbedder(CamelBase): @@ -89,12 +93,16 @@ class RestEmbedder(CamelBase): headers: JsonDict | None = None request: JsonDict response: JsonDict + binary_quantized: bool | None = None class UserProvidedEmbedder(CamelBase): source: str = "userProvided" dimensions: int distribution: Distribution | None = None + document_template: str | None = None + document_template_max_bytes: int | None = None + binary_quantized: bool | None = None class Embedders(CamelBase): diff --git a/tests/conftest.py b/tests/conftest.py index b1a82908..0b1d5a3f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -295,15 +295,6 @@ async def default_search_key(async_client): return key -@pytest.fixture(scope="session", autouse=True) -async def enable_vector_search(base_url, ssl_verify): - async with HttpxAsyncClient( - base_url=base_url, headers={"Authorization": f"Bearer {MASTER_KEY}"}, verify=ssl_verify - ) as client: - await client.patch("/experimental-features", json={"vectorStore": True}) - yield - - @pytest.fixture(scope="session", autouse=True) async def enable_edit_by_function(base_url, ssl_verify): async with HttpxAsyncClient( diff --git a/tests/test_async_index.py b/tests/test_async_index.py index bf318bfa..78f3d3b2 100644 --- a/tests/test_async_index.py +++ b/tests/test_async_index.py @@ -139,7 +139,7 @@ async def test_get_settings_default( assert response.non_separator_tokens == [] assert response.search_cutoff_ms is None assert response.dictionary == [] - assert response.embedders is None + assert response.embedders == {} assert response.facet_search is True assert response.prefix_search == "indexingTime" @@ -233,7 +233,7 @@ async def test_reset_settings(async_empty_index, new_settings, default_ranking_r assert response.faceting.max_values_per_facet == 100 assert response.pagination.max_total_hits == 1000 assert response.proximity_precision is ProximityPrecision.BY_WORD - assert response.embedders is None + assert response.embedders == {} async def test_get_ranking_rules_default(async_empty_index, default_ranking_rules): diff --git a/tests/test_async_search.py b/tests/test_async_search.py index 34697077..2ff19ca9 100644 --- a/tests/test_async_search.py +++ b/tests/test_async_search.py @@ -506,6 +506,30 @@ async def test_vector_search(async_index_with_documents_and_vectors): assert len(response.hits) >= 1 +async def test_vector_search_retrieve_vectors(async_index_with_documents_and_vectors): + index = await async_index_with_documents_and_vectors() + response = await index.search( + "", + vector=[0.1, 0.2], + hybrid=Hybrid(semantic_ratio=1.0, embedder="default"), + retrieve_vectors=True, + ) + assert len(response.hits) >= 1 + assert response.hits[0].get("_vectors") is not None + + +async def test_vector_search_retrieve_vectors_false(async_index_with_documents_and_vectors): + index = await async_index_with_documents_and_vectors() + response = await index.search( + "", + vector=[0.1, 0.2], + hybrid=Hybrid(semantic_ratio=1.0, embedder="default"), + retrieve_vectors=False, + ) + assert len(response.hits) >= 1 + assert response.hits[0].get("_vectors") is None + + async def test_basic_facet_search(async_index_with_documents): index = await async_index_with_documents() update = await index.update_filterable_attributes(["genre"]) diff --git a/tests/test_index.py b/tests/test_index.py index 76b4febb..7f324a32 100644 --- a/tests/test_index.py +++ b/tests/test_index.py @@ -131,7 +131,7 @@ def test_get_settings_default( assert response.non_separator_tokens == [] assert response.search_cutoff_ms is None assert response.dictionary == [] - assert response.embedders is None + assert response.embedders == {} assert response.facet_search is True assert response.prefix_search == "indexingTime" @@ -225,7 +225,7 @@ def test_reset_settings(empty_index, new_settings, default_ranking_rules): assert response.faceting.max_values_per_facet == 100 assert response.pagination.max_total_hits == 1000 assert response.proximity_precision is ProximityPrecision.BY_WORD - assert response.embedders is None + assert response.embedders == {} def test_get_ranking_rules_default(empty_index, default_ranking_rules): diff --git a/tests/test_search.py b/tests/test_search.py index 11c598d7..481d7f95 100644 --- a/tests/test_search.py +++ b/tests/test_search.py @@ -483,6 +483,30 @@ def test_vector_search(index_with_documents_and_vectors): assert len(response.hits) >= 1 +def test_vector_search_retrieve_vectors(index_with_documents_and_vectors): + index = index_with_documents_and_vectors() + response = index.search( + "", + vector=[0.1, 0.2], + hybrid=Hybrid(semantic_ratio=1.0, embedder="default"), + retrieve_vectors=True, + ) + assert len(response.hits) >= 1 + assert response.hits[0].get("_vectors") is not None + + +def test_vector_search_retrieve_vectors_false(index_with_documents_and_vectors): + index = index_with_documents_and_vectors() + response = index.search( + "", + vector=[0.1, 0.2], + hybrid=Hybrid(semantic_ratio=1.0, embedder="default"), + retrieve_vectors=False, + ) + assert len(response.hits) >= 1 + assert response.hits[0].get("_vectors") is None + + def test_basic_facet_search(index_with_documents): index = index_with_documents() update = index.update_filterable_attributes(["genre"])