From 930ed930b17203f151225a27396088df51093d66 Mon Sep 17 00:00:00 2001 From: 1fanwang <1fannnw@gmail.com> Date: Wed, 2 Sep 2026 00:38:44 -0500 Subject: [PATCH 1/2] Avoid duplicate Deep Agents messages after continue-as-new Signed-off-by: 1fanwang <1fannnw@gmail.com> --- CHANGELOG.md | 2 ++ temporalio/contrib/deepagents/workflow.py | 13 +++---------- tests/contrib/deepagents/test_continue_as_new.py | 4 +--- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dc178710..d641e798b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,8 @@ to include examples, links to docs, or any other relevant information. ### Fixed +- `temporalio.contrib.deepagents.run_deep_agent` no longer duplicates the original + input messages when carrying state through continue-as-new. - `StrandsPlugin` now disables Botocore retries for its default Bedrock model so model request retries are handled exclusively by Temporal. - `temporalio.contrib.openai_agents` now honors the `retry-after-ms` and diff --git a/temporalio/contrib/deepagents/workflow.py b/temporalio/contrib/deepagents/workflow.py index cb3e45b03..1007402d4 100644 --- a/temporalio/contrib/deepagents/workflow.py +++ b/temporalio/contrib/deepagents/workflow.py @@ -158,23 +158,16 @@ async def call_backend_op( def _merge_snapshot(input: Any, snapshot: Mapping[str, Any]) -> Any: - """Prepend a snapshot's carried messages onto the next turn's input.""" + """Restore a snapshot's carried messages for the next turn.""" raw_prior: Any = snapshot.get("messages") or [] prior = list(raw_prior) if not prior: return input if isinstance(input, Mapping): merged = dict(input) - raw_next: Any = input.get("messages") or [] - merged["messages"] = [*prior, *list(raw_next)] + merged["messages"] = prior return merged - return {"messages": [*prior, *_as_message_list(input)]} - - -def _as_message_list(input: Any) -> list[Any]: - if isinstance(input, (list, tuple)): - return list(input) - return [input] + return {"messages": prior} async def run_deep_agent( diff --git a/tests/contrib/deepagents/test_continue_as_new.py b/tests/contrib/deepagents/test_continue_as_new.py index c5add7cf7..e6497fa14 100644 --- a/tests/contrib/deepagents/test_continue_as_new.py +++ b/tests/contrib/deepagents/test_continue_as_new.py @@ -77,9 +77,7 @@ async def test_can_threshold_and_cache(env: WorkflowEnvironment) -> None: ) result = await handle.result() - # The only way the conversation reaches >= 3 messages is if the snapshot from - # the pre-continue-as-new run was carried into the continued run and merged. - assert len(result["messages"]) >= 3, result + assert result["messages"] == ["start", "step", "step"], result assert result["todos"][0]["status"] == "completed" From e4afcb3f58891f562bfd200f21b22f32165a710a Mon Sep 17 00:00:00 2001 From: 1fanwang <1fannnw@gmail.com> Date: Wed, 2 Sep 2026 01:02:20 -0500 Subject: [PATCH 2/2] Test repeated Deep Agents continue-as-new Signed-off-by: 1fanwang <1fannnw@gmail.com> --- tests/contrib/deepagents/test_continue_as_new.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/contrib/deepagents/test_continue_as_new.py b/tests/contrib/deepagents/test_continue_as_new.py index e6497fa14..11a2c8935 100644 --- a/tests/contrib/deepagents/test_continue_as_new.py +++ b/tests/contrib/deepagents/test_continue_as_new.py @@ -37,7 +37,7 @@ class FakeAgent: async def ainvoke(self, input: Any) -> dict: messages = list(input.get("messages", [])) if isinstance(input, dict) else [] messages = [*messages, "step"] - done = len(messages) >= 3 + done = messages.count("step") >= 3 return { "messages": messages, "todos": [ @@ -51,7 +51,7 @@ class ContinueAsNewWorkflow: @workflow.run async def run(self, input: dict, state_snapshot: dict | None = None) -> dict: # Threshold of 1 means: continue-as-new as soon as there is pending work, - # which the fake agent reports until the conversation reaches 3 messages. + # which the fake agent reports until it has appended 3 steps. return await run_deep_agent( FakeAgent(), input, @@ -77,7 +77,7 @@ async def test_can_threshold_and_cache(env: WorkflowEnvironment) -> None: ) result = await handle.result() - assert result["messages"] == ["start", "step", "step"], result + assert result["messages"] == ["start", "step", "step", "step"], result assert result["todos"][0]["status"] == "completed" @@ -153,7 +153,7 @@ async def test_can_defaults_to_server_suggestion( # Carry across the suggested continue-as-new: the conversation only reaches # 3 messages if snapshots crossed run boundaries. - assert len(result["messages"]) >= 3, result + assert result["messages"] == ["start", "step", "step"], result assert result["todos"][0]["status"] == "completed" # The first run really did continue-as-new (not complete). first = env.client.get_workflow_handle(