Add memory management APIs for OpenViking: list, read, write, and delete memories

This commit is contained in:
2026-05-29 16:38:57 +08:00
parent 0ab2a35e16
commit 68b2513043
9 changed files with 578 additions and 3 deletions

View File

@ -5,6 +5,7 @@ from fastapi import APIRouter, Depends, HTTPException, Query, status
from .auth import verify_api_key
from .schemas import (
MemoryWriteRequest,
MessageIngestRequest,
ProfileRequest,
ResourceUploadRequest,
@ -80,6 +81,58 @@ async def delete_resource(
raise user_auth_error(exc) from exc
@router.get("/memories")
async def list_memories(
user_id: str = Query(min_length=1),
user_key: str = Query(min_length=1),
uri: str = Query(default="viking://user/memories", min_length=1),
recursive: bool = Query(default=True),
service: MemorySystemService = Depends(get_service),
):
try:
return await service.list_memories(user_id, user_key, uri=uri, recursive=recursive)
except PermissionError as exc:
raise user_auth_error(exc) from exc
@router.get("/memories/content")
async def read_memory(
user_id: str = Query(min_length=1),
user_key: str = Query(min_length=1),
uri: str = Query(min_length=1),
service: MemorySystemService = Depends(get_service),
):
try:
return await service.read_memory(user_id, user_key, uri)
except PermissionError as exc:
raise user_auth_error(exc) from exc
@router.post("/memories")
async def write_memory(
request: MemoryWriteRequest,
service: MemorySystemService = Depends(get_service),
):
try:
return await service.write_memory(request)
except PermissionError as exc:
raise user_auth_error(exc) from exc
@router.delete("/memories")
async def delete_memory(
user_id: str = Query(min_length=1),
user_key: str = Query(min_length=1),
uri: str = Query(min_length=1),
recursive: bool = Query(default=False),
service: MemorySystemService = Depends(get_service),
):
try:
return await service.delete_memory(user_id, user_key, uri, recursive=recursive)
except PermissionError as exc:
raise user_auth_error(exc) from exc
@router.post("/sessions/{session_id}/commit")
async def commit_session(
session_id: str,

View File

@ -190,6 +190,62 @@ class OpenVikingMemorySystemClient:
response.raise_for_status()
return response.json()
async def list_memories(
self,
credential: OpenVikingCredential | str,
uri: str = "viking://user/memories",
recursive: bool = True,
) -> dict[str, Any]:
async with self._credential_client(credential) as client:
response = await client.get(
"/api/v1/fs/ls",
params={"uri": uri, "recursive": str(recursive).lower()},
)
response.raise_for_status()
return response.json()
async def read_memory(self, credential: OpenVikingCredential | str, uri: str) -> dict[str, Any]:
async with self._credential_client(credential) as client:
response = await client.get("/api/v1/content/read", params={"uri": uri})
response.raise_for_status()
return response.json()
async def write_memory(
self,
credential: OpenVikingCredential | str,
*,
uri: str,
content: str,
mode: str = "create",
wait: bool = True,
) -> dict[str, Any]:
async with self._credential_client(credential) as client:
response = await client.post(
"/api/v1/content/write",
json={
"uri": uri,
"content": content,
"mode": mode,
"wait": wait,
},
)
response.raise_for_status()
return response.json()
async def delete_memory(
self,
credential: OpenVikingCredential | str,
uri: str,
recursive: bool = False,
) -> dict[str, Any]:
async with self._credential_client(credential) as client:
response = await client.delete(
"/api/v1/fs",
params={"uri": uri, "recursive": str(recursive).lower()},
)
response.raise_for_status()
return response.json()
async def find(self, credential: OpenVikingCredential | str, query: str, limit: int) -> dict[str, Any]:
user_id = credential.user_id if isinstance(credential, OpenVikingCredential) else None
target_uri = f"viking://user/{user_id}/memories/" if user_id else "viking://user/memories/"

View File

@ -7,6 +7,7 @@ from pydantic import BaseModel, Field
OperationStatus = Literal["success", "partial_success", "failed"]
MemoryWriteMode = Literal["create", "replace", "append"]
class MessageIngestRequest(BaseModel):
@ -54,6 +55,15 @@ class ProfileRequest(BaseModel):
level: int = Field(default=2, ge=0)
class MemoryWriteRequest(BaseModel):
user_id: str = Field(min_length=1)
user_key: str = Field(min_length=1)
uri: str = Field(min_length=1)
content: str
mode: MemoryWriteMode = "create"
wait: bool = True
class ResourceUploadRequest(BaseModel):
user_id: str = Field(min_length=1)
user_key: str = Field(min_length=1)
@ -116,6 +126,12 @@ class ProfileResponse(BaseModel):
backends: dict[str, BackendStatus]
class MemoryOperationResponse(BaseModel):
status: OperationStatus
memory: Any = None
backends: dict[str, BackendStatus]
class ResourceMutationResponse(BaseModel):
status: OperationStatus
resource: Any = None

View File

@ -11,6 +11,8 @@ from .schemas import (
BackendStatus,
CommitResponse,
ExtractResponse,
MemoryOperationResponse,
MemoryWriteRequest,
MessageIngestRequest,
MessageIngestResponse,
ProfileResponse,
@ -76,6 +78,63 @@ class MemorySystemService:
resource = backends["openviking"].result if backends["openviking"].status == "success" else None
return ResourceMutationResponse(status=self._aggregate_status(backends), resource=resource, backends=backends)
async def list_memories(
self,
user_id: str,
user_key: str,
uri: str = "viking://user/memories",
recursive: bool = True,
) -> MemoryOperationResponse:
credential = self.openviking.credential_for_user(user_id, user_key)
backends = {
"openviking": await self._capture(lambda: self.openviking.list_memories(credential, uri, recursive)),
}
memory = backends["openviking"].result if backends["openviking"].status == "success" else None
return MemoryOperationResponse(status=self._aggregate_status(backends), memory=memory, backends=backends)
async def read_memory(
self,
user_id: str,
user_key: str,
uri: str,
) -> MemoryOperationResponse:
credential = self.openviking.credential_for_user(user_id, user_key)
backends = {
"openviking": await self._capture(lambda: self.openviking.read_memory(credential, uri)),
}
memory = backends["openviking"].result if backends["openviking"].status == "success" else None
return MemoryOperationResponse(status=self._aggregate_status(backends), memory=memory, backends=backends)
async def write_memory(self, request: MemoryWriteRequest) -> MemoryOperationResponse:
credential = self.openviking.credential_for_user(request.user_id, request.user_key)
backends = {
"openviking": await self._capture(
lambda: self.openviking.write_memory(
credential,
uri=request.uri,
content=request.content,
mode=request.mode,
wait=request.wait,
)
),
}
memory = backends["openviking"].result if backends["openviking"].status == "success" else None
return MemoryOperationResponse(status=self._aggregate_status(backends), memory=memory, backends=backends)
async def delete_memory(
self,
user_id: str,
user_key: str,
uri: str,
recursive: bool = False,
) -> MemoryOperationResponse:
credential = self.openviking.credential_for_user(user_id, user_key)
backends = {
"openviking": await self._capture(lambda: self.openviking.delete_memory(credential, uri, recursive)),
}
memory = backends["openviking"].result if backends["openviking"].status == "success" else None
return MemoryOperationResponse(status=self._aggregate_status(backends), memory=memory, backends=backends)
async def ingest_messages(self, request: MessageIngestRequest) -> MessageIngestResponse:
messages = self._messages_from_request(request)
if not messages: