返回 JoyAI-Echo
webui_sessions.py
1 """WebUI session key helpers for the local browser gateway."""
2
3 from __future__ import annotations
4
5 WEBUI_CHANNEL = "websocket"
6 _PREFIX = f"{WEBUI_CHANNEL}:"
7
8
9 class WebuiSessionKeyError(ValueError):
10 """Raised when a WebUI session key does not meet the persisted key shape."""
11
12
13 def webui_session_key(user_id: str | None, chat_id: str) -> str:
14 """Build a browser session key (normally ``websocket:local:<chat>``)."""
15 if not user_id or not str(user_id).strip():
16 raise WebuiSessionKeyError("user_id is required for WebUI session keys")
17 if not chat_id or not str(chat_id).strip():
18 raise WebuiSessionKeyError("chat_id is required for WebUI session keys")
19 return f"{WEBUI_CHANNEL}:{user_id}:{chat_id}"
20
21
22 def is_legacy_two_part_webui_session_key(key: str) -> bool:
23 """Return True for deprecated ``websocket:<chat_id>`` keys (no user segment)."""
24 if not key.startswith(_PREFIX):
25 return False
26 rest = key[len(_PREFIX) :]
27 return bool(rest) and ":" not in rest
28
29
30 def parse_webui_session_key(key: str) -> tuple[str, str] | None:
31 """Return ``(user_id, chat_id)`` for valid three-part keys; None otherwise."""
32 if is_legacy_two_part_webui_session_key(key):
33 return None
34 if not key.startswith(_PREFIX):
35 return None
36 rest = key[len(_PREFIX) :]
37 if not rest or ":" not in rest:
38 return None
39 user_id, _, chat_id = rest.partition(":")
40 if not user_id or not chat_id:
41 return None
42 return user_id, chat_id
43
44
45 def webui_wire_chat_id(session_key: str) -> str:
46 """Extract the wire ``chat_id`` (uuid) from a persisted session key."""
47 parsed = parse_webui_session_key(session_key)
48 return parsed[1] if parsed else ""
49
50
51 def session_workspace_dir_name(session_key: str) -> str:
52 """Map a persisted session key to a workspace subdirectory name.
53
54 ``websocket:local:<chatId>`` becomes ``websocket_local_<chatId>``,
55 matching how WebUI sessions are laid out on disk.
56 """
57 return session_key.replace(":", "_").replace("/", "_")
58
58 lines PYTHON