• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

deepset-ai / haystack / 14859791142

06 May 2025 12:35PM UTC coverage: 90.415% (+0.005%) from 90.41%
14859791142

Pull #9345

github

web-flow
Merge 0190e0497 into 4ce6934dd
Pull Request #9345: feat: add serialization to `State` / move `State` to utils

10961 of 12123 relevant lines covered (90.41%)

0.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.12
haystack/utils/base_serialization.py
1
# SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>
2
#
3
# SPDX-License-Identifier: Apache-2.0
4

5
from typing import Any, Dict
1✔
6

7
from haystack.core.errors import DeserializationError, SerializationError
1✔
8
from haystack.core.serialization import generate_qualified_class_name, import_class_by_name
1✔
9
from haystack.dataclasses import Answer, ChatMessage, Document, ExtractedAnswer, GeneratedAnswer, SparseEmbedding
1✔
10

11

12
def serialize_class_instance(obj: Any) -> Dict[str, Any]:
1✔
13
    """
14
    Serializes an object that has a `to_dict` method into a dictionary.
15

16
    :param obj:
17
        The object to be serialized.
18
    :returns:
19
        A dictionary representation of the object.
20
    :raises SerializationError:
21
        If the object does not have a `to_dict` method.
22
    """
23
    if not hasattr(obj, "to_dict"):
1✔
24
        raise SerializationError(f"Object of class '{type(obj).__name__}' does not have a 'to_dict' method")
1✔
25

26
    output = obj.to_dict()
1✔
27
    return {"type": generate_qualified_class_name(type(obj)), "data": output}
1✔
28

29

30
def deserialize_class_instance(data: Dict[str, Any]) -> Any:
1✔
31
    """
32
    Deserializes an object from a dictionary representation generated by `auto_serialize_class_instance`.
33

34
    :param data:
35
        The dictionary to deserialize from.
36
    :returns:
37
        The deserialized object.
38
    :raises DeserializationError:
39
        If the serialization data is malformed, the class type cannot be imported, or the
40
        class does not have a `from_dict` method.
41
    """
42
    if "type" not in data:
1✔
43
        raise DeserializationError("Missing 'type' in serialization data")
1✔
44
    if "data" not in data:
1✔
45
        raise DeserializationError("Missing 'data' in serialization data")
1✔
46

47
    try:
1✔
48
        obj_class = import_class_by_name(data["type"])
1✔
49
    except ImportError as e:
1✔
50
        raise DeserializationError(f"Class '{data['type']}' not correctly imported") from e
1✔
51

52
    if not hasattr(obj_class, "from_dict"):
1✔
53
        raise DeserializationError(f"Class '{data['type']}' does not have a 'from_dict' method")
1✔
54

55
    return obj_class.from_dict(data["data"])
1✔
56

57

58
def serialize_value(value: Any) -> Any:
1✔
59
    """
60
    Serializes a value into a format suitable for storage or transmission.
61

62
    Handles various types including:
63
    - Haystack dataclass objects (Answer, Document, etc.)
64
    - Primitive types (returned as is)
65
    - Lists of primitives (returned as is)
66
    - Lists of complex types (recursively serialized)
67
    - Dictionaries (recursively serialized)
68

69
    :param value: The value to serialize
70
    :returns: The serialized representation of the value
71
    """
72

73
    if hasattr(value, "to_dict") and callable(getattr(value, "to_dict")):
1✔
74
        serialized_value = value.to_dict()
1✔
75
        serialized_value["_type"] = value.__class__.__name__
1✔
76
        return serialized_value
1✔
77

78
    # this is a hack to serialize inputs that don't have a to_dict
79
    elif hasattr(value, "__dict__"):
1✔
80
        return {"_type": value.__class__.__name__, "attributes": value.__dict__}
×
81

82
    # recursively serialize all inputs in a dict
83
    elif isinstance(value, dict):
1✔
84
        return {k: serialize_value(v) for k, v in value.items()}
1✔
85

86
    # recursively serialize all inputs in lists or tuples
87
    elif isinstance(value, list):
1✔
88
        return [serialize_value(item) for item in value]
1✔
89

90
    return value
1✔
91

92

93
# pylint: disable=too-many-return-statements
94
def deserialize_value(value: Any) -> Any:
1✔
95
    """
96
    Deserializes a value from its serialized representation.
97

98
    Handles various types including:
99
    - Haystack dataclass objects (Answer, Document, etc.)
100
    - Primitive types (returned as is)
101
    - Lists of primitives (returned as is)
102
    - Lists of complex types (recursively deserialized)
103
    - Dictionaries (recursively deserialized)
104

105
    :param value: The serialized value to deserialize
106
    :returns: The deserialized value
107
    """
108

109
    # None or primitive types are returned as is
110
    if not value or isinstance(value, (str, int, float, bool)):
1✔
111
        return value
1✔
112

113
    # list of primitive types are returned as is
114
    if isinstance(value, list) and all(isinstance(i, (str, int, float, bool)) for i in value):
1✔
115
        return value
1✔
116

117
    if isinstance(value, list):
1✔
118
        # list of lists are called recursively
119
        if all(isinstance(i, list) for i in value):
1✔
120
            return [deserialize_value(i) for i in value]
×
121
        # list of dicts are called recursively
122
        if all(isinstance(i, dict) for i in value):
1✔
123
            return [deserialize_value(i) for i in value]
1✔
124

125
    # Define the mapping of types to their deserialization functions
126
    _type_deserializers = {
1✔
127
        "Answer": Answer.from_dict,
128
        "ChatMessage": ChatMessage.from_dict,
129
        "Document": Document.from_dict,
130
        "ExtractedAnswer": ExtractedAnswer.from_dict,
131
        "GeneratedAnswer": GeneratedAnswer.from_dict,
132
        "SparseEmbedding": SparseEmbedding.from_dict,
133
    }
134

135
    # check if the dictionary has a "_type" key and if it's a known type
136
    if isinstance(value, dict):
1✔
137
        if "_type" in value:
1✔
138
            type_name = value.pop("_type")
1✔
139
            if type_name in _type_deserializers:
1✔
140
                return _type_deserializers[type_name](value)
1✔
141

142
        # If not a known type, recursively deserialize each item in the dictionary
143
        return {k: deserialize_value(v) for k, v in value.items()}
1✔
144

145
    return value
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc