Coverage for haystack/components/converters/utils.py: 100%
37 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-21 13:53 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-21 13:53 +0000
1# SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>
2#
3# SPDX-License-Identifier: Apache-2.0
5from copy import deepcopy
6from enum import Enum
7from pathlib import Path
8from typing import Any
10from haystack.dataclasses import ByteStream
13class LinkFormat(Enum):
14 """
15 Supported formats for storing link information in a Document.
16 """
18 MARKDOWN = "markdown"
19 PLAIN = "plain"
20 NONE = "none"
22 def __str__(self) -> str:
23 return self.value
25 @staticmethod
26 def from_str(string: str) -> "LinkFormat":
27 """
28 Convert a string to a LinkFormat enum.
29 """
30 enum_map = {e.value: e for e in LinkFormat}
31 link_format = enum_map.get(string.lower())
32 if link_format is None:
33 msg = f"Unknown link format '{string}'. Supported formats are: {list(enum_map.keys())}"
34 raise ValueError(msg)
35 return link_format
38def get_bytestream_from_source(source: str | Path | ByteStream, guess_mime_type: bool = False) -> ByteStream:
39 """
40 Creates a ByteStream object from a source.
42 :param source:
43 A source to convert to a ByteStream. Can be a string (path to a file), a Path object, or a ByteStream.
44 :param guess_mime_type:
45 Whether to guess the mime type from the file.
46 :return:
47 A ByteStream object.
48 """
50 if isinstance(source, ByteStream):
51 return source
52 if isinstance(source, (str, Path)):
53 bs = ByteStream.from_file_path(Path(source), guess_mime_type=guess_mime_type)
54 bs.meta["file_path"] = str(source)
55 return bs
56 raise ValueError(f"Unsupported source type {type(source)}")
59def normalize_metadata(meta: dict[str, Any] | list[dict[str, Any]] | None, sources_count: int) -> list[dict[str, Any]]:
60 """
61 Normalize the metadata input for a converter.
63 Given all the possible value of the meta input for a converter (None, dictionary or list of dicts),
64 makes sure to return a list of dictionaries of the correct length for the converter to use.
66 :param meta: the meta input of the converter, as-is
67 :param sources_count: the number of sources the converter received
68 :returns: a list of dictionaries of the make length as the sources list
70 Each source always gets its own independent dictionary. When ``meta`` is ``None`` or a single
71 dictionary, a separate copy is returned for every source so that mutating one source's metadata
72 downstream does not leak into the others.
73 """
74 if meta is None:
75 return [{} for _ in range(sources_count)]
76 if isinstance(meta, dict):
77 return [deepcopy(meta) for _ in range(sources_count)]
78 if isinstance(meta, list):
79 if sources_count != len(meta):
80 raise ValueError("The length of the metadata list must match the number of sources.")
81 return meta
82 raise ValueError("meta must be either None, a dictionary or a list of dictionaries.")