Skip to content

Commit 8d4811c

Browse files
committed
Make concurrent tool result archives atomic
1 parent 1b658bd commit 8d4811c

2 files changed

Lines changed: 23 additions & 2 deletions

File tree

‎rlm/v100/research_tools.py‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -841,16 +841,22 @@ def fetch(url: str) -> dict:
841841

842842
def bounded_tool_result(root: Path, result: dict, limit: int = 4096) -> str:
843843
"""Keep complete evidence on disk, exposing explicitly incomplete previews."""
844-
encoded = json.dumps(result, ensure_ascii=False, sort_keys=True)
844+
encoded = json.dumps(result, ensure_ascii=False, sort_keys=True, allow_nan=False)
845845
raw = encoded.encode("utf-8")
846846
if len(raw) <= limit:
847847
return encoded
848+
from rlm.v100.common import atomic_json
849+
850+
ordered = json.loads(encoded)
851+
raw = (json.dumps(ordered, ensure_ascii=False, indent=2, allow_nan=False) + "\n").encode(
852+
"utf-8"
853+
)
848854
digest = hashlib.sha256(raw).hexdigest()
849855
folder = root / "research/tool-results"
850856
folder.mkdir(parents=True, exist_ok=True)
851857
path = folder / f"{digest}.json"
852858
if not path.exists():
853-
path.write_bytes(raw)
859+
atomic_json(path, ordered)
854860
if hashlib.sha256(path.read_bytes()).hexdigest() != digest:
855861
raise ValueError("Archived tool result changed")
856862
return json.dumps(

‎tests/test_v100_research_context.py‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,18 @@ def request(endpoint, payload):
7171
assert generated[0]["max_tokens"] == 128
7272
assert "large" not in generated[1]["messages"][-1]["content"]
7373
assert client.sampling_args == original
74+
75+
76+
def test_parallel_workers_archive_identical_evidence_without_partial_reads(tmp_path):
77+
from concurrent.futures import ThreadPoolExecutor
78+
79+
result = {"rows": [{"id": i, "text": "evidence" * 300} for i in range(20)]}
80+
with ThreadPoolExecutor(max_workers=6) as workers:
81+
previews = list(
82+
workers.map(
83+
lambda _: json.loads(research_tools.bounded_tool_result(tmp_path, result, 2048)),
84+
range(24),
85+
)
86+
)
87+
assert len({row["full_result_sha256"] for row in previews}) == 1
88+
assert len(list((tmp_path / "research/tool-results").glob("*.json"))) == 1

0 commit comments

Comments
 (0)