From 53adac0cc9c63282e8e179de6f62478c4662a3dc Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Wed, 29 Apr 2026 23:41:29 +0200 Subject: [PATCH] Compose supervisor prompt from SurfSense fragments and composer blocks. --- .../supervisor_system_prompt.py | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 surfsense_backend/app/agents/new_chat_supervisor_baseline/supervisor_system_prompt.py diff --git a/surfsense_backend/app/agents/new_chat_supervisor_baseline/supervisor_system_prompt.py b/surfsense_backend/app/agents/new_chat_supervisor_baseline/supervisor_system_prompt.py new file mode 100644 index 000000000..82c0077e3 --- /dev/null +++ b/surfsense_backend/app/agents/new_chat_supervisor_baseline/supervisor_system_prompt.py @@ -0,0 +1,121 @@ +"""Supervisor-scoped system prompt for ``new_chat_supervisor_baseline``. + +Composition follows the same fragment discipline as +:func:`app.agents.new_chat.prompts.composer.compose_system_prompt`, but **omits** +sections that assume registry tools: ``base/tool_routing_*.md``, ``tools/_preamble.md``, +the tools/examples blocks, ``base/parameter_resolution.md`` (discovery lists concrete +tools), and ``base/memory_protocol_*.md`` (requires ``update_memory`` calls). + +**Authoritative supervisor semantics:** LangChain Reference documents +``langgraph_supervisor.create_supervisor`` — the supervisor graph accepts an optional +``prompt`` (typically a ``SystemMessage``) that scopes the supervisor LLM alongside +managed worker graphs. + +**SurfSense sources reused verbatim where applicable:** ``prompts/base/agent_private.md`` / +``agent_team.md`` from :mod:`app.agents.new_chat.prompts`. KB policy is adapted from +``base/kb_only_policy_*.md`` into supervisor-local fragments that reference injected +context instead of tool outputs. Provider and citation blocks reuse +``composer._build_provider_block`` / ``_build_citation_block`` and +``composer.detect_provider_variant`` unchanged. +""" + +from __future__ import annotations + +from datetime import UTC, datetime +from importlib import resources + +from langchain_core.language_models import BaseChatModel + +from app.agents.new_chat.llm_config import AgentConfig +from app.agents.new_chat.prompts import composer as pc +from app.db import ChatVisibility + +_SUP_PROMPTS_PKG = "app.agents.new_chat_supervisor_baseline.prompts" + + +def _read_supervisor_fragment(filename: str) -> str: + try: + ref = resources.files(_SUP_PROMPTS_PKG).joinpath(filename) + if not ref.is_file(): + return "" + text = ref.read_text(encoding="utf-8") + except (FileNotFoundError, ModuleNotFoundError, OSError): + return "" + if text.endswith("\n"): + text = text[:-1] + return text + + +def _build_supervisor_system_instruction_block( + *, + visibility: ChatVisibility, + resolved_today: str, +) -> str: + """```` body: LangGraph supervisor scope + SurfSense identity + adapted KB + memory limits.""" + variant = "team" if visibility == ChatVisibility.SEARCH_SPACE else "private" + sections = [ + _read_supervisor_fragment("supervisor_graph_role.md"), + pc._read_fragment(f"base/agent_{variant}.md"), + _read_supervisor_fragment(f"kb_policy_supervisor_{variant}.md"), + _read_supervisor_fragment("memory_context_supervisor.md"), + ] + body = "\n\n".join(s for s in sections if s) + block = f"\n\n{body}\n\n\n" + return block.format(resolved_today=resolved_today) + + +def resolve_llm_model_name(llm: BaseChatModel) -> str | None: + """Best-effort model id string for :func:`composer.detect_provider_variant`.""" + name = getattr(llm, "model_name", None) + if isinstance(name, str) and name.strip(): + return name.strip() + model = getattr(llm, "model", None) + if isinstance(model, str) and model.strip(): + return model.strip() + profile = getattr(llm, "profile", None) + if isinstance(profile, dict): + for key in ("model", "model_name"): + m = profile.get(key) + if isinstance(m, str) and m.strip(): + return m.strip() + return None + + +def build_supervisor_system_prompt( + *, + agent_config: AgentConfig | None, + thread_visibility: ChatVisibility | None, + llm: BaseChatModel, +) -> str: + """Assemble the supervisor system prompt (no tool-list or tool-routing fragments).""" + resolved_today = datetime.now(UTC).astimezone(UTC).date().isoformat() + visibility = thread_visibility or ChatVisibility.PRIVATE + model_name = resolve_llm_model_name(llm) + + if agent_config is not None: + custom = (agent_config.system_instructions or "").strip() + if custom: + sys_block = agent_config.system_instructions.format(resolved_today=resolved_today) + elif agent_config.use_default_system_instructions: + sys_block = _build_supervisor_system_instruction_block( + visibility=visibility, + resolved_today=resolved_today, + ) + else: + sys_block = "" + else: + sys_block = _build_supervisor_system_instruction_block( + visibility=visibility, + resolved_today=resolved_today, + ) + + provider_variant = pc.detect_provider_variant(model_name) + sys_block += pc._build_provider_block(provider_variant) + + if agent_config is None: + citations_enabled = True + else: + citations_enabled = agent_config.citations_enabled + + sys_block += pc._build_citation_block(citations_enabled) + return sys_block