Skip to content
Closed
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
3 changes: 2 additions & 1 deletion haystack/components/agents/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
)
from haystack.utils.async_utils import _execute_component_async
from haystack.utils.callable_serialization import deserialize_callable, serialize_callable
from haystack.utils.deserialization import deserialize_component_inplace
from haystack.utils.deserialization import _copy_serialized_data, deserialize_component_inplace

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -638,6 +638,7 @@ def from_dict(cls, data: dict[str, Any]) -> "Agent":
:param data: Dictionary to deserialize from.
:returns: Deserialized agent.
"""
data = _copy_serialized_data(data) # `from_dict` must not modify the caller's data
init_params = data.get("init_parameters", {})

deserialize_component_inplace(init_params, key="chat_generator")
Expand Down
2 changes: 2 additions & 0 deletions haystack/components/builders/chat_prompt_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from haystack.dataclasses.chat_message import ChatMessage, ChatRole, TextContent
from haystack.lazy_imports import LazyImport
from haystack.utils import Jinja2TimeExtension
from haystack.utils.deserialization import _copy_serialized_data
from haystack.utils.jinja2_chat_extension import ChatMessageExtension
from haystack.utils.jinja2_extensions import _extract_template_variables_and_assignments
from haystack.utils.jinja2_sandbox import HaystackSandboxedEnvironment
Expand Down Expand Up @@ -347,6 +348,7 @@ def from_dict(cls, data: dict[str, Any]) -> "ChatPromptBuilder":
:returns:
The deserialized component.
"""
data = _copy_serialized_data(data) # `from_dict` must not modify the caller's data
init_parameters = data["init_parameters"]
template = init_parameters.get("template")
if template:
Expand Down
2 changes: 2 additions & 0 deletions haystack/components/converters/docx.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from haystack.components.converters.utils import LinkFormat, get_bytestream_from_source, normalize_metadata
from haystack.dataclasses import ByteStream
from haystack.lazy_imports import LazyImport
from haystack.utils.deserialization import _copy_serialized_data

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -167,6 +168,7 @@ def from_dict(cls, data: dict[str, Any]) -> "DOCXToDocument":
:returns:
The deserialized component.
"""
data = _copy_serialized_data(data) # `from_dict` must not modify the caller's data
if "table_format" in data["init_parameters"]:
data["init_parameters"]["table_format"] = DOCXTableFormat.from_str(data["init_parameters"]["table_format"])
if "link_format" in data["init_parameters"]:
Expand Down
2 changes: 2 additions & 0 deletions haystack/components/converters/output_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from haystack.core.errors import DeserializationError
from haystack.core.serialization_security import _is_unsafe_deserialization
from haystack.utils import deserialize_callable, deserialize_type, serialize_callable, serialize_type
from haystack.utils.deserialization import _copy_serialized_data
from haystack.utils.jinja2_extensions import _extract_template_variables_and_assignments
from haystack.utils.jinja2_sandbox import HaystackSandboxedEnvironment

Expand Down Expand Up @@ -171,6 +172,7 @@ def from_dict(cls, data: dict[str, Any]) -> "OutputAdapter":
:returns:
The deserialized component.
"""
data = _copy_serialized_data(data) # `from_dict` must not modify the caller's data
init_params = data.get("init_parameters", {})

# `unsafe=True` swaps the Jinja sandbox for a NativeEnvironment that executes arbitrary code.
Expand Down
2 changes: 2 additions & 0 deletions haystack/components/converters/pdfminer.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from haystack.components.converters.utils import LinkFormat, get_bytestream_from_source, normalize_metadata
from haystack.dataclasses import ByteStream
from haystack.lazy_imports import LazyImport
from haystack.utils.deserialization import _copy_serialized_data

with LazyImport("Run 'pip install pdfminer.six'") as pdfminer_import:
from pdfminer.converter import PDFPageAggregator
Expand Down Expand Up @@ -150,6 +151,7 @@ def from_dict(cls, data: dict[str, Any]) -> "PDFMinerToDocument":
:returns:
Deserialized component.
"""
data = _copy_serialized_data(data) # `from_dict` must not modify the caller's data
if "link_format" in data.get("init_parameters", {}):
data["init_parameters"]["link_format"] = LinkFormat.from_str(data["init_parameters"]["link_format"])
return default_from_dict(cls, data)
Expand Down
2 changes: 2 additions & 0 deletions haystack/components/converters/pypdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from haystack.components.converters.utils import LinkFormat, get_bytestream_from_source, normalize_metadata
from haystack.dataclasses import ByteStream
from haystack.lazy_imports import LazyImport
from haystack.utils.deserialization import _copy_serialized_data

with LazyImport("Run 'pip install pypdf'") as pypdf_import:
from pypdf import PdfReader
Expand Down Expand Up @@ -167,6 +168,7 @@ def from_dict(cls, data: dict[str, Any]) -> "PyPDFToDocument":
:returns:
Deserialized component.
"""
data = _copy_serialized_data(data) # `from_dict` must not modify the caller's data
if "link_format" in data.get("init_parameters", {}):
data["init_parameters"]["link_format"] = LinkFormat.from_str(data["init_parameters"]["link_format"])
return default_from_dict(cls, data)
Expand Down
2 changes: 2 additions & 0 deletions haystack/components/embedders/azure_document_embedder.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from haystack import component, default_from_dict, default_to_dict, logging
from haystack.components.embedders import OpenAIDocumentEmbedder
from haystack.utils import Secret, deserialize_callable, serialize_callable
from haystack.utils.deserialization import _copy_serialized_data
from haystack.utils.http_client import init_http_client

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -250,6 +251,7 @@ def from_dict(cls, data: dict[str, Any]) -> "AzureOpenAIDocumentEmbedder":
:returns:
Deserialized component.
"""
data = _copy_serialized_data(data) # `from_dict` must not modify the caller's data
serialized_azure_ad_token_provider = data["init_parameters"].get("azure_ad_token_provider")
if serialized_azure_ad_token_provider:
data["init_parameters"]["azure_ad_token_provider"] = deserialize_callable(
Expand Down
2 changes: 2 additions & 0 deletions haystack/components/embedders/azure_text_embedder.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from haystack import component, default_from_dict, default_to_dict
from haystack.components.embedders import OpenAITextEmbedder
from haystack.utils import Secret, deserialize_callable, serialize_callable
from haystack.utils.deserialization import _copy_serialized_data
from haystack.utils.http_client import init_http_client


Expand Down Expand Up @@ -226,6 +227,7 @@ def from_dict(cls, data: dict[str, Any]) -> "AzureOpenAITextEmbedder":
:returns:
Deserialized component.
"""
data = _copy_serialized_data(data) # `from_dict` must not modify the caller's data
serialized_azure_ad_token_provider = data["init_parameters"].get("azure_ad_token_provider")
if serialized_azure_ad_token_provider:
data["init_parameters"]["azure_ad_token_provider"] = deserialize_callable(
Expand Down
2 changes: 2 additions & 0 deletions haystack/components/embedders/mock_document_embedder.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
_estimate_usage,
)
from haystack.utils import deserialize_callable, serialize_callable
from haystack.utils.deserialization import _copy_serialized_data


@component
Expand Down Expand Up @@ -122,6 +123,7 @@ def to_dict(self) -> dict[str, Any]:
@classmethod
def from_dict(cls, data: dict[str, Any]) -> MockDocumentEmbedder:
"""Deserialize the component from a dictionary."""
data = _copy_serialized_data(data) # `from_dict` must not modify the caller's data
init_params = data.get("init_parameters", {})
embedding_fn = init_params.get("embedding_fn")
if embedding_fn:
Expand Down
2 changes: 2 additions & 0 deletions haystack/components/embedders/mock_text_embedder.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
_estimate_usage,
)
from haystack.utils import deserialize_callable, serialize_callable
from haystack.utils.deserialization import _copy_serialized_data


