From adaa7260e2a6c722fb4bcbc5517664ad0266dffb Mon Sep 17 00:00:00 2001 From: ayaangazali Date: Mon, 17 Aug 2026 16:56:07 -0700 Subject: [PATCH] docs: note that Agent.clone shares non-list mutable attributes #4474 corrected the clone docstring for list attributes, but its wording is list-only, so a reader reasonably concludes non-list attributes are copied. They are not: model_settings and mcp_config both arrive as the original agent's own object when omitted, so mutating them through the clone changes the original. Also records that overriding model alone does not detach 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 clone(model=...). --- src/agents/agent.py | 10 +++++++++ tests/test_agent_clone_shallow_copy.py | 31 +++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/agents/agent.py b/src/agents/agent.py index 72d3e03265..14333e0104 100644 --- a/src/agents/agent.py +++ b/src/agents/agent.py @@ -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 + `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") diff --git a/tests/test_agent_clone_shallow_copy.py b/tests/test_agent_clone_shallow_copy.py index 79559898b2..52683be9e7 100644 --- a/tests/test_agent_clone_shallow_copy.py +++ b/tests/test_agent_clone_shallow_copy.py @@ -1,4 +1,4 @@ -from agents import Agent, function_tool, handoff +from agents import Agent, ModelSettings, function_tool, handoff @function_tool @@ -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