Pulse_ER_env / tool_availability.py
KChad's picture
Add all docs_assets image assets to Hugging Face Space snapshot
9b1756a
"""Consumer-side validation for backend-exposed tool availability.
This module keeps Person 2's prompt and policy layers fail-closed. The mock and
real backends may evolve independently during integration, so we validate the
adapter contract before building prompts or selecting actions.
"""
from __future__ import annotations
from .tool_catalog import TOOL_SPEC_BY_NAME
class ToolAvailabilityError(ValueError):
"""Raised when the backend omits or corrupts the available-tools contract."""
def validate_tool_availability(available_tools: list[str] | None) -> list[str]:
"""Validate adapter-exposed tools and return a normalized list.
The consumer stack must fail closed here: a missing or empty tool list is a
contract violation during integration, not a signal to expose the full
catalog speculatively.
"""
if available_tools is None:
raise ToolAvailabilityError(
"available_tools is missing from the backend contract; refusing to build prompts or select actions."
)
if not isinstance(available_tools, list):
raise ToolAvailabilityError(
f"available_tools must be a list of tool names, received {type(available_tools).__name__}."
)
normalized = [tool_name for tool_name in available_tools if isinstance(tool_name, str) and tool_name.strip()]
if not normalized:
raise ToolAvailabilityError(
"available_tools is empty; the backend must expose at least one supported tool."
)
unknown_tools = sorted(set(normalized) - set(TOOL_SPEC_BY_NAME))
if unknown_tools:
raise ToolAvailabilityError(
f"available_tools contains unknown tool names: {', '.join(unknown_tools)}."
)
return normalized