mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
feat: add persistent memory system + SQLite session store
Two-part implementation: Part A - Curated Bounded Memory: - New memory tool (tools/memory_tool.py) with MEMORY.md + USER.md stores - Character-limited (2200/1375 chars), § delimited entries - Frozen snapshot injected into system prompt at session start - Model manages pruning via replace/remove with substring matching - Usage indicator shown in system prompt header Part B - SQLite Session Store: - New hermes_state.py with SessionDB class, FTS5 full-text search - Gateway session.py rewritten to dual-write SQLite + legacy JSONL - Compression-triggered session splitting with parent_session_id chains - New session_search tool with Gemini Flash summarization of matched sessions - CLI session lifecycle (create on launch, close on exit) Also: - System prompt now cached per session, only rebuilt on compression (fixes prefix cache invalidation from date/time changes every turn) - Config version bumped to 3, hermes doctor checks for new artifacts - Disabled in batch_runner and RL environments
This commit is contained in:
parent
655303f2f1
commit
440c244cac
19 changed files with 2397 additions and 327 deletions
|
|
@ -87,6 +87,10 @@ from tools.browser_tool import (
|
|||
from tools.tts_tool import text_to_speech_tool, check_tts_requirements
|
||||
# Planning & task management tool
|
||||
from tools.todo_tool import todo_tool, check_todo_requirements, TODO_SCHEMA
|
||||
# Persistent memory tool
|
||||
from tools.memory_tool import memory_tool, check_memory_requirements, MEMORY_SCHEMA
|
||||
# Session search tool (past conversation recall with summarization)
|
||||
from tools.session_search_tool import session_search, check_session_search_requirements, SESSION_SEARCH_SCHEMA
|
||||
from toolsets import (
|
||||
get_toolset, resolve_toolset, resolve_multiple_toolsets,
|
||||
get_all_toolsets, get_toolset_names, validate_toolset,
|
||||
|
|
@ -183,6 +187,20 @@ TOOLSET_REQUIREMENTS = {
|
|||
"setup_url": None,
|
||||
"tools": ["todo"],
|
||||
},
|
||||
"memory": {
|
||||
"name": "Persistent Memory",
|
||||
"env_vars": [], # File-based, no external deps
|
||||
"check_fn": check_memory_requirements,
|
||||
"setup_url": None,
|
||||
"tools": ["memory"],
|
||||
},
|
||||
"session_search": {
|
||||
"name": "Session History Search",
|
||||
"env_vars": ["OPENROUTER_API_KEY"], # Needs summarizer model
|
||||
"check_fn": check_session_search_requirements,
|
||||
"setup_url": "https://openrouter.ai/keys",
|
||||
"tools": ["session_search"],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -936,6 +954,26 @@ def get_todo_tool_definitions() -> List[Dict[str, Any]]:
|
|||
return [{"type": "function", "function": TODO_SCHEMA}]
|
||||
|
||||
|
||||
def get_memory_tool_definitions() -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Get tool definitions for the persistent memory tool.
|
||||
|
||||
Returns:
|
||||
List[Dict]: List containing the memory tool definition compatible with OpenAI API
|
||||
"""
|
||||
return [{"type": "function", "function": MEMORY_SCHEMA}]
|
||||
|
||||
|
||||
def get_session_search_tool_definitions() -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Get tool definitions for the session history search tool.
|
||||
|
||||
Returns:
|
||||
List[Dict]: List containing the session_search tool definition compatible with OpenAI API
|
||||
"""
|
||||
return [{"type": "function", "function": SESSION_SEARCH_SCHEMA}]
|
||||
|
||||
|
||||
def get_send_message_tool_definitions():
|
||||
"""Tool definitions for cross-channel messaging."""
|
||||
return [
|
||||
|
|
@ -1093,6 +1131,14 @@ def get_all_tool_names() -> List[str]:
|
|||
if check_todo_requirements():
|
||||
tool_names.extend(["todo"])
|
||||
|
||||
# Persistent memory (always available)
|
||||
if check_memory_requirements():
|
||||
tool_names.extend(["memory"])
|
||||
|
||||
# Session history search
|
||||
if check_session_search_requirements():
|
||||
tool_names.extend(["session_search"])
|
||||
|
||||
# Cross-channel messaging (always available on messaging platforms)
|
||||
tool_names.extend(["send_message"])
|
||||
|
||||
|
|
@ -1150,6 +1196,10 @@ TOOL_TO_TOOLSET_MAP = {
|
|||
"send_message": "messaging_tools",
|
||||
# Planning & task management
|
||||
"todo": "todo_tools",
|
||||
# Persistent memory
|
||||
"memory": "memory_tools",
|
||||
# Session history search
|
||||
"session_search": "session_search_tools",
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1261,6 +1311,16 @@ def get_tool_definitions(
|
|||
for tool in get_todo_tool_definitions():
|
||||
all_available_tools_map[tool["function"]["name"]] = tool
|
||||
|
||||
# Persistent memory tool
|
||||
if check_memory_requirements():
|
||||
for tool in get_memory_tool_definitions():
|
||||
all_available_tools_map[tool["function"]["name"]] = tool
|
||||
|
||||
# Session history search tool
|
||||
if check_session_search_requirements():
|
||||
for tool in get_session_search_tool_definitions():
|
||||
all_available_tools_map[tool["function"]["name"]] = tool
|
||||
|
||||
# Cross-channel messaging (always available on messaging platforms)
|
||||
for tool in get_send_message_tool_definitions():
|
||||
all_available_tools_map[tool["function"]["name"]] = tool
|
||||
|
|
@ -2001,6 +2061,14 @@ def handle_function_call(
|
|||
elif function_name == "todo":
|
||||
return json.dumps({"error": "todo must be handled by the agent loop"})
|
||||
|
||||
# Memory tool -- handled by the agent loop (needs MemoryStore instance).
|
||||
elif function_name == "memory":
|
||||
return json.dumps({"error": "Memory is not available. It may be disabled in config or this environment."})
|
||||
|
||||
# Session search -- handled by the agent loop (needs SessionDB instance).
|
||||
elif function_name == "session_search":
|
||||
return json.dumps({"error": "Session search is not available. The session database may not be initialized."})
|
||||
|
||||
else:
|
||||
error_msg = f"Unknown function: {function_name}"
|
||||
print(f"❌ {error_msg}")
|
||||
|
|
@ -2090,6 +2158,18 @@ def get_available_toolsets() -> Dict[str, Dict[str, Any]]:
|
|||
"tools": ["todo"],
|
||||
"description": "Planning & task management: in-memory todo list for multi-step work",
|
||||
"requirements": []
|
||||
},
|
||||
"memory_tools": {
|
||||
"available": check_memory_requirements(),
|
||||
"tools": ["memory"],
|
||||
"description": "Persistent memory: bounded MEMORY.md + USER.md injected into system prompt",
|
||||
"requirements": []
|
||||
},
|
||||
"session_search_tools": {
|
||||
"available": check_session_search_requirements(),
|
||||
"tools": ["session_search"],
|
||||
"description": "Session history search: FTS5 search + Gemini Flash summarization of past conversations",
|
||||
"requirements": ["OPENROUTER_API_KEY", "~/.hermes/state.db"]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue