From 255124a1b7d586c731aca372ba05d0fc196e7314 Mon Sep 17 00:00:00 2001 From: pewdiepie-archdaemon Date: Thu, 17 Sep 2026 21:37:34 +0000 Subject: [PATCH] Keep discovered-source retrieval available after empty search followups --- src/clean_agent_preview.py | 12 ++++-- tests/test_search_exhaustion_retrieval.py | 52 +++++++++++++++++++++++ 2 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 tests/test_search_exhaustion_retrieval.py diff --git a/src/clean_agent_preview.py b/src/clean_agent_preview.py index 1ec0ebf0f..5f9396ba8 100644 --- a/src/clean_agent_preview.py +++ b/src/clean_agent_preview.py @@ -5291,11 +5291,17 @@ async def stream_preview(*, endpoint_url, model, messages, headers, turn_contrac 'source; do not repeat equivalent wording.' ) else: - suppressed_tool_until_round['web_search'] = round_number + 1 - force_no_tools_next_round = True + suppressed_tool_until_round['web_search'] = round_limit + 1 + force_no_tools_next_round = not bool(discovered_web_sources) round_recovery_messages.append( 'Two search attempts returned no usable evidence. Do not search ' - 'again this turn; report the limitation without inventing results.' + 'again this turn. ' + ( + 'Previously discovered source URLs remain available. Inspect a ' + 'relevant source with web_fetch or private_browser before answering; ' + 'search snippets alone do not establish the full report.' + if discovered_web_sources else + 'Report the limitation without inventing results.' + ) ) if canonical(actual_tool) == 'private_browser' and not failed: requested_url = private_browser_open_url(args) diff --git a/tests/test_search_exhaustion_retrieval.py b/tests/test_search_exhaustion_retrieval.py new file mode 100644 index 000000000..c836522c2 --- /dev/null +++ b/tests/test_search_exhaustion_retrieval.py @@ -0,0 +1,52 @@ +import json +from dataclasses import replace +import pytest + + +@pytest.mark.asyncio +async def test_empty_followups_do_not_disable_reading_discovered_sources(monkeypatch): + import src.clean_agent_preview as runtime + from src.tool_schemas import FUNCTION_TOOL_SCHEMAS + from src.tool_policy import ToolPolicy + from src.turn_contract import resolve_full_inventory_contract + calls = [('web_search', {'query': q}) for q in ['storage research', 'sodium life cycles', 'cold climate chemistry']] + calls.append(('web_fetch', {'url': 'https://example.org/report'})) + packets = iter([ + {'choices': [{'delta': {'tool_calls': [{'index': 0, 'id': str(i), 'function': {'name': name, 'arguments': json.dumps(args)}}]}}]} + for i, (name, args) in enumerate(calls) + ] + [{'choices': [{'delta': {'content': 'Read the report and found the requested evidence.'}}]}]) + requests = [] + class Response: + def __init__(self, packet): self.packet = packet + async def __aenter__(self): return self + async def __aexit__(self, *args): pass + def raise_for_status(self): pass + async def aiter_lines(self): + yield 'data: ' + json.dumps(self.packet) + yield 'data: [DONE]' + class Client: + def __init__(self, **kwargs): pass + async def __aenter__(self): return self + async def __aexit__(self, *args): pass + def stream(self, *args, **kwargs): + requests.append(kwargs['json']) + return Response(next(packets)) + executed = [] + async def execute(block, **kwargs): + executed.append(block.tool_type) + if len(executed) == 1: + return block.tool_type, {'output': '[1] Storage report\n https://example.org/report', 'exit_code': 0, 'evidence_status': 'available'} + if block.tool_type == 'web_search': + return block.tool_type, {'output': 'No results', 'exit_code': 0, 'evidence_status': 'empty'} + return block.tool_type, {'output': 'Report evidence is readable.', 'exit_code': 0} + monkeypatch.setattr(runtime.httpx, 'AsyncClient', Client) + monkeypatch.setattr(runtime, 'execute_tool_block', execute) + schemas = [s for s in FUNCTION_TOOL_SCHEMAS if s['function']['name'] in {'web_search', 'web_fetch'}] + contract = replace(resolve_full_inventory_contract(schemas=schemas, policy=ToolPolicy()), routing_experiment='recent_model_choice') + raw = [x async for x in runtime.stream_preview(endpoint_url='http://test', model='test', headers={}, + messages=[{'role': 'user', 'content': 'Research storage technology.'}], turn_contract=contract, + session_id='test', owner='test', disabled_tools=set(), tool_policy=ToolPolicy(), max_rounds=5)] + assert executed == ['web_search', 'web_search', 'web_search', 'web_fetch'] + assert [s['function']['name'] for s in requests[3]['tools']] == ['web_fetch'] + assert any('Previously discovered source URLs remain available' in str(m.get('content')) for m in requests[3]['messages']) + assert any('Read the report' in x for x in raw)