Coverage for haystack/tools/utils.py: 100%
53 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-12 09:42 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-12 09:42 +0000
1# SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>
2#
3# SPDX-License-Identifier: Apache-2.0
5from collections.abc import Sequence
6from typing import TYPE_CHECKING
8from haystack.tools.tool import Tool
9from haystack.tools.toolset import Toolset
11if TYPE_CHECKING:
12 from haystack.tools import ToolsType
15def _as_tool_sequence(tools: "ToolsType | None") -> Sequence[Tool | Toolset]:
16 if tools is None:
17 return []
18 if isinstance(tools, Toolset):
19 return [tools]
20 return tools
23def warm_up_tools(tools: "ToolsType | None" = None) -> None:
24 """
25 Warm up tools from various formats (Tools, Toolsets, or mixed lists).
27 For Toolset objects, this delegates to warm_up() if implemented; otherwise,
28 it calls warm_up() on each tool that implements it. Toolset subclasses can implement warm_up()
29 to customize initialization behavior (e.g., setting up shared resources).
31 :param tools: A sequence of Tool and/or Toolset objects, a single Toolset, or None.
32 """
33 for item in _as_tool_sequence(tools):
34 if hasattr(item, "warm_up"):
35 item.warm_up()
36 elif isinstance(item, Toolset):
37 warm_up_tools(tools=item.tools)
40async def warm_up_tools_async(tools: "ToolsType | None" = None) -> None:
41 """
42 Warm up tools asynchronously from various formats (Tools, Toolsets, or mixed lists).
44 Call warm_up_async() if implemented, otherwise warm_up() if available.
45 If a Toolset implements neither method, this function warms up its tools.
47 :param tools: A sequence of Tool and/or Toolset objects, a single Toolset, or None.
48 """
49 for item in _as_tool_sequence(tools):
50 if hasattr(item, "warm_up_async"):
51 await item.warm_up_async()
52 elif hasattr(item, "warm_up"):
53 item.warm_up()
54 elif isinstance(item, Toolset):
55 await warm_up_tools_async(tools=item.tools)
58def close_tools(tools: "ToolsType | None" = None) -> None:
59 """
60 Close tools from various formats (Tools, Toolsets, or mixed lists).
62 For Toolset objects, this delegates to close() if implemented; otherwise,
63 it calls close() on each tool that implements it.
65 :param tools: A sequence of Tool and/or Toolset objects, a single Toolset, or None.
66 """
67 for item in _as_tool_sequence(tools):
68 if hasattr(item, "close"):
69 item.close()
70 elif isinstance(item, Toolset):
71 close_tools(tools=item.tools)
74async def close_tools_async(tools: "ToolsType | None" = None) -> None:
75 """
76 Close tools asynchronously from various formats (Tools, Toolsets, or mixed lists).
78 Call close_async() if implemented, otherwise close() if available.
79 If a Toolset implements neither method, this function closes its tools.
81 :param tools: A sequence of Tool and/or Toolset objects, a single Toolset, or None.
82 """
83 for item in _as_tool_sequence(tools):
84 if hasattr(item, "close_async"):
85 await item.close_async()
86 elif hasattr(item, "close"):
87 item.close()
88 elif isinstance(item, Toolset):
89 await close_tools_async(tools=item.tools)
92def flatten_tools_or_toolsets(tools: "ToolsType | None") -> list[Tool]:
93 """
94 Flatten tools from various formats into a list of Tool instances.
96 :param tools: Tools in list[Union[Tool, Toolset]], Toolset, or None format.
97 :returns: A flat list of Tool instances.
98 """
99 if tools is None:
100 return []
102 if isinstance(tools, Toolset):
103 return list(tools)
105 if isinstance(tools, list):
106 flattened: list[Tool] = []
107 for item in tools:
108 if isinstance(item, Toolset):
109 flattened.extend(list(item))
110 elif isinstance(item, Tool):
111 flattened.append(item)
112 else:
113 raise TypeError("Items in the tools list must be Tool or Toolset instances.")
114 return flattened
116 raise TypeError("tools must be list[Union[Tool, Toolset]], Toolset, or None")