Add three reusable helpers to eliminate pervasive boilerplate:
tools/registry.py — tool_error() and tool_result():
Every tool handler returns JSON strings. The pattern
json.dumps({"error": msg}, ensure_ascii=False) appeared 106 times,
and json.dumps({"success": False, "error": msg}, ...) another 23.
Now: tool_error(msg) or tool_error(msg, success=False).
tool_result() handles arbitrary result dicts:
tool_result(success=True, data=payload) or tool_result(some_dict).
hermes_cli/config.py — read_raw_config():
Lightweight YAML reader that returns the raw config dict without
load_config()'s deep-merge + migration overhead. Available for
callsites that just need a single config value.
Migration (129 callsites across 32 files):
- tools/: browser_camofox (18), file_tools (10), homeassistant (8),
web_tools (7), skill_manager (7), cronjob (11), code_execution (4),
delegate (5), send_message (4), tts (4), memory (7), session_search (3),
mcp (2), clarify (2), skills_tool (3), todo (1), vision (1),
browser (1), process_registry (2), image_gen (1)
- plugins/memory/: honcho (9), supermemory (9), hindsight (8),
holographic (7), openviking (7), mem0 (7), byterover (6), retaindb (2)
- agent/: memory_manager (2), builtin_memory_provider (1)
115 lines
3.9 KiB
Python
115 lines
3.9 KiB
Python
"""BuiltinMemoryProvider — wraps MEMORY.md / USER.md as a MemoryProvider.
|
|
|
|
Always registered as the first provider. Cannot be disabled or removed.
|
|
This is the existing Hermes memory system exposed through the provider
|
|
interface for compatibility with the MemoryManager.
|
|
|
|
The actual storage logic lives in tools/memory_tool.py (MemoryStore).
|
|
This provider is a thin adapter that delegates to MemoryStore and
|
|
exposes the memory tool schema.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import logging
|
|
from typing import Any, Dict, List
|
|
|
|
from agent.memory_provider import MemoryProvider
|
|
from tools.registry import tool_error
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class BuiltinMemoryProvider(MemoryProvider):
|
|
"""Built-in file-backed memory (MEMORY.md + USER.md).
|
|
|
|
Always active, never disabled by other providers. The `memory` tool
|
|
is handled by run_agent.py's agent-level tool interception (not through
|
|
the normal registry), so get_tool_schemas() returns an empty list —
|
|
the memory tool is already wired separately.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
memory_store=None,
|
|
memory_enabled: bool = False,
|
|
user_profile_enabled: bool = False,
|
|
):
|
|
self._store = memory_store
|
|
self._memory_enabled = memory_enabled
|
|
self._user_profile_enabled = user_profile_enabled
|
|
|
|
@property
|
|
def name(self) -> str:
|
|
return "builtin"
|
|
|
|
def is_available(self) -> bool:
|
|
"""Built-in memory is always available."""
|
|
return True
|
|
|
|
def initialize(self, session_id: str, **kwargs) -> None:
|
|
"""Load memory from disk if not already loaded."""
|
|
if self._store is not None:
|
|
self._store.load_from_disk()
|
|
|
|
def system_prompt_block(self) -> str:
|
|
"""Return MEMORY.md and USER.md content for the system prompt.
|
|
|
|
Uses the frozen snapshot captured at load time. This ensures the
|
|
system prompt stays stable throughout a session (preserving the
|
|
prompt cache), even though the live entries may change via tool calls.
|
|
"""
|
|
if not self._store:
|
|
return ""
|
|
|
|
parts = []
|
|
if self._memory_enabled:
|
|
mem_block = self._store.format_for_system_prompt("memory")
|
|
if mem_block:
|
|
parts.append(mem_block)
|
|
if self._user_profile_enabled:
|
|
user_block = self._store.format_for_system_prompt("user")
|
|
if user_block:
|
|
parts.append(user_block)
|
|
|
|
return "\n\n".join(parts)
|
|
|
|
def prefetch(self, query: str, *, session_id: str = "") -> str:
|
|
"""Built-in memory doesn't do query-based recall — it's injected via system_prompt_block."""
|
|
return ""
|
|
|
|
def sync_turn(self, user_content: str, assistant_content: str, *, session_id: str = "") -> None:
|
|
"""Built-in memory doesn't auto-sync turns — writes happen via the memory tool."""
|
|
|
|
def get_tool_schemas(self) -> List[Dict[str, Any]]:
|
|
"""Return empty list.
|
|
|
|
The `memory` tool is an agent-level intercepted tool, handled
|
|
specially in run_agent.py before normal tool dispatch. It's not
|
|
part of the standard tool registry. We don't duplicate it here.
|
|
"""
|
|
return []
|
|
|
|
def handle_tool_call(self, tool_name: str, args: Dict[str, Any], **kwargs) -> str:
|
|
"""Not used — the memory tool is intercepted in run_agent.py."""
|
|
return tool_error("Built-in memory tool is handled by the agent loop")
|
|
|
|
def shutdown(self) -> None:
|
|
"""No cleanup needed — files are saved on every write."""
|
|
|
|
# -- Property access for backward compatibility --------------------------
|
|
|
|
@property
|
|
def store(self):
|
|
"""Access the underlying MemoryStore for legacy code paths."""
|
|
return self._store
|
|
|
|
@property
|
|
def memory_enabled(self) -> bool:
|
|
return self._memory_enabled
|
|
|
|
@property
|
|
def user_profile_enabled(self) -> bool:
|
|
return self._user_profile_enabled
|