refactor(capabilities): group framework into core/, keep verbs top-level

This commit is contained in:
CREDO23 2026-07-02 19:54:20 +02:00
parent 87df0bb629
commit 6bd0d640e6
19 changed files with 60 additions and 32 deletions

View file

@ -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

View 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",
]

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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] = {}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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

View file

@ -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()

View file

@ -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:

View file

@ -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)

View file

@ -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

View file

@ -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