feat: connect Shane voice sessions to memory
This commit is contained in:
1 parent
2a2a2ff768
commit
c940923539
15 files changed
+201
-97
No files matched your search
+2
-2
@@ -24,9 +24,9 @@ python -m pytest tests/ -v -m "not slow" # skip LLM/network-heavy tests
|
||||
|
||||
| File | Target | Notes |
|
||||
|------|--------|-------|
|
||||
| `test_web_ui.py` | nginx HTTPS UI (:8090) + token endpoint (:8091) | static assets, `/token` (direct + via nginx), `/livekit/` proxy |
|
||||
| `test_web_ui.py` | nginx HTTPS UI (:9178) + token endpoint (:8091) | static assets, `/token` (direct + via nginx), `/livekit/` proxy |
|
||||
| `test_llm_api.py` | Gemma LLM API directly | completion, tool calling, `enable_thinking=false` latency, streaming |
|
||||
| `test_mcp_tools.py` | MCP servers via `docker exec` | weather (Fahrenheit check), time, memory save/recall/list, skills save/recall/list — probe files are cleaned up |
|
||||
| `test_mcp_tools.py` | MCP servers via `docker exec` | weather/time, authenticated remote memory save/recall/delete, and local skills — probes are cleaned up |
|
||||
| `test_agent_integration.py` | running container | agent process alive, worker registered with LiveKit, all 4 supervised processes up, container health, LiveKit HTTP API |
|
||||
| `test_compaction.py` | `GemmaLLM._compact_context` in-container | compaction triggers >24 items, system prompt preserved, short context untouched |
|
||||
|
||||
|
||||
+14
-11
@@ -1,7 +1,7 @@
|
||||
"""Shared fixtures for the Hope voice-assistant test suite.
|
||||
|
||||
Tests run on the HOST machine against the live "voice" container:
|
||||
- HTTPS web UI -> https://localhost:8090 (self-signed cert, verify=False)
|
||||
- HTTPS web UI -> https://localhost:9178 (self-signed cert, verify=False)
|
||||
- Token server -> http://127.0.0.1:8091 (host network mode)
|
||||
- LLM API -> GEMMA_BASE_URL (default http://192.168.86.2:8023/v1)
|
||||
- In-container -> docker exec voice ...
|
||||
@@ -10,16 +10,20 @@ Tests run on the HOST machine against the live "voice" container:
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
CONTAINER = "voice"
|
||||
WEB_BASE_URL = os.environ.get("VOICE_WEB_URL", "https://localhost:8090")
|
||||
WEB_BASE_URL = os.environ.get("VOICE_WEB_URL", "https://localhost:9178")
|
||||
TOKEN_URL = os.environ.get("VOICE_TOKEN_URL", "http://127.0.0.1:8091/token")
|
||||
GEMMA_BASE_URL = os.environ.get("GEMMA_BASE_URL", "http://192.168.86.2:8023/v1").rstrip("/")
|
||||
GEMMA_MODEL = os.environ.get("GEMMA_MODEL", "gemma-4-e4b")
|
||||
COGNEE_MCP_URL = os.environ.get("COGNEE_MCP_URL", "http://192.168.86.2:8003/mcp")
|
||||
_users = json.loads((Path(__file__).parents[1] / "config" / "users.json").read_text())
|
||||
_shane_memory = _users["users"]["shane"]["memory"]
|
||||
MEMORY_MCP_URL = os.environ.get("MEMORY_MCP_URL", _shane_memory["url"])
|
||||
MEMORY_MCP_TOKEN = os.environ.get("MEMORY_MCP_TOKEN", _shane_memory["api_key"])
|
||||
|
||||
# Path to the agent's venv python inside the container (has mcp, livekit.agents)
|
||||
CONTAINER_PYTHON = "/opt/voice-agent/.venv/bin/python"
|
||||
@@ -138,12 +142,8 @@ asyncio.run(main())
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def cognee_client():
|
||||
"""Factory that calls tools on the remote Cognee MCP server (Streamable HTTP).
|
||||
|
||||
Returns (result_text, is_error). Uses the mcp client library from the
|
||||
agent venv inside the container.
|
||||
"""
|
||||
def memory_client():
|
||||
"""Call Shane's authenticated remote memory MCP server."""
|
||||
|
||||
def _call(tool_name: str, args: dict | None = None) -> tuple[str, bool]:
|
||||
code = f"""
|
||||
@@ -152,7 +152,10 @@ from mcp import ClientSession
|
||||
from mcp.client.streamable_http import streamablehttp_client
|
||||
|
||||
async def main():
|
||||
async with streamablehttp_client({COGNEE_MCP_URL!r}) as (read, write, _):
|
||||
async with streamablehttp_client(
|
||||
{MEMORY_MCP_URL!r},
|
||||
headers={{"Authorization": "Bearer " + {MEMORY_MCP_TOKEN!r}}},
|
||||
) as (read, write, _):
|
||||
async with ClientSession(read, write) as session:
|
||||
await session.initialize()
|
||||
result = await session.call_tool({tool_name!r}, {json.dumps(args or {})})
|
||||
@@ -167,7 +170,7 @@ asyncio.run(main())
|
||||
proc = docker_exec_python(code, timeout=120, check=False)
|
||||
if proc.returncode != 0:
|
||||
raise AssertionError(
|
||||
f"Cognee MCP call {tool_name} failed (rc={proc.returncode}): "
|
||||
f"Memory MCP call {tool_name} failed (rc={proc.returncode}): "
|
||||
f"stderr={proc.stderr[-800:]}"
|
||||
)
|
||||
data = json.loads(proc.stdout.strip().splitlines()[-1])
|
||||
|
||||
+25
-23
@@ -4,7 +4,9 @@ Each test spawns the real MCP server script inside the container and drives it
|
||||
with a JSON-RPC session using the mcp client library from the agent venv.
|
||||
"""
|
||||
|
||||
import json
|
||||
import re
|
||||
import uuid
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -46,30 +48,30 @@ def test_time_get(mcp_client_factory):
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_memory_remember_and_recall(cognee_client):
|
||||
"""Cognee: store a fact, then recall it."""
|
||||
probe = "zebra42 is the probe fact for the voice test suite"
|
||||
def test_memory_save_recall_and_delete(memory_client):
|
||||
"""Authenticated memory is durable and scoped to Shane's token."""
|
||||
marker = f"voice-probe-{uuid.uuid4().hex[:10]}"
|
||||
saved, err = memory_client(
|
||||
"memory_save",
|
||||
{
|
||||
"title": f"Voice integration probe {marker}",
|
||||
"body": f"{marker} is the remote memory integration probe.",
|
||||
"tags": ["test", "voice"],
|
||||
},
|
||||
)
|
||||
assert not err, f"memory_save failed: {saved}"
|
||||
note = json.loads(saved)
|
||||
|
||||
stored, err = cognee_client("remember", {"data": probe})
|
||||
assert not err, f"remember failed: {stored}"
|
||||
|
||||
recalled, err = cognee_client("recall", {"query": "zebra42"})
|
||||
assert not err
|
||||
assert "zebra42" in recalled
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_memory_forget(cognee_client):
|
||||
"""Cognee: store a fact, verify recall, then forget it."""
|
||||
probe = "purple giraffe77 is the forget probe"
|
||||
|
||||
stored, err = cognee_client("remember", {"data": probe})
|
||||
assert not err, f"remember failed: {stored}"
|
||||
|
||||
# Verify it's there
|
||||
recalled, err = cognee_client("recall", {"query": "purple giraffe77"})
|
||||
assert not err
|
||||
assert "giraffe77" in recalled
|
||||
try:
|
||||
recalled, err = memory_client("memory_recall", {"query": marker, "topK": 3})
|
||||
assert not err
|
||||
assert marker in recalled
|
||||
finally:
|
||||
deleted, err = memory_client(
|
||||
"delete_note",
|
||||
{"folder": note["folder"], "slug": note["slug"]},
|
||||
)
|
||||
assert not err, f"delete_note failed: {deleted}"
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Tests for the web UI served by nginx (HTTPS :8090) and the token endpoint."""
|
||||
"""Tests for the web UI served by nginx (HTTPS :9178) and the token endpoint."""
|
||||
|
||||
import json
|
||||
|
||||
@@ -54,6 +54,7 @@ def test_token_endpoint_direct():
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert "token" in data and data["token"].count(".") == 2 # JWT shape
|
||||
assert data["user"]["id"] == "shane"
|
||||
|
||||
|
||||
def test_token_endpoint_via_nginx(client):
|
||||
|
||||
Reference in new issue
Block a user