@component
Expand Down Expand Up @@ -105,6 +106,7 @@ def to_dict(self) -> dict[str, Any]:
@classmethod
def from_dict(cls, data: dict[str, Any]) -> MockTextEmbedder:
"""Deserialize the component from a dictionary."""
data = _copy_serialized_data(data) # `from_dict` must not modify the caller's data
init_params = data.get("init_parameters", {})
embedding_fn = init_params.get("embedding_fn")
if embedding_fn:
Expand Down
2 changes: 2 additions & 0 deletions haystack/components/evaluators/context_relevance.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from haystack.components.generators.chat.types import ChatGenerator
from haystack.core.serialization import component_to_dict
from haystack.utils import deserialize_chatgenerator_inplace
from haystack.utils.deserialization import _copy_serialized_data

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -264,6 +265,7 @@ def from_dict(cls, data: dict[str, Any]) -> "ContextRelevanceEvaluator":
:returns:
The deserialized component instance.
"""
data = _copy_serialized_data(data) # `from_dict` must not modify the caller's data
if data["init_parameters"].get("chat_generator"):
deserialize_chatgenerator_inplace(data["init_parameters"], key="chat_generator")
return default_from_dict(cls, data)
2 changes: 2 additions & 0 deletions haystack/components/evaluators/faithfulness.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from haystack.components.generators.chat.types import ChatGenerator
from haystack.core.serialization import component_to_dict
from haystack.utils import deserialize_chatgenerator_inplace
from haystack.utils.deserialization import _copy_serialized_data

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -263,6 +264,7 @@ def from_dict(cls, data: dict[str, Any]) -> "FaithfulnessEvaluator":
:returns:
The deserialized component instance.
"""
data = _copy_serialized_data(data) # `from_dict` must not modify the caller's data
if data["init_parameters"].get("chat_generator"):
deserialize_chatgenerator_inplace(data["init_parameters"], key="chat_generator")
return default_from_dict(cls, data)
2 changes: 2 additions & 0 deletions haystack/components/evaluators/llm_evaluator.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from haystack.core.serialization import component_to_dict
from haystack.dataclasses.chat_message import ChatMessage
from haystack.utils import deserialize_chatgenerator_inplace, deserialize_type, serialize_type
from haystack.utils.deserialization import _copy_serialized_data
from haystack.utils.misc import _parse_dict_from_json

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -419,6 +420,7 @@ def from_dict(cls, data: dict[str, Any]) -> "LLMEvaluator":
:returns:
The deserialized component instance.
"""
data = _copy_serialized_data(data) # `from_dict` must not modify the caller's data
data["init_parameters"]["inputs"] = [
(name, deserialize_type(type_)) for name, type_ in data["init_parameters"]["inputs"]
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from haystack.dataclasses.chat_message import ChatMessage
from haystack.utils import deserialize_chatgenerator_inplace
from haystack.utils.async_utils import _execute_component_async
from haystack.utils.deserialization import _copy_serialized_data
from haystack.utils.misc import _parse_dict_from_json

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -237,6 +238,7 @@ def from_dict(cls, data: dict[str, Any]) -> "LLMDocumentContentExtractor":
:returns:
An instance of the component.
"""
data = _copy_serialized_data(data) # `from_dict` must not modify the caller's data
init_params = data.get("init_parameters", {})
deserialize_chatgenerator_inplace(init_params, key="chat_generator")

Expand Down
2 changes: 2 additions & 0 deletions haystack/components/extractors/llm_metadata_extractor.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from haystack.dataclasses import ChatMessage
from haystack.utils import deserialize_chatgenerator_inplace, expand_page_range
from haystack.utils.async_utils import _execute_component_async
from haystack.utils.deserialization import _copy_serialized_data
from haystack.utils.misc import _parse_dict_from_json

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -264,6 +265,7 @@ def from_dict(cls, data: dict[str, Any]) -> "LLMMetadataExtractor":
:returns:
An instance of the component.
"""
data = _copy_serialized_data(data) # `from_dict` must not modify the caller's data

deserialize_chatgenerator_inplace(data["init_parameters"], key="chat_generator")
return default_from_dict(cls, data)
Expand Down
2 changes: 2 additions & 0 deletions haystack/components/generators/chat/azure.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
warm_up_tools,
)
from haystack.utils import Secret, deserialize_callable, serialize_callable
from haystack.utils.deserialization import _copy_serialized_data
from haystack.utils.http_client import init_http_client


Expand Down Expand Up @@ -364,6 +365,7 @@ def from_dict(cls, data: dict[str, Any]) -> "AzureOpenAIChatGenerator":
:returns:
The deserialized component instance.
"""
data = _copy_serialized_data(data) # `from_dict` must not modify the caller's data
deserialize_tools_or_toolset_inplace(data["init_parameters"], key="tools")
init_params = data.get("init_parameters", {})
serialized_callback_handler = init_params.get("streaming_callback")
Expand Down
2 changes: 2 additions & 0 deletions haystack/components/generators/chat/azure_responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from haystack.dataclasses.streaming_chunk import StreamingCallbackT
from haystack.tools import ToolsType, deserialize_tools_or_toolset_inplace, serialize_tools_or_toolset
from haystack.utils import Secret, deserialize_callable, serialize_callable
from haystack.utils.deserialization import _copy_serialized_data


@component
Expand Down Expand Up @@ -249,6 +250,7 @@ def from_dict(cls, data: dict[str, Any]) -> "AzureOpenAIResponsesChatGenerator":
:returns:
The deserialized component instance.
"""
data = _copy_serialized_data(data) # `from_dict` must not modify the caller's data
# If api_key is a str, it's a callable (Secrets are handled automatically by default_from_dict)
serialized_api_key = data["init_parameters"].get("api_key")
if isinstance(serialized_api_key, str):
Expand Down
3 changes: 2 additions & 1 deletion haystack/components/generators/chat/fallback.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from haystack.dataclasses import ChatMessage, StreamingCallbackT
from haystack.tools import ToolsType
from haystack.utils.async_utils import _execute_component_async
from haystack.utils.deserialization import deserialize_component_inplace
from haystack.utils.deserialization import _copy_serialized_data, deserialize_component_inplace

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -73,6 +73,7 @@ def to_dict(self) -> dict[str, Any]:
@classmethod
def from_dict(cls, data: dict[str, Any]) -> FallbackChatGenerator:
"""Rebuild the component from a serialized representation, restoring nested chat generators."""
data = _copy_serialized_data(data) # `from_dict` must not modify the caller's data
# Reconstruct nested chat generators from their serialized dicts
init_params = data.get("init_parameters", {})
serialized = init_params.get("chat_generators") or []
Expand Down
3 changes: 2 additions & 1 deletion haystack/components/generators/chat/llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from haystack.core.serialization import component_to_dict, default_from_dict, default_to_dict
from haystack.dataclasses import ChatMessage, StreamingCallbackT
from haystack.utils.callable_serialization import deserialize_callable, serialize_callable
from haystack.utils.deserialization import deserialize_component_inplace
from haystack.utils.deserialization import _copy_serialized_data, deserialize_component_inplace

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -116,6 +116,7 @@ def from_dict(cls, data: dict[str, Any]) -> "LLM":
:param data: Dictionary to deserialize from.
:return: Deserialized LLM instance.
"""
data = _copy_serialized_data(data) # `from_dict` must not modify the caller's data
init_params = data.get("init_parameters", {})

