Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions examples/hermes-plugin/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,20 @@ OPENVIKING_ENDPOINT=http://127.0.0.1:1933
The default profile uses `~/.hermes/.env`. A named profile uses
`~/.hermes/profiles/<profile>/.env`. Start a new Hermes session after changing it.

## Capture and explicit retrieval without automatic recall

To keep conversation capture and explicit `viking_*` tools without injecting
historical context automatically, run this in the profile you want to change:

```bash
hermes config set memory.openviking.auto_recall false
```

The profile-scoped environment override is `OPENVIKING_AUTO_RECALL=false`.
This suppresses both session-start memory context and per-turn query recall
before recall-related client or network I/O. Turn capture, memory commits,
and explicit tools keep their existing behavior. The default is `true`.

## Quick Local model support

Quick Local copies Hermes's language model settings into its own server config.
Expand Down
4 changes: 4 additions & 0 deletions examples/hermes-plugin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ def _cfg_field(key: str, description: str, **extra) -> dict:
_cfg_field("account", "Advanced local identity override (leave blank for user API keys)"),
_cfg_field("user", "Advanced local user override (leave blank for user API keys)"),
_cfg_field("agent", "Optional peer ID for separate assistant context. Uses user memory when no peer is configured.", default=_DEFAULT_AGENT),
_cfg_field("auto_recall", "Inject historical context automatically; capture and explicit tools remain available when disabled", type="boolean", default=True),
_cfg_field("recall_scope", "Automatic recall: all peers (shared) or current sender (peer). Unset preserves existing behavior.",
type="string", choices=["shared", "peer"], default=None),
_cfg_field(
Expand Down Expand Up @@ -2001,6 +2002,9 @@ def system_prompt_block(self) -> str:

def prefetch(self, query: str, *, session_id: str = "") -> str:
"""Session-start memory block (once per session) + query recall."""
cfg, env = self._profile_config_and_env()
if not self._setting("auto_recall", cfg, env=env):
return ""
query_text = _derive_openviking_user_text(query).strip()
if not self._ensure_client():
return ""
Expand Down
49 changes: 49 additions & 0 deletions examples/hermes-plugin/tests/test_standalone.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import threading
from contextlib import contextmanager
from http.server import BaseHTTPRequestHandler, HTTPServer
from unittest.mock import Mock

import pytest

Expand Down Expand Up @@ -1354,3 +1355,51 @@ def test_runtime_spawn_uses_provider_home_under_another_profile(external_provide
assert spawned[0]["OPENAI_API_KEY"] == "owner-key"
assert (home_a / "logs/openviking-server.log").exists()
assert not (home_b / "logs/openviking-server.log").exists()


@pytest.mark.parametrize("query", ["", "Recall my preferences"])
@pytest.mark.parametrize("source", ["config", "env"])
def test_auto_recall_off_skips_all_prefetch_io(external_provider, monkeypatch, query, source):
home, provider, _, _ = external_provider("recall-off")
provider._hermes_home = str(home)
provider._hermes_home_bound = True
if source == "config":
provider.save_config({"auto_recall": False}, str(home))
else:
(home / ".env").write_text("OPENVIKING_AUTO_RECALL=false\n")
ensure_client = Mock(side_effect=AssertionError("automatic recall performed client I/O"))
monkeypatch.setattr(provider, "_ensure_client", ensure_client)
assert provider.prefetch(query, session_id="new-session") == ""
ensure_client.assert_not_called()


def test_auto_recall_default_preserves_both_context_blocks(external_provider, monkeypatch):
home, provider, _, _ = external_provider("recall-default")
provider._hermes_home = str(home)
provider._hermes_home_bound = True
monkeypatch.setattr(provider, "_ensure_client", lambda: True)
startup = Mock(return_value="profile context")
recall = Mock(return_value="query context")
monkeypatch.setattr(provider, "_session_start_memory_context", startup)
monkeypatch.setattr(provider, "_search_prefetch_context", recall)
assert provider.prefetch("Recall preferences", session_id="session") == (
"## OpenViking Context\nprofile context\n\nquery context"
)
startup.assert_called_once_with("session")
recall.assert_called_once_with("Recall preferences", session_id="session")


def test_auto_recall_off_preserves_explicit_tools_and_capture(external_provider, monkeypatch):
home, provider, _ = _live_provider(external_provider, monkeypatch, "capture-only")
schemas = provider.get_tool_schemas()
provider.save_config({"auto_recall": False}, str(home))
assert provider.prefetch("Remember preferences", session_id="live-sid") == ""
assert provider.get_tool_schemas() == schemas
provider._client.post.return_value = {"result": {"memories": [{"uri": "viking://user/test/memories/preference.md", "abstract": "A preference"}]}}
result = provider.handle_tool_call("viking_search", {"query": "preferences", "mode": "deep"})
assert json.loads(result)["results"][0]["abstract"] == "A preference"
assert any(c.args[0].endswith("/search") for c in provider._client.post.call_args_list)
_finish_turn(provider, user="A new preference")
captured = [c for c in provider._client.post.call_args_list if c.args[0].endswith("/messages/batch")]
assert captured
assert [m["role"] for m in captured[0].args[1]["messages"]] == ["user", "assistant"]