fix(moa): skip aggregator synthesis when all references fail

When every MoA reference model returns a failure (HTTP error, timeout,
etc.) or is skipped by the recursion guard, the one-shot aggregator
synthesis call is now skipped entirely. Previously it would try to
synthesise a wall of failure sentinels, which could block for the full
provider timeout (observed ~6 min on SenseNova) before returning a
non-retryable error that left the session hanging.

The early return carries the sanitized unavailability notice (never raw
provider error text, per the failed-reference containment) so the main
agent loop can still act in single-model mode.

Salvaged from #56975, reworked atop the _is_failed_reference helpers.
This commit is contained in:
liuhao1024 2026-07-23 12:17:53 -07:00 committed by Teknium
parent d3fc27bbf8
commit f0ed77b627
2 changed files with 82 additions and 0 deletions

View file

@ -963,6 +963,26 @@ def aggregate_moa_context(
degraded = _degraded_notice(failed_labels, degraded_reference_policy)
if degraded:
joined = f"{joined}\n\n{degraded}" if joined else degraded
# Skip the aggregator call when every reference failed or was skipped —
# synthesising over zero real advice wastes tokens and can block for the
# full provider timeout (observed: ~6 min on SenseNova) before returning
# a non-retryable error that leaves the session hanging. The early return
# carries only the sanitized unavailability notice (never raw provider
# error text) so the main agent loop can still act in single-model mode.
if reference_outputs and not successful_outputs:
logger.warning(
"MoA: all %d reference(s) failed — skipping aggregator synthesis",
len(reference_outputs),
)
notice = degraded or "[Reference models unavailable]"
return (
"[Mixture of Agents context — all reference models failed. "
"Proceeding without aggregated guidance.]\n"
f"References: {', '.join(_slot_label(slot) for slot in reference_models)}\n\n"
f"{notice}"
)
synth_prompt = (
"You are the aggregator in a Mixture of Agents process. Synthesize the "
"reference responses into concise, actionable guidance for the main "

View file

@ -2067,3 +2067,65 @@ def test_run_reference_forwards_configured_timeout(monkeypatch):
assert (label, text) == ("openrouter:advisor", "advice")
assert calls[0]["timeout"] == 23.5
assert isinstance(accounting, moa_loop._RefAccounting)
def test_aggregate_skips_aggregator_when_all_references_failed(monkeypatch):
"""When every reference returns [failed: …], the aggregator is skipped entirely."""
from agent.moa_loop import aggregate_moa_context
call_count = {"n": 0}
def fake_call_llm(**kwargs):
call_count["n"] += 1
if kwargs["task"] == "moa_reference":
raise RuntimeError("provider down key=super-secret")
raise AssertionError("aggregator should not be called when all references fail")
monkeypatch.setattr("agent.moa_loop.call_llm", fake_call_llm)
monkeypatch.setattr(
"agent.moa_loop._slot_runtime",
lambda slot: {"provider": slot["provider"], "model": slot["model"]},
)
result = aggregate_moa_context(
user_prompt="do something",
api_messages=[{"role": "user", "content": "do something"}],
reference_models=[
{"provider": "openai", "model": "gpt-4"},
{"provider": "anthropic", "model": "claude-opus"},
],
aggregator={"provider": "openrouter", "model": "anthropic/claude-opus-4.8"},
)
# The aggregator LLM call was never made.
assert call_count["n"] == 2 # only the two reference calls
# The result carries a sanitized unavailability notice (never raw
# provider error text) so the main agent can still act.
assert "all reference models failed" in result
assert "Reference models unavailable" in result
assert "super-secret" not in result
def test_aggregate_skips_aggregator_when_all_references_skipped(monkeypatch):
"""References that are skipped (MoA recursion guard) also trigger the early return."""
from agent.moa_loop import aggregate_moa_context
def fake_call_llm(**kwargs):
raise AssertionError("aggregator should not be called when all references are skipped")
monkeypatch.setattr("agent.moa_loop.call_llm", fake_call_llm)
# Both reference models are "moa" — they hit the recursion guard and are
# returned as "[skipped: …]" without calling call_llm at all.
result = aggregate_moa_context(
user_prompt="do something",
api_messages=[{"role": "user", "content": "do something"}],
reference_models=[
{"provider": "moa", "model": "preset-a"},
{"provider": "moa", "model": "preset-b"},
],
aggregator={"provider": "openrouter", "model": "anthropic/claude-opus-4.8"},
)
assert "all reference models failed" in result
assert "Reference models unavailable" in result