mirror of
https://github.com/x1xhlol/system-prompts-and-models-of-ai-tools.git
synced 2026-06-18 15:29:36 +00:00
350 lines
13 KiB
Python
350 lines
13 KiB
Python
"""
|
|
Projections — read models computed by replaying the event stream.
|
|
|
|
Each projection is a deterministic function of (event_stream, filter).
|
|
This means: nuke the projection storage, replay events, get identical state.
|
|
That property is the foundation of the audit trail and disaster recovery.
|
|
|
|
Six projections cover the dashboard's needs:
|
|
- AccountTimeline — chronological story of one account/company
|
|
- DealHealthProjection — current state + risk signals for one deal
|
|
- CampaignPerformanceProjection — sent/replied/won per campaign
|
|
- AgentActionLedger — every AI agent action + approvals
|
|
- ComplianceAuditProjection — for SDAIA / DPO inspection
|
|
- CustomerROIProjection — what Dealix delivered this period
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections import defaultdict
|
|
from collections.abc import Iterable
|
|
from dataclasses import dataclass, field
|
|
from datetime import datetime
|
|
from typing import Any
|
|
|
|
from auto_client_acquisition.revenue_memory.events import RevenueEvent
|
|
|
|
|
|
# ── 1. Account Timeline ──────────────────────────────────────────
|
|
@dataclass
|
|
class TimelineEntry:
|
|
occurred_at: datetime
|
|
event_type: str
|
|
headline: str
|
|
payload: dict[str, Any]
|
|
|
|
|
|
@dataclass
|
|
class AccountTimeline:
|
|
customer_id: str
|
|
account_id: str
|
|
entries: list[TimelineEntry] = field(default_factory=list)
|
|
first_seen: datetime | None = None
|
|
last_activity: datetime | None = None
|
|
n_messages_sent: int = 0
|
|
n_replies: int = 0
|
|
n_meetings: int = 0
|
|
n_signals: int = 0
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
return {
|
|
"customer_id": self.customer_id,
|
|
"account_id": self.account_id,
|
|
"first_seen": self.first_seen.isoformat() if self.first_seen else None,
|
|
"last_activity": self.last_activity.isoformat() if self.last_activity else None,
|
|
"metrics": {
|
|
"messages_sent": self.n_messages_sent,
|
|
"replies": self.n_replies,
|
|
"meetings": self.n_meetings,
|
|
"signals": self.n_signals,
|
|
},
|
|
"entries": [
|
|
{
|
|
"at": e.occurred_at.isoformat(),
|
|
"type": e.event_type,
|
|
"headline": e.headline,
|
|
"payload": e.payload,
|
|
}
|
|
for e in self.entries
|
|
],
|
|
}
|
|
|
|
|
|
_TYPE_HEADLINES_AR: dict[str, str] = {
|
|
"lead.created": "أُنشئ lead جديد",
|
|
"lead.qualified": "تمت تأهيل الـ lead",
|
|
"company.enriched": "اكتمل enrichment الشركة",
|
|
"signal.detected": "اكتُشفت إشارة شراء",
|
|
"message.drafted": "تم إعداد رسالة",
|
|
"message.approved": "وافق المستخدم على الرسالة",
|
|
"message.sent": "أُرسلت الرسالة",
|
|
"reply.received": "وصل رد",
|
|
"reply.classified": "تم تصنيف الرد",
|
|
"meeting.booked": "تم حجز اجتماع",
|
|
"meeting.held": "انعقد الاجتماع",
|
|
"deal.created": "أُنشئت صفقة",
|
|
"deal.stage_changed": "تغيرت مرحلة الصفقة",
|
|
"deal.won": "🏆 صفقة مكسوبة",
|
|
"deal.lost": "صفقة ضائعة",
|
|
"deal.stalled": "صفقة جامدة",
|
|
"compliance.opt_out_received": "وصل طلب opt-out",
|
|
"compliance.blocked": "حُظرت رسالة لأسباب امتثال",
|
|
}
|
|
|
|
|
|
def build_account_timeline(
|
|
*,
|
|
customer_id: str,
|
|
account_id: str,
|
|
events: Iterable[RevenueEvent],
|
|
) -> AccountTimeline:
|
|
"""Replay events for one account into a chronological timeline."""
|
|
timeline = AccountTimeline(customer_id=customer_id, account_id=account_id)
|
|
sorted_events = sorted(events, key=lambda e: e.occurred_at)
|
|
for e in sorted_events:
|
|
if e.subject_type not in ("account", "company") or e.subject_id != account_id:
|
|
continue
|
|
if timeline.first_seen is None:
|
|
timeline.first_seen = e.occurred_at
|
|
timeline.last_activity = e.occurred_at
|
|
if e.event_type == "message.sent":
|
|
timeline.n_messages_sent += 1
|
|
elif e.event_type in ("reply.received", "reply.classified"):
|
|
if e.event_type == "reply.received":
|
|
timeline.n_replies += 1
|
|
elif e.event_type in ("meeting.booked", "meeting.held"):
|
|
if e.event_type == "meeting.booked":
|
|
timeline.n_meetings += 1
|
|
elif e.event_type == "signal.detected":
|
|
timeline.n_signals += 1
|
|
|
|
timeline.entries.append(
|
|
TimelineEntry(
|
|
occurred_at=e.occurred_at,
|
|
event_type=e.event_type,
|
|
headline=_TYPE_HEADLINES_AR.get(e.event_type, e.event_type),
|
|
payload=e.payload,
|
|
)
|
|
)
|
|
return timeline
|
|
|
|
|
|
# ── 2. Deal Health Projection ────────────────────────────────────
|
|
@dataclass
|
|
class DealHealthProjection:
|
|
customer_id: str
|
|
deal_id: str
|
|
current_stage: str = "unknown"
|
|
value_sar: float = 0.0
|
|
days_in_current_stage: int = 0
|
|
last_activity_at: datetime | None = None
|
|
risk_flags: list[str] = field(default_factory=list)
|
|
health_score: float = 50.0
|
|
expected_close: datetime | None = None
|
|
stage_history: list[tuple[datetime, str]] = field(default_factory=list)
|
|
|
|
|
|
def build_deal_health(
|
|
*, customer_id: str, deal_id: str, events: Iterable[RevenueEvent], now: datetime
|
|
) -> DealHealthProjection:
|
|
"""Compute current deal health from its event stream."""
|
|
proj = DealHealthProjection(customer_id=customer_id, deal_id=deal_id)
|
|
last_stage_at = None
|
|
for e in sorted(events, key=lambda x: x.occurred_at):
|
|
if e.subject_type != "deal" or e.subject_id != deal_id:
|
|
continue
|
|
proj.last_activity_at = e.occurred_at
|
|
if e.event_type == "deal.created":
|
|
proj.value_sar = float(e.payload.get("value_sar", 0))
|
|
proj.current_stage = e.payload.get("stage", "open")
|
|
last_stage_at = e.occurred_at
|
|
proj.stage_history.append((e.occurred_at, proj.current_stage))
|
|
elif e.event_type == "deal.stage_changed":
|
|
new_stage = e.payload.get("to_stage", proj.current_stage)
|
|
proj.current_stage = new_stage
|
|
last_stage_at = e.occurred_at
|
|
proj.stage_history.append((e.occurred_at, new_stage))
|
|
elif e.event_type == "deal.won":
|
|
proj.current_stage = "won"
|
|
proj.health_score = 100
|
|
proj.risk_flags = []
|
|
elif e.event_type == "deal.lost":
|
|
proj.current_stage = "lost"
|
|
proj.health_score = 0
|
|
elif e.event_type == "deal.stalled":
|
|
proj.risk_flags.append("stalled")
|
|
proj.health_score = max(0, proj.health_score - 30)
|
|
|
|
if last_stage_at and proj.current_stage not in ("won", "lost"):
|
|
proj.days_in_current_stage = max(0, (now - last_stage_at).days)
|
|
if proj.days_in_current_stage > 21:
|
|
proj.risk_flags.append(f"in_stage_{proj.days_in_current_stage}d")
|
|
proj.health_score = max(0, proj.health_score - 20)
|
|
return proj
|
|
|
|
|
|
# ── 3. Campaign Performance Projection ──────────────────────────
|
|
@dataclass
|
|
class CampaignPerformanceProjection:
|
|
customer_id: str
|
|
campaign_id: str
|
|
sent: int = 0
|
|
replied: int = 0
|
|
meetings_booked: int = 0
|
|
deals_won: int = 0
|
|
revenue_won_sar: float = 0.0
|
|
blocked_compliance: int = 0
|
|
open_rate: float = 0.0
|
|
reply_rate: float = 0.0
|
|
win_rate: float = 0.0
|
|
|
|
|
|
def build_campaign_performance(
|
|
*, customer_id: str, campaign_id: str, events: Iterable[RevenueEvent]
|
|
) -> CampaignPerformanceProjection:
|
|
proj = CampaignPerformanceProjection(customer_id=customer_id, campaign_id=campaign_id)
|
|
n_opened = 0
|
|
for e in events:
|
|
if e.payload.get("campaign_id") != campaign_id:
|
|
continue
|
|
if e.event_type == "message.sent":
|
|
proj.sent += 1
|
|
elif e.event_type == "message.opened":
|
|
n_opened += 1
|
|
elif e.event_type == "reply.received":
|
|
proj.replied += 1
|
|
elif e.event_type == "meeting.booked":
|
|
proj.meetings_booked += 1
|
|
elif e.event_type == "deal.won":
|
|
proj.deals_won += 1
|
|
proj.revenue_won_sar += float(e.payload.get("value_sar", 0))
|
|
elif e.event_type == "compliance.blocked":
|
|
proj.blocked_compliance += 1
|
|
if proj.sent:
|
|
proj.open_rate = round(n_opened / proj.sent, 4)
|
|
proj.reply_rate = round(proj.replied / proj.sent, 4)
|
|
if proj.meetings_booked:
|
|
proj.win_rate = round(proj.deals_won / proj.meetings_booked, 4)
|
|
return proj
|
|
|
|
|
|
# ── 4. Agent Action Ledger ──────────────────────────────────────
|
|
@dataclass
|
|
class AgentAction:
|
|
occurred_at: datetime
|
|
event_type: str # requested / approved / rejected / executed / failed
|
|
agent_id: str
|
|
task_id: str
|
|
actor: str
|
|
payload: dict[str, Any]
|
|
|
|
|
|
@dataclass
|
|
class AgentActionLedger:
|
|
customer_id: str
|
|
actions: list[AgentAction] = field(default_factory=list)
|
|
by_agent: dict[str, int] = field(default_factory=dict)
|
|
by_status: dict[str, int] = field(default_factory=dict)
|
|
requires_review: int = 0
|
|
|
|
|
|
def build_agent_ledger(
|
|
*, customer_id: str, events: Iterable[RevenueEvent]
|
|
) -> AgentActionLedger:
|
|
ledger = AgentActionLedger(customer_id=customer_id)
|
|
for e in events:
|
|
if not e.event_type.startswith("agent."):
|
|
continue
|
|
agent_id = e.payload.get("agent_id", "unknown")
|
|
action = AgentAction(
|
|
occurred_at=e.occurred_at,
|
|
event_type=e.event_type,
|
|
agent_id=agent_id,
|
|
task_id=e.payload.get("task_id", ""),
|
|
actor=e.actor,
|
|
payload=e.payload,
|
|
)
|
|
ledger.actions.append(action)
|
|
ledger.by_agent[agent_id] = ledger.by_agent.get(agent_id, 0) + 1
|
|
status = e.event_type.split(".", 1)[1]
|
|
ledger.by_status[status] = ledger.by_status.get(status, 0) + 1
|
|
if e.event_type == "agent.action_requested" and e.payload.get("requires_approval"):
|
|
ledger.requires_review += 1
|
|
return ledger
|
|
|
|
|
|
# ── 5. Compliance Audit Projection ──────────────────────────────
|
|
@dataclass
|
|
class ComplianceAuditProjection:
|
|
customer_id: str
|
|
consent_recorded: int = 0
|
|
opt_outs: int = 0
|
|
blocked_messages: int = 0
|
|
dsr_received: int = 0
|
|
dsr_completed: int = 0
|
|
last_block_reason: str | None = None
|
|
|
|
|
|
def build_compliance_audit(
|
|
*, customer_id: str, events: Iterable[RevenueEvent]
|
|
) -> ComplianceAuditProjection:
|
|
proj = ComplianceAuditProjection(customer_id=customer_id)
|
|
for e in events:
|
|
if e.event_type == "compliance.consent_recorded":
|
|
proj.consent_recorded += 1
|
|
elif e.event_type == "compliance.opt_out_received":
|
|
proj.opt_outs += 1
|
|
elif e.event_type == "compliance.blocked":
|
|
proj.blocked_messages += 1
|
|
proj.last_block_reason = e.payload.get("reason")
|
|
elif e.event_type == "compliance.dsr_received":
|
|
proj.dsr_received += 1
|
|
elif e.event_type == "compliance.dsr_completed":
|
|
proj.dsr_completed += 1
|
|
return proj
|
|
|
|
|
|
# ── 6. Customer ROI Projection ──────────────────────────────────
|
|
@dataclass
|
|
class CustomerROIProjection:
|
|
customer_id: str
|
|
period_start: datetime | None
|
|
period_end: datetime | None
|
|
n_leads: int = 0
|
|
n_meetings: int = 0
|
|
n_proposals: int = 0
|
|
n_deals_won: int = 0
|
|
revenue_won_sar: float = 0.0
|
|
pipeline_added_sar: float = 0.0
|
|
|
|
|
|
def build_customer_roi(
|
|
*,
|
|
customer_id: str,
|
|
events: Iterable[RevenueEvent],
|
|
period_start: datetime | None = None,
|
|
period_end: datetime | None = None,
|
|
) -> CustomerROIProjection:
|
|
proj = CustomerROIProjection(
|
|
customer_id=customer_id,
|
|
period_start=period_start,
|
|
period_end=period_end,
|
|
)
|
|
for e in events:
|
|
if period_start and e.occurred_at < period_start:
|
|
continue
|
|
if period_end and e.occurred_at > period_end:
|
|
continue
|
|
if e.event_type == "lead.created":
|
|
proj.n_leads += 1
|
|
elif e.event_type == "meeting.booked":
|
|
proj.n_meetings += 1
|
|
elif e.event_type == "deal.proposal_sent":
|
|
proj.n_proposals += 1
|
|
elif e.event_type == "deal.created":
|
|
proj.pipeline_added_sar += float(e.payload.get("value_sar", 0))
|
|
elif e.event_type == "deal.won":
|
|
proj.n_deals_won += 1
|
|
proj.revenue_won_sar += float(e.payload.get("value_sar", 0))
|
|
return proj
|