tao-shen Claude Opus 4.6 commited on
Commit
869a98d
Β·
1 Parent(s): a2fe46e

perf: major efficiency overhaul for conversation-loop mechanism

Browse files

1. Dynamic cooldown: auto-clears when build finishes instead of
fixed 10min wait. Polls HF API to detect BUILDING→RUNNING/ERROR.

2. Smart wait during BUILDING: skips LLM API calls entirely when
Cain is building/restarting. Polls health every 20s instead,
saving API costs and avoiding wasted turns.

3. Reduced base cooldown from 600s to 360s (6 min).

4. Knowledge cache clearing: after write_file, the file is removed
from the "already read" cache so agents can re-read to verify.

5. Per-cycle file write dedup: blocks writing the same Space file
twice in one build cycle, preventing overwrites.

6. Cycle reset on RUNNING: write dedup set clears when Cain
successfully enters RUNNING state.

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

Files changed (1) hide show
  1. scripts/conversation-loop.py +70 -7
scripts/conversation-loop.py CHANGED
@@ -130,8 +130,28 @@ child_state = {
130
  }
131
 
132
  # Rebuild cooldown β€” prevent rapid write_file to Space that keeps resetting builds
133
- REBUILD_COOLDOWN_SECS = 600 # 10 minutes
134
  last_rebuild_trigger_at = 0 # timestamp of last write_file to space
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
 
136
 
137
  def init_child_state():
@@ -217,6 +237,7 @@ def action_check_health():
217
  child_state["state"] = data.get("state", "unknown")
218
  child_state["detail"] = data.get("detail", "")
219
  child_state["stage"] = "RUNNING"
 
220
  return (f"{CHILD_NAME} is ALIVE! State: {child_state['state']}, "
221
  f"Detail: {child_state['detail'] or 'healthy'}")
222
  except:
@@ -287,7 +308,7 @@ def action_restart():
287
  child_state["alive"] = False
288
  child_state["stage"] = "RESTARTING"
289
  last_rebuild_trigger_at = time.time()
290
- return f"{CHILD_NAME} is restarting. Will take a few minutes. 10-min cooldown starts now."
291
  except Exception as e:
292
  return f"Restart failed: {e}"
293
 
@@ -354,7 +375,7 @@ def action_write_file(target, path, content):
354
  rebuild_note = ""
355
  if target == "space":
356
  last_rebuild_trigger_at = time.time()
357
- rebuild_note = " ⚠️ This triggers a Space rebuild! 10-min cooldown starts now."
358
  return f"βœ“ Wrote {len(content)} bytes to {CHILD_NAME}'s {target}:{path}{rebuild_note}"
359
  except Exception as e:
360
  return f"Error writing {target}:{path}: {e}"
@@ -440,18 +461,27 @@ def parse_and_execute_actions(raw_text):
440
  if write_match:
441
  target, path, content = write_match.group(1), write_match.group(2).strip(), write_match.group(3).strip()
442
  key = f"write_file:{target}:{path}"
 
443
  if key not in executed:
444
  executed.add(key)
 
 
 
 
 
 
 
445
  # Guard: block write_file during BUILDING/APP_STARTING/RESTARTING
446
- if target == "space" and child_state["stage"] in ("BUILDING", "RESTARTING", "APP_STARTING"):
447
  result = (f"β›” BLOCKED: Cain is currently {child_state['stage']}. "
448
  "Writing to Space during build RESETS the entire build from scratch. "
449
  "Wait for it to finish, then try again.")
450
  results.append({"action": key, "result": result})
451
  print(f"[BLOCKED] {key} β€” Cain is {child_state['stage']}")
452
- # Guard: rebuild cooldown
453
  elif target == "space" and last_rebuild_trigger_at > 0:
454
- elapsed = time.time() - last_rebuild_trigger_at
 
455
  if elapsed < REBUILD_COOLDOWN_SECS:
456
  remaining = int(REBUILD_COOLDOWN_SECS - elapsed)
457
  result = (f"β›” BLOCKED: Rebuild cooldown active ({remaining}s remaining). "
@@ -462,10 +492,16 @@ def parse_and_execute_actions(raw_text):
462
  result = action_write_file(target, path, content)
463
  results.append({"action": key, "result": result})
464
  print(f"[ACTION] {key} β†’ {result[:100]}")
 
 
 
465
  else:
466
  result = action_write_file(target, path, content)
467
  results.append({"action": key, "result": result})
468
  print(f"[ACTION] {key} β†’ {result[:100]}")
 
 
 
469
 
470
  # 2. Handle all [ACTION/Action/ζ“δ½œ/动作: ...] tags β€” case-insensitive, multilingual
471
  for match in re.finditer(r'\[(?:ACTION|Action|action|ζ“δ½œ|动作)\s*[::]\s*([^\]]+)\]', raw_text):
@@ -501,7 +537,8 @@ def parse_and_execute_actions(raw_text):
501
 
502
  # Rebuild cooldown β€” prevent writing to Space repo too soon after last rebuild trigger
503
  if name in ("write_file", "set_env", "set_secret", "restart") and last_rebuild_trigger_at > 0:
504
- elapsed = time.time() - last_rebuild_trigger_at
 
505
  if elapsed < REBUILD_COOLDOWN_SECS:
506
  remaining = int(REBUILD_COOLDOWN_SECS - elapsed)
507
  result = (f"β›” BLOCKED: Rebuild cooldown active β€” last Space change was {int(elapsed)}s ago. "
@@ -1114,8 +1151,34 @@ if reply:
1114
  time.sleep(15)
1115
 
1116
  while True:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1117
  do_turn("Eve", "Adam", EVE_SPACE)
1118
  time.sleep(15)
 
 
 
 
 
 
 
1119
  do_turn("Adam", "Eve", ADAM_SPACE)
1120
 
1121
  if len(history) > MAX_HISTORY:
 
130
  }
131
 
132
  # Rebuild cooldown β€” prevent rapid write_file to Space that keeps resetting builds
133
+ REBUILD_COOLDOWN_SECS = 360 # 6 minutes (builds typically finish in 3-5 min)
134
  last_rebuild_trigger_at = 0 # timestamp of last write_file to space
135
+ files_written_this_cycle = set() # track files written since last RUNNING state
136
+
137
+ def check_and_clear_cooldown():
138
+ """Auto-clear cooldown if Cain has finished building (dynamic cooldown)."""
139
+ global last_rebuild_trigger_at
140
+ if last_rebuild_trigger_at == 0:
141
+ return
142
+ elapsed = time.time() - last_rebuild_trigger_at
143
+ if elapsed < 60: # always wait at least 60s
144
+ return
145
+ try:
146
+ info = hf_api.space_info(CHILD_SPACE_ID)
147
+ stage = info.runtime.stage if info.runtime else "unknown"
148
+ if stage in ("RUNNING", "RUNTIME_ERROR", "BUILD_ERROR"):
149
+ print(f"[COOLDOWN] Build finished (stage={stage}), clearing cooldown early ({int(elapsed)}s elapsed)")
150
+ last_rebuild_trigger_at = 0
151
+ child_state["stage"] = stage
152
+ child_state["alive"] = (stage == "RUNNING")
153
+ except:
154
+ pass
155
 
156
 
157
  def init_child_state():
 
237
  child_state["state"] = data.get("state", "unknown")
238
  child_state["detail"] = data.get("detail", "")
239
  child_state["stage"] = "RUNNING"
240
+ files_written_this_cycle.clear() # reset write dedup on successful run
241
  return (f"{CHILD_NAME} is ALIVE! State: {child_state['state']}, "
242
  f"Detail: {child_state['detail'] or 'healthy'}")
243
  except:
 
308
  child_state["alive"] = False
309
  child_state["stage"] = "RESTARTING"
310
  last_rebuild_trigger_at = time.time()
311
+ return f"{CHILD_NAME} is restarting. Will take a few minutes. Cooldown starts now (clears automatically when build finishes)."
312
  except Exception as e:
313
  return f"Restart failed: {e}"
314
 
 
375
  rebuild_note = ""
376
  if target == "space":
377
  last_rebuild_trigger_at = time.time()
378
+ rebuild_note = " ⚠️ This triggers a Space rebuild! Cooldown starts now (auto-clears when build finishes)."
379
  return f"βœ“ Wrote {len(content)} bytes to {CHILD_NAME}'s {target}:{path}{rebuild_note}"
380
  except Exception as e:
381
  return f"Error writing {target}:{path}: {e}"
 
461
  if write_match:
462
  target, path, content = write_match.group(1), write_match.group(2).strip(), write_match.group(3).strip()
463
  key = f"write_file:{target}:{path}"
464
+ file_id = f"{target}:{path}"
465
  if key not in executed:
466
  executed.add(key)
467
+ # Guard: duplicate write to same file this cycle
468
+ if target == "space" and file_id in files_written_this_cycle:
469
+ result = (f"β›” BLOCKED: {path} was already written this cycle. "
470
+ "Wait for the build to finish and verify before writing again. "
471
+ "Writing the same file twice wastes a rebuild cycle.")
472
+ results.append({"action": key, "result": result})
473
+ print(f"[BLOCKED] {key} β€” duplicate write this cycle")
474
  # Guard: block write_file during BUILDING/APP_STARTING/RESTARTING
475
+ elif target == "space" and child_state["stage"] in ("BUILDING", "RESTARTING", "APP_STARTING"):
476
  result = (f"β›” BLOCKED: Cain is currently {child_state['stage']}. "
477
  "Writing to Space during build RESETS the entire build from scratch. "
478
  "Wait for it to finish, then try again.")
479
  results.append({"action": key, "result": result})
480
  print(f"[BLOCKED] {key} β€” Cain is {child_state['stage']}")
481
+ # Guard: rebuild cooldown (check dynamically first)
482
  elif target == "space" and last_rebuild_trigger_at > 0:
483
+ check_and_clear_cooldown() # may clear cooldown early if build done
484
+ elapsed = time.time() - last_rebuild_trigger_at if last_rebuild_trigger_at > 0 else 9999
485
  if elapsed < REBUILD_COOLDOWN_SECS:
486
  remaining = int(REBUILD_COOLDOWN_SECS - elapsed)
487
  result = (f"β›” BLOCKED: Rebuild cooldown active ({remaining}s remaining). "
 
492
  result = action_write_file(target, path, content)
493
  results.append({"action": key, "result": result})
494
  print(f"[ACTION] {key} β†’ {result[:100]}")
495
+ files_written_this_cycle.add(file_id)
496
+ # Clear knowledge cache so agents can re-read the file they just wrote
497
+ knowledge["files_read"].discard(file_id)
498
  else:
499
  result = action_write_file(target, path, content)
500
  results.append({"action": key, "result": result})
501
  print(f"[ACTION] {key} β†’ {result[:100]}")
502
+ if target == "space":
503
+ files_written_this_cycle.add(file_id)
504
+ knowledge["files_read"].discard(file_id)
505
 
506
  # 2. Handle all [ACTION/Action/ζ“δ½œ/动作: ...] tags β€” case-insensitive, multilingual
507
  for match in re.finditer(r'\[(?:ACTION|Action|action|ζ“δ½œ|动作)\s*[::]\s*([^\]]+)\]', raw_text):
 
537
 
538
  # Rebuild cooldown β€” prevent writing to Space repo too soon after last rebuild trigger
539
  if name in ("write_file", "set_env", "set_secret", "restart") and last_rebuild_trigger_at > 0:
540
+ check_and_clear_cooldown() # may clear cooldown early if build done
541
+ elapsed = time.time() - last_rebuild_trigger_at if last_rebuild_trigger_at > 0 else 9999
542
  if elapsed < REBUILD_COOLDOWN_SECS:
543
  remaining = int(REBUILD_COOLDOWN_SECS - elapsed)
544
  result = (f"β›” BLOCKED: Rebuild cooldown active β€” last Space change was {int(elapsed)}s ago. "
 
1151
  time.sleep(15)
1152
 
1153
  while True:
1154
+ # Smart wait: if Cain is BUILDING/APP_STARTING, skip LLM calls and just poll
1155
+ if child_state["stage"] in ("BUILDING", "RESTARTING", "APP_STARTING"):
1156
+ print(f"[WAIT] Cain is {child_state['stage']} β€” polling health instead of LLM call...")
1157
+ check_and_clear_cooldown()
1158
+ # Quick health check to update stage
1159
+ try:
1160
+ info = hf_api.space_info(CHILD_SPACE_ID)
1161
+ new_stage = info.runtime.stage if info.runtime else "unknown"
1162
+ if new_stage != child_state["stage"]:
1163
+ print(f"[WAIT] Stage changed: {child_state['stage']} β†’ {new_stage}")
1164
+ child_state["stage"] = new_stage
1165
+ child_state["alive"] = (new_stage == "RUNNING")
1166
+ else:
1167
+ print(f"[WAIT] Still {new_stage}... waiting 20s")
1168
+ except Exception as e:
1169
+ print(f"[WAIT] Health check error: {e}")
1170
+ time.sleep(20)
1171
+ continue
1172
+
1173
  do_turn("Eve", "Adam", EVE_SPACE)
1174
  time.sleep(15)
1175
+
1176
+ # Check if we just triggered a build β€” skip Adam's turn if so
1177
+ if child_state["stage"] in ("BUILDING", "RESTARTING", "APP_STARTING"):
1178
+ print(f"[SKIP] Cain entered {child_state['stage']} β€” skipping Adam's turn to avoid wasted LLM call")
1179
+ time.sleep(10)
1180
+ continue
1181
+
1182
  do_turn("Adam", "Eve", ADAM_SPACE)
1183
 
1184
  if len(history) > MAX_HISTORY: