fix(agent): rebase MoA prepared request even when guidance is empty

guidance=None is a real prepared shape (all references failed / silent
degraded policy builds prepared_request without attaching guidance), and
the MoA facade sends prepared['messages'] — not api_kwargs['messages'].
Gating the rebase on 'and guidance' left the stale decoration in the
prepared object for the no-guidance MoA sub-path, so #72626 persisted
there. rebase_prepared_request already handles falsy guidance (copies
messages, skips the attach).
This commit is contained in:
kshitijk4poor 2026-07-28 00:26:20 +05:00 committed by kshitij
parent bfd82660b5
commit f9be15d0f9
2 changed files with 41 additions and 1 deletions

View file

@ -926,8 +926,14 @@ def _redecorate_prompt_cache_for_provider(
if (
prepared is not None
and getattr(agent, "provider", None) == "moa"
and guidance
):
# No `and guidance` here: guidance=None is a real prepared shape
# (all-references-failed / silent degraded policy builds the
# prepared request without attaching guidance), and the MoA facade
# sends prepared["messages"] — not api_kwargs["messages"] — so the
# rebase must refresh the prepared object even when there is no
# guidance to re-attach. rebase_prepared_request handles falsy
# guidance by copying the messages and skipping the attach.
completions = getattr(getattr(agent.client, "chat", None), "completions", None)
rebase = getattr(completions, "rebase_prepared_request", None)
if callable(rebase):

View file

@ -376,3 +376,37 @@ class TestRedecoratePromptCacheOnPolicyChange:
else:
assert guidance in content
def test_moa_no_guidance_prepared_messages_still_refreshed(self):
# guidance=None is a real prepared shape (all references failed /
# silent degraded policy). The MoA facade sends prepared["messages"],
# so the rebase must refresh the prepared object even without
# guidance — otherwise the aggregator ships the STALE decoration
# and #72626 persists for the no-guidance MoA sub-path.
prompt = "sys"
decorated = apply_anthropic_cache_control(
[
{"role": "system", "content": prompt},
{"role": "user", "content": "task"},
],
native_anthropic=True,
)
class _Completions:
def rebase_prepared_request(self, prepared, messages):
# Mirrors MoAChatCompletions.rebase_prepared_request with
# falsy guidance: copy messages, skip the attach.
return {**prepared, "messages": [dict(m) for m in messages]}
# Policy change while staying on moa: cache-on -> cache-off.
agent = _cache_agent(use_caching=False, prompt=prompt, provider="moa")
agent.client = SimpleNamespace(chat=SimpleNamespace(completions=_Completions()))
prepared = {"guidance": None, "messages": decorated}
out, new_prepared = _redecorate_prompt_cache_for_provider(
agent, decorated, moa_prepared=prepared
)
assert new_prepared is not None
# The prepared object the facade will send must carry the
# redecorated (stripped) messages, not the stale decorated list.
assert _count_cache_markers(new_prepared["messages"]) == 0
assert new_prepared["messages"] == out