tao-shen Claude Opus 4.6 commited on
Commit
b447519
Β·
1 Parent(s): 1956abb

feat: persist Adam & Eve conversation to HF Dataset

Browse files

Every turn is saved as a JSONL record to tao-shen/HuggingClaw-Home-data
at conversation-log/chatlog.jsonl. Records include timestamp, speaker,
bilingual text, actions with results, workflow state, and child stage.

- Buffer 3 turns then flush to HF Dataset (reduces API calls)
- Also writes to /tmp/conversation-loop-full.jsonl as local backup
- Flushes on SIGTERM/exit so no data lost when process is killed
- Re-buffers on flush failure to prevent data loss

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Files changed (1) hide show
  1. scripts/conversation-loop.py +73 -0
scripts/conversation-loop.py CHANGED
@@ -948,6 +948,69 @@ def post_chatlog(entries):
948
  pass
949
 
950
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
951
  def set_bubble(url, text_en, text_zh=""):
952
  try:
953
  requests.post(f"{url}/api/bubble",
@@ -1322,6 +1385,7 @@ def do_turn(speaker, other, space_url):
1322
 
1323
  set_bubble(space_url, en, zh)
1324
  post_chatlog(history)
 
1325
  return True
1326
 
1327
 
@@ -1334,6 +1398,14 @@ def do_turn(speaker, other, space_url):
1334
  # 5. History trimmed to MAX_HISTORY (24) to control context window
1335
  # ══════════════════════════════════════════════════════════════════════════════
1336
 
 
 
 
 
 
 
 
 
1337
  print("\n" + "="*60)
1338
  print(" Adam & Eve β€” Multi-Action Agents (GLM-4.5)")
1339
  print(" Up to 5 actions/turn, sub-agent delegation, parallel work")
@@ -1376,6 +1448,7 @@ if reply:
1376
  history.append(entry)
1377
  set_bubble(ADAM_SPACE, en, zh)
1378
  post_chatlog(history)
 
1379
 
1380
  time.sleep(20)
1381
 
 
948
  pass
949
 
950
 
951
+ # ── Persistent conversation log β†’ HF Dataset ──────────────────────────────
952
+ HOME_DATASET_ID = "tao-shen/HuggingClaw-Home-data"
953
+ CHATLOG_PATH = "conversation-log/chatlog.jsonl"
954
+ _chatlog_buffer = [] # Buffer entries, flush every N turns to avoid API spam
955
+ CHATLOG_FLUSH_INTERVAL = 3 # Flush every 3 turns
956
+
957
+ def persist_turn(speaker, turn_num, text_en, text_zh, actions, workflow_state_str, child_stage):
958
+ """Append a turn record to buffer. Flush to HF Dataset periodically."""
959
+ import datetime
960
+ record = {
961
+ "timestamp": datetime.datetime.utcnow().isoformat() + "Z",
962
+ "turn": turn_num,
963
+ "speaker": speaker,
964
+ "text_en": text_en,
965
+ "text_zh": text_zh,
966
+ "actions": [{"action": a["action"], "result": a["result"][:500]} for a in actions],
967
+ "workflow_state": workflow_state_str,
968
+ "child_stage": child_stage,
969
+ }
970
+ _chatlog_buffer.append(json.dumps(record, ensure_ascii=False))
971
+
972
+ # Also append to local file as backup
973
+ try:
974
+ with open("/tmp/conversation-loop-full.jsonl", "a") as f:
975
+ f.write(_chatlog_buffer[-1] + "\n")
976
+ except:
977
+ pass
978
+
979
+ # Flush to HF Dataset every N turns
980
+ if len(_chatlog_buffer) >= CHATLOG_FLUSH_INTERVAL:
981
+ flush_chatlog()
982
+
983
+
984
+ def flush_chatlog():
985
+ """Upload buffered entries to HF Dataset by appending to the jsonl file."""
986
+ global _chatlog_buffer
987
+ if not _chatlog_buffer:
988
+ return
989
+ batch = "\n".join(_chatlog_buffer) + "\n"
990
+ _chatlog_buffer = []
991
+ try:
992
+ # Try to download existing file and append
993
+ existing = ""
994
+ try:
995
+ dl = hf_hub_download(HOME_DATASET_ID, CHATLOG_PATH,
996
+ repo_type="dataset", token=HF_TOKEN)
997
+ with open(dl) as f:
998
+ existing = f.read()
999
+ except:
1000
+ pass # File doesn't exist yet, start fresh
1001
+ combined = existing + batch
1002
+ hf_api.upload_file(
1003
+ path_or_fileobj=io.BytesIO(combined.encode()),
1004
+ path_in_repo=CHATLOG_PATH,
1005
+ repo_id=HOME_DATASET_ID, repo_type="dataset",
1006
+ )
1007
+ print(f"[PERSIST] Flushed {batch.count(chr(10))} turn(s) to {HOME_DATASET_ID}/{CHATLOG_PATH}")
1008
+ except Exception as e:
1009
+ # Re-buffer on failure so we don't lose data
1010
+ _chatlog_buffer = batch.strip().split("\n") + _chatlog_buffer
1011
+ print(f"[PERSIST] Flush failed: {e}")
1012
+
1013
+
1014
  def set_bubble(url, text_en, text_zh=""):
1015
  try:
1016
  requests.post(f"{url}/api/bubble",
 
1385
 
1386
  set_bubble(space_url, en, zh)
1387
  post_chatlog(history)
1388
+ persist_turn(speaker, turn_count, en, zh, action_results, workflow_state, child_state["stage"])
1389
  return True
1390
 
1391
 
 
1398
  # 5. History trimmed to MAX_HISTORY (24) to control context window
1399
  # ══════════════════════════════════════════════════════════════════════════════
1400
 
1401
+ # Flush conversation log on exit (SIGTERM from kill, or normal exit)
1402
+ import atexit, signal
1403
+ atexit.register(flush_chatlog)
1404
+ def _signal_flush(signum, frame):
1405
+ flush_chatlog()
1406
+ sys.exit(0)
1407
+ signal.signal(signal.SIGTERM, _signal_flush)
1408
+
1409
  print("\n" + "="*60)
1410
  print(" Adam & Eve β€” Multi-Action Agents (GLM-4.5)")
1411
  print(" Up to 5 actions/turn, sub-agent delegation, parallel work")
 
1448
  history.append(entry)
1449
  set_bubble(ADAM_SPACE, en, zh)
1450
  post_chatlog(history)
1451
+ persist_turn("Adam", 0, en, zh, actions, workflow_state, child_state["stage"])
1452
 
1453
  time.sleep(20)
1454