from __future__ import annotations import uuid from datetime import datetime from typing import TYPE_CHECKING, NamedTuple from pydantic import BaseModel, Field from resonance_engine.dsp.enums import DSPClientName from resonance_engine.fandata.types import FanRecord from resonance_engine.tasks.enums import FanoutSource, TaskFinishedReason, TaskStatus if TYPE_CHECKING: from resonance_engine.fandata.models import FanConnectionFilter class FanCollectBatch(BaseModel): collect_task_id: uuid.UUID = Field(default_factory=uuid.uuid4) fanout_task_id: uuid.UUID dsp_client_name: DSPClientName fans: list[FanRecord] force: bool = False class FanoutTaskView(NamedTuple): id: uuid.UUID started_at: datetime finished_at: datetime | None source: FanoutSource status: TaskStatus finished_reason: TaskFinishedReason | None fans_dispatched: int messages_sent: int filters: FanConnectionFilter fans_collected: int fans_errors: int fans_stale_tokens: int fans_skipped: int requests: int requests_rate_limited: int collect_tasks_done: int class FanoutTaskViewPage(NamedTuple): items: list[FanoutTaskView] next_cursor: str | None total: int class FanoutAggStats(NamedTuple): total: int running: int last_started_at: datetime | None last_fans_dispatched: int | None class CollectAggStats(NamedTuple): total: int running: int stale: int last_started_at: datetime | None class CollectActivityBucket(NamedTuple): bucket: str dsp_client_name: str dsp_client_display_name: str fans_processed: int fans_errors: int fans_stale_tokens: int requests_rate_limited: int requests: int class FanoutSignals(NamedTuple): rps: float # requests / drain span — achieved fleet request rate reqs_per_fan: float # requests / fans worked duration_s: float # p90 drain span (overran check) latency_s: float # serial wall-clock per request (Σserial / Σrequests) time_per_fan_s: float # serial wall-clock per fan (Σserial / Σfans) — batch size throttle_rate: float # rate-limited requests / requests — 429 share fans_per_s: float # fans / drain span — fan drain rate; the fanout sizing signal last_fans: int # most recent fanout's worked fans — overrun no-growth cap