amaye15 commited on
Commit
d36647a
·
1 Parent(s): eba3628

Debug - Notifications

Browse files
Files changed (1) hide show
  1. app/main.py +55 -40
app/main.py CHANGED
@@ -114,66 +114,69 @@ async def make_api_request(method: str, endpoint: str, **kwargs):
114
 
115
  async def listen_to_websockets(token: str, notification_state: list):
116
  """Connects to WS and updates state list when a message arrives."""
 
 
 
 
117
  if not token:
118
- logger.warning("WebSocket listener: No token provided.")
119
- return notification_state # Return current state if no token
120
 
121
  ws_url_base = API_BASE_URL.replace("http", "ws")
122
  ws_url = f"{ws_url_base}/ws/{token}"
123
- logger.info(f"Attempting to connect to WebSocket: {ws_url}")
124
 
125
  try:
126
- # Add timeout to websocket connection attempt
127
- async with asyncio.wait_for(websockets.connect(ws_url), timeout=10.0) as websocket:
128
- logger.info(f"WebSocket connected successfully to {ws_url}")
129
  while True:
130
  try:
131
  message_str = await websocket.recv()
132
- logger.info(f"Received raw message: {message_str}")
133
- message_data = json.loads(message_str)
134
- logger.info(f"Parsed message data: {message_data}")
135
-
136
- # Ensure it's the expected notification format
137
- if message_data.get("type") == "new_user":
138
- notification = schemas.Notification(**message_data)
139
- # Prepend to show newest first
140
- notification_state.insert(0, notification.message)
141
- logger.info(f"Notification added: {notification.message}")
142
- # Limit state history (optional)
143
- if len(notification_state) > 10:
144
- notification_state.pop()
145
- # IMPORTANT: Need to trigger Gradio update. This function itself
146
- # cannot directly update UI. It modifies the state, and we need
147
- # a gr.update() returned by a Gradio event handler that *reads*
148
- # this state. We use a polling mechanism or a hidden button trick.
149
- # Let's use polling via `every=` parameter in Gradio.
150
- # This function's primary job is just modifying the list.
151
- # We return the modified list, but Gradio needs an event.
152
- # ---> See the use of `gr.Textbox.change` and `every` below.
 
 
 
153
 
154
  except websockets.ConnectionClosedOK:
155
- logger.info("WebSocket connection closed normally.")
156
  break
157
  except websockets.ConnectionClosedError as e:
158
- logger.error(f"WebSocket connection closed with error: {e}")
159
  break
160
- except json.JSONDecodeError:
161
- logger.error(f"Failed to decode JSON from WebSocket message: {message_str}")
162
  except Exception as e:
163
- logger.error(f"Error in WebSocket listener loop: {e}")
164
- # Avoid breaking the loop on transient errors maybe? Add delay?
165
- await asyncio.sleep(1) # Short delay before retrying receive
166
  except asyncio.TimeoutError:
167
- logger.error(f"WebSocket connection timed out: {ws_url}")
168
  except websockets.exceptions.InvalidURI:
169
- logger.error(f"Invalid WebSocket URI: {ws_url}")
170
  except websockets.exceptions.WebSocketException as e:
171
- logger.error(f"WebSocket connection failed: {e}")
172
  except Exception as e:
173
- logger.error(f"Unexpected error connecting/listening to WebSocket: {e}")
174
 
175
- # Return the state as it is if connection failed or ended
176
- # The calling Gradio function will handle this return value.
177
  return notification_state
178
 
179
 
@@ -225,6 +228,18 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
225
  # This function will read the `notification_list` state
226
  every=1
227
  )
 
 
 
 
 
 
 
 
 
 
 
 
228
 
229
  # --- Event Handlers ---
230
 
 
114
 
115
  async def listen_to_websockets(token: str, notification_state: list):
116
  """Connects to WS and updates state list when a message arrives."""
117
+ # <<< Add Logging >>>
118
+ ws_listener_id = f"WSListener-{os.getpid()}-{asyncio.current_task().get_name()}"
119
+ logger.info(f"[{ws_listener_id}] Starting WebSocket listener task.")
120
+
121
  if not token:
