mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-09 11:17:44 +02:00
[eric] agents: prompt_context public fns lose leading-_ (convention); drop dead delegators
This commit is contained in:
@@ -82,11 +82,7 @@ from backend.apps.agents.manager.session.history_compaction import (
|
||||
_estimate_post_compact_input,
|
||||
_get_branch_messages,
|
||||
)
|
||||
from backend.apps.agents.manager.prompt.prompt_context import (
|
||||
_resolve_attached_skills,
|
||||
_resolve_forced_tools,
|
||||
_resolve_mode,
|
||||
)
|
||||
from backend.apps.agents.manager.prompt.prompt_context import resolve_mode
|
||||
from backend.apps.agents.manager.prompt.attachments import (
|
||||
_build_dir_tree,
|
||||
_build_prompt_content,
|
||||
@@ -108,9 +104,6 @@ class AgentManager:
|
||||
# multi-second SDK teardown the cancel handler sits behind.
|
||||
self._live_partial: dict[str, dict] = {}
|
||||
|
||||
def _resolve_mode(self, mode_id: str) -> tuple[list[str], str | None, str | None]:
|
||||
return _resolve_mode(mode_id, get_all_tool_names)
|
||||
|
||||
async def _build_mcp_servers(
|
||||
self,
|
||||
allowed_tools: list[str],
|
||||
@@ -188,7 +181,7 @@ class AgentManager:
|
||||
async def launch_agent(self, config: AgentConfig) -> AgentSession:
|
||||
session_id = uuid4().hex
|
||||
|
||||
mode_tools, _, mode_folder = self._resolve_mode(config.mode)
|
||||
mode_tools, _, mode_folder = resolve_mode(config.mode, get_all_tool_names)
|
||||
tools = mode_tools
|
||||
|
||||
global_settings = load_settings()
|
||||
@@ -291,12 +284,6 @@ class AgentManager:
|
||||
def _build_dir_tree(self, root: str, max_depth: int = 4, prefix: str = "") -> list[str]:
|
||||
return _build_dir_tree(root, max_depth, prefix)
|
||||
|
||||
def _resolve_forced_tools(self, forced_tools: list[str] | None) -> str:
|
||||
return _resolve_forced_tools(forced_tools)
|
||||
|
||||
def _resolve_attached_skills(self, attached_skills: list | None) -> str:
|
||||
return _resolve_attached_skills(attached_skills)
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Compaction & token guard (Phase 2)
|
||||
#
|
||||
@@ -410,7 +397,7 @@ class AgentManager:
|
||||
return await tool_result_hook.post_tool_hook(hook_ctx, input_data, tool_use_id, context)
|
||||
|
||||
try:
|
||||
_, mode_sys_prompt, _ = self._resolve_mode(session.mode)
|
||||
_, mode_sys_prompt, _ = resolve_mode(session.mode, get_all_tool_names)
|
||||
|
||||
# Reconcile active_mcps against currently-enabled tools (Phase 3).
|
||||
# If the user toggled a server off in the Tools page mid-session,
|
||||
@@ -2527,7 +2514,7 @@ class AgentManager:
|
||||
session_changed = True
|
||||
if mode and mode != session.mode:
|
||||
session.mode = mode
|
||||
mode_tools, _, _ = self._resolve_mode(mode)
|
||||
mode_tools, _, _ = resolve_mode(mode, get_all_tool_names)
|
||||
session.allowed_tools = mode_tools
|
||||
session_changed = True
|
||||
if session_changed:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import os
|
||||
|
||||
from backend.apps.agents.manager.prompt.prompt_context import _resolve_attached_skills, _resolve_forced_tools
|
||||
from backend.apps.agents.manager.prompt.prompt_context import resolve_attached_skills, resolve_forced_tools
|
||||
|
||||
|
||||
def _build_dir_tree(root: str, max_depth: int = 4, prefix: str = "") -> list[str]:
|
||||
@@ -45,8 +45,8 @@ def _build_prompt_content(prompt: str, images: list | None = None, context_paths
|
||||
context_text, native_blocks, refusals = _resolve_attachments(
|
||||
context_paths, api_type=api_type, model=model,
|
||||
)
|
||||
forced_tools_text = _resolve_forced_tools(forced_tools)
|
||||
skills_text = _resolve_attached_skills(attached_skills)
|
||||
forced_tools_text = resolve_forced_tools(forced_tools)
|
||||
skills_text = resolve_attached_skills(attached_skills)
|
||||
|
||||
refusal_text = "\n\n".join(refusals)
|
||||
parts = [p for p in (forced_tools_text, context_text, refusal_text, skills_text, prompt) if p]
|
||||
|
||||
@@ -8,7 +8,7 @@ from backend.apps.tools_lib.tools_lib import (
|
||||
from backend.apps.agents.manager.prompt.tool_catalog import _get_denied_tool_names, _is_fully_denied
|
||||
|
||||
|
||||
def _resolve_mode(mode_id: str, get_all_tool_names: Callable[[], list[str]]) -> tuple[list[str], str | None, str | None]:
|
||||
def resolve_mode(mode_id: str, get_all_tool_names: Callable[[], list[str]]) -> tuple[list[str], str | None, str | None]:
|
||||
"""Return (tools, system_prompt, default_folder) resolved from the mode store."""
|
||||
mode_def = load_mode(mode_id)
|
||||
if mode_def:
|
||||
@@ -17,7 +17,7 @@ def _resolve_mode(mode_id: str, get_all_tool_names: Callable[[], list[str]]) ->
|
||||
return get_all_tool_names(), None, None
|
||||
|
||||
|
||||
def _build_connected_tools_context(allowed_tools: list[str], get_all_tool_names: Callable[[], list[str]]) -> str | None:
|
||||
def build_connected_tools_context(allowed_tools: list[str], get_all_tool_names: Callable[[], list[str]]) -> str | None:
|
||||
"""Build a context block describing connected MCP tools and their accounts.
|
||||
|
||||
Tools set to 'deny' and fully-denied servers are excluded.
|
||||
@@ -127,7 +127,7 @@ def toolsearch_loop_redirect(consecutive_toolsearch: int, gated_servers: list[st
|
||||
return reason
|
||||
|
||||
|
||||
def _build_browser_context(dashboard_id: str | None, selected_browser_ids: list[str] | None = None) -> str | None:
|
||||
def build_browser_context(dashboard_id: str | None, selected_browser_ids: list[str] | None = None) -> str | None:
|
||||
"""Build a context block listing browser cards and delegation instructions.
|
||||
|
||||
Only browser cards explicitly selected by the user are included.
|
||||
@@ -203,7 +203,7 @@ def _build_browser_context(dashboard_id: str | None, selected_browser_ids: list[
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def _build_selected_app_context(selected_app_output_ids: list[str] | None) -> str | None:
|
||||
def build_selected_app_context(selected_app_output_ids: list[str] | None) -> str | None:
|
||||
"""Build a context block for dashboard App cards the user selected to edit.
|
||||
|
||||
Resolves each Output id to its on-disk workspace so the agent edits the
|
||||
@@ -262,7 +262,7 @@ def _build_selected_app_context(selected_app_output_ids: list[str] | None) -> st
|
||||
)
|
||||
|
||||
|
||||
def _build_selected_settings_context(selected_setting_ids: list[str] | None) -> str | None:
|
||||
def build_selected_settings_context(selected_setting_ids: list[str] | None) -> str | None:
|
||||
"""Context block when the user points the agent at specific Settings rows.
|
||||
|
||||
A targeting aid, NOT a gate: the settings tools (SettingsRead/SettingsWrite)
|
||||
@@ -284,7 +284,7 @@ def _build_selected_settings_context(selected_setting_ids: list[str] | None) ->
|
||||
)
|
||||
|
||||
|
||||
def _build_mcp_registry_summary(allowed_tools: list[str], active_mcps: list[str], get_all_tool_names: Callable[[], list[str]]) -> str | None:
|
||||
def build_mcp_registry_summary(allowed_tools: list[str], active_mcps: list[str], get_all_tool_names: Callable[[], list[str]]) -> str | None:
|
||||
"""Compact registry of installed MCP servers, one line per server.
|
||||
|
||||
This is the visible surface that drives the activation gate: the model
|
||||
@@ -411,14 +411,14 @@ AGENT_IDENTITY = (
|
||||
)
|
||||
|
||||
|
||||
def _compose_system_prompt(default_prompt: str | None, mode_prompt: str | None, session_prompt: str | None, connected_tools_ctx: str | None = None, browser_ctx: str | None = None, mcp_registry_ctx: str | None = None) -> str | None:
|
||||
def compose_system_prompt(default_prompt: str | None, mode_prompt: str | None, session_prompt: str | None, connected_tools_ctx: str | None = None, browser_ctx: str | None = None, mcp_registry_ctx: str | None = None) -> str | None:
|
||||
# Identity always leads so it overrides the preset's Claude Code persona, even
|
||||
# when the user has no custom default/mode/session prompt of their own.
|
||||
parts = [AGENT_IDENTITY] + [p for p in (default_prompt, mode_prompt, session_prompt, connected_tools_ctx, mcp_registry_ctx, browser_ctx) if p]
|
||||
return "\n\n".join(parts)
|
||||
|
||||
|
||||
def _resolve_forced_tools(forced_tools: list[str] | None) -> str:
|
||||
def resolve_forced_tools(forced_tools: list[str] | None) -> str:
|
||||
"""Build a context block describing explicitly requested tools."""
|
||||
if not forced_tools:
|
||||
return ""
|
||||
@@ -458,7 +458,7 @@ def _resolve_forced_tools(forced_tools: list[str] | None) -> str:
|
||||
)
|
||||
|
||||
|
||||
def _resolve_attached_skills(attached_skills: list | None) -> str:
|
||||
def resolve_attached_skills(attached_skills: list | None) -> str:
|
||||
"""Build a context block injecting attached skill content into the prompt.
|
||||
|
||||
For a multi-file (folder) skill we inject the SKILL.md body as text AND point
|
||||
|
||||
@@ -12,11 +12,11 @@ from typeguard import typechecked
|
||||
from backend.apps.agents.core.models import AgentSession
|
||||
from backend.apps.agents.manager.prompt.tool_catalog import get_all_tool_names
|
||||
from backend.apps.agents.manager.prompt.prompt_context import (
|
||||
_build_browser_context as build_browser_context,
|
||||
_build_mcp_registry_summary as build_mcp_registry_summary,
|
||||
_build_selected_app_context as build_selected_app_context,
|
||||
_build_selected_settings_context as build_selected_settings_context,
|
||||
_compose_system_prompt as compose_system_prompt,
|
||||
build_browser_context,
|
||||
build_mcp_registry_summary,
|
||||
build_selected_app_context,
|
||||
build_selected_settings_context,
|
||||
compose_system_prompt,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ from backend.apps.agents.manager.prompt import prompt_context as pc
|
||||
|
||||
|
||||
def _fake_dashboard(monkeypatch):
|
||||
# _build_browser_context loads the dashboard; give it a minimal one so it
|
||||
# build_browser_context loads the dashboard; give it a minimal one so it
|
||||
# gets past the load and emits the static delegation guidance.
|
||||
import backend.apps.dashboards.dashboards as dash
|
||||
|
||||
@@ -25,7 +25,7 @@ def _fake_dashboard(monkeypatch):
|
||||
|
||||
def test_orchestrator_routes_same_flow_batches_to_one_agent(monkeypatch):
|
||||
_fake_dashboard(monkeypatch)
|
||||
ctx = pc._build_browser_context("dash-1", selected_browser_ids=[])
|
||||
ctx = pc.build_browser_context("dash-1", selected_browser_ids=[])
|
||||
assert ctx is not None
|
||||
# the key guidance: one agent + the whole list, not one agent per item
|
||||
assert "Give ONE agent the whole list" in ctx
|
||||
@@ -35,4 +35,4 @@ def test_orchestrator_routes_same_flow_batches_to_one_agent(monkeypatch):
|
||||
|
||||
|
||||
def test_browser_context_is_none_without_a_dashboard():
|
||||
assert pc._build_browser_context(None) is None
|
||||
assert pc.build_browser_context(None) is None
|
||||
|
||||
@@ -42,10 +42,10 @@ def test_message_endpoint_threads_selected_setting_ids(client, monkeypatch):
|
||||
|
||||
|
||||
def test_selected_settings_context_block_targets_the_fields():
|
||||
from backend.apps.agents.manager.prompt.prompt_context import _build_selected_settings_context
|
||||
assert _build_selected_settings_context(None) is None
|
||||
assert _build_selected_settings_context([]) is None
|
||||
block = _build_selected_settings_context(["theme", "default_model"])
|
||||
from backend.apps.agents.manager.prompt.prompt_context import build_selected_settings_context
|
||||
assert build_selected_settings_context(None) is None
|
||||
assert build_selected_settings_context([]) is None
|
||||
block = build_selected_settings_context(["theme", "default_model"])
|
||||
assert "theme" in block and "default_model" in block
|
||||
# It tells the agent to use the always-on settings tools on exactly these fields.
|
||||
assert "SettingsRead" in block and "SettingsWrite" in block
|
||||
|
||||
@@ -142,13 +142,13 @@ def test_install_dedups_instead_of_clobbering_existing_skill(skills_dir):
|
||||
def test_confirm_install_writes_folder_lists_and_injects(skills_dir, monkeypatch):
|
||||
"""End-to-end install->usable: confirm=true through the real /install endpoint
|
||||
writes the folder skill, it shows up in /api/skills/list with supporting
|
||||
files, and _resolve_attached_skills injects it with the folder path so an
|
||||
files, and resolve_attached_skills injects it with the folder path so an
|
||||
agent can read its scripts. (resolve is mocked to skip the network; the live
|
||||
GitHub resolve is proven separately.)"""
|
||||
import secrets as _secrets
|
||||
from fastapi.testclient import TestClient
|
||||
from backend.main import app
|
||||
from backend.apps.agents.manager.prompt.prompt_context import _resolve_attached_skills
|
||||
from backend.apps.agents.manager.prompt.prompt_context import resolve_attached_skills
|
||||
import backend.auth as auth_mod
|
||||
if not auth_mod._TOKEN:
|
||||
auth_mod._TOKEN = _secrets.token_urlsafe(32)
|
||||
@@ -175,7 +175,7 @@ def test_confirm_install_writes_folder_lists_and_injects(skills_dir, monkeypatch
|
||||
assert (skills_dir / slug / "SKILL.md").exists()
|
||||
assert (skills_dir / slug / "scripts" / "extract.py").exists()
|
||||
# Injectable: the agent gets the body AND a pointer to the folder for on-demand reads.
|
||||
block = _resolve_attached_skills([{"id": slug, "name": "PDF Tools", "content": "# PDF Tools\nRun scripts/extract.py to pull text."}])
|
||||
block = resolve_attached_skills([{"id": slug, "name": "PDF Tools", "content": "# PDF Tools\nRun scripts/extract.py to pull text."}])
|
||||
assert "[Using skill: PDF Tools]" in block
|
||||
assert str(skills_dir / slug) in block
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import json
|
||||
import pytest
|
||||
|
||||
import backend.apps.skills.skills as skills_mod
|
||||
from backend.apps.agents.manager.prompt.prompt_context import _resolve_attached_skills
|
||||
from backend.apps.agents.manager.prompt.prompt_context import resolve_attached_skills
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -116,7 +116,7 @@ def test_injection_points_at_folder_for_supporting_files(skills_dir):
|
||||
_write(str(base / "SKILL.md"), "use the template")
|
||||
_write(str(base / "template.html"), "<html></html>")
|
||||
|
||||
block = _resolve_attached_skills([{"id": "withfiles", "name": "WithFiles", "content": "use the template"}])
|
||||
block = resolve_attached_skills([{"id": "withfiles", "name": "WithFiles", "content": "use the template"}])
|
||||
assert "[Using skill: WithFiles]" in block
|
||||
assert str(base) in block
|
||||
assert "Read" in block # tells the agent to read supporting files
|
||||
@@ -129,17 +129,17 @@ def test_skill_injection_is_provider_agnostic_by_construction(skills_dir):
|
||||
builtin set every provider gets. So a non-Claude agent receives byte-identical
|
||||
skill text and the same file-reading tools."""
|
||||
import inspect
|
||||
from backend.apps.agents.manager.prompt.prompt_context import _resolve_attached_skills
|
||||
from backend.apps.agents.manager.prompt.prompt_context import resolve_attached_skills
|
||||
from backend.apps.agents.manager.prompt.tool_catalog import FULL_TOOLS
|
||||
|
||||
# 1. No provider/api parameter -> the injected text cannot branch on the model.
|
||||
assert set(inspect.signature(_resolve_attached_skills).parameters) == {"attached_skills"}
|
||||
assert set(inspect.signature(resolve_attached_skills).parameters) == {"attached_skills"}
|
||||
|
||||
# 2. A folder skill yields the body + a pointer to its folder via Read/Glob/Bash.
|
||||
base = skills_dir / "vid"
|
||||
_write(str(base / "SKILL.md"), "render it")
|
||||
_write(str(base / "helper.py"), "x")
|
||||
block = _resolve_attached_skills([{"id": "vid", "name": "Vid", "content": "render it"}])
|
||||
block = resolve_attached_skills([{"id": "vid", "name": "Vid", "content": "render it"}])
|
||||
assert "[Using skill: Vid]" in block
|
||||
assert str(base) in block and "Read" in block
|
||||
|
||||
@@ -149,7 +149,7 @@ def test_skill_injection_is_provider_agnostic_by_construction(skills_dir):
|
||||
|
||||
def test_injection_no_folder_note_for_flat_skill(skills_dir):
|
||||
_write(str(skills_dir / "plain.md"), "plain content")
|
||||
block = _resolve_attached_skills([{"id": "plain", "name": "Plain", "content": "plain content"}])
|
||||
block = resolve_attached_skills([{"id": "plain", "name": "Plain", "content": "plain content"}])
|
||||
assert "[Using skill: Plain]" in block
|
||||
assert "supporting files" not in block.lower()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user