Coerce memory load numeric args

Normalize memory_load threshold and limit values from model calls before vector search so numeric strings from Responses tool arguments do not reach FAISS unchanged. Add a focused regression covering string threshold/limit values and document the memory plugin contract.

Alessandro committed Jun 30, 2026 at 16:35 UTC b07d142938420e5e658a97556dd841bdd2713e60
3 files changed +56
plugins/_memory/AGENTS.md
+1
@@ -16,6 +16,7 @@
16
17 - Keep memory scoped by configured subdirectory/context.
18 - Preserve embedding metadata needed to rebuild indexes safely.
19 +- `memory_load` accepts numeric `threshold` and `limit` values as native numbers or numeric strings and coerces them before vector search.
20 - Avoid storing transient action-history noise as durable memory.
21
22 ## Work Guidance
plugins/_memory/tools/memory_load.py
+7
@@ -8,6 +8,13 @@ DEFAULT_LIMIT = 10
8 class MemoryLoad(Tool):
9
10 async def execute(self, query="", threshold=DEFAULT_THRESHOLD, limit=DEFAULT_LIMIT, filter="", **kwargs):
11 + if threshold is None or threshold == "":
12 + threshold = DEFAULT_THRESHOLD
13 + if limit is None or limit == "":
14 + limit = DEFAULT_LIMIT
15 + threshold = float(threshold)
16 + limit = int(limit)
17 +
18 db = await Memory.get(self.agent)
19 docs = await db.search_similarity_threshold(query=query, limit=limit, threshold=threshold, filter=filter)
20
tests/test_tool_action_contracts.py
+48
@@ -548,6 +548,54 @@ def test_memory_forget_tool_imports_plugin_memory_load(monkeypatch):
548 ]
549
550
551 +def test_memory_load_coerces_numeric_string_args(monkeypatch):
552 + _install_tool_stub(monkeypatch)
553 + monkeypatch.syspath_prepend(str(Path.cwd()))
554 +
555 + class FakeDb:
556 + def __init__(self) -> None:
557 + self.calls = []
558 +
559 + async def search_similarity_threshold(self, **kwargs):
560 + self.calls.append(kwargs)
561 + return []
562 +
563 + fake_db = FakeDb()
564 +
565 + async def get_memory(_agent):
566 + return fake_db
567 +
568 + memory_stub = types.ModuleType("plugins._memory.helpers.memory")
569 + memory_stub.Memory = types.SimpleNamespace(get=get_memory)
570 + monkeypatch.setitem(sys.modules, "plugins._memory.helpers.memory", memory_stub)
571 +
572 + sys.modules.pop("plugins._memory.tools.memory_load", None)
573 + module = importlib.import_module("plugins._memory.tools.memory_load")
574 + tool = module.MemoryLoad(
575 + _FakeAgent(),
576 + "memory_load",
577 + None,
578 + {
579 + "query": "smoke test project context",
580 + "threshold": "0.7",
581 + "limit": "3",
582 + },
583 + "",
584 + None,
585 + )
586 +
587 + asyncio.run(tool.execute(**tool.args))
588 +
589 + assert fake_db.calls == [
590 + {
591 + "query": "smoke test project context",
592 + "threshold": 0.7,
593 + "limit": 3,
594 + "filter": "",
595 + }
596 + ]
597 +
598 +
599 def test_behaviour_adjustment_normalizes_duplicate_rules(monkeypatch):
600 _install_tool_stub(monkeypatch)
601 monkeypatch.syspath_prepend(str(Path.cwd()))