Engine honours org policy and monthly budget, legacy runner skips engine tasks, sandbox session provisioned on demand, project fallback for threads, reviewer fallback, tenant checks on project ids, memory reflection respects grants, expiry worker, MCP pipeline_status, login redirect, sharing settings, e2e smoke. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
99 lines
3.4 KiB
Python
99 lines
3.4 KiB
Python
"""Wiring for the org-platform workstream (memory, model grants).
|
|
|
|
``build_memory`` is the frozen ``MemoryPort`` builder the engine calls; it now
|
|
returns the real ``MemoryService``. ``build_memory_service`` is the same object,
|
|
typed for the router (list, teach, pin, confirm, delete). ``build_model_grants``
|
|
returns the real ``ModelGrantService``; ``build_model_grant_service`` is its
|
|
typed twin for the admin router.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.application.ports.memory import MemoryPort
|
|
from app.application.ports.model_grants import ModelGrantPort
|
|
from app.application.services.memory_service import MemoryService
|
|
from app.application.services.model_grant_service import ModelGrantService
|
|
from app.infrastructure.repositories.ai_control import (
|
|
AiModelRepository,
|
|
ModelCredentialRepository,
|
|
RateCardRepository,
|
|
)
|
|
from app.infrastructure.repositories.audit import UsageLedgerRepository
|
|
from app.infrastructure.repositories.memory import MemoryRepository
|
|
from app.infrastructure.repositories.org_grants import (
|
|
DefaultModelGrantRepository,
|
|
OrgModelGrantRepository,
|
|
OrgPolicyRepository,
|
|
)
|
|
|
|
|
|
def build_memory(session: AsyncSession) -> MemoryPort:
|
|
"""The real memory service: recall, teach, and one reflection per task.
|
|
|
|
The reflection runs only on a model the organization was granted, so the
|
|
owner's grants decide which model learns on the organization's behalf.
|
|
|
|
``get_model_provider`` is resolved from ``container``'s namespace rather
|
|
than from the registry directly: that module is what the composition root
|
|
re-exports, so a deployment (or the test suite) that swaps the adapter in
|
|
one place swaps it here too.
|
|
"""
|
|
from app.application.container import get_model_provider
|
|
|
|
return build_memory_service(
|
|
session, provider=get_model_provider(), grants=build_model_grants(session)
|
|
)
|
|
|
|
|
|
def build_memory_service(
|
|
session: AsyncSession,
|
|
*,
|
|
provider=None,
|
|
grants: ModelGrantPort | None = None,
|
|
) -> MemoryService:
|
|
"""The same object, typed for the router (list, teach, pin, confirm, delete).
|
|
|
|
``grants`` is optional. The router never reflects, and a direct construction
|
|
without it keeps the routing-table path; the engine's path (``build_memory``)
|
|
always passes it.
|
|
"""
|
|
if provider is None:
|
|
from app.application.container import get_model_provider
|
|
|
|
provider = get_model_provider()
|
|
return MemoryService(
|
|
session=session,
|
|
memories=MemoryRepository(session),
|
|
models=AiModelRepository(session),
|
|
credentials=ModelCredentialRepository(session),
|
|
rate_cards=RateCardRepository(session),
|
|
ledger=UsageLedgerRepository(session),
|
|
provider=provider,
|
|
grants=grants,
|
|
)
|
|
|
|
|
|
def build_model_grants(session: AsyncSession) -> ModelGrantPort:
|
|
"""The real grant service: an organization sees only what the owner gave it."""
|
|
return build_model_grant_service(session)
|
|
|
|
|
|
def build_model_grant_service(session: AsyncSession) -> ModelGrantService:
|
|
"""The same object, typed for the admin router (grants, defaults, policy)."""
|
|
return ModelGrantService(
|
|
session=session,
|
|
grants=OrgModelGrantRepository(session),
|
|
defaults=DefaultModelGrantRepository(session),
|
|
policies=OrgPolicyRepository(session),
|
|
)
|
|
|
|
|
|
__all__ = [
|
|
"build_memory",
|
|
"build_memory_service",
|
|
"build_model_grant_service",
|
|
"build_model_grants",
|
|
]
|