fix(tool-schemas): preserve web_search time_filter through native tool-call conversion (#2757)
This commit is contained in:
@@ -1176,6 +1176,12 @@ def function_call_to_tool_block(name: str, arguments: str) -> Optional[ToolBlock
|
|||||||
content = str(queries)
|
content = str(queries)
|
||||||
else:
|
else:
|
||||||
content = args.get("query", "")
|
content = args.get("query", "")
|
||||||
|
# Preserve the model-requested freshness filter — the web_search schema
|
||||||
|
# advertises time_filter and the executor parses {"query","time_filter"},
|
||||||
|
# but a bare query string dropped it. Mirrors the read_file JSON idiom.
|
||||||
|
tf = args.get("time_filter")
|
||||||
|
if content and isinstance(tf, str) and tf in ("day", "week", "month", "year"):
|
||||||
|
content = json.dumps({"query": content, "time_filter": tf})
|
||||||
elif tool_type == "read_file":
|
elif tool_type == "read_file":
|
||||||
# Plain path (back-compat) unless a line range is requested → JSON.
|
# Plain path (back-compat) unless a line range is requested → JSON.
|
||||||
if args.get("offset") or args.get("limit"):
|
if args.get("offset") or args.get("limit"):
|
||||||
|
|||||||
60
tests/test_web_search_time_filter.py
Normal file
60
tests/test_web_search_time_filter.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
"""Issue #2756 — a native web_search function call must preserve time_filter.
|
||||||
|
|
||||||
|
The web_search schema advertises a time_filter enum and the executor honors it
|
||||||
|
when content is JSON {"query","time_filter"}, but function_call_to_tool_block's
|
||||||
|
web_search branch emitted a bare query string and dropped time_filter. These pin
|
||||||
|
that a valid filter is passed through as JSON, while plain/invalid cases stay a
|
||||||
|
bare string (back-compat).
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
# Clean up any mocks from previous tests to ensure we load real modules.
|
||||||
|
for mod in ['src.agent_tools', 'src.tool_parsing', 'src.tool_schemas', 'src.tool_execution']:
|
||||||
|
sys.modules.pop(mod, None)
|
||||||
|
|
||||||
|
# Mock heavy database/model dependencies before importing (avoids the
|
||||||
|
# src.tool_schemas <-> src.agent_tools circular import pulling in the DB layer).
|
||||||
|
for mod in [
|
||||||
|
'sqlalchemy', 'sqlalchemy.orm', 'sqlalchemy.ext', 'sqlalchemy.ext.declarative',
|
||||||
|
'sqlalchemy.ext.hybrid', 'sqlalchemy.sql', 'sqlalchemy.sql.expression',
|
||||||
|
'src.database', 'core.models', 'core.database', 'core.auth'
|
||||||
|
]:
|
||||||
|
if mod not in sys.modules:
|
||||||
|
sys.modules[mod] = MagicMock()
|
||||||
|
|
||||||
|
import json # noqa: E402
|
||||||
|
|
||||||
|
import src.agent_tools # noqa: E402, F401
|
||||||
|
from src.tool_schemas import function_call_to_tool_block # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
def test_time_filter_is_preserved_as_json():
|
||||||
|
block = function_call_to_tool_block(
|
||||||
|
"web_search", json.dumps({"query": "openai pricing", "time_filter": "year"})
|
||||||
|
)
|
||||||
|
assert block is not None and block.tool_type == "web_search"
|
||||||
|
parsed = json.loads(block.content)
|
||||||
|
assert parsed["query"] == "openai pricing"
|
||||||
|
assert parsed["time_filter"] == "year"
|
||||||
|
|
||||||
|
|
||||||
|
def test_plain_query_stays_bare_string():
|
||||||
|
block = function_call_to_tool_block("web_search", json.dumps({"query": "openai pricing"}))
|
||||||
|
assert block.content == "openai pricing"
|
||||||
|
|
||||||
|
|
||||||
|
def test_invalid_time_filter_falls_back_to_bare_query():
|
||||||
|
block = function_call_to_tool_block(
|
||||||
|
"web_search", json.dumps({"query": "openai pricing", "time_filter": "decade"})
|
||||||
|
)
|
||||||
|
assert block.content == "openai pricing"
|
||||||
|
|
||||||
|
|
||||||
|
def test_queries_list_shape_still_carries_filter():
|
||||||
|
block = function_call_to_tool_block(
|
||||||
|
"web_search", json.dumps({"queries": ["latest gpu prices"], "time_filter": "week"})
|
||||||
|
)
|
||||||
|
parsed = json.loads(block.content)
|
||||||
|
assert parsed["query"] == "latest gpu prices"
|
||||||
|
assert parsed["time_filter"] == "week"
|
||||||
Reference in New Issue
Block a user