File size: 2,775 Bytes
0a2e8cf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import asyncio

import pytest

from openjev_server.profile import Profile
from openjev_server.readout import BadQuestion, Readout, choice_confidence, compose_chunks, score_confidence, softmax


def test_confidence_formulas():
    assert choice_confidence([1.0]) == 1.0
    assert choice_confidence([0.25] * 4) == 0.0
    assert abs(choice_confidence([0.7, 0.1, 0.1, 0.1]) - (0.7 - 0.25) / 0.75) < 1e-12
    assert score_confidence([0.0, 0.0, 1.0]) == 1.0
    assert 0.0 <= score_confidence([0.5, 0.0, 0.5]) <= 1.0


def test_compose_chunks_is_a_distribution_and_order_free():
    parts = [([0.7, 0.2, 0.1], 0), ([0.1, 0.6, 0.3], 1)]
    p = compose_chunks(parts, [0.4, 0.6])
    assert abs(sum(p) - 1) < 1e-12 and all(x > 0 for x in p)
    assert abs(p[0] / p[4] - (0.4 * 0.7 / 0.7) / (0.6 * 0.6 / 0.6)) < 1e-12  # winners keep the final ratio


def test_softmax():
    p = softmax([0.0, 0.0])
    assert abs(p[0] - 0.5) < 1e-12


def test_bad_questions(fake_backend):
    ro = Readout(fake_backend, Profile())
    with pytest.raises(BadQuestion):
        asyncio.run(ro.answer_all("s", {"q": {"type": "choice", "instructions": "x", "criteria": {}}}))
    with pytest.raises(BadQuestion):
        asyncio.run(ro.answer_all("s", {"q": {"type": "score", "instructions": "x", "criteria": ["one"]}}))
    with pytest.raises(BadQuestion):
        asyncio.run(ro.answer_all("s", {"q": {"type": "choice", "criteria": {"a": None}}}))
    with pytest.raises(BadQuestion):
        asyncio.run(ro.answer_all("s", {"q": {"type": "essay"}}))


def test_many_options_sum_to_one_and_use_two_stages(fake_backend):
    ro = Readout(fake_backend, Profile())
    ans, _ = asyncio.run(ro.answer_all("s", {"q": {"type": "choice", "instructions": "pick", "criteria": {f"o{i}": None for i in range(130)}}}))
    p = ans["q"]["probabilities"]
    assert len(p) == 130 and abs(sum(p.values()) - 1) < 0.01 and ans["q"]["choice"] in p
    assert len(fake_backend.prompts) == 4  # 3 chunks of <= 52 + 1 over the winners


def test_stagger_runs_first_question_alone(fake_backend):
    ro = Readout(fake_backend, Profile(stagger=True, stagger_min_chars=10))
    ans, toks = asyncio.run(ro.answer_all("x" * 100, {"a": {"type": "noul", "instructions": "yes?"}, "b": {"type": "noul", "instructions": "no?"}}))
    assert set(ans) == {"a", "b"} and toks > 0 and 0 <= ans["a"]["noul"] <= 1


def test_noul_calibration_direction(fake_backend):
    ro = Readout(fake_backend, Profile(noul_t=1.0, noul_bias=0.0))
    ans, _ = asyncio.run(ro.answer_all("s", {"q": {"type": "noul", "instructions": "?"}}))
    ro2 = Readout(fake_backend, Profile(noul_t=1.0, noul_bias=3.0))
    ans2, _ = asyncio.run(ro2.answer_all("s", {"q": {"type": "noul", "instructions": "?"}}))
    assert ans2["q"]["noul"] > ans["q"]["noul"]