linxinhua commited on
Commit
7f552fa
·
verified ·
1 Parent(s): f0afbbe

logger.py: batch-checkpoint refactor (see app.py commit for details)

Browse files
Files changed (1) hide show
  1. core/logger.py +37 -10
core/logger.py CHANGED
@@ -228,7 +228,9 @@ class SessionLogger:
228
 
229
  logger._last_confirmed_state = logger._state_hash()
230
  logger._ensure_worker()
231
- logger._enqueue_upload()
 
 
232
  return logger
233
 
234
  def _filename(self):
@@ -267,7 +269,6 @@ class SessionLogger:
267
  "submitted_at": _now_iso(),
268
  "char_count": len(text),
269
  }
270
- self._enqueue_upload()
271
 
272
  def set_discussion_plan(self, text):
273
  if not self.session_id:
@@ -276,7 +277,6 @@ class SessionLogger:
276
  "text": text,
277
  "extracted_at": _now_iso(),
278
  }
279
- self._enqueue_upload()
280
 
281
  def set_perfect_answer(self, text):
282
  if not self.session_id:
@@ -286,7 +286,6 @@ class SessionLogger:
286
  "generated_at": _now_iso(),
287
  "char_count": len(text),
288
  }
289
- self._enqueue_upload()
290
 
291
  def set_dominant_framework(self, framework):
292
  """Record which of the 3 frameworks (consequence / duty_rights /
@@ -295,7 +294,6 @@ class SessionLogger:
295
  if not self.session_id:
296
  return
297
  self.dominant_framework = framework
298
- self._enqueue_upload()
299
 
300
  def add_feedback(self, message_index, kind, message_excerpt=""):
301
  """Record a participant aha! / ugh. reaction on a bot message.
@@ -304,6 +302,10 @@ class SessionLogger:
304
  "stress" (made participant uncomfortable),
305
  "none" (cancel, second click on the already-active emoji).
306
  Every click (including cancels) is appended with its own timestamp.
 
 
 
 
307
  """
308
  if not self.session_id:
309
  return
@@ -314,7 +316,6 @@ class SessionLogger:
314
  "timestamp": _now_iso(),
315
  "message_excerpt": (message_excerpt or "")[:200],
316
  })
317
- self._enqueue_upload()
318
 
319
  def get_current_kind(self, message_index):
320
  """Return the current ('aha' / 'stress' / None) reaction for a given
@@ -327,7 +328,11 @@ class SessionLogger:
327
 
328
  def add_argument_snapshot(self, trigger, text, context=None):
329
  """Snapshot the participant's notepad textbox at an interaction event.
330
- The bot NEVER sees this; logger-only side-channel."""
 
 
 
 
331
  if not self.session_id:
332
  return
333
  text = text or ""
@@ -343,18 +348,19 @@ class SessionLogger:
343
  if context:
344
  snap["context"] = context
345
  self.argument_snapshots.append(snap)
346
- self._enqueue_upload()
347
 
348
  def add_manager_verdict(self, verdict_dict):
349
  """Append a manager verdict to the session-level history list.
350
  Called once per chat turn, right after the manager LLM returns.
351
 
352
  `verdict_dict` should be the dict form of `ManagerVerdict.to_dict()`.
 
 
 
353
  """
354
  if not self.session_id:
355
  return
356
  self.manager_history.append(dict(verdict_dict))
357
- self._enqueue_upload()
358
 
359
  def add_turn(self, user_message, manager_verdict, plan_control,
360
  assistant_response, scratch=None, latency_s=None):
@@ -376,7 +382,8 @@ class SessionLogger:
376
  if latency_s:
377
  turn["latency_s"] = latency_s
378
  self.turns.append(turn)
379
- self._enqueue_upload()
 
380
 
381
  def _derive_feedback_current(self):
382
  """Collapse feedback events to {message_index: latest_kind},
@@ -468,6 +475,26 @@ class SessionLogger:
468
  "session_summary": self._derive_session_summary(),
469
  }
470
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
471
  def _enqueue_upload(self):
472
  """Push the latest payload onto the upload queue. If a payload is
473
  already queued, it gets superseded (only the newest survives).
 
228
 
229
  logger._last_confirmed_state = logger._state_hash()
230
  logger._ensure_worker()
231
+ # NOTE: caller (on_recover_session in app.py) must call
232
+ # checkpoint() after this returns, to persist the
233
+ # 'session_resumed' snapshot appended above.
234
  return logger
