diff --git a/agent/_patch_messages_reducer.py b/agent/_patch_messages_reducer.py deleted file mode 100644 index 532534ab..00000000 --- a/agent/_patch_messages_reducer.py +++ /dev/null @@ -1,72 +0,0 @@ -"""Guard deepagents message reducer against None checkpoint state. - -DeltaChannel snapshots can persist ``None`` for ``messages`` (e.g. after a -cancelled run). The stock reducer calls ``convert_to_messages(None)``, which -raises when LangGraph replays checkpoint history for ``GET /threads/.../state``. - -Must load ``deepagents._messages_reducer`` without importing ``deepagents.graph`` -first — ``deepagents.__init__`` eagerly imports ``create_deep_agent``, which -binds the reducer into ``DeltaChannel`` at class definition time. -""" - -from __future__ import annotations - -import importlib -import importlib.util -import sys -from collections.abc import Sequence -from pathlib import Path - -from langchain_core.messages import AnyMessage - -_PATCHED_ATTR = "_open_swe_messages_reducer_patched" - - -def _load_messages_reducer_module(): - name = "deepagents._messages_reducer" - existing = sys.modules.get(name) - if existing is not None: - return existing - - spec = importlib.util.find_spec("deepagents") - if spec is None or not spec.origin: - raise ImportError("deepagents package not found") - root = Path(spec.origin).parent - reducer_spec = importlib.util.spec_from_file_location( - name, - root / "_messages_reducer.py", - ) - if reducer_spec is None or reducer_spec.loader is None: - raise ImportError("deepagents._messages_reducer not found") - mod = importlib.util.module_from_spec(reducer_spec) - sys.modules[name] = mod - reducer_spec.loader.exec_module(mod) - return mod - - -def _apply() -> None: - mod = _load_messages_reducer_module() - if getattr(mod._messages_delta_reducer, _PATCHED_ATTR, False): - return - - orig = mod._messages_delta_reducer - if getattr(orig, _PATCHED_ATTR, False): - return - - def _messages_delta_reducer( - state: list[AnyMessage] | None, writes: Sequence[list[AnyMessage]] - ) -> list[AnyMessage]: - if state is None: - state = [] - return orig(state, writes) - - mod._messages_delta_reducer = _messages_delta_reducer - setattr(_messages_delta_reducer, _PATCHED_ATTR, True) - - graph_mod = sys.modules.get("deepagents.graph") - if graph_mod is not None: - graph_mod._messages_delta_reducer = mod._messages_delta_reducer - importlib.reload(graph_mod) - - -_apply() diff --git a/agent/review_style_analyzer.py b/agent/review_style_analyzer.py index 4cc9dd11..77a2c83a 100644 --- a/agent/review_style_analyzer.py +++ b/agent/review_style_analyzer.py @@ -19,10 +19,6 @@ from langgraph.pregel import Pregel warnings.filterwarnings("ignore", module="langchain_core._api.deprecation") warnings.filterwarnings("ignore", message=".*Pydantic V1.*", category=UserWarning) -from ._patch_messages_reducer import _apply as _apply_messages_reducer_patch - -_apply_messages_reducer_patch() - from deepagents import create_deep_agent from deepagents.backends.protocol import SandboxBackendProtocol from langchain.agents.middleware import ModelCallLimitMiddleware diff --git a/agent/reviewer.py b/agent/reviewer.py index 048b2eee..2f64f606 100644 --- a/agent/reviewer.py +++ b/agent/reviewer.py @@ -27,10 +27,6 @@ from langgraph.pregel import Pregel warnings.filterwarnings("ignore", module="langchain_core._api.deprecation") warnings.filterwarnings("ignore", message=".*Pydantic V1.*", category=UserWarning) -from ._patch_messages_reducer import _apply as _apply_messages_reducer_patch - -_apply_messages_reducer_patch() - from deepagents import create_deep_agent from langchain.agents.middleware import ModelCallLimitMiddleware diff --git a/agent/server.py b/agent/server.py index fe7bb2a2..1bac01df 100644 --- a/agent/server.py +++ b/agent/server.py @@ -21,11 +21,6 @@ import asyncio # Suppress Pydantic v1 compatibility warnings from langchain on Python 3.14+ warnings.filterwarnings("ignore", message=".*Pydantic V1.*", category=UserWarning) -# Now safe to import agent (which imports LangChain modules) -from ._patch_messages_reducer import _apply as _apply_messages_reducer_patch - -_apply_messages_reducer_patch() - from deepagents import create_deep_agent from deepagents.backends import LangSmithSandbox from deepagents.backends.protocol import SandboxBackendProtocol diff --git a/pyproject.toml b/pyproject.toml index 4e491352..65d60946 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ readme = "README.md" requires-python = ">=3.11" license = { text = "MIT" } dependencies = [ - "deepagents>=0.6.4", + "deepagents>=0.6.6", "fastapi>=0.136.1", "uvicorn>=0.46.0", "httpx>=0.28.1", diff --git a/tests/test_messages_reducer_patch.py b/tests/test_messages_reducer_patch.py deleted file mode 100644 index e520317e..00000000 --- a/tests/test_messages_reducer_patch.py +++ /dev/null @@ -1,82 +0,0 @@ -import importlib -import sys - -import pytest -from langchain_core.messages import HumanMessage - - -def _clear_deepagents_modules( - *, - include_agent_server: bool = False, - include_agent_reviewer: bool = False, -) -> None: - for name in list(sys.modules): - if name == "deepagents" or name.startswith("deepagents."): - del sys.modules[name] - elif include_agent_server and name == "agent.server": - del sys.modules[name] - elif include_agent_reviewer and name == "agent.reviewer": - del sys.modules[name] - importlib.invalidate_caches() - - -def _restore_agent_modules() -> None: - patch_mod = importlib.import_module("agent._patch_messages_reducer") - importlib.reload(patch_mod) - for name in ("agent.server", "agent.reviewer"): - mod = sys.modules.get(name) - if mod is not None: - importlib.reload(mod) - else: - importlib.import_module(name) - - -def _apply_patch() -> None: - _clear_deepagents_modules() - patch_mod = importlib.import_module("agent._patch_messages_reducer") - importlib.reload(patch_mod) - - -def test_messages_delta_reducer_accepts_none_state() -> None: - _apply_patch() - from deepagents import _messages_reducer as reducer_mod - - result = reducer_mod._messages_delta_reducer(None, []) - assert result == [] - - -def test_messages_delta_reducer_still_merges_writes() -> None: - _apply_patch() - from deepagents import _messages_reducer as reducer_mod - - msg = HumanMessage(content="hi", id="m1") - result = reducer_mod._messages_delta_reducer(None, [[msg]]) - assert len(result) == 1 - assert result[0].content == "hi" - - -def test_graph_binds_patched_reducer_on_server_import() -> None: - _clear_deepagents_modules(include_agent_server=True) - try: - importlib.import_module("agent.server") - from deepagents.graph import _messages_delta_reducer - - assert _messages_delta_reducer(None, []) == [] - finally: - _restore_agent_modules() - - -def test_late_graph_import_is_repaired_by_patch() -> None: - _clear_deepagents_modules() - try: - from deepagents.graph import _messages_delta_reducer as unpatched - - with pytest.raises(TypeError): - unpatched(None, []) - - importlib.reload(importlib.import_module("agent._patch_messages_reducer")) - from deepagents.graph import _messages_delta_reducer as patched - - assert patched(None, []) == [] - finally: - _restore_agent_modules() diff --git a/uv.lock b/uv.lock index 9937eb19..3e72b1aa 100644 --- a/uv.lock +++ b/uv.lock @@ -649,7 +649,7 @@ wheels = [ [[package]] name = "deepagents" -version = "0.6.4" +version = "0.6.6" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "langchain" }, @@ -659,9 +659,9 @@ dependencies = [ { name = "langsmith" }, { name = "wcmatch" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c0/6b/b40d13fd003fd8d0bddabdc4c5e4467b00710fd1cd6f2eae431fb2871300/deepagents-0.6.4.tar.gz", hash = "sha256:97fe9dba710f987197dd29a183e44d1bf828e400a1a60ee38d5508597db3ebfe", size = 182423, upload-time = "2026-05-26T18:53:46.889Z" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/e8/384e13fba2010f6c5fef71a9b30ec5ec1797c849e0b1b40756f9b213b8f5/deepagents-0.6.6.tar.gz", hash = "sha256:523a709c2d2abbc4e1801cd1ae21877ed79bf317419367c7361da9980f03cc8a", size = 193936, upload-time = "2026-05-28T21:09:53.526Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/80/01/68f60b3633cc07958e71ac103a2a92841cefc13a23661f28643865b92290/deepagents-0.6.4-py3-none-any.whl", hash = "sha256:10125ea3665740afddd384e837016875fbf703f7993865db72081109b5d2615d", size = 206371, upload-time = "2026-05-26T18:53:45.43Z" }, + { url = "https://files.pythonhosted.org/packages/1e/92/d41fb83f66c540ccb33891d7ad39f4cdebc07ea34b17540fbc5f6e567d23/deepagents-0.6.6-py3-none-any.whl", hash = "sha256:c42a06b03945b750ae6d431cb67824843f4e87510b1a5fd50e2235a821423525", size = 218459, upload-time = "2026-05-28T21:09:52.3Z" }, ] [[package]] @@ -1877,7 +1877,7 @@ dev = [ [package.metadata] requires-dist = [ { name = "cryptography", specifier = ">=41.0.0" }, - { name = "deepagents", specifier = ">=0.6.4" }, + { name = "deepagents", specifier = ">=0.6.6" }, { name = "exa-py", specifier = ">=2.12.1" }, { name = "fastapi", specifier = ">=0.136.1" }, { name = "httpx", specifier = ">=0.28.1" },