"""Standalone EverMemOS-compatible consolidation service. This is a lightweight local service for POC use. It intentionally exposes the same HTTP contract that Memory Gateway calls: POST /v1/sessions/consolidate The service does not own Memory Gateway's metadata database. It receives episodes and existing memories in the request, returns candidate/promoted MemoryRecord payloads, and creates Obsidian review drafts for high-value or conflicting candidates. """ from __future__ import annotations import argparse import logging from typing import Any from fastapi import FastAPI from pydantic import BaseModel, Field from .config import load_config, set_config from .repositories import InMemoryRepository from .schemas import AccessContext, EpisodeRecord, MemoryRecord from .workers.evermemos_worker import EverMemOSWorker logger = logging.getLogger(__name__) class ConsolidateRequest(BaseModel): schema_version: str = "memory-gateway.evermemos.consolidate.v1" session_id: str context: dict[str, Any] min_importance: float = 0.6 target_namespace: str | None = None episodes: list[dict[str, Any]] = Field(default_factory=list) existing_memories: list[dict[str, Any]] = Field(default_factory=list) app = FastAPI(title="Local EverMemOS POC Service", version="0.1.0") @app.get("/health") async def health() -> dict[str, Any]: return { "status": "ok", "service": "evermemos-local", "version": "0.1.0", "contract": "memory-gateway.evermemos.consolidate.v1", } @app.post("/v1/sessions/consolidate") async def consolidate_session(request: ConsolidateRequest) -> dict[str, Any]: repo = InMemoryRepository() ctx = AccessContext.model_validate(request.context) for item in request.existing_memories: try: repo.upsert_memory(MemoryRecord.model_validate(item)) except Exception as exc: # noqa: BLE001 logger.warning("Skipping invalid existing memory: %s", exc) for item in request.episodes: try: repo.append_episode(EpisodeRecord.model_validate(item)) except Exception as exc: # noqa: BLE001 logger.warning("Skipping invalid episode: %s", exc) worker = EverMemOSWorker(repo) result = worker.consolidate_session( session_id=request.session_id, ctx=ctx, min_importance=request.min_importance, target_namespace=request.target_namespace, ) return { "status": "ok", "backend": "evermemos-local", "result": { "session_id": result.session_id, "episodes": result.episodes, "candidates": [memory.model_dump(mode="json") for memory in result.candidates], "promoted": [memory.model_dump(mode="json") for memory in result.promoted], "duplicates": result.duplicates, "conflicts": result.conflicts, "review_drafts": result.review_drafts, }, } def main() -> None: import uvicorn parser = argparse.ArgumentParser(description="Run the local EverMemOS POC service.") parser.add_argument("--config", default="config.yaml") parser.add_argument("--host", default="127.0.0.1") parser.add_argument("--port", type=int, default=1995) args = parser.parse_args() config = load_config(args.config) set_config(config) uvicorn.run(app, host=args.host, port=args.port, log_level=config.logging.level.lower()) if __name__ == "__main__": main()