mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-08 22:22:17 +02:00
feat: docs and ui tweaks
This commit is contained in:
parent
64f2b4a6eb
commit
271a21aee6
103 changed files with 2161 additions and 2625 deletions
|
|
@ -25,6 +25,7 @@ from app.agents.chat.multi_agent_chat.shared.permissions.middleware.core import
|
|||
PermissionMiddleware,
|
||||
)
|
||||
from app.agents.chat.multi_agent_chat.subagents.shared.subagent_builder import (
|
||||
append_today_utc,
|
||||
pack_subagent,
|
||||
)
|
||||
|
||||
|
|
@ -104,6 +105,35 @@ async def test_subagent_recovers_when_primary_llm_fails():
|
|||
assert final.content == "recovered via fallback"
|
||||
|
||||
|
||||
def test_packed_subagent_prompt_carries_todays_utc_date():
|
||||
"""Subagents must inherit the main agent's clock, not guess the date."""
|
||||
from datetime import UTC, datetime
|
||||
|
||||
today = datetime.now(UTC).date().isoformat()
|
||||
|
||||
result = pack_subagent(
|
||||
name="date_test",
|
||||
description="test",
|
||||
system_prompt="be helpful",
|
||||
tools=[],
|
||||
ruleset=Ruleset(origin="date_test", rules=[]),
|
||||
dependencies={"flags": AgentFeatureFlags()},
|
||||
)
|
||||
|
||||
prompt = result.spec["system_prompt"]
|
||||
assert prompt.startswith("be helpful")
|
||||
assert f"Today (UTC): {today}" in prompt
|
||||
|
||||
|
||||
def test_append_today_utc_is_idempotent_shape():
|
||||
"""Helper appends exactly one dated line and preserves the original body."""
|
||||
from datetime import UTC, datetime
|
||||
|
||||
today = datetime.now(UTC).date().isoformat()
|
||||
out = append_today_utc("body")
|
||||
assert out == f"body\n\nToday (UTC): {today}\n"
|
||||
|
||||
|
||||
def _extract_permission_mw(spec) -> PermissionMiddleware:
|
||||
"""Find the lone PermissionMiddleware in a subagent's middleware list."""
|
||||
matches = [m for m in spec["middleware"] if isinstance(m, PermissionMiddleware)]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
"""Allowlist filtering in ``_load_http_mcp_tools``.
|
||||
|
||||
Covers the fallback added after Notion renamed its MCP tools ("notion-"
|
||||
prefix) and a stale allowlist silently disabled the connector: when the
|
||||
allowlist matches zero advertised tools, load everything (HITL-gated)
|
||||
instead of nothing.
|
||||
"""
|
||||
|
||||
from datetime import UTC, datetime
|
||||
|
||||
from app.agents.chat.multi_agent_chat.shared.tools.mcp.cache import (
|
||||
CachedMCPTools,
|
||||
)
|
||||
from app.agents.chat.multi_agent_chat.shared.tools.mcp.tool import (
|
||||
_load_http_mcp_tools,
|
||||
)
|
||||
|
||||
_CACHED = CachedMCPTools(
|
||||
discovered_at=datetime.now(UTC),
|
||||
tools=[
|
||||
{"name": "notion-search", "description": "search", "input_schema": {}},
|
||||
{"name": "notion-update-page", "description": "write", "input_schema": {}},
|
||||
],
|
||||
)
|
||||
_SERVER_CONFIG = {"url": "https://example.com/mcp", "headers": {}}
|
||||
|
||||
|
||||
async def test_allowlist_match_filters_and_flags_readonly():
|
||||
tools = await _load_http_mcp_tools(
|
||||
1,
|
||||
"Notion",
|
||||
_SERVER_CONFIG,
|
||||
allowed_tools=["notion-search"],
|
||||
readonly_tools=frozenset({"notion-search"}),
|
||||
cached_tools=_CACHED,
|
||||
)
|
||||
assert [t.name for t in tools] == ["notion-search"]
|
||||
assert tools[0].metadata["hitl"] is False
|
||||
|
||||
|
||||
async def test_stale_allowlist_falls_back_to_all_tools_hitl_gated():
|
||||
tools = await _load_http_mcp_tools(
|
||||
1,
|
||||
"Notion",
|
||||
_SERVER_CONFIG,
|
||||
allowed_tools=["search", "update-page"], # server renamed everything
|
||||
readonly_tools=frozenset({"search"}),
|
||||
cached_tools=_CACHED,
|
||||
)
|
||||
assert sorted(t.name for t in tools) == ["notion-search", "notion-update-page"]
|
||||
# Renamed tools match no readonly entry -> every tool requires approval.
|
||||
assert all(t.metadata["hitl"] is True for t in tools)
|
||||
|
|
@ -123,6 +123,41 @@ async def test_capabilities_endpoint_lists_verbs_with_input_schema(monkeypatch):
|
|||
assert "properties" in entry["output_schema"]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_capabilities_endpoint_exposes_live_pricing(monkeypatch):
|
||||
"""Billed verbs report their per-item rate; free verbs report an empty list."""
|
||||
from app.capabilities.core.types import BillingUnit
|
||||
from app.config import config
|
||||
|
||||
monkeypatch.setattr(config, "PLATFORM_SCRAPE_BILLING_ENABLED", True)
|
||||
monkeypatch.setattr(config, "YOUTUBE_MICROS_PER_VIDEO", 2500)
|
||||
|
||||
billed = Capability(
|
||||
name="test.billed",
|
||||
description="Billed verb for tests.",
|
||||
input_schema=_EchoInput,
|
||||
output_schema=_EchoOutput,
|
||||
executor=_echo_executor,
|
||||
billing_unit=BillingUnit.YOUTUBE_VIDEO,
|
||||
)
|
||||
|
||||
app = _build_app([_ECHO, billed], monkeypatch)
|
||||
async with _client(app) as client:
|
||||
resp = await client.get("/api/v1/workspaces/7/scrapers/capabilities")
|
||||
assert resp.status_code == 200
|
||||
by_name = {entry["name"]: entry for entry in resp.json()}
|
||||
assert by_name["test.echo"]["pricing"] == []
|
||||
assert by_name["test.billed"]["pricing"] == [
|
||||
{"unit": "video", "micros_per_unit": 2500}
|
||||
]
|
||||
|
||||
# Rates are read live: a config retune shows up without a router rebuild.
|
||||
monkeypatch.setattr(config, "PLATFORM_SCRAPE_BILLING_ENABLED", False)
|
||||
async with _client(app) as client:
|
||||
resp = await client.get("/api/v1/workspaces/7/scrapers/capabilities")
|
||||
assert resp.json()[1]["pricing"] == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_over_budget_is_blocked_before_the_executor(monkeypatch):
|
||||
from app.capabilities.core.access import rest
|
||||
|
|
|
|||
|
|
@ -344,6 +344,9 @@ def test_validate_connector_config_invalid():
|
|||
"SEARXNG_API",
|
||||
"LINKUP_API",
|
||||
"BAIDU_SEARCH_API",
|
||||
"YOUTUBE_CONNECTOR",
|
||||
"WEBCRAWLER_CONNECTOR",
|
||||
"ELASTICSEARCH_CONNECTOR",
|
||||
],
|
||||
)
|
||||
def test_raise_if_connector_deprecated_blocks(connector_type):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue