Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions benchmark/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,18 @@ The local `sforge_checkpoint_opportunity_v0`
artifact records these facts; it grants no restored admission. Disabling capture
means omitting both the judge flag and worker declaration option on a new run.

Finite opportunity responses and saved participants keep that existing JSON
shape. Released history/results containing non-finite numbers use the explicit
[`sforge_nonfinite_json: 1` envelope](edgebench/README.md#feedback-protocol-new-run-default-native)
over the same logical artifact. The client decodes it before checking the
incumbent; checkpoint capture saves it through the same lossless codec.
Only released result fields may contain encoded numbers: opportunity clocks,
round counters and authority fields retain their original strict representation.
The archive binding covers the canonical encoded state, including numeric kinds,
so repeated NaN reads retain a binding and changed released data invalidates it.
Use `benchmark.runtime.sforge_json.loads` for saved participant readback. This
transport neither changes native selection nor qualifies continued execution.

For an isolated single-Goal trial, `"loopx_state": true` adds the existing
state-backup owner's inventory: registered active state, project research state,
runtime authority/effect/quota journals and registry. The declared worker and
Expand Down
11 changes: 11 additions & 0 deletions benchmark/edgebench/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,17 @@ covers online submissions only; use the complete offline result for post-run
qualification. Neither report by itself certifies integrity or score countability.
No offline result is routed to the worker.

Offline score files, the combined result and the command's summary retain native
non-finite scores and nested metrics. Finite documents keep their existing JSON.
Other documents use the local `sforge_nonfinite_json: 1` transport envelope:
`value` contains JSON data with each non-finite number replaced by `null`, and
`nonfinite_values` records its path and `positive_infinity`, `negative_infinity`
or `nan` kind. Use `benchmark.runtime.sforge_json.loads` to read either form.
Restart decodes saved scores before calling the unchanged native selector;
it does not turn these results into zero, missing feedback or grading errors.
Literal marker-shaped metrics remain data, including an escaped root marker.
Malformed paths, duplicate keys and unmarked non-finite constants are rejected.

The first completed valid finite score establishes a silent baseline. The task's
native selection policy is the sole improvement criterion among valid scored
snapshots. Only a strictly better native rank updates `/opt/edgebench-feedback/latest.json`; ties,
Expand Down
10 changes: 7 additions & 3 deletions benchmark/edgebench/checkpoint_judge.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
import uuid
from dataclasses import replace
from pathlib import Path
from benchmark.runtime.sforge_checkpoint import ARCHIVE_ROUTE, ROUTE, SCHEMA
from benchmark.runtime import sforge_json
from benchmark.runtime.sforge_checkpoint import ARCHIVE_ROUTE, ROUTE, SCHEMA, RELEASED_FIELDS


def _read_archive(root, run, task, round_id):
Expand Down Expand Up @@ -142,8 +143,10 @@ def snapshot_locked(token):
binding = {key: value for key, value in packet.items()
if key not in {"observed_at_epoch", "remaining_cooldown_seconds"}}
packet["capture_binding_sha256"] = hashlib.sha256(
json.dumps(binding, sort_keys=True, separators=(",", ":")).encode()).hexdigest()
return json.loads(json.dumps(packet)), raw # No live references or tokens leave the lock.
sforge_json.dumps(binding, fields=RELEASED_FIELDS,
sort_keys=True, separators=(",", ":")).encode()).hexdigest()
# The wire object contains no non-JSON constants or live references.
return json.loads(sforge_json.dumps(packet, fields=RELEASED_FIELDS)), raw

def capture(request):
with state._admission_lock, state._tokens_lock, state._history_lock:
Expand All @@ -152,6 +155,7 @@ def capture(request):
def archive(request):
with state._admission_lock, state._tokens_lock, state._history_lock:
packet, raw = snapshot_locked(request.token)
packet = sforge_json.decode_document(packet, fields=RELEASED_FIELDS)
incumbent = packet["native_incumbent"]
if packet["capture_binding_sha256"] != request.capture_binding_sha256 or incumbent is None:
raise HTTPException(409, "Native checkpoint basis moved or has no selected incumbent")
Expand Down
9 changes: 5 additions & 4 deletions benchmark/edgebench/offline_scoring.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from sforge.harness.run_evaluation import judge_submission
from sforge.harness.selection import select_best
from sforge.harness.task_spec import make_task_spec
from benchmark.runtime import sforge_json
from .online_judge import cohort_lock, resource_preflight


Expand Down Expand Up @@ -51,7 +52,7 @@ def score_captures(trial, task, config, backend):
directory.mkdir(exist_ok=True)
result_path = directory / "score.json"
if result_path.is_file():
entry = json.loads(result_path.read_text())
entry = sforge_json.loads(result_path.read_text())
if (entry["source_sha256"] != digest or entry["task_sha256"] != receipt["task_sha256"]
or entry.get("evaluator_sha256") != evaluator_digest):
raise ValueError("Offline score provenance differs from the trial")
Expand All @@ -67,7 +68,7 @@ def score_captures(trial, task, config, backend):
except Exception as error:
entry.update(status="error", error_kind=type(error).__name__)
pending = result_path.with_suffix(".tmp")
pending.write_text(json.dumps(entry, allow_nan=False))
pending.write_text(sforge_json.dumps(entry))
pending.replace(result_path)
entries.append(entry)
result = dict(run_id=receipt["run_id"], task=task.task_id,
Expand All @@ -76,7 +77,7 @@ def score_captures(trial, task, config, backend):
**select_best(entries, task.judge.score_direction, task.judge.selection), entries=entries)
path = destination / "result.json"
pending = path.with_suffix(".tmp")
pending.write_text(json.dumps(result, allow_nan=False))
pending.write_text(sforge_json.dumps(result))
pending.replace(path)
return result

Expand Down Expand Up @@ -105,7 +106,7 @@ def main():
result = score_captures(trial, task, config, create_backend_from_config(config))
except BlockingIOError:
parser.error("Online cohort still owns resources; defer offline scoring")
print(json.dumps({k: v for k, v in result.items() if k != "entries"}))
print(sforge_json.dumps({k: v for k, v in result.items() if k != "entries"}))
return 0 if result["offline_scoring_complete"] else 1


Expand Down
5 changes: 3 additions & 2 deletions benchmark/runtime/checkpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,12 +340,13 @@ def capture_after_wake(env: dict[str, str], receipt: dict[str, Any], *,
components, research = research_participants(env, components, metadata, destination, evidence, deadline)
opportunity = None
if request.get("evaluation_opportunity") and not request.get("controller_handoff"):
from .sforge_checkpoint import capture_opportunity
from . import sforge_json
from .sforge_checkpoint import capture_opportunity, RELEASED_FIELDS

evidence = Path(env["LOOPX_WAKE_LOG_DIR"]) / receipt["turn_id"] / "checkpoint-opportunity.json"
incumbent = evidence.with_name("checkpoint-incumbent.tar.gz")
opportunity = capture_opportunity(env, timeout=max(.1, deadline - time.time()), incumbent_path=incumbent)
evidence.write_text(json.dumps(opportunity), encoding="utf-8")
evidence.write_text(sforge_json.dumps(opportunity, fields=RELEASED_FIELDS), encoding="utf-8")
evidence.chmod(0o600)
opportunity_id = _participant_id(components, "evaluation_opportunity")
components = [*components, {"id": opportunity_id, "source_path": str(evidence)}]
Expand Down
5 changes: 4 additions & 1 deletion benchmark/runtime/sforge_checkpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@
from urllib.request import Request, urlopen
from pathlib import Path

from . import sforge_json

SCHEMA = "sforge_checkpoint_opportunity_v0" # Local provider artifact, not a LoopX authority contract.
ROUTE = "/api/v1/checkpoint/opportunity"
ARCHIVE_ROUTE = "/api/v1/checkpoint/incumbent"
RELEASED_FIELDS = ("released_history", "released_results")


def capture_opportunity(env, *, timeout, incumbent_path: Path):
Expand All @@ -23,7 +26,7 @@ def capture_opportunity(env, *, timeout, incumbent_path: Path):
data=json.dumps({"token": env["SFORGE_TOKEN"]}).encode(),
headers={"Content-Type": "application/json"}, method="POST")
with urlopen(request, timeout=max(.1, stop - time.time())) as response:
packet = json.load(response)
packet = sforge_json.loads(response.read(), fields=RELEASED_FIELDS)
if (packet.get("schema_version") != SCHEMA or packet.get("execution_authority_granted") is not False or
packet.get("outstanding_evaluations") != 0):
raise ValueError("native opportunity checkpoint readback is unavailable")
Expand Down
119 changes: 119 additions & 0 deletions benchmark/runtime/sforge_json.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
"""Lossless JSON transport for local SForge result artifacts.

Finite legacy documents keep their ordinary JSON representation. Nonfinite
numbers use an explicit envelope and paths into its value, never inline tags
that could be confused with a task's literal metric. This is transport only;
native grading and selection still receive the original numeric values.
"""
from __future__ import annotations

import json
import math

_MARKER = "sforge_nonfinite_json"
_KINDS = {"positive_infinity": "inf", "negative_infinity": "-inf", "nan": "nan"}


def encode_document(document, *, fields=None):
"""Return a JSON-safe document; optional fields restrict numeric encoding."""
if not isinstance(document, dict):
raise ValueError("SForge result transport requires an object")
values = []

def visit(value, path):
if isinstance(value, float) and not math.isfinite(value):
if fields is not None and path[0] not in fields:
raise ValueError("Nonfinite number outside released result fields")
kind = "nan" if math.isnan(value) else (
"positive_infinity" if value > 0 else "negative_infinity")
values.append({"path": path, "kind": kind})
return None
if isinstance(value, dict):
pairs = {}
for key, item in value.items():
# Follow JSON's existing conversion of scalar object keys.
if not isinstance(key, str):
key = next(iter(json.loads(json.dumps({key: None}, allow_nan=False))))
if key in pairs:
raise ValueError("SForge result keys collide in JSON")
pairs[key] = item
# Stable path order also keeps capture bindings independent of dict order.
return {key: visit(item, [*path, key]) for key, item in sorted(pairs.items())}
if isinstance(value, (list, tuple)):
return [visit(item, [*path, index]) for index, item in enumerate(value)]
return value

value = visit(document, [])
if not values and _MARKER not in document:
return document
# Escape a literal root marker as data, even when it contains only finite
# numbers. Decoding unwraps exactly once; nested marker-shaped values stay data.
return {_MARKER: 1, "value": value, "nonfinite_values": values}


def decode_document(document, *, fields=None):
"""Decode one envelope from a strictly parsed JSON document."""
if not isinstance(document, dict):
raise ValueError("SForge result transport requires an object")
if _MARKER not in document:
return document
if (set(document) != {_MARKER, "value", "nonfinite_values"}
or type(document[_MARKER]) is not int or document[_MARKER] != 1
or not isinstance(document["value"], dict)):
raise ValueError("Invalid SForge nonfinite transport envelope")
value, entries = document["value"], document["nonfinite_values"]
if not isinstance(entries, list) or (not entries and _MARKER not in value):
raise ValueError("SForge nonfinite transport requires value paths")

def lookup(container, key):
if isinstance(container, dict) and isinstance(key, str) and key in container:
return container[key]
if isinstance(container, list) and type(key) is int and 0 <= key < len(container):
return container[key]
raise ValueError("SForge nonfinite value path is unavailable")

seen = set()
for entry in entries:
if not isinstance(entry, dict) or set(entry) != {"path", "kind"}:
raise ValueError("Invalid SForge nonfinite value")
path, kind = entry["path"], entry["kind"]
if (not isinstance(path, list) or not path
or not all(type(key) is str or (type(key) is int and key >= 0) for key in path)
or not isinstance(kind, str) or kind not in _KINDS
or (fields is not None and path[0] not in fields)):
raise ValueError("Invalid SForge nonfinite value path or kind")
identity = tuple(path)
if identity in seen:
raise ValueError("Duplicate SForge nonfinite value path")
seen.add(identity)
container = value
for key in path[:-1]:
container = lookup(container, key)
if lookup(container, path[-1]) is not None:
raise ValueError("SForge nonfinite value must replace null")
container[path[-1]] = float(_KINDS[kind])
return value


def dumps(document, *, fields=None, **options):
return json.dumps(encode_document(document, fields=fields), allow_nan=False, **options)


def loads(encoded, *, fields=None):
def finite_number(text):
value = float(text)
if not math.isfinite(value):
raise ValueError("Unmarked nonfinite SForge result number")
return value

def unique_object(pairs):
value = {}
for key, item in pairs:
if key in value:
raise ValueError("Duplicate SForge result object key")
value[key] = item
return value

document = json.loads(encoded, parse_float=finite_number, parse_constant=finite_number,
object_pairs_hook=unique_object)
return decode_document(document, fields=fields)
Loading
Loading