fix(simulation): Resolve simulation timeouts and stabilize history checks

This commit is contained in:
2026-03-03 00:56:35 -05:00
parent aed1f9a97e
commit dbd955a45b
4 changed files with 145 additions and 29 deletions

View File

@@ -17,6 +17,8 @@ class WorkflowSimulator:
self.client.set_value("project_git_dir", git_dir)
self.client.click("btn_project_save")
time.sleep(1)
# Force state deterministic for tests
self.client.set_value("auto_add_history", True)
def create_discussion(self, name: str) -> None:
print(f"Creating discussion: {name}")
@@ -62,29 +64,79 @@ class WorkflowSimulator:
def wait_for_ai_response(self, timeout: int = 60) -> dict | None:
print("Waiting for AI response...", end="", flush=True)
start_time = time.time()
last_print_time = start_time
last_count = len(self.client.get_session().get('session', {}).get('entries', []))
last_debug_time = 0
stalled_start_time = None
# Statuses that indicate the system is still actively processing the AI request
busy_indicators = [
"thinking", "streaming", "sending", "running powershell",
"awaiting ai", "fetching", "searching"
]
was_busy = False
while time.time() - start_time < timeout:
# Check for error status first
status = self.client.get_value("ai_status")
if status and status.lower().startswith("error"):
elapsed = time.time() - start_time
status = (self.client.get_value("ai_status") or "idle").lower()
is_busy = any(indicator in status for indicator in busy_indicators)
if is_busy:
was_busy = True
# Always fetch latest entries
session_data = self.client.get_session() or {}
entries = session_data.get('session', {}).get('entries', [])
# Find the last entry that is NOT role 'System'
non_system_entries = [e for e in entries if e.get('role') != 'System']
last_entry = non_system_entries[-1] if non_system_entries else {}
last_role = last_entry.get('role', 'none')
# AI entries for return value
current_ai_entries = [e for e in entries if e.get('role') == 'AI']
last_ai_entry = current_ai_entries[-1] if current_ai_entries else {}
if elapsed - last_debug_time >= 5:
roles = [e.get("role") for e in entries]
print(f"\n[DEBUG] {elapsed:.1f}s - status: '{status}', roles: {roles}")
last_debug_time = elapsed
if "error" in status:
print(f"\n[ABORT] GUI reported error status: {status}")
return {"role": "AI", "content": f"ERROR: {status}"}
return last_ai_entry if last_ai_entry else {"role": "AI", "content": f"ERROR: {status}"}
# Turn completion logic:
# 1. Transition: we were busy and now we are not, and the last role is AI.
# 2. Fallback: we are idle/done and the last role is AI, after some initial delay.
is_complete = False
if was_busy and not is_busy and last_role == 'AI':
is_complete = True
elif status in ("idle", "done") and last_role == 'AI' and elapsed > 2:
is_complete = True
if is_complete:
content = last_ai_entry.get('content', '')
print(f"\n[AI]: {content[:100]}...")
return last_ai_entry
if non_system_entries:
# Stall detection for 'Tool' results
if last_role == 'Tool' and not is_busy:
if stalled_start_time is None:
stalled_start_time = time.time()
elif time.time() - stalled_start_time > 5:
print("\n[STALL DETECTED] Turn stalled with Tool result. Clicking 'btn_gen_send' to continue.")
self.client.click("btn_gen_send")
stalled_start_time = time.time()
else:
stalled_start_time = None
# Maintain the 'thinking/streaming' wait loop
time.sleep(1)
print(".", end="", flush=True)
entries = self.client.get_session().get('session', {}).get('entries', [])
if time.time() - last_print_time >= 5:
print(f"\n[DEBUG] Current total entries: {len(entries)}")
last_print_time = time.time()
if len(entries) > last_count:
last_entry = entries[-1]
if last_entry.get('role') == 'AI' and last_entry.get('content'):
content = last_entry.get('content')
print(f"\n[AI]: {content[:100]}...")
if "error" in content.lower() or "blocked" in content.lower():
print("[WARN] AI response appears to contain an error message.")
return last_entry
print("\nTimeout waiting for AI")
active_disc = self.client.get_value("active_discussion")
print(f"[DEBUG] Active discussion in GUI at timeout: {active_disc}")