PMFlow/backend/app/services/context_cache_service/mixin_session_invalidate.py

110 lines
3.7 KiB
Python

from __future__ import annotations
from .shared import *
class ContextCacheSessionInvalidateMixin:
async def get_session_messages(self, *, tenant_id: int, session_id: Optional[int]) -> list[dict[str, str]]:
if session_id is None:
return []
sid = _safe_int(session_id, 0)
if sid <= 0:
return []
redis = await self._get_redis()
if redis is None:
return []
key = self._session_key(_safe_int(tenant_id, 1), sid)
try:
raw_rows = await redis.lrange(key, -SESSION_MAX_MESSAGES, -1)
except Exception:
return []
messages: list[dict[str, str]] = []
for raw in raw_rows:
row = _json_loads(raw, None)
if not isinstance(row, dict):
continue
role = str(row.get("role") or "").strip().lower()
content = str(row.get("content") or "").strip()
if role in {"user", "assistant"} and content:
messages.append({"role": role, "content": content})
return messages
async def append_session_messages(
self,
*,
tenant_id: int,
session_id: Optional[int],
question: str,
answer: str,
) -> None:
if session_id is None:
return
sid = _safe_int(session_id, 0)
if sid <= 0:
return
redis = await self._get_redis()
if redis is None:
return
key = self._session_key(_safe_int(tenant_id, 1), sid)
user_payload = json.dumps({"role": "user", "content": str(question or "")[:3000]}, ensure_ascii=False)
assistant_payload = json.dumps({"role": "assistant", "content": str(answer or "")[:4000]}, ensure_ascii=False)
try:
pipe = redis.pipeline()
pipe.rpush(key, user_payload, assistant_payload)
pipe.ltrim(key, -SESSION_MAX_MESSAGES, -1)
pipe.expire(key, SESSION_TTL_SECONDS)
await pipe.execute()
except Exception:
return
async def invalidate_system_context(self, *, tenant_id: int) -> None:
redis = await self._get_redis()
if redis is None:
return
tid = _safe_int(tenant_id, 1)
keys = [
self._system_key(tid),
self._project_list_key(tid),
self._signal_pending_key(tid),
]
try:
await redis.delete(*keys)
except Exception:
pass
async def invalidate_user_context(self, *, tenant_id: int, user_id: int) -> None:
redis = await self._get_redis()
if redis is None:
return
uid = _safe_int(user_id, 0)
if uid <= 0:
return
try:
await redis.delete(self._user_key(_safe_int(tenant_id, 1), uid))
except Exception:
pass
@staticmethod
def messages_to_turns(messages: list[dict[str, str]]) -> list[dict[str, str]]:
turns: list[dict[str, str]] = []
pending_question: Optional[str] = None
for row in messages:
role = str(row.get("role") or "").lower()
content = str(row.get("content") or "")
if role == "user":
if pending_question:
turns.append({"question": pending_question, "answer": ""})
pending_question = content
continue
if role == "assistant":
if pending_question is None:
continue
turns.append({"question": pending_question, "answer": content})
pending_question = None
if pending_question:
turns.append({"question": pending_question, "answer": ""})
return turns