multi_agent_chat/shared: drop bucket types and helpers

This commit is contained in:
CREDO23 2026-05-14 20:10:25 +02:00
parent 014801c764
commit 31d6b43a42
7 changed files with 2 additions and 173 deletions

View file

@ -5,19 +5,13 @@ from __future__ import annotations
from app.agents.multi_agent_chat.subagents.shared.md_file_reader import (
read_md_file,
)
from app.agents.multi_agent_chat.subagents.shared.spec import SurfSenseSubagentSpec
from app.agents.multi_agent_chat.subagents.shared.subagent_builder import (
pack_subagent,
)
from app.agents.multi_agent_chat.subagents.shared.tool_kinds import (
ToolPermissionItem,
ToolsPermissions,
merge_tools_permissions,
)
__all__ = [
"ToolPermissionItem",
"ToolsPermissions",
"merge_tools_permissions",
"SurfSenseSubagentSpec",
"pack_subagent",
"read_md_file",
]

View file

@ -1,19 +0,0 @@
"""Middleware-gated approval primitives — interception via langchain middlewares.
Public surface:
- :func:`middleware_gated_tool_permission_row` tag a tool's row for interception.
- :func:`middleware_gated_interrupt_on` build the ``interrupt_on`` map fed
into ``HumanInTheLoopMiddleware``.
The actual ``HumanInTheLoopMiddleware`` and ``PermissionMiddleware`` instances
that consume these helpers live under
``middleware/shared/permissions/`` (rule-engine slice).
"""
from .interrupt_on import middleware_gated_interrupt_on
from .tool_row import middleware_gated_tool_permission_row
__all__ = [
"middleware_gated_interrupt_on",
"middleware_gated_tool_permission_row",
]

View file

@ -1,23 +0,0 @@
"""Build the ``interrupt_on`` map fed into ``HumanInTheLoopMiddleware``.
The map keys are tool names whose execution must be intercepted before
the call runs. Self-gated rows are intentionally excluded: their bodies
already pause via :func:`request_approval`, and intercepting them too
would double-prompt the user.
"""
from __future__ import annotations
from app.agents.multi_agent_chat.subagents.shared.tool_kinds import ToolsPermissions
def middleware_gated_interrupt_on(bucket: ToolsPermissions) -> dict[str, bool]:
"""``interrupt_on`` map for ``ask`` rows whose bodies don't self-gate."""
return {
r["name"]: True
for r in bucket["ask"]
if r.get("name") and r.get("kind") == "middleware_gated"
}
__all__ = ["middleware_gated_interrupt_on"]

View file

@ -1,27 +0,0 @@
"""Row builder tagging a tool for middleware-gated approval.
Used by MCP tool loading (``mcp_tools/index.py``) so each row carries
``kind="middleware_gated"`` and surfaces in :func:`middleware_gated_interrupt_on`.
Self-gated factories don't call this — they build rows inline with the
default ``kind`` (which collapses to self-gated).
"""
from __future__ import annotations
from langchain_core.tools import BaseTool
from app.agents.multi_agent_chat.subagents.shared.tool_kinds import (
ToolPermissionItem,
)
def middleware_gated_tool_permission_row(tool: BaseTool) -> ToolPermissionItem:
"""Build one allow/ask row tagged ``kind="middleware_gated"``."""
return {
"name": getattr(tool, "name", "") or "",
"tool": tool,
"kind": "middleware_gated",
}
__all__ = ["middleware_gated_tool_permission_row"]

View file

@ -2,7 +2,6 @@
Public surface:
- :func:`request_approval` entry point for sensitive tool bodies.
- :func:`self_gated_tool_permission_row` build an allow/ask row for a self-gated tool.
- :class:`HITLResult` outcome contract.
- ``DEFAULT_AUTO_APPROVED_TOOLS`` safe-by-construction allowlist.
"""
@ -10,11 +9,9 @@ Public surface:
from .auto_approved import DEFAULT_AUTO_APPROVED_TOOLS
from .request import request_approval
from .result import HITLResult
from .tool_row import self_gated_tool_permission_row
__all__ = [
"DEFAULT_AUTO_APPROVED_TOOLS",
"HITLResult",
"request_approval",
"self_gated_tool_permission_row",
]

View file

@ -1,24 +0,0 @@
"""Row builder for tools that self-gate via :func:`request_approval`.
The default ``kind`` is omitted on purpose: ``ToolPermissionItem`` defaults
to ``self_gated`` when ``kind`` is absent, so the row stays compact while
keeping the type system honest. Symmetric with
:mod:`hitl.approvals.middleware_gated.tool_row` so connector factories can
read the same way for either kind.
"""
from __future__ import annotations
from langchain_core.tools import BaseTool
from app.agents.multi_agent_chat.subagents.shared.tool_kinds import (
ToolPermissionItem,
)
def self_gated_tool_permission_row(tool: BaseTool) -> ToolPermissionItem:
"""Build one allow/ask row for a self-gated tool (body calls ``request_approval``)."""
return {"name": getattr(tool, "name", "") or "", "tool": tool}
__all__ = ["self_gated_tool_permission_row"]

View file

@ -1,69 +0,0 @@
"""Cross-kind primitives for tool permission rows.
Subagents classify their tools into ``allow`` and ``ask`` buckets, and each
row may be either *self-gated* (the tool body calls
:func:`request_approval`) or *middleware-gated* (a middleware intercepts
the call). This module owns the shared types both kinds need:
- :data:`ToolKind` the discriminator literal.
- :class:`ToolPermissionItem` one row in an allow/ask bucket.
- :class:`ToolsPermissions` the bucket pair.
- :func:`merge_tools_permissions` concatenates two buckets (typically a
self-gated factory bucket and a middleware-gated MCP bucket).
Kind-specific helpers live under ``hitl/approvals/`` next to their gating
mechanism:
- ``hitl/approvals/self_gated/`` body-level ``request_approval`` primitive.
- ``hitl/approvals/middleware_gated/`` row builder + ``interrupt_on`` map.
"""
from __future__ import annotations
from typing import Literal, NotRequired, TypedDict
from langchain_core.tools import BaseTool
ToolKind = Literal["self_gated", "middleware_gated"]
class ToolPermissionItem(TypedDict):
"""One allow/ask row.
``name`` is always set; ``tool`` is present when a bound BaseTool exists
(absent for name-only MCP allow/ask rows). ``kind`` defaults to
``self_gated`` when absent so existing connector factories keep working
without explicit tagging.
"""
name: str
tool: NotRequired[BaseTool]
kind: NotRequired[ToolKind]
class ToolsPermissions(TypedDict):
"""Allow/ask buckets shared by self-gated factories and middleware-gated MCP rows."""
allow: list[ToolPermissionItem]
ask: list[ToolPermissionItem]
def merge_tools_permissions(
base: ToolsPermissions,
extra: ToolsPermissions | None,
) -> ToolsPermissions:
"""Concatenate allow/ask lists (e.g. self-gated factory + middleware-gated MCP) before building HITL maps."""
if not extra:
return base
return {
"allow": [*base["allow"], *extra["allow"]],
"ask": [*base["ask"], *extra["ask"]],
}
__all__ = [
"ToolKind",
"ToolPermissionItem",
"ToolsPermissions",
"merge_tools_permissions",
]