Compare commits

..

3 Commits

Author SHA1 Message Date
8202649ca0 fix(memory): add remove action to on_memory_write bridge
All checks were successful
Forge CI / smoke-and-build (pull_request) Successful in 43s
- Extend on_memory_write trigger in run_agent.py to fire for 'remove' action
- Holographic provider now handles 'replace' (re-adds content) and 'remove' (lowers trust on matching facts)
- Fixes orphaned facts when entries are deleted from built-in memory

Fixes #243
2026-04-10 15:31:45 -04:00
Alexander Whitestone
5a942d71a1 ci: add duplicate model check step to CI workflow
All checks were successful
Forge CI / smoke-and-build (pull_request) Successful in 49s
2026-04-08 08:16:00 -04:00
Alexander Whitestone
044f0f8951 ci: add check_no_duplicate_models.py - catches duplicate model IDs (#224) 2026-04-08 08:15:27 -04:00
4 changed files with 101 additions and 6 deletions

View File

@@ -47,6 +47,11 @@ jobs:
source .venv/bin/activate
python scripts/syntax_guard.py
- name: No duplicate models
run: |
source .venv/bin/activate
python scripts/check_no_duplicate_models.py
- name: Green-path E2E
run: |
source .venv/bin/activate

View File

@@ -241,13 +241,29 @@ class HolographicMemoryProvider(MemoryProvider):
self._auto_extract_facts(messages)
def on_memory_write(self, action: str, target: str, content: str) -> None:
"""Mirror built-in memory writes as facts."""
if action == "add" and self._store and content:
try:
"""Mirror built-in memory writes as facts.
- add: mirror new fact to holographic store
- replace: search for old content, update or re-add
- remove: lower trust on matching facts so they fade naturally
"""
if not self._store:
return
try:
if action == "add" and content:
category = "user_pref" if target == "user" else "general"
self._store.add_fact(content, category=category)
except Exception as e:
logger.debug("Holographic memory_write mirror failed: %s", e)
elif action == "replace" and content:
category = "user_pref" if target == "user" else "general"
self._store.add_fact(content, category=category)
elif action == "remove" and content:
# Lower trust on matching facts so they decay naturally
results = self._store.search_facts(content, limit=5)
for fact in results:
if content.strip().lower() in fact.get("content", "").lower():
self._store.update_fact(fact["fact_id"], trust=max(0.0, fact.get("trust", 0.5) - 0.4))
except Exception as e:
logger.debug("Holographic memory_write mirror failed: %s", e)
def shutdown(self) -> None:
self._store = None

View File

@@ -6086,7 +6086,7 @@ class AIAgent:
store=self._memory_store,
)
# Bridge: notify external memory provider of built-in memory writes
if self._memory_manager and function_args.get("action") in ("add", "replace"):
if self._memory_manager and function_args.get("action") in ("add", "replace", "remove"):
try:
self._memory_manager.on_memory_write(
function_args.get("action", ""),

View File

@@ -0,0 +1,74 @@
#!/usr/bin/env python3
"""CI check: ensure no duplicate model IDs exist in provider configs.
Catches the class of bugs where a rename introduces a duplicate entry
(e.g. PR #225 kimi-for-coding -> kimi-k2.5 when kimi-k2.5 already existed).
Runtime target: < 2 seconds.
"""
from __future__ import annotations
import sys
from pathlib import Path
# Allow running from repo root
REPO_ROOT = Path(__file__).parent.parent
sys.path.insert(0, str(REPO_ROOT))
def check_openrouter_models() -> list[str]:
"""Check OPENROUTER_MODELS for duplicate model IDs."""
try:
from hermes_cli.models import OPENROUTER_MODELS
except ImportError:
return []
errors = []
seen: dict[str, int] = {}
for i, (model_id, _desc) in enumerate(OPENROUTER_MODELS):
if model_id in seen:
errors.append(
f" OPENROUTER_MODELS: duplicate '{model_id}' "
f"(index {seen[model_id]} and {i})"
)
else:
seen[model_id] = i
return errors
def check_provider_models() -> list[str]:
"""Check _PROVIDER_MODELS for duplicate model IDs within each provider list."""
from hermes_cli.models import _PROVIDER_MODELS
errors = []
for provider, models in _PROVIDER_MODELS.items():
seen: dict[str, int] = {}
for i, model_id in enumerate(models):
if model_id in seen:
errors.append(
f" _PROVIDER_MODELS['{provider}']: duplicate '{model_id}' "
f"(index {seen[model_id]} and {i})"
)
else:
seen[model_id] = i
return errors
def main() -> int:
errors = []
errors.extend(check_openrouter_models())
errors.extend(check_provider_models())
if errors:
print(f"FAIL: {len(errors)} duplicate model(s) found:")
for e in errors:
print(e)
return 1
print("OK: no duplicate model entries")
return 0
if __name__ == "__main__":
raise SystemExit(main())