Local customizations: vault injection (Layer 3) preserved after upstream update

This commit is contained in:
AJ 2026-04-22 17:23:54 -04:00
parent 4fade39c90
commit de1a3922ed
5 changed files with 495 additions and 2 deletions

View file

@ -0,0 +1,174 @@
"""Tests for agent/vault_injection.py — Obsidian vault auto-injection into system prompt."""
import pytest
import os
from pathlib import Path
from agent.vault_injection import (
build_vault_system_prompt,
_read_vault_file,
_strip_yaml_frontmatter,
WORKING_CONTEXT_CHAR_LIMIT,
USER_PROFILE_CHAR_LIMIT,
)
# ---------------------------------------------------------------------------
# _strip_yaml_frontmatter
# ---------------------------------------------------------------------------
class TestStripYamlFrontmatter:
def test_strips_simple_frontmatter(self):
content = "---\ndate: 2026-04-22\n---\nHello world"
assert _strip_yaml_frontmatter(content) == "Hello world"
def test_no_frontmatter(self):
content = "Just some text"
assert _strip_yaml_frontmatter(content) == "Just some text"
def test_frontmatter_with_blank_lines(self):
content = "---\ndate: 2026-04-22\nprojects: [X]\n---\n\nActual content here"
result = _strip_yaml_frontmatter(content)
assert result == "Actual content here"
def test_unclosed_frontmatter_returns_original(self):
content = "---\ndate: 2026-04-22\nNo closing dashes"
assert _strip_yaml_frontmatter(content) == content
# ---------------------------------------------------------------------------
# _read_vault_file
# ---------------------------------------------------------------------------
class TestReadVaultFile:
def test_reads_existing_file(self, tmp_path):
f = tmp_path / "test.md"
f.write_text("some content", encoding="utf-8")
result = _read_vault_file(f, 4000)
assert result == "some content"
def test_returns_none_for_missing_file(self, tmp_path):
f = tmp_path / "nonexistent.md"
result = _read_vault_file(f, 4000)
assert result is None
def test_returns_none_for_empty_file(self, tmp_path):
f = tmp_path / "empty.md"
f.write_text("", encoding="utf-8")
result = _read_vault_file(f, 4000)
assert result is None
def test_returns_none_for_whitespace_only(self, tmp_path):
f = tmp_path / "ws.md"
f.write_text(" \n\n ", encoding="utf-8")
result = _read_vault_file(f, 4000)
assert result is None
def test_strips_frontmatter(self, tmp_path):
f = tmp_path / "frontmatter.md"
f.write_text("---\ndate: 2026-04-22\n---\nReal content", encoding="utf-8")
result = _read_vault_file(f, 4000)
assert result == "Real content"
def test_truncates_long_file(self, tmp_path):
f = tmp_path / "long.md"
long_content = "x" * 5000
f.write_text(long_content, encoding="utf-8")
result = _read_vault_file(f, 100)
assert len(result) < 200 # truncation + notice
assert "truncated" in result
def test_truncation_at_newline(self, tmp_path):
f = tmp_path / "multiline.md"
lines = ["line " + str(i) for i in range(100)]
content = "\n".join(lines)
f.write_text(content, encoding="utf-8")
# Small limit, should truncate at a newline boundary
result = _read_vault_file(f, 50)
assert "truncated" in result
# Should not cut mid-line
for line in result.split("\n"):
if line and "truncated" not in line:
assert line.startswith("line")
# ---------------------------------------------------------------------------
# build_vault_system_prompt
# ---------------------------------------------------------------------------
class TestBuildVaultSystemPrompt:
def test_empty_path_returns_empty(self):
assert build_vault_system_prompt("") == ""
def test_nonexistent_path_returns_empty(self, tmp_path):
assert build_vault_system_prompt(str(tmp_path / "nope")) == ""
def test_empty_vault_dir_returns_empty(self, tmp_path):
assert build_vault_system_prompt(str(tmp_path)) == ""
def test_injects_working_context(self, tmp_path):
vault = tmp_path / "vault"
agent_dir = vault / "Agent-Hermes"
agent_dir.mkdir(parents=True)
wc = agent_dir / "working-context.md"
wc.write_text("---\ndate: 2026-04-22\n---\n## Current Status\n- Status: Active", encoding="utf-8")
result = build_vault_system_prompt(str(vault))
assert "VAULT: WORKING CONTEXT" in result
assert "Status: Active" in result
def test_injects_user_profile(self, tmp_path):
vault = tmp_path / "vault"
shared_dir = vault / "Agent-Shared"
shared_dir.mkdir(parents=True)
up = shared_dir / "user-profile.md"
up.write_text("# User Profile\n\nName: AJ", encoding="utf-8")
result = build_vault_system_prompt(str(vault))
assert "VAULT: USER PROFILE" in result
assert "Name: AJ" in result
def test_injects_both_files(self, tmp_path):
vault = tmp_path / "vault"
agent_dir = vault / "Agent-Hermes"
shared_dir = vault / "Agent-Shared"
agent_dir.mkdir(parents=True)
shared_dir.mkdir(parents=True)
(agent_dir / "working-context.md").write_text(
"---\ndate: 2026-04-22\n---\nWorking on X", encoding="utf-8"
)
(shared_dir / "user-profile.md").write_text(
"# User Profile\n\nName: AJ", encoding="utf-8"
)
result = build_vault_system_prompt(str(vault))
assert "VAULT: WORKING CONTEXT" in result
assert "VAULT: USER PROFILE" in result
assert "Working on X" in result
assert "Name: AJ" in result
def test_skips_empty_working_context(self, tmp_path):
vault = tmp_path / "vault"
agent_dir = vault / "Agent-Hermes"
shared_dir = vault / "Agent-Shared"
agent_dir.mkdir(parents=True)
shared_dir.mkdir(parents=True)
(agent_dir / "working-context.md").write_text("", encoding="utf-8")
(shared_dir / "user-profile.md").write_text("Name: AJ", encoding="utf-8")
result = build_vault_system_prompt(str(vault))
assert "VAULT: WORKING CONTEXT" not in result
assert "VAULT: USER PROFILE" in result
def test_format_matches_memory_block_style(self, tmp_path):
vault = tmp_path / "vault"
agent_dir = vault / "Agent-Hermes"
agent_dir.mkdir(parents=True)
(agent_dir / "working-context.md").write_text("Active task", encoding="utf-8")
result = build_vault_system_prompt(str(vault))
# Should use the same separator as memory_tool (═══)
assert "\u2550" in result # ═ character
assert "VAULT: WORKING CONTEXT" in result

