mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-10-27 05:30:39 +00:00
fix(serper): report a format error for results with no usable objects (#6779)
* fix(serper): report a format error for results with no usable objects `_response_items` skipped non-object entries but returned the filtered list even when it was empty, so a non-empty `organic`/`images` array holding no objects fell through to "No results found" / "No images found" -- telling the agent the query legitimately matched nothing instead of surfacing the malformed payload. Treat a non-empty container with no object entries as malformed, matching the containment rule #6603 documents for this provider family (brave #6550, groundroute #6603, tavily #6645, webz). `web_search` and `image_search` share this helper, so both now report the provider's format error. Missing, null, and empty containers keep reading as an empty search, and mixed lists still keep their valid entries in order. * fix: address review and validation findings for PR 6779
This commit is contained in:
parent
ac9ed11b8b
commit
3195ed1f09
@ -182,6 +182,8 @@ streams simulate chunks from a non-streaming response, while no-tool streams sta
|
||||
|
||||
Sofya web search and fetch contain malformed HTTP-success payloads the same way: a non-object payload, a `results` container that is not a list, or a non-empty list with no object entries returns the provider's format error, while non-object entries in an otherwise valid list are skipped in order. Missing, null, or empty results keep the existing "No results found" response.
|
||||
|
||||
Serper web and image search preserve valid entries in mixed result lists. A malformed `organic`/`images` container, or a non-empty list holding no object entries, returns Serper's format error; missing, null, or empty containers keep the existing "No results found" / "No images found" response. Format errors log the container field name only, without queries, credentials, or payload values.
|
||||
|
||||
Tencent Cloud WSA web search contains malformed HTTP-success payloads the same way: a `Pages` container that is not a list, or a non-empty container holding no usable page, returns the provider's format error, while malformed page entries in an otherwise valid container are skipped so the valid rows survive. Missing, null, or empty `Pages` keep the existing "No results found" response.
|
||||
|
||||
Serply web search contains malformed HTTP-success payloads the same way in every vertical: a `results`/`entries`/`articles` container that is not a list, or a non-empty list with no object entries, returns the provider's format error, while non-object entries in an otherwise valid list are skipped in order. Missing, null, or empty containers keep the existing "No results found" response.
|
||||
|
||||
@ -90,7 +90,14 @@ def _response_items(data: dict, field: str, query: str) -> tuple[list[dict] | No
|
||||
if not isinstance(items, list):
|
||||
logger.error("Serper returned unexpected '%s' payload type: %s", field, type(items).__name__)
|
||||
return None, _unexpected_format_error(query)
|
||||
return [item for item in items if isinstance(item, dict)], None
|
||||
usable = [item for item in items if isinstance(item, dict)]
|
||||
if items and not usable:
|
||||
# Same containment rule as groundroute/tavily: a populated container
|
||||
# holding nothing usable is malformed, not an empty search. Reporting
|
||||
# "No results found" tells the agent the query matched nothing.
|
||||
logger.error("Serper returned '%s' with no usable result objects", field)
|
||||
return None, _unexpected_format_error(query)
|
||||
return usable, None
|
||||
|
||||
|
||||
def _clean_query(query: str) -> str:
|
||||
|
||||
@ -776,6 +776,25 @@ class TestWebSearchTool:
|
||||
assert parsed["total_results"] == 1
|
||||
assert parsed["results"][0]["title"] == "T"
|
||||
|
||||
def test_organic_with_no_usable_objects_returns_error(self, mock_config_with_key):
|
||||
"""A populated container holding nothing usable is malformed, not an empty search.
|
||||
|
||||
Mirrors groundroute/tavily: reporting "No results found" would tell the
|
||||
agent the query legitimately matched nothing.
|
||||
"""
|
||||
mock_resp = _make_serper_response(["bad", 3, None, ["nested"]])
|
||||
|
||||
with patch("deerflow.community.serper.tools.httpx.Client") as mock_client_cls:
|
||||
mock_client_cls.return_value.__enter__.return_value.post.return_value = mock_resp
|
||||
|
||||
from deerflow.community.serper.tools import web_search_tool
|
||||
|
||||
result = web_search_tool.invoke({"query": "test"})
|
||||
parsed = json.loads(result)
|
||||
|
||||
assert parsed["error"] == "Serper returned an unexpected response format"
|
||||
assert parsed["query"] == "test"
|
||||
|
||||
def test_timeout_returns_error(self, mock_config_with_key):
|
||||
with patch("deerflow.community.serper.tools.httpx.Client") as mock_client_cls:
|
||||
mock_client_cls.return_value.__enter__.return_value.post.side_effect = httpx.TimeoutException("Read timed out")
|
||||
@ -991,6 +1010,21 @@ class TestImageSearchTool:
|
||||
assert parsed["error"] == "No images found"
|
||||
assert parsed["query"] == "no results"
|
||||
|
||||
def test_images_with_no_usable_objects_returns_error(self, mock_config_with_key):
|
||||
"""Same containment rule on the images container, which shares the helper."""
|
||||
mock_resp = _make_serper_images_response(["https://x/y.png", 7])
|
||||
|
||||
with patch("deerflow.community.serper.tools.httpx.Client") as mock_client_cls:
|
||||
mock_client_cls.return_value.__enter__.return_value.post.return_value = mock_resp
|
||||
|
||||
from deerflow.community.serper.tools import image_search_tool
|
||||
|
||||
result = image_search_tool.invoke({"query": "no results"})
|
||||
parsed = json.loads(result)
|
||||
|
||||
assert parsed["error"] == "Serper returned an unexpected response format"
|
||||
assert parsed["query"] == "no results"
|
||||
|
||||
def test_missing_api_key_returns_error_json(self, mock_config_no_key):
|
||||
with patch.dict("os.environ", {}, clear=True):
|
||||
import os
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user