110 lines
3.7 KiB
Python
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
|
|
|