diff --git a/libs/core/langchain_core/callbacks/base.py b/libs/core/langchain_core/callbacks/base.py index f8877360571b3..e11713b0af7f9 100644 --- a/libs/core/langchain_core/callbacks/base.py +++ b/libs/core/langchain_core/callbacks/base.py @@ -965,28 +965,29 @@ def merge(self, other: BaseCallbackManager) -> Self: # ['tag2', 'tag1'] ``` """ # noqa: E501 - manager = self.__class__( + # Combine handlers and inheritable_handlers separately, using sets + # to deduplicate (order not preserved) + combined_handlers = list(set(self.handlers) | set(other.handlers)) + combined_inheritable = list( + set(self.inheritable_handlers) | set(other.inheritable_handlers) + ) + + return self.__class__( parent_run_id=self.parent_run_id or other.parent_run_id, - handlers=[], - inheritable_handlers=[], + handlers=combined_handlers, + inheritable_handlers=combined_inheritable, tags=list(set(self.tags + other.tags)), inheritable_tags=list(set(self.inheritable_tags + other.inheritable_tags)), metadata={ **self.metadata, **other.metadata, }, + inheritable_metadata={ + **self.inheritable_metadata, + **other.inheritable_metadata, + }, ) - handlers = self.handlers + other.handlers - inheritable_handlers = self.inheritable_handlers + other.inheritable_handlers - - for handler in handlers: - manager.add_handler(handler) - - for handler in inheritable_handlers: - manager.add_handler(handler, inherit=True) - return manager - @property def is_async(self) -> bool: """Whether the callback manager is async.""" diff --git a/libs/core/tests/unit_tests/callbacks/test_sync_callback_manager.py b/libs/core/tests/unit_tests/callbacks/test_sync_callback_manager.py index 0cdabea9cc410..3c880ff666b33 100644 --- a/libs/core/tests/unit_tests/callbacks/test_sync_callback_manager.py +++ b/libs/core/tests/unit_tests/callbacks/test_sync_callback_manager.py @@ -1,5 +1,3 @@ -import pytest - from langchain_core.callbacks.base import BaseCallbackHandler, BaseCallbackManager @@ -17,9 +15,6 @@ def test_remove_handler() -> None: manager.remove_handler(handler2) -@pytest.mark.xfail( - reason="TODO: #32028 merge() incorrectly mixes handlers and inheritable_handlers" -) def test_merge_preserves_handler_distinction() -> None: """Test that merging managers preserves the distinction between handlers.