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.
62 lines
2.9 KiB
Python
62 lines
2.9 KiB
Python
"""Business service exports with lazy imports for backend isolation.
|
|
|
|
The local memory backend must be importable without eagerly loading Zep-only
|
|
consumers. Legacy package-level names remain available through ``__getattr__``
|
|
when a caller explicitly requests them.
|
|
"""
|
|
|
|
from importlib import import_module
|
|
|
|
_EXPORTS = {
|
|
"OntologyGenerator": (".ontology_generator", "OntologyGenerator"),
|
|
"GraphBuilderService": (".graph_builder", "GraphBuilderService"),
|
|
"TextProcessor": (".text_processor", "TextProcessor"),
|
|
"ZepEntityReader": (".zep_entity_reader", "ZepEntityReader"),
|
|
"EntityNode": (".zep_entity_reader", "EntityNode"),
|
|
"FilteredEntities": (".zep_entity_reader", "FilteredEntities"),
|
|
"OasisProfileGenerator": (".oasis_profile_generator", "OasisProfileGenerator"),
|
|
"OasisAgentProfile": (".oasis_profile_generator", "OasisAgentProfile"),
|
|
"SimulationManager": (".simulation_manager", "SimulationManager"),
|
|
"SimulationState": (".simulation_manager", "SimulationState"),
|
|
"SimulationStatus": (".simulation_manager", "SimulationStatus"),
|
|
"SimulationConfigGenerator": (".simulation_config_generator", "SimulationConfigGenerator"),
|
|
"SimulationParameters": (".simulation_config_generator", "SimulationParameters"),
|
|
"AgentActivityConfig": (".simulation_config_generator", "AgentActivityConfig"),
|
|
"TimeSimulationConfig": (".simulation_config_generator", "TimeSimulationConfig"),
|
|
"EventConfig": (".simulation_config_generator", "EventConfig"),
|
|
"PlatformConfig": (".simulation_config_generator", "PlatformConfig"),
|
|
"SimulationRunner": (".simulation_runner", "SimulationRunner"),
|
|
"SimulationRunState": (".simulation_runner", "SimulationRunState"),
|
|
"RunnerStatus": (".simulation_runner", "RunnerStatus"),
|
|
"AgentAction": (".simulation_runner", "AgentAction"),
|
|
"RoundSummary": (".simulation_runner", "RoundSummary"),
|
|
"ZepGraphMemoryUpdater": (".zep_graph_memory_updater", "ZepGraphMemoryUpdater"),
|
|
"ZepGraphMemoryManager": (".zep_graph_memory_updater", "ZepGraphMemoryManager"),
|
|
"AgentActivity": (".memory_activity", "AgentActivity"),
|
|
"SimulationIPCClient": (".simulation_ipc", "SimulationIPCClient"),
|
|
"SimulationIPCServer": (".simulation_ipc", "SimulationIPCServer"),
|
|
"IPCCommand": (".simulation_ipc", "IPCCommand"),
|
|
"IPCResponse": (".simulation_ipc", "IPCResponse"),
|
|
"CommandType": (".simulation_ipc", "CommandType"),
|
|
"CommandStatus": (".simulation_ipc", "CommandStatus"),
|
|
}
|
|
|
|
|
|
def __getattr__(name: str):
|
|
try:
|
|
module_name, attribute_name = _EXPORTS[name]
|
|
except KeyError as exc:
|
|
raise AttributeError(f"module {__name__!r} has no attribute {name!r}") from exc
|
|
module = import_module(module_name, __name__)
|
|
value = getattr(module, attribute_name)
|
|
globals()[name] = value
|
|
return value
|
|
|
|
|
|
def __dir__() -> list[str]:
|
|
return sorted(set(globals()) | set(_EXPORTS))
|
|
|
|
|
|
__all__ = sorted(_EXPORTS)
|
|
|