File size: 2,957 Bytes
21a8fc4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
"""Tests for the ScriptedClient opponent."""
from server.contract_fixtures import get_fixture
from server.private_briefs import generate_client_brief
from server.scripted_client import ScriptedClient


def test_responds_to_offer():
    fixture = get_fixture("simple_saas")
    cbrief = generate_client_brief(fixture)
    sc = ScriptedClient(cbrief, fixture, strategy="balanced", seed=1)
    vendor_action = {
        "action_type": "make_offer",
        "agent_role": "vendor",
        "proposed_terms": {
            "annual_price": 90000,
            "payment_net_days": 35,
            "contract_length_months": 30,
            "support_tier": "basic",
        },
    }
    resp = sc.respond(vendor_action, [], turn=1)
    assert isinstance(resp, dict)
    assert resp.get("agent_role") == "client"
    assert resp.get("action_type") in (
        "counter_offer",
        "accept_offer",
        "walk_away",
        "make_offer",
    )


def test_walks_away_on_dealbreaker():
    fixture = get_fixture("gdpr_dpa")
    cbrief = generate_client_brief(fixture)
    sc = ScriptedClient(cbrief, fixture, strategy="balanced", seed=2)
    # Client dealbreaker: breach_notification_hours > 72
    vendor_action = {
        "action_type": "make_offer",
        "agent_role": "vendor",
        "proposed_terms": {
            "data_retention_months": 24,
            "breach_notification_hours": 96,  # past client's dealbreaker
            "audit_frequency": "annual",
            "subprocessor_approval": "notice_only",
            "liability_cap_multiple": 1.0,
            "indemnification_scope": "limited",
        },
    }
    resp = sc.respond(vendor_action, [], turn=1)
    assert resp["action_type"] == "walk_away"


def test_concedes_in_late_game():
    fixture = get_fixture("simple_saas")
    cbrief = generate_client_brief(fixture)
    sc = ScriptedClient(cbrief, fixture, strategy="conciliatory", seed=3)
    # Late-game, vendor offers terms that are at least acceptable
    vendor_action = {
        "action_type": "counter_offer",
        "agent_role": "vendor",
        "proposed_terms": {
            "annual_price": 70000,
            "payment_net_days": 65,
            "contract_length_months": 18,
            "support_tier": "premium",
        },
    }
    resp = sc.respond(
        vendor_action,
        [{"agent_role": "vendor", "proposed_terms": vendor_action["proposed_terms"]}],
        turn=fixture["max_turns"] - 1,
    )
    assert resp["action_type"] in ("accept_offer", "counter_offer", "walk_away")


def test_strategies_are_distinguishable():
    fixture = get_fixture("simple_saas")
    cbrief = generate_client_brief(fixture)
    aggressive = ScriptedClient(cbrief, fixture, strategy="aggressive", seed=4)
    conciliatory = ScriptedClient(cbrief, fixture, strategy="conciliatory", seed=4)
    # Conciliatory accepts at lower utility than aggressive
    assert conciliatory._accept_threshold < aggressive._accept_threshold