fix(api_server): persist ResponseStore to SQLite across restarts (#2472)

The /v1/responses endpoint used an in-memory OrderedDict that lost
all conversation state on gateway restart. Replace with SQLite-backed
storage at ~/.hermes/response_store.db.

- Responses and conversation name mappings survive restarts
- Same LRU eviction behavior (configurable max_size)
- WAL mode for concurrent read performance
- Falls back to in-memory SQLite if disk path unavailable
- Conversation name→response_id mapping moved into the store
This commit is contained in:
Teknium 2026-03-22 04:56:06 -07:00 committed by GitHub
parent fd32e3d6e8
commit 8d528e0045
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 92 additions and 26 deletions

View file

@ -1295,7 +1295,7 @@ class TestConversationParameter:
data = await resp.json()
assert data["status"] == "completed"
# Conversation mapping should be set
assert "my-chat" in adapter._conversations
assert adapter._response_store.get_conversation("my-chat") is not None
@pytest.mark.asyncio
async def test_conversation_chains_automatically(self, adapter):
@ -1369,7 +1369,7 @@ class TestConversationParameter:
await cli.post("/v1/responses", json={"input": "conv-b msg", "conversation": "conv-b"})
# They should have different response IDs in the mapping
assert adapter._conversations["conv-a"] != adapter._conversations["conv-b"]
assert adapter._response_store.get_conversation("conv-a") != adapter._response_store.get_conversation("conv-b")
@pytest.mark.asyncio
async def test_conversation_store_false_no_mapping(self, adapter):
@ -1388,4 +1388,4 @@ class TestConversationParameter:
})
assert resp.status == 200
# Conversation mapping should NOT be set since store=false
assert "ephemeral-chat" not in adapter._conversations
assert adapter._response_store.get_conversation("ephemeral-chat") is None