agentloop-profiler 0.1.0__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.
- agentloop/__init__.py +136 -0
- agentloop/ablation_protocol.py +229 -0
- agentloop/ablations.py +564 -0
- agentloop/aggregate_cli.py +88 -0
- agentloop/aggregate_stats.py +175 -0
- agentloop/aggregates.py +532 -0
- agentloop/audit.py +39 -0
- agentloop/autoinstrument.py +145 -0
- agentloop/batch_experiments.py +295 -0
- agentloop/batching.py +95 -0
- agentloop/budget_types.py +335 -0
- agentloop/budgets.py +539 -0
- agentloop/calibration.py +872 -0
- agentloop/checkpoint_state.py +306 -0
- agentloop/checkpoint_store.py +86 -0
- agentloop/ci.py +202 -0
- agentloop/cli.py +1314 -0
- agentloop/client.py +228 -0
- agentloop/completion.py +345 -0
- agentloop/completion_types.py +199 -0
- agentloop/config.py +50 -0
- agentloop/context_controls.py +443 -0
- agentloop/context_experiments.py +337 -0
- agentloop/context_types.py +216 -0
- agentloop/costs.py +667 -0
- agentloop/cursor_validation.py +48 -0
- agentloop/decorators.py +445 -0
- agentloop/demo.py +300 -0
- agentloop/doctor.py +341 -0
- agentloop/drift.py +361 -0
- agentloop/drift_cli.py +147 -0
- agentloop/drift_types.py +110 -0
- agentloop/drift_windows.py +340 -0
- agentloop/entrypoint.py +201 -0
- agentloop/estimates.py +133 -0
- agentloop/events.py +75 -0
- agentloop/experiment_artifacts.py +80 -0
- agentloop/experiment_observations.py +169 -0
- agentloop/experiment_reports.py +545 -0
- agentloop/experiment_types.py +355 -0
- agentloop/experiments.py +392 -0
- agentloop/exporters.py +117 -0
- agentloop/finding_benchmark_types.py +131 -0
- agentloop/finding_benchmarks.py +424 -0
- agentloop/findings.py +419 -0
- agentloop/graph.py +464 -0
- agentloop/harness.py +825 -0
- agentloop/harness_bench/__init__.py +1 -0
- agentloop/harness_bench/cli.py +77 -0
- agentloop/harness_bench/drivers.py +96 -0
- agentloop/harness_bench/offline.py +507 -0
- agentloop/harness_evidence.py +585 -0
- agentloop/harness_usage.py +91 -0
- agentloop/html_report.py +860 -0
- agentloop/integrations/__init__.py +1 -0
- agentloop/integrations/crewai.py +144 -0
- agentloop/integrations/harbor/__init__.py +1 -0
- agentloop/integrations/harbor/atif.py +982 -0
- agentloop/integrations/harbor/atif_validation.py +329 -0
- agentloop/integrations/harbor/cli.py +207 -0
- agentloop/integrations/harbor/manifest.py +86 -0
- agentloop/integrations/harbor/trial_evidence.py +196 -0
- agentloop/integrations/harbor/trials.py +1109 -0
- agentloop/integrations/harbor/verifier.py +118 -0
- agentloop/integrations/langgraph.py +150 -0
- agentloop/integrations/langgraph_harness.py +466 -0
- agentloop/integrations/langgraph_recovery.py +366 -0
- agentloop/integrations/omnigent/__init__.py +1 -0
- agentloop/integrations/omnigent/cli.py +78 -0
- agentloop/integrations/omnigent/evidence.py +93 -0
- agentloop/integrations/omnigent/telemetry.py +422 -0
- agentloop/integrations/openai.py +415 -0
- agentloop/integrations/openai_agents.py +291 -0
- agentloop/integrations/sdk.py +82 -0
- agentloop/integrations/vercel_ai.py +83 -0
- agentloop/interoperability/__init__.py +5 -0
- agentloop/interoperability/artifacts.py +112 -0
- agentloop/interoperability/cli.py +59 -0
- agentloop/interoperability/cohort_evidence.py +175 -0
- agentloop/interoperability/cohorts.py +681 -0
- agentloop/interoperability/contracts.py +427 -0
- agentloop/interoperability/coordination.py +554 -0
- agentloop/interoperability/evidence.py +215 -0
- agentloop/interoperability/otlp_jsonl.py +775 -0
- agentloop/interoperability/otlp_validation.py +496 -0
- agentloop/interoperability/registry.py +561 -0
- agentloop/interoperability/validation.py +333 -0
- agentloop/intervention_service.py +110 -0
- agentloop/intervention_store.py +118 -0
- agentloop/interventions.py +190 -0
- agentloop/issues.py +90 -0
- agentloop/judgment_adapters.py +125 -0
- agentloop/judgment_benchmark_types.py +98 -0
- agentloop/judgment_benchmarks.py +606 -0
- agentloop/judgment_types.py +241 -0
- agentloop/judgment_views.py +38 -0
- agentloop/judgments.py +342 -0
- agentloop/loop_guards.py +274 -0
- agentloop/loop_types.py +66 -0
- agentloop/markdown.py +102 -0
- agentloop/metrics.py +342 -0
- agentloop/migrations.py +303 -0
- agentloop/model_routing.py +470 -0
- agentloop/onboarding.py +315 -0
- agentloop/operations.py +44 -0
- agentloop/optimizer.py +199 -0
- agentloop/otel.py +715 -0
- agentloop/otel_ids.py +81 -0
- agentloop/otel_semantics.py +134 -0
- agentloop/parallelism.py +141 -0
- agentloop/patches.py +603 -0
- agentloop/plan_export.py +126 -0
- agentloop/policy_promotion.py +325 -0
- agentloop/promotion_evidence.py +304 -0
- agentloop/promotion_types.py +209 -0
- agentloop/quality.py +446 -0
- agentloop/ranking.py +529 -0
- agentloop/recovery_evidence.py +96 -0
- agentloop/replay.py +753 -0
- agentloop/retention.py +375 -0
- agentloop/retention_types.py +113 -0
- agentloop/reuse_experiments.py +247 -0
- agentloop/routing_checks.py +116 -0
- agentloop/routing_types.py +241 -0
- agentloop/rules.py +396 -0
- agentloop/runtime.py +286 -0
- agentloop/savings.py +158 -0
- agentloop/scheduling_plan.py +99 -0
- agentloop/scheduling_types.py +169 -0
- agentloop/schema.py +257 -0
- agentloop/semantic_rules.py +200 -0
- agentloop/semantic_waste.py +345 -0
- agentloop/semantic_waste_types.py +88 -0
- agentloop/server.py +494 -0
- agentloop/store.py +1429 -0
- agentloop/structural_experiments.py +431 -0
- agentloop/structural_types.py +113 -0
- agentloop/structured_quality.py +1020 -0
- agentloop/studies.py +629 -0
- agentloop/study_statistics.py +58 -0
- agentloop/substitution_evidence.py +199 -0
- agentloop/substitution_export.py +190 -0
- agentloop/substitution_reports.py +407 -0
- agentloop/substitution_types.py +284 -0
- agentloop/substitutions.py +267 -0
- agentloop/timing.py +142 -0
- agentloop/tokens.py +188 -0
- agentloop/tool_scheduling.py +410 -0
- agentloop/trace_sources.py +31 -0
- agentloop/tracer.py +494 -0
- agentloop/value.py +306 -0
- agentloop/version.py +3 -0
- agentloop/workflow_types.py +264 -0
- agentloop/workflows.py +284 -0
- agentloop_profiler-0.1.0.dist-info/METADATA +513 -0
- agentloop_profiler-0.1.0.dist-info/RECORD +166 -0
- agentloop_profiler-0.1.0.dist-info/WHEEL +5 -0
- agentloop_profiler-0.1.0.dist-info/entry_points.txt +2 -0
- agentloop_profiler-0.1.0.dist-info/licenses/LICENSE +201 -0
- agentloop_profiler-0.1.0.dist-info/licenses/THIRD_PARTY_LICENSES.md +101 -0
- agentloop_profiler-0.1.0.dist-info/top_level.txt +2 -0
- dashboard/__init__.py +5 -0
- dashboard/app.py +922 -0
- dashboard/streamlit_compat.py +63 -0
- dashboard/trace_upload.py +57 -0
- dashboard/value_view.py +105 -0
agentloop/calibration.py
ADDED
|
@@ -0,0 +1,872 @@
|
|
|
1
|
+
"""Offline calibration of preserved prediction/outcome artifacts, never runtime learning."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import math
|
|
7
|
+
import re
|
|
8
|
+
from collections import Counter, defaultdict
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from fractions import Fraction
|
|
11
|
+
from hashlib import sha256
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
from agentloop.harness_evidence import METADATA_KEY, validate_evidence
|
|
15
|
+
from agentloop.interventions import InterventionRecord, canonical_json
|
|
16
|
+
from agentloop.markdown import markdown_code_span, markdown_heading, markdown_table_cell
|
|
17
|
+
from agentloop.study_statistics import task_weighted_summary
|
|
18
|
+
from agentloop.trace_sources import TraceSource
|
|
19
|
+
|
|
20
|
+
SCHEMA_VERSION = "1.0"
|
|
21
|
+
_DIGEST = re.compile(r"[a-f0-9]{64}\Z")
|
|
22
|
+
_LABEL = re.compile(r"[A-Za-z0-9_.:-]{1,96}\Z")
|
|
23
|
+
_CONTEXT_FIELDS = {
|
|
24
|
+
"workload",
|
|
25
|
+
"model",
|
|
26
|
+
"provider",
|
|
27
|
+
"environment",
|
|
28
|
+
"scorer",
|
|
29
|
+
"policy",
|
|
30
|
+
"cost_basis",
|
|
31
|
+
"pricing",
|
|
32
|
+
"quality_gate",
|
|
33
|
+
"intervention",
|
|
34
|
+
}
|
|
35
|
+
_METRICS = {
|
|
36
|
+
"latency": ("estimated_latency_savings_ms", "runtime_ms"),
|
|
37
|
+
"cost": ("estimated_cost_savings_usd", "estimated_cost_usd"),
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class CalibrationValidationError(ValueError):
|
|
42
|
+
"""The calibration declaration is ambiguous or malformed."""
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _fields(value, keys, label):
|
|
46
|
+
if not isinstance(value, dict) or set(value) != set(keys):
|
|
47
|
+
raise CalibrationValidationError(f"{label} must contain exactly: {', '.join(sorted(keys))}")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _text(value, label):
|
|
51
|
+
if not isinstance(value, str) or not value.strip():
|
|
52
|
+
raise CalibrationValidationError(f"{label} must be nonempty text")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _finite(value, *, nonnegative=False):
|
|
56
|
+
try:
|
|
57
|
+
return (
|
|
58
|
+
type(value) in {int, float} and math.isfinite(value) and (not nonnegative or value >= 0)
|
|
59
|
+
)
|
|
60
|
+
except (OverflowError, TypeError):
|
|
61
|
+
return False
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _time(value):
|
|
65
|
+
try:
|
|
66
|
+
result = datetime.fromisoformat(value.replace("Z", "+00:00"))
|
|
67
|
+
if result.utcoffset() is None:
|
|
68
|
+
raise ValueError
|
|
69
|
+
return result
|
|
70
|
+
except (AttributeError, TypeError, ValueError):
|
|
71
|
+
raise CalibrationValidationError("timestamps require ISO-8601 with a timezone") from None
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _digest(value):
|
|
75
|
+
return sha256(canonical_json(value).encode()).hexdigest()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _scalar(value, label):
|
|
79
|
+
if type(value) is int or isinstance(value, str) and value:
|
|
80
|
+
return canonical_json([type(value).__name__, value])
|
|
81
|
+
raise CalibrationValidationError(f"{label} must be a nonempty string or integer")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _validate(manifest):
|
|
85
|
+
_fields(
|
|
86
|
+
manifest,
|
|
87
|
+
{
|
|
88
|
+
"schema_version",
|
|
89
|
+
"name",
|
|
90
|
+
"as_of",
|
|
91
|
+
"valid_until",
|
|
92
|
+
"fit_method",
|
|
93
|
+
"min_fit_tasks",
|
|
94
|
+
"selection_inventory_complete",
|
|
95
|
+
"bootstrap",
|
|
96
|
+
"registrations",
|
|
97
|
+
},
|
|
98
|
+
"calibration manifest",
|
|
99
|
+
)
|
|
100
|
+
if manifest["schema_version"] != SCHEMA_VERSION:
|
|
101
|
+
raise CalibrationValidationError("calibration schema_version must be 1.0")
|
|
102
|
+
_text(manifest["name"], "name")
|
|
103
|
+
_time(manifest["as_of"])
|
|
104
|
+
_time(manifest["valid_until"])
|
|
105
|
+
if manifest["fit_method"] not in ("none", "scale"):
|
|
106
|
+
raise CalibrationValidationError("fit_method must be none or scale")
|
|
107
|
+
if type(manifest["min_fit_tasks"]) is not int or manifest["min_fit_tasks"] < 2:
|
|
108
|
+
raise CalibrationValidationError("min_fit_tasks must be an integer of at least two")
|
|
109
|
+
if type(manifest["selection_inventory_complete"]) is not bool:
|
|
110
|
+
raise CalibrationValidationError("selection_inventory_complete must be a boolean")
|
|
111
|
+
settings = manifest["bootstrap"]
|
|
112
|
+
_fields(settings, {"samples", "seed", "confidence"}, "bootstrap")
|
|
113
|
+
if (
|
|
114
|
+
type(settings["samples"]) is not int
|
|
115
|
+
or not 1 <= settings["samples"] <= 10000
|
|
116
|
+
or type(settings["seed"]) is not int
|
|
117
|
+
or not _finite(settings["confidence"])
|
|
118
|
+
or not 0 < settings["confidence"] < 1
|
|
119
|
+
):
|
|
120
|
+
raise CalibrationValidationError(
|
|
121
|
+
"bootstrap requires 1..10000 samples, integer seed and confidence in (0,1)"
|
|
122
|
+
)
|
|
123
|
+
cases = manifest["registrations"]
|
|
124
|
+
if not isinstance(cases, list):
|
|
125
|
+
raise CalibrationValidationError("registrations must be a list, including when empty")
|
|
126
|
+
ids, tasks, digests = set(), {}, {}
|
|
127
|
+
for case in cases:
|
|
128
|
+
_fields(
|
|
129
|
+
case,
|
|
130
|
+
{
|
|
131
|
+
"case_id",
|
|
132
|
+
"task_id",
|
|
133
|
+
"task_sha256",
|
|
134
|
+
"repetition",
|
|
135
|
+
"split",
|
|
136
|
+
"selection",
|
|
137
|
+
"selection_reason",
|
|
138
|
+
"synthetic",
|
|
139
|
+
"context",
|
|
140
|
+
"prediction",
|
|
141
|
+
"prediction_recorded_at",
|
|
142
|
+
"outcome",
|
|
143
|
+
},
|
|
144
|
+
"registration",
|
|
145
|
+
)
|
|
146
|
+
_text(case["case_id"], "case_id")
|
|
147
|
+
if case["case_id"] in ids:
|
|
148
|
+
raise CalibrationValidationError("case IDs must be unique")
|
|
149
|
+
ids.add(case["case_id"])
|
|
150
|
+
task = _scalar(case["task_id"], "task_id")
|
|
151
|
+
_scalar(case["repetition"], "repetition")
|
|
152
|
+
if case["split"] not in ("fit", "held_out") or case["selection"] not in (
|
|
153
|
+
"selected",
|
|
154
|
+
"rejected",
|
|
155
|
+
"unselected",
|
|
156
|
+
):
|
|
157
|
+
raise CalibrationValidationError("invalid split or selection")
|
|
158
|
+
if not isinstance(case["selection_reason"], str) or not _LABEL.fullmatch(
|
|
159
|
+
case["selection_reason"]
|
|
160
|
+
):
|
|
161
|
+
raise CalibrationValidationError("selection_reason must be a bounded identifier")
|
|
162
|
+
if type(case["synthetic"]) is not bool:
|
|
163
|
+
raise CalibrationValidationError("synthetic must be a boolean")
|
|
164
|
+
if not isinstance(case["task_sha256"], str) or not _DIGEST.fullmatch(case["task_sha256"]):
|
|
165
|
+
raise CalibrationValidationError("task_sha256 must identify the full frozen task")
|
|
166
|
+
context = case["context"]
|
|
167
|
+
_fields(context, _CONTEXT_FIELDS, "context")
|
|
168
|
+
for key in ("workload", "model", "provider", "environment", "scorer"):
|
|
169
|
+
_text(context[key], key)
|
|
170
|
+
_fields(context["intervention"], {"type", "configuration"}, "planned intervention")
|
|
171
|
+
_text(context["intervention"]["type"], "intervention type")
|
|
172
|
+
if not isinstance(context["intervention"]["configuration"], dict):
|
|
173
|
+
raise CalibrationValidationError("planned intervention configuration must be an object")
|
|
174
|
+
if context["cost_basis"] not in ("provider_reported", "calculated", "mixed", "unknown"):
|
|
175
|
+
raise CalibrationValidationError("unsupported cost_basis")
|
|
176
|
+
if context["pricing"] is not None:
|
|
177
|
+
_text(context["pricing"], "pricing")
|
|
178
|
+
gate = context["quality_gate"]
|
|
179
|
+
_fields(gate, {"min_score", "max_regression"}, "quality_gate")
|
|
180
|
+
if any(not _finite(value) or not 0 <= value <= 1 for value in gate.values()):
|
|
181
|
+
raise CalibrationValidationError("quality thresholds must be in [0,1]")
|
|
182
|
+
policy = context["policy"]
|
|
183
|
+
if policy is not None:
|
|
184
|
+
_fields(policy, {"id", "version", "config_hash"}, "policy")
|
|
185
|
+
for key in ("id", "version"):
|
|
186
|
+
_text(policy[key], key)
|
|
187
|
+
if not isinstance(policy["config_hash"], str) or not _DIGEST.fullmatch(
|
|
188
|
+
policy["config_hash"]
|
|
189
|
+
):
|
|
190
|
+
raise CalibrationValidationError("policy config_hash must be a SHA-256 digest")
|
|
191
|
+
identity = (context["workload"], task)
|
|
192
|
+
declaration = (case["split"], case["task_sha256"])
|
|
193
|
+
if identity in tasks and tasks[identity] != declaration:
|
|
194
|
+
raise CalibrationValidationError(
|
|
195
|
+
"a task cannot cross splits or change its input digest"
|
|
196
|
+
)
|
|
197
|
+
tasks[identity] = declaration
|
|
198
|
+
digest = case["task_sha256"]
|
|
199
|
+
if digest in digests and digests[digest] != (case["split"], identity):
|
|
200
|
+
raise CalibrationValidationError(
|
|
201
|
+
"identical task inputs must not cross splits or inflate task IDs"
|
|
202
|
+
)
|
|
203
|
+
digests[digest] = (case["split"], identity)
|
|
204
|
+
if not isinstance(case["prediction"], dict):
|
|
205
|
+
raise CalibrationValidationError(
|
|
206
|
+
"prediction must be the original complete finding snapshot"
|
|
207
|
+
)
|
|
208
|
+
_text(case["prediction"].get("finding_id"), "prediction finding_id")
|
|
209
|
+
if case["prediction_recorded_at"] is not None:
|
|
210
|
+
_time(case["prediction_recorded_at"])
|
|
211
|
+
outcome = case["outcome"]
|
|
212
|
+
if outcome is not None:
|
|
213
|
+
_fields(
|
|
214
|
+
outcome,
|
|
215
|
+
{"record", "baseline_trace", "candidate_trace", "recorded_at", "status"},
|
|
216
|
+
"outcome",
|
|
217
|
+
)
|
|
218
|
+
for key in ("record", "baseline_trace", "candidate_trace"):
|
|
219
|
+
if outcome[key] is not None:
|
|
220
|
+
_text(outcome[key], key)
|
|
221
|
+
_time(outcome["recorded_at"])
|
|
222
|
+
if outcome["status"] not in (
|
|
223
|
+
"completed",
|
|
224
|
+
"failed",
|
|
225
|
+
"cancelled",
|
|
226
|
+
"timed_out",
|
|
227
|
+
"stopped",
|
|
228
|
+
"incomplete",
|
|
229
|
+
):
|
|
230
|
+
raise CalibrationValidationError("unsupported outcome status")
|
|
231
|
+
if case["selection"] != "selected":
|
|
232
|
+
raise CalibrationValidationError("an executed outcome requires selected status")
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def _load(path, kind, sources, cache):
|
|
236
|
+
if path is None:
|
|
237
|
+
return None, f"missing_{kind}"
|
|
238
|
+
path = Path(path).resolve()
|
|
239
|
+
sources.add(path)
|
|
240
|
+
key = (path, kind)
|
|
241
|
+
if key not in cache:
|
|
242
|
+
try:
|
|
243
|
+
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
244
|
+
value = (
|
|
245
|
+
InterventionRecord.from_dict(payload).to_dict()
|
|
246
|
+
if kind == "intervention"
|
|
247
|
+
else TraceSource.from_dict(payload)
|
|
248
|
+
)
|
|
249
|
+
cache[key] = (value, None)
|
|
250
|
+
except FileNotFoundError:
|
|
251
|
+
cache[key] = (None, f"missing_{kind}")
|
|
252
|
+
except (OSError, ValueError, TypeError, KeyError, RecursionError):
|
|
253
|
+
cache[key] = (None, f"invalid_{kind}")
|
|
254
|
+
return cache[key]
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _safe_difference(before, after):
|
|
258
|
+
if not _finite(before) or not _finite(after):
|
|
259
|
+
return None
|
|
260
|
+
result = before - after
|
|
261
|
+
return float(result) if _finite(result) else None
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def _quality(case, record, traces, verified):
|
|
265
|
+
outcome = case["outcome"]
|
|
266
|
+
if case["selection"] != "selected":
|
|
267
|
+
return "not_evaluated"
|
|
268
|
+
if outcome is not None and outcome["status"] != "completed":
|
|
269
|
+
return "rejected"
|
|
270
|
+
if not verified or record is None or outcome is None:
|
|
271
|
+
return "indeterminate"
|
|
272
|
+
success = [trace.metadata.get("success") for trace in traces]
|
|
273
|
+
if any(value is False for value in success):
|
|
274
|
+
return "rejected"
|
|
275
|
+
if any(value is not True for value in success):
|
|
276
|
+
return "indeterminate"
|
|
277
|
+
quality = record["measured"].get("quality")
|
|
278
|
+
if isinstance(quality, dict):
|
|
279
|
+
scores = [quality.get("baseline_score"), quality.get("candidate_score")]
|
|
280
|
+
else:
|
|
281
|
+
scores = [
|
|
282
|
+
record["measured"].get(side, {}).get("quality_score")
|
|
283
|
+
for side in ("baseline", "candidate")
|
|
284
|
+
]
|
|
285
|
+
if any(not _finite(value) or not 0 <= value <= 1 for value in scores):
|
|
286
|
+
return "indeterminate"
|
|
287
|
+
before, after = scores
|
|
288
|
+
gate = case["context"]["quality_gate"]
|
|
289
|
+
if min(scores) < gate["min_score"] or Fraction(str(before)) - Fraction(str(after)) > Fraction(
|
|
290
|
+
str(gate["max_regression"])
|
|
291
|
+
):
|
|
292
|
+
return "rejected"
|
|
293
|
+
return "accepted"
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def _policy_observed(policy, trace):
|
|
297
|
+
if policy is None:
|
|
298
|
+
return True
|
|
299
|
+
try:
|
|
300
|
+
evidence = validate_evidence(trace.metadata.get(METADATA_KEY), trace_id=trace.run_id)
|
|
301
|
+
if evidence["capture_errors"]:
|
|
302
|
+
return False
|
|
303
|
+
snapshot = evidence["policies"].get(policy["config_hash"], {})
|
|
304
|
+
return (
|
|
305
|
+
snapshot.get("policy_id") == policy["id"]
|
|
306
|
+
and snapshot.get("version") == policy["version"]
|
|
307
|
+
and any(
|
|
308
|
+
item["policy_config_hash"] == policy["config_hash"]
|
|
309
|
+
and item["mode"] == "enforce"
|
|
310
|
+
and item["origin"] == "policy"
|
|
311
|
+
for item in evidence["decisions"].values()
|
|
312
|
+
)
|
|
313
|
+
)
|
|
314
|
+
except (ValueError, TypeError, KeyError):
|
|
315
|
+
return False
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def _cost_known(summary, basis):
|
|
319
|
+
return (
|
|
320
|
+
basis in {"provider_reported", "calculated"}
|
|
321
|
+
and summary.get("cost_status") in {"complete", "empty"}
|
|
322
|
+
and summary.get("has_unknown_cost") is False
|
|
323
|
+
and (basis == "provider_reported" or summary.get("token_status") in {"exact", "empty"})
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def _source_cost_basis(traces, summaries, basis):
|
|
328
|
+
"""Verify reported-vs-calculated origin without looking up current prices."""
|
|
329
|
+
for trace, summary in zip(traces, summaries):
|
|
330
|
+
if trace is None:
|
|
331
|
+
return False
|
|
332
|
+
model_events = [event for event in trace.events if event.event_type == "model_call"]
|
|
333
|
+
amounts = [
|
|
334
|
+
event.metadata.get("provider_reported_cost_usd", event.metadata.get("cost_usd"))
|
|
335
|
+
for event in model_events
|
|
336
|
+
]
|
|
337
|
+
if basis == "calculated":
|
|
338
|
+
if any(value is not None for value in amounts):
|
|
339
|
+
return False
|
|
340
|
+
elif basis == "provider_reported":
|
|
341
|
+
if any(not _finite(value, nonnegative=True) for value in amounts):
|
|
342
|
+
return False
|
|
343
|
+
total = sum(amounts)
|
|
344
|
+
if not _finite(total) or round(total, 6) != summary.get("estimated_cost_usd"):
|
|
345
|
+
return False
|
|
346
|
+
else:
|
|
347
|
+
return False
|
|
348
|
+
return len(traces) == 2
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def _case(case, root, manifest, sources, cache):
|
|
352
|
+
issues = []
|
|
353
|
+
prediction = case["prediction"]
|
|
354
|
+
estimate = prediction.get("estimate")
|
|
355
|
+
estimator = (
|
|
356
|
+
{
|
|
357
|
+
key: estimate.get(key)
|
|
358
|
+
for key in ("estimator_id", "estimator_version", "method", "formula", "parameters")
|
|
359
|
+
}
|
|
360
|
+
if isinstance(estimate, dict)
|
|
361
|
+
else None
|
|
362
|
+
)
|
|
363
|
+
if (
|
|
364
|
+
estimator is None
|
|
365
|
+
or any(
|
|
366
|
+
not isinstance(estimator[key], str) or not estimator[key]
|
|
367
|
+
for key in ("estimator_id", "estimator_version", "method", "formula")
|
|
368
|
+
)
|
|
369
|
+
or not isinstance(estimator["parameters"], dict)
|
|
370
|
+
):
|
|
371
|
+
issues.append("missing_estimator_provenance")
|
|
372
|
+
cohort = {"context": case["context"], "estimator": estimator}
|
|
373
|
+
record, traces = None, []
|
|
374
|
+
synthetic = case["synthetic"]
|
|
375
|
+
outcome = case["outcome"]
|
|
376
|
+
if case["selection"] != "selected":
|
|
377
|
+
issues.append("not_selected")
|
|
378
|
+
if case["prediction_recorded_at"] is None:
|
|
379
|
+
issues.append("prediction_time_unknown")
|
|
380
|
+
elif _time(case["prediction_recorded_at"]) > _time(manifest["as_of"]):
|
|
381
|
+
issues.append("prediction_after_as_of")
|
|
382
|
+
if outcome is None:
|
|
383
|
+
issues.append("outcome_not_recorded")
|
|
384
|
+
else:
|
|
385
|
+
observed = _time(outcome["recorded_at"])
|
|
386
|
+
if observed > _time(manifest["as_of"]):
|
|
387
|
+
issues.append("outcome_after_as_of")
|
|
388
|
+
if (
|
|
389
|
+
case["prediction_recorded_at"] is not None
|
|
390
|
+
and _time(case["prediction_recorded_at"]) >= observed
|
|
391
|
+
):
|
|
392
|
+
issues.append("prediction_not_frozen_before_outcome")
|
|
393
|
+
record, error = _load(
|
|
394
|
+
root / outcome["record"] if outcome["record"] is not None else None,
|
|
395
|
+
"intervention",
|
|
396
|
+
sources,
|
|
397
|
+
cache,
|
|
398
|
+
)
|
|
399
|
+
if error is not None:
|
|
400
|
+
issues.append(error)
|
|
401
|
+
for side in ("baseline", "candidate"):
|
|
402
|
+
source, error = _load(
|
|
403
|
+
root / outcome[f"{side}_trace"] if outcome[f"{side}_trace"] is not None else None,
|
|
404
|
+
"trace",
|
|
405
|
+
sources,
|
|
406
|
+
cache,
|
|
407
|
+
)
|
|
408
|
+
if error is not None:
|
|
409
|
+
issues.append(f"{side}_{error}")
|
|
410
|
+
trace = source.trace if source is not None else None
|
|
411
|
+
traces.append(trace)
|
|
412
|
+
if trace is not None:
|
|
413
|
+
synthetic = synthetic or trace.metadata.get("synthetic") is True
|
|
414
|
+
if side == "candidate" and case["prediction_recorded_at"] is not None:
|
|
415
|
+
try:
|
|
416
|
+
if _time(case["prediction_recorded_at"]) >= _time(trace.started_at):
|
|
417
|
+
issues.append("prediction_not_frozen_before_candidate")
|
|
418
|
+
except CalibrationValidationError:
|
|
419
|
+
issues.append("candidate_time_unverified")
|
|
420
|
+
actual_repetition = trace.metadata.get("repetition", trace.metadata.get("seed"))
|
|
421
|
+
if canonical_json(
|
|
422
|
+
[
|
|
423
|
+
trace.metadata.get("task_id"),
|
|
424
|
+
type(trace.metadata.get("task_id")).__name__,
|
|
425
|
+
actual_repetition,
|
|
426
|
+
type(actual_repetition).__name__,
|
|
427
|
+
]
|
|
428
|
+
) != canonical_json(
|
|
429
|
+
[
|
|
430
|
+
case["task_id"],
|
|
431
|
+
type(case["task_id"]).__name__,
|
|
432
|
+
case["repetition"],
|
|
433
|
+
type(case["repetition"]).__name__,
|
|
434
|
+
]
|
|
435
|
+
):
|
|
436
|
+
issues.append(f"{side}_pairing_unverified")
|
|
437
|
+
if record is not None and (
|
|
438
|
+
trace.run_id != record[f"{side}_run_id"]
|
|
439
|
+
or record["trace_fingerprints"][side] not in source.fingerprints
|
|
440
|
+
):
|
|
441
|
+
issues.append(f"{side}_source_mismatch")
|
|
442
|
+
if record is not None:
|
|
443
|
+
synthetic = synthetic or record["metadata"].get("synthetic") is True
|
|
444
|
+
planned = case["context"]["intervention"]
|
|
445
|
+
if record["intervention_type"] != planned["type"] or canonical_json(
|
|
446
|
+
record["configuration"]
|
|
447
|
+
) != canonical_json(planned["configuration"]):
|
|
448
|
+
issues.append("intervention_configuration_mismatch")
|
|
449
|
+
findings = record["predicted"]["findings"]
|
|
450
|
+
if len(findings) != 1:
|
|
451
|
+
issues.append("combined_intervention_unattributed")
|
|
452
|
+
saved = next(
|
|
453
|
+
(item for item in findings if item.get("finding_id") == prediction["finding_id"]),
|
|
454
|
+
None,
|
|
455
|
+
)
|
|
456
|
+
if saved is None or canonical_json(saved) != canonical_json(prediction):
|
|
457
|
+
issues.append("original_prediction_mismatch")
|
|
458
|
+
if (
|
|
459
|
+
len(traces) == 2
|
|
460
|
+
and traces[1] is not None
|
|
461
|
+
and not _policy_observed(case["context"]["policy"], traces[1])
|
|
462
|
+
):
|
|
463
|
+
issues.append("policy_not_observed_enforced")
|
|
464
|
+
verified = not issues
|
|
465
|
+
quality = _quality(case, record, traces, verified)
|
|
466
|
+
if synthetic:
|
|
467
|
+
issues.append("synthetic")
|
|
468
|
+
metrics = {}
|
|
469
|
+
for name, (prediction_key, measured_key) in _METRICS.items():
|
|
470
|
+
reasons = []
|
|
471
|
+
savings = prediction.get("savings")
|
|
472
|
+
predicted = savings.get(prediction_key) if isinstance(savings, dict) else None
|
|
473
|
+
modeled = estimate.get("unmodeled_metrics") if isinstance(estimate, dict) else None
|
|
474
|
+
if not isinstance(modeled, list) or any(not isinstance(item, str) for item in modeled):
|
|
475
|
+
predicted = None
|
|
476
|
+
reasons.append("modeled_metric_unknown")
|
|
477
|
+
elif name in modeled:
|
|
478
|
+
predicted = None
|
|
479
|
+
reasons.append("unmodeled_metric")
|
|
480
|
+
if not _finite(predicted):
|
|
481
|
+
predicted = None
|
|
482
|
+
reasons.append("prediction_unavailable")
|
|
483
|
+
before = record["measured"]["baseline"] if record is not None else {}
|
|
484
|
+
after = record["measured"]["candidate"] if record is not None else {}
|
|
485
|
+
a, b = before.get(measured_key), after.get(measured_key)
|
|
486
|
+
realized = (
|
|
487
|
+
_safe_difference(a, b)
|
|
488
|
+
if _finite(a, nonnegative=True) and _finite(b, nonnegative=True)
|
|
489
|
+
else None
|
|
490
|
+
)
|
|
491
|
+
if name == "latency" and any(trace is None or trace.ended_at is None for trace in traces):
|
|
492
|
+
realized = None
|
|
493
|
+
reasons.append("incomplete_timing")
|
|
494
|
+
if name == "cost":
|
|
495
|
+
basis = case["context"]["cost_basis"]
|
|
496
|
+
inputs = estimate.get("inputs", {}) if isinstance(estimate, dict) else {}
|
|
497
|
+
if (
|
|
498
|
+
not isinstance(inputs, dict)
|
|
499
|
+
or inputs.get("cost_status") not in {"complete", "empty"}
|
|
500
|
+
or basis not in {"provider_reported", "calculated"}
|
|
501
|
+
or (basis == "calculated" and inputs.get("token_status") not in {"exact", "empty"})
|
|
502
|
+
):
|
|
503
|
+
predicted = None
|
|
504
|
+
reasons.append("prediction_cost_provenance_incomplete")
|
|
505
|
+
if not _cost_known(before, basis) or not _cost_known(after, basis):
|
|
506
|
+
realized = None
|
|
507
|
+
reasons.append("outcome_cost_provenance_incomplete")
|
|
508
|
+
if not _source_cost_basis(traces, (before, after), basis):
|
|
509
|
+
realized = None
|
|
510
|
+
reasons.append("source_cost_basis_unverified")
|
|
511
|
+
if basis == "calculated" and case["context"]["pricing"] is None:
|
|
512
|
+
predicted = realized = None
|
|
513
|
+
reasons.append("pricing_reference_missing")
|
|
514
|
+
if realized is None:
|
|
515
|
+
reasons.append("outcome_unavailable")
|
|
516
|
+
error = _safe_difference(predicted, realized)
|
|
517
|
+
ratio, ratio_status = None, "unavailable"
|
|
518
|
+
if predicted is not None and realized is not None:
|
|
519
|
+
if predicted == 0:
|
|
520
|
+
ratio_status = "zero_prediction"
|
|
521
|
+
else:
|
|
522
|
+
try:
|
|
523
|
+
ratio = float(Fraction(str(realized)) / Fraction(str(predicted)))
|
|
524
|
+
ratio_status = "available" if _finite(ratio) else "numeric_range"
|
|
525
|
+
except (OverflowError, ValueError):
|
|
526
|
+
ratio_status = "numeric_range"
|
|
527
|
+
if ratio_status != "available":
|
|
528
|
+
ratio = None
|
|
529
|
+
if error is None:
|
|
530
|
+
reasons.append("error_numeric_range")
|
|
531
|
+
metrics[name] = {
|
|
532
|
+
"predicted_savings": predicted,
|
|
533
|
+
"realized_savings": realized,
|
|
534
|
+
"signed_error": error,
|
|
535
|
+
"realization_ratio": ratio,
|
|
536
|
+
"ratio_status": ratio_status,
|
|
537
|
+
"reasons": sorted(set(reasons)),
|
|
538
|
+
"empirical_eligible": not issues and error is not None,
|
|
539
|
+
}
|
|
540
|
+
return {
|
|
541
|
+
"case_id": case["case_id"],
|
|
542
|
+
"repetition_key": _scalar(case["repetition"], "repetition"),
|
|
543
|
+
"task_key": canonical_json(
|
|
544
|
+
[case["context"]["workload"], _scalar(case["task_id"], "task_id")]
|
|
545
|
+
),
|
|
546
|
+
"split": case["split"],
|
|
547
|
+
"selection": case["selection"],
|
|
548
|
+
"selection_reason": case["selection_reason"],
|
|
549
|
+
"outcome_status": outcome["status"] if outcome is not None else None,
|
|
550
|
+
"cohort_id": "cal_" + _digest(cohort),
|
|
551
|
+
"cohort": cohort,
|
|
552
|
+
"synthetic": synthetic,
|
|
553
|
+
"issues": sorted(set(issues)),
|
|
554
|
+
"quality_gate": quality,
|
|
555
|
+
"metrics": metrics,
|
|
556
|
+
"prediction_snapshot": prediction,
|
|
557
|
+
"intervention": record,
|
|
558
|
+
"source_event_error_count": sum(
|
|
559
|
+
event.status == "error"
|
|
560
|
+
for trace in traces
|
|
561
|
+
if trace is not None
|
|
562
|
+
for event in trace.events
|
|
563
|
+
),
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
def _fit_scale(rows, metric, minimum):
|
|
568
|
+
pairs = defaultdict(list)
|
|
569
|
+
for row in rows:
|
|
570
|
+
value = row["metrics"][metric]
|
|
571
|
+
if row["split"] == "fit" and value["empirical_eligible"]:
|
|
572
|
+
pairs[row["task_key"]].append(
|
|
573
|
+
(
|
|
574
|
+
Fraction(str(value["predicted_savings"])),
|
|
575
|
+
Fraction(str(value["realized_savings"])),
|
|
576
|
+
)
|
|
577
|
+
)
|
|
578
|
+
task_means = [
|
|
579
|
+
(sum(pair[0] for pair in group) / len(group), sum(pair[1] for pair in group) / len(group))
|
|
580
|
+
for _, group in sorted(pairs.items())
|
|
581
|
+
]
|
|
582
|
+
result = {
|
|
583
|
+
"method": "multiplicative_least_squares_on_equal_weight_task_means",
|
|
584
|
+
"task_count": len(task_means),
|
|
585
|
+
"factor": None,
|
|
586
|
+
"status": "insufficient_tasks",
|
|
587
|
+
}
|
|
588
|
+
if len(task_means) < minimum:
|
|
589
|
+
return result
|
|
590
|
+
denominator = sum(before * before for before, _ in task_means)
|
|
591
|
+
if denominator == 0:
|
|
592
|
+
return {**result, "status": "zero_predictions"}
|
|
593
|
+
factor = sum(before * after for before, after in task_means) / denominator
|
|
594
|
+
try:
|
|
595
|
+
numeric = float(factor)
|
|
596
|
+
except OverflowError:
|
|
597
|
+
numeric = math.inf
|
|
598
|
+
if not _finite(numeric):
|
|
599
|
+
return {**result, "status": "numeric_range"}
|
|
600
|
+
return {**result, "factor": numeric, "status": "fitted"}
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
def _mark_duplicates(rows):
|
|
604
|
+
by_outcome, by_pair = defaultdict(list), defaultdict(list)
|
|
605
|
+
for row in rows:
|
|
606
|
+
if row["intervention"] is not None:
|
|
607
|
+
by_outcome[row["intervention"]["intervention_id"]].append(row)
|
|
608
|
+
by_pair[
|
|
609
|
+
(
|
|
610
|
+
row["cohort_id"],
|
|
611
|
+
row["task_key"],
|
|
612
|
+
row["repetition_key"],
|
|
613
|
+
row["prediction_snapshot"]["finding_id"],
|
|
614
|
+
)
|
|
615
|
+
].append(row)
|
|
616
|
+
for groups, issue in ((by_outcome, "duplicate_outcome"), (by_pair, "duplicate_pairing_key")):
|
|
617
|
+
for group in groups.values():
|
|
618
|
+
if len(group) > 1:
|
|
619
|
+
for row in group:
|
|
620
|
+
row["issues"] = sorted(set([*row["issues"], issue]))
|
|
621
|
+
if row["quality_gate"] == "accepted":
|
|
622
|
+
row["quality_gate"] = "indeterminate"
|
|
623
|
+
for metric in row["metrics"].values():
|
|
624
|
+
metric["empirical_eligible"] = False
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
def _metric_summary(rows, metric, key, settings, *, quality_only=False):
|
|
628
|
+
return task_weighted_summary(
|
|
629
|
+
(
|
|
630
|
+
(
|
|
631
|
+
row["task_key"],
|
|
632
|
+
row["metrics"][metric].get(key)
|
|
633
|
+
if row["metrics"][metric]["empirical_eligible"]
|
|
634
|
+
and (not quality_only or row["quality_gate"] == "accepted")
|
|
635
|
+
else None,
|
|
636
|
+
)
|
|
637
|
+
for row in rows
|
|
638
|
+
if not row["synthetic"]
|
|
639
|
+
),
|
|
640
|
+
settings,
|
|
641
|
+
)
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
def _scaled_prediction(value, factor):
|
|
645
|
+
if value is None or factor is None:
|
|
646
|
+
return None
|
|
647
|
+
try:
|
|
648
|
+
result = float(Fraction(str(value)) * Fraction(str(factor)))
|
|
649
|
+
return result if _finite(result) else None
|
|
650
|
+
except (ValueError, OverflowError):
|
|
651
|
+
return None
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
def _cohort(rows, manifest):
|
|
655
|
+
metrics = {}
|
|
656
|
+
for metric in _METRICS:
|
|
657
|
+
fitted = (
|
|
658
|
+
_fit_scale(rows, metric, manifest["min_fit_tasks"])
|
|
659
|
+
if manifest["fit_method"] == "scale"
|
|
660
|
+
else {"method": "none", "status": "not_requested", "factor": None, "task_count": 0}
|
|
661
|
+
)
|
|
662
|
+
for row in rows:
|
|
663
|
+
values = row["metrics"][metric]
|
|
664
|
+
values["scaled_prediction"] = _scaled_prediction(
|
|
665
|
+
values["predicted_savings"], fitted["factor"]
|
|
666
|
+
)
|
|
667
|
+
values["scaled_signed_error"] = _safe_difference(
|
|
668
|
+
values["scaled_prediction"], values["realized_savings"]
|
|
669
|
+
)
|
|
670
|
+
splits = {}
|
|
671
|
+
for split in ("fit", "held_out"):
|
|
672
|
+
subset = [row for row in rows if row["split"] == split]
|
|
673
|
+
non_synthetic = [row for row in subset if not row["synthetic"]]
|
|
674
|
+
selected = [row for row in non_synthetic if row["selection"] == "selected"]
|
|
675
|
+
counts = Counter(row["quality_gate"] for row in selected)
|
|
676
|
+
splits[split] = {
|
|
677
|
+
"registration_count": len(subset),
|
|
678
|
+
"non_synthetic_registration_count": len(non_synthetic),
|
|
679
|
+
"selected_count": len(selected),
|
|
680
|
+
"synthetic_excluded_count": len(subset) - len(non_synthetic),
|
|
681
|
+
"quality_gate_counts": {
|
|
682
|
+
key: counts[key] for key in ("accepted", "rejected", "indeterminate")
|
|
683
|
+
},
|
|
684
|
+
"quality_acceptance_rate": counts["accepted"] / len(selected) if selected else None,
|
|
685
|
+
"selection_counts": dict(
|
|
686
|
+
sorted(Counter(row["selection"] for row in non_synthetic).items())
|
|
687
|
+
),
|
|
688
|
+
"original": {
|
|
689
|
+
key: _metric_summary(subset, metric, key, manifest["bootstrap"])
|
|
690
|
+
for key in (
|
|
691
|
+
"predicted_savings",
|
|
692
|
+
"realized_savings",
|
|
693
|
+
"signed_error",
|
|
694
|
+
"realization_ratio",
|
|
695
|
+
)
|
|
696
|
+
},
|
|
697
|
+
"quality_preserving": {
|
|
698
|
+
key: _metric_summary(
|
|
699
|
+
subset, metric, key, manifest["bootstrap"], quality_only=True
|
|
700
|
+
)
|
|
701
|
+
for key in ("realized_savings", "signed_error")
|
|
702
|
+
},
|
|
703
|
+
"scaled_error": _metric_summary(
|
|
704
|
+
subset, metric, "scaled_signed_error", manifest["bootstrap"]
|
|
705
|
+
),
|
|
706
|
+
}
|
|
707
|
+
metrics[metric] = {
|
|
708
|
+
"fit": fitted,
|
|
709
|
+
"splits": splits,
|
|
710
|
+
"held_out_status": "observed"
|
|
711
|
+
if splits["held_out"]["original"]["signed_error"]["observation_summary"]["count"]
|
|
712
|
+
else "no_evaluable_held_out_outcomes",
|
|
713
|
+
}
|
|
714
|
+
return {
|
|
715
|
+
"cohort_id": rows[0]["cohort_id"],
|
|
716
|
+
**rows[0]["cohort"],
|
|
717
|
+
"registration_count": len(rows),
|
|
718
|
+
"metrics": metrics,
|
|
719
|
+
"issues": dict(sorted(Counter(issue for row in rows for issue in row["issues"]).items())),
|
|
720
|
+
"selection_reasons": dict(sorted(Counter(row["selection_reason"] for row in rows).items())),
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
|
|
724
|
+
def summarize_calibration(manifest_path):
|
|
725
|
+
"""Read historical artifacts without re-diagnosing, re-pricing, or rerunning agents."""
|
|
726
|
+
path = Path(manifest_path).resolve()
|
|
727
|
+
sources, cache = {path}, {}
|
|
728
|
+
try:
|
|
729
|
+
manifest = json.loads(path.read_text(encoding="utf-8"))
|
|
730
|
+
# Own a finite JSON snapshot so neither reads nor reporting can mutate inputs.
|
|
731
|
+
manifest = json.loads(canonical_json(manifest))
|
|
732
|
+
_validate(manifest)
|
|
733
|
+
rows = [
|
|
734
|
+
_case(case, path.parent, manifest, sources, cache) for case in manifest["registrations"]
|
|
735
|
+
]
|
|
736
|
+
rows.sort(key=lambda row: row["case_id"])
|
|
737
|
+
_mark_duplicates(rows)
|
|
738
|
+
groups = defaultdict(list)
|
|
739
|
+
for row in rows:
|
|
740
|
+
groups[row["cohort_id"]].append(row)
|
|
741
|
+
cohorts = [_cohort(group, manifest) for _, group in sorted(groups.items())]
|
|
742
|
+
report = {
|
|
743
|
+
"schema_version": SCHEMA_VERSION,
|
|
744
|
+
"name": manifest["name"],
|
|
745
|
+
"manifest": manifest,
|
|
746
|
+
"manifest_hash": _digest(manifest),
|
|
747
|
+
"registrations": rows,
|
|
748
|
+
"cohorts": cohorts,
|
|
749
|
+
"registration_count": len(rows),
|
|
750
|
+
"synthetic_excluded_count": sum(row["synthetic"] for row in rows),
|
|
751
|
+
"selection_inventory_complete": manifest["selection_inventory_complete"],
|
|
752
|
+
"source_paths": sorted(str(source) for source in sources),
|
|
753
|
+
"validity": {
|
|
754
|
+
"as_of": manifest["as_of"],
|
|
755
|
+
"valid_until": manifest["valid_until"],
|
|
756
|
+
"expired": _time(manifest["as_of"]) > _time(manifest["valid_until"]),
|
|
757
|
+
"scope": "Only the exact recorded estimator/policy, workload, model/provider, environment, scorer and quality-gate cohort. Changes require renewed validation; freshness is assessed at the declared as_of, not the reader's current clock.",
|
|
758
|
+
},
|
|
759
|
+
"runtime_coefficients_changed": False,
|
|
760
|
+
"interpretation": "Historical descriptive calibration, not a causal or universal-confidence claim. Savings are baseline minus candidate; signed error is prediction minus realized savings (positive means overprediction). Synthetic, ambiguous and unverifiable cases are excluded from empirical aggregates but retained. All measurable outcomes, including failures and unknown quality, enter raw error summaries and any requested scale fit. Quality-preserving subsets are conditional and retain denominators. Scale fitting targets raw resource savings, never task utility; fitting-task error is in-sample and held-out error is separate. Task means, not seeds, are the bootstrap units. Selection, missingness, small-task uncertainty, multiplicity, scorer validity and workload drift remain limitations. No estimator or runtime policy is modified.",
|
|
761
|
+
}
|
|
762
|
+
report["artifact_hash"] = _digest(report)
|
|
763
|
+
return report
|
|
764
|
+
except (OSError, ValueError, TypeError, KeyError, RecursionError, OverflowError) as exc:
|
|
765
|
+
raise CalibrationValidationError(f"cannot read calibration manifest: {exc}") from exc
|
|
766
|
+
|
|
767
|
+
|
|
768
|
+
def calibration_to_markdown(report):
|
|
769
|
+
lines = [
|
|
770
|
+
f"# Calibration: {markdown_heading(report['name'])}",
|
|
771
|
+
"",
|
|
772
|
+
report["interpretation"],
|
|
773
|
+
"",
|
|
774
|
+
f"Registrations: {report['registration_count']}; synthetic excluded: {report['synthetic_excluded_count']}.",
|
|
775
|
+
"",
|
|
776
|
+
f"Selection inventory declared complete: {report['selection_inventory_complete']}. This is a host declaration, not proof of unbiased selection.",
|
|
777
|
+
"",
|
|
778
|
+
f"Freshness at {markdown_heading(report['validity']['as_of'])}: {'expired' if report['validity']['expired'] else 'within declared validity window'}; valid until {markdown_heading(report['validity']['valid_until'])}.",
|
|
779
|
+
"",
|
|
780
|
+
]
|
|
781
|
+
for cohort in report["cohorts"]:
|
|
782
|
+
context = cohort["context"]
|
|
783
|
+
lines.extend(
|
|
784
|
+
[
|
|
785
|
+
f"## {markdown_heading(cohort['cohort_id'])}",
|
|
786
|
+
"",
|
|
787
|
+
f"Workload: {markdown_heading(context['workload'])}; model/provider: {markdown_heading(context['model'])} / {markdown_heading(context['provider'])}; cost basis: {markdown_heading(context['cost_basis'])}.",
|
|
788
|
+
"",
|
|
789
|
+
"Estimator: " + markdown_code_span(canonical_json(cohort["estimator"])),
|
|
790
|
+
"",
|
|
791
|
+
"Environment/scorer/pricing/policy: "
|
|
792
|
+
+ markdown_code_span(
|
|
793
|
+
canonical_json(
|
|
794
|
+
{
|
|
795
|
+
key: context[key]
|
|
796
|
+
for key in (
|
|
797
|
+
"environment",
|
|
798
|
+
"scorer",
|
|
799
|
+
"pricing",
|
|
800
|
+
"policy",
|
|
801
|
+
"quality_gate",
|
|
802
|
+
"intervention",
|
|
803
|
+
)
|
|
804
|
+
}
|
|
805
|
+
)
|
|
806
|
+
),
|
|
807
|
+
"",
|
|
808
|
+
"Selection reasons: "
|
|
809
|
+
+ markdown_code_span(canonical_json(cohort["selection_reasons"])),
|
|
810
|
+
"",
|
|
811
|
+
"Excluded/unverifiable evidence: "
|
|
812
|
+
+ markdown_code_span(canonical_json(cohort["issues"])),
|
|
813
|
+
"",
|
|
814
|
+
"| Metric / split | Registered (non-synthetic) | Selected | Quality accepted / rejected / unknown | Evaluable / planned: observations; tasks | Mean original error | Mean scaled error | Mean realized savings | Original error interval |",
|
|
815
|
+
"| --- | ---: | ---: | --- | --- | ---: | ---: | ---: | --- |",
|
|
816
|
+
]
|
|
817
|
+
)
|
|
818
|
+
notes = []
|
|
819
|
+
for metric, details in cohort["metrics"].items():
|
|
820
|
+
for split, values in details["splits"].items():
|
|
821
|
+
errors = values["original"]["signed_error"]
|
|
822
|
+
savings = values["original"]["realized_savings"]
|
|
823
|
+
counts, interval = values["quality_gate_counts"], errors["interval"]
|
|
824
|
+
bounds = (
|
|
825
|
+
f"{interval['confidence']:.1%}: [{interval['lower']:.6g}, {interval['upper']:.6g}]"
|
|
826
|
+
if interval["status"] == "computed"
|
|
827
|
+
else "insufficient tasks"
|
|
828
|
+
)
|
|
829
|
+
cells = [
|
|
830
|
+
f"{metric} / {split}",
|
|
831
|
+
values["non_synthetic_registration_count"],
|
|
832
|
+
values["selected_count"],
|
|
833
|
+
f"{counts['accepted']} / {counts['rejected']} / {counts['indeterminate']}",
|
|
834
|
+
f"{errors['observation_summary']['count']}/{errors['planned_observation_count']}; {errors['task_summary']['count']}/{errors['planned_task_count']}",
|
|
835
|
+
errors["task_summary"]["mean"],
|
|
836
|
+
values["scaled_error"]["task_summary"]["mean"],
|
|
837
|
+
savings["task_summary"]["mean"],
|
|
838
|
+
bounds,
|
|
839
|
+
]
|
|
840
|
+
lines.append(
|
|
841
|
+
"| "
|
|
842
|
+
+ " | ".join(
|
|
843
|
+
markdown_table_cell("unavailable" if value is None else str(value))
|
|
844
|
+
for value in cells
|
|
845
|
+
)
|
|
846
|
+
+ " |"
|
|
847
|
+
)
|
|
848
|
+
accepted = values["quality_acceptance_rate"]
|
|
849
|
+
eligible = values["quality_preserving"]["realized_savings"]
|
|
850
|
+
notes.extend(
|
|
851
|
+
[
|
|
852
|
+
"",
|
|
853
|
+
f"{metric}/{split} quality acceptance among selected non-synthetic registrations: {'undefined (zero selected)' if accepted is None else f'{accepted:.1%}'}. Conditional mean savings: {eligible['task_summary']['mean']}; eligible tasks: {eligible['task_summary']['count']}/{eligible['planned_task_count']}.",
|
|
854
|
+
"",
|
|
855
|
+
]
|
|
856
|
+
)
|
|
857
|
+
fitted = details["fit"]
|
|
858
|
+
notes.extend(
|
|
859
|
+
[
|
|
860
|
+
"",
|
|
861
|
+
f"{metric} diagnostic fit: {fitted['status']}; factor: {fitted['factor']}; fitting tasks: {fitted['task_count']}. {details['held_out_status']}.",
|
|
862
|
+
"",
|
|
863
|
+
]
|
|
864
|
+
)
|
|
865
|
+
lines.extend(notes)
|
|
866
|
+
lines.extend(
|
|
867
|
+
[
|
|
868
|
+
"Every registration, original snapshot, outcome status, exclusion reason, task/observation denominator and held-out scaled error remains in JSON. No runtime coefficients were changed.",
|
|
869
|
+
"",
|
|
870
|
+
]
|
|
871
|
+
)
|
|
872
|
+
return "\n".join(lines)
|