109 lines
3.4 KiB
Python
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()
|
|
|