* feat: HindsightEmbedded python SDK * feat: HindsightEmbedded python SDK * fixes * improve * ci * improvemnts * fix test * fix test * fix: update tests to use Pydantic model attributes instead of dict access - Fixed test_server_integration.py to access Pydantic model attributes directly - Changed dict-style access (response["field"]) to attribute access (response.field) - Fixed .get() calls on Pydantic models - Updated recall() calls to access .results attribute - Updated reflect() calls to access .text attribute - Fixed test_list_banks to use namespace API instead of deleted default_api - Fixed attribute shadowing in HindsightClient wrapper (renamed _*_api to _*_namespace) * fix: add list() method to BanksAPI namespace * fix: remove leftover async cleanup code from test_list_banks * docs: remove Advanced Configuration section from embed.md
170 lines
5.5 KiB
Python
170 lines
5.5 KiB
Python
"""Test that API namespaces ensure daemon is started before each call."""
|
|
|
|
from unittest.mock import Mock, patch
|
|
|
|
import pytest
|
|
|
|
from hindsight import HindsightEmbedded
|
|
|
|
|
|
@pytest.fixture
|
|
def embedded_client():
|
|
"""Create an embedded client for testing."""
|
|
return HindsightEmbedded(
|
|
profile="test",
|
|
llm_provider="openai",
|
|
llm_api_key="test-key",
|
|
)
|
|
|
|
|
|
def test_banks_create_ensures_daemon_started(embedded_client):
|
|
"""Test that banks.create() calls _ensure_started()."""
|
|
# Mock _ensure_started to track calls
|
|
with patch.object(embedded_client, "_ensure_started") as mock_ensure:
|
|
# Mock the underlying client to avoid actual API call
|
|
mock_client = Mock()
|
|
embedded_client._client = mock_client
|
|
|
|
# Call namespace method
|
|
try:
|
|
embedded_client.banks.create(bank_id="test", name="Test Bank")
|
|
except Exception:
|
|
pass # We don't care if the actual call fails
|
|
|
|
# Verify _ensure_started was called
|
|
mock_ensure.assert_called_once()
|
|
|
|
|
|
def test_mental_models_list_ensures_daemon_started(embedded_client):
|
|
"""Test that mental_models.list() calls _ensure_started()."""
|
|
with patch.object(embedded_client, "_ensure_started") as mock_ensure:
|
|
mock_client = Mock()
|
|
embedded_client._client = mock_client
|
|
|
|
try:
|
|
embedded_client.mental_models.list(bank_id="test")
|
|
except Exception:
|
|
pass
|
|
|
|
mock_ensure.assert_called_once()
|
|
|
|
|
|
def test_directives_list_ensures_daemon_started(embedded_client):
|
|
"""Test that directives.list() calls _ensure_started()."""
|
|
with patch.object(embedded_client, "_ensure_started") as mock_ensure:
|
|
mock_client = Mock()
|
|
embedded_client._client = mock_client
|
|
|
|
try:
|
|
embedded_client.directives.list(bank_id="test")
|
|
except Exception:
|
|
pass
|
|
|
|
mock_ensure.assert_called_once()
|
|
|
|
|
|
def test_memories_list_ensures_daemon_started(embedded_client):
|
|
"""Test that memories.list() calls _ensure_started()."""
|
|
with patch.object(embedded_client, "_ensure_started") as mock_ensure:
|
|
mock_client = Mock()
|
|
embedded_client._client = mock_client
|
|
|
|
try:
|
|
embedded_client.memories.list(bank_id="test")
|
|
except Exception:
|
|
pass
|
|
|
|
mock_ensure.assert_called_once()
|
|
|
|
|
|
def test_multiple_calls_ensure_daemon_each_time(embedded_client):
|
|
"""Test that each namespace call ensures daemon is started."""
|
|
with patch.object(embedded_client, "_ensure_started") as mock_ensure:
|
|
mock_client = Mock()
|
|
embedded_client._client = mock_client
|
|
|
|
# Make multiple calls
|
|
try:
|
|
embedded_client.banks.create(bank_id="test", name="Test")
|
|
except Exception:
|
|
pass
|
|
|
|
try:
|
|
embedded_client.mental_models.list(bank_id="test")
|
|
except Exception:
|
|
pass
|
|
|
|
try:
|
|
embedded_client.directives.list(bank_id="test")
|
|
except Exception:
|
|
pass
|
|
|
|
# Should be called 3 times (once per namespace method call)
|
|
assert mock_ensure.call_count == 3
|
|
|
|
|
|
def test_daemon_restart_handling(embedded_client):
|
|
"""Test that namespace methods can recover from daemon crash."""
|
|
call_count = 0
|
|
|
|
def mock_ensure_started():
|
|
"""Mock that simulates daemon restart."""
|
|
nonlocal call_count
|
|
call_count += 1
|
|
# Create a new mock client each time (simulating daemon restart)
|
|
embedded_client._client = Mock()
|
|
embedded_client._started = True
|
|
|
|
with patch.object(embedded_client, "_ensure_started", side_effect=mock_ensure_started):
|
|
# First call - daemon starts
|
|
embedded_client.banks.create(bank_id="test", name="Test")
|
|
assert call_count == 1
|
|
|
|
# Simulate daemon crash by clearing client
|
|
embedded_client._client = None
|
|
embedded_client._started = False
|
|
|
|
# Second call - daemon restarts
|
|
embedded_client.banks.create(bank_id="test", name="Test")
|
|
assert call_count == 2
|
|
|
|
|
|
def test_ensure_started_calls_manager(embedded_client):
|
|
"""Test that _ensure_started actually starts the daemon via manager."""
|
|
# Mock the manager
|
|
mock_manager = Mock()
|
|
mock_manager.ensure_running.return_value = True
|
|
mock_manager.get_url.return_value = "http://localhost:54321"
|
|
|
|
embedded_client._manager = mock_manager
|
|
|
|
# Mock Hindsight client constructor
|
|
with patch("hindsight.embedded.Hindsight") as mock_hindsight_class:
|
|
mock_client = Mock()
|
|
mock_hindsight_class.return_value = mock_client
|
|
|
|
# Call _ensure_started
|
|
embedded_client._ensure_started()
|
|
|
|
# Verify manager was called
|
|
mock_manager.ensure_running.assert_called_once_with(
|
|
embedded_client.config, embedded_client.profile
|
|
)
|
|
mock_manager.get_url.assert_called_once_with(embedded_client.profile)
|
|
|
|
# Verify Hindsight client was created
|
|
mock_hindsight_class.assert_called_once_with(base_url="http://localhost:54321")
|
|
|
|
|
|
def test_namespace_singleton_behavior(embedded_client):
|
|
"""Test that namespace properties return the same instance."""
|
|
banks1 = embedded_client.banks
|
|
banks2 = embedded_client.banks
|
|
|
|
# Should be the same instance
|
|
assert banks1 is banks2
|
|
|
|
# Same for other namespaces
|
|
assert embedded_client.mental_models is embedded_client.mental_models
|
|
assert embedded_client.directives is embedded_client.directives
|
|
assert embedded_client.memories is embedded_client.memories
|