Elevate MiroFish/CrowdSight from single-container dev to a SaaS foundation: - Local memory backend (Zep-compatible): memory services/models, local graph builder + updater, AgentActivity seam, import-boundary isolation; Zep stays default, local is opt-in behind MEMORY_BACKEND. Semantic parity not yet proven. - Durable product persistence: projects/simulations/reports schema (migration 0007) + tenant/owner-scoped ProductRepository + dual-write + scoped_project read-first + ArtifactStore abstraction; durable JobQueue + worker.py. - SaaS hardening: durable RateLimiter (wired to login), UsageService (LLM accounting), redacted AuditService, idempotency, CORS allowlist, safe API errors, single-use PasswordResetService + endpoints (covers invite-pending). - Exactly 3 roles (super_admin/admin/user) with tenant authz policy. - Admin UI: GET/POST/PATCH /api/admin/users + GET/PUT /api/admin/settings (super-admin only, encrypted/masked); AdminView.vue + SettingsView.vue with admin/super-admin route guards, th/en i18n. - Production deploy topology: multi-stage Dockerfile (frontend build + gunicorn wsgi + nginx SPA-proxy + supervisord worker), backend/wsgi.py, gunicorn dep. Backend 197 passed; frontend 10 tests + build green. ruff unavailable (gap). No commit of credentials; secrets handled via env/.env.example. Deferred: Zep semantic A/B parity, object storage cutover, mobile QA, EasyPanel container build of deploy topology.
35 lines
1.1 KiB
Python
35 lines
1.1 KiB
Python
import builtins
|
|
|
|
from app.services.local_graph_memory_updater import LocalGraphMemoryUpdater
|
|
|
|
|
|
def test_local_activity_dict_path_does_not_import_zep_client(monkeypatch):
|
|
updater = object.__new__(LocalGraphMemoryUpdater)
|
|
captured = []
|
|
setattr(updater, "add_activity", captured.append)
|
|
|
|
original_import = builtins.__import__
|
|
|
|
def reject_zep_import(name, *args, **kwargs):
|
|
if name == "zep_cloud" or name.startswith("zep_cloud."):
|
|
raise AssertionError("local runtime imported the Zep client")
|
|
return original_import(name, *args, **kwargs)
|
|
|
|
monkeypatch.setattr(builtins, "__import__", reject_zep_import)
|
|
|
|
updater.add_activity_from_dict(
|
|
{
|
|
"agent_id": 7,
|
|
"agent_name": "Alice",
|
|
"action_type": "CREATE_POST",
|
|
"action_args": {"content": "Local memory is durable."},
|
|
"round": 2,
|
|
"timestamp": "2026-08-24T00:00:00+00:00",
|
|
},
|
|
"twitter",
|
|
)
|
|
|
|
assert len(captured) == 1
|
|
assert captured[0].__class__.__module__ == "app.services.memory_activity"
|
|
assert captured[0].to_episode_text().startswith("Alice:")
|