Skip to content

Clarify shallow-copy behavior of tools and handoffs in Agent.clone() #1296

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Aug 1, 2025

Conversation

abdul-kabir-jawed
Copy link
Contributor

Fixes #1293

This PR clarifies that tools and handoffs in Agent.clone() are shallow-copied:

  • New list objects are created.
  • Inner tool and handoff objects are shared.

Includes:

  • Updated docstring in Agent.clone()
  • Minimal test verifying shallow-copy behavior

Copy link
Member

@seratch seratch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you fix the lint error?

uv run ruff check
src/agents/agent.py:230:81: W291 Trailing whitespace
    |
228 |         Notes:
229 |             - Uses `dataclasses.replace`, which performs a **shallow copy**.
230 |             - Mutable attributes like `tools` and `handoffs` are shallow‑copied: 
    |                                                                                 ^ W291
231 |               new list objects are created only if overridden, but their contents 
232 |               (tool functions and handoff objects) are shared with the original.
    |
    = help: Remove trailing whitespace

src/agents/agent.py:231:82: W291 Trailing whitespace
    |
229 |             - Uses `dataclasses.replace`, which performs a **shallow copy**.
230 |             - Mutable attributes like `tools` and `handoffs` are shallow‑copied: 
231 |               new list objects are created only if overridden, but their contents 
    |                                                                                  ^ W291
232 |               (tool functions and handoff objects) are shared with the original.
233 |             - To modify these independently, pass new lists when calling `clone()`.
    |
    = help: Remove trailing whitespace

Found 2 errors.
No fixes available (2 hidden fixes can be enabled with the `--unsafe-fixes` option).
make: *** [Makefile:16: lint] Error 1
Error: Process completed with exit code 2.

@seratch seratch added documentation Improvements or additions to documentation feature:core labels Jul 29, 2025
Fixed ruff lint errors (W291) caused by trailing whitespace in the docstring.
abdul-kabir-jawed

This comment was marked as outdated.

Copy link
Contributor Author

@abdul-kabir-jawed abdul-kabir-jawed left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed the lint issues. Ready for review again!

@seratch
Copy link
Member

seratch commented Jul 30, 2025

still failing

@abdul-kabir-jawed abdul-kabir-jawed force-pushed the docs/agent-clone-shallow-copy branch from 6673a30 to 10d167b Compare July 30, 2025 22:11
Copy link
Contributor Author

@abdul-kabir-jawed abdul-kabir-jawed left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Appreciate the review! I’ve addressed the test failure and pushed a fix. Waiting for checks to pass.

@seratch
Copy link
Member

seratch commented Jul 30, 2025

lint is now fine, but the test is failing (thanks for adding the tests!)

Copy link
Contributor Author

@abdul-kabir-jawed abdul-kabir-jawed left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The updated test file should resolve the failure. Could you please review the latest Actions logs once the checks rerun? If the test still fails, I’d appreciate it if you could share the specific error message, and I’ll address it promptly

@seratch
Copy link
Member

seratch commented Jul 31, 2025

tests:

=================================== FAILURES ===================================
________________________ test_agent_clone_shallow_copy _________________________

    def test_agent_clone_shallow_copy():
        target_agent = Agent(name="Target")
        original = Agent(
            name="Original",
            instructions="Testing clone shallow copy",
            tools=[greet],
            handoffs=[handoff(target_agent)],
        )
    
        cloned = original.clone(name="Cloned")
    
        # Ensure new lists, but same inner objects
