fix(moa): scope the non-text placeholder to structured content only

Follow-up to the cherry-picked empty-user-turn drop: the placeholder
introduced in 8582f35d9 fired for whitespace-only STRING turns too
(content='   ' flattens to non-stripping text but isn't in the
(None, '', []) exclusion set), fabricating an attachment note for a turn
that carried nothing. Gate the placeholder on isinstance(content, list)
so only genuinely structured (e.g. image-only) turns get it; empty and
whitespace-only string turns now fall through to the drop path.

Edge cases verified: trailing empty user turn still ends the view on the
synthetic advisory marker; an all-empty transcript degenerates to [].
This commit is contained in:
Teknium 2026-07-14 05:31:07 -07:00
parent b4c2c4f922
commit b013ed03e5
2 changed files with 28 additions and 2 deletions

View file

@ -491,7 +491,7 @@ def _reference_messages(messages: list[dict[str, Any]]) -> list[dict[str, Any]]:
if role == "system":
continue
if role == "user":
if not text.strip() and content not in (None, "", []):
if not text.strip() and isinstance(content, list) and content:
# Structured content with no extractable text (e.g. an
# image-only turn). Emitting an empty user message would be
# dropped/rejected by strict providers (Anthropic 400s on
@ -499,7 +499,9 @@ def _reference_messages(messages: list[dict[str, Any]]) -> list[dict[str, Any]]:
# mode), and silently skipping the turn would break
# user/assistant alternation in the advisory view. Substitute
# a placeholder so the reference knows a non-text turn
# happened.
# happened. Only structured content qualifies — an empty or
# whitespace-only STRING turn carries nothing and is dropped
# below instead.
text = "[user sent non-text content (e.g. an image attachment)]"
if not text.strip():
# Genuinely empty user turn (content="" / None). It carries