77- Manages lifecycle and cleanup
88"""
99
10- from typing import Any , Callable , Dict , List , Set , TYPE_CHECKING
10+ from typing import Any , Callable , Dict , List , TYPE_CHECKING
1111from functools import partial
1212from jupyterlab_chat .models import Message
1313from pycrdt import ArrayEvent
1919from .utils import get_first_word
2020
2121
22-
2322class MessageRouter (LoggingConfigurable ):
2423 """
2524 Router that manages ychat message routing.
26-
25+
2726 The Router provides three callback points:
2827 1. When new chats are initialized
2928 2. When slash commands are received
3029 3. When regular (non-slash) messages are received
3130 """
32-
31+
3332 def __init__ (self , * args , ** kwargs ):
3433 super ().__init__ (* args , ** kwargs )
35-
34+
3635 # Callback lists
3736 self .chat_init_observers : List [Callable [[str , "YChat" ], Any ]] = []
3837 self .slash_cmd_observers : Dict [str , List [Callable [[str , Message ], Any ]]] = {}
3938 self .chat_msg_observers : Dict [str , List [Callable [[str , Message ], Any ]]] = {}
40-
39+
4140 # Active chat rooms
4241 self .active_chats : Dict [str , "YChat" ] = {}
4342
4443 # Root observers for keeping track of incoming messages
4544 self .message_observers : Dict [str , Callable ] = {}
46-
45+
4746 def observe_chat_init (self , callback : Callable [[str , "YChat" ], Any ]) -> None :
4847 """
4948 Register a callback for when new chats are initialized.
50-
49+
5150 Args:
5251 callback: Function called with (room_id: str, ychat: YChat) when chat connects
5352 """
5453 self .chat_init_observers .append (callback )
5554 self .log .info ("Registered new chat initialization callback" )
56-
57- def observe_slash_cmd_msg (self , room_id : str , callback : Callable [[str , Message ], Any ]) -> None :
55+
56+ def observe_slash_cmd_msg (
57+ self , room_id : str , callback : Callable [[str , Message ], Any ]
58+ ) -> None :
5859 """
5960 Register a callback for when slash commands are received.
60-
61+
6162 Args:
6263 callback: Function called with (room_id: str, message: Message) for slash commands
6364 """
6465 if room_id not in self .slash_cmd_observers :
6566 self .slash_cmd_observers [room_id ] = []
66-
67+
6768 self .slash_cmd_observers [room_id ].append (callback )
6869 self .log .info ("Registered slash command callback" )
69-
70- def observe_chat_msg (self , room_id : str , callback : Callable [[str , Message ], Any ]) -> None :
70+
71+ def observe_chat_msg (
72+ self , room_id : str , callback : Callable [[str , Message ], Any ]
73+ ) -> None :
7174 """
7275 Register a callback for when regular (non-slash) messages are received.
73-
76+
7477 Args:
7578 callback: Function called with (room_id: str, message: Message) for regular messages
7679 """
@@ -79,41 +82,41 @@ def observe_chat_msg(self, room_id: str, callback: Callable[[str, Message], Any]
7982
8083 self .chat_msg_observers [room_id ].append (callback )
8184 self .log .info ("Registered message callback" )
82-
85+
8386 def connect_chat (self , room_id : str , ychat : "YChat" ) -> None :
8487 """
8588 Connect a new chat session to the router.
86-
89+
8790 Args:
8891 room_id: Unique identifier for the chat room
8992 ychat: YChat instance for the room
9093 """
9194 if room_id in self .active_chats :
9295 self .log .warning (f"Chat { room_id } already connected to router" )
9396 return
94-
97+
9598 self .active_chats [room_id ] = ychat
96-
99+
97100 # Set up message observer
98101 callback = partial (self ._on_message_change , room_id , ychat )
99102 ychat .ymessages .observe (callback )
100103 self .message_observers [room_id ] = callback
101-
104+
102105 self .log .info (f"Connected chat { room_id } to router" )
103-
106+
104107 # Notify new chat observers
105108 self ._notify_chat_init_observers (room_id , ychat )
106-
109+
107110 def disconnect_chat (self , room_id : str ) -> None :
108111 """
109112 Disconnect a chat session from the router.
110-
113+
111114 Args:
112115 room_id: Unique identifier for the chat room
113116 """
114117 if room_id not in self .active_chats :
115118 return
116-
119+
117120 # Remove message observer
118121 if room_id in self .message_observers :
119122 ychat = self .active_chats [room_id ]
@@ -122,49 +125,50 @@ def disconnect_chat(self, room_id: str) -> None:
122125 except Exception as e :
123126 self .log .warning (f"Failed to unobserve chat { room_id } : { e } " )
124127 del self .message_observers [room_id ]
125-
128+
126129 del self .active_chats [room_id ]
127130 self .log .info (f"Disconnected chat { room_id } from router" )
128-
129- def _on_message_change (self , room_id : str , ychat : "YChat" , events : ArrayEvent ) -> None :
131+
132+ def _on_message_change (
133+ self , room_id : str , ychat : "YChat" , events : ArrayEvent
134+ ) -> None :
130135 """Handle incoming messages from YChat."""
131136 for change in events .delta : # type: ignore[attr-defined]
132137 if "insert" not in change .keys ():
133138 continue
134-
139+
135140 # Process new messages (filter out raw_time duplicates)
136141 new_messages = [
137- Message (** m ) for m in change ["insert" ]
138- if not m .get ("raw_time" , False )
142+ Message (** m ) for m in change ["insert" ] if not m .get ("raw_time" , False )
139143 ]
140-
144+
141145 for message in new_messages :
142146 self ._route_message (room_id , message )
143-
147+
144148 def _route_message (self , room_id : str , message : Message ) -> None :
145149 """
146150 Route an incoming message to appropriate observers.
147-
151+
148152 Args:
149153 room_id: The chat room ID
150154 message: The message to route
151155 """
152156 first_word = get_first_word (message .body )
153-
157+
154158 # Check if it's a slash command
155159 if first_word and first_word .startswith ("/" ):
156160 self ._notify_slash_cmd_observers (room_id , message )
157161 else :
158162 self ._notify_msg_observers (room_id , message )
159-
163+
160164 def _notify_chat_init_observers (self , room_id : str , ychat : "YChat" ) -> None :
161165 """Notify all new chat observers."""
162166 for callback in self .chat_init_observers :
163167 try :
164168 callback (room_id , ychat )
165169 except Exception as e :
166170 self .log .error (f"New chat observer error for { room_id } : { e } " )
167-
171+
168172 def _notify_slash_cmd_observers (self , room_id : str , message : Message ) -> None :
169173 """Notify all slash command observers."""
170174 callbacks = self .slash_cmd_observers .get (room_id , [])
@@ -173,28 +177,28 @@ def _notify_slash_cmd_observers(self, room_id: str, message: Message) -> None:
173177 callback (room_id , message )
174178 except Exception as e :
175179 self .log .error (f"Slash command observer error for { room_id } : { e } " )
176-
180+
177181 def _notify_msg_observers (self , room_id : str , message : Message ) -> None :
178- """Notify all message observers."""
182+ """Notify all message observers."""
179183 callbacks = self .chat_msg_observers .get (room_id , [])
180184 for callback in callbacks :
181185 try :
182186 callback (room_id , message )
183187 except Exception as e :
184188 self .log .error (f"Message observer error for { room_id } : { e } " )
185-
189+
186190 def cleanup (self ) -> None :
187191 """Clean up router resources."""
188192 self .log .info ("Cleaning up MessageRouter..." )
189-
193+
190194 # Disconnect all chats
191195 room_ids = list (self .active_chats .keys ())
192196 for room_id in room_ids :
193197 self .disconnect_chat (room_id )
194-
198+
195199 # Clear callbacks
196200 self .chat_init_observers .clear ()
197201 self .slash_cmd_observers .clear ()
198202 self .chat_msg_observers .clear ()
199-
200- self .log .info ("MessageRouter cleanup complete" )
203+
204+ self .log .info ("MessageRouter cleanup complete" )
0 commit comments