View file

@ -0,0 +1,161 @@
"""Tests for vault auto-injection integration with _build_system_prompt.
Verifies that vault content appears in the system prompt when vault is
configured, and is absent otherwise.
"""
import os
import pytest
from pathlib import Path
from unittest.mock import MagicMock, patch
def _make_minimal_agent(**overrides):
"""Create a minimal AIAgent for testing, with vault attrs settable."""
from run_agent import AIAgent
with (
patch("run_agent.get_tool_definitions", return_value=[]),
patch("run_agent.check_toolset_requirements", return_value={}),
patch("run_agent.OpenAI"),
):
a = AIAgent(
api_key="test-k...7890",
base_url="https://openrouter.ai/api/v1",
quiet_mode=True,
skip_context_files=True,
skip_memory=True,
)
a.client = MagicMock()
# Apply overrides after creation
for k, v in overrides.items():
setattr(a, k, v)
return a
class TestVaultSystemPromptIntegration:
"""Test that _build_system_prompt injects vault content when configured."""
def test_vault_not_injected_when_disabled(self, tmp_path):
"""Vault content should not appear when vault_enabled=False."""
vault_dir = tmp_path / "vault"
agent_dir = vault_dir / "Agent-Hermes"
agent_dir.mkdir(parents=True)
(agent_dir / "working-context.md").write_text("Active task X", encoding="utf-8")
agent = _make_minimal_agent(
_vault_enabled=False,
_vault_path=str(vault_dir),
)
prompt = agent._build_system_prompt()
assert "VAULT: WORKING CONTEXT" not in prompt
assert "Active task X" not in prompt
def test_vault_injected_when_enabled(self, tmp_path):
"""Vault content should appear in system prompt when vault_enabled=True."""
vault_dir = tmp_path / "vault"
agent_dir = vault_dir / "Agent-Hermes"
shared_dir = vault_dir / "Agent-Shared"
agent_dir.mkdir(parents=True)
shared_dir.mkdir(parents=True)
(agent_dir / "working-context.md").write_text(
"---\ndate: 2026-04-22\n---\n## Status\nActive: vault fix",
encoding="utf-8",
)
(shared_dir / "user-profile.md").write_text(
"# User Profile\n\nName: Test User",
encoding="utf-8",
)
agent = _make_minimal_agent(
_vault_enabled=True,
_vault_path=str(vault_dir),
)
prompt = agent._build_system_prompt()
assert "VAULT: WORKING CONTEXT" in prompt
assert "Active: vault fix" in prompt
assert "VAULT: USER PROFILE" in prompt
assert "Name: Test User" in prompt
def test_vault_after_memory_blocks(self, tmp_path):
"""Vault injection should appear after Layer 1 memory blocks."""
# Set up memory files
mem_dir = tmp_path / "memories"
mem_dir.mkdir(parents=True)
(mem_dir / "MEMORY.md").write_text("Layer 1 memory note", encoding="utf-8")
# Set up vault files
vault_dir = tmp_path / "vault"
agent_dir = vault_dir / "Agent-Hermes"
agent_dir.mkdir(parents=True)
(agent_dir / "working-context.md").write_text("Vault content", encoding="utf-8")
# Create agent with memory enabled
from run_agent import AIAgent
from tools.memory_tool import MemoryStore
with (
patch("run_agent.get_tool_definitions", return_value=[]),
patch("run_agent.check_toolset_requirements", return_value={}),
patch("run_agent.OpenAI"),
patch(
"hermes_cli.config.load_config",
return_value={
"memory": {
"memory_enabled": True,
"user_profile_enabled": False,
"memory_char_limit": 2200,
"user_char_limit": 1375,
},
},
),
):
monkeypatch_env = {}
# Set HERMES_HOME so MemoryStore reads from tmp_path
os.environ["HERMES_HOME"] = str(tmp_path)
try:
a = AIAgent(
api_key="test-k...7890",
base_url="https://openrouter.ai/api/v1",
quiet_mode=True,
skip_context_files=True,
skip_memory=False,
)
a.client = MagicMock()
finally:
del os.environ["HERMES_HOME"]
a._vault_enabled = True
a._vault_path = str(vault_dir)
prompt = a._build_system_prompt()
mem_pos = prompt.find("MEMORY (your personal notes)")
vault_pos = prompt.find("VAULT: WORKING CONTEXT")
assert mem_pos > 0, "Layer 1 memory block not found in prompt"
assert vault_pos > 0, "Vault block not found in prompt"
assert mem_pos < vault_pos, "Vault should appear after Layer 1 memory"
def test_missing_vault_path_graceful(self, tmp_path):
"""Agent works fine even if vault path doesn't exist."""
agent = _make_minimal_agent(
_vault_enabled=True,
_vault_path="/nonexistent/vault/path",
)
# Should not crash
prompt = agent._build_system_prompt()
assert "VAULT:" not in prompt
def test_no_vault_config_graceful(self):
"""Agent works fine with no vault set (defaults)."""
agent = _make_minimal_agent(
_vault_enabled=False,
_vault_path="",
)
prompt = agent._build_system_prompt()
assert "VAULT:" not in prompt