From 93c7522b6547dc9f1894bcab6bf5ef959eecc81f Mon Sep 17 00:00:00 2001 From: Nicolas Brichet Date: Thu, 8 Aug 2024 17:37:07 +0200 Subject: [PATCH] Add some documentation about integrating jupyterlab-collaborative-chat --- docs/source/developers/contributing/index.md | 2 +- .../jupyterlab-collaborative-chat.md | 2 +- .../contributing/jupyterlab-ws-chat.md | 2 +- .../extending-extension.md | 1 - .../extension-providing-chat.md | 2 + .../developers/developing_extensions/index.md | 6 +- .../using-chat-extensions.md | 152 ++++++++++++++++++ docs/source/developers/index.md | 2 +- docs/source/users/index.md | 6 +- 9 files changed, 164 insertions(+), 11 deletions(-) delete mode 100644 docs/source/developers/developing_extensions/extending-extension.md create mode 100644 docs/source/developers/developing_extensions/using-chat-extensions.md diff --git a/docs/source/developers/contributing/index.md b/docs/source/developers/contributing/index.md index b0864d7..8ff6df4 100644 --- a/docs/source/developers/contributing/index.md +++ b/docs/source/developers/contributing/index.md @@ -1,6 +1,6 @@ # Contributing -This project is part of *jupyterlab* organization. +This project is included in *jupyterlab* organization. Before contributing to it, please read the [jupyter contributing guide](https://docs.jupyter.org/en/latest/contributing/content-contributor.html). diff --git a/docs/source/developers/contributing/jupyterlab-collaborative-chat.md b/docs/source/developers/contributing/jupyterlab-collaborative-chat.md index 13659ec..3bc588d 100644 --- a/docs/source/developers/contributing/jupyterlab-collaborative-chat.md +++ b/docs/source/developers/contributing/jupyterlab-collaborative-chat.md @@ -1,4 +1,4 @@ -# jupyterlab-collaborative-chat +# Collaborative chat The `jupyterlab-collaborative-chat` extension adds collaborative chats to jupyterlab. diff --git a/docs/source/developers/contributing/jupyterlab-ws-chat.md b/docs/source/developers/contributing/jupyterlab-ws-chat.md index ca672fb..246e3fe 100644 --- a/docs/source/developers/contributing/jupyterlab-ws-chat.md +++ b/docs/source/developers/contributing/jupyterlab-ws-chat.md @@ -1,4 +1,4 @@ -# jupyterlab-collaborative-chat +# Websocket chat The `jupyterlab-ws-chat` extension adds a chat panel relying on websocket for messaging. diff --git a/docs/source/developers/developing_extensions/extending-extension.md b/docs/source/developers/developing_extensions/extending-extension.md deleted file mode 100644 index 8d6c202..0000000 --- a/docs/source/developers/developing_extensions/extending-extension.md +++ /dev/null @@ -1 +0,0 @@ -# Extending a chat extension diff --git a/docs/source/developers/developing_extensions/extension-providing-chat.md b/docs/source/developers/developing_extensions/extension-providing-chat.md index c66488a..46c32ca 100644 --- a/docs/source/developers/developing_extensions/extension-providing-chat.md +++ b/docs/source/developers/developing_extensions/extension-providing-chat.md @@ -303,6 +303,8 @@ const myChatExtension: JupyterFrontEndPlugin = { }; ``` +(autocompletion-registry)= + ### autocompletionRegistry The `autocompletionRegistry` adds autocompletion feature to the chat input. This can be diff --git a/docs/source/developers/developing_extensions/index.md b/docs/source/developers/developing_extensions/index.md index e2edec2..b3c47b1 100644 --- a/docs/source/developers/developing_extensions/index.md +++ b/docs/source/developers/developing_extensions/index.md @@ -2,8 +2,8 @@ Other extensions can depends on one or the other of the packages. -This section describe how an extension can provide a chat, and how an extension can -extend the features of a chat extension. +This section describe how an extension can provide a chat, and how to make use of one +of the chat extensions in another extension. ```{toctree} --- @@ -11,5 +11,5 @@ maxdepth: 2 --- ./extension-providing-chat -./extending-extension +./using-chat-extensions ``` diff --git a/docs/source/developers/developing_extensions/using-chat-extensions.md b/docs/source/developers/developing_extensions/using-chat-extensions.md new file mode 100644 index 0000000..d76f746 --- /dev/null +++ b/docs/source/developers/developing_extensions/using-chat-extensions.md @@ -0,0 +1,152 @@ +# Using a chat extension in another extension + +## Collaborative chat + +The collaborative chat depends on [jupyter collaboration](https://jupyterlab-realtime-collaboration.readthedocs.io/en/latest/index.html) +to exchange the messages. + +As a very brief summary, jupyter collaboration allows jupyterlab users to share a +document in real time, based on [CRDT](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type). +All changes made to the document are propagated to all users. These change can occur +from the frontend or from the backend. The shared document has an object representation +in Typescript (for the frontend) and in Python (for the backend). These representation +can be accessed and used by external extensions. + +### Exposed token + +`jupyterlab-collaborative-chat` expose several token that allow external extension to +interact with. + +#### IChatFactory + +This token is composed of: + +- `widgetConfig` object, to retrieve and change the current [settings](#chat-settings) +of all the chats +- `tracker`, a widget tracker that allow to track all the collaborative chats, and to +get the current one. + +```{caution} +Currently the widget tracker only track the main area widgets, not the one opened in the +side panel. +``` + +#### IChatPanel + +This token is a pointer to the left panel containing chats.\ +It can be useful to programmatically open chat in the panel for example, or to list the +opened chats. + +#### IAutocompletionRegistry + +this is the [autocompletion registry](#autocompletion-registry) used by the chat widgets. + +Autocompletion commands can be added to it, and than usable from the chat widget. + +### Interact with the chat from the backend + +`jupyter_collaboration` provides a websocket server to handle every shared document +server side. This server can be used in an extension to retrieve a shared document. + +In addition, when a shared document is created, an event is emitted. We can use that +event data to trigger a connection to the shared document. + +Below is an example of an server extension that respond every message written to any +collaborative chat: + +```python +import jupyter_collaboration +import time +import uuid +from functools import partial +from jupyter_collaboration.utils import JUPYTER_COLLABORATION_EVENTS_URI +from jupyter_events import EventLogger +from jupyter_server.extension.application import ExtensionApp +from pycrdt import ArrayEvent + +from .ychat import YChat + + +if int(jupyter_collaboration.__version__[0]) >= 3: + COLLAB_VERSION = 3 +else: + COLLAB_VERSION = 2 + +BOT = { + "username": str(uuid.uuid4()), + "name": "user", + "display_name": "User" +} + + +class MyExtension(ExtensionApp): + name = "my_extension" + app_name = "My Extension" + description = """ + this extension interact with collaborative chats + """ + + def initialize(self): + super().initialize() + self.event_logger = self.serverapp.web_app.settings["event_logger"] + self.event_logger.add_listener( + schema_id=JUPYTER_COLLABORATION_EVENTS_URI, + listener=self.connect_chat + ) + + async def connect_chat(self, logger: EventLogger, schema_id: str, data: dict) -> None: + if data["room"].startswith("text:chat:") \ + and data["action"] == "initialize"\ + and data["msg"] == "Room initialized": + + self.log.info(f"Collaborative chat server is listening for {data["room"]}") + chat = await self.get_chat(data["room"]) + callback = partial(self.on_change, chat) + chat.ymessages.observe(callback) + + async def get_chat(self, room_id: str) -> YChat: + if COLLAB_VERSION == 3: + collaboration = self.serverapp.web_app.settings["jupyter_server_ydoc"] + document = await collaboration.get_document( + room_id=room_id, + copy=False + ) + else: + collaboration = self.serverapp.web_app.settings["jupyter_collaboration"] + server = collaboration.ywebsocket_server + + room = await server.get_room(room_id) + document = room._document + return document + + def on_change(self, chat: YChat, events: ArrayEvent) -> None: + for change in events.delta: + if not "insert" in change.keys(): + continue + messages = change["insert"] + for message in messages: + if message["sender"] == BOT["username"] or message["raw_time"]: + continue + chat.create_task( + self.write_message( + chat, + f"Received:\n\n- **id**: *{message["id"]}*:\n\n- **body**: *{message["body"]}*") + ) + + async def write_message(self, chat: YChat, body: str) -> None: + bot = chat.get_user_by_name(BOT["name"]) + if not bot: + chat.set_user(BOT) + else: + BOT["username"] = bot["username"] + + chat.add_message({ + "type": "msg", + "body": body, + "id": str(uuid.uuid4()), + "time": time.time(), + "sender": BOT["username"], + "raw_time": False + }) + +``` diff --git a/docs/source/developers/index.md b/docs/source/developers/index.md index 6194065..92d0d42 100644 --- a/docs/source/developers/index.md +++ b/docs/source/developers/index.md @@ -2,7 +2,7 @@ This section is addressed to developers, and contains documentation about: -- how to develop an extension providing a chat or extending a chat extension +- how to develop an extension providing a chat or to include a chat extension - how to contribute to this project ```{toctree} diff --git a/docs/source/users/index.md b/docs/source/users/index.md index facf34a..95051dd 100644 --- a/docs/source/users/index.md +++ b/docs/source/users/index.md @@ -1,6 +1,6 @@ # Users -## jupyterlab-collaborative-chat +## Collaborative chat The `jupyterlab-collaborative-chat` extension adds collaborative chats to jupyterlab. @@ -61,7 +61,7 @@ area, like any other document. Opening a chat from the left panel will open it in the left panel. ``` -## jupyterlab-ws-chat +## Websocket chat The `jupyterlab-ws-chat` extension adds a chat panel relying on websocket for messaging. @@ -87,7 +87,7 @@ pip uninstall jupyterlab-ws-chat The chat can be opened from the left panel ![chat icon](../../../packages/jupyter-chat/style/icons/chat.svg){w=24px}. -## Using the chat +## Chat usage The chat UI is composed of a list of messages and an input to send new messages.