mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-11-12 14:00:35 +00:00
* fix(agents): drop empty text items from sanitized list content
InputSanitizationMiddleware merges list-form user content into one typed
{"type": "text"} block, but only replaces the items it collected. Empty
bare strings and empty text blocks are skipped by the extractor (matching
message_content_to_text) and then re-emitted as-is, so a request whose
content was ["", {"type": "text", ...}] still carried a bare "" content
part. The Chat Completions schema only allows typed content-part objects
in a content list, so endpoints that validate against it rejected the
whole turn — and kept rejecting every later call that resent the message.
Empty text items are now recognised during extraction and dropped when the
content list is rebuilt: leading, interleaved and trailing positions. A
list made only of empty items collapses to one empty typed block, and an
image-only message still passes through untouched. Nothing else changes:
non-text blocks keep their positions, and the transform stays
request-scoped.
* fix(agents): drop empty text items when rebuilding sanitized content
InputSanitizationMiddleware merges list-form user content into one typed
{"type": "text"} block, but it only replaced the items it had collected.
Empty bare strings and empty text blocks were skipped during extraction
(matching message_content_to_text) and then re-emitted unchanged, so a
message whose content was ["", {"type": "text", ...}] still carried a bare
"" content part. The Chat Completions schema only allows typed content-part
objects inside a content list, so endpoints that validate against it
rejected the whole turn, and kept rejecting every later model call that
resent the message.
Empty text items are now collected during extraction (they are text-shaped,
so the rebuild is what has to drop them) and therefore disappear in all
positions: leading, interleaved and trailing. A list made only of empty
items collapses to one empty typed block instead of leaking the bare
string, and an image-only message still passes through untouched.
Non-text blocks keep their existing placement rule, and the transform stays
request-scoped.
* docs(middleware): state that empty typed text blocks are dropped
The #8038 scope note in middlewares/AGENTS.md still said empty typed text
blocks remain unchanged and that providers can still reject them. This
change drops those blocks too, so the note described the opposite of what
the code does. Rewrite the summary and that bullet to match, and keep the
remaining limits explicit: user content only, and an empty user turn can
still be rejected.
* test(middleware): keep the legacy cases out of the new class and pin keep-alive
Inserting TestEmptyTextBlockItemsAreDropped before the existing
already-wrapped, graph-bubble-up and fail-open cases silently moved those
three tests into the new class, changing their node IDs and putting them
under a docstring about empty typed text blocks. Place the new class after
test_fail_open_on_processing_error so the legacy tests keep their home.
Also pin the boundary the sweep must not cross: a whitespace-only part,
typed or bare, carries no words but still keeps the turn alive. Assert
both shapes survive next to an image so tightening not block to not
block.strip() fails here rather than turning an empty user turn into a
rejected one.
* docs(middleware): keep the empty-text note inside the guidance size limit
The added paragraph pushed the effective AGENTS.md chain 55 bytes past its hard limit. Condense the same facts instead of adding a new line to the budget.
---------
Co-authored-by: 2SinceJuly <202121119+2SinceJuly@users.noreply.github.com>