context-engineering-core 0.12.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.
- context_engine/__init__.py +15 -0
- context_engine/__main__.py +3 -0
- context_engine/_sqlite.py +19 -0
- context_engine/_windows_security.py +125 -0
- context_engine/_windows_storage.py +110 -0
- context_engine/adapters.py +148 -0
- context_engine/answers.py +90 -0
- context_engine/budget.py +93 -0
- context_engine/cli.py +222 -0
- context_engine/config.py +193 -0
- context_engine/context.py +116 -0
- context_engine/demo.py +98 -0
- context_engine/errors.py +91 -0
- context_engine/evaluation/__init__.py +1 -0
- context_engine/evaluation/commands.py +90 -0
- context_engine/evaluation/corpus.py +247 -0
- context_engine/evaluation/data/freeze.json +26 -0
- context_engine/evaluation/data/ground_truth.json +287 -0
- context_engine/evaluation/data/pin_retention.json +8 -0
- context_engine/evaluation/data/protocol.json +122 -0
- context_engine/evaluation/data/scenario.json +1226 -0
- context_engine/evaluation/data/summary_fixture.json +7 -0
- context_engine/evaluation/execution.py +538 -0
- context_engine/evaluation/fixtures.py +32 -0
- context_engine/evaluation/grading.py +62 -0
- context_engine/evaluation/harness.py +165 -0
- context_engine/evaluation/heldout/__init__.py +260 -0
- context_engine/evaluation/heldout/__main__.py +40 -0
- context_engine/evaluation/heldout/data/ground_truth.json +18 -0
- context_engine/evaluation/heldout/data/manifest.json +9 -0
- context_engine/evaluation/heldout/data/protocol.json +14 -0
- context_engine/evaluation/heldout/data/scenarios.json +18 -0
- context_engine/evaluation/heldout/data/scoring_manifest.json +4 -0
- context_engine/evaluation/heldout/data/scoring_protocol.json +12 -0
- context_engine/evaluation/heldout/scoring.py +176 -0
- context_engine/evaluation/protocol.py +264 -0
- context_engine/evaluation/reporting.py +294 -0
- context_engine/evaluation/results.py +331 -0
- context_engine/evaluation/run_commands.py +146 -0
- context_engine/evaluation/strategies.py +261 -0
- context_engine/inspection.py +266 -0
- context_engine/layers/__init__.py +18 -0
- context_engine/layers/cap.py +99 -0
- context_engine/layers/common.py +162 -0
- context_engine/layers/pin.py +68 -0
- context_engine/layers/retrieve.py +233 -0
- context_engine/layers/summarize.py +202 -0
- context_engine/layers/window.py +29 -0
- context_engine/mcp_server.py +267 -0
- context_engine/memory/__init__.py +5 -0
- context_engine/memory/codec.py +91 -0
- context_engine/memory/demo.py +90 -0
- context_engine/memory/store.py +770 -0
- context_engine/models.py +384 -0
- context_engine/pipeline.py +383 -0
- context_engine/providers/__init__.py +1 -0
- context_engine/providers/client.py +301 -0
- context_engine/providers/commands.py +138 -0
- context_engine/providers/contracts.py +324 -0
- context_engine/providers/credentials.py +66 -0
- context_engine/providers/memory.py +112 -0
- context_engine/providers/store.py +408 -0
- context_engine/providers/transport.py +195 -0
- context_engine/providers/windows_credentials.py +23 -0
- context_engine/py.typed +0 -0
- context_engine/service/__init__.py +1 -0
- context_engine/service/__main__.py +68 -0
- context_engine/service/app.py +404 -0
- context_engine/service/auth.py +132 -0
- context_engine/service/client.py +52 -0
- context_engine/service/control.py +134 -0
- context_engine/service/demo.py +151 -0
- context_engine/service/integrations.py +28 -0
- context_engine/service/schemas.py +59 -0
- context_engine/service/worker.py +59 -0
- context_engine/service/workers.py +156 -0
- context_engine/summaries.py +196 -0
- context_engine/tokens.py +171 -0
- context_engine/work.py +46 -0
- context_engineering_core-0.12.0.dist-info/METADATA +203 -0
- context_engineering_core-0.12.0.dist-info/RECORD +84 -0
- context_engineering_core-0.12.0.dist-info/WHEEL +4 -0
- context_engineering_core-0.12.0.dist-info/entry_points.txt +3 -0
- context_engineering_core-0.12.0.dist-info/licenses/LICENSE +202 -0
context_engine/errors.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Structured, content-free errors safe for default diagnostic output."""
|
|
2
|
+
|
|
3
|
+
from types import MappingProxyType
|
|
4
|
+
from typing import ClassVar
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ContextEngineError(Exception):
|
|
8
|
+
code: ClassVar[str] = "context_engine_error"
|
|
9
|
+
|
|
10
|
+
def __init__(self, message: str, **details: int | str) -> None:
|
|
11
|
+
super().__init__(message)
|
|
12
|
+
self.details = MappingProxyType(dict(details))
|
|
13
|
+
|
|
14
|
+
def to_dict(self) -> dict:
|
|
15
|
+
return {"code": self.code, "message": str(self), "details": dict(self.details)}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ConfigurationError(ContextEngineError):
|
|
19
|
+
code = "invalid_configuration"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ContractError(ContextEngineError):
|
|
23
|
+
code = "invalid_contract"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class TokenizerUnavailable(ContextEngineError):
|
|
27
|
+
code = "tokenizer_unavailable"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class RequiredContextTooLarge(ContextEngineError):
|
|
31
|
+
code = "required_context_too_large"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class LayerAllocationError(ContextEngineError):
|
|
35
|
+
code = "layer_allocation_exceeds_budget"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class PromptTooLarge(ContextEngineError):
|
|
39
|
+
code = "prompt_too_large"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class LayerStateError(ContextEngineError):
|
|
43
|
+
code = "invalid_layer_state"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class CapBudgetError(ContextEngineError):
|
|
47
|
+
code = "cap_budget_too_small"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class SummaryLeakageError(ContextEngineError):
|
|
51
|
+
code = "summary_leakage"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class InspectionError(ContextEngineError):
|
|
55
|
+
code = "invalid_inspection_input"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class SummaryPolicyError(ContextEngineError):
|
|
59
|
+
code = "summary_policy_error"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class BenchmarkError(ContextEngineError):
|
|
63
|
+
code = "invalid_benchmark"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class ProviderError(ContextEngineError):
|
|
67
|
+
code = "provider_error"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class StorageError(ContextEngineError):
|
|
71
|
+
code = "runtime_storage_error"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class QuotaError(ContextEngineError):
|
|
75
|
+
code = "quota_exhausted"
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class CacheError(ContextEngineError):
|
|
79
|
+
code = "invalid_replay_cache"
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class MemoryConflict(ContextEngineError):
|
|
83
|
+
code = "memory_revision_conflict"
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class MemoryIntegrityError(ContextEngineError):
|
|
87
|
+
code = "memory_integrity_error"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class WorkCancelled(ContextEngineError):
|
|
91
|
+
code = "work_cancelled"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Fixture validation only; the complete benchmark harness arrives at C04."""
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""Bounded offline owner demonstrations, not a model benchmark runner."""
|
|
2
|
+
|
|
3
|
+
from copy import deepcopy
|
|
4
|
+
from dataclasses import asdict
|
|
5
|
+
|
|
6
|
+
from ..models import canonical_json
|
|
7
|
+
from .corpus import Bundle, load_bundle
|
|
8
|
+
from .harness import prepare_probe
|
|
9
|
+
from .protocol import load_freeze, make_manifest, preflight
|
|
10
|
+
from .results import assess_targets, record_response, statistics, validate_run
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def altered_bundle(bundle: Bundle, name: str, value: dict) -> Bundle:
|
|
14
|
+
return Bundle(
|
|
15
|
+
tuple((key, canonical_json(value) if key == name else raw) for key, raw in bundle.parts)
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def corruption_demo(bundle, frozen, manifest, records) -> dict:
|
|
20
|
+
"""Synthetic fault injection on detached copies; never rewrite frozen resources."""
|
|
21
|
+
result = {}
|
|
22
|
+
for label, name, mutate in (
|
|
23
|
+
("malformed_fixture", "scenario", lambda data: data["history"].pop()),
|
|
24
|
+
("summary_answer_leak", "summary_fixture", lambda data: data.update(content="shard-19")),
|
|
25
|
+
("configuration_drift", "protocol", lambda data: data["cap"].update(max_tokens=36)),
|
|
26
|
+
):
|
|
27
|
+
value = bundle.get(name)
|
|
28
|
+
mutate(value)
|
|
29
|
+
gates = preflight(altered_bundle(bundle, name, value), frozen)
|
|
30
|
+
result[label] = {
|
|
31
|
+
"status": "INVALID" if any(g.status == "FAIL" for g in gates) else "UNEXPECTED_PASS",
|
|
32
|
+
"failed_gates": [g.gate for g in gates if g.status == "FAIL"],
|
|
33
|
+
}
|
|
34
|
+
bad = deepcopy(records)
|
|
35
|
+
bad[-1]["request"]["messages"][-1]["content"] = "Tampered question"
|
|
36
|
+
result["changed_request"] = validate_run(bundle, frozen, manifest, bad)
|
|
37
|
+
bad = deepcopy(records)
|
|
38
|
+
bad[-1] = record_response(
|
|
39
|
+
bundle, bad[-1], answer="synthetic partial response", finish_reason="length", origin="test"
|
|
40
|
+
)
|
|
41
|
+
result["truncated_test_response"] = validate_run(bundle, frozen, manifest, bad)
|
|
42
|
+
return result
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def benchmark_check(
|
|
46
|
+
*, fact="shard", budget=900, model=None, show_content=False, data_dir=None
|
|
47
|
+
) -> dict:
|
|
48
|
+
bundle, frozen = load_bundle(data_dir), load_freeze()
|
|
49
|
+
gates = preflight(bundle, frozen)
|
|
50
|
+
if any(g.status == "FAIL" for g in gates):
|
|
51
|
+
return {"status": "INVALID", "gates": [asdict(g) for g in gates], "inference_calls": 0}
|
|
52
|
+
model = model or bundle.get("protocol")["models"][0]
|
|
53
|
+
manifest = make_manifest(
|
|
54
|
+
bundle, frozen, facts=[fact], budgets=[budget], variants=["A0", "A5"], models=[model]
|
|
55
|
+
)
|
|
56
|
+
records = [prepare_probe(bundle, frozen, manifest, slot["probe_id"]) for slot in manifest.slots]
|
|
57
|
+
validation = validate_run(bundle, frozen, manifest, records)
|
|
58
|
+
exported = (
|
|
59
|
+
records
|
|
60
|
+
if show_content
|
|
61
|
+
else [{k: v for k, v in row.items() if k != "request"} for row in records]
|
|
62
|
+
)
|
|
63
|
+
return {
|
|
64
|
+
**validation,
|
|
65
|
+
"run_id": manifest.run_id,
|
|
66
|
+
"fixture_hashes": bundle.hashes,
|
|
67
|
+
"protocol_approval": bundle.get("protocol")["approval"],
|
|
68
|
+
"inference_calls": 0,
|
|
69
|
+
"content_included": show_content,
|
|
70
|
+
"probes": exported,
|
|
71
|
+
"statistics": statistics(manifest, records),
|
|
72
|
+
"quality_acceptance": assess_targets(manifest, records, validation),
|
|
73
|
+
"deliberately_invalid": corruption_demo(bundle, frozen, manifest, records),
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def benchmark_plan(*, data_dir=None) -> dict:
|
|
78
|
+
bundle, frozen = load_bundle(data_dir), load_freeze()
|
|
79
|
+
manifest = make_manifest(bundle, frozen)
|
|
80
|
+
completion = bundle.get("protocol")["generation"]["max_completion_tokens"]
|
|
81
|
+
return {
|
|
82
|
+
"status": "PLANNED",
|
|
83
|
+
"run_id": manifest.run_id,
|
|
84
|
+
"manifest": manifest.data,
|
|
85
|
+
"slots": list(manifest.slots),
|
|
86
|
+
"planned_slots": len(manifest.slots),
|
|
87
|
+
"conservative_tokens_before_retries": sum(s["budget"] + completion for s in manifest.slots),
|
|
88
|
+
"inference_calls": 0,
|
|
89
|
+
"spending_authorized": False,
|
|
90
|
+
}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"""Frozen, packaged synthetic resources; ground truth never enters an inspection job."""
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import json
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from importlib import resources
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from ..config import Settings
|
|
11
|
+
from ..errors import BenchmarkError, ContextEngineError
|
|
12
|
+
from ..inspection import parse_job
|
|
13
|
+
from ..models import canonical_json
|
|
14
|
+
from .grading import normalize, present
|
|
15
|
+
|
|
16
|
+
PARTS = ("scenario", "ground_truth", "summary_fixture", "protocol", "pin_retention")
|
|
17
|
+
MAX_BYTES = 2_000_000
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def digest(value) -> str:
|
|
21
|
+
return hashlib.sha256(canonical_json(value).encode()).hexdigest()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def strict_json(raw: str, *, maximum: int = MAX_BYTES) -> dict:
|
|
25
|
+
def unique(pairs):
|
|
26
|
+
result = {}
|
|
27
|
+
for key, value in pairs:
|
|
28
|
+
if key in result:
|
|
29
|
+
raise BenchmarkError("Duplicate benchmark JSON key")
|
|
30
|
+
result[key] = value
|
|
31
|
+
return result
|
|
32
|
+
|
|
33
|
+
def nonfinite(_):
|
|
34
|
+
raise BenchmarkError("Nonfinite benchmark JSON number")
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
if len(raw.encode("utf-8")) > maximum:
|
|
38
|
+
raise BenchmarkError("Benchmark document exceeds byte limit")
|
|
39
|
+
parsed = json.loads(raw, object_pairs_hook=unique, parse_constant=nonfinite)
|
|
40
|
+
if not isinstance(parsed, dict):
|
|
41
|
+
raise BenchmarkError("Benchmark document must be an object")
|
|
42
|
+
canonical_json(parsed)
|
|
43
|
+
return parsed
|
|
44
|
+
except (UnicodeError, ValueError, TypeError, RecursionError):
|
|
45
|
+
raise BenchmarkError("Malformed benchmark JSON") from None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass(frozen=True)
|
|
49
|
+
class Bundle:
|
|
50
|
+
parts: tuple[tuple[str, str], ...]
|
|
51
|
+
|
|
52
|
+
def __post_init__(self):
|
|
53
|
+
if not isinstance(self.parts, tuple) or tuple(name for name, _ in self.parts) != PARTS:
|
|
54
|
+
raise BenchmarkError("Incomplete benchmark bundle")
|
|
55
|
+
for _, raw in self.parts:
|
|
56
|
+
strict_json(raw)
|
|
57
|
+
|
|
58
|
+
def get(self, name: str) -> dict:
|
|
59
|
+
return strict_json(dict(self.parts)[name]) # Fresh objects, no mutable shared fixtures.
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def hashes(self) -> dict:
|
|
63
|
+
return {name: hashlib.sha256(raw.encode("utf-8")).hexdigest() for name, raw in self.parts}
|
|
64
|
+
|
|
65
|
+
def job(self, question: str):
|
|
66
|
+
scenario = self.get("scenario")
|
|
67
|
+
scenario.pop("scenario_id")
|
|
68
|
+
scenario["question"] = question
|
|
69
|
+
return parse_job(scenario, Settings.from_env({}))
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def facts(self) -> tuple[dict, ...]:
|
|
73
|
+
return tuple(self.get("ground_truth")["facts"])
|
|
74
|
+
|
|
75
|
+
def fact(self, identifier: str) -> dict:
|
|
76
|
+
for fact in self.facts:
|
|
77
|
+
if fact["fact_id"] == identifier:
|
|
78
|
+
return fact
|
|
79
|
+
raise BenchmarkError("Unknown benchmark fact")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def read_resource(name: str, directory: Path | None = None) -> str:
|
|
83
|
+
path = directory / name if directory else resources.files(__package__).joinpath("data", name)
|
|
84
|
+
try:
|
|
85
|
+
with path.open("rb") as handle:
|
|
86
|
+
raw = handle.read(MAX_BYTES + 1)
|
|
87
|
+
if len(raw) > MAX_BYTES:
|
|
88
|
+
raise BenchmarkError("Benchmark document exceeds byte limit")
|
|
89
|
+
return raw.decode("utf-8")
|
|
90
|
+
except (OSError, UnicodeError, ValueError):
|
|
91
|
+
raise BenchmarkError("Cannot read benchmark resource") from None
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def load_bundle(directory: Path | None = None) -> Bundle:
|
|
95
|
+
return Bundle(tuple((name, read_resource(name + ".json", directory)) for name in PARTS))
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def aliases(fact: dict) -> tuple[str, ...]:
|
|
99
|
+
return (fact["expected"], *fact["aliases"])
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def check_fixture(bundle: Bundle) -> None:
|
|
103
|
+
"""V1 schema, cardinality, relationships, plant locations and unique metadata."""
|
|
104
|
+
try:
|
|
105
|
+
scenario, truth = bundle.get("scenario"), bundle.get("ground_truth")
|
|
106
|
+
summary, protocol = bundle.get("summary_fixture"), bundle.get("protocol")
|
|
107
|
+
if any(
|
|
108
|
+
type(part["schema_version"]) is not int or part["schema_version"] != 1
|
|
109
|
+
for part in (scenario, truth, summary, protocol, bundle.get("pin_retention"))
|
|
110
|
+
):
|
|
111
|
+
raise BenchmarkError("Unsupported benchmark schema")
|
|
112
|
+
if set(truth) != {"schema_version", "scenario_id", "facts"}:
|
|
113
|
+
raise BenchmarkError("Unexpected ground-truth fields")
|
|
114
|
+
if any(
|
|
115
|
+
part["scenario_id"] != scenario["scenario_id"] for part in (truth, summary, protocol)
|
|
116
|
+
):
|
|
117
|
+
raise BenchmarkError("Scenario identity mismatch")
|
|
118
|
+
job = bundle.job("Fixture validation")
|
|
119
|
+
from ..layers.summarize import SummarySnapshot
|
|
120
|
+
from ..models import Pin
|
|
121
|
+
|
|
122
|
+
if set(summary) != {
|
|
123
|
+
"schema_version",
|
|
124
|
+
"scenario_id",
|
|
125
|
+
"policy_version",
|
|
126
|
+
"created_at",
|
|
127
|
+
"content",
|
|
128
|
+
}:
|
|
129
|
+
raise BenchmarkError("Unexpected summary fields")
|
|
130
|
+
snapshot = SummarySnapshot.from_history(
|
|
131
|
+
scope=job.arguments["pinned_facts"].scope,
|
|
132
|
+
content=summary["content"],
|
|
133
|
+
history=job.arguments["history"],
|
|
134
|
+
policy_version=summary["policy_version"],
|
|
135
|
+
created_at=datetime.fromisoformat(summary["created_at"]),
|
|
136
|
+
)
|
|
137
|
+
if snapshot.created_at > job.arguments["at"]:
|
|
138
|
+
raise BenchmarkError("Frozen summary is not available at evaluation time")
|
|
139
|
+
fixture = bundle.get("pin_retention")
|
|
140
|
+
if set(fixture) != {"schema_version", "fixture_id", "question", "key", "value", "origin"}:
|
|
141
|
+
raise BenchmarkError("Unexpected separate pin fixture fields")
|
|
142
|
+
Pin(
|
|
143
|
+
scope=job.arguments["pinned_facts"].scope,
|
|
144
|
+
key=fixture["key"],
|
|
145
|
+
value=fixture["value"],
|
|
146
|
+
origin=fixture["origin"],
|
|
147
|
+
effective_at=job.arguments["at"],
|
|
148
|
+
)
|
|
149
|
+
if (
|
|
150
|
+
not isinstance(fixture["question"], str)
|
|
151
|
+
or not fixture["question"].strip()
|
|
152
|
+
or present(fixture["question"], (fixture["value"],))
|
|
153
|
+
):
|
|
154
|
+
raise BenchmarkError("Invalid separate pin question")
|
|
155
|
+
# Constructed carrier validation catches duplicate/mixed source identity without inference.
|
|
156
|
+
from ..context import ContextCarrier
|
|
157
|
+
from ..models import Message, Role
|
|
158
|
+
|
|
159
|
+
history = job.arguments["history"]
|
|
160
|
+
ContextCarrier(
|
|
161
|
+
job.arguments["pinned_facts"].scope,
|
|
162
|
+
history,
|
|
163
|
+
history,
|
|
164
|
+
job.arguments["pinned_facts"],
|
|
165
|
+
Message("fixture-question", Role.USER, "Q"),
|
|
166
|
+
)
|
|
167
|
+
if len(history) != 100 or [t.turn_id for t in history] != [f"t{i}" for i in range(1, 101)]:
|
|
168
|
+
raise BenchmarkError("Expected ordered 100-turn fixture")
|
|
169
|
+
if len(bundle.facts) != 31 or len({f["fact_id"] for f in bundle.facts}) != 31:
|
|
170
|
+
raise BenchmarkError("Expected exactly 31 unique facts")
|
|
171
|
+
turns = {t.turn_id: t for t in history}
|
|
172
|
+
seen_answers, seen_questions = set(), set()
|
|
173
|
+
fields = {
|
|
174
|
+
"fact_id",
|
|
175
|
+
"expected",
|
|
176
|
+
"aliases",
|
|
177
|
+
"source_turn_id",
|
|
178
|
+
"source_message_id",
|
|
179
|
+
"question",
|
|
180
|
+
"zone",
|
|
181
|
+
}
|
|
182
|
+
for fact in bundle.facts:
|
|
183
|
+
if set(fact) != fields or not isinstance(fact["aliases"], list):
|
|
184
|
+
raise BenchmarkError("Unexpected fact fields")
|
|
185
|
+
if any(
|
|
186
|
+
not isinstance(fact[k], str) or not fact[k].strip() for k in fields - {"aliases"}
|
|
187
|
+
):
|
|
188
|
+
raise BenchmarkError("Invalid fact text")
|
|
189
|
+
values = aliases(fact)
|
|
190
|
+
if any(not isinstance(value, str) or not normalize(value) for value in values):
|
|
191
|
+
raise BenchmarkError("Invalid answer alias")
|
|
192
|
+
normalized = {normalize(value) for value in values}
|
|
193
|
+
if len(normalized) != len(values) or seen_answers & normalized:
|
|
194
|
+
raise BenchmarkError("Ambiguous answer aliases")
|
|
195
|
+
seen_answers |= normalized
|
|
196
|
+
if normalize(fact["question"]) in seen_questions:
|
|
197
|
+
raise BenchmarkError("Duplicate probe question")
|
|
198
|
+
seen_questions.add(normalize(fact["question"]))
|
|
199
|
+
turn = turns[fact["source_turn_id"]]
|
|
200
|
+
message = next(m for m in turn.messages if m.message_id == fact["source_message_id"])
|
|
201
|
+
if not present(message.content, (fact["expected"],)):
|
|
202
|
+
raise BenchmarkError("Planted value missing from declared source")
|
|
203
|
+
locations = [
|
|
204
|
+
(t.turn_id, m.message_id)
|
|
205
|
+
for t in history
|
|
206
|
+
for m in t.messages
|
|
207
|
+
if present(m.content, values)
|
|
208
|
+
]
|
|
209
|
+
if locations != [(turn.turn_id, message.message_id)]:
|
|
210
|
+
raise BenchmarkError("Fact appears outside its declared source")
|
|
211
|
+
number = int(turn.turn_id[1:])
|
|
212
|
+
if fact["zone"] != ("early" if number <= 33 else "middle" if number <= 66 else "late"):
|
|
213
|
+
raise BenchmarkError("Incorrect source age zone")
|
|
214
|
+
flagship = bundle.fact("shard")
|
|
215
|
+
if (flagship["source_turn_id"], flagship["expected"], flagship["question"]) != (
|
|
216
|
+
"t82",
|
|
217
|
+
"shard-19",
|
|
218
|
+
"Which partition ended up carrying the blame?",
|
|
219
|
+
):
|
|
220
|
+
raise BenchmarkError("Flagship source contract changed")
|
|
221
|
+
except BenchmarkError:
|
|
222
|
+
raise
|
|
223
|
+
except (ContextEngineError, KeyError, TypeError, ValueError, StopIteration, OverflowError):
|
|
224
|
+
raise BenchmarkError("Malformed benchmark fixture") from None
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def check_leakage(bundle: Bundle) -> None:
|
|
228
|
+
"""V2 primary input has no evaluator fields, answer pins, question/summary leakage."""
|
|
229
|
+
from ..layers.summarize import SummarySnapshot
|
|
230
|
+
from .fixtures import validate_frozen_summary
|
|
231
|
+
|
|
232
|
+
job = bundle.job("Leakage validation")
|
|
233
|
+
scenario, summary = bundle.get("scenario"), bundle.get("summary_fixture")
|
|
234
|
+
values = tuple(value for fact in bundle.facts for value in aliases(fact))
|
|
235
|
+
if scenario["pins"] or bundle.get("protocol")["primary_pins"] != "empty":
|
|
236
|
+
raise BenchmarkError("Primary corpus must have no answer pins")
|
|
237
|
+
for content in [scenario["system"], *(fact["question"] for fact in bundle.facts)]:
|
|
238
|
+
if present(content, values):
|
|
239
|
+
raise BenchmarkError("Answer leakage in system or probe question")
|
|
240
|
+
snapshot = SummarySnapshot.from_history(
|
|
241
|
+
scope=job.arguments["pinned_facts"].scope,
|
|
242
|
+
content=summary["content"],
|
|
243
|
+
history=job.arguments["history"],
|
|
244
|
+
policy_version=summary["policy_version"],
|
|
245
|
+
created_at=job.arguments["at"],
|
|
246
|
+
)
|
|
247
|
+
validate_frozen_summary(snapshot, values)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"fixture_hashes": {
|
|
3
|
+
"ground_truth": "c319c47c0548ced89dc68deba183c3028c35aad2aabd756db8b3bbdfe4c064e6",
|
|
4
|
+
"pin_retention": "febe00b1b71016fd679a3d99a2bccdb901ff9b956f0933d1003578c06faf5f3b",
|
|
5
|
+
"protocol": "789585023df970bcfea2723b5b7994a1251f0aa512f6ee5820b8ad7353eb571c",
|
|
6
|
+
"scenario": "19f3bc62987b9b2253eed316e903c4f860b445b441d9151573588bce283bae54",
|
|
7
|
+
"summary_fixture": "b3c1b76bb330b52493e5150647747bad77d5de3a55d05d032bb5703be1c70196"
|
|
8
|
+
},
|
|
9
|
+
"runtime": {
|
|
10
|
+
"code_hash": "59eeb4d28e6b3106a18317d5b6acee255f0b22d5e5143e3e7bb74b1146a39ff4",
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"certifi": "2026.10.23",
|
|
13
|
+
"charset-normalizer": "3.5.1",
|
|
14
|
+
"idna": "3.19",
|
|
15
|
+
"numpy": "2.5.3",
|
|
16
|
+
"rank-bm25": "0.2.2",
|
|
17
|
+
"regex": "2026.9.3",
|
|
18
|
+
"requests": "2.34.2",
|
|
19
|
+
"tiktoken": "0.14.0",
|
|
20
|
+
"urllib3": "2.8.0"
|
|
21
|
+
},
|
|
22
|
+
"package_version": "0.12.0",
|
|
23
|
+
"python": "3.12.13"
|
|
24
|
+
},
|
|
25
|
+
"schema_version": 1
|
|
26
|
+
}
|