mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-10-26 13:16:24 +00:00
* fix(agents): skip content blocks with unhashable type values
has_visible_content and ParentContextSnapshot.from_state classified a
content block's type with set membership (`in {"text", "output_text"}`
plus the _MEDIA_BLOCK_TYPES frozenset). `type: []` / `type: {}` are
legal JSON from a provider, and hashing an unhashable value raises
TypeError instead of skipping the block:
- has_visible_content runs on every model response through the
llm-error-handling (_raise_for_empty_response), terminal-response and
model-length middlewares, so one malformed block turned a completed
response into a failed run instead of a skipped block;
- from_state builds the history every background subagent dispatch is
seeded from, so the same block failed the dispatch.
Probe the type by equality with tuples so a non-string type simply fails
the probe, mirroring #6614's fix for extract_response_text and the
isinstance guard clarification already uses for form field types. The
media constant becomes a tuple too, so the second probe in the same loop
cannot move the crash one line down.
Tests pin the RED-to-GREEN cycle at both call sites: unit-level
has_visible_content, middleware-level empty-response classification,
and the dispatch snapshot across human/ai/tool messages.
Fixes #6663
Signed-off-by: yetuge <2219677952@qq.com>
* fix(agents): warn when a snapshot skips a non-string-typed block
Review follow-up on #6664: with the malformed block now skipped
instead of raising, a provider that systematically emits non-string
types would degrade the child's history with nothing in the logs to
explain it. The elif chain cannot log on fall-through —
reasoning/signature and tool-use exclusions are deliberate and must
stay quiet — so the signal keys on the type not being a str, matching
the guard the review suggested.
has_visible_content keeps its silence on purpose: the empty-response
retry already surfaces the symptom there.
Fixes #6663
Signed-off-by: yetuge <2219677952@qq.com>
---------
Signed-off-by: yetuge <2219677952@qq.com>
39 lines
1.9 KiB
Python
39 lines
1.9 KiB
Python
"""Shared model-response classification helpers."""
|
|
|
|
from langchain_core.messages import AIMessage
|
|
|
|
from deerflow.agents.middlewares.model_response import has_visible_content
|
|
|
|
|
|
def test_has_visible_content_reads_plain_string_content() -> None:
|
|
assert has_visible_content(AIMessage(content="visible answer")) is True
|
|
assert has_visible_content(AIMessage(content=" \n ")) is False
|
|
assert has_visible_content(AIMessage(content=[])) is False
|
|
|
|
|
|
def test_has_visible_content_reads_text_and_output_text_blocks() -> None:
|
|
message = AIMessage(content=[{"type": "text", "text": "visible answer"}])
|
|
assert has_visible_content(message) is True
|
|
message = AIMessage(content=[{"type": "output_text", "text": "visible answer"}])
|
|
assert has_visible_content(message) is True
|
|
|
|
|
|
def test_has_visible_content_ignores_whitespace_only_and_textless_blocks() -> None:
|
|
assert has_visible_content(AIMessage(content=[{"type": "text", "text": " "}])) is False
|
|
assert has_visible_content(AIMessage(content=[{"type": "text"}])) is False
|
|
assert has_visible_content(AIMessage(content=[{"type": "text", "text": 42}])) is False
|
|
assert has_visible_content(AIMessage(content=[{"type": "reasoning", "reasoning": "thinking"}])) is False
|
|
|
|
|
|
def test_has_visible_content_keeps_visible_text_around_an_unhashable_block_type() -> None:
|
|
# `type: []` / `type: {}` are legal JSON from a provider; probing the type
|
|
# by equality skips the block instead of raising TypeError out of the
|
|
# middleware chain (llm error handling, terminal response, model length).
|
|
message = AIMessage(content=[{"type": [], "text": "ignored"}, {"type": "text", "text": "visible answer"}])
|
|
assert has_visible_content(message) is True
|
|
|
|
|
|
def test_has_visible_content_skips_unhashable_block_types_without_other_text() -> None:
|
|
message = AIMessage(content=[{"type": {}, "text": "ignored"}])
|
|
assert has_visible_content(message) is False
|