mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-06-06 20:15:17 +02:00
refactor(agents): evict mac-only tools/middleware from shared kernel
These were never shared with anonymous_chat (nor podcaster/video_presentation)
-- only multi_agent_chat (subagents/main agent) and the boundary use them:
shared/tools/mcp/ -> multi_agent_chat/shared/tools/mcp/
shared/tools/hitl.py -> multi_agent_chat/shared/tools/hitl.py
shared/tools/catalog.py -> multi_agent_chat/shared/tools/catalog.py
shared/middleware/dedup_tool_calls.py
-> multi_agent_chat/shared/middleware/dedup_tool_calls.py
app/agents/shared/ now holds only the genuine anon<->mac kernel:
context, middleware/{compaction,retry_after}, tools/web_search.
This commit is contained in:
parent
b7ea829371
commit
d59bb2b5aa
21 changed files with 50 additions and 40 deletions
|
|
@ -29,7 +29,7 @@ from langchain.agents.middleware import AgentMiddleware, AgentState
|
||||||
from langchain_core.tools import BaseTool
|
from langchain_core.tools import BaseTool
|
||||||
from langgraph.runtime import Runtime
|
from langgraph.runtime import Runtime
|
||||||
|
|
||||||
from app.agents.shared.middleware.dedup_tool_calls import (
|
from app.agents.multi_agent_chat.shared.middleware.dedup_tool_calls import (
|
||||||
DedupResolver,
|
DedupResolver,
|
||||||
wrap_dedup_key_by_arg_name,
|
wrap_dedup_key_by_arg_name,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ factories for those few tools and nothing else, so the main agent's tool
|
||||||
surface stays self-contained and connector-free.
|
surface stays self-contained and connector-free.
|
||||||
|
|
||||||
Tool *display* metadata for the whole app (the ``/agent/tools`` listing
|
Tool *display* metadata for the whole app (the ``/agent/tools`` listing
|
||||||
endpoint) lives separately in :mod:`app.agents.shared.tools.catalog`, a
|
endpoint) lives separately in :mod:`app.agents.multi_agent_chat.shared.tools.catalog`, a
|
||||||
pure-data module that imports no connectors. This registry only governs what
|
pure-data module that imports no connectors. This registry only governs what
|
||||||
the main agent actually builds and binds.
|
the main agent actually builds and binds.
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
"""Tools shared across multi_agent_chat (main agent + subagents + boundary)."""
|
||||||
|
|
@ -6,7 +6,7 @@ shared by every sensitive tool (native connectors and MCP tools alike).
|
||||||
|
|
||||||
Usage inside a tool::
|
Usage inside a tool::
|
||||||
|
|
||||||
from app.agents.shared.tools.hitl import request_approval
|
from app.agents.multi_agent_chat.shared.tools.hitl import request_approval
|
||||||
|
|
||||||
result = request_approval(
|
result = request_approval(
|
||||||
action_type="gmail_email_send",
|
action_type="gmail_email_send",
|
||||||
|
|
@ -112,7 +112,9 @@ def refresh_mcp_tools_cache_for_connector(
|
||||||
when an event loop is available. Neither path raises.
|
when an event loop is available. Neither path raises.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
from app.agents.shared.tools.mcp.tool import invalidate_mcp_tools_cache
|
from app.agents.multi_agent_chat.shared.tools.mcp.tool import (
|
||||||
|
invalidate_mcp_tools_cache,
|
||||||
|
)
|
||||||
|
|
||||||
invalidate_mcp_tools_cache(search_space_id)
|
invalidate_mcp_tools_cache(search_space_id)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
@ -133,7 +135,9 @@ def refresh_mcp_tools_cache_for_connector(
|
||||||
|
|
||||||
|
|
||||||
async def _run_connector_prefetch(connector_id: int) -> None:
|
async def _run_connector_prefetch(connector_id: int) -> None:
|
||||||
from app.agents.shared.tools.mcp.tool import discover_single_mcp_connector
|
from app.agents.multi_agent_chat.shared.tools.mcp.tool import (
|
||||||
|
discover_single_mcp_connector,
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await discover_single_mcp_connector(connector_id)
|
await discover_single_mcp_connector(connector_id)
|
||||||
|
|
@ -33,14 +33,16 @@ from sqlalchemy import cast, select
|
||||||
from sqlalchemy.dialects.postgresql import JSONB
|
from sqlalchemy.dialects.postgresql import JSONB
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from app.agents.shared.middleware.dedup_tool_calls import dedup_key_full_args
|
from app.agents.multi_agent_chat.shared.middleware.dedup_tool_calls import (
|
||||||
from app.agents.shared.tools.hitl import request_approval
|
dedup_key_full_args,
|
||||||
from app.agents.shared.tools.mcp.cache import (
|
)
|
||||||
|
from app.agents.multi_agent_chat.shared.tools.hitl import request_approval
|
||||||
|
from app.agents.multi_agent_chat.shared.tools.mcp.cache import (
|
||||||
CachedMCPTools,
|
CachedMCPTools,
|
||||||
read_cached_tools,
|
read_cached_tools,
|
||||||
write_cached_tools,
|
write_cached_tools,
|
||||||
)
|
)
|
||||||
from app.agents.shared.tools.mcp.client import MCPClient
|
from app.agents.multi_agent_chat.shared.tools.mcp.client import MCPClient
|
||||||
from app.db import SearchSourceConnector
|
from app.db import SearchSourceConnector
|
||||||
from app.services.mcp_oauth.registry import MCP_SERVICES, get_service_by_connector_type
|
from app.services.mcp_oauth.registry import MCP_SERVICES, get_service_by_connector_type
|
||||||
from app.utils.perf import get_perf_logger
|
from app.utils.perf import get_perf_logger
|
||||||
|
|
@ -21,7 +21,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from app.agents.multi_agent_chat.constants import (
|
from app.agents.multi_agent_chat.constants import (
|
||||||
CONNECTOR_TYPE_TO_CONNECTOR_AGENT_MAPS,
|
CONNECTOR_TYPE_TO_CONNECTOR_AGENT_MAPS,
|
||||||
)
|
)
|
||||||
from app.agents.shared.tools.mcp.tool import load_mcp_tools
|
from app.agents.multi_agent_chat.shared.tools.mcp.tool import load_mcp_tools
|
||||||
from app.db import SearchSourceConnector
|
from app.db import SearchSourceConnector
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,5 @@
|
||||||
"""Cross-agent shared tools and tool metadata.
|
"""Cross-agent shared tools.
|
||||||
|
|
||||||
Tool *implementations* live with the agents that own them (e.g. deliverable
|
Only genuinely cross-agent tool code lives here (currently web_search, imported
|
||||||
generators and their knowledge-base search helper under
|
directly from its module).
|
||||||
``subagents/builtins/deliverables/tools``). This package holds only the
|
|
||||||
genuinely shared piece: the display-metadata catalog.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from .catalog import TOOL_CATALOG, ToolMetadata
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
"TOOL_CATALOG",
|
|
||||||
"ToolMetadata",
|
|
||||||
]
|
|
||||||
|
|
|
||||||
|
|
@ -665,7 +665,7 @@ def _refresh_mcp_cache(connector_id: int, space_id: int) -> None:
|
||||||
isolated from the OAuth response flow.
|
isolated from the OAuth response flow.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
from app.agents.shared.tools.mcp.cache import (
|
from app.agents.multi_agent_chat.shared.tools.mcp.cache import (
|
||||||
refresh_mcp_tools_cache_for_connector,
|
refresh_mcp_tools_cache_for_connector,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1668,7 +1668,7 @@ async def list_agent_tools(
|
||||||
|
|
||||||
Hidden (WIP) tools are excluded from the response.
|
Hidden (WIP) tools are excluded from the response.
|
||||||
"""
|
"""
|
||||||
from app.agents.shared.tools.catalog import TOOL_CATALOG
|
from app.agents.multi_agent_chat.shared.tools.catalog import TOOL_CATALOG
|
||||||
|
|
||||||
return [
|
return [
|
||||||
AgentToolInfo(
|
AgentToolInfo(
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ from app.db import (
|
||||||
async_session_maker,
|
async_session_maker,
|
||||||
get_async_session,
|
get_async_session,
|
||||||
)
|
)
|
||||||
|
from app.notifications.service import NotificationService
|
||||||
from app.observability import metrics as ot_metrics, otel as ot
|
from app.observability import metrics as ot_metrics, otel as ot
|
||||||
from app.schemas import (
|
from app.schemas import (
|
||||||
GoogleDriveIndexRequest,
|
GoogleDriveIndexRequest,
|
||||||
|
|
@ -55,7 +56,6 @@ from app.schemas import (
|
||||||
SearchSourceConnectorUpdate,
|
SearchSourceConnectorUpdate,
|
||||||
)
|
)
|
||||||
from app.services.composio_service import ComposioService, get_composio_service
|
from app.services.composio_service import ComposioService, get_composio_service
|
||||||
from app.notifications.service import NotificationService
|
|
||||||
from app.users import current_active_user
|
from app.users import current_active_user
|
||||||
|
|
||||||
# NOTE: connector indexer functions are imported lazily inside each
|
# NOTE: connector indexer functions are imported lazily inside each
|
||||||
|
|
@ -675,7 +675,9 @@ async def delete_search_source_connector(
|
||||||
await session.commit()
|
await session.commit()
|
||||||
|
|
||||||
if is_mcp:
|
if is_mcp:
|
||||||
from app.agents.shared.tools.mcp.tool import invalidate_mcp_tools_cache
|
from app.agents.multi_agent_chat.shared.tools.mcp.tool import (
|
||||||
|
invalidate_mcp_tools_cache,
|
||||||
|
)
|
||||||
|
|
||||||
invalidate_mcp_tools_cache(search_space_id)
|
invalidate_mcp_tools_cache(search_space_id)
|
||||||
|
|
||||||
|
|
@ -2687,7 +2689,7 @@ async def create_mcp_connector(
|
||||||
f"for user {user.id} in search space {search_space_id}"
|
f"for user {user.id} in search space {search_space_id}"
|
||||||
)
|
)
|
||||||
|
|
||||||
from app.agents.shared.tools.mcp.cache import (
|
from app.agents.multi_agent_chat.shared.tools.mcp.cache import (
|
||||||
refresh_mcp_tools_cache_for_connector,
|
refresh_mcp_tools_cache_for_connector,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -2867,7 +2869,7 @@ async def update_mcp_connector(
|
||||||
|
|
||||||
logger.info(f"Updated MCP connector {connector_id}")
|
logger.info(f"Updated MCP connector {connector_id}")
|
||||||
|
|
||||||
from app.agents.shared.tools.mcp.cache import (
|
from app.agents.multi_agent_chat.shared.tools.mcp.cache import (
|
||||||
refresh_mcp_tools_cache_for_connector,
|
refresh_mcp_tools_cache_for_connector,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -2927,7 +2929,9 @@ async def delete_mcp_connector(
|
||||||
await session.delete(connector)
|
await session.delete(connector)
|
||||||
await session.commit()
|
await session.commit()
|
||||||
|
|
||||||
from app.agents.shared.tools.mcp.tool import invalidate_mcp_tools_cache
|
from app.agents.multi_agent_chat.shared.tools.mcp.tool import (
|
||||||
|
invalidate_mcp_tools_cache,
|
||||||
|
)
|
||||||
|
|
||||||
invalidate_mcp_tools_cache(search_space_id)
|
invalidate_mcp_tools_cache(search_space_id)
|
||||||
|
|
||||||
|
|
@ -2966,7 +2970,7 @@ async def test_mcp_server_connection(
|
||||||
Connection status and list of available tools
|
Connection status and list of available tools
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
from app.agents.shared.tools.mcp.client import (
|
from app.agents.multi_agent_chat.shared.tools.mcp.client import (
|
||||||
test_mcp_connection,
|
test_mcp_connection,
|
||||||
test_mcp_http_connection,
|
test_mcp_http_connection,
|
||||||
)
|
)
|
||||||
|
|
@ -3157,7 +3161,9 @@ async def trust_mcp_tool(
|
||||||
connectors (``LINEAR_CONNECTOR``, ``JIRA_CONNECTOR``, ...) — the
|
connectors (``LINEAR_CONNECTOR``, ``JIRA_CONNECTOR``, ...) — the
|
||||||
storage primitive is the same JSON list under ``config.trusted_tools``.
|
storage primitive is the same JSON list under ``config.trusted_tools``.
|
||||||
"""
|
"""
|
||||||
from app.agents.shared.tools.mcp.tool import invalidate_mcp_tools_cache
|
from app.agents.multi_agent_chat.shared.tools.mcp.tool import (
|
||||||
|
invalidate_mcp_tools_cache,
|
||||||
|
)
|
||||||
from app.services.user_tool_allowlist import add_user_trust
|
from app.services.user_tool_allowlist import add_user_trust
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -3197,7 +3203,9 @@ async def untrust_mcp_tool(
|
||||||
|
|
||||||
The tool will require HITL approval again on subsequent calls.
|
The tool will require HITL approval again on subsequent calls.
|
||||||
"""
|
"""
|
||||||
from app.agents.shared.tools.mcp.tool import invalidate_mcp_tools_cache
|
from app.agents.multi_agent_chat.shared.tools.mcp.tool import (
|
||||||
|
invalidate_mcp_tools_cache,
|
||||||
|
)
|
||||||
from app.services.user_tool_allowlist import remove_user_trust
|
from app.services.user_tool_allowlist import remove_user_trust
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -137,10 +137,10 @@ def install(active_patches: list[Any]) -> None:
|
||||||
"""Patch production MCP streamable-HTTP boundaries exactly once."""
|
"""Patch production MCP streamable-HTTP boundaries exactly once."""
|
||||||
targets = [
|
targets = [
|
||||||
(
|
(
|
||||||
"app.agents.shared.tools.mcp.tool.streamablehttp_client",
|
"app.agents.multi_agent_chat.shared.tools.mcp.tool.streamablehttp_client",
|
||||||
_fake_streamablehttp_client,
|
_fake_streamablehttp_client,
|
||||||
),
|
),
|
||||||
("app.agents.shared.tools.mcp.tool.ClientSession", _FakeClientSession),
|
("app.agents.multi_agent_chat.shared.tools.mcp.tool.ClientSession", _FakeClientSession),
|
||||||
]
|
]
|
||||||
for target, replacement in targets:
|
for target, replacement in targets:
|
||||||
p = patch(target, replacement)
|
p = patch(target, replacement)
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,9 @@ def test_full_args_dedup_keeps_distinct_calls_sharing_a_field() -> None:
|
||||||
|
|
||||||
With :func:`dedup_key_full_args` only fully identical arg dicts dedup.
|
With :func:`dedup_key_full_args` only fully identical arg dicts dedup.
|
||||||
"""
|
"""
|
||||||
from app.agents.shared.middleware.dedup_tool_calls import dedup_key_full_args
|
from app.agents.multi_agent_chat.shared.middleware.dedup_tool_calls import (
|
||||||
|
dedup_key_full_args,
|
||||||
|
)
|
||||||
|
|
||||||
tool = _make_tool("createJiraIssue", dedup_key=dedup_key_full_args)
|
tool = _make_tool("createJiraIssue", dedup_key=dedup_key_full_args)
|
||||||
mw = DedupHITLToolCallsMiddleware(agent_tools=[tool])
|
mw = DedupHITLToolCallsMiddleware(agent_tools=[tool])
|
||||||
|
|
@ -157,7 +159,9 @@ def test_full_args_dedup_keeps_distinct_calls_sharing_a_field() -> None:
|
||||||
|
|
||||||
|
|
||||||
def test_full_args_dedup_drops_only_exact_duplicates() -> None:
|
def test_full_args_dedup_drops_only_exact_duplicates() -> None:
|
||||||
from app.agents.shared.middleware.dedup_tool_calls import dedup_key_full_args
|
from app.agents.multi_agent_chat.shared.middleware.dedup_tool_calls import (
|
||||||
|
dedup_key_full_args,
|
||||||
|
)
|
||||||
|
|
||||||
tool = _make_tool("createJiraIssue", dedup_key=dedup_key_full_args)
|
tool = _make_tool("createJiraIssue", dedup_key=dedup_key_full_args)
|
||||||
mw = DedupHITLToolCallsMiddleware(agent_tools=[tool])
|
mw = DedupHITLToolCallsMiddleware(agent_tools=[tool])
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ caused two production-painful behaviors:
|
||||||
read-only tool calls, raising ``RejectedError("ls")``.
|
read-only tool calls, raising ``RejectedError("ls")``.
|
||||||
* Mutating connector tools got *double* prompted — once via the
|
* Mutating connector tools got *double* prompted — once via the
|
||||||
middleware ``ask`` and again via the per-tool ``interrupt()`` in
|
middleware ``ask`` and again via the per-tool ``interrupt()`` in
|
||||||
``app.agents.shared.tools.hitl``.
|
``app.agents.multi_agent_chat.shared.tools.hitl``.
|
||||||
|
|
||||||
These tests pin the layering so a refactor that drops the default
|
These tests pin the layering so a refactor that drops the default
|
||||||
ruleset fails loud.
|
ruleset fails loud.
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from app.agents.shared.tools.hitl import (
|
from app.agents.multi_agent_chat.shared.tools.hitl import (
|
||||||
DEFAULT_AUTO_APPROVED_TOOLS,
|
DEFAULT_AUTO_APPROVED_TOOLS,
|
||||||
HITLResult,
|
HITLResult,
|
||||||
request_approval,
|
request_approval,
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ from types import SimpleNamespace
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from app.agents.shared.tools.mcp.cache import (
|
from app.agents.multi_agent_chat.shared.tools.mcp.cache import (
|
||||||
CachedMCPToolDef,
|
CachedMCPToolDef,
|
||||||
CachedMCPTools,
|
CachedMCPTools,
|
||||||
read_cached_tools,
|
read_cached_tools,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ from langchain_core.tools import StructuredTool
|
||||||
from app.agents.multi_agent_chat.main_agent.middleware.dedup_hitl import (
|
from app.agents.multi_agent_chat.main_agent.middleware.dedup_hitl import (
|
||||||
DedupHITLToolCallsMiddleware,
|
DedupHITLToolCallsMiddleware,
|
||||||
)
|
)
|
||||||
from app.agents.shared.middleware.dedup_tool_calls import (
|
from app.agents.multi_agent_chat.shared.middleware.dedup_tool_calls import (
|
||||||
wrap_dedup_key_by_arg_name,
|
wrap_dedup_key_by_arg_name,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue