Mercury Genesys Token Calculator

This commit is contained in:
2026-08-07 14:49:39 -04:00
parent 22a5d907d6
commit 89b835d681
35 changed files with 14105 additions and 2912 deletions

View File

@@ -0,0 +1,206 @@
"""Volumes → tokens, with the three published rules that trip the naive math.
Every number below was computed by hand first, then pinned.
"""
from __future__ import annotations
import pytest
from genesyscalc.meters import LicenceModel, Tier
from genesyscalc.usage import (
DeflectionMix,
VoiceBotUsage,
Volumes,
apply_copilot_covers_summary,
subscription_tokens,
tier_interactions,
virtual_agent_tokens,
voice_bot_billable_minutes,
voice_bot_roundup_uplift,
voice_bot_tokens,
)
# ── 1 · the 15-second per-call round-up ──────────────────────────────────
def test_voice_bot_15_second_roundup() -> None:
"""1,000 calls at a 40s average.
ceil(40/15) = 3 increments = 45s = 0.75 min/call → 750.0 min/month.
The naive 40/60 × 1,000 = 666.67 min understates by 12.5%.
"""
usage = VoiceBotUsage(calls_per_month=1_000, avg_bot_seconds_per_call=40)
assert voice_bot_billable_minutes(usage) == pytest.approx(750.0)
assert voice_bot_tokens(usage) == pytest.approx(750.0 / 17.0)
assert voice_bot_tokens(usage) == pytest.approx(44.117647, rel=1e-6)
def test_roundup_uplift_is_reportable() -> None:
"""The stage-facing number: 12.5% more than the raw duration implies."""
usage = VoiceBotUsage(calls_per_month=1_000, avg_bot_seconds_per_call=40)
assert voice_bot_roundup_uplift(usage) == pytest.approx(0.125)
def test_roundup_is_exact_on_increment_boundaries() -> None:
"""45s is exactly three increments — no inflation, uplift 0."""
usage = VoiceBotUsage(calls_per_month=1_000, avg_bot_seconds_per_call=45)
assert voice_bot_billable_minutes(usage) == pytest.approx(750.0)
assert voice_bot_roundup_uplift(usage) == pytest.approx(0.0)
def test_roundup_penalises_short_calls_hardest() -> None:
"""A 5-second bot greeting bills as 15 seconds — 3× its duration.
1,000 calls × 5s = 83.33 raw minutes, billed as 250.0.
"""
usage = VoiceBotUsage(calls_per_month=1_000, avg_bot_seconds_per_call=5)
assert voice_bot_billable_minutes(usage) == pytest.approx(250.0)
assert voice_bot_roundup_uplift(usage) == pytest.approx(2.0)
def test_zero_bot_volume_is_free_and_does_not_divide_by_zero() -> None:
usage = VoiceBotUsage(calls_per_month=0, avg_bot_seconds_per_call=0)
assert voice_bot_billable_minutes(usage) == 0.0
assert voice_bot_roundup_uplift(usage) == 0.0
assert voice_bot_tokens(usage) == 0.0
def test_negative_bot_usage_rejected() -> None:
with pytest.raises(ValueError, match="calls_per_month"):
VoiceBotUsage(calls_per_month=-1, avg_bot_seconds_per_call=30)
# ── 2 · the highest-tier rule as a partition ─────────────────────────────
def test_tier_shares_must_partition() -> None:
"""Shares summing past 1.0 would bill some interaction twice."""
with pytest.raises(ValueError, match="sum to at most 1.0"):
DeflectionMix(
bot_only_share=0.5, virtual_agent_share=0.4, agentic_va_share=0.3
)
def test_tier_share_bounds() -> None:
with pytest.raises(ValueError, match="must be in"):
DeflectionMix(virtual_agent_share=1.4)
def test_agent_handled_share_is_the_remainder() -> None:
mix = DeflectionMix(
bot_only_share=0.30, virtual_agent_share=0.15, agentic_va_share=0.05
)
assert mix.total_deflected_share == pytest.approx(0.50)
assert mix.agent_handled_share == pytest.approx(0.50)
def test_highest_tier_charges_each_interaction_once() -> None:
"""100,000 interactions: 30% bot, 15% VA, 5% agentic VA.
VA = 100,000 × 0.15 × 0.5 tokens = 7,500
AVA = 100,000 × 0.05 × 1.2 tokens = 6,000
------
13,500
Applying every tier to the whole volume — the additive reading — would
give 50,000 + 120,000 = far more, billing the same interaction at
several tiers at once.
"""
mix = DeflectionMix(
bot_only_share=0.30, virtual_agent_share=0.15, agentic_va_share=0.05
)
tokens = virtual_agent_tokens(100_000, mix)
assert tokens["virtual_agent"] == pytest.approx(7_500.0)
assert tokens["agentic_virtual_agent"] == pytest.approx(6_000.0)
assert sum(tokens.values()) == pytest.approx(13_500.0)
def test_tier_interactions_never_exceed_the_volume() -> None:
mix = DeflectionMix(
bot_only_share=0.40, virtual_agent_share=0.35, agentic_va_share=0.25
)
split = tier_interactions(100_000, mix)
assert sum(split.values()) == pytest.approx(100_000.0)
assert split[Tier.BOT] == pytest.approx(40_000.0)
def test_bot_tier_is_not_priced_per_interaction() -> None:
"""Voice bots bill in minutes with a round-up, so they are absent here."""
mix = DeflectionMix(bot_only_share=1.0)
assert virtual_agent_tokens(100_000, mix) == {
"virtual_agent": 0.0,
"agentic_virtual_agent": 0.0,
}
# ── 3 · Copilot covers Supervisor summaries ──────────────────────────────
def test_summary_billed_when_copilot_off() -> None:
usage = apply_copilot_covers_summary(
{"ai_summary_and_insights": 120_000.0}, copilot_enabled=False
)
assert usage.get("ai_summary_and_insights") == pytest.approx(120_000.0)
assert usage.notes == ()
def test_copilot_covers_summary_zeroes_the_line() -> None:
"""The published exclusion, enforced — and reported, not silent."""
usage = apply_copilot_covers_summary(
{"ai_summary_and_insights": 120_000.0}, copilot_enabled=True
)
assert usage.get("ai_summary_and_insights") == 0.0
assert len(usage.notes) == 1
assert "Agent Copilot is enabled" in usage.notes[0]
def test_copilot_exclusion_leaves_other_meters_alone() -> None:
usage = apply_copilot_covers_summary(
{"ai_summary_and_insights": 100.0, "ai_scoring": 500.0}, copilot_enabled=True
)
assert usage.get("ai_scoring") == pytest.approx(500.0)
def test_copilot_exclusion_is_quiet_when_there_is_nothing_to_zero() -> None:
usage = apply_copilot_covers_summary(
{"ai_summary_and_insights": 0.0}, copilot_enabled=True
)
assert usage.notes == ()
# ── subscription tokens ──────────────────────────────────────────────────
def test_per_user_tokens_are_exact_and_licence_sensitive() -> None:
"""500 users × 40 = 20,000 named; × 60 = 30,000 concurrent."""
keys = frozenset({"agent_copilot_named"})
assert subscription_tokens(500, keys, LicenceModel.NAMED) == {
"agent_copilot_named": 20_000.0
}
assert subscription_tokens(500, keys, LicenceModel.CONCURRENT) == {
"agent_copilot_named": 30_000.0
}
def test_subscription_rejects_a_consumption_meter() -> None:
with pytest.raises(ValueError, match="not a per-user meter"):
subscription_tokens(10, frozenset({"ai_scoring"}), LicenceModel.NAMED)
# ── volumes ──────────────────────────────────────────────────────────────
def test_volume_totals() -> None:
v = Volumes(
voice_inbound_monthly=100_000,
voice_outbound_monthly=20_000,
digital_sessions_monthly=8_000,
email_monthly=12_000,
)
assert v.voice_total_monthly == pytest.approx(120_000.0)
assert v.all_interactions_monthly == pytest.approx(140_000.0)
def test_negative_volume_rejected() -> None:
with pytest.raises(ValueError, match="voice_inbound_monthly"):
Volumes(voice_inbound_monthly=-1)