>       assert cloned.tools is not original.tools
E       AssertionError: assert [FunctionTool(name='greet', description='', params_json_schema={'properties': {'name': {'title': 'Name', 'type': 'stri....<locals>._create_function_tool.<locals>._on_invoke_tool at 0x7f8e0ae87ce0>, strict_json_schema=True, is_enabled=True)] is not [FunctionTool(name='greet', description='', params_json_schema={'properties': {'name': {'title': 'Name', 'type': 'stri....<locals>._create_function_tool.<locals>._on_invoke_tool at 0x7f8e0ae87ce0>, strict_json_schema=True, is_enabled=True)]
E        +  where [FunctionTool(name='greet', description='', params_json_schema={'properties': {'name': {'title': 'Name', 'type': 'stri....<locals>._create_function_tool.<locals>._on_invoke_tool at 0x7f8e0ae87ce0>, strict_json_schema=True, is_enabled=True)] = Agent(name='Cloned', handoff_description=None, tools=[FunctionTool(name='greet', description='', params_json_schema={'...ails=[], output_guardrails=[], output_type=None, hooks=None, tool_use_behavior='run_llm_again', reset_tool_choice=True).tools
E        +  and   [FunctionTool(name='greet', description='', params_json_schema={'properties': {'name': {'title': 'Name', 'type': 'stri....<locals>._create_function_tool.<locals>._on_invoke_tool at 0x7f8e0ae87ce0>, strict_json_schema=True, is_enabled=True)] = Agent(name='Original', handoff_description=None, tools=[FunctionTool(name='greet', description='', params_json_schema=...ails=[], output_guardrails=[], output_type=None, hooks=None, tool_use_behavior='run_llm_again', reset_tool_choice=True).tools

tests/test_agent_clone_shallow_copy.py:21: AssertionError

old_versions:

=================================== FAILURES ===================================
________________________ test_agent_clone_shallow_copy _________________________

    def test_agent_clone_shallow_copy():
        target_agent = Agent(name="Target")
        original = Agent(
            name="Original",
            instructions="Testing clone shallow copy",
            tools=[greet],
            handoffs=[handoff(target_agent)],
        )
    
        cloned = original.clone(name="Cloned")
    
        # Ensure new lists, but same inner objects
>       assert cloned.tools is not original.tools
E       AssertionError: assert [FunctionTool(name='greet', description='', params_json_schema={'properties': {'name': {'title': 'Name', 'type': 'stri....<locals>._create_function_tool.<locals>._on_invoke_tool at 0x7f52688de310>, strict_json_schema=True, is_enabled=True)] is not [FunctionTool(name='greet', description='', params_json_schema={'properties': {'name': {'title': 'Name', 'type': 'stri....<locals>._create_function_tool.<locals>._on_invoke_tool at 0x7f52688de310>, strict_json_schema=True, is_enabled=True)]
E        +  where [FunctionTool(name='greet', description='', params_json_schema={'properties': {'name': {'title': 'Name', 'type': 'stri....<locals>._create_function_tool.<locals>._on_invoke_tool at 0x7f52688de310>, strict_json_schema=True, is_enabled=True)] = Agent(name='Cloned', handoff_description=None, tools=[FunctionTool(name='greet', description='', params_json_schema={'...ails=[], output_guardrails=[], output_type=None, hooks=None, tool_use_behavior='run_llm_again', reset_tool_choice=True).tools
E        +  and   [FunctionTool(name='greet', description='', params_json_schema={'properties': {'name': {'title': 'Name', 'type': 'stri....<locals>._create_function_tool.<locals>._on_invoke_tool at 0x7f52688de310>, strict_json_schema=True, is_enabled=True)] = Agent(name='Original', handoff_description=None, tools=[FunctionTool(name='greet', description='', params_json_schema=...ails=[], output_guardrails=[], output_type=None, hooks=None, tool_use_behavior='run_llm_again', reset_tool_choice=True).tools

tests/test_agent_clone_shallow_copy.py:21: AssertionError

@seratch
Copy link
Member

seratch commented Aug 1, 2025

The lint job is failing now. Can you resolve it?

@seratch seratch enabled auto-merge (squash) August 1, 2025 15:12
@seratch seratch merged commit af76c36 into openai:main Aug 1, 2025
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation feature:core
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Clarify Copy Type for tools and handoffs in Agent.clone() Documentation
2 participants