Python: Bug: ChromaCollection mishandles storage_name on the key and vector fields, depending on whether the record is a dict, dataclass or pydantic model
Maintainer thường phản hồi trong vòng 5 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 3/5
- Thời gian dự kiến
- 1-2 ngày
- Mức phù hợp với người mới
- 35/100
Hướng nghiên cứu
Start with python/semantic_kernel/connectors/chroma.py, especially _serialize_dicts_to_store_models and _deserialize_store_models_to_dicts, then read tests/unit/connectors/memory/test_chroma.py. Reproduce the dict, dataclass, and pydantic cases using an ephemeral Chroma client. Done means upsert, get, search with vectors, and filters using storage names behave consistently for all three record types; the issue reports a ready branch and no pull request.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Description
ChromaCollection handles a field with a storage_name in four different ways, depending on the record type and the field:
- A dict or dataclass record with a
storage_nameon the key field fails to upsert:VectorStoreModelSerializationException: Error serializing records: 'id'. - A pydantic record with a
storage_nameon the vector field fails to upsert:Error serializing records: 'doc_vector'. - A dict or dataclass record with a
storage_nameon the vector field upserts, butgetandsearchreturnvector=Noneeven withinclude_vectors=True. - A pydantic record is stored with its metadata under the declared field names. Filters only accept storage names, so a filter on a field with a
storage_namefinds nothing.
The base class hands the connector a dict keyed by storage name for dict and dataclass records, and keyed by field name for pydantic models. The Chroma serializer and deserializer assume one shape for some fields and the other shape for the rest.
Reproduction
I ran this on current main (df1cb33d0), Python 3.13, chromadb 1.5.9 with chromadb.EphemeralClient(), so no server is involved.
import asyncio
import uuid
import warnings
from dataclasses import dataclass
warnings.filterwarnings("ignore")
import chromadb
from pydantic import BaseModel
from semantic_kernel.connectors.chroma import ChromaCollection
from semantic_kernel.data.vector import VectorStoreCollectionDefinition, VectorStoreField
CLIENT = chromadb.EphemeralClient()
def definition(key_alias=None, vector_alias=None):
return VectorStoreCollectionDefinition(
fields=[
VectorStoreField("key", name="id", type="str", storage_name=key_alias),
VectorStoreField("data", name="text", type="str", storage_name="doc_text", is_indexed=True),
VectorStoreField("vector", name="vector", dimensions=2, type="float", storage_name=vector_alias),
]
)
@dataclass
class DC:
id: str
text: str
vector: list[float] | None = None
class PM(BaseModel):
id: str
text: str
vector: list[float] | None = None
def record(kind):
values = {"id": "k1", "text": "a", "vector": [1.0, 0.0]}
return values if kind is dict else kind(**values)
def show(rec):
values = rec if isinstance(rec, dict) else vars(rec) if isinstance(rec, DC) else rec.model_dump()
return {k: ([float(x) for x in v] if k == "vector" and v is not None else v) for k, v in values.items()}
async def roundtrip(kind, defn, label):
collection = ChromaCollection(
record_type=kind, definition=defn, collection_name=f"t-{uuid.uuid4().hex}", client=CLIENT
)
await collection.ensure_collection_exists()
try:
await collection.upsert(record(kind))
print(f"{kind.__name__:5s} {label}: get -> {show(await collection.get('k1', include_vectors=True))}")
except Exception as ex:
print(f"{kind.__name__:5s} {label}: {type(ex).__name__}: {ex}")
async def filtered(kind):
collection = ChromaCollection(
record_type=kind, definition=definition(), collection_name=f"t-{uuid.uuid4().hex}", client=CLIENT
)
await collection.ensure_collection_exists()
await collection.upsert(record(kind))
results = await collection.search(vector=[1.0, 0.0], filter=lambda x: x.doc_text == "a")
print(f"{kind.__name__:5s} filter on doc_text: {len([r async for r in results.results])} result(s)")
async def main():
for kind in (dict, DC, PM):
await roundtrip(kind, definition(vector_alias="doc_vector"), "vector storage_name")
for kind in (dict, DC, PM):
await roundtrip(kind, definition(key_alias="point_id"), "key storage_name ")
for kind in (dict, DC, PM):
await filtered(kind)
asyncio.run(main())
Output on main:
dict vector storage_name: get -> {'id': 'k1', 'text': 'a', 'vector': None}
DC vector storage_name: get -> {'id': 'k1', 'text': 'a', 'vector': None}
PM vector storage_name: VectorStoreModelSerializationException: Error serializing records: 'doc_vector'
dict key storage_name : VectorStoreModelSerializationException: Error serializing records: 'id'
DC key storage_name : VectorStoreModelSerializationException: Error serializing records: 'id'
PM key storage_name : get -> {'id': 'k1', 'text': 'a', 'vector': [1.0, 0.0]}
dict filter on doc_text: 1 result(s)
DC filter on doc_text: 1 result(s)
PM filter on doc_text: 0 result(s)
The data field text has a storage_name in every run. That alone works for dict and dataclass records.
Cause
In python/semantic_kernel/connectors/chroma.py:
_serialize_dicts_to_store_modelsreads the key withrecord[self.definition.key_name](declared name) and the vector withrecord[vector_field.storage_name or vector_field.name](storage name). Each is right for only one of the two record shapes. Everything else stays in the metadata under the key it arrived with, so pydantic metadata keeps the declared names._deserialize_store_models_to_dictsputs the key and the vector back under the declared names. For dict and dataclass records the data model step readsrecord.get(field.storage_name or field.name), so a vector with astorage_nameis lost.
The same kind of mismatch exists in the Qdrant connector, see #14754.
Expected
Upsert, get and search behave the same for dict, dataclass and pydantic records, with or without storage_name. The metadata uses the storage names, which is what filters use.
Possible fix
I have a branch ready: https://github.com/VANDRANKI/semantic-kernel/tree/fix/chroma-storage-name-record-keys
_serialize_dicts_to_store_modelsrenames declared names to storage names in a copy of the record (only when the storage name is not already there), and then reads the key and the vector by storage name._deserialize_store_models_to_dictsreturns the key and the vector under their storage names. The data model step maps them back for pydantic records.
It adds 6 tests to tests/unit/connectors/memory/test_chroma.py (dict, dataclass and pydantic, each with and without a key storage_name). They upsert, get, search with vectors and filter on a storage name, on an ephemeral Chroma client. All 6 fail on main. The 26 tests in that file pass on the branch, and ruff check and format are clean.
I can't open a pull request from this account right now, so the compare link is https://github.com/microsoft/semantic-kernel/compare/main...VANDRANKI:semantic-kernel:fix/chroma-storage-name-record-keys
- Ngôn ngữ chính
- C#
- Star
- 28.7k
- Fork
- 4.9k
- Merge trung bình
- 5 ngày 3 giờ
- Pull request đã merge (30 ngày)
- 63
Chuẩn bị môi trường
Khởi chạy dev container của dự án ngay trên trình duyệt, bằng tài khoản GitHub của bạn.
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của microsoft/semantic-kernel
-
python triage
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
microsoft/semantic-kernel#14753 ·
Maintainer thường phản hồi trong vòng 5 ngày
-
.Net: gpt-image-1 is the default image model in the .NET OpenAI connector, and OpenAI shuts it down on October 23Có thể đã có người làm @nightcityblade đã nhận 37 ngày trước. Đang mở.NET triage
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 73/100
microsoft/semantic-kernel#14526 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 5 ngày
-
Python: VolatileMemoryStore.get_batch and get_nearest_matches ignore with_embeddings=False (deepcopy result is discarded)Có thể đã có người làm @VANDRANKI đã nhận 38 ngày trước. Đang mởpython triage
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 84/100
microsoft/semantic-kernel#14522 ·
Maintainer thường phản hồi trong vòng 5 ngày
-
.Net: Bug: BinaryContent does not decode the %xx escapes of a non-base64 data URICó thể đã có người làm @Laurianti đã nhận 39 ngày trước. Đang mở.NET triage
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
microsoft/semantic-kernel#14518 ·
Maintainer thường phản hồi trong vòng 5 ngày
-
Python: FunctionCallContent.combine_arguments drops a streamed "{}" chunk, producing invalid JSON argumentsCó thể đã có người làm @VANDRANKI đã nhận 39 ngày trước. Đang mởpython triage
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
microsoft/semantic-kernel#14512 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 5 ngày
Tất cả issue của microsoft/semantic-kernel
Issue tương tự
-
Assert.That on a null non-generic IEnumerable throws ArgumentNullException, so IsNull() cannot passĐang mởbug P2
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
Maintainer thường phản hồi trong vòng 1 ngày
-
area/automation area/sdk kind/bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
unoplatform/uno.templates#2406 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
bug frontend ui
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
Maintainer thường phản hồi trong vòng 1 ngày
-
agentic-workflows
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
Maintainer thường phản hồi trong vòng 1 ngày
-
documentation trivial
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
Maintainer thường phản hồi trong vòng 1 ngày