fix(simulation): Resolve simulation timeouts and stabilize history checks
This commit is contained in:
@@ -45,11 +45,15 @@ class ContextSimulation(BaseSimulation):
|
||||
msg = "What is the current date and time? Answer in one sentence."
|
||||
print(f"[Sim] Sending message: {msg}")
|
||||
self.sim.run_discussion_turn(msg)
|
||||
time.sleep(10)
|
||||
# 4. Verify History
|
||||
print("[Sim] Verifying history...")
|
||||
session = self.client.get_session()
|
||||
entries = session.get('session', {}).get('entries', [])
|
||||
if not entries:
|
||||
print("[Sim] !!! WARNING: entries list is EMPTY. Waiting another 2 seconds for eventual consistency...")
|
||||
time.sleep(2)
|
||||
session = self.client.get_session()
|
||||
entries = session.get('session', {}).get('entries', [])
|
||||
# We expect at least 2 entries (User and AI)
|
||||
assert len(entries) >= 2, f"Expected at least 2 entries, found {len(entries)}"
|
||||
assert entries[-2]['role'] == 'User', "Expected second to last entry to be User"
|
||||
@@ -61,9 +65,9 @@ class ContextSimulation(BaseSimulation):
|
||||
time.sleep(1)
|
||||
session = self.client.get_session()
|
||||
entries = session.get('session', {}).get('entries', [])
|
||||
# Truncating to 1 pair means 2 entries max (if it's already at 2, it might not change,
|
||||
# but if we had more, it would).
|
||||
assert len(entries) <= 2, f"Expected <= 2 entries after truncation, found {len(entries)}"
|
||||
print(f"[DEBUG] Entries after truncation: {entries}")
|
||||
chat_entries = [e for e in entries if e.get('role') in ('User', 'AI')]
|
||||
assert len(chat_entries) == 2, f"Expected exactly 2 chat entries after truncation, found {len(chat_entries)}"
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_sim(ContextSimulation)
|
||||
|
||||
@@ -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}")
|
||||
|
||||
Reference in New Issue
Block a user