From 8da8cdc97544ef0041928badb070f1d2952e403b Mon Sep 17 00:00:00 2001 From: Jeevan Babu Gotru <114808955+jeevan1098@users.noreply.github.com> Date: Wed, 7 Oct 2026 10:46:25 -0500 Subject: [PATCH 1/3] fix(python): Fix InMemoryCollection upsert with key storage_name alias --- python/semantic_kernel/connectors/in_memory.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/python/semantic_kernel/connectors/in_memory.py b/python/semantic_kernel/connectors/in_memory.py index f2e1dc30fbe1..ee0876583abe 100644 --- a/python/semantic_kernel/connectors/in_memory.py +++ b/python/semantic_kernel/connectors/in_memory.py @@ -629,12 +629,18 @@ async def _inner_get( return [self.inner_storage[key] for key in keys if key in self.inner_storage] @override - async def _inner_upsert(self, records: Sequence[Any], **kwargs: Any) -> Sequence[TKey]: + async def _inner_upsert(self, records: Sequence[Any], **kwargs: Any) -> Sequence[TKey]: updated_keys = [] + key_storage_name = self._key_field_storage_name + key_name = self._key_field_name for record in records: record = AttributeDict(record) - self.inner_storage[record[self._key_field_name]] = record - updated_keys.append(record[self._key_field_name]) + # Serialized records index the key by its storage name (e.g. dict and + # dataclass records), while some record types (e.g. Pydantic models) + # serialize with the Python field name, so fall back to that. + key = record[key_storage_name] if key_storage_name in record else record[key_name] + self.inner_storage[key] = record + updated_keys.append(key) return updated_keys def _deserialize_store_models_to_dicts(self, records: Sequence[Any], **kwargs: Any) -> Sequence[dict[str, Any]]: From 00c355fae4a773ca6c0cefe7ecffaf29c2aae4ee Mon Sep 17 00:00:00 2001 From: Jeevan Babu Gotru <114808955+jeevan1098@users.noreply.github.com> Date: Wed, 7 Oct 2026 10:53:48 -0500 Subject: [PATCH 2/3] test(python): add regression tests for upsert with key storage_name alias --- .../unit/connectors/memory/test_in_memory.py | 51 ++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/python/tests/unit/connectors/memory/test_in_memory.py b/python/tests/unit/connectors/memory/test_in_memory.py index 7955b7696632..b3239d1df60e 100644 --- a/python/tests/unit/connectors/memory/test_in_memory.py +++ b/python/tests/unit/connectors/memory/test_in_memory.py @@ -1,12 +1,14 @@ # Copyright (c) Microsoft. All rights reserved. import ast +from dataclasses import dataclass from pytest import fixture, mark, raises +from pydantic import BaseModel from semantic_kernel.connectors.in_memory import InMemoryCollection, InMemoryStore from semantic_kernel.data._shared import default_dynamic_filter_function -from semantic_kernel.data.vector import DistanceFunction +from semantic_kernel.data.vector import DistanceFunction, VectorStoreCollectionDefinition, VectorStoreField from semantic_kernel.exceptions.vector_store_exceptions import VectorStoreOperationException @@ -292,3 +294,50 @@ async def test_large_sequence_repeat_filter_is_blocked(collection): with raises(VectorStoreOperationException, match="Sequence repetition in filter expressions exceeds the maximum"): collection._get_filtered_records(type("opt", (), {"filter": "lambda x: [0] * 2000000000"})()) + + +@fixture +def storage_name_definition(): + return VectorStoreCollectionDefinition( + fields=[ + VectorStoreField("key", name="id", type="str", storage_name="stored_id"), + VectorStoreField("vector", name="vector", type="float", dimensions=2), + ] + ) + + +async def test_upsert_with_key_storage_name_dict(storage_name_definition): + collection = InMemoryCollection(collection_name="test", record_type=dict, definition=storage_name_definition) + key = await collection.upsert({"id": "first", "vector": [1.0, 0.0]}) + assert key == "first" + result = await collection.get("first") + assert result["id"] == "first" + await collection.delete("first") + assert collection.inner_storage == {} + + +async def test_upsert_with_key_storage_name_dataclass(storage_name_definition): + @dataclass + class DataRecord: + id: str + vector: list[float] + + collection = InMemoryCollection(collection_name="test", record_type=DataRecord, definition=storage_name_definition) + key = await collection.upsert(DataRecord(id="first", vector=[1.0, 0.0])) + assert key == "first" + result = await collection.get("first", include_vectors=True) + assert result.id == "first" + + +async def test_upsert_with_key_storage_name_pydantic(storage_name_definition): + class PydanticRecord(BaseModel): + id: str + vector: list[float] + + collection = InMemoryCollection( + collection_name="test", record_type=PydanticRecord, definition=storage_name_definition + ) + key = await collection.upsert(PydanticRecord(id="first", vector=[1.0, 0.0])) + assert key == "first" + result = await collection.get("first", include_vectors=True) + assert result.id == "first" From df22de73c988f2b039f5fae848075f9e025ab1f9 Mon Sep 17 00:00:00 2001 From: Jeevan Babu Gotru <114808955+jeevan1098@users.noreply.github.com> Date: Sat, 10 Oct 2026 11:20:16 -0500 Subject: [PATCH 3/3] fix(python): correct indentation of _inner_upsert (review feedback) --- python/semantic_kernel/connectors/in_memory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/semantic_kernel/connectors/in_memory.py b/python/semantic_kernel/connectors/in_memory.py index ee0876583abe..7f6a6daba04b 100644 --- a/python/semantic_kernel/connectors/in_memory.py +++ b/python/semantic_kernel/connectors/in_memory.py @@ -629,7 +629,7 @@ async def _inner_get( return [self.inner_storage[key] for key in keys if key in self.inner_storage] @override - async def _inner_upsert(self, records: Sequence[Any], **kwargs: Any) -> Sequence[TKey]: + async def _inner_upsert(self, records: Sequence[Any], **kwargs: Any) -> Sequence[TKey]: updated_keys = [] key_storage_name = self._key_field_storage_name key_name = self._key_field_name