Merge pull request #1280 from NousResearch/hermes/hermes-de3d4e49-pr944

fix: make session log writes reuse shared atomic JSON helper
This commit is contained in:
Teknium 2026-03-14 03:15:52 -07:00 committed by GitHub
commit 7901d863dd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 87 additions and 5 deletions

View file

@ -1,5 +1,6 @@
"""Shared fixtures for the hermes-agent test suite."""
import asyncio
import os
import signal
import sys
@ -59,6 +60,39 @@ def mock_config():
def _timeout_handler(signum, frame):
raise TimeoutError("Test exceeded 30 second timeout")
@pytest.fixture(autouse=True)
def _ensure_current_event_loop(request):
"""Provide a default event loop for sync tests that call get_event_loop().
Python 3.11+ no longer guarantees a current loop for plain synchronous tests.
A number of gateway tests still use asyncio.get_event_loop().run_until_complete(...).
Ensure they always have a usable loop without interfering with pytest-asyncio's
own loop management for @pytest.mark.asyncio tests.
"""
if request.node.get_closest_marker("asyncio") is not None:
yield
return
try:
loop = asyncio.get_event_loop_policy().get_event_loop()
except RuntimeError:
loop = None
created = loop is None or loop.is_closed()
if created:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
yield
finally:
if created and loop is not None:
try:
loop.close()
finally:
asyncio.set_event_loop(None)
@pytest.fixture(autouse=True)
def _enforce_test_timeout():
"""Kill any individual test that takes longer than 30 seconds."""

View file

@ -97,6 +97,17 @@ class TestAtomicJsonWrite:
text = target.read_text()
assert ' "a"' in text # 4-space indent
def test_accepts_json_dump_default_hook(self, tmp_path):
class CustomValue:
def __str__(self):
return "custom-value"
target = tmp_path / "custom_default.json"
atomic_json_write(target, {"value": CustomValue()}, default=str)
result = json.loads(target.read_text(encoding="utf-8"))
assert result == {"value": "custom-value"}
def test_unicode_content(self, tmp_path):
target = tmp_path / "unicode.json"
data = {"emoji": "🎉", "japanese": "日本語"}

View file

@ -1928,6 +1928,24 @@ class TestSafeWriter:
assert inner.getvalue() == "test"
class TestSaveSessionLogAtomicWrite:
def test_uses_shared_atomic_json_helper(self, agent, tmp_path):
agent.session_log_file = tmp_path / "session.json"
messages = [{"role": "user", "content": "hello"}]
with patch("run_agent.atomic_json_write", create=True) as mock_atomic_write:
agent._save_session_log(messages)
mock_atomic_write.assert_called_once()
call_args = mock_atomic_write.call_args
assert call_args.args[0] == agent.session_log_file
payload = call_args.args[1]
assert payload["session_id"] == agent.session_id
assert payload["messages"] == messages
assert call_args.kwargs["indent"] == 2
assert call_args.kwargs["default"] is str
# ===================================================================
# Anthropic adapter integration fixes
# ===================================================================