Files
wehub-resource-sync c56bef871b
Sync docs with Docusaurus / sync (push) Waiting to run
Tests / Check if changed (push) Waiting to run
Tests / format (push) Blocked by required conditions
Tests / check-imports (push) Blocked by required conditions
Tests / Unit / macos-latest (push) Blocked by required conditions
Tests / Unit / ubuntu-latest (push) Blocked by required conditions
Tests / Unit / windows-latest (push) Blocked by required conditions
Tests / mypy (push) Blocked by required conditions
Tests / Integration / ubuntu-latest (push) Blocked by required conditions
Tests / Integration / macos-latest (push) Blocked by required conditions
Tests / Integration / windows-latest (push) Blocked by required conditions
Tests / notify-slack-on-failure (push) Blocked by required conditions
Tests / Mark tests as completed (push) Blocked by required conditions
Docker image release / Build base image (push) Waiting to run
CodeQL / Analyze (python) (push) Has been cancelled
Update Platform Components Table / update (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:22:28 +08:00

115 lines
4.2 KiB
Python

# SPDX-FileCopyrightText: 2022-present deepset GmbH <info@deepset.ai>
#
# SPDX-License-Identifier: Apache-2.0
from typing import Any
from haystack.core.serialization import component_to_dict
from haystack.hooks.protocol import Hook, HookPoint
from haystack.utils.deserialization import deserialize_component_inplace
# Hooks are (de)serialized with `component_to_dict` / `deserialize_component_inplace` even though they aren't
# Components. Despite the name, those helpers aren't component-specific: they just produce/consume the standard
# `{"type", "init_parameters"}` dict, and deserialization enforces the import allowlist (so only trusted modules are
# loaded).
def _serialize_hooks_dictionary(hooks: dict[HookPoint, list[Hook]]) -> dict[str, list[dict[str, Any]]]:
"""
Serialize a hook-point-keyed dict of hooks to plain dictionaries.
:param hooks: Hooks keyed by hook point; each hook must implement `to_dict`.
:returns: The same mapping with each hook replaced by its serialized dictionary.
"""
return {
hook_point: [component_to_dict(obj=h, name="hook") for h in hook_list]
for hook_point, hook_list in hooks.items()
}
def _deserialize_hooks_dictionary(data: dict[str, list[dict[str, Any]]]) -> dict[str, list[Hook]]:
"""
Deserialize a hook-point-keyed dict of hooks from its serialized form.
:param data: Hook-point-keyed lists of serialized hook dictionaries (each with a `type` field).
:returns: The same mapping with each entry rebuilt into a `Hook` instance.
"""
deserialized: dict[str, list[Hook]] = {}
for hook_point, serialized_hooks in data.items():
hooks: list[Hook] = []
for serialized_hook in serialized_hooks:
wrapper: dict[str, Any] = {"hook": serialized_hook}
deserialize_component_inplace(wrapper, key="hook")
hooks.append(wrapper["hook"])
deserialized[hook_point] = hooks
return deserialized
def _unique_hooks(hooks: dict[HookPoint, list[Hook]]) -> list[Hook]:
"""
Collect each distinct hook once, preserving first-seen order.
A hook may be registered under several hook points; deduplicating by identity ensures lifecycle methods
(warm up / close) run once per hook object.
:param hooks: Hooks keyed by hook point.
:returns: The distinct hook objects, in the order first encountered.
"""
unique: list[Hook] = []
seen: set[int] = set()
for hook_list in hooks.values():
for h in hook_list:
if id(h) not in seen:
seen.add(id(h))
unique.append(h)
return unique
def warm_up_hooks(hooks: dict[HookPoint, list[Hook]]) -> None:
"""
Warm up every hook that defines a `warm_up` method (e.g. to open clients or load credentials).
:param hooks: Hooks keyed by hook point. Each distinct hook is warmed up at most once.
"""
for h in _unique_hooks(hooks):
if hasattr(h, "warm_up"):
h.warm_up()
async def warm_up_hooks_async(hooks: dict[HookPoint, list[Hook]]) -> None:
"""
Warm up every hook, awaiting `warm_up_async` when defined and falling back to `warm_up` otherwise.
:param hooks: Hooks keyed by hook point. Each distinct hook is warmed up at most once.
"""
for h in _unique_hooks(hooks):
warm_up_async = getattr(h, "warm_up_async", None)
if warm_up_async is not None:
await warm_up_async()
elif hasattr(h, "warm_up"):
h.warm_up()
def close_hooks(hooks: dict[HookPoint, list[Hook]]) -> None:
"""
Release the resources of every hook that defines a `close` method.
:param hooks: Hooks keyed by hook point. Each distinct hook is closed at most once.
"""
for h in _unique_hooks(hooks):
if hasattr(h, "close"):
h.close()
async def close_hooks_async(hooks: dict[HookPoint, list[Hook]]) -> None:
"""
Release hook resources, awaiting `close_async` when defined and falling back to `close` otherwise.
:param hooks: Hooks keyed by hook point. Each distinct hook is closed at most once.
"""
for h in _unique_hooks(hooks):
close_async = getattr(h, "close_async", None)
if close_async is not None:
await close_async()
elif hasattr(h, "close"):
h.close()