Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions haystack/dataclasses/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,12 @@ def from_dict(cls, data: dict[str, Any]) -> "Document":
# a document field is a metadata key. We treat legacy fields as document fields, so that
# `_RemoveLegacyFields` drops them instead of them ending up in `meta`.
flatten_meta = {}
# A non-mapping value under the "meta" key can't be the `meta` parameter (which must be a
# dictionary). It can only be a flattened metadata key literally named "meta", produced by
# to_dict(flatten=True) for a document whose metadata contains a "meta" key.
if not isinstance(meta, dict):
flatten_meta["meta"] = meta
meta = {}
document_fields = _LEGACY_FIELDS + [f.name for f in fields(cls)]
for key in list(data.keys()):
if key not in document_fields:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
fixes:
- |
Fixed ``Document.from_dict()`` raising ``TypeError: 'str' object is not a mapping`` when
deserializing a document whose metadata contained a non-mapping key named ``meta``
(for example ``Document(content="hi", meta={"meta": "value"})``). Such a document is now
correctly reconstructed from the default flattened ``to_dict()`` output, so the
``to_dict()``/``from_dict()`` round-trip no longer crashes.
11 changes: 11 additions & 0 deletions test/dataclasses/test_document.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,17 @@ def test_from_dict_with_flat_meta():
)


def test_to_dict_from_dict_roundtrip_with_meta_key_named_meta():
"""
A metadata key literally named "meta" must survive the default
to_dict(flatten=True)/from_dict() round-trip.
"""
doc = Document(content="hi", meta={"meta": "value"})

assert Document.from_dict(doc.to_dict()) == doc
assert Document.from_dict(doc.to_dict()).meta == {"meta": "value"}


def test_from_dict_with_flat_and_non_flat_meta():
with pytest.raises(ValueError, match="Pass either the 'meta' parameter or flattened metadata keys"):
Document.from_dict(
Expand Down
Loading