235
 
236
  def _filename(self):
 
269
  "submitted_at": _now_iso(),
270
  "char_count": len(text),
271
  }
 
272
 
273
  def set_discussion_plan(self, text):
274
  if not self.session_id:
 
277
  "text": text,
278
  "extracted_at": _now_iso(),
279
  }
 
280
 
281
  def set_perfect_answer(self, text):
282
  if not self.session_id:
 
286
  "generated_at": _now_iso(),
287
  "char_count": len(text),
288
  }
 
289
 
290
  def set_dominant_framework(self, framework):
291
  """Record which of the 3 frameworks (consequence / duty_rights /
 
294
  if not self.session_id:
295
  return
296
  self.dominant_framework = framework
 
297
 
298
  def add_feedback(self, message_index, kind, message_excerpt=""):
299
  """Record a participant aha! / ugh. reaction on a bot message.
 
302
  "stress" (made participant uncomfortable),
303
  "none" (cancel, second click on the already-active emoji).
304
  Every click (including cancels) is appended with its own timestamp.
305
+
306
+ NOTE: in-memory only. The next checkpoint() (triggered by next chat
307
+ turn / end_session) will persist these events. Direct upload was
308
+ removed in the batch-push refactor to reduce HF commit pressure.
309
  """
310
  if not self.session_id:
311
  return
 
316
  "timestamp": _now_iso(),
317
  "message_excerpt": (message_excerpt or "")[:200],
318
  })
 
319
 
320
  def get_current_kind(self, message_index):
321
  """Return the current ('aha' / 'stress' / None) reaction for a given
 
328
 
329
  def add_argument_snapshot(self, trigger, text, context=None):
330
  """Snapshot the participant's notepad textbox at an interaction event.
331
+ The bot NEVER sees this; logger-only side-channel.
332
+
333
+ NOTE: in-memory only. The next checkpoint() (triggered by next chat
334
+ turn / submit_initial / end_session) will persist these snapshots.
335
+ """
336
  if not self.session_id:
337
  return
338
  text = text or ""
 
348
  if context:
349
  snap["context"] = context
350
  self.argument_snapshots.append(snap)
 
351
 
352
  def add_manager_verdict(self, verdict_dict):
353
  """Append a manager verdict to the session-level history list.
354
  Called once per chat turn, right after the manager LLM returns.
355
 
356
  `verdict_dict` should be the dict form of `ManagerVerdict.to_dict()`.
357
+
358
+ NOTE: in-memory only. The next checkpoint() (called right after
359
+ the parent chat turn completes) will persist this verdict.
360
  """
361
  if not self.session_id:
362
  return
363
  self.manager_history.append(dict(verdict_dict))
 
364
 
365
  def add_turn(self, user_message, manager_verdict, plan_control,
366
  assistant_response, scratch=None, latency_s=None):
 
382
  if latency_s:
383
  turn["latency_s"] = latency_s
384
  self.turns.append(turn)
385
+ # NOTE: in-memory only. app.py's per-turn handler must call
386
+ # checkpoint() right after _log_turn() to persist this turn.
387
 
388
  def _derive_feedback_current(self):
389
  """Collapse feedback events to {message_index: latest_kind},
 
475
  "session_summary": self._derive_session_summary(),
476
  }
477
 
478
+ def checkpoint(self):
479
+ """Public entry-point: explicitly push the current session state
480
+ to HF. Called from app.py at well-defined trigger points:
481
+
482
+ - per chat turn (after _log_turn in on_send)
483
+ - submit_initial (after add_argument_snapshot in on_submit_initial_confirm)
484
+ - session recovery (after from_dict in on_recover_session)
485
+
486
+ end_session_confirm uses flush() instead (synchronous, retry-heavy).
487
+ end_session_arm is NOT a checkpoint trigger — the arm event is
488
+ captured in-memory and ride along the next push.
489
+
490
+ Replaces the older behaviour where every add_xxx / set_xxx method
491
+ triggered its own upload. That created O(N) HF commits per session
492
+ and caused 256-commits/hour rate-limit pressure during concurrent
493
+ prod runs. New behaviour: O(turns) commits per session, with the
494
+ per-turn push carrying all interim feedback/snapshot/manager events
495
+ in a single cumulative payload."""
496
+ self._enqueue_upload()
497
+
498
  def _enqueue_upload(self):
499
  """Push the latest payload onto the upload queue. If a payload is
500
  already queued, it gets superseded (only the newest survives).