mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-08 15:22:39 +02:00
Refactor multi-agent supervisor prompts and wiring; thread citations into streaming.
This commit is contained in:
parent
74337b462a
commit
ba131f5736
28 changed files with 286 additions and 161 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
"""Search-space / DB kwargs shared by ``new_chat`` tool factories (distinct from ``expert_agent.connectors`` integrations)."""
|
"""Search-space / DB kwargs shared by main-chat tool factories (distinct from ``expert_agent.connectors`` integrations)."""
|
||||||
|
|
||||||
from app.agents.multi_agent_chat.core.bindings.binding import connector_binding
|
from app.agents.multi_agent_chat.core.bindings.binding import connector_binding
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
"""Shared kwargs dict for ``new_chat`` tool factories (DB session + search space + user)."""
|
"""Shared kwargs dict for main-chat tool factories (DB session + search space + user)."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
"""Partition MCP tools onto multi-agent expert routes without modifying ``new_chat``.
|
"""Partition MCP tools onto multi-agent expert routes (read-only; does not change the MCP loader).
|
||||||
|
|
||||||
Uses the same connector discovery shape as ``load_mcp_tools`` (copied query below). Tools come from
|
Uses the same connector discovery shape as ``load_mcp_tools`` (copied query below). Tools come from
|
||||||
``app.agents.new_chat.tools.mcp_tool.load_mcp_tools``; routing uses metadata already set there:
|
``app.agents.new_chat.tools.mcp_tool.load_mcp_tools``; routing uses metadata already set there:
|
||||||
|
|
@ -61,7 +61,7 @@ async def fetch_mcp_connector_metadata_maps(
|
||||||
) -> tuple[dict[int, str], dict[str, str]]:
|
) -> tuple[dict[int, str], dict[str, str]]:
|
||||||
"""Read-only copy of connector discovery used alongside ``load_mcp_tools``.
|
"""Read-only copy of connector discovery used alongside ``load_mcp_tools``.
|
||||||
|
|
||||||
Same filter as ``new_chat.tools.mcp_tool.load_mcp_tools`` (connectors with ``server_config``).
|
Same filter as :func:`app.agents.new_chat.tools.mcp_tool.load_mcp_tools` (connectors with ``server_config``).
|
||||||
"""
|
"""
|
||||||
result = await session.execute(
|
result = await session.execute(
|
||||||
select(SearchSourceConnector).filter(
|
select(SearchSourceConnector).filter(
|
||||||
|
|
@ -90,7 +90,7 @@ def partition_mcp_tools_by_expert_route(
|
||||||
) -> dict[str, list[BaseTool]]:
|
) -> dict[str, list[BaseTool]]:
|
||||||
"""Bucket MCP tools by expert route key. Supervisor never receives raw MCP tools.
|
"""Bucket MCP tools by expert route key. Supervisor never receives raw MCP tools.
|
||||||
|
|
||||||
Same inclusion rule as ``new_chat.tools.registry.build_tools_async``: all tools returned by
|
Same inclusion rule as :func:`app.agents.new_chat.tools.registry.build_tools_async`: all tools returned by
|
||||||
``load_mcp_tools`` are partitioned — connector availability for **registry** builtins is handled via
|
``load_mcp_tools`` are partitioned — connector availability for **registry** builtins is handled via
|
||||||
``get_connector_gated_tools`` / routing gates; MCP tools are not pre-filtered by inventory here.
|
``get_connector_gated_tools`` / routing gates; MCP tools are not pre-filtered by inventory here.
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
"""``new_chat`` tool registry grouping + dependency bundles for domain slices."""
|
"""Main chat tool registry grouping + dependency bundles for domain slices."""
|
||||||
|
|
||||||
from app.agents.multi_agent_chat.core.registry.categories import (
|
from app.agents.multi_agent_chat.core.registry.categories import (
|
||||||
REGISTRY_ROUTING_CATEGORY_KEYS,
|
REGISTRY_ROUTING_CATEGORY_KEYS,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
"""Dependency dict for :func:`app.agents.new_chat.tools.registry.build_tools` in multi-agent graphs."""
|
"""Dependency dict for :func:`app.agents.new_chat.tools.registry.build_tools` on expert subgraphs."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
"""Build :mod:`new_chat` registry tool subsets for multi-agent domain slices."""
|
"""Build registry tool subsets (``app.agents.new_chat.tools.registry``) for multi-agent domain slices."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,5 @@ from app.agents.multi_agent_chat.core.registry import build_registry_tools_for_c
|
||||||
|
|
||||||
|
|
||||||
def build_deliverables_tools(dependencies: dict[str, Any]) -> list[BaseTool]:
|
def build_deliverables_tools(dependencies: dict[str, Any]) -> list[BaseTool]:
|
||||||
"""Tools from ``new_chat`` registry: ``deliverables`` category."""
|
"""Registry-backed tools for the ``deliverables`` category."""
|
||||||
return build_registry_tools_for_category(dependencies, "deliverables")
|
return build_registry_tools_for_category(dependencies, "deliverables")
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,5 @@ from app.agents.multi_agent_chat.core.registry import build_registry_tools_for_c
|
||||||
|
|
||||||
|
|
||||||
def build_memory_tools(dependencies: dict[str, Any]) -> list[BaseTool]:
|
def build_memory_tools(dependencies: dict[str, Any]) -> list[BaseTool]:
|
||||||
"""Tools from ``new_chat`` registry: ``memory`` category."""
|
"""Registry-backed tools for the ``memory`` category."""
|
||||||
return build_registry_tools_for_category(dependencies, "memory")
|
return build_registry_tools_for_category(dependencies, "memory")
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,5 @@ from app.agents.multi_agent_chat.core.registry import build_registry_tools_for_c
|
||||||
|
|
||||||
|
|
||||||
def build_research_tools(dependencies: dict[str, Any]) -> list[BaseTool]:
|
def build_research_tools(dependencies: dict[str, Any]) -> list[BaseTool]:
|
||||||
"""Tools from ``new_chat`` registry: ``research`` category."""
|
"""Registry-backed tools for the ``research`` category."""
|
||||||
return build_registry_tools_for_category(dependencies, "research")
|
return build_registry_tools_for_category(dependencies, "research")
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,5 @@ from app.agents.multi_agent_chat.core.registry import build_registry_tools_for_c
|
||||||
|
|
||||||
|
|
||||||
def build_calendar_tools(dependencies: dict[str, Any]) -> list[BaseTool]:
|
def build_calendar_tools(dependencies: dict[str, Any]) -> list[BaseTool]:
|
||||||
"""Tools from ``new_chat`` registry: ``calendar`` category."""
|
"""Registry-backed tools for the ``calendar`` category."""
|
||||||
return build_registry_tools_for_category(dependencies, "calendar")
|
return build_registry_tools_for_category(dependencies, "calendar")
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,5 @@ from app.agents.multi_agent_chat.core.registry import build_registry_tools_for_c
|
||||||
|
|
||||||
|
|
||||||
def build_confluence_tools(dependencies: dict[str, Any]) -> list[BaseTool]:
|
def build_confluence_tools(dependencies: dict[str, Any]) -> list[BaseTool]:
|
||||||
"""Tools from ``new_chat`` registry: ``confluence`` category."""
|
"""Registry-backed tools for the ``confluence`` category."""
|
||||||
return build_registry_tools_for_category(dependencies, "confluence")
|
return build_registry_tools_for_category(dependencies, "confluence")
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,5 @@ from app.agents.multi_agent_chat.core.registry import build_registry_tools_for_c
|
||||||
|
|
||||||
|
|
||||||
def build_discord_tools(dependencies: dict[str, Any]) -> list[BaseTool]:
|
def build_discord_tools(dependencies: dict[str, Any]) -> list[BaseTool]:
|
||||||
"""Tools from ``new_chat`` registry: ``discord`` category."""
|
"""Registry-backed tools for the ``discord`` category."""
|
||||||
return build_registry_tools_for_category(dependencies, "discord")
|
return build_registry_tools_for_category(dependencies, "discord")
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,5 @@ from app.agents.multi_agent_chat.core.registry import build_registry_tools_for_c
|
||||||
|
|
||||||
|
|
||||||
def build_dropbox_tools(dependencies: dict[str, Any]) -> list[BaseTool]:
|
def build_dropbox_tools(dependencies: dict[str, Any]) -> list[BaseTool]:
|
||||||
"""Tools from ``new_chat`` registry: ``dropbox`` category."""
|
"""Registry-backed tools for the ``dropbox`` category."""
|
||||||
return build_registry_tools_for_category(dependencies, "dropbox")
|
return build_registry_tools_for_category(dependencies, "dropbox")
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,5 @@ from app.agents.multi_agent_chat.core.registry import build_registry_tools_for_c
|
||||||
|
|
||||||
|
|
||||||
def build_gmail_tools(dependencies: dict[str, Any]) -> list[BaseTool]:
|
def build_gmail_tools(dependencies: dict[str, Any]) -> list[BaseTool]:
|
||||||
"""Tools from ``new_chat`` registry: ``gmail`` category."""
|
"""Registry-backed tools for the ``gmail`` category."""
|
||||||
return build_registry_tools_for_category(dependencies, "gmail")
|
return build_registry_tools_for_category(dependencies, "gmail")
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,5 @@ from app.agents.multi_agent_chat.core.registry import build_registry_tools_for_c
|
||||||
|
|
||||||
|
|
||||||
def build_google_drive_tools(dependencies: dict[str, Any]) -> list[BaseTool]:
|
def build_google_drive_tools(dependencies: dict[str, Any]) -> list[BaseTool]:
|
||||||
"""Tools from ``new_chat`` registry: ``google_drive`` category."""
|
"""Registry-backed tools for the ``google_drive`` category."""
|
||||||
return build_registry_tools_for_category(dependencies, "google_drive")
|
return build_registry_tools_for_category(dependencies, "google_drive")
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,5 @@ from app.agents.multi_agent_chat.core.registry import build_registry_tools_for_c
|
||||||
|
|
||||||
|
|
||||||
def build_luma_tools(dependencies: dict[str, Any]) -> list[BaseTool]:
|
def build_luma_tools(dependencies: dict[str, Any]) -> list[BaseTool]:
|
||||||
"""Tools from ``new_chat`` registry: ``luma`` category."""
|
"""Registry-backed tools for the ``luma`` category."""
|
||||||
return build_registry_tools_for_category(dependencies, "luma")
|
return build_registry_tools_for_category(dependencies, "luma")
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,5 @@ from app.agents.multi_agent_chat.core.registry import build_registry_tools_for_c
|
||||||
|
|
||||||
|
|
||||||
def build_notion_tools(dependencies: dict[str, Any]) -> list[BaseTool]:
|
def build_notion_tools(dependencies: dict[str, Any]) -> list[BaseTool]:
|
||||||
"""Tools from ``new_chat`` registry: ``notion`` category."""
|
"""Registry-backed tools for the ``notion`` category."""
|
||||||
return build_registry_tools_for_category(dependencies, "notion")
|
return build_registry_tools_for_category(dependencies, "notion")
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,5 @@ from app.agents.multi_agent_chat.core.registry import build_registry_tools_for_c
|
||||||
|
|
||||||
|
|
||||||
def build_onedrive_tools(dependencies: dict[str, Any]) -> list[BaseTool]:
|
def build_onedrive_tools(dependencies: dict[str, Any]) -> list[BaseTool]:
|
||||||
"""Tools from ``new_chat`` registry: ``onedrive`` category."""
|
"""Registry-backed tools for the ``onedrive`` category."""
|
||||||
return build_registry_tools_for_category(dependencies, "onedrive")
|
return build_registry_tools_for_category(dependencies, "onedrive")
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,5 @@ from app.agents.multi_agent_chat.core.registry import build_registry_tools_for_c
|
||||||
|
|
||||||
|
|
||||||
def build_teams_tools(dependencies: dict[str, Any]) -> list[BaseTool]:
|
def build_teams_tools(dependencies: dict[str, Any]) -> list[BaseTool]:
|
||||||
"""Tools from ``new_chat`` registry: ``teams`` category."""
|
"""Registry-backed tools for the ``teams`` category."""
|
||||||
return build_registry_tools_for_category(dependencies, "teams")
|
return build_registry_tools_for_category(dependencies, "teams")
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
"""Single entry: SurfSense connectors + multi-agent stack → compiled supervisor graph."""
|
"""Build the multi-agent supervisor graph: MCP partition, registry, routing tools, optional SurfSense middleware."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
@ -11,14 +11,6 @@ from langchain_core.tools import BaseTool
|
||||||
from langgraph.types import Checkpointer
|
from langgraph.types import Checkpointer
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from app.agents.new_chat.chat_deepagent import _map_connectors_to_searchable_types
|
|
||||||
from app.agents.new_chat.context import SurfSenseContextSchema
|
|
||||||
from app.agents.new_chat.feature_flags import get_flags
|
|
||||||
from app.agents.new_chat.filesystem_backends import build_backend_resolver
|
|
||||||
from app.agents.new_chat.filesystem_selection import FilesystemSelection
|
|
||||||
from app.agents.new_chat.tools.mcp_tool import load_mcp_tools
|
|
||||||
from app.db import ChatVisibility
|
|
||||||
|
|
||||||
from app.agents.multi_agent_chat.core.mcp_partition import (
|
from app.agents.multi_agent_chat.core.mcp_partition import (
|
||||||
fetch_mcp_connector_metadata_maps,
|
fetch_mcp_connector_metadata_maps,
|
||||||
partition_mcp_tools_by_expert_route,
|
partition_mcp_tools_by_expert_route,
|
||||||
|
|
@ -27,14 +19,95 @@ from app.agents.multi_agent_chat.core.registry.dependencies import (
|
||||||
build_registry_dependencies,
|
build_registry_dependencies,
|
||||||
coerce_thread_id_for_registry,
|
coerce_thread_id_for_registry,
|
||||||
)
|
)
|
||||||
from app.agents.multi_agent_chat.middleware.supervisor_stack import build_supervisor_middleware_stack
|
from app.agents.multi_agent_chat.middleware.supervisor_stack import (
|
||||||
from app.agents.multi_agent_chat.routing.supervisor_routing import build_supervisor_routing_tools
|
build_supervisor_middleware_stack,
|
||||||
|
)
|
||||||
|
from app.agents.multi_agent_chat.routing.supervisor_routing import (
|
||||||
|
build_supervisor_routing_tools,
|
||||||
|
)
|
||||||
from app.agents.multi_agent_chat.supervisor import build_supervisor_agent
|
from app.agents.multi_agent_chat.supervisor import build_supervisor_agent
|
||||||
|
from app.agents.new_chat.chat_deepagent import _map_connectors_to_searchable_types
|
||||||
|
from app.agents.new_chat.context import SurfSenseContextSchema
|
||||||
|
from app.agents.new_chat.feature_flags import get_flags
|
||||||
|
from app.agents.new_chat.filesystem_backends import build_backend_resolver
|
||||||
|
from app.agents.new_chat.filesystem_selection import FilesystemSelection
|
||||||
|
from app.agents.new_chat.tools.mcp_tool import load_mcp_tools
|
||||||
|
from app.db import ChatVisibility
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def _compile_supervisor_chat_blocking(
|
async def _discover_connectors_and_doc_types(
|
||||||
|
*,
|
||||||
|
connector_service: Any | None,
|
||||||
|
search_space_id: int,
|
||||||
|
available_connectors: list[str] | None,
|
||||||
|
available_document_types: list[str] | None,
|
||||||
|
) -> tuple[list[str] | None, list[str] | None]:
|
||||||
|
"""Fill connector / document-type lists from ``connector_service`` when callers omit them."""
|
||||||
|
connectors = available_connectors
|
||||||
|
doc_types = available_document_types
|
||||||
|
if connector_service is None:
|
||||||
|
return connectors, doc_types
|
||||||
|
try:
|
||||||
|
if connectors is None:
|
||||||
|
raw = await connector_service.get_available_connectors(search_space_id)
|
||||||
|
if raw:
|
||||||
|
connectors = _map_connectors_to_searchable_types(raw)
|
||||||
|
if doc_types is None:
|
||||||
|
doc_types = await connector_service.get_available_document_types(search_space_id)
|
||||||
|
except Exception as exc:
|
||||||
|
logger.warning("Failed to discover available connectors/document types: %s", exc)
|
||||||
|
return connectors, doc_types
|
||||||
|
|
||||||
|
|
||||||
|
async def _mcp_tools_by_expert_route(
|
||||||
|
*,
|
||||||
|
db_session: AsyncSession,
|
||||||
|
search_space_id: int,
|
||||||
|
) -> dict[str, list[BaseTool]] | None:
|
||||||
|
mcp_flat = await load_mcp_tools(db_session, search_space_id)
|
||||||
|
id_map, name_map = await fetch_mcp_connector_metadata_maps(db_session, search_space_id)
|
||||||
|
return partition_mcp_tools_by_expert_route(mcp_flat, id_map, name_map)
|
||||||
|
|
||||||
|
|
||||||
|
def _make_supervisor_routing_tools(
|
||||||
|
llm: BaseChatModel,
|
||||||
|
*,
|
||||||
|
db_session: AsyncSession,
|
||||||
|
search_space_id: int,
|
||||||
|
user_id: str,
|
||||||
|
thread_id: str | int | None,
|
||||||
|
firecrawl_api_key: str | None,
|
||||||
|
connector_service: Any | None,
|
||||||
|
available_connectors: list[str] | None,
|
||||||
|
available_document_types: list[str] | None,
|
||||||
|
thread_visibility: ChatVisibility,
|
||||||
|
mcp_tools_by_route: dict[str, list[BaseTool]] | None,
|
||||||
|
) -> list[BaseTool]:
|
||||||
|
registry_dependencies = build_registry_dependencies(
|
||||||
|
db_session=db_session,
|
||||||
|
search_space_id=search_space_id,
|
||||||
|
user_id=user_id,
|
||||||
|
thread_id=thread_id,
|
||||||
|
llm=llm,
|
||||||
|
firecrawl_api_key=firecrawl_api_key,
|
||||||
|
connector_service=connector_service,
|
||||||
|
available_connectors=available_connectors,
|
||||||
|
available_document_types=available_document_types,
|
||||||
|
thread_visibility=thread_visibility,
|
||||||
|
)
|
||||||
|
return build_supervisor_routing_tools(
|
||||||
|
llm,
|
||||||
|
registry_dependencies=registry_dependencies,
|
||||||
|
include_deliverables=coerce_thread_id_for_registry(thread_id) is not None,
|
||||||
|
mcp_tools_by_route=mcp_tools_by_route,
|
||||||
|
available_connectors=available_connectors,
|
||||||
|
thread_visibility=thread_visibility,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _compile_supervisor_agent_sync(
|
||||||
*,
|
*,
|
||||||
llm: BaseChatModel,
|
llm: BaseChatModel,
|
||||||
routing_tools: list[BaseTool],
|
routing_tools: list[BaseTool],
|
||||||
|
|
@ -43,16 +116,16 @@ def _compile_supervisor_chat_blocking(
|
||||||
filesystem_mode: Any,
|
filesystem_mode: Any,
|
||||||
search_space_id: int,
|
search_space_id: int,
|
||||||
user_id: str,
|
user_id: str,
|
||||||
thread_id: str | None,
|
thread_id: str | int | None,
|
||||||
thread_visibility: ChatVisibility,
|
thread_visibility: ChatVisibility,
|
||||||
anon_session_id: str | None,
|
anon_session_id: str | None,
|
||||||
available_connectors: list[str] | None,
|
available_connectors: list[str] | None,
|
||||||
available_document_types: list[str] | None,
|
available_document_types: list[str] | None,
|
||||||
mentioned_document_ids: list[int] | None,
|
mentioned_document_ids: list[int] | None,
|
||||||
max_input_tokens: int | None,
|
max_input_tokens: int | None,
|
||||||
|
citations_enabled: bool,
|
||||||
) -> Any:
|
) -> Any:
|
||||||
"""CPU-heavy: middleware assembly + ``create_agent`` (runs in a worker thread)."""
|
"""CPU-heavy: middleware stack + ``create_agent`` (intended for ``asyncio.to_thread``)."""
|
||||||
flags = get_flags()
|
|
||||||
middleware = build_supervisor_middleware_stack(
|
middleware = build_supervisor_middleware_stack(
|
||||||
llm=llm,
|
llm=llm,
|
||||||
tools=routing_tools,
|
tools=routing_tools,
|
||||||
|
|
@ -67,7 +140,7 @@ def _compile_supervisor_chat_blocking(
|
||||||
available_document_types=available_document_types,
|
available_document_types=available_document_types,
|
||||||
mentioned_document_ids=mentioned_document_ids,
|
mentioned_document_ids=mentioned_document_ids,
|
||||||
max_input_tokens=max_input_tokens,
|
max_input_tokens=max_input_tokens,
|
||||||
flags=flags,
|
flags=get_flags(),
|
||||||
)
|
)
|
||||||
return build_supervisor_agent(
|
return build_supervisor_agent(
|
||||||
llm,
|
llm,
|
||||||
|
|
@ -76,6 +149,7 @@ def _compile_supervisor_chat_blocking(
|
||||||
thread_visibility=thread_visibility,
|
thread_visibility=thread_visibility,
|
||||||
middleware=middleware,
|
middleware=middleware,
|
||||||
context_schema=SurfSenseContextSchema,
|
context_schema=SurfSenseContextSchema,
|
||||||
|
citations_enabled=citations_enabled,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -98,16 +172,17 @@ async def create_multi_agent_chat(
|
||||||
mentioned_document_ids: list[int] | None = None,
|
mentioned_document_ids: list[int] | None = None,
|
||||||
max_input_tokens: int | None = None,
|
max_input_tokens: int | None = None,
|
||||||
surfsense_stack: bool = True,
|
surfsense_stack: bool = True,
|
||||||
|
citations_enabled: bool | None = None,
|
||||||
):
|
):
|
||||||
"""Build the full multi-agent chat graph (supervisor + domain subgraphs via routing tools).
|
"""Build the full multi-agent chat graph (supervisor + expert subgraphs via routing tools).
|
||||||
|
|
||||||
**Builtins** (:mod:`expert_agent.builtins`): registry-grouped **categories** (research, memory, deliverables).
|
**Builtins** (:mod:`expert_agent.builtins`): registry-grouped **categories** (research, memory, deliverables).
|
||||||
**Connectors** (:mod:`expert_agent.connectors`): **vendor integrations** — one subgraph per route in
|
**Connectors** (:mod:`expert_agent.connectors`): **vendor integrations** — one subgraph per route in
|
||||||
``TOOL_NAMES_BY_CATEGORY`` (e.g. calendar, confluence, discord, dropbox, gmail, google_drive, luma, notion, onedrive, teams).
|
``TOOL_NAMES_BY_CATEGORY`` (e.g. calendar, confluence, discord, dropbox, gmail, google_drive, luma, notion, onedrive, teams).
|
||||||
|
|
||||||
MCP tools from ``new_chat`` (``load_mcp_tools``) are partitioned inside this package and attached only
|
MCP tools (via ``load_mcp_tools``) are partitioned inside this package and attached only
|
||||||
to the matching expert subgraphs — not to the supervisor tool list as raw MCP calls. Inclusion matches
|
to the matching expert subgraphs — not to the supervisor tool list as raw MCP calls. Inclusion matches
|
||||||
``new_chat.tools.registry.build_tools_async``: all tools returned by ``load_mcp_tools`` are merged
|
``app.agents.new_chat.tools.registry.build_tools_async``: all tools returned by ``load_mcp_tools`` are merged
|
||||||
after partitioning (no extra inventory filter on MCP). Connector routing uses ``available_connectors``:
|
after partitioning (no extra inventory filter on MCP). Connector routing uses ``available_connectors``:
|
||||||
pass explicitly, or provide ``connector_service`` so lists are resolved like
|
pass explicitly, or provide ``connector_service`` so lists are resolved like
|
||||||
``create_surfsense_deep_agent`` (``get_available_connectors`` → searchable types).
|
``create_surfsense_deep_agent`` (``get_available_connectors`` → searchable types).
|
||||||
|
|
@ -115,57 +190,38 @@ async def create_multi_agent_chat(
|
||||||
Deliverables (thread-scoped reports, podcasts, etc.) are registered only when ``thread_id`` is set.
|
Deliverables (thread-scoped reports, podcasts, etc.) are registered only when ``thread_id`` is set.
|
||||||
|
|
||||||
When ``surfsense_stack`` is true (default), the supervisor uses the same SurfSense middleware shell as
|
When ``surfsense_stack`` is true (default), the supervisor uses the same SurfSense middleware shell as
|
||||||
``new_chat`` (KB priority/tree, filesystem, compaction, permissions, etc.) except ``SubAgentMiddleware`` /
|
the main single-agent chat (KB priority/tree, filesystem, compaction, permissions, etc.) except
|
||||||
``task``, since experts are separate graphs behind routing tools. Graph compilation runs in
|
``SubAgentMiddleware`` / ``task``, since experts are separate graphs behind routing tools. Graph
|
||||||
``asyncio.to_thread`` so heavy CPU work does not block the event loop.
|
compilation runs in ``asyncio.to_thread`` so heavy CPU work does not block the event loop.
|
||||||
|
|
||||||
|
``citations_enabled``: when ``None``, defaults to ``True`` (same default as ``AgentConfig`` / main chat).
|
||||||
"""
|
"""
|
||||||
resolved_connectors = available_connectors
|
citations = True if citations_enabled is None else citations_enabled
|
||||||
resolved_doc_types = available_document_types
|
connectors, doc_types = await _discover_connectors_and_doc_types(
|
||||||
if connector_service is not None:
|
connector_service=connector_service,
|
||||||
try:
|
search_space_id=search_space_id,
|
||||||
if resolved_connectors is None:
|
available_connectors=available_connectors,
|
||||||
connector_types = await connector_service.get_available_connectors(
|
available_document_types=available_document_types,
|
||||||
search_space_id
|
|
||||||
)
|
|
||||||
if connector_types:
|
|
||||||
resolved_connectors = _map_connectors_to_searchable_types(
|
|
||||||
connector_types
|
|
||||||
)
|
|
||||||
if resolved_doc_types is None:
|
|
||||||
resolved_doc_types = (
|
|
||||||
await connector_service.get_available_document_types(search_space_id)
|
|
||||||
)
|
|
||||||
except Exception as exc:
|
|
||||||
logger.warning(
|
|
||||||
"Failed to discover available connectors/document types: %s",
|
|
||||||
exc,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
mcp_tools_by_route: dict[str, list[BaseTool]] | None = None
|
mcp_by_route: dict[str, list[BaseTool]] | None = None
|
||||||
if include_mcp_tools:
|
if include_mcp_tools:
|
||||||
mcp_flat = await load_mcp_tools(db_session, search_space_id)
|
mcp_by_route = await _mcp_tools_by_expert_route(
|
||||||
id_map, name_map = await fetch_mcp_connector_metadata_maps(db_session, search_space_id)
|
db_session=db_session, search_space_id=search_space_id
|
||||||
mcp_tools_by_route = partition_mcp_tools_by_expert_route(mcp_flat, id_map, name_map)
|
)
|
||||||
|
|
||||||
registry_dependencies = build_registry_dependencies(
|
routing_tools = _make_supervisor_routing_tools(
|
||||||
|
llm,
|
||||||
db_session=db_session,
|
db_session=db_session,
|
||||||
search_space_id=search_space_id,
|
search_space_id=search_space_id,
|
||||||
user_id=user_id,
|
user_id=user_id,
|
||||||
thread_id=thread_id,
|
thread_id=thread_id,
|
||||||
llm=llm,
|
|
||||||
firecrawl_api_key=firecrawl_api_key,
|
firecrawl_api_key=firecrawl_api_key,
|
||||||
connector_service=connector_service,
|
connector_service=connector_service,
|
||||||
available_connectors=resolved_connectors,
|
available_connectors=connectors,
|
||||||
available_document_types=resolved_doc_types,
|
available_document_types=doc_types,
|
||||||
thread_visibility=thread_visibility,
|
|
||||||
)
|
|
||||||
routing_tools = build_supervisor_routing_tools(
|
|
||||||
llm,
|
|
||||||
registry_dependencies=registry_dependencies,
|
|
||||||
include_deliverables=coerce_thread_id_for_registry(thread_id) is not None,
|
|
||||||
mcp_tools_by_route=mcp_tools_by_route,
|
|
||||||
available_connectors=resolved_connectors,
|
|
||||||
thread_visibility=thread_visibility,
|
thread_visibility=thread_visibility,
|
||||||
|
mcp_tools_by_route=mcp_by_route,
|
||||||
)
|
)
|
||||||
|
|
||||||
fs_sel = filesystem_selection or FilesystemSelection()
|
fs_sel = filesystem_selection or FilesystemSelection()
|
||||||
|
|
@ -177,10 +233,11 @@ async def create_multi_agent_chat(
|
||||||
tools=routing_tools,
|
tools=routing_tools,
|
||||||
checkpointer=checkpointer,
|
checkpointer=checkpointer,
|
||||||
thread_visibility=thread_visibility,
|
thread_visibility=thread_visibility,
|
||||||
|
citations_enabled=citations,
|
||||||
)
|
)
|
||||||
|
|
||||||
return await asyncio.to_thread(
|
return await asyncio.to_thread(
|
||||||
_compile_supervisor_chat_blocking,
|
_compile_supervisor_agent_sync,
|
||||||
llm=llm,
|
llm=llm,
|
||||||
routing_tools=routing_tools,
|
routing_tools=routing_tools,
|
||||||
checkpointer=checkpointer,
|
checkpointer=checkpointer,
|
||||||
|
|
@ -191,8 +248,9 @@ async def create_multi_agent_chat(
|
||||||
thread_id=thread_id,
|
thread_id=thread_id,
|
||||||
thread_visibility=thread_visibility,
|
thread_visibility=thread_visibility,
|
||||||
anon_session_id=anon_session_id,
|
anon_session_id=anon_session_id,
|
||||||
available_connectors=resolved_connectors,
|
available_connectors=connectors,
|
||||||
available_document_types=resolved_doc_types,
|
available_document_types=doc_types,
|
||||||
mentioned_document_ids=mentioned_document_ids,
|
mentioned_document_ids=mentioned_document_ids,
|
||||||
max_input_tokens=max_input_tokens,
|
max_input_tokens=max_input_tokens,
|
||||||
|
citations_enabled=citations,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
"""SurfSense supervisor middleware (parity with ``new_chat`` main agent, minus subagents)."""
|
"""SurfSense supervisor middleware (parity with the main single-agent chat, minus subagents)."""
|
||||||
|
|
||||||
from app.agents.multi_agent_chat.middleware.supervisor_stack import (
|
from app.agents.multi_agent_chat.middleware.supervisor_stack import (
|
||||||
build_supervisor_middleware_stack,
|
build_supervisor_middleware_stack,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
"""Supervisor middleware stack matching ``new_chat`` main agent (no ``SubAgentMiddleware`` / ``task``)."""
|
"""Supervisor middleware stack matching the main single-agent chat (no ``SubAgentMiddleware`` / ``task``)."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
"""Gate supervisor routing tools by connected searchable connector types (aligned with ``new_chat`` KB).
|
"""Gate supervisor routing tools by connected searchable connector types (aligned with main chat KB).
|
||||||
|
|
||||||
When ``available_connectors`` is ``None``, all routes are emitted (caller did not pass an inventory).
|
When ``available_connectors`` is ``None``, all routes are emitted (caller did not pass an inventory).
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,7 @@ def build_supervisor_routing_tools(
|
||||||
|
|
||||||
``mcp_tools_by_route`` maps route keys to MCP tools merged into the matching expert subgraph.
|
``mcp_tools_by_route`` maps route keys to MCP tools merged into the matching expert subgraph.
|
||||||
|
|
||||||
When ``available_connectors`` is set (searchable connector strings, same shape as ``new_chat``),
|
When ``available_connectors`` is set (searchable connector strings, same shape as the main chat agent),
|
||||||
a connector-backed route is registered only if its required searchable connector type is available.
|
a connector-backed route is registered only if its required searchable connector type is available.
|
||||||
"""
|
"""
|
||||||
if registry_dependencies is None:
|
if registry_dependencies is None:
|
||||||
|
|
|
||||||
|
|
@ -1,80 +1,18 @@
|
||||||
"""Compile the supervisor agent graph (supervisor prompt + caller-supplied routing tools)."""
|
"""Compile the supervisor agent graph (LangChain ``create_agent`` + caller routing tools)."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Sequence
|
from collections.abc import Sequence
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
import app.agents.multi_agent_chat.supervisor as supervisor_pkg
|
|
||||||
|
|
||||||
from langchain.agents import create_agent
|
from langchain.agents import create_agent
|
||||||
from langchain_core.language_models import BaseChatModel
|
from langchain_core.language_models import BaseChatModel
|
||||||
from langchain_core.tools import BaseTool
|
from langchain_core.tools import BaseTool
|
||||||
from langgraph.types import Checkpointer
|
from langgraph.types import Checkpointer
|
||||||
|
|
||||||
from app.agents.multi_agent_chat.core.prompts import read_prompt_md
|
from app.agents.multi_agent_chat.supervisor.prompt_assembly import (
|
||||||
|
build_supervisor_system_prompt,
|
||||||
_BUILTIN_SPECIALISTS: frozenset[str] = frozenset({"research", "memory", "deliverables"})
|
)
|
||||||
_SPECIALIST_CAPABILITIES: dict[str, str] = {
|
|
||||||
"research": "external research: web lookup, source gathering, and SurfSense documentation help.",
|
|
||||||
"memory": "save durable long-lived memory items.",
|
|
||||||
"deliverables": "deliverables and shareable artifacts: reports, podcasts, video presentations, resumes, and images.",
|
|
||||||
"gmail": "email inbox actions: search/read emails, draft updates, send messages, and trash emails.",
|
|
||||||
"calendar": "scheduling actions: check availability, inspect events, create events, and update events.",
|
|
||||||
"google_drive": "Drive file/document actions: locate files, inspect content, and manage files/folders.",
|
|
||||||
"notion": "Notion page actions: create pages, update content, and delete pages.",
|
|
||||||
"confluence": "Confluence page actions: find/read pages and create/update pages.",
|
|
||||||
"dropbox": "Dropbox file storage actions: browse folders, read files, and manage file content.",
|
|
||||||
"onedrive": "OneDrive file storage actions: browse folders, read files, and manage file content.",
|
|
||||||
"discord": "Discord communication actions: read channels/threads and post replies.",
|
|
||||||
"teams": "Microsoft Teams communication actions: read channels/threads and post replies.",
|
|
||||||
"luma": "Luma event actions: list events, inspect event details, and create events.",
|
|
||||||
"linear": "Linear workflow actions: search/update issues and inspect projects/cycles.",
|
|
||||||
"jira": "Jira workflow actions: search/update issues and manage workflow transitions.",
|
|
||||||
"clickup": "ClickUp workflow actions: find/update tasks and lists.",
|
|
||||||
"airtable": "Airtable data actions: locate bases/tables and create/read/update records.",
|
|
||||||
"slack": "Slack communication actions: read channel/thread history and post replies.",
|
|
||||||
# generic_mcp specialist intentionally disabled for now.
|
|
||||||
# "generic_mcp": "handle tasks through user-defined custom app integration tools not covered above.",
|
|
||||||
}
|
|
||||||
_SPECIALIST_ORDER: tuple[str, ...] = tuple(_SPECIALIST_CAPABILITIES.keys())
|
|
||||||
|
|
||||||
|
|
||||||
def _memory_capability_for_visibility(thread_visibility: Any | None) -> str:
|
|
||||||
vis = str(getattr(thread_visibility, "value", thread_visibility)).upper()
|
|
||||||
if vis == "SEARCH_SPACE":
|
|
||||||
return "team memory actions: save shared team preferences, conventions, and long-lived team facts."
|
|
||||||
return "user memory actions: save personal preferences, instructions, and long-lived user facts."
|
|
||||||
|
|
||||||
|
|
||||||
def _render_available_specialists_list(
|
|
||||||
tools: Sequence[BaseTool],
|
|
||||||
*,
|
|
||||||
thread_visibility: Any | None,
|
|
||||||
) -> str:
|
|
||||||
available_names = {
|
|
||||||
tool.name for tool in tools if isinstance(getattr(tool, "name", None), str)
|
|
||||||
}
|
|
||||||
capabilities = dict(_SPECIALIST_CAPABILITIES)
|
|
||||||
capabilities["memory"] = _memory_capability_for_visibility(thread_visibility)
|
|
||||||
lines: list[str] = []
|
|
||||||
for name in _SPECIALIST_ORDER:
|
|
||||||
if name in _BUILTIN_SPECIALISTS or name in available_names:
|
|
||||||
capability = capabilities[name]
|
|
||||||
lines.append(f"- {name}: {capability}")
|
|
||||||
return "\n".join(lines)
|
|
||||||
|
|
||||||
|
|
||||||
def _render_supervisor_prompt(
|
|
||||||
template: str,
|
|
||||||
tools: Sequence[BaseTool],
|
|
||||||
*,
|
|
||||||
thread_visibility: Any | None,
|
|
||||||
) -> str:
|
|
||||||
specialist_list = _render_available_specialists_list(
|
|
||||||
tools, thread_visibility=thread_visibility
|
|
||||||
)
|
|
||||||
return template.replace("{{AVAILABLE_SPECIALISTS_LIST}}", specialist_list)
|
|
||||||
|
|
||||||
|
|
||||||
def build_supervisor_agent(
|
def build_supervisor_agent(
|
||||||
|
|
@ -85,13 +23,13 @@ def build_supervisor_agent(
|
||||||
thread_visibility: Any | None = None,
|
thread_visibility: Any | None = None,
|
||||||
middleware: Sequence[Any] | None = None,
|
middleware: Sequence[Any] | None = None,
|
||||||
context_schema: Any | None = None,
|
context_schema: Any | None = None,
|
||||||
|
citations_enabled: bool = True,
|
||||||
):
|
):
|
||||||
"""Compile the supervisor **agent** (graph). ``tools`` = output of ``build_supervisor_routing_tools``."""
|
"""Compile the supervisor **agent** (graph). ``tools`` = output of ``build_supervisor_routing_tools``."""
|
||||||
template = read_prompt_md(supervisor_pkg.__name__, "supervisor_prompt")
|
system_prompt = build_supervisor_system_prompt(
|
||||||
system_prompt = _render_supervisor_prompt(
|
|
||||||
template,
|
|
||||||
tools,
|
tools,
|
||||||
thread_visibility=thread_visibility,
|
thread_visibility=thread_visibility,
|
||||||
|
citations_enabled=citations_enabled,
|
||||||
)
|
)
|
||||||
kwargs: dict[str, Any] = {
|
kwargs: dict[str, Any] = {
|
||||||
"system_prompt": system_prompt,
|
"system_prompt": system_prompt,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,128 @@
|
||||||
|
"""Supervisor system prompt: template load, shared agent-identity injection, specialist list."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Sequence
|
||||||
|
from datetime import UTC, datetime
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from langchain_core.tools import BaseTool
|
||||||
|
|
||||||
|
import app.agents.multi_agent_chat.supervisor as supervisor_pkg
|
||||||
|
from app.agents.multi_agent_chat.core.prompts import read_prompt_md
|
||||||
|
from app.agents.new_chat.prompts.composer import _build_citation_block, _read_fragment
|
||||||
|
from app.db import ChatVisibility
|
||||||
|
|
||||||
|
_MEMORY_SPECIALIST_PHRASE = "invoke the **memory** specialist"
|
||||||
|
|
||||||
|
_BUILTIN_SPECIALISTS: frozenset[str] = frozenset({"research", "memory", "deliverables"})
|
||||||
|
_SPECIALIST_CAPABILITIES: dict[str, str] = {
|
||||||
|
"research": "external research: web lookup, source gathering, and SurfSense documentation help.",
|
||||||
|
"memory": "save durable long-lived memory items.",
|
||||||
|
"deliverables": "deliverables and shareable artifacts: reports, podcasts, video presentations, resumes, and images.",
|
||||||
|
"gmail": "email inbox actions: search/read emails, draft updates, send messages, and trash emails.",
|
||||||
|
"calendar": "scheduling actions: check availability, inspect events, create events, and update events.",
|
||||||
|
"google_drive": "Drive file/document actions: locate files, inspect content, and manage files/folders.",
|
||||||
|
"notion": "Notion page actions: create pages, update content, and delete pages.",
|
||||||
|
"confluence": "Confluence page actions: find/read pages and create/update pages.",
|
||||||
|
"dropbox": "Dropbox file storage actions: browse folders, read files, and manage file content.",
|
||||||
|
"onedrive": "OneDrive file storage actions: browse folders, read files, and manage file content.",
|
||||||
|
"discord": "Discord communication actions: read channels/threads and post replies.",
|
||||||
|
"teams": "Microsoft Teams communication actions: read channels/threads and post replies.",
|
||||||
|
"luma": "Luma event actions: list events, inspect event details, and create events.",
|
||||||
|
"linear": "Linear workflow actions: search/update issues and inspect projects/cycles.",
|
||||||
|
"jira": "Jira workflow actions: search/update issues and manage workflow transitions.",
|
||||||
|
"clickup": "ClickUp workflow actions: find/update tasks and lists.",
|
||||||
|
"airtable": "Airtable data actions: locate bases/tables and create/read/update records.",
|
||||||
|
"slack": "Slack communication actions: read channel/thread history and post replies.",
|
||||||
|
# generic_mcp specialist intentionally disabled for now.
|
||||||
|
# "generic_mcp": "handle tasks through user-defined custom app integration tools not covered above.",
|
||||||
|
}
|
||||||
|
_SPECIALIST_ORDER: tuple[str, ...] = tuple(_SPECIALIST_CAPABILITIES.keys())
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_chat_visibility(thread_visibility: Any | None) -> ChatVisibility:
|
||||||
|
if thread_visibility is None:
|
||||||
|
return ChatVisibility.PRIVATE
|
||||||
|
if thread_visibility == ChatVisibility.SEARCH_SPACE:
|
||||||
|
return ChatVisibility.SEARCH_SPACE
|
||||||
|
raw = getattr(thread_visibility, "value", thread_visibility)
|
||||||
|
if str(raw).upper() == "SEARCH_SPACE":
|
||||||
|
return ChatVisibility.SEARCH_SPACE
|
||||||
|
return ChatVisibility.PRIVATE
|
||||||
|
|
||||||
|
|
||||||
|
def _identity_fragment_key(thread_visibility: Any | None) -> str:
|
||||||
|
"""``private`` / ``team`` suffix for ``agent_*`` and ``memory_protocol_*`` fragments."""
|
||||||
|
return (
|
||||||
|
"team"
|
||||||
|
if _normalize_chat_visibility(thread_visibility) == ChatVisibility.SEARCH_SPACE
|
||||||
|
else "private"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _compose_identity_memory_citations(
|
||||||
|
*,
|
||||||
|
thread_visibility: Any | None,
|
||||||
|
citations_enabled: bool,
|
||||||
|
) -> str:
|
||||||
|
"""Main-chat identity, memory protocol, and citation fragments (supervisor slice only)."""
|
||||||
|
key = _identity_fragment_key(thread_visibility)
|
||||||
|
today = datetime.now(UTC).date().isoformat()
|
||||||
|
|
||||||
|
intro = _read_fragment(f"base/agent_{key}.md")
|
||||||
|
if intro:
|
||||||
|
intro = intro.format(resolved_today=today)
|
||||||
|
|
||||||
|
memory = _read_fragment(f"base/memory_protocol_{key}.md").replace(
|
||||||
|
"call update_memory",
|
||||||
|
_MEMORY_SPECIALIST_PHRASE,
|
||||||
|
)
|
||||||
|
tail = (
|
||||||
|
f"<system_instruction>\n{memory}\n\n</system_instruction>\n"
|
||||||
|
+ _build_citation_block(citations_enabled)
|
||||||
|
)
|
||||||
|
return "\n\n".join(part for part in (intro.strip(), tail.strip()) if part)
|
||||||
|
|
||||||
|
|
||||||
|
def _memory_specialist_capability(thread_visibility: Any | None) -> str:
|
||||||
|
vis = str(getattr(thread_visibility, "value", thread_visibility)).upper()
|
||||||
|
if vis == "SEARCH_SPACE":
|
||||||
|
return "team memory actions: save shared team preferences, conventions, and long-lived team facts."
|
||||||
|
return "user memory actions: save personal preferences, instructions, and long-lived user facts."
|
||||||
|
|
||||||
|
|
||||||
|
def _specialists_markdown(
|
||||||
|
tools: Sequence[BaseTool],
|
||||||
|
*,
|
||||||
|
thread_visibility: Any | None,
|
||||||
|
) -> str:
|
||||||
|
available_names = {
|
||||||
|
tool.name for tool in tools if isinstance(getattr(tool, "name", None), str)
|
||||||
|
}
|
||||||
|
capabilities = dict(_SPECIALIST_CAPABILITIES)
|
||||||
|
capabilities["memory"] = _memory_specialist_capability(thread_visibility)
|
||||||
|
lines: list[str] = []
|
||||||
|
for name in _SPECIALIST_ORDER:
|
||||||
|
if name in _BUILTIN_SPECIALISTS or name in available_names:
|
||||||
|
lines.append(f"- {name}: {capabilities[name]}")
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
def build_supervisor_system_prompt(
|
||||||
|
tools: Sequence[BaseTool],
|
||||||
|
*,
|
||||||
|
thread_visibility: Any | None,
|
||||||
|
citations_enabled: bool,
|
||||||
|
) -> str:
|
||||||
|
"""Load ``supervisor_prompt.md`` and fill placeholders."""
|
||||||
|
template = read_prompt_md(supervisor_pkg.__name__, "supervisor_prompt")
|
||||||
|
specialists = _specialists_markdown(tools, thread_visibility=thread_visibility)
|
||||||
|
injected = _compose_identity_memory_citations(
|
||||||
|
thread_visibility=thread_visibility,
|
||||||
|
citations_enabled=citations_enabled,
|
||||||
|
)
|
||||||
|
return template.replace("{{AVAILABLE_SPECIALISTS_LIST}}", specialists).replace(
|
||||||
|
"{{SUPERVISOR_BASE_INJECTION}}",
|
||||||
|
injected,
|
||||||
|
)
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
You are SurfSense's multi-agent supervisor.
|
{{SUPERVISOR_BASE_INJECTION}}
|
||||||
|
|
||||||
<role>
|
<supervisor_role>
|
||||||
Your job is to decide whether to answer directly or delegate to one or more specialists.
|
In this **multi-agent** session you also **coordinate specialists** (listed below): call a specialist only when their domain matches the need; give each call a compact, outcome-focused task; merge structured results into one clear user-facing reply. When you can satisfy the turn with your own tools and reasoning, do so without delegating.
|
||||||
You optimize for correctness, low confusion, and minimal unnecessary delegation.
|
</supervisor_role>
|
||||||
</role>
|
|
||||||
|
|
||||||
<available_specialists>
|
<available_specialists>
|
||||||
Use only the specialists listed below.
|
Use only the specialists listed below.
|
||||||
|
|
|
||||||
|
|
@ -1581,6 +1581,7 @@ async def stream_new_chat(
|
||||||
thread_visibility=visibility,
|
thread_visibility=visibility,
|
||||||
filesystem_selection=filesystem_selection,
|
filesystem_selection=filesystem_selection,
|
||||||
mentioned_document_ids=mentioned_document_ids,
|
mentioned_document_ids=mentioned_document_ids,
|
||||||
|
citations_enabled=agent_config.citations_enabled,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
agent = await create_surfsense_deep_agent(
|
agent = await create_surfsense_deep_agent(
|
||||||
|
|
@ -2305,6 +2306,7 @@ async def stream_resume_chat(
|
||||||
connector_service=connector_service,
|
connector_service=connector_service,
|
||||||
thread_visibility=visibility,
|
thread_visibility=visibility,
|
||||||
filesystem_selection=filesystem_selection,
|
filesystem_selection=filesystem_selection,
|
||||||
|
citations_enabled=agent_config.citations_enabled,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
agent = await create_surfsense_deep_agent(
|
agent = await create_surfsense_deep_agent(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue