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

@ -12,13 +12,14 @@ from .backend_contracts import (
BackendOperation,
BackendCommitResult,
BackendProducedRef,
BackendRetrieveResult,
BackendResultStatus,
BackendWriteResult,
CommitJob,
OutboxEvent,
OutboxEventStatus,
)
from .evermemos_client import EverMemOSClient
from .everos_client import EverOSClient
from .openviking_client import get_openviking_client
from .repositories import MetadataRepository, repository
from .schemas import AuditLog
@ -52,11 +53,11 @@ class MemoryGatewayV2Service:
self,
repo: MetadataRepository = repository,
openviking_client_factory: OpenVikingClientFactory = get_openviking_client,
evermemos_client: Any | None = None,
everos_client: Any | None = None,
) -> None:
self.repo = repo
self.openviking_client_factory = openviking_client_factory
self.evermemos_client = evermemos_client
self.everos_client = everos_client
async def ingest_conversation_turn(self, request: IngestRequest) -> IngestResponse:
normalized = self._normalize_ingest_request(request)
@ -92,9 +93,9 @@ class MemoryGatewayV2Service:
)
)
if normalized.policy.allow_evermemos:
if normalized.policy.allow_everos:
refs.append(
await self._write_evermemos_message(
await self._write_everos_message(
normalized,
payload,
gateway_id=gateway_id,
@ -108,7 +109,7 @@ class MemoryGatewayV2Service:
normalized,
gateway_id,
provenance_id,
BackendType.EVERMEMOS,
BackendType.EVEROS,
MemoryRefType.MESSAGE_MEMORY,
BackendRefStatus.SKIPPED,
content_hash=content_hash,
@ -188,8 +189,21 @@ class MemoryGatewayV2Service:
)
async def retrieve_context(self, request: RetrieveRequest) -> RetrieveResponse:
# TODO(v2): expand namespace ACL, fan out concurrently to OpenViking and
# EverMemOS, then apply lightweight merge/rerank before returning.
payload = {
"workspace_id": request.workspace_id,
"user_id": request.user_id,
"agent_id": request.agent_id,
"session_id": request.session_id,
"namespace": request.namespace,
"query": request.query,
"limit": request.limit,
"metadata": request.metadata,
}
results = [
await self._retrieve_openviking_context(payload),
await self._retrieve_everos_context(payload),
]
items = self._merge_retrieve_items(results, limit=request.limit)
refs = self.repo.list_memory_refs(
workspace_id=request.workspace_id,
user_id=request.user_id,
@ -198,21 +212,6 @@ class MemoryGatewayV2Service:
namespace=request.namespace,
limit=request.limit,
)
items = [
ContextItem(
text=None,
source_backend=ref.backend_type,
ref_id=ref.id,
score=0.0,
memory_type=ref.ref_type.value,
metadata={
"status": ref.status.value,
"native_id": ref.native_id,
"native_uri": ref.native_uri,
},
)
for ref in refs
]
trace_id = request.metadata.get("trace_id") if request.metadata else None
return RetrieveResponse(
status=OperationStatus.SUCCESS,
@ -220,7 +219,7 @@ class MemoryGatewayV2Service:
refs=self._view_refs(refs),
conflicts=[],
trace_id=trace_id,
metadata={"skeleton": True},
metadata=self._retrieve_metadata(results),
)
async def record_memory_feedback(self, request: FeedbackRequest) -> FeedbackResponse:
@ -386,6 +385,83 @@ class MemoryGatewayV2Service:
limit=limit,
)
async def _retrieve_openviking_context(self, payload: dict[str, Any]) -> BackendRetrieveResult:
try:
client = await self.openviking_client_factory()
if not hasattr(client, "retrieve_context_v2"):
return BackendRetrieveResult(
backend_type=BackendType.OPENVIKING,
status=BackendResultStatus.SKIPPED,
metadata={"reason": "adapter_method_missing"},
)
result = client.retrieve_context_v2(payload)
if hasattr(result, "__await__"):
result = await result
return result
except Exception as exc: # noqa: BLE001
return BackendRetrieveResult(
backend_type=BackendType.OPENVIKING,
status=BackendResultStatus.FAILED,
error_code="adapter_exception",
error_message=str(exc),
retryable=True,
)
async def _retrieve_everos_context(self, payload: dict[str, Any]) -> BackendRetrieveResult:
try:
client = self.everos_client or EverOSClient()
if not hasattr(client, "retrieve_context_v2"):
return BackendRetrieveResult(
backend_type=BackendType.EVEROS,
status=BackendResultStatus.SKIPPED,
metadata={"reason": "adapter_method_missing"},
)
result = client.retrieve_context_v2(payload)
if hasattr(result, "__await__"):
result = await result
return result
except Exception as exc: # noqa: BLE001
return BackendRetrieveResult(
backend_type=BackendType.EVEROS,
status=BackendResultStatus.FAILED,
error_code="adapter_exception",
error_message=str(exc),
retryable=True,
)
def _merge_retrieve_items(self, results: list[BackendRetrieveResult], limit: int) -> list[ContextItem]:
items: list[ContextItem] = []
for result in results:
if result.status != BackendResultStatus.SUCCESS:
continue
for item in result.items:
items.append(
ContextItem(
text=item.text,
source_backend=item.source_backend,
ref_id=item.ref_id,
score=item.score,
memory_type=item.memory_type,
metadata=item.metadata,
)
)
items.sort(key=lambda item: item.score, reverse=True)
return items[:limit]
def _retrieve_metadata(self, results: list[BackendRetrieveResult]) -> dict[str, Any]:
return {
"backend_results": [
{
"backend_type": result.backend_type.value,
"status": result.status.value,
"items": len(result.items),
"error_code": result.error_code,
"error_message": result.error_message,
}
for result in results
]
}
async def _execute_outbox_event(self, event: OutboxEvent) -> BackendCommitResult | BackendWriteResult:
payload = self._outbox_payload(event)
if event.operation != BackendOperation.COMMIT_SESSION:
@ -406,11 +482,11 @@ class MemoryGatewayV2Service:
)
result = await client.commit_session_v2(payload)
return result
if event.backend_type == BackendType.EVERMEMOS:
client = self.evermemos_client or EverMemOSClient()
if event.backend_type == BackendType.EVEROS:
client = self.everos_client or EverOSClient()
if not hasattr(client, "extract_profile_long_term_v2"):
return BackendCommitResult(
backend_type=BackendType.EVERMEMOS,
backend_type=event.backend_type,
operation=BackendOperation.COMMIT_SESSION,
status=BackendResultStatus.SKIPPED,
metadata={"reason": "adapter_method_missing"},
@ -557,7 +633,7 @@ class MemoryGatewayV2Service:
pass
if event.backend_type == BackendType.OPENVIKING:
return MemoryRefType.SESSION_ARCHIVE
if event.backend_type == BackendType.EVERMEMOS:
if event.backend_type == BackendType.EVEROS:
return MemoryRefType.LONG_TERM_MEMORY
return MemoryRefType.DRAFT_REVIEW
@ -712,7 +788,7 @@ class MemoryGatewayV2Service:
metadata=self._control_metadata(request, content_hash),
)
async def _write_evermemos_message(
async def _write_everos_message(
self,
request: IngestRequest,
payload: dict[str, Any],
@ -721,13 +797,13 @@ class MemoryGatewayV2Service:
content_hash: str,
) -> MemoryRef:
try:
client = self.evermemos_client or EverMemOSClient()
client = self.everos_client or EverOSClient()
if not hasattr(client, "ingest_message"):
return self._save_ref(
request,
gateway_id,
provenance_id,
BackendType.EVERMEMOS,
BackendType.EVEROS,
MemoryRefType.MESSAGE_MEMORY,
BackendRefStatus.SKIPPED,
content_hash=content_hash,
@ -740,7 +816,7 @@ class MemoryGatewayV2Service:
request,
gateway_id,
provenance_id,
BackendType.EVERMEMOS,
BackendType.EVEROS,
MemoryRefType.MESSAGE_MEMORY,
result,
content_hash,
@ -750,7 +826,7 @@ class MemoryGatewayV2Service:
request,
gateway_id,
provenance_id,
BackendType.EVERMEMOS,
BackendType.EVEROS,
MemoryRefType.MESSAGE_MEMORY,
BackendRefStatus.FAILED,
content_hash=content_hash,
@ -946,7 +1022,7 @@ class MemoryGatewayV2Service:
"idempotency_key": request.idempotency_key,
"request_id": request.request_id,
}
for backend_type in (BackendType.OPENVIKING, BackendType.EVERMEMOS):
for backend_type in (BackendType.OPENVIKING, BackendType.EVEROS):
event = OutboxEvent(
id=self._outbox_event_id(gateway_id, backend_type, BackendOperation.COMMIT_SESSION),
event_type="commit_session",