122
+ logger.warning(f"[{ws_listener_id}] No token provided. Listener task exiting.")
123
+ return notification_state
124
 
125
  ws_url_base = API_BASE_URL.replace("http", "ws")
126
  ws_url = f"{ws_url_base}/ws/{token}"
127
+ logger.info(f"[{ws_listener_id}] Attempting to connect to WebSocket: {ws_url}")
128
 
129
  try:
130
+ async with asyncio.wait_for(websockets.connect(ws_url), timeout=15.0) as websocket: # Increased timeout slightly
131
+ logger.info(f"[{ws_listener_id}] WebSocket connected successfully to {ws_url}")
 
132
  while True:
133
  try:
134
  message_str = await websocket.recv()
135
+ # <<< Add Logging >>>
136
+ logger.info(f"[{ws_listener_id}] Received raw message: {message_str}")
137
+ try:
138
+ message_data = json.loads(message_str)
139
+ logger.info(f"[{ws_listener_id}] Parsed message data: {message_data}")
140
+
141
+ if message_data.get("type") == "new_user":
142
+ notification = schemas.Notification(**message_data)
143
+ # <<< Add Logging >>>
144
+ logger.info(f"[{ws_listener_id}] Processing 'new_user' notification: {notification.message}")
145
+ # Modify the list in place
146
+ notification_state.insert(0, notification.message)
147
+ logger.info(f"[{ws_listener_id}] State list updated. New length: {len(notification_state)}. Content: {notification_state[:5]}") # Log first few items
148
+ # Limit state history
149
+ if len(notification_state) > 10:
150
+ notification_state.pop()
151
+ else:
152
+ logger.warning(f"[{ws_listener_id}] Received message of unknown type: {message_data.get('type')}")
153
+
154
+ except json.JSONDecodeError:
155
+ logger.error(f"[{ws_listener_id}] Failed to decode JSON from WebSocket message: {message_str}")
156
+ except Exception as parse_err:
157
+ logger.error(f"[{ws_listener_id}] Error processing received message: {parse_err}")
158
+
159
 
160
  except websockets.ConnectionClosedOK:
161
+ logger.info(f"[{ws_listener_id}] WebSocket connection closed normally.")
162
  break
163
  except websockets.ConnectionClosedError as e:
164
+ logger.error(f"[{ws_listener_id}] WebSocket connection closed with error: {e}")
165
  break
 
 
166
  except Exception as e:
167
+ logger.error(f"[{ws_listener_id}] Error in WebSocket listener receive loop: {e}")
168
+ await asyncio.sleep(1) # Avoid tight loop on errors
169
+
170
  except asyncio.TimeoutError:
171
+ logger.error(f"[{ws_listener_id}] WebSocket connection timed out: {ws_url}")
172
  except websockets.exceptions.InvalidURI:
173
+ logger.error(f"[{ws_listener_id}] Invalid WebSocket URI: {ws_url}")
174
  except websockets.exceptions.WebSocketException as e:
175
+ logger.error(f"[{ws_listener_id}] WebSocket connection failed: {e}")
176
  except Exception as e:
177
+ logger.error(f"[{ws_listener_id}] Unexpected error in WebSocket listener task: {e}")
178
 
179
+ logger.info(f"[{ws_listener_id}] Listener task finished.")
 
180
  return notification_state
181
 
182
 
 
228
  # This function will read the `notification_list` state
229
  every=1
230
  )
231
+ def update_notification_ui(notif_list_state):
232
+ # <<< Add Logging >>>
233
+ # notif_list_state here *is* the Python list from the gr.State object
234
+ logger.debug(f"UI Update Triggered. State List Length: {len(notif_list_state)}. Content: {notif_list_state[:5]}")
235
+ # Join the list items into a string for display
236
+ return "\n".join(notif_list_state)
237
+
238
+ notification_display.change( # Use .change with every= setup on the component
239
+ fn=update_notification_ui,
240
+ inputs=[notification_list], # Read the state
241
+ outputs=[notification_display] # Update the component
242
+ )
243
 
244
  # --- Event Handlers ---
245