Skip to content

Commit bac19d4

Browse files
committed
Added cleanup for notebook observers
1 parent 8072ada commit bac19d4

File tree

1 file changed

+68
-18
lines changed

1 file changed

+68
-18
lines changed

jupyter_ai_router/router.py

Lines changed: 68 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import re
1414
from dataclasses import replace
1515
from jupyterlab_chat.models import Message
16-
from pycrdt import ArrayEvent
16+
from pycrdt import ArrayEvent, TextEvent, MapEvent
1717
from traitlets.config import LoggingConfigurable
1818
from jupyter_ydoc.ybasedoc import YBaseDoc
1919

@@ -112,6 +112,9 @@ def __init__(self, *args, **kwargs):
112112
# Root observers for keeping track of incoming messages
113113
self.message_observers: Dict[str, Callable] = {}
114114

115+
# Global awareness observer subscriber ID for cleanup
116+
self._global_awareness_subscriber_id = None
117+
115118
self.event_loop.create_task(self._start_observing_global_awareness())
116119

117120
async def _room_id_from_path(self, path: str) -> str | None:
@@ -133,7 +136,7 @@ def event_loop(self):
133136

134137
async def _start_observing_global_awareness(self):
135138
awareness = self._get_global_awareness()
136-
awareness.observe(self._on_global_awareness_change)
139+
self._global_awareness_subscriber_id = awareness.observe(self._on_global_awareness_change)
137140

138141
async def _start_observing_room(self, room_id: str, username: str):
139142
"""Start observing a room's document and awareness changes."""
@@ -401,7 +404,8 @@ def _on_global_awareness_change(self, topic, updates):
401404
async def _handle_user_document_switch(
402405
self, username: str, current_doc: str, prev_doc: str | None
403406
):
404-
"""Handle user switching documents - async version for room operations."""
407+
"""Handles user switching documents, unobserves current doc room,
408+
and registers new observers for the room that becomes active."""
405409
try:
406410
# Only handle users we have observers for
407411
if username not in self.users:
@@ -436,15 +440,16 @@ async def _handle_user_document_switch(
436440
except Exception as e:
437441
self.log.error(f"Error handling document switch for user {username}: {e}")
438442

439-
def _on_notebook_change(self, room_id: str):
443+
def _on_notebook_change(self, room_id: str, events):
440444
"""Handle notebook document changes and log event details."""
441445

446+
self.log.info(f"Change event is {events}")
447+
442448
# Save the timestamp that a change was made indicating notebook has changed
443449
current_time = time.time()
444450
if room_id in self.rooms:
445451
self.rooms[room_id].last_edit_time = current_time
446-
self.log.info(f"Notebook cells changed in {room_id} at {current_time}")
447-
452+
self.log.info(f"Notebook cells changed in {room_id} at {current_time}")
448453

449454
def _on_awareness_change(self, room_id: str, ydoc: YBaseDoc, topic, updates):
450455
"""Handle awareness changes for notebook activity tracking."""
@@ -486,17 +491,15 @@ def _on_awareness_change(self, room_id: str, ydoc: YBaseDoc, topic, updates):
486491
}
487492
continue
488493

489-
if prev_active_cell != active_cell:
490-
# Check if there was a notebook change since last check
491-
if room.last_edit_time > prev_check:
492-
# Check if enough time has passed since last trigger (debouncing)
493-
if current_time - room.last_trigger_time >= self.trigger_cooldown:
494-
room.last_trigger_time = current_time
495-
self._notify_notebook_activity_observers(
496-
username=username,
497-
prev_active_cell=prev_active_cell,
498-
notebook_path=notebook_path
499-
)
494+
if prev_active_cell != active_cell and room.last_edit_time > prev_check:
495+
# Check if enough time has passed since last trigger
496+
if current_time - room.last_trigger_time >= self.trigger_cooldown:
497+
room.last_trigger_time = current_time
498+
self._notify_notebook_activity_observers(
499+
username=username,
500+
prev_active_cell=prev_active_cell,
501+
notebook_path=notebook_path
502+
)
500503

501504
# Update stored state for this user
502505
user.room_states[room_id] = {
@@ -676,19 +679,66 @@ def _on_notebook_reset(self, room_id, ydoc: YBaseDoc) -> None:
676679
except Exception as e:
677680
self.log.error(f"Reset notebook observer error for {room_id}: {e}")
678681

682+
def _cleanup_rooms(self) -> None:
683+
"""Clean up all room trackers and their subscriptions."""
684+
room_ids = list(self.rooms.keys())
685+
for room_id in room_ids:
686+
room_tracker = self.rooms[room_id]
687+
try:
688+
room_tracker.stop_observing()
689+
self.log.debug(f"Cleaned up room tracker for {room_id}")
690+
except Exception as e:
691+
self.log.warning(f"Failed to clean up room tracker {room_id}: {e}")
692+
self.rooms.clear()
693+
694+
def _cleanup_awareness_observers(self) -> None:
695+
"""Clean up global and local awareness observers."""
696+
# Clean up global awareness observer
697+
if self._global_awareness_subscriber_id is not None:
698+
try:
699+
awareness = self._get_global_awareness()
700+
awareness.unobserve(self._global_awareness_subscriber_id)
701+
self._global_awareness_subscriber_id = None
702+
self.log.debug("Cleaned up global awareness observer")
703+
except Exception as e:
704+
self.log.warning(f"Failed to clean up global awareness observer: {e}")
705+
706+
# Clean up notebook activity observers
707+
observer_ids = list(self._observer_callbacks.keys())
708+
for observer_id in observer_ids:
709+
try:
710+
# Observer callbacks are already disconnected via room cleanup
711+
# Just need to clear the registry
712+
del self._observer_callbacks[observer_id]
713+
except Exception as e:
714+
self.log.warning(f"Failed to clean up observer {observer_id}: {e}")
715+
716+
# Reset observer counter
717+
self.observer_counter = 0
718+
679719
def cleanup(self) -> None:
680720
"""Clean up router resources."""
681721
self.log.info("Cleaning up MessageRouter...")
682722

723+
# Clean up room trackers and their subscriptions
724+
self._cleanup_rooms()
725+
726+
# Clean up user trackers
727+
self.users.clear()
728+
729+
# Clean up awareness observers (global and local)
730+
self._cleanup_awareness_observers()
731+
683732
# Disconnect all chats
684733
room_ids = list(self.active_chats.keys())
685734
for room_id in room_ids:
686735
self.disconnect_chat(room_id)
687736

688-
# Clear callbacks
737+
# Clear all observer callback lists
689738
self.chat_init_observers.clear()
690739
self.slash_cmd_observers.clear()
691740
self.chat_msg_observers.clear()
692741
self.chat_reset_observers.clear()
742+
self.notebook_reset_observers.clear()
693743

694744
self.log.info("MessageRouter cleanup complete")

0 commit comments

Comments
 (0)