chore: import upstream snapshot with attribution
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
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
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
# 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()
|
||||
Reference in New Issue
Block a user