Files
memory-gateway/memory_gateway/evermemos_service.py
2026-05-05 16:18:31 +08:00

109 lines
3.4 KiB
Python

"""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()