Replace EverMemOS with EverOS backend

This commit is contained in:
2026-05-13 17:56:50 +08:00
parent 0acee1ec6c
commit b226749c61
37 changed files with 1327 additions and 1986 deletions

View File

@ -1,12 +1,13 @@
"""Application services for the generic Memory Gateway v1 API."""
from __future__ import annotations
from dataclasses import dataclass, field
from datetime import datetime, timezone
from fastapi import HTTPException, status
from .config import get_config
from .evermemos_client import EverMemOSError, EverMemOSClient
from .everos_client import EverOSError, EverOSClient
from .namespace import can_access_memory, default_namespace_for_context, user_long_term_namespace, visible_namespaces
from .openviking_client import get_openviking_client
from .repositories import MetadataRepository, repository
@ -29,13 +30,23 @@ from .schemas import (
UserRecord,
Visibility,
)
from .workers.evermemos_worker import EverMemOSWorker
@dataclass
class ConsolidationResult:
session_id: str
episodes: int
candidates: list[MemoryRecord] = field(default_factory=list)
promoted: list[MemoryRecord] = field(default_factory=list)
duplicates: list[dict] = field(default_factory=list)
review_drafts: list[str] = field(default_factory=list)
conflicts: list[dict] = field(default_factory=list)
class MemoryGatewayService:
def __init__(self, repo: MetadataRepository = repository, evermemos_client: EverMemOSClient | None = None) -> None:
def __init__(self, repo: MetadataRepository = repository, everos_client: EverOSClient | None = None) -> None:
self.repo = repo
self.evermemos_client = evermemos_client
self.everos_client = everos_client
def create_user(self, request: CreateUserRequest) -> UserRecord:
user = UserRecord(
@ -204,10 +215,10 @@ class MemoryGatewayService:
session_id=session_id,
)
target_namespace = request.target_namespace or user_long_term_namespace(request.user_id)
config = get_config().evermemos
config = get_config().everos
if config.enabled:
try:
external_result = (self.evermemos_client or EverMemOSClient()).consolidate_session(
external_result = (self.everos_client or EverOSClient()).consolidate_session(
session_id=session_id,
ctx=ctx,
episodes=episodes,
@ -217,32 +228,29 @@ class MemoryGatewayService:
)
result = self._persist_external_consolidation(external_result, ctx, session_id)
backend = "external"
except EverMemOSError as exc:
except EverOSError as exc:
error = str(exc)
if not config.fallback_to_local:
self._audit(
"evermemos_commit_failed",
"session",
session_id,
actor_user_id=request.user_id,
actor_agent_id=request.agent_id,
decision="deny",
metadata={"error": error},
)
raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=f"EverMemOS failed: {error}") from exc
result = self._commit_session_locally(session_id, ctx, request)
backend = "local-fallback"
self._audit(
"everos_commit_failed",
"session",
session_id,
actor_user_id=request.user_id,
actor_agent_id=request.agent_id,
decision="deny",
metadata={"error": error},
)
raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=f"EverOS failed: {error}") from exc
else:
result = self._commit_session_locally(session_id, ctx, request)
backend = "local-disabled"
result = None
backend = "disabled"
else:
result = None
self._audit("commit_session", "session", session_id, actor_user_id=request.user_id, actor_agent_id=request.agent_id)
if not result:
return {"session_id": session_id, "episodes": len(episodes), "promoted": [], "evermemos_backend": backend}
return {"session_id": session_id, "episodes": len(episodes), "promoted": [], "everos_backend": backend}
return {
"evermemos_backend": backend,
"evermemos_error": error,
"everos_backend": backend,
"everos_error": error,
"session_id": session_id,
"episodes": result.episodes,
"candidates": result.candidates,
@ -252,24 +260,13 @@ class MemoryGatewayService:
"review_drafts": result.review_drafts,
}
def evermemos_health(self) -> dict:
config = get_config().evermemos
def everos_health(self) -> dict:
config = get_config().everos
if not config.enabled:
return {"status": "disabled", "url": config.url}
return (self.evermemos_client or EverMemOSClient()).health()
def _commit_session_locally(self, session_id: str, ctx: AccessContext, request: CommitSessionRequest):
worker = EverMemOSWorker(self.repo)
return worker.consolidate_session(
session_id=session_id,
ctx=ctx,
min_importance=request.min_importance,
target_namespace=request.target_namespace or user_long_term_namespace(request.user_id),
)
return (self.everos_client or EverOSClient()).health()
def _persist_external_consolidation(self, external_result: dict, ctx: AccessContext, session_id: str):
from .workers.evermemos_worker import ConsolidationResult
result = ConsolidationResult(
session_id=session_id,
episodes=external_result.get("episodes") or len(self.repo.list_session_episodes(session_id)),
@ -302,11 +299,11 @@ class MemoryGatewayService:
data.setdefault("memory_type", MemoryType.SUMMARY.value)
data.setdefault("content", data.get("text") or data.get("summary") or "")
data.setdefault("summary", data.get("content", "")[:180])
data.setdefault("tags", ["evermemos-external"])
data.setdefault("tags", ["everos-external"])
data.setdefault("importance", 0.7)
data.setdefault("confidence", 0.65)
data.setdefault("visibility", Visibility.PRIVATE.value)
data.setdefault("source", SourceType.EVERMEMOS.value)
data.setdefault("source", SourceType.EVEROS.value)
if not data["content"]:
return None
return MemoryRecord.model_validate(data)