Files
smart-org/apps/api/app/application/container_org.py
T
mjalmoudy94andClaude Sonnet 5.5 fef5a9deab feat(integration): wire v2 workstreams together (engine policy/budget, sandbox, tenant checks, order route, docs)
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>
2026-10-14 20:57:56 +03:30

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