Files
microfish/backend/app/services/__init__.py
Kunthawat Greethong 8b84378fe1 feat: SaaS foundation for CrowdSight
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.
2026-08-31 13:05:21 +07:00

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)