""" Client overlay — Variant 4's personalization layer. The verbatim anchor is Forrester's *composite organization* ($2.5B revenue, 600 CX agents, 80k weekly interactions). This module rescales that composite to a client's size: a 🟡 **first-order linear rescale**, answering "what does the composite look like at your scale?", not "what is your TEI?". Each verbatim row is tied to the driver that dominates its derivation in the PDF (see ``BENEFIT_DRIVERS``/``COST_DRIVERS``); rows scale linearly with their driver, project-based costs stay fixed. This composite's trajectory is flat (Y2 = Y3), so there is no growth re-base; linear scaling preserves the legacy-retirement ramp shape. The one non-ratio driver is ``ai_tokens_annual``: the published study models **$0** Genesys AI Experience token consumption (see the anchor's footnote), so a client case prices that line directly — the negotiated annual figure from the Genesys quote replaces the row's year values outright. ``overlay_rows(COMPOSITE)`` is the identity — it reproduces the verbatim numbers exactly (tokens included, at $0), so headless widget defaults form the published-study reproduction the gate expects. The anchor is never mutated: every function deep-copies. """ from __future__ import annotations from copy import deepcopy from dataclasses import dataclass from .anchor import ASSUMPTIONS, BENEFITS_VERBATIM, COSTS_VERBATIM @dataclass(frozen=True) class ClientDrivers: """Client inputs; defaults are the Forrester composite (identity overlay).""" agents_fte: int = ASSUMPTIONS["agents_fte"] # 600 (400 concurrent licenses) weekly_interactions: int = ASSUMPTIONS["weekly_interactions"] # 80,000 @ 12 min annual_revenue: float = ASSUMPTIONS["annual_revenue"] # $2.5B ai_tokens_annual: float = 0.0 # 🔴 published study models $0 AI consumption discount_rate: float = ASSUMPTIONS["discount_rate"] # 0.10 COMPOSITE = ClientDrivers() #: 🟡 Which driver each verbatim row scales with, per its PDF derivation. BENEFIT_DRIVERS: dict[str, str] = { "legacy_retirement": "agents", # seat-scoped legacy platform costs "self_service_savings": "interactions", # deflected volume → FTEs "agent_efficiency": "interactions", # MTTR saving × handled volume "agent_assist_sales": "revenue", # 20% of revenue × lift × margin } COST_DRIVERS: dict[str, str] = { "cx_cloud_licenses": "agents", # 400 concurrent of 600 agents "implementation": "fixed", # 10-week project — does not scale "ongoing_management": "fixed", # small fixed team "genesys_ai_tokens": "ai_tokens", # 🔴 direct annual input, not a ratio } def scale_factor(driver: str, d: ClientDrivers) -> float: """Linear size ratio vs the composite for one ratio-driver kind.""" if driver == "agents": return d.agents_fte / ASSUMPTIONS["agents_fte"] if driver == "interactions": return d.weekly_interactions / ASSUMPTIONS["weekly_interactions"] if driver == "revenue": return d.annual_revenue / ASSUMPTIONS["annual_revenue"] if driver == "fixed": return 1.0 raise KeyError(f"Unknown driver: {driver!r}") def overlay_rows(d: ClientDrivers = COMPOSITE) -> tuple[list[dict], list[dict]]: """ Deep-copied (benefits, costs) rows rescaled to the client's drivers. Ratio-driven rows: ``year_values[n] ×= scale_factor(driver)`` (and ``initial`` likewise). Fixed rows are untouched. The ``ai_tokens`` row takes ``d.ai_tokens_annual`` as each year's value directly — the negotiated quote figure, not a rescale of the anchor's $0. Risk factors, labels, and notes are unchanged everywhere. """ def _apply(rows: list[dict], drivers: dict[str, str]) -> list[dict]: out = [] for raw in rows: row = deepcopy(raw) driver = drivers[row["field_key"]] if driver == "ai_tokens": row["year_values"] = { k: float(d.ai_tokens_annual) for k in row["year_values"] } elif driver != "fixed": s = scale_factor(driver, d) row["year_values"] = { k: float(v) * s for k, v in row["year_values"].items() } if row.get("initial"): row["initial"] = float(row["initial"]) * s out.append(row) return out return (_apply(BENEFITS_VERBATIM, BENEFIT_DRIVERS), _apply(COSTS_VERBATIM, COST_DRIVERS))