File size: 2,149 Bytes
ffb5352
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""Per-request rotation over equally-plausible structural options.

The rewrite engine is deterministic: the same sentence always yields the same
winner, so repeating a request reproduces the previous output. Rotating the
candidate order with a request-scoped seed makes repeated rewrites pick a
different valid structure without loosening any safety gate — only options that
already passed ranking are reordered, and only within a confidence band of the
best one, so a weak template never displaces a clearly better one.
"""

from __future__ import annotations

import secrets
from typing import Callable, TypeVar

T = TypeVar("T")

# How far below the top confidence an option may sit and still be considered
# interchangeable with it. Wide enough to cover the usual spread of valid
# reorders for one sentence, narrow enough to exclude last-resort templates.
_BAND = 0.20


def new_seed() -> int:
    """Fresh request seed. Callers may pass their own for reproducible output."""
    return secrets.randbelow(1_000_000)


def rotate(
    options: list[T],
    *,
    seed: int | None,
    position: int = 0,
    confidence_of: Callable[[T], float] | None = None,
    band: float = _BAND,
) -> list[T]:
    """Reorder ``options`` so a different near-best option leads each request.

    ``position`` (usually the sentence index) keeps neighbouring sentences from
    all rotating to the same offset, which would trade one fixed pattern for
    another. Options outside the confidence band keep their original order and
    stay behind the rotated group.
    """
    if seed is None or len(options) < 2:
        return list(options)

    if confidence_of is None:
        head, tail = list(options), []
    else:
        best = max(confidence_of(option) for option in options)
        head = [
            option for option in options if confidence_of(option) >= best - band
        ]
        tail = [option for option in options if confidence_of(option) < best - band]

    if len(head) < 2:
        return [*head, *tail]

    offset = (seed + position * 7919) % len(head)
    rotated = head[offset:] + head[:offset]
    return [*rotated, *tail]