From 71226fe219d013e2e185c19dddcf52c92454a77b Mon Sep 17 00:00:00 2001 From: Nilesh Patil <128893479+nileshpatil6@users.noreply.github.com> Date: Fri, 8 May 2026 16:13:02 +0530 Subject: [PATCH 1/3] fix(redis): wrap IndexDefinition prefix in list and use _get_redis_key in JSON delete Two bugs in the Redis connector: 1. ensure_collection_exists passed prefix as a bare str to IndexDefinition. redis-py iterates over it character by character, producing a PREFIX clause with one entry per character instead of a single collection prefix. Fix: wrap in a list. 2. RedisJsonCollection._inner_delete called json().delete(key) with the raw caller-supplied key, but upsert stores keys as {collection_name}:{key}. The delete always targeted a non-existent key and silently returned 0. Fix: use self._get_redis_key(key), consistent with RedisHashsetCollection. Fixes #13894, #13904 --- python/semantic_kernel/connectors/redis.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/semantic_kernel/connectors/redis.py b/python/semantic_kernel/connectors/redis.py index 575624895aca..5ad4dd4c5fc6 100644 --- a/python/semantic_kernel/connectors/redis.py +++ b/python/semantic_kernel/connectors/redis.py @@ -278,7 +278,7 @@ async def ensure_collection_exists(self, **kwargs) -> None: raise VectorStoreOperationException("Invalid index type supplied.") fields = _definition_to_redis_fields(self.definition, self.collection_type) index_definition = IndexDefinition( - prefix=f"{self.collection_name}:", index_type=INDEX_TYPE_MAP[self.collection_type] + prefix=[f"{self.collection_name}:"], index_type=INDEX_TYPE_MAP[self.collection_type] ) await self.redis_database.ft(self.collection_name).create_index(fields, definition=index_definition, **kwargs) @@ -706,7 +706,7 @@ def _add_key(self, key: TKey, record: dict[str, Any]) -> dict[str, Any]: @override async def _inner_delete(self, keys: Sequence[str], **kwargs: Any) -> None: - await asyncio.gather(*[self.redis_database.json().delete(key, **kwargs) for key in keys]) + await asyncio.gather(*[self.redis_database.json().delete(self._get_redis_key(key), **kwargs) for key in keys]) @override def _serialize_dicts_to_store_models( From 1f578acfd83f3822d7dc347b33f3a2cbad55776a Mon Sep 17 00:00:00 2001 From: Nilesh Patil <128893479+nileshpatil6@users.noreply.github.com> Date: Fri, 8 May 2026 16:24:16 +0530 Subject: [PATCH 2/3] fix(redis): conditionally apply IndexDefinition prefix based on prefix_collection_name_to_key_names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When prefix_collection_name_to_key_names=False (the default for both RedisHashsetCollection and RedisJsonCollection), _get_redis_key() returns the raw key, so upsert stores e.g. "id1". The previous fix still unconditionally set prefix=["collection_name:"] in ensure_collection_exists, meaning the RediSearch index only matched prefixed keys while records were stored under raw keys — vector search would return nothing by default. Fix: only set the prefix when prefix_collection_name_to_key_names=True; otherwise create the IndexDefinition without a prefix so the index matches all keys regardless of naming scheme. --- python/semantic_kernel/connectors/redis.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/python/semantic_kernel/connectors/redis.py b/python/semantic_kernel/connectors/redis.py index 5ad4dd4c5fc6..006b1b9d97a2 100644 --- a/python/semantic_kernel/connectors/redis.py +++ b/python/semantic_kernel/connectors/redis.py @@ -277,9 +277,12 @@ async def ensure_collection_exists(self, **kwargs) -> None: return raise VectorStoreOperationException("Invalid index type supplied.") fields = _definition_to_redis_fields(self.definition, self.collection_type) - index_definition = IndexDefinition( - prefix=[f"{self.collection_name}:"], index_type=INDEX_TYPE_MAP[self.collection_type] - ) + if self.prefix_collection_name_to_key_names: + index_definition = IndexDefinition( + prefix=[f"{self.collection_name}:"], index_type=INDEX_TYPE_MAP[self.collection_type] + ) + else: + index_definition = IndexDefinition(index_type=INDEX_TYPE_MAP[self.collection_type]) await self.redis_database.ft(self.collection_name).create_index(fields, definition=index_definition, **kwargs) @override From 2013d1fc110d24ae4f53c925c881394fbdbe0b6c Mon Sep 17 00:00:00 2001 From: Nilesh Patil <128893479+nileshpatil6@users.noreply.github.com> Date: Fri, 8 May 2026 16:24:54 +0530 Subject: [PATCH 3/3] test(redis): add coverage for prefix-aware IndexDefinition and JSON delete key - test_create_index_respects_prefix_flag: asserts that ensure_collection_exists passes a list prefix ["test:"] to create_index when prefix_collection_name_to_key_names=True, and no prefix when False. - test_delete_json_with_prefix: asserts that RedisJsonCollection._inner_delete calls json().delete() with the prefixed key "test:id1" when prefix_collection_name_to_key_names=True. --- .../connectors/memory/test_redis_store.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/python/tests/unit/connectors/memory/test_redis_store.py b/python/tests/unit/connectors/memory/test_redis_store.py index e779ad945a97..05e8457a5003 100644 --- a/python/tests/unit/connectors/memory/test_redis_store.py +++ b/python/tests/unit/connectors/memory/test_redis_store.py @@ -306,3 +306,28 @@ async def test_create_index_manual(collection_hash, mock_ensure_collection_exist async def test_create_index_fail(collection_hash, mock_ensure_collection_exists): with raises(VectorStoreOperationException, match="Invalid index type supplied."): await collection_hash.ensure_collection_exists(index_definition="index_definition", fields="fields") + + +async def test_create_index_respects_prefix_flag( + collection_hash, collection_with_prefix_hash, mock_ensure_collection_exists +): + from redis.commands.search.index_definition import IndexDefinition + + # Without prefix flag: IndexDefinition should NOT contain the collection prefix + await collection_hash.ensure_collection_exists() + index_def = mock_ensure_collection_exists.call_args.kwargs["definition"] + assert isinstance(index_def, IndexDefinition) + assert "test:" not in index_def.args + + mock_ensure_collection_exists.reset_mock() + + # With prefix flag: IndexDefinition should include ["test:"] as prefix + await collection_with_prefix_hash.ensure_collection_exists() + index_def = mock_ensure_collection_exists.call_args.kwargs["definition"] + assert isinstance(index_def, IndexDefinition) + assert "test:" in index_def.args + + +async def test_delete_json_with_prefix(collection_with_prefix_json, mock_delete_json): + await collection_with_prefix_json._inner_delete(["id1"]) + mock_delete_json.assert_called_once_with("test:id1")