diff --git a/python/semantic_kernel/data/text_search.py b/python/semantic_kernel/data/text_search.py index 8272af63b18b..f0a29cf3055f 100644 --- a/python/semantic_kernel/data/text_search.py +++ b/python/semantic_kernel/data/text_search.py @@ -275,8 +275,8 @@ async def search_wrapper(**kwargs: Any) -> Sequence[str]: try: inner_options = create_options(SearchOptions, deepcopy(options), **kwargs) except ValidationError: - # this usually only happens when the kwargs are invalid, so blank options in this case. - inner_options = SearchOptions() + # Ignore invalid invocation overrides and retain the configured options. + inner_options = create_options(SearchOptions, deepcopy(options)) inner_options.filter = update_func(filter=inner_options.filter, parameters=parameters, **kwargs) try: results = await self.search( diff --git a/python/semantic_kernel/data/vector.py b/python/semantic_kernel/data/vector.py index 9a4e68c80817..964e9d92bec7 100644 --- a/python/semantic_kernel/data/vector.py +++ b/python/semantic_kernel/data/vector.py @@ -2095,8 +2095,8 @@ async def search_wrapper(**kwargs: Any) -> Sequence[str]: try: inner_options = create_options(self.options_class, deepcopy(options), **kwargs) except ValidationError: - # this usually only happens when the kwargs are invalid, so blank options in this case. - inner_options = self.options_class() + # Ignore invalid invocation overrides and retain the configured options. + inner_options = create_options(self.options_class, deepcopy(options)) inner_options.filter = update_func(filter=inner_options.filter, parameters=parameters, **kwargs) match search_type: case SearchType.VECTOR: diff --git a/python/tests/unit/data/test_text_search.py b/python/tests/unit/data/test_text_search.py index 7275a0fbd883..773b3b523fc5 100644 --- a/python/tests/unit/data/test_text_search.py +++ b/python/tests/unit/data/test_text_search.py @@ -117,6 +117,19 @@ async def test_create_kernel_function_inner_with_options(kernel: Kernel): assert results.value == ["test"] +async def test_create_search_function_preserves_filter_for_invalid_invocation_options(kernel: Kernel): + test_search = TestSearch() + search_filter = "lambda x: x.category == 'books'" + kernel_function = test_search.create_search_function(filter=search_filter) + + with patch.object(test_search, "search") as mock_search: + mock_search.return_value = KernelSearchResults(results=desync_list([])) + await kernel_function.invoke(kernel, KernelArguments(query="query", top=0)) + + mock_search.assert_awaited_once() + assert mock_search.await_args.kwargs["filter"] == search_filter + + async def test_create_kernel_function_inner_with_other_options_type(kernel: Kernel): test_search = TestSearch() diff --git a/python/tests/unit/data/test_vector_search_base.py b/python/tests/unit/data/test_vector_search_base.py index 63eeb5ef38d6..dffc280b2d66 100644 --- a/python/tests/unit/data/test_vector_search_base.py +++ b/python/tests/unit/data/test_vector_search_base.py @@ -1,9 +1,14 @@ # Copyright (c) Microsoft. All rights reserved. +from unittest.mock import patch + import pytest -from semantic_kernel.data.vector import VectorSearch, VectorSearchOptions, VectorSearchProtocol +from semantic_kernel import Kernel +from semantic_kernel.data.vector import KernelSearchResults, VectorSearch, VectorSearchOptions, VectorSearchProtocol +from semantic_kernel.functions import KernelArguments +from semantic_kernel.utils.list_handler import desync_list async def test_search(vector_store_record_collection: VectorSearch): @@ -24,3 +29,17 @@ async def test_get_vector_search_results(vector_store_record_collection: VectorS ): assert result.record == results[0] if include_vectors else {"id": "test_id", "content": "test_content"} break + + +async def test_create_search_function_preserves_filter_for_invalid_invocation_options( + kernel: Kernel, vector_store_record_collection: VectorSearch +): + search_filter = "lambda x: x.content == 'test_content'" + kernel_function = vector_store_record_collection.create_search_function(filter=search_filter) + + with patch.object(type(vector_store_record_collection), "search") as mock_search: + mock_search.return_value = KernelSearchResults(results=desync_list([])) + await kernel_function.invoke(kernel, KernelArguments(query="query", top=0)) + + mock_search.assert_awaited_once() + assert mock_search.await_args.kwargs["filter"] == search_filter