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