مخزن اختصاصی برنامهٔ وب؛ محتوای پوشهٔ web در ریشهٔ مخزن قرار گرفت. تاریخچهٔ پروژهٔ ربات (agy-bot) در این مخزن وجود ندارد و هیچ وابستگی runtime به آن نیست.
74 lines
2.0 KiB
Python
74 lines
2.0 KiB
Python
"""Audit and usage-ledger ports.
|
|
|
|
``AuditPort`` writes the immutable trail; ``UsageLedgerPort`` is the billing
|
|
source of truth. Both are append-only by contract.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
from datetime import datetime
|
|
from typing import Protocol, runtime_checkable
|
|
|
|
from app.domain.ai_control.models import RateUnit
|
|
from app.domain.audit.models import AuditEvent
|
|
|
|
|
|
@dataclass(slots=True, kw_only=True)
|
|
class UsageRecord:
|
|
organization_id: str
|
|
unit: RateUnit
|
|
quantity: float
|
|
unit_price: float = 0.0
|
|
internal_cost_estimate: float = 0.0
|
|
total_price: float = 0.0
|
|
project_id: str | None = None
|
|
user_id: str | None = None
|
|
employee_id: str | None = None
|
|
run_id: str | None = None
|
|
metadata_json: dict = field(default_factory=dict)
|
|
|
|
|
|
@dataclass(slots=True, kw_only=True)
|
|
class UsageAggregate:
|
|
unit: RateUnit
|
|
quantity: float = 0.0
|
|
total_price: float = 0.0
|
|
internal_cost_estimate: float = 0.0
|
|
|
|
|
|
@dataclass(slots=True, kw_only=True)
|
|
class UsageBreakdown:
|
|
"""Grouped totals for the billing dashboard."""
|
|
|
|
by_unit: list[UsageAggregate] = field(default_factory=list)
|
|
by_employee: dict[str, float] = field(default_factory=dict)
|
|
by_project: dict[str, float] = field(default_factory=dict)
|
|
total_price: float = 0.0
|
|
total_cost: float = 0.0
|
|
|
|
|
|
@runtime_checkable
|
|
class AuditPort(Protocol):
|
|
async def record(self, *, event: AuditEvent) -> AuditEvent: ...
|
|
|
|
async def list_for_organization(
|
|
self, *, organization_id: str, limit: int = 100, offset: int = 0
|
|
) -> list[AuditEvent]: ...
|
|
|
|
|
|
@runtime_checkable
|
|
class UsageLedgerPort(Protocol):
|
|
async def record(self, *, usage: UsageRecord) -> None:
|
|
"""Append one usage event. Never raises for business reasons — a failed
|
|
ledger write is an infrastructure error and must surface loudly."""
|
|
...
|
|
|
|
async def breakdown(
|
|
self,
|
|
*,
|
|
organization_id: str,
|
|
since: datetime | None = None,
|
|
until: datetime | None = None,
|
|
) -> UsageBreakdown: ...
|