Files
MemoryBear/api/app/utils/tmp_session.py
Eternity 3f9740412a feat(memory): add session-based chat history and user metadata retrieval
- Add ChatSessionCache to manage chat history per session
- Add SEARCH_USER_METADATA cypher query for retrieving user entity metadata
- Add "str" mode support to StructResponse for raw text extraction
- Add content_str field to MemorySearchResult for pre-formatted content
- Fix sandbox URL by removing hardcoded port
- Add description field to entity search results
- Remove history from UserInput schema, use session_id instead
2026-05-06 17:45:16 +08:00

78 lines
2.5 KiB
Python

import json
import logging
import redis.asyncio as redis
from app.aioRedis import get_redis_connection
logger = logging.getLogger(__name__)
DEFAULT_TTL = 3600
class ChatSessionCache:
"""Cache user-AI conversation history in Redis with TTL-based expiry.
Usage::
cache = ChatSessionCache(session_id="user_123")
await cache.append("user", "Hello")
await cache.append("assistant", "Hi there!")
history = await cache.get_history()
"""
def __init__(self, session_id: str, ttl: int = DEFAULT_TTL):
self.session_id = session_id
self.ttl = ttl
self._key = f"chat:session:{session_id}"
@staticmethod
async def _client() -> redis.StrictRedis:
return await get_redis_connection()
async def append(self, role: str, content: str) -> None:
r = await self._client()
entry = json.dumps({"role": role, "content": content}, ensure_ascii=False)
await r.rpush(self._key, entry)
await r.expire(self._key, self.ttl)
async def append_many(self, messages: list[dict[str, str]]) -> None:
"""Batch append messages. Each dict should have ``role`` and ``content`` keys."""
if not messages:
return
r = await self._client()
entries = [
json.dumps(m, ensure_ascii=False)
for m in messages
if "role" in m and "content" in m
]
if entries:
await r.rpush(self._key, *entries)
await r.expire(self._key, self.ttl)
async def get_history(self) -> list[dict[str, str]]:
r = await self._client()
raw = await r.lrange(self._key, 0, -1)
return [json.loads(item) for item in raw]
async def get_history_text(self, user_label: str = "User", ai_label: str = "Assistant") -> str:
"""Return conversation as a formatted text block."""
history = await self.get_history()
lines = []
for msg in history:
role = msg.get("role", "")
content = msg.get("content", "")
label = user_label if role == "user" else ai_label if role == "assistant" else role
lines.append(f"{label}: {content}")
return "\n".join(lines)
async def reset(self) -> None:
"""Delete the session from Redis."""
r = await self._client()
await r.delete(self._key)
async def touch(self) -> None:
"""Refresh the TTL without modifying data."""
r = await self._client()
await r.expire(self._key, self.ttl)