mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-20 23:21:06 +02:00
refactor(capabilities): group framework into core/, keep verbs top-level
This commit is contained in:
parent
87df0bb629
commit
6bd0d640e6
19 changed files with 60 additions and 32 deletions
|
|
@ -7,7 +7,7 @@ from typing import Any
|
||||||
from langchain_core.tools import BaseTool
|
from langchain_core.tools import BaseTool
|
||||||
|
|
||||||
from app.agents.chat.multi_agent_chat.shared.permissions import Ruleset
|
from app.agents.chat.multi_agent_chat.shared.permissions import Ruleset
|
||||||
from app.capabilities.access.agent import build_capability_tools
|
from app.capabilities.core.access.agent import build_capability_tools
|
||||||
from app.capabilities.web.discover.definition import WEB_DISCOVER
|
from app.capabilities.web.discover.definition import WEB_DISCOVER
|
||||||
from app.capabilities.web.scrape.definition import WEB_SCRAPE
|
from app.capabilities.web.scrape.definition import WEB_SCRAPE
|
||||||
|
|
||||||
|
|
|
||||||
30
surfsense_backend/app/capabilities/core/__init__.py
Normal file
30
surfsense_backend/app/capabilities/core/__init__.py
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
"""Capability framework kernel: registry contracts, store, billing, and access doors."""
|
||||||
|
|
||||||
|
from app.capabilities.core.billing import charge_capability, gate_capability
|
||||||
|
from app.capabilities.core.store import (
|
||||||
|
all_capabilities,
|
||||||
|
get_capability,
|
||||||
|
register_capability,
|
||||||
|
)
|
||||||
|
from app.capabilities.core.types import (
|
||||||
|
BillableInput,
|
||||||
|
BillableOutput,
|
||||||
|
BillingUnit,
|
||||||
|
Capability,
|
||||||
|
CapabilityContext,
|
||||||
|
Executor,
|
||||||
|
)
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"BillableInput",
|
||||||
|
"BillableOutput",
|
||||||
|
"BillingUnit",
|
||||||
|
"Capability",
|
||||||
|
"CapabilityContext",
|
||||||
|
"Executor",
|
||||||
|
"all_capabilities",
|
||||||
|
"charge_capability",
|
||||||
|
"gate_capability",
|
||||||
|
"get_capability",
|
||||||
|
"register_capability",
|
||||||
|
]
|
||||||
|
|
@ -10,9 +10,9 @@ from __future__ import annotations
|
||||||
|
|
||||||
from langchain_core.tools import BaseTool, StructuredTool
|
from langchain_core.tools import BaseTool, StructuredTool
|
||||||
|
|
||||||
from app.capabilities.billing import charge_capability, gate_capability
|
from app.capabilities.core.billing import charge_capability, gate_capability
|
||||||
from app.capabilities.store import all_capabilities
|
from app.capabilities.core.store import all_capabilities
|
||||||
from app.capabilities.types import Capability, CapabilityContext
|
from app.capabilities.core.types import Capability, CapabilityContext
|
||||||
from app.db import async_session_maker
|
from app.db import async_session_maker
|
||||||
from app.services.web_crawl_credit_service import InsufficientCreditsError
|
from app.services.web_crawl_credit_service import InsufficientCreditsError
|
||||||
|
|
||||||
|
|
@ -8,10 +8,10 @@ from fastapi import APIRouter, Depends, HTTPException, status
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from app.auth.context import AuthContext
|
from app.auth.context import AuthContext
|
||||||
from app.capabilities.access.rate_limit import enforce_capability_rate_limit
|
from app.capabilities.core.access.rate_limit import enforce_capability_rate_limit
|
||||||
from app.capabilities.billing import charge_capability, gate_capability
|
from app.capabilities.core.billing import charge_capability, gate_capability
|
||||||
from app.capabilities.store import all_capabilities
|
from app.capabilities.core.store import all_capabilities
|
||||||
from app.capabilities.types import Capability, CapabilityContext
|
from app.capabilities.core.types import Capability, CapabilityContext
|
||||||
from app.db import get_async_session
|
from app.db import get_async_session
|
||||||
from app.services.web_crawl_credit_service import InsufficientCreditsError
|
from app.services.web_crawl_credit_service import InsufficientCreditsError
|
||||||
from app.users import get_auth_context
|
from app.users import get_auth_context
|
||||||
|
|
@ -6,7 +6,7 @@ from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
|
|
||||||
from app.capabilities.types import (
|
from app.capabilities.core.types import (
|
||||||
BillableInput,
|
BillableInput,
|
||||||
BillableOutput,
|
BillableOutput,
|
||||||
BillingUnit,
|
BillingUnit,
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from app.capabilities.types import Capability
|
from app.capabilities.core.types import Capability
|
||||||
|
|
||||||
_REGISTRY: dict[str, Capability] = {}
|
_REGISTRY: dict[str, Capability] = {}
|
||||||
|
|
||||||
|
|
@ -2,8 +2,7 @@
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from app.capabilities.store import register_capability
|
from app.capabilities.core import Capability, register_capability
|
||||||
from app.capabilities.types import Capability
|
|
||||||
from app.capabilities.web.discover.executor import build_discover_executor
|
from app.capabilities.web.discover.executor import build_discover_executor
|
||||||
from app.capabilities.web.discover.schemas import DiscoverInput, DiscoverOutput
|
from app.capabilities.web.discover.schemas import DiscoverInput, DiscoverOutput
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Sequence
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from app.capabilities.types import Executor
|
from app.capabilities.core import Executor
|
||||||
from app.capabilities.web.discover.providers.base import DiscoverProvider
|
from app.capabilities.web.discover.providers.base import DiscoverProvider
|
||||||
from app.capabilities.web.discover.schemas import DiscoverInput, DiscoverOutput
|
from app.capabilities.web.discover.schemas import DiscoverInput, DiscoverOutput
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,7 @@
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from app.capabilities.store import register_capability
|
from app.capabilities.core import BillingUnit, Capability, register_capability
|
||||||
from app.capabilities.types import BillingUnit, Capability
|
|
||||||
from app.capabilities.web.scrape.executor import build_scrape_executor
|
from app.capabilities.web.scrape.executor import build_scrape_executor
|
||||||
from app.capabilities.web.scrape.schemas import ScrapeInput, ScrapeOutput
|
from app.capabilities.web.scrape.schemas import ScrapeInput, ScrapeOutput
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from app.capabilities.types import Executor
|
from app.capabilities.core import Executor
|
||||||
from app.capabilities.web.scrape.schemas import ScrapeInput, ScrapeOutput, ScrapeRow
|
from app.capabilities.web.scrape.schemas import ScrapeInput, ScrapeOutput, ScrapeRow
|
||||||
from app.proprietary.web_crawler import (
|
from app.proprietary.web_crawler import (
|
||||||
CrawlOutcome,
|
CrawlOutcome,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ from fastapi import APIRouter, Depends
|
||||||
|
|
||||||
import app.capabilities.web # noqa: F401 (registers web.* verbs before the door builds)
|
import app.capabilities.web # noqa: F401 (registers web.* verbs before the door builds)
|
||||||
from app.automations.api import router as automations_router
|
from app.automations.api import router as automations_router
|
||||||
from app.capabilities.access.rest import build_capabilities_router
|
from app.capabilities.core.access.rest import build_capabilities_router
|
||||||
from app.file_storage.api import router as file_storage_router
|
from app.file_storage.api import router as file_storage_router
|
||||||
from app.gateway import require_gateway_enabled
|
from app.gateway import require_gateway_enabled
|
||||||
from app.notifications.api import router as notifications_router
|
from app.notifications.api import router as notifications_router
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ from unittest.mock import AsyncMock
|
||||||
import pytest
|
import pytest
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
from app.capabilities.types import BillingUnit, Capability
|
from app.capabilities.core.types import BillingUnit, Capability
|
||||||
from app.services.web_crawl_credit_service import InsufficientCreditsError
|
from app.services.web_crawl_credit_service import InsufficientCreditsError
|
||||||
|
|
||||||
pytestmark = pytest.mark.asyncio
|
pytestmark = pytest.mark.asyncio
|
||||||
|
|
@ -54,7 +54,7 @@ class _FakeSessionCtx:
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def isolate(monkeypatch):
|
def isolate(monkeypatch):
|
||||||
"""Stub the billing session + charge/gate so tools never hit the DB."""
|
"""Stub the billing session + charge/gate so tools never hit the DB."""
|
||||||
from app.capabilities.access import agent as mod
|
from app.capabilities.core.access import agent as mod
|
||||||
|
|
||||||
monkeypatch.setattr(mod, "async_session_maker", lambda: _FakeSessionCtx())
|
monkeypatch.setattr(mod, "async_session_maker", lambda: _FakeSessionCtx())
|
||||||
charge = AsyncMock()
|
charge = AsyncMock()
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import pytest
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
from starlette.requests import Request
|
from starlette.requests import Request
|
||||||
|
|
||||||
from app.capabilities.access import rate_limit
|
from app.capabilities.core.access import rate_limit
|
||||||
|
|
||||||
|
|
||||||
def _request(workspace_id: int) -> Request:
|
def _request(workspace_id: int) -> Request:
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ from fastapi import FastAPI
|
||||||
from httpx import ASGITransport, AsyncClient
|
from httpx import ASGITransport, AsyncClient
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from app.capabilities.types import Capability
|
from app.capabilities.core.types import Capability
|
||||||
from app.db import get_async_session
|
from app.db import get_async_session
|
||||||
from app.users import get_auth_context
|
from app.users import get_auth_context
|
||||||
|
|
||||||
|
|
@ -36,8 +36,8 @@ _ECHO = Capability(
|
||||||
|
|
||||||
def _build_app(capabilities, monkeypatch) -> FastAPI:
|
def _build_app(capabilities, monkeypatch) -> FastAPI:
|
||||||
"""Mount the generated door with auth/workspace/session/rate-limit stubbed."""
|
"""Mount the generated door with auth/workspace/session/rate-limit stubbed."""
|
||||||
from app.capabilities.access import rest
|
from app.capabilities.core.access import rest
|
||||||
from app.capabilities.access.rate_limit import enforce_capability_rate_limit
|
from app.capabilities.core.access.rate_limit import enforce_capability_rate_limit
|
||||||
|
|
||||||
monkeypatch.setattr(rest, "check_workspace_access", _noop_async, raising=True)
|
monkeypatch.setattr(rest, "check_workspace_access", _noop_async, raising=True)
|
||||||
|
|
||||||
|
|
@ -91,7 +91,7 @@ async def test_input_is_validated_against_the_verb_schema(monkeypatch):
|
||||||
def test_registered_verbs_appear_on_rest():
|
def test_registered_verbs_appear_on_rest():
|
||||||
"""A verb in the registry shows up as a route with no per-verb wiring."""
|
"""A verb in the registry shows up as a route with no per-verb wiring."""
|
||||||
import app.capabilities.web # noqa: F401 (registers web.* at import)
|
import app.capabilities.web # noqa: F401 (registers web.* at import)
|
||||||
from app.capabilities.access import rest
|
from app.capabilities.core.access import rest
|
||||||
|
|
||||||
router = rest.build_capabilities_router()
|
router = rest.build_capabilities_router()
|
||||||
paths = {route.path for route in router.routes}
|
paths = {route.path for route in router.routes}
|
||||||
|
|
@ -101,7 +101,7 @@ def test_registered_verbs_appear_on_rest():
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_over_budget_is_blocked_before_the_executor(monkeypatch):
|
async def test_over_budget_is_blocked_before_the_executor(monkeypatch):
|
||||||
from app.capabilities.access import rest
|
from app.capabilities.core.access import rest
|
||||||
from app.services.web_crawl_credit_service import InsufficientCreditsError
|
from app.services.web_crawl_credit_service import InsufficientCreditsError
|
||||||
|
|
||||||
async def _raise(*args, **kwargs):
|
async def _raise(*args, **kwargs):
|
||||||
|
|
@ -123,7 +123,7 @@ async def test_over_budget_is_blocked_before_the_executor(monkeypatch):
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_rate_limit_blocks_the_workspace(monkeypatch):
|
async def test_rate_limit_blocks_the_workspace(monkeypatch):
|
||||||
"""The generated route enforces the per-workspace limit (429)."""
|
"""The generated route enforces the per-workspace limit (429)."""
|
||||||
from app.capabilities.access import rate_limit, rest
|
from app.capabilities.core.access import rate_limit, rest
|
||||||
|
|
||||||
monkeypatch.setattr(rest, "check_workspace_access", _noop_async, raising=True)
|
monkeypatch.setattr(rest, "check_workspace_access", _noop_async, raising=True)
|
||||||
monkeypatch.setattr(
|
monkeypatch.setattr(
|
||||||
|
|
@ -153,7 +153,7 @@ async def test_rate_limit_blocks_the_workspace(monkeypatch):
|
||||||
async def test_success_charges_once(monkeypatch):
|
async def test_success_charges_once(monkeypatch):
|
||||||
from unittest.mock import AsyncMock
|
from unittest.mock import AsyncMock
|
||||||
|
|
||||||
from app.capabilities.access import rest
|
from app.capabilities.core.access import rest
|
||||||
|
|
||||||
charge = AsyncMock()
|
charge = AsyncMock()
|
||||||
monkeypatch.setattr(rest, "charge_capability", charge, raising=True)
|
monkeypatch.setattr(rest, "charge_capability", charge, raising=True)
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,9 @@ from uuid import UUID
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import app.capabilities.billing as billing
|
import app.capabilities.core.billing as billing
|
||||||
from app.capabilities.billing import charge_capability, gate_capability
|
from app.capabilities.core.billing import charge_capability, gate_capability
|
||||||
from app.capabilities.types import BillingUnit, CapabilityContext
|
from app.capabilities.core.types import BillingUnit, CapabilityContext
|
||||||
from app.capabilities.web.scrape.schemas import ScrapeInput, ScrapeOutput, ScrapeRow
|
from app.capabilities.web.scrape.schemas import ScrapeInput, ScrapeOutput, ScrapeRow
|
||||||
from app.config import config
|
from app.config import config
|
||||||
from app.services.web_crawl_credit_service import InsufficientCreditsError
|
from app.services.web_crawl_credit_service import InsufficientCreditsError
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ import pytest
|
||||||
from app.capabilities import (
|
from app.capabilities import (
|
||||||
web, # noqa: F401 — importing the namespace registers its verbs
|
web, # noqa: F401 — importing the namespace registers its verbs
|
||||||
)
|
)
|
||||||
from app.capabilities.store import get_capability
|
from app.capabilities.core.store import get_capability
|
||||||
from app.capabilities.types import BillingUnit
|
from app.capabilities.core.types import BillingUnit
|
||||||
from app.capabilities.web.discover.schemas import DiscoverInput, DiscoverOutput
|
from app.capabilities.web.discover.schemas import DiscoverInput, DiscoverOutput
|
||||||
from app.capabilities.web.scrape.schemas import ScrapeInput, ScrapeOutput
|
from app.capabilities.web.scrape.schemas import ScrapeInput, ScrapeOutput
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue