II.
Page JSON
Structured · livepage:docs-hermes-research-raw-memory-provider-plugin
Memory Provider Plugins json
Inspect the normalized record payload exactly as the atlas UI reads it.
{
"id": "page:docs-hermes-research-raw-memory-provider-plugin",
"_kind": "Page",
"_file": "wiki/docs/hermes-research/raw/memory-provider-plugin.md",
"_cluster": "wiki",
"attributes": {
"nodeKind": "Page",
"sourcePath": "docs/hermes-research/raw/memory-provider-plugin.md",
"sourceKind": "repo-docs",
"title": "Memory Provider Plugins",
"displayName": "Memory Provider Plugins",
"slug": "docs/hermes-research/raw/memory-provider-plugin",
"articlePath": "wiki/docs/hermes-research/raw/memory-provider-plugin.md",
"article": "\n# Memory Provider Plugins\n\n> Source: https://hermes-agent.nousresearch.com/docs/developer-guide/memory-provider-plugin\n\n## Overview\n\nMemory provider plugins extend Hermes Agent with persistent, cross-session knowledge capabilities beyond the built-in MEMORY.md and USER.md files. These are \"provider plugin\" types that follow a single-select, config-driven pattern managed via `hermes plugins`.\n\n## Directory Structure\n\nMemory providers reside in `plugins/memory/<name>/`:\n\n```\nplugins/memory/my-provider/\n __init__.py # MemoryProvider implementation + register() entry point\n plugin.yaml # Metadata (name, description, hooks)\n cli.py # Optional: register_cli(subparser) -- CLI commands\n README.md # Setup instructions, config reference, tools\n```\n\n## Core Implementation\n\n### The MemoryProvider ABC\n\nPlugins implement `MemoryProvider` from `agent/memory_provider.py`:\n\n```python\nfrom agent.memory_provider import MemoryProvider\n\nclass MyMemoryProvider(MemoryProvider):\n @property\n def name(self) -> str:\n return \"my-provider\"\n\n def is_available(self) -> bool:\n \"\"\"Check if this provider can activate. NO network calls.\"\"\"\n return bool(os.environ.get(\"MY_API_KEY\"))\n\n def initialize(self, session_id: str, **kwargs) -> None:\n \"\"\"Called once at agent startup.\n kwargs always includes:\n hermes_home (str): Active HERMES_HOME path. Use for storage.\n \"\"\"\n self._api_key = os.environ.get(\"MY_API_KEY\", \"\")\n self._session_id = session_id\n```\n\n### Required Methods\n\n**Core Lifecycle:**\n\n| Method | When Called | Must Implement? |\n|--------|------------|-----------------|\n| `name` (property) | Always | **Yes** |\n| `is_available()` | Agent init, before activation | **Yes** -- no network calls |\n| `initialize(session_id, **kwargs)` | Agent startup | **Yes** |\n| `get_tool_schemas()` | After init, for tool injection | **Yes** |\n| `handle_tool_call(tool_name, args, **kwargs)` | When agent uses your tools | **Yes** (if you have tools) |\n\n**Config:**\n\n| Method | Purpose | Must Implement? |\n|--------|---------|-----------------|\n| `get_config_schema()` | Declare config fields for `hermes memory setup` | **Yes** |\n| `save_config(values, hermes_home)` | Write non-secret config to native location | **Yes** (unless env-var-only) |\n\n**Optional Hooks:**\n\n| Method | When Called | Use Case |\n|--------|------------|----------|\n| `system_prompt_block()` | System prompt assembly | Static provider info |\n| `prefetch(query, *, session_id=\"\")` | Before each API call | Return recalled context |\n| `queue_prefetch(query)` | After each turn | Pre-warm for next turn |\n| `sync_turn(user, assistant, *, session_id=\"\")` | After each completed turn | Persist conversation |\n| `on_session_end(messages)` | Conversation ends | Final extraction/flush |\n| `on_pre_compress(messages)` | Before context compression | Save insights before discard |\n| `on_memory_write(action, target, content)` | Built-in memory writes | Mirror to your backend |\n| `shutdown()` | Process exit | Clean up connections |\n\n## Configuration\n\n### Config Schema\n\nThe `get_config_schema()` method returns field descriptors for `hermes memory setup`:\n\n```python\ndef get_config_schema(self):\n return [\n {\n \"key\": \"api_key\",\n \"description\": \"My Provider API key\",\n \"secret\": True, # written to .env\n \"required\": True,\n \"env_var\": \"MY_API_KEY\", # explicit env var name\n \"url\": \"https://my-provider.com/keys\", # where to get it\n },\n {\n \"key\": \"region\",\n \"description\": \"Server region\",\n \"default\": \"us-east\",\n \"choices\": [\"us-east\", \"eu-west\", \"ap-south\"],\n },\n {\n \"key\": \"project\",\n \"description\": \"Project identifier\",\n \"default\": \"hermes\",\n },\n ]\n```\n\n**Best Practice:** Keep schemas minimal -- only prompt for essential settings (API keys, credentials). Document optional settings in config files referenced in README rather than adding to setup prompts.\n\n### Save Config\n\n```python\ndef save_config(self, values: dict, hermes_home: str) -> None:\n \"\"\"Write non-secret config to your native location.\"\"\"\n import json\n from pathlib import Path\n config_path = Path(hermes_home) / \"my-provider.json\"\n config_path.write_text(json.dumps(values, indent=2))\n```\n\nFor environment-variable-only providers, leave the default no-op.\n\n## Plugin Registration\n\n### Entry Point\n\n```python\ndef register(ctx) -> None:\n \"\"\"Called by the memory plugin discovery system.\"\"\"\n ctx.register_memory_provider(MyMemoryProvider())\n```\n\n### plugin.yaml\n\n```yaml\nname: my-provider\nversion: 1.0.0\ndescription: \"Short description of what this provider does.\"\nhooks:\n - on_session_end # list hooks you implement\n```\n\n## Threading Contract\n\nThe `sync_turn()` method **must be non-blocking**. Run backend latency work in daemon threads:\n\n```python\ndef sync_turn(self, user_content, assistant_content, *, session_id=\"\", messages=None):\n def _sync():\n try:\n self._api.ingest(user_content, assistant_content,\n session_id=session_id, messages=messages)\n except Exception as e:\n logger.warning(\"Sync failed: %s\", e)\n\n if self._sync_thread and self._sync_thread.is_alive():\n self._sync_thread.join(timeout=5.0)\n self._sync_thread = threading.Thread(target=_sync, daemon=True)\n self._sync_thread.start()\n```\n\nThe `messages` parameter includes OpenAI-style conversation context: user/assistant messages, tool calls, and results. Cloud providers must document which message parts transmit off-device.\n\n## Profile Isolation\n\nAll storage paths must use the `hermes_home` kwarg from `initialize()`:\n\n```python\n# CORRECT -- profile-scoped\nfrom hermes_constants import get_hermes_home\ndata_dir = get_hermes_home() / \"my-provider\"\n\n# WRONG -- shared across all profiles\ndata_dir = Path(\"~/.hermes/my-provider\").expanduser()\n```\n\n## Testing\n\n```python\nfrom agent.memory_manager import MemoryManager\n\nmgr = MemoryManager()\nmgr.add_provider(my_provider)\nmgr.initialize_all(session_id=\"test-1\", platform=\"cli\")\n\n# Test tool routing\nresult = mgr.handle_tool_call(\"my_tool\", {\"action\": \"add\", \"content\": \"test\"})\n\n# Test lifecycle\nmgr.sync_all(\"user msg\", \"assistant msg\")\nmgr.on_session_end([])\nmgr.shutdown_all()\n```\n\nReference test patterns: `tests/agent/test_memory_provider.py`, `tests/agent/test_memory_session_switch.py`, `tests/agent/test_memory_user_id.py`, `tests/run_agent/test_memory_provider_init.py`\n\n## CLI Commands\n\nMemory provider plugins can register custom CLI subcommands via convention-based discovery (no core file changes needed).\n\n### Implementation\n\n1. Add a `cli.py` file to the plugin directory\n2. Define a `register_cli(subparser)` function building the argparse tree\n3. The system discovers it at startup via `discover_plugin_cli_commands()`\n4. Commands appear under `hermes <provider-name> <subcommand>`\n\n**Active-provider gating:** CLI commands only appear when your provider is the active `memory.provider` in config.\n\n## Constraints\n\n**Single Provider Rule:** Only one external memory provider can be active at a time. Attempting to register a second triggers a MemoryManager warning, preventing tool schema bloat and backend conflicts.\n",
"documents": []
},
"outgoingEdges": [],
"incomingEdges": [
{
"from": "page:docs-hermes-research",
"to": "page:docs-hermes-research-raw-memory-provider-plugin",
"kind": "contains_page"
}
]
}