diff --git a/src/clean_agent_preview.py b/src/clean_agent_preview.py index 028bda68b..48766cef9 100644 --- a/src/clean_agent_preview.py +++ b/src/clean_agent_preview.py @@ -1473,10 +1473,23 @@ def artifact_completion_tool_schemas(offered_schemas, required_artifacts): # ``write_file`` deliberately accepts UTF-8 text only. Keeping it in # a binary artifact completion round lets a forced writer choice trap # the model in an impossible retry loop even when Python is offered. - return [ - copy.deepcopy(schema) for schema in offered_schemas - if canonical((schema.get('function') or {}).get('name')) != 'write_file' - ] + bound = [] + for source in offered_schemas: + if canonical((source.get('function') or {}).get('name')) == 'write_file': + continue + schema = copy.deepcopy(source) + function = schema.get('function') or {} + if canonical(function.get('name')) == 'python': + properties = (function.get('parameters') or {}).get('properties') or {} + code_schema = properties.get('code') + if isinstance(code_schema, dict): + code_schema['pattern'] = re.escape(target) + code_schema['description'] = ( + 'Python code that creates or updates this exact required ' + f'binary artifact path: {target}' + ) + bound.append(schema) + return bound bound = copy.deepcopy(offered_schemas) for schema in bound: function = schema.get('function') or {} diff --git a/tests/test_clean_agent_preview.py b/tests/test_clean_agent_preview.py index a7c1e9ca6..2dc1b771a 100644 --- a/tests/test_clean_agent_preview.py +++ b/tests/test_clean_agent_preview.py @@ -521,7 +521,14 @@ def test_artifact_completion_schema_does_not_bind_directory_or_multiple_outputs( def test_required_binary_artifact_forces_python_instead_of_text_writer(): offered = [ {'type': 'function', 'function': {'name': 'write_file'}}, - {'type': 'function', 'function': {'name': 'python'}}, + {'type': 'function', 'function': { + 'name': 'python', + 'parameters': { + 'type': 'object', + 'properties': {'code': {'type': 'string'}}, + 'required': ['code'], + }, + }}, ] assert required_artifact_completion_tool_choice( @@ -533,12 +540,14 @@ def test_required_binary_artifact_forces_python_instead_of_text_writer(): assert required_artifact_completion_tool_choice( ('/workspace/output.png',), offered[:1], ) is None - assert [ - schema['function']['name'] - for schema in artifact_completion_tool_schemas( - offered, ('/workspace/output.png',), - ) - ] == ['python'] + binary_bound = artifact_completion_tool_schemas( + offered, ('/workspace/output.png',), + ) + assert [schema['function']['name'] for schema in binary_bound] == ['python'] + code_schema = binary_bound[0]['function']['parameters']['properties']['code'] + assert re.search(code_schema['pattern'], "open('/workspace/output.png', 'wb')") + assert not re.search(code_schema['pattern'], "print('more analysis')") + assert '/workspace/output.png' in code_schema['description'] assert [schema['function']['name'] for schema in offered] == [ 'write_file', 'python', ]