Add The Biographer household member for user memory management: Memory Service (direct access layer): - src/core/memory_service.py for fast, LLM-free lookups - Profile, preference, and fact management - Session context with Redis caching - Steward integration via prefetch_context() The Biographer Agent: - src/agents/biographer/ package with PydanticAI agent - Discreet chronicler personality for privacy - Tools: recall_semantic, list_memories, store_insight, update_profile, update_preference, forget_memory - Registered with Household Registry on startup Steward Integration: - Memory context pre-fetch during analysis - Profile/preferences included in Butler note - Keyword-based context determination Also includes: - delegate_to_biographer() wrapper - 34 new tests (capability + memory service) - Version bump to 1.2.0 Documentation cleanup: - Removed obsolete PHASE2_COMPLETE.md, PHASE2_PLAN.md - Removed docs/library-desk-requirements.md - Moved ORCHESTRATION_SCENARIOS.md to project root 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
146 lines
4.9 KiB
Python
146 lines
4.9 KiB
Python
"""
|
|
Tests for Biographer capability registration.
|
|
"""
|
|
|
|
import pytest
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from src.agents.biographer.capability import (
|
|
BIOGRAPHER_CAPABILITY,
|
|
get_biographer_capability,
|
|
register_biographer,
|
|
unregister_biographer,
|
|
)
|
|
from src.core.household_registry import HouseholdCapability
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestBiographerCapability:
|
|
"""Tests for the Biographer capability definition."""
|
|
|
|
def test_capability_is_household_capability(self):
|
|
"""Test capability is correct type."""
|
|
assert isinstance(BIOGRAPHER_CAPABILITY, HouseholdCapability)
|
|
|
|
def test_capability_name(self):
|
|
"""Test capability has correct name."""
|
|
assert BIOGRAPHER_CAPABILITY.name == "biographer"
|
|
|
|
def test_capability_role(self):
|
|
"""Test capability has correct role."""
|
|
assert BIOGRAPHER_CAPABILITY.role == "The Biographer"
|
|
|
|
def test_capability_category(self):
|
|
"""Test capability is in context category."""
|
|
assert BIOGRAPHER_CAPABILITY.category == "context"
|
|
|
|
def test_capability_domains(self):
|
|
"""Test capability covers expected domains."""
|
|
domains = BIOGRAPHER_CAPABILITY.domains
|
|
|
|
assert "remember" in domains
|
|
assert "recall" in domains
|
|
assert "forget" in domains
|
|
assert "memory" in domains
|
|
assert "preferences" in domains
|
|
assert "profile" in domains
|
|
|
|
def test_capability_does_not_require_network(self):
|
|
"""Test capability does not require network access."""
|
|
assert BIOGRAPHER_CAPABILITY.requires_network is False
|
|
|
|
def test_capability_low_cost(self):
|
|
"""Test capability has low cost (vector search, minimal LLM)."""
|
|
assert BIOGRAPHER_CAPABILITY.cost == "low"
|
|
|
|
def test_get_biographer_capability(self):
|
|
"""Test getter returns same capability."""
|
|
cap = get_biographer_capability()
|
|
|
|
assert cap is BIOGRAPHER_CAPABILITY
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestBiographerRegistration:
|
|
"""Tests for Biographer registration functions."""
|
|
|
|
def test_register_biographer(self):
|
|
"""Test registering biographer with registry."""
|
|
mock_registry = MagicMock()
|
|
mock_registry.__contains__ = MagicMock(return_value=False)
|
|
|
|
with patch(
|
|
"src.agents.biographer.capability.get_household_registry",
|
|
return_value=mock_registry,
|
|
):
|
|
with patch(
|
|
"src.agents.biographer.capability.get_biographer_agent"
|
|
) as mock_get_agent:
|
|
mock_agent = MagicMock()
|
|
mock_get_agent.return_value = mock_agent
|
|
|
|
register_biographer()
|
|
|
|
mock_registry.register.assert_called_once()
|
|
call_kwargs = mock_registry.register.call_args[1]
|
|
|
|
assert call_kwargs["name"] == "biographer"
|
|
assert call_kwargs["capability"] is BIOGRAPHER_CAPABILITY
|
|
assert call_kwargs["agent"] is mock_agent
|
|
|
|
def test_register_biographer_already_registered(self):
|
|
"""Test registering when already registered does nothing."""
|
|
mock_registry = MagicMock()
|
|
mock_registry.__contains__ = MagicMock(return_value=True)
|
|
|
|
with patch(
|
|
"src.agents.biographer.capability.get_household_registry",
|
|
return_value=mock_registry,
|
|
):
|
|
register_biographer()
|
|
|
|
# Should not call register since already registered
|
|
mock_registry.register.assert_not_called()
|
|
|
|
def test_unregister_biographer(self):
|
|
"""Test unregistering biographer from registry."""
|
|
mock_registry = MagicMock()
|
|
|
|
with patch(
|
|
"src.agents.biographer.capability.get_household_registry",
|
|
return_value=mock_registry,
|
|
):
|
|
unregister_biographer()
|
|
|
|
mock_registry.unregister.assert_called_once_with("biographer")
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestCapabilityDescription:
|
|
"""Tests for capability description."""
|
|
|
|
def test_description_mentions_recall(self):
|
|
"""Test description mentions recall capabilities."""
|
|
desc = BIOGRAPHER_CAPABILITY.description.lower()
|
|
assert "recall" in desc
|
|
|
|
def test_description_mentions_record(self):
|
|
"""Test description mentions recording capability."""
|
|
desc = BIOGRAPHER_CAPABILITY.description.lower()
|
|
assert "record" in desc
|
|
|
|
def test_description_mentions_forget(self):
|
|
"""Test description mentions forget capability."""
|
|
desc = BIOGRAPHER_CAPABILITY.description.lower()
|
|
assert "forget" in desc
|
|
|
|
def test_description_mentions_profile(self):
|
|
"""Test description mentions profile updates."""
|
|
desc = BIOGRAPHER_CAPABILITY.description.lower()
|
|
assert "profile" in desc
|
|
|
|
def test_description_mentions_preferences(self):
|
|
"""Test description mentions preferences."""
|
|
desc = BIOGRAPHER_CAPABILITY.description.lower()
|
|
assert "preferences" in desc
|