diff --git a/backend/open_webui/socket/main.py b/backend/open_webui/socket/main.py index 00af651d6a..c3678f592d 100644 --- a/backend/open_webui/socket/main.py +++ b/backend/open_webui/socket/main.py @@ -5,6 +5,7 @@ import logging import random import sys import time +from typing import Any import pycrdt as Y import socketio @@ -47,6 +48,7 @@ from open_webui.utils.redis import ( get_redis_connection, get_sentinels_from_env, ) +from socketio.packet import Packet logging.basicConfig(stream=sys.stdout, level=GLOBAL_LOG_LEVEL) log = logging.getLogger(__name__) @@ -65,6 +67,17 @@ def get_room_sid_map(manager, namespace: str, room: str): return manager.rooms.get(namespace, {}).get(room) +class JSONOnlyPacket(Packet): + """Packet class for JSON-serializable payloads only, skipping python-socketio's per-emit binary scan.""" + + uses_binary_events = False + + @classmethod + def reconstruct_binary(cls, data: Any, attachments: list[bytes]): + """Normalize client attachments to int lists, the form the Yjs handlers store and apply.""" + return super().reconstruct_binary(data, [list(attachment) for attachment in attachments]) + + if WEBSOCKET_MANAGER == 'redis': sentinel_hosts = WEBSOCKET_SENTINEL_HOSTS or '' ws_redis_url = ( @@ -77,6 +90,7 @@ if WEBSOCKET_MANAGER == 'redis': cors_allowed_origins=SOCKETIO_CORS_ORIGINS, async_mode='asgi', json=SOCKETIO_JSON, + serializer=JSONOnlyPacket, transports=(['websocket'] if ENABLE_WEBSOCKET_SUPPORT else ['polling']), allow_upgrades=ENABLE_WEBSOCKET_SUPPORT, always_connect=True, @@ -91,6 +105,7 @@ else: cors_allowed_origins=SOCKETIO_CORS_ORIGINS, async_mode='asgi', json=SOCKETIO_JSON, + serializer=JSONOnlyPacket, transports=(['websocket'] if ENABLE_WEBSOCKET_SUPPORT else ['polling']), allow_upgrades=ENABLE_WEBSOCKET_SUPPORT, always_connect=True, diff --git a/src/lib/components/common/RichTextInput/Collaboration.ts b/src/lib/components/common/RichTextInput/Collaboration.ts index 539da07a1e..1ef1c063ae 100644 --- a/src/lib/components/common/RichTextInput/Collaboration.ts +++ b/src/lib/components/common/RichTextInput/Collaboration.ts @@ -163,7 +163,7 @@ export class SocketIOCollaborationProvider { document_id: this.documentId, user_id: this.user?.id, socket_id: this.socket.id, - update: Y.encodeStateAsUpdate(this.doc) + update: Array.from(Y.encodeStateAsUpdate(this.doc)) }); } else { console.warn('Yjs document is empty, not sending state.');