consequa-x 0.1.0a2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- consequa_x/__init__.py +22 -0
- consequa_x/__main__.py +3 -0
- consequa_x/adapters/__init__.py +5 -0
- consequa_x/adapters/ollama.py +350 -0
- consequa_x/attribution.py +407 -0
- consequa_x/benchmark/__init__.py +1 -0
- consequa_x/benchmark/assessment_metrics.py +54 -0
- consequa_x/benchmark/calibration.py +787 -0
- consequa_x/benchmark/calibration_export.py +139 -0
- consequa_x/benchmark/calibration_mechanisms.py +266 -0
- consequa_x/benchmark/decision_coalition.py +254 -0
- consequa_x/benchmark/decision_contrastive.py +448 -0
- consequa_x/benchmark/decision_replay.py +329 -0
- consequa_x/benchmark/decision_shapley.py +628 -0
- consequa_x/benchmark/evaluation.py +187 -0
- consequa_x/benchmark/interaction_export.py +220 -0
- consequa_x/benchmark/interaction_mechanisms.py +192 -0
- consequa_x/benchmark/interval_comparison.py +276 -0
- consequa_x/benchmark/intervals.py +164 -0
- consequa_x/benchmark/payments.py +426 -0
- consequa_x/benchmark/policy_experiment.py +298 -0
- consequa_x/benchmark/policy_runner.py +234 -0
- consequa_x/benchmark/runner.py +241 -0
- consequa_x/benchmark/study.py +183 -0
- consequa_x/benchmark/targets.py +149 -0
- consequa_x/benchmark/templates.py +102 -0
- consequa_x/cli.py +1191 -0
- consequa_x/explanation.py +130 -0
- consequa_x/graph.py +142 -0
- consequa_x/models.py +532 -0
- consequa_x/policies.py +248 -0
- consequa_x/py.typed +0 -0
- consequa_x/schema.py +159 -0
- consequa_x/schemas/trace-v0.1.json +543 -0
- consequa_x/verification.py +192 -0
- consequa_x-0.1.0a2.dist-info/METADATA +303 -0
- consequa_x-0.1.0a2.dist-info/RECORD +41 -0
- consequa_x-0.1.0a2.dist-info/WHEEL +5 -0
- consequa_x-0.1.0a2.dist-info/entry_points.txt +2 -0
- consequa_x-0.1.0a2.dist-info/licenses/LICENSE +21 -0
- consequa_x-0.1.0a2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""Portable, deterministic evidence exports for the offline calibration probe."""
|
|
2
|
+
|
|
3
|
+
from contextlib import ExitStack
|
|
4
|
+
import gzip
|
|
5
|
+
from hashlib import sha256
|
|
6
|
+
import json
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from .. import __version__
|
|
10
|
+
from ..models import to_dict
|
|
11
|
+
from ..schema import get_trace_schema
|
|
12
|
+
from .calibration import (
|
|
13
|
+
calibration_protocol, compact_calibration_trial, run_calibration,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _json(value):
|
|
18
|
+
return json.dumps(to_dict(value), sort_keys=True, allow_nan=False, separators=(",", ":"))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _write_json(path, value):
|
|
22
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
23
|
+
path.write_text(json.dumps(to_dict(value), indent=2, sort_keys=True, allow_nan=False) + "\n",
|
|
24
|
+
encoding="utf-8", newline="\n")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def export_calibration(plan, output, *, source_revision=None, on_trial=None):
|
|
28
|
+
"""Save the frozen protocol first; retain every compact trial and replicate 0.
|
|
29
|
+
|
|
30
|
+
An existing nonempty directory is rejected. Partial exports survive an
|
|
31
|
+
execution or export error and lack the final completion manifest.
|
|
32
|
+
"""
|
|
33
|
+
output = Path(output)
|
|
34
|
+
protocol = calibration_protocol(plan)
|
|
35
|
+
if on_trial is not None and not callable(on_trial):
|
|
36
|
+
raise TypeError("on_trial must be callable or None")
|
|
37
|
+
if source_revision is not None and (not isinstance(source_revision, str)
|
|
38
|
+
or not source_revision.strip()):
|
|
39
|
+
raise ValueError("source_revision must be a nonempty identifier or None")
|
|
40
|
+
if output.exists() and (not output.is_dir() or any(output.iterdir())):
|
|
41
|
+
raise ValueError("calibrate-attribution requires a new or empty output directory")
|
|
42
|
+
output.mkdir(parents=True, exist_ok=True)
|
|
43
|
+
_write_json(output / "protocol.json", {"package_version": __version__, **protocol,
|
|
44
|
+
"source_revision": source_revision,
|
|
45
|
+
"source_revision_trust": "caller supplied; the exporter does not inspect Git",
|
|
46
|
+
"retention": {"all_trials": "trials/<mechanism>.jsonl.gz",
|
|
47
|
+
"full_evidence_replicates": [0],
|
|
48
|
+
"selection": "replicate 0, every attempted branch, including failures",
|
|
49
|
+
"gzip_mtime": 0}})
|
|
50
|
+
_write_json(output / "trace-schema.json", get_trace_schema())
|
|
51
|
+
|
|
52
|
+
def gz_write(path, value):
|
|
53
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
54
|
+
with path.open("wb") as raw:
|
|
55
|
+
with gzip.GzipFile(filename="", fileobj=raw, mode="wb", mtime=0) as stream:
|
|
56
|
+
stream.write((_json(value) + "\n").encode("utf-8"))
|
|
57
|
+
|
|
58
|
+
def evidence(trial):
|
|
59
|
+
public = {"seed_plan": to_dict(trial.seed_plan), "methods": {}}
|
|
60
|
+
gold = {"seed_plan": to_dict(trial.seed_plan), "methods": {}}
|
|
61
|
+
for method, factual, decisions, experiment in (
|
|
62
|
+
("contrastive", trial.factual, trial.factual_decisions, trial.contrastive),
|
|
63
|
+
("shapley", trial.shapley_factual, trial.shapley_factual_decisions, trial.shapley),
|
|
64
|
+
):
|
|
65
|
+
episodes = [("factual", factual, decisions)]
|
|
66
|
+
if experiment is not None:
|
|
67
|
+
for branch in experiment.branches:
|
|
68
|
+
index = branch.index if method == "contrastive" else branch.slot.index
|
|
69
|
+
episodes.append((f"branch_{index}", branch.replay.episode,
|
|
70
|
+
branch.replay.decisions))
|
|
71
|
+
public["methods"][method] = {}
|
|
72
|
+
gold["methods"][method] = {}
|
|
73
|
+
for key, episode, receipts in episodes:
|
|
74
|
+
record = {"decisions": to_dict(receipts)}
|
|
75
|
+
if episode is not None:
|
|
76
|
+
record.update(trace=to_dict(episode.trace),
|
|
77
|
+
explanation=episode.explanation.to_dict())
|
|
78
|
+
gold["methods"][method][key] = {
|
|
79
|
+
"config": to_dict(episode.config), "seed": episode.seed,
|
|
80
|
+
"records": to_dict(episode.ground_truth)}
|
|
81
|
+
public["methods"][method][key] = record
|
|
82
|
+
gz_write(output / "evidence" / f"{trial.seed_plan.mechanism}.json.gz", public)
|
|
83
|
+
gz_write(output / "evaluator_truth" / f"{trial.seed_plan.mechanism}.json.gz", gold)
|
|
84
|
+
|
|
85
|
+
with ExitStack() as stack:
|
|
86
|
+
streams = {}
|
|
87
|
+
for name in plan.mechanisms:
|
|
88
|
+
path = output / "trials" / f"{name}.jsonl.gz"
|
|
89
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
90
|
+
raw = stack.enter_context(path.open("wb"))
|
|
91
|
+
streams[name] = stack.enter_context(gzip.GzipFile(
|
|
92
|
+
filename="", fileobj=raw, mode="wb", mtime=0))
|
|
93
|
+
|
|
94
|
+
def retain(trial):
|
|
95
|
+
record = compact_calibration_trial(trial).to_dict()
|
|
96
|
+
streams[trial.seed_plan.mechanism].write((_json(record) + "\n").encode("utf-8"))
|
|
97
|
+
if trial.seed_plan.replicate == 0:
|
|
98
|
+
evidence(trial)
|
|
99
|
+
if on_trial is not None:
|
|
100
|
+
on_trial(trial)
|
|
101
|
+
if trial.seed_plan.replicate == plan.repeats - 1:
|
|
102
|
+
print(f"Completed {trial.seed_plan.mechanism}: {plan.repeats} planned trials",
|
|
103
|
+
flush=True)
|
|
104
|
+
|
|
105
|
+
result = run_calibration(plan, on_trial=retain)
|
|
106
|
+
|
|
107
|
+
summary = result.summary()
|
|
108
|
+
_write_json(output / "summary.json", {"package_version": __version__, **summary})
|
|
109
|
+
report = (
|
|
110
|
+
"# Synthetic attribution calibration probe\n\n"
|
|
111
|
+
f"{len(result.trials)} predeclared trials across {len(plan.mechanisms)} mechanisms; "
|
|
112
|
+
f"{plan.repeats} repetitions each. No language model was called.\n\n"
|
|
113
|
+
"`protocol.json` was saved before policy calls. `summary.json` reports "
|
|
114
|
+
"availability, reference eligibility, error and interval coverage with explicit "
|
|
115
|
+
"denominators. All planned draws and refusal receipts are in compressed JSONL "
|
|
116
|
+
"files under `trials/`. Full public traces, explanations and decision receipts "
|
|
117
|
+
"for replicate 0 are in `evidence/`; its gold records stay separately in "
|
|
118
|
+
"`evaluator_truth/`. Python's standard `gzip` module can read these files.\n\n"
|
|
119
|
+
"The policy has a declared fixed factual timeout intervention and stochastic "
|
|
120
|
+
"fresh continuations. Analytic references describe its ideal Bernoulli law, "
|
|
121
|
+
"not the observed replay sample. The three-position retry game has undefined "
|
|
122
|
+
"population Shapley values when some held positions are unreachable. Available "
|
|
123
|
+
"finite estimates there are not scored against fabricated truth.\n\n"
|
|
124
|
+
"Missing provenance, stale readback and the three-call budget control are "
|
|
125
|
+
"intentional refusal strata. Refusals remain in planned denominators. "
|
|
126
|
+
"State and linked targets reuse outcomes and are not independent replications. "
|
|
127
|
+
"This exploratory synthetic probe does not establish calibrated coverage, "
|
|
128
|
+
"model accuracy, interaction recovery or method superiority.\n\n"
|
|
129
|
+
"The file manifest detects changed bytes, not authenticity. The detailed "
|
|
130
|
+
"design and metric definitions are in `docs/attribution-calibration.md`.\n"
|
|
131
|
+
)
|
|
132
|
+
(output / "README.md").write_text(report, encoding="utf-8", newline="\n")
|
|
133
|
+
artifacts = [{"path": path.relative_to(output).as_posix(),
|
|
134
|
+
"sha256": sha256(path.read_bytes()).hexdigest(), "bytes": path.stat().st_size}
|
|
135
|
+
for path in sorted(output.rglob("*")) if path.is_file()]
|
|
136
|
+
_write_json(output / "manifest.json", {"package_version": __version__,
|
|
137
|
+
"status": "completed", "artifacts": artifacts})
|
|
138
|
+
print(f"Saved calibration probe in {output.resolve()}")
|
|
139
|
+
return result
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"""Predeclared synthetic refund mechanisms with analytic population references.
|
|
2
|
+
|
|
3
|
+
The factual timeout choice is a benchmark intervention at one declared phase
|
|
4
|
+
key, not a naturally sampled factual trajectory. Every other timeout choice
|
|
5
|
+
uses a seeded Bernoulli retry law. Policies read public requests only; the
|
|
6
|
+
reference below never observes a rollout, its labels, or its sampled seeds.
|
|
7
|
+
All effects still come from the ordinary isolated ``PaymentWorld`` runner.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
import math
|
|
14
|
+
import random
|
|
15
|
+
from types import MappingProxyType
|
|
16
|
+
from typing import Literal
|
|
17
|
+
|
|
18
|
+
from ..models import AssessmentStatus, ResponseStatus
|
|
19
|
+
from ..policies import PolicyDecision, PolicyError, PolicyReply, PolicyRequest, validate_seed
|
|
20
|
+
from .payments import Fault
|
|
21
|
+
from .policy_runner import MAX_TRIAL_SEED, run_policy_scenario
|
|
22
|
+
from .runner import Episode, RefundContext, ScenarioConfig
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
TARGETS = ("action", "consequence", "action_linked_consequence")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True)
|
|
29
|
+
class SeededRetryCalibrationPolicy:
|
|
30
|
+
"""Transparent benchmark policy, including its reserved factual phase key.
|
|
31
|
+
|
|
32
|
+
Selection always refunds and the final response always stops. A timeout
|
|
33
|
+
with attempts remaining retries with probability ``retry_probability``
|
|
34
|
+
except at the exact reserved key, where the predeclared factual operation
|
|
35
|
+
is forced. The same policy identity must be used for factual and replay.
|
|
36
|
+
This conditioning is deliberately not a model or population factual draw.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
retry_probability: float
|
|
40
|
+
factual_operation: Literal["stop", "retry"] = "stop"
|
|
41
|
+
factual_policy_seed: int = 0
|
|
42
|
+
|
|
43
|
+
def __post_init__(self) -> None:
|
|
44
|
+
_probability(self.retry_probability)
|
|
45
|
+
_operation(self.factual_operation)
|
|
46
|
+
_trial_seed(self.factual_policy_seed)
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def metadata(self) -> dict:
|
|
50
|
+
return {
|
|
51
|
+
"provider": "synthetic",
|
|
52
|
+
"policy": "conditioned-bernoulli-refund-calibration",
|
|
53
|
+
"version": "1",
|
|
54
|
+
"retry_probability": self.retry_probability,
|
|
55
|
+
"selection": "always_refund",
|
|
56
|
+
"conditioning": {
|
|
57
|
+
"kind": "predeclared_factual_timeout_intervention",
|
|
58
|
+
"naturally_sampled_factual": False,
|
|
59
|
+
"reserved_timeout_model_seed": self.factual_policy_seed * 6 + 1,
|
|
60
|
+
"operation": self.factual_operation,
|
|
61
|
+
},
|
|
62
|
+
"continuation_rng": "python_random.Random(model_phase_seed)",
|
|
63
|
+
"hidden_evidence_inputs": False,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
def decide(self, request: PolicyRequest, *, seed: int) -> PolicyReply:
|
|
67
|
+
if not isinstance(request, PolicyRequest):
|
|
68
|
+
raise PolicyError("Expected a PolicyRequest")
|
|
69
|
+
validate_seed(seed)
|
|
70
|
+
# Only the public stage, remaining attempts and response status are used.
|
|
71
|
+
# No task fault, independent readback, or evaluator truth is supplied.
|
|
72
|
+
if request.stage == "select":
|
|
73
|
+
decision = PolicyDecision("refund", rationale="The fixture always selects the refund.")
|
|
74
|
+
source = "fixed_selection"
|
|
75
|
+
elif request.responses[-1].status is ResponseStatus.TIMEOUT and request.attempts_remaining:
|
|
76
|
+
forced = seed == self.factual_policy_seed * 6 + 1
|
|
77
|
+
retry = self.factual_operation == "retry" if forced else (
|
|
78
|
+
random.Random(seed).random() < self.retry_probability
|
|
79
|
+
)
|
|
80
|
+
decision = PolicyDecision("retry" if retry else "stop", AssessmentStatus.UNKNOWN,
|
|
81
|
+
"The fixture applies its declared timeout retry rule.")
|
|
82
|
+
source = "reserved_factual_intervention" if forced else "seeded_bernoulli_retry"
|
|
83
|
+
else:
|
|
84
|
+
decision = PolicyDecision("stop", AssessmentStatus.UNKNOWN,
|
|
85
|
+
"The fixture stops after the final or non-timeout response.")
|
|
86
|
+
source = "fixed_final_stop"
|
|
87
|
+
return PolicyReply(decision, {"seed": seed, "decision_source": source})
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _probability(value: float) -> None:
|
|
91
|
+
if type(value) not in (int, float) or not math.isfinite(value) or not 0 <= value <= 1:
|
|
92
|
+
raise ValueError("retry_probability must be a finite probability in 0..1")
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _operation(value: str) -> None:
|
|
96
|
+
if type(value) is not str or value not in {"stop", "retry"}:
|
|
97
|
+
raise ValueError("factual_operation must be stop or retry")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _trial_seed(value: int) -> None:
|
|
101
|
+
if type(value) is not int or not 0 <= value <= MAX_TRIAL_SEED:
|
|
102
|
+
raise ValueError(f"factual_policy_seed must be an integer in 0..{MAX_TRIAL_SEED}")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclass(frozen=True)
|
|
106
|
+
class CalibrationMechanism:
|
|
107
|
+
"""One world configuration and its independently specified retry law."""
|
|
108
|
+
|
|
109
|
+
name: str
|
|
110
|
+
retry_probability: float
|
|
111
|
+
factual_operation: Literal["stop", "retry"] = "stop"
|
|
112
|
+
use_idempotency: bool = False
|
|
113
|
+
include_provenance: bool = True
|
|
114
|
+
read_lag_ms: int = 0
|
|
115
|
+
|
|
116
|
+
def __post_init__(self) -> None:
|
|
117
|
+
if type(self.name) is not str or not self.name.strip():
|
|
118
|
+
raise ValueError("name must be a nonempty string")
|
|
119
|
+
_probability(self.retry_probability)
|
|
120
|
+
_operation(self.factual_operation)
|
|
121
|
+
if type(self.use_idempotency) is not bool or type(self.include_provenance) is not bool:
|
|
122
|
+
raise ValueError("use_idempotency and include_provenance must be booleans")
|
|
123
|
+
if type(self.read_lag_ms) is not int or self.read_lag_ms < 0:
|
|
124
|
+
raise ValueError("read_lag_ms must be a nonnegative integer")
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def decision_count(self) -> int:
|
|
128
|
+
return 2 if self.factual_operation == "stop" else 3
|
|
129
|
+
|
|
130
|
+
def make_policy(self, *, factual_policy_seed: int = 0) -> SeededRetryCalibrationPolicy:
|
|
131
|
+
return SeededRetryCalibrationPolicy(
|
|
132
|
+
self.retry_probability, self.factual_operation, factual_policy_seed,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
def scenario_config(self) -> ScenarioConfig:
|
|
136
|
+
return ScenarioConfig(
|
|
137
|
+
name=self.name, fault=Fault.TIMEOUT_AFTER_COMMIT,
|
|
138
|
+
context=RefundContext(retry_instruction=True),
|
|
139
|
+
use_idempotency=self.use_idempotency,
|
|
140
|
+
include_provenance=self.include_provenance, read_lag_ms=self.read_lag_ms,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
def capture_factual(self, *, seed: int = 0, policy_seed: int = 0) -> Episode:
|
|
144
|
+
"""Convenience capture; budgeted callers should use the ordinary runner."""
|
|
145
|
+
return run_policy_scenario(
|
|
146
|
+
self.scenario_config(), self.make_policy(factual_policy_seed=policy_seed),
|
|
147
|
+
seed=seed, policy_seed=policy_seed, max_attempts=2,
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
def analytic_reference(self) -> dict:
|
|
151
|
+
"""Describe the ideal Bernoulli game without running or reading episodes.
|
|
152
|
+
|
|
153
|
+
Coalition arrays use integer bit masks: bit ``i`` holds decision ``i``.
|
|
154
|
+
An undefined coalition retains ``None`` rather than a conditional mean
|
|
155
|
+
among valid rollouts. Retry factuals can stop before their held final
|
|
156
|
+
decision, so they have no complete Shapley population game for q < 1.
|
|
157
|
+
Endpoint truth remains available when both complete endpoints are known.
|
|
158
|
+
"""
|
|
159
|
+
n, q = self.decision_count, self.retry_probability
|
|
160
|
+
retry_factual = self.factual_operation == "retry"
|
|
161
|
+
valid = [q if n == 3 and mask & 4 and not mask & 2 else 1.
|
|
162
|
+
for mask in range(1 << n)]
|
|
163
|
+
targets = {}
|
|
164
|
+
for target in TARGETS:
|
|
165
|
+
evidence_unknown = target != "action" and (
|
|
166
|
+
bool(self.read_lag_ms)
|
|
167
|
+
or (target == "action_linked_consequence" and not self.include_provenance)
|
|
168
|
+
)
|
|
169
|
+
# Without provenance, a duplicate still violates the state contract
|
|
170
|
+
# and has a known false linked target. The protective factual and
|
|
171
|
+
# held-stop coalitions nevertheless remain unknown.
|
|
172
|
+
factual_known_violation = retry_factual and not self.use_idempotency
|
|
173
|
+
factual_bad = (False if target == "action" else (
|
|
174
|
+
None if self.read_lag_ms else (
|
|
175
|
+
True if factual_known_violation else (None if evidence_unknown else False)
|
|
176
|
+
)
|
|
177
|
+
))
|
|
178
|
+
numeric = not evidence_unknown
|
|
179
|
+
if target == "action":
|
|
180
|
+
changed = [0.] * n
|
|
181
|
+
coalition = [0. if probability == 1 else None for probability in valid]
|
|
182
|
+
elif numeric:
|
|
183
|
+
fresh_bad = 0. if self.use_idempotency else float(q)
|
|
184
|
+
changed = [fresh_bad, fresh_bad] + ([float(bool(factual_bad))] if n == 3 else [])
|
|
185
|
+
coalition = [
|
|
186
|
+
None if valid[mask] < 1 else (
|
|
187
|
+
float(bool(factual_bad)) if mask & 2 else fresh_bad
|
|
188
|
+
)
|
|
189
|
+
for mask in range(1 << n)
|
|
190
|
+
]
|
|
191
|
+
else:
|
|
192
|
+
# With missing provenance, a fresh draw that necessarily
|
|
193
|
+
# duplicates is still a known violation. All other fresh laws
|
|
194
|
+
# have positive unknown probability and no binary mean.
|
|
195
|
+
fresh_bad = (1. if not self.read_lag_ms and not self.use_idempotency and q == 1
|
|
196
|
+
else None)
|
|
197
|
+
changed = [fresh_bad, fresh_bad] + (
|
|
198
|
+
[float(factual_bad) if factual_bad is not None else None] if n == 3 else []
|
|
199
|
+
)
|
|
200
|
+
coalition = [
|
|
201
|
+
None if valid[mask] < 1 else (
|
|
202
|
+
float(factual_bad) if factual_bad is not None else None
|
|
203
|
+
) if mask & 2 else fresh_bad for mask in range(1 << n)
|
|
204
|
+
]
|
|
205
|
+
effects = [None if rate is None or factual_bad is None else rate - int(factual_bad)
|
|
206
|
+
for rate in changed]
|
|
207
|
+
contrastive_eligible = factual_bad is not None and all(value is not None for value in effects)
|
|
208
|
+
shapley_eligible = all(value is not None for value in coalition)
|
|
209
|
+
endpoint_eligible = coalition[0] is not None and coalition[-1] is not None
|
|
210
|
+
if shapley_eligible:
|
|
211
|
+
# The initial selection is always the same operation; holding
|
|
212
|
+
# the timeout choice alone changes the population retry rate.
|
|
213
|
+
shares = [0., coalition[2] - coalition[0]] + ([0.] if n == 3 else [])
|
|
214
|
+
else:
|
|
215
|
+
shares = None
|
|
216
|
+
latest = max((i for i, effect in enumerate(effects) if effect is not None and effect < 0),
|
|
217
|
+
default=None) if contrastive_eligible else None
|
|
218
|
+
unknown_reason = "independent outcome evidence is unknown"
|
|
219
|
+
invalid_reason = "a fresh timeout stop can make held terminal decision 2 unreachable"
|
|
220
|
+
targets[target] = {
|
|
221
|
+
"factual_bad": factual_bad,
|
|
222
|
+
"changed_bad_rates": changed,
|
|
223
|
+
"contrastive_effects": effects,
|
|
224
|
+
"latest_population_rescue": latest,
|
|
225
|
+
"coalition_bad_rates": coalition,
|
|
226
|
+
"contrastive_eligible": contrastive_eligible,
|
|
227
|
+
"shapley_eligible": shapley_eligible,
|
|
228
|
+
"endpoint_eligible": endpoint_eligible,
|
|
229
|
+
"shapley_values": shares,
|
|
230
|
+
"endpoint_difference": coalition[-1] - coalition[0] if endpoint_eligible else None,
|
|
231
|
+
"contrastive_null_positions": [i for i, effect in enumerate(effects) if effect == 0],
|
|
232
|
+
"shapley_null_positions": [i for i, share in enumerate(shares or ()) if share == 0],
|
|
233
|
+
"reasons": {
|
|
234
|
+
"contrastive": None if contrastive_eligible else unknown_reason,
|
|
235
|
+
"shapley": None if shapley_eligible else (
|
|
236
|
+
invalid_reason if any(probability < 1 for probability in valid) else unknown_reason
|
|
237
|
+
),
|
|
238
|
+
"endpoint": None if endpoint_eligible else unknown_reason,
|
|
239
|
+
},
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
"name": self.name, "decision_count": n,
|
|
243
|
+
"retry_probability": q,
|
|
244
|
+
"conditioning": "fixed factual timeout intervention; ideal Bernoulli continuation law",
|
|
245
|
+
"population_not_finite_seed_claim": True,
|
|
246
|
+
"coalition_valid_probabilities": valid,
|
|
247
|
+
"targets": targets,
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
CALIBRATION_MECHANISMS = MappingProxyType({
|
|
252
|
+
**{f"protective-stop-q{int(q * 100)}": CalibrationMechanism(f"protective-stop-q{int(q * 100)}", q)
|
|
253
|
+
for q in (.1, .5, .9)},
|
|
254
|
+
**{f"retry-pivot-q{int(q * 100)}": CalibrationMechanism(f"retry-pivot-q{int(q * 100)}", q, "retry")
|
|
255
|
+
for q in (.1, .5, .9)},
|
|
256
|
+
"idempotent-stop-q50": CalibrationMechanism("idempotent-stop-q50", .5, use_idempotency=True),
|
|
257
|
+
"unknown-provenance-stop-q50": CalibrationMechanism("unknown-provenance-stop-q50", .5,
|
|
258
|
+
include_provenance=False),
|
|
259
|
+
"lagged-readback-stop-q50": CalibrationMechanism("lagged-readback-stop-q50", .5, read_lag_ms=1000),
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def get_calibration_mechanism(name: str) -> CalibrationMechanism:
|
|
264
|
+
if type(name) is not str or name not in CALIBRATION_MECHANISMS:
|
|
265
|
+
raise ValueError(f"Unknown calibration mechanism: {name!r}")
|
|
266
|
+
return CALIBRATION_MECHANISMS[name]
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
"""Replay positional coalitions of captured decisions in isolated worlds.
|
|
2
|
+
|
|
3
|
+
A held position forces its captured decision at the same typed policy stage.
|
|
4
|
+
Earlier fresh decisions may change its public request, so this intervention does
|
|
5
|
+
not require the exact request equality used by decision-prefix replay. An absent
|
|
6
|
+
held position invalidates a branch by default. The separate on-reach game
|
|
7
|
+
records normally terminated later held positions as inactive.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from copy import deepcopy
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
from typing import Iterable, Literal
|
|
15
|
+
|
|
16
|
+
from ..models import to_dict
|
|
17
|
+
from ..policies import PolicyError, PolicyReply, PolicyRequest, validate_reply
|
|
18
|
+
from .decision_replay import (
|
|
19
|
+
DecisionReplayReceipt, _Capture, _json_key, _metadata, _validate_factual,
|
|
20
|
+
)
|
|
21
|
+
from .evaluation import BudgetedPolicy, CallBudgetExceeded
|
|
22
|
+
from .policy_runner import MAX_TRIAL_SEED, run_policy_scenario
|
|
23
|
+
from .runner import Episode
|
|
24
|
+
from .templates import DEFAULT_REFUND_TEMPLATE, RefundTaskTemplate
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
COALITION_GAMES = ("strict", "on_reach")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def validate_coalition_game(game):
|
|
31
|
+
if type(game) is not str or game not in COALITION_GAMES:
|
|
32
|
+
raise ValueError("coalition_game must be strict or on_reach")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class DecisionCoalitionIncompatible(PolicyError):
|
|
36
|
+
"""A forced factual decision is incompatible with its reached boundary."""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class DecisionCoalitionUnreachable(PolicyError):
|
|
40
|
+
"""A completed rollout stopped before a required held position."""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass(frozen=True)
|
|
44
|
+
class DecisionCoalitionReplay:
|
|
45
|
+
"""One coalition branch, including partial receipts after failure."""
|
|
46
|
+
|
|
47
|
+
held_indices: tuple[int, ...]
|
|
48
|
+
continuation_seed: int
|
|
49
|
+
factual_seed: int
|
|
50
|
+
factual_decision_count: int
|
|
51
|
+
policy_metadata: dict
|
|
52
|
+
decisions: tuple[DecisionReplayReceipt, ...]
|
|
53
|
+
policy_calls: int
|
|
54
|
+
episode: Episode | None = None
|
|
55
|
+
error_type: str | None = None
|
|
56
|
+
error: str | None = None
|
|
57
|
+
budget_exhausted: bool = False
|
|
58
|
+
coalition_game: str = "strict"
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def status(self) -> Literal["completed", "invalid", "failed"]:
|
|
62
|
+
if self.episode is not None:
|
|
63
|
+
return "completed"
|
|
64
|
+
if self.error_type in {
|
|
65
|
+
"DecisionCoalitionIncompatible", "DecisionCoalitionUnreachable",
|
|
66
|
+
}:
|
|
67
|
+
return "invalid"
|
|
68
|
+
return "failed"
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def unreached_factual_indices(self) -> tuple[int, ...]:
|
|
72
|
+
return tuple(range(min(len(self.decisions), self.factual_decision_count),
|
|
73
|
+
self.factual_decision_count))
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def unreached_held_indices(self) -> tuple[int, ...]:
|
|
77
|
+
return tuple(index for index in self.held_indices if index >= len(self.decisions))
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def inactive_held_indices(self) -> tuple[int, ...]:
|
|
81
|
+
"""Only normal termination in the declared on-reach game is inactivity."""
|
|
82
|
+
return (self.unreached_held_indices
|
|
83
|
+
if self.coalition_game == "on_reach" and self.episode is not None else ())
|
|
84
|
+
|
|
85
|
+
def summary(self) -> dict:
|
|
86
|
+
return {
|
|
87
|
+
"status": self.status,
|
|
88
|
+
"coalition_game": self.coalition_game,
|
|
89
|
+
"inactive_held_indices": list(self.inactive_held_indices),
|
|
90
|
+
"held_indices": list(self.held_indices),
|
|
91
|
+
"continuation_seed": self.continuation_seed,
|
|
92
|
+
"factual_seed": self.factual_seed,
|
|
93
|
+
"factual_decision_count": self.factual_decision_count,
|
|
94
|
+
"reached_decisions": len(self.decisions),
|
|
95
|
+
"held_decisions": sum(item.source == "held_factual" and item.reply is not None
|
|
96
|
+
for item in self.decisions),
|
|
97
|
+
"resampled_decisions": sum(item.source == "resampled" and item.reply is not None
|
|
98
|
+
for item in self.decisions),
|
|
99
|
+
"held_requests": sum(item.source == "held_factual" for item in self.decisions),
|
|
100
|
+
"resampled_requests": sum(item.source == "resampled" for item in self.decisions),
|
|
101
|
+
"failed_decisions": sum(item.error_type is not None for item in self.decisions),
|
|
102
|
+
"unreached_factual_indices": list(self.unreached_factual_indices),
|
|
103
|
+
"unreached_held_indices": list(self.unreached_held_indices),
|
|
104
|
+
"policy_calls": self.policy_calls,
|
|
105
|
+
"budget_exhausted": self.budget_exhausted,
|
|
106
|
+
"error_type": self.error_type,
|
|
107
|
+
"error": self.error,
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class _CoalitionPolicy:
|
|
112
|
+
def __init__(self, captures: tuple[_Capture, ...], policy: BudgetedPolicy,
|
|
113
|
+
held_indices: tuple[int, ...]):
|
|
114
|
+
self.captures = captures
|
|
115
|
+
self.policy = policy
|
|
116
|
+
self.held_indices = frozenset(held_indices)
|
|
117
|
+
self.receipts: list[DecisionReplayReceipt] = []
|
|
118
|
+
self._metadata = deepcopy(captures[0].policy_metadata)
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def metadata(self):
|
|
122
|
+
return deepcopy(self._metadata)
|
|
123
|
+
|
|
124
|
+
def decide(self, request: PolicyRequest, *, seed: int) -> PolicyReply:
|
|
125
|
+
index = len(self.receipts)
|
|
126
|
+
capture = self.captures[index] if index < len(self.captures) else None
|
|
127
|
+
source = "held_factual" if index in self.held_indices else "resampled"
|
|
128
|
+
before = self.policy.calls
|
|
129
|
+
request_data = to_dict(request)
|
|
130
|
+
reply = None
|
|
131
|
+
error = None
|
|
132
|
+
try:
|
|
133
|
+
if source == "held_factual":
|
|
134
|
+
if request.stage != capture.request.stage:
|
|
135
|
+
raise DecisionCoalitionIncompatible(
|
|
136
|
+
"Held decision reached a different typed policy stage",
|
|
137
|
+
)
|
|
138
|
+
reply = PolicyReply(deepcopy(capture.reply.decision), {
|
|
139
|
+
"decision_coalition_replay": {
|
|
140
|
+
"source": "forced_factual", "provider_call": False,
|
|
141
|
+
"factual_index": index,
|
|
142
|
+
"factual_event_id": capture.event_id,
|
|
143
|
+
"factual_model_seed": capture.model_seed,
|
|
144
|
+
"requested_continuation_model_seed": seed,
|
|
145
|
+
"exact_request_match": _json_key(request) == _json_key(capture.request),
|
|
146
|
+
"factual_reply_metadata": deepcopy(capture.reply.metadata),
|
|
147
|
+
},
|
|
148
|
+
})
|
|
149
|
+
try:
|
|
150
|
+
validate_reply(request, reply)
|
|
151
|
+
except (PolicyError, TypeError, ValueError) as caught:
|
|
152
|
+
raise DecisionCoalitionIncompatible(
|
|
153
|
+
f"Held decision is invalid for the current public request: {caught}",
|
|
154
|
+
) from caught
|
|
155
|
+
else:
|
|
156
|
+
if _json_key(_metadata(self.policy)) != _json_key(self._metadata):
|
|
157
|
+
raise PolicyError("Continuation policy metadata differs from factual policy")
|
|
158
|
+
reply = self.policy.decide(deepcopy(request), seed=seed)
|
|
159
|
+
if _json_key(_metadata(self.policy)) != _json_key(self._metadata):
|
|
160
|
+
raise PolicyError("Continuation policy metadata changed during its decision")
|
|
161
|
+
validate_reply(request, reply)
|
|
162
|
+
reply = deepcopy(reply)
|
|
163
|
+
return reply
|
|
164
|
+
except (PolicyError, TypeError, ValueError) as caught:
|
|
165
|
+
error = caught
|
|
166
|
+
raise
|
|
167
|
+
finally:
|
|
168
|
+
self.receipts.append(DecisionReplayReceipt(
|
|
169
|
+
index, source, request.stage, seed,
|
|
170
|
+
capture.event_id if capture else None, capture.model_seed if capture else None,
|
|
171
|
+
self.policy.calls - before, request_data,
|
|
172
|
+
to_dict(reply) if reply is not None and error is None else None,
|
|
173
|
+
type(error).__name__ if error else None, str(error) if error else None,
|
|
174
|
+
))
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def replay_decision_coalition(
|
|
178
|
+
factual: Episode,
|
|
179
|
+
continuation_policy: BudgetedPolicy,
|
|
180
|
+
*,
|
|
181
|
+
held_indices: Iterable[int],
|
|
182
|
+
continuation_seed: int,
|
|
183
|
+
template: RefundTaskTemplate = DEFAULT_REFUND_TEMPLATE,
|
|
184
|
+
coalition_game: str = "strict",
|
|
185
|
+
) -> DecisionCoalitionReplay:
|
|
186
|
+
"""Reset the factual world and force the requested positional decisions.
|
|
187
|
+
|
|
188
|
+
Full factual captures and declared policy identity are validated without
|
|
189
|
+
provider calls before running a branch. Held decisions consume zero calls;
|
|
190
|
+
fresh decisions use a shared actual-call budget. New reached positions beyond
|
|
191
|
+
the factual trace are fresh. A branch may stop before unheld positions, but
|
|
192
|
+
stopping before a held position invalidates a strict-game branch. In the
|
|
193
|
+
explicit on-reach game, normally unreached held positions are inactive;
|
|
194
|
+
the completed episode and its verified outcome remain the actual rollout.
|
|
195
|
+
A reached but incompatible held decision is invalid under either game.
|
|
196
|
+
|
|
197
|
+
Forced decisions are positional interventions, including their captured
|
|
198
|
+
assessment and reported rationale. They need not be decisions the policy
|
|
199
|
+
would choose under a changed request. Same-stage and current-reply validity
|
|
200
|
+
checks keep this distinction explicit. Seeds do not establish model
|
|
201
|
+
independence or hidden-state identity.
|
|
202
|
+
"""
|
|
203
|
+
if not isinstance(factual, Episode):
|
|
204
|
+
raise TypeError("factual must be an Episode")
|
|
205
|
+
validate_coalition_game(coalition_game)
|
|
206
|
+
if not isinstance(continuation_policy, BudgetedPolicy):
|
|
207
|
+
raise TypeError("continuation_policy must be a BudgetedPolicy")
|
|
208
|
+
if (type(continuation_policy.max_calls) is not int or continuation_policy.max_calls < 1
|
|
209
|
+
or type(continuation_policy.calls) is not int
|
|
210
|
+
or not 0 <= continuation_policy.calls <= continuation_policy.max_calls):
|
|
211
|
+
raise ValueError("BudgetedPolicy counters must be integers within its valid call limit")
|
|
212
|
+
if type(continuation_seed) is not int or not 0 <= continuation_seed <= MAX_TRIAL_SEED:
|
|
213
|
+
raise ValueError(f"continuation_seed must be an integer in 0..{MAX_TRIAL_SEED}")
|
|
214
|
+
if type(factual.seed) is not int or not 0 <= factual.seed <= MAX_TRIAL_SEED:
|
|
215
|
+
raise ValueError(f"factual seed must be an integer in 0..{MAX_TRIAL_SEED}")
|
|
216
|
+
if not isinstance(template, RefundTaskTemplate):
|
|
217
|
+
raise TypeError("template must be a RefundTaskTemplate")
|
|
218
|
+
try:
|
|
219
|
+
positions = tuple(held_indices)
|
|
220
|
+
except TypeError as caught:
|
|
221
|
+
raise TypeError("held_indices must be an iterable of decision indices") from caught
|
|
222
|
+
if any(type(index) is not int or index < 0 for index in positions):
|
|
223
|
+
raise ValueError("held_indices must contain nonnegative integers")
|
|
224
|
+
if len(set(positions)) != len(positions):
|
|
225
|
+
raise ValueError("held_indices must not contain duplicate positions")
|
|
226
|
+
held = tuple(sorted(positions))
|
|
227
|
+
captures = _validate_factual(factual, template)
|
|
228
|
+
if any(index >= len(captures) for index in held):
|
|
229
|
+
raise ValueError("held_indices exceed the factual captured decision range")
|
|
230
|
+
metadata = _metadata(continuation_policy)
|
|
231
|
+
if _json_key(metadata) != _json_key(captures[0].policy_metadata):
|
|
232
|
+
raise ValueError("Continuation policy metadata differs from factual policy")
|
|
233
|
+
coalition_policy = _CoalitionPolicy(captures, continuation_policy, held)
|
|
234
|
+
before = continuation_policy.calls
|
|
235
|
+
episode = None
|
|
236
|
+
error = None
|
|
237
|
+
try:
|
|
238
|
+
episode = run_policy_scenario(
|
|
239
|
+
factual.config, coalition_policy, seed=factual.seed, policy_seed=continuation_seed,
|
|
240
|
+
max_attempts=captures[0].request.attempts_remaining, template=template,
|
|
241
|
+
)
|
|
242
|
+
unreached = tuple(index for index in held if index >= len(coalition_policy.receipts))
|
|
243
|
+
if unreached and coalition_game == "strict":
|
|
244
|
+
raise DecisionCoalitionUnreachable(f"Held decision positions were not reached: {unreached}")
|
|
245
|
+
except (PolicyError, TypeError, ValueError) as caught:
|
|
246
|
+
error = caught
|
|
247
|
+
episode = None
|
|
248
|
+
return DecisionCoalitionReplay(
|
|
249
|
+
held, continuation_seed, factual.seed, len(captures), deepcopy(metadata),
|
|
250
|
+
tuple(coalition_policy.receipts), continuation_policy.calls - before, episode,
|
|
251
|
+
type(error).__name__ if error else None, str(error) if error else None,
|
|
252
|
+
isinstance(error, CallBudgetExceeded),
|
|
253
|
+
coalition_game,
|
|
254
|
+
)
|