add multimodal memory proxy and API logging

This commit is contained in:
2026-06-12 11:04:53 +08:00
parent 8afb460883
commit a29009dc07
12 changed files with 2229 additions and 33 deletions

View File

@ -1,10 +1,12 @@
from __future__ import annotations
import asyncio
import base64
import hashlib
import mimetypes
import secrets
import shutil
import time
import uuid
from pathlib import Path
from typing import Any
@ -28,6 +30,10 @@ def public_resource_uri(user_id: str, resource_id: str) -> str:
return f"resource://{user_id}/{resource_id}"
def current_timestamp_ms() -> int:
return int(time.time() * 1000)
def infer_content_type(filename: str | None, mime_type: str | None) -> str:
mime = (mime_type or mimetypes.guess_type(filename or "")[0] or "").lower()
suffix = Path(filename or "").suffix.lower()
@ -249,6 +255,7 @@ class MemoryGatewayService:
project_id: str,
filename: str,
) -> dict[str, Any]:
content_item = self._build_content_item(resource=resource, filename=filename)
return {
"session_id": resource["session_id"],
"app_id": app_id,
@ -257,23 +264,43 @@ class MemoryGatewayService:
{
"sender_id": user_id,
"role": "user",
"timestamp": 1781068800000,
"content": [
{
"type": resource["content_type"],
"uri": resource["uri"],
"name": filename,
"ext": Path(filename).suffix.lstrip(".") or None,
"extras": {
"resource_id": resource["id"],
"source": "user_upload",
},
}
],
"timestamp": current_timestamp_ms(),
"content": [content_item],
}
],
}
def _build_content_item(
self,
*,
resource: dict[str, Any],
filename: str,
) -> dict[str, Any]:
content_type = str(resource["content_type"])
path = self._resource_file_path(resource)
content = path.read_bytes()
item = {
"type": content_type,
"name": filename,
"ext": Path(filename).suffix.lstrip(".") or None,
"extras": {
"resource_id": resource["id"],
"source": "user_upload",
},
}
if content_type == "text":
item["text"] = content.decode("utf-8", errors="replace")
else:
item["base64"] = base64.b64encode(content).decode("ascii")
return item
def _resource_file_path(self, resource: dict[str, Any]) -> Path:
uri = str(resource["uri"])
parsed = urlparse(uri)
if parsed.scheme != "file":
raise ValueError(f"unsupported resource uri scheme: {parsed.scheme}")
return Path(unquote(parsed.path)).resolve(strict=True)
def list_resources(self, user_id: str) -> list[dict[str, Any]]:
return [self._resource_detail(item) for item in self.repository.list_resources(user_id)]
@ -395,6 +422,41 @@ class MemoryGatewayService:
overridden = self._apply_overrides(user_id, filtered)
return {"results": overridden}
async def add_memory(
self,
*,
session_id: str,
app_id: str,
project_id: str,
messages: list[dict[str, Any]],
) -> dict[str, Any]:
payload = {
"session_id": session_id,
"app_id": app_id,
"project_id": project_id,
"messages": messages,
}
return {
"session_id": session_id,
"everos": await self.everos_client.add_memory(payload),
}
async def flush_memory(
self,
*,
session_id: str,
app_id: str,
project_id: str,
) -> dict[str, Any]:
return {
"session_id": session_id,
"everos": await self.everos_client.flush_memory(
session_id,
app_id,
project_id,
),
}
def _search_payload(
self,
*,