mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
fix(tools): enforce ID uniqueness in TODO store during replace operations
Deduplicate todo items by ID before writing to the store, keeping the last occurrence. Prevents ghost entries when the model sends duplicate IDs in a single write() call, which corrupts subsequent merge operations. Co-authored-by: WAXLYY <WAXLYY@users.noreply.github.com>
This commit is contained in:
parent
97b0cd51ee
commit
6d272ba477
2 changed files with 23 additions and 2 deletions
|
|
@ -24,6 +24,18 @@ class TestWriteAndRead:
|
|||
items[0]["content"] = "MUTATED"
|
||||
assert store.read()[0]["content"] == "Task"
|
||||
|
||||
def test_write_deduplicates_duplicate_ids(self):
|
||||
store = TodoStore()
|
||||
result = store.write([
|
||||
{"id": "1", "content": "First version", "status": "pending"},
|
||||
{"id": "2", "content": "Other task", "status": "pending"},
|
||||
{"id": "1", "content": "Latest version", "status": "in_progress"},
|
||||
])
|
||||
assert result == [
|
||||
{"id": "2", "content": "Other task", "status": "pending"},
|
||||
{"id": "1", "content": "Latest version", "status": "in_progress"},
|
||||
]
|
||||
|
||||
|
||||
class TestHasItems:
|
||||
def test_empty_store(self):
|
||||
|
|
|
|||
|
|
@ -46,11 +46,11 @@ class TodoStore:
|
|||
"""
|
||||
if not merge:
|
||||
# Replace mode: new list entirely
|
||||
self._items = [self._validate(t) for t in todos]
|
||||
self._items = [self._validate(t) for t in self._dedupe_by_id(todos)]
|
||||
else:
|
||||
# Merge mode: update existing items by id, append new ones
|
||||
existing = {item["id"]: item for item in self._items}
|
||||
for t in todos:
|
||||
for t in self._dedupe_by_id(todos):
|
||||
item_id = str(t.get("id", "")).strip()
|
||||
if not item_id:
|
||||
continue # Can't merge without an id
|
||||
|
|
@ -143,6 +143,15 @@ class TodoStore:
|
|||
|
||||
return {"id": item_id, "content": content, "status": status}
|
||||
|
||||
@staticmethod
|
||||
def _dedupe_by_id(todos: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
||||
"""Collapse duplicate ids, keeping the last occurrence in its position."""
|
||||
last_index: Dict[str, int] = {}
|
||||
for i, item in enumerate(todos):
|
||||
item_id = str(item.get("id", "")).strip() or "?"
|
||||
last_index[item_id] = i
|
||||
return [todos[i] for i in sorted(last_index.values())]
|
||||
|
||||
|
||||
def todo_tool(
|
||||
todos: Optional[List[Dict[str, Any]]] = None,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue