Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/agents/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,16 @@ def clone(self, **kwargs: Any) -> Agent[TContext]:
- To give the clone a list that no other agent holds, pass a new one, for example
`agent.clone(tools=[*agent.tools, extra_tool])`. The entries copied into it remain
the same objects the original agent holds.
- The same applies to the mutable attributes that are not lists, `model_settings` and
`mcp_config`. An omitted one arrives as the original agent's own object, so
Comment on lines +563 to +564

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Cover all shared non-list mutable fields

When an agent uses a static prompt dictionary or a StopAtTools dictionary for tool_use_behavior, dataclasses.replace() shares those objects too, so mutating either field through the clone also changes the original agent. Calling model_settings and mcp_config “the mutable attributes that are not lists” therefore preserves the same misleading implication this change is intended to remove; describe shallow reference sharing generally or also cover these fields and their replacement patterns.

AGENTS.md reference: AGENTS.md:L153-L153

Useful? React with 👍 / 👎.

`cloned.model_settings.temperature = 0.9` also changes the original. Pass a
replacement to keep them separate, for example
`agent.clone(model_settings=dataclasses.replace(agent.model_settings,
temperature=0.9))`.
- Overriding `model` alone does not give the clone its own `model_settings`. Fresh
settings are substituted only when the current ones still match the implicit
defaults for the current model, so an agent carrying any explicit setting keeps
sharing that object across `agent.clone(model=...)`.
Example:
```python
new_agent = agent.clone(instructions="New instructions")
Expand Down
31 changes: 30 additions & 1 deletion tests/test_agent_clone_shallow_copy.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from agents import Agent, function_tool, handoff
from agents import Agent, ModelSettings, function_tool, handoff


@function_tool
Expand Down Expand Up @@ -79,3 +79,32 @@ def test_agent_clone_shared_list_mutation_affects_both_agents():

assert original.tools == cloned.tools
assert len(original.tools) == 2


def test_agent_clone_shares_non_list_mutable_attributes():
"""`model_settings` and `mcp_config` are shared too, which the list wording does not cover."""
agent = Agent(
name="Original",
model="gpt-4o",
model_settings=ModelSettings(temperature=0.1),
mcp_config={"convert_schemas_to_strict": True},
)

cloned = agent.clone(instructions="Changed")

assert cloned.model_settings is agent.model_settings
assert cloned.mcp_config is agent.mcp_config

cloned.model_settings.temperature = 0.9
cloned.mcp_config["convert_schemas_to_strict"] = False
assert agent.model_settings.temperature == 0.9
assert agent.mcp_config["convert_schemas_to_strict"] is False


def test_agent_clone_with_only_model_override_keeps_shared_model_settings():
"""Overriding `model` alone does not detach explicit settings from the original."""
agent = Agent(name="Original", model="gpt-4o", model_settings=ModelSettings(temperature=0.1))

cloned = agent.clone(model="gpt-4o-mini")

assert cloned.model_settings is agent.model_settings