deserialize_component_inplace(init_params, key="chat_generator")
Expand Down
2 changes: 2 additions & 0 deletions haystack/components/generators/chat/mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from haystack.dataclasses.streaming_chunk import ToolCallDelta, _invoke_streaming_callback
from haystack.tools import ToolsType
from haystack.utils import deserialize_callable, serialize_callable
from haystack.utils.deserialization import _copy_serialized_data

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -186,6 +187,7 @@ def to_dict(self) -> dict[str, Any]:
@classmethod
def from_dict(cls, data: dict[str, Any]) -> MockChatGenerator:
"""Deserialize the component from a dictionary."""
data = _copy_serialized_data(data) # `from_dict` must not modify the caller's data
init_params = data.get("init_parameters", {})
responses = init_params.get("responses")
if responses is not None:
Expand Down
2 changes: 2 additions & 0 deletions haystack/components/generators/chat/openai.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
warm_up_tools,
)
from haystack.utils import Secret, deserialize_callable, serialize_callable
from haystack.utils.deserialization import _copy_serialized_data
from haystack.utils.http_client import init_http_client

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -331,6 +332,7 @@ def from_dict(cls, data: dict[str, Any]) -> "OpenAIChatGenerator":
:returns:
The deserialized component instance.
"""
data = _copy_serialized_data(data) # `from_dict` must not modify the caller's data
deserialize_tools_or_toolset_inplace(data["init_parameters"], key="tools")
init_params = data.get("init_parameters", {})
serialized_callback_handler = init_params.get("streaming_callback")
Expand Down
2 changes: 2 additions & 0 deletions haystack/components/generators/chat/openai_responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
warm_up_tools,
)
from haystack.utils import Secret, deserialize_callable, serialize_callable
from haystack.utils.deserialization import _copy_serialized_data
from haystack.utils.http_client import init_http_client

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -344,6 +345,7 @@ def from_dict(cls, data: dict[str, Any]) -> "OpenAIResponsesChatGenerator":
:returns:
The deserialized component instance.
"""
data = _copy_serialized_data(data) # `from_dict` must not modify the caller's data
# we only deserialize the tools if they are haystack tools
# because openai tools are not serialized in the same way
tools = data["init_parameters"].get("tools")
Expand Down
2 changes: 2 additions & 0 deletions haystack/components/joiners/branch.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from haystack import component, default_from_dict, default_to_dict
from haystack.core.component.types import GreedyVariadic
from haystack.utils import deserialize_type, serialize_type
from haystack.utils.deserialization import _copy_serialized_data


@component
Expand Down Expand Up @@ -113,6 +114,7 @@ def from_dict(cls, data: dict[str, Any]) -> "BranchJoiner":
:returns:
A deserialized `BranchJoiner` instance.
"""
data = _copy_serialized_data(data) # `from_dict` must not modify the caller's data
data["init_parameters"]["type_"] = deserialize_type(data["init_parameters"]["type_"])
return default_from_dict(cls, data)

Expand Down
2 changes: 2 additions & 0 deletions haystack/components/joiners/list_joiner.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from haystack import component, default_from_dict, default_to_dict
from haystack.core.component.types import Variadic
from haystack.utils import deserialize_type, serialize_type
from haystack.utils.deserialization import _copy_serialized_data


@component
Expand Down Expand Up @@ -96,6 +97,7 @@ def from_dict(cls, data: dict[str, Any]) -> "ListJoiner":
:param data: Dictionary to deserialize from.
:returns: Deserialized component.
"""
data = _copy_serialized_data(data) # `from_dict` must not modify the caller's data
init_parameters = data.get("init_parameters")
if init_parameters is not None and init_parameters.get("list_type_") is not None:
data["init_parameters"]["list_type_"] = deserialize_type(data["init_parameters"]["list_type_"])
Expand Down
Loading
Loading