When a single LLM provider goes down mid-task (auth, network, quota, etc.), the conductor falls completely silent: MixinSession yields errors as text chunks (not Python exceptions), so the existing try/except guard in conductor_loop never triggers, and the conductor's only way to talk to the user (LLM-decided POST /chat tool call) is dead. Add a tiny watchdog: monitor_conductor_queue now returns the conductor agent's final done text, and conductor_loop scans the tail-1000-char window for "!!!Error:" — the prefix llmcore uses for unrecoverable errors. Tail window (not whole text) avoids false positives from earlier turns; using a character window (not last-line) matches ga.do_no_tool's content[-100:] style and tolerates the decoration (fence + "[Info] Final response to user.") that do_no_tool appends after the error chunk when its own short window check misses. When detected, push the matched error line as a system chat message so the user knows what happened. A dedup check prevents repeated warnings if multiple events fail in succession. +16 -4 lines. Only frontends/conductor.py touched. No agent_loop / llmcore changes; other frontends unaffected.
This commit is contained in:
1 parent
4c550cbb72
commit
9fda145746
1 file changed
+16
-4
+16
-4
@@ -288,13 +288,14 @@ def _auto_cleanup_loop():
|
||||
if to_abort:
|
||||
push_cards()
|
||||
|
||||
def monitor_conductor_queue(dq: "queue.Queue"):
|
||||
"""Conductor output is discarded. Visible reply must go through POST /chat only."""
|
||||
def monitor_conductor_queue(dq: "queue.Queue") -> str:
|
||||
"""Block until done. Conductor output is not surfaced to users; the returned
|
||||
text is for caller-side error detection only (#342)."""
|
||||
while True:
|
||||
item = dq.get()
|
||||
if "done" in item:
|
||||
print(f"Conductor task done")
|
||||
break
|
||||
return item.get("done", "") or ""
|
||||
|
||||
def conductor_loop():
|
||||
global conductor_agent, conductor_started
|
||||
@@ -321,7 +322,18 @@ def conductor_loop():
|
||||
prompt = conductor_prompt_from_events(events)
|
||||
dq = conductor_agent.put_task(prompt, source="conductor")
|
||||
# Block here until conductor finishes — serializes execution
|
||||
monitor_conductor_queue(dq)
|
||||
done_text = monitor_conductor_queue(dq)
|
||||
# Fallback: if conductor's last turn ended with an LLM error (yielded as
|
||||
# text by MixinSession instead of raised), surface it directly (#342).
|
||||
# Use tail-window substring check (same style as ga.do_no_tool's
|
||||
# content[-100:]); window covers the error line plus any decoration
|
||||
# (fence + "[Info] Final response to user.") appended by do_no_tool.
|
||||
tail = (done_text or '')[-1000:]
|
||||
if '!!!Error:' in tail:
|
||||
last = chat_messages[-1] if chat_messages else None
|
||||
if not (last and last.get('role') == 'system' and last.get('msg', '').startswith('⚠ LLM')):
|
||||
err = next((l for l in reversed(tail.splitlines()) if l.startswith('!!!Error:')), '')
|
||||
add_chat(f"⚠ LLM 暂不可用:{err[:200]}", role="system")
|
||||
except Exception as e:
|
||||
add_chat(f"Conductor error: {e}", role="system")
|
||||
|
||||
|
||||
Reference in new issue
Block a user