From 1a6e3dff177eaa0ce2079d29af3fea3bb38c4aac Mon Sep 17 00:00:00 2001 From: "open-swe[bot]" Date: Wed, 13 Aug 2025 15:35:53 +0000 Subject: [PATCH] Apply patch [skip ci] --- libs/core/langchain_core/runnables/config.py | 14 +-- .../tests/unit_tests/runnables/test_config.py | 118 ++++++++++-------- 2 files changed, 72 insertions(+), 60 deletions(-) diff --git a/libs/core/langchain_core/runnables/config.py b/libs/core/langchain_core/runnables/config.py index e3d3c6cd5b3..513f171760f 100644 --- a/libs/core/langchain_core/runnables/config.py +++ b/libs/core/langchain_core/runnables/config.py @@ -301,11 +301,11 @@ def patch_config( configurable: Optional[dict[str, Any]] = None, ) -> RunnableConfig: """Patch a config with new values. - + By default, when callbacks are replaced, the run_name and run_id are removed from the config to ensure they only apply to the run with the original callbacks. This maintains backward compatibility. - + To preserve run_name across child runs (e.g., for consistent tracing), set ``inherit_run_name=True`` in the config. This allows dynamic run names to propagate through complex runnable chains. @@ -324,7 +324,7 @@ def patch_config( Returns: RunnableConfig: The patched config. - + Note: The ``inherit_run_name`` config key (default: False) controls whether run_name is preserved when creating child runs. When True, the run_name @@ -335,9 +335,9 @@ def patch_config( if callbacks is not None: # Check if run_name should be inherited to child runs inherit_run_name = config.get("inherit_run_name", False) - + config["callbacks"] = callbacks - + # Only delete run_name/run_id if inherit_run_name is False (default behavior) # This preserves backward compatibility while allowing opt-in inheritance if not inherit_run_name: @@ -649,7 +649,3 @@ async def run_in_executor( ) return await asyncio.get_running_loop().run_in_executor(executor_or_config, wrapper) - - - - diff --git a/libs/core/tests/unit_tests/runnables/test_config.py b/libs/core/tests/unit_tests/runnables/test_config.py index 24cb2bc9685..397adf7b06c 100644 --- a/libs/core/tests/unit_tests/runnables/test_config.py +++ b/libs/core/tests/unit_tests/runnables/test_config.py @@ -165,83 +165,91 @@ async def test_run_in_executor() -> None: def test_inherit_run_name_default_behavior() -> None: """Test that by default, run_name is NOT inherited to child runs.""" - from langchain_core.runnables.config import patch_config from langchain_core.callbacks.manager import CallbackManager - from langchain_core.callbacks.base import BaseCallbackHandler - + from langchain_core.runnables.config import patch_config + # Create a real callback manager with a handler handler = StdOutCallbackHandler() callback_manager = CallbackManager(handlers=[handler]) - + # Test default behavior (inherit_run_name not set or False) config: RunnableConfig = { "run_name": "parent_run", "callbacks": callback_manager, } - + # When callbacks are replaced, run_name should be deleted by default new_callback_manager = CallbackManager(handlers=[handler]) patched = patch_config(config, callbacks=new_callback_manager) assert "run_name" not in patched, "run_name should be deleted by default" - + # Explicitly set inherit_run_name to False config_explicit_false: RunnableConfig = { "run_name": "parent_run", "inherit_run_name": False, "callbacks": callback_manager, } - - patched_explicit = patch_config(config_explicit_false, callbacks=new_callback_manager) - assert "run_name" not in patched_explicit, "run_name should be deleted when inherit_run_name=False" + + patched_explicit = patch_config( + config_explicit_false, callbacks=new_callback_manager + ) + assert "run_name" not in patched_explicit, ( + "run_name should be deleted when inherit_run_name=False" + ) def test_inherit_run_name_enabled() -> None: """Test that when inherit_run_name=True, run_name is preserved for child runs.""" - from langchain_core.runnables.config import patch_config from langchain_core.callbacks.manager import CallbackManager - + from langchain_core.runnables.config import patch_config + # Create a real callback manager with a handler handler = StdOutCallbackHandler() callback_manager = CallbackManager(handlers=[handler]) - + # Test with inherit_run_name=True config: RunnableConfig = { "run_name": "parent_run", "inherit_run_name": True, "callbacks": callback_manager, } - + # When callbacks are replaced, run_name should be preserved new_callback_manager = CallbackManager(handlers=[handler]) patched = patch_config(config, callbacks=new_callback_manager) - assert "run_name" in patched, "run_name should be preserved when inherit_run_name=True" + assert "run_name" in patched, ( + "run_name should be preserved when inherit_run_name=True" + ) assert patched["run_name"] == "parent_run", "run_name value should be unchanged" - assert patched.get("inherit_run_name") is True, "inherit_run_name should be preserved" + assert patched.get("inherit_run_name") is True, ( + "inherit_run_name should be preserved" + ) def test_inherit_run_name_with_chain() -> None: """Test inherit_run_name behavior in a chain of runnables.""" from typing import List + from langchain_core.callbacks.base import BaseCallbackHandler from langchain_core.runnables import RunnableLambda - + # Track run names through callbacks captured_names: List[str] = [] - + class TestCallbackHandler(BaseCallbackHandler): def on_chain_start(self, serialized: dict, inputs: Any, **kwargs: Any) -> None: name = kwargs.get("name", "unnamed") captured_names.append(name) - + # Create a simple chain def identity(x: Any) -> Any: return x - + def process(x: Any) -> str: return f"processed: {x}" - + chain = RunnableLambda(identity) | RunnableLambda(process) - + # Test 1: Default behavior (run_name not inherited) captured_names.clear() config_default: RunnableConfig = { @@ -254,8 +262,10 @@ def test_inherit_run_name_with_chain() -> None: assert captured_names[0] == "custom_chain_run", "Root run should have custom name" # Child runs should NOT have the custom name (they'll have their default names) for i in range(1, len(captured_names)): - assert captured_names[i] != "custom_chain_run", f"Child run {i} should not inherit run_name by default" - + assert captured_names[i] != "custom_chain_run", ( + f"Child run {i} should not inherit run_name by default" + ) + # Test 2: With inherit_run_name=True captured_names.clear() config_inherit: RunnableConfig = { @@ -266,53 +276,58 @@ def test_inherit_run_name_with_chain() -> None: result = chain.invoke("test", config=config_inherit) assert result == "processed: test" # All runs should have the same custom name when inherit_run_name=True - assert captured_names[0] == "inherited_chain_run", "Root run should have custom name" + assert captured_names[0] == "inherited_chain_run", ( + "Root run should have custom name" + ) # With inherit_run_name=True, child runs should also have the custom name for i in range(1, len(captured_names)): - assert captured_names[i] == "inherited_chain_run", f"Child run {i} should inherit run_name when inherit_run_name=True" + assert captured_names[i] == "inherited_chain_run", ( + f"Child run {i} should inherit run_name when inherit_run_name=True" + ) def test_inherit_run_name_with_override() -> None: """Test that per-step with_config can still set different run_names when inherit_run_name is NOT used. - + This test verifies that the traditional behavior of setting different run_names per step via with_config still works when inherit_run_name is not enabled. """ from typing import List + from langchain_core.callbacks.base import BaseCallbackHandler from langchain_core.runnables import RunnableLambda - + # Track run names through callbacks captured_names: List[str] = [] - + class TestCallbackHandler(BaseCallbackHandler): def on_chain_start(self, serialized: dict, inputs: Any, **kwargs: Any) -> None: name = kwargs.get("name", "unnamed") captured_names.append(name) - + def identity(x: Any) -> Any: return x - + def process(x: Any) -> str: return f"processed: {x}" - + # Test: Without inherit_run_name, per-step with_config works as before chain = ( - RunnableLambda(identity).with_config(run_name="step1") | - RunnableLambda(process).with_config(run_name="step2") | - RunnableLambda(identity).with_config(run_name="step3") + RunnableLambda(identity).with_config(run_name="step1") + | RunnableLambda(process).with_config(run_name="step2") + | RunnableLambda(identity).with_config(run_name="step3") ) - + captured_names.clear() config: RunnableConfig = { "run_name": "root_run", # Note: inherit_run_name is NOT set (defaults to False) "callbacks": [TestCallbackHandler()], } - + result = chain.invoke("test", config=config) assert result == "processed: test" - + # The root should have "root_run", and each step should have its own name assert "root_run" in captured_names, "Root run should have the config run_name" assert "step1" in captured_names, "First step should have its own run_name" @@ -323,34 +338,35 @@ def test_inherit_run_name_with_override() -> None: def test_inherit_run_name_merge_configs() -> None: """Test that inherit_run_name is properly handled in merge_configs.""" from langchain_core.runnables.config import merge_configs - + # Test merging with inherit_run_name base: RunnableConfig = { "run_name": "base_run", "inherit_run_name": False, } - + override: RunnableConfig = { "inherit_run_name": True, } - + merged = merge_configs(base, override) - assert merged.get("inherit_run_name") is True, "inherit_run_name should be overridden" - assert merged.get("run_name") == "base_run", "run_name should be preserved from base" - + assert merged.get("inherit_run_name") is True, ( + "inherit_run_name should be overridden" + ) + assert merged.get("run_name") == "base_run", ( + "run_name should be preserved from base" + ) + # Test that inherit_run_name passes through ensure_config from langchain_core.runnables.config import ensure_config - + config_with_inherit: RunnableConfig = { "run_name": "test_run", "inherit_run_name": True, } - + ensured = ensure_config(config_with_inherit) - assert ensured.get("inherit_run_name") is True, "inherit_run_name should pass through ensure_config" + assert ensured.get("inherit_run_name") is True, ( + "inherit_run_name should pass through ensure_config" + ) assert ensured.get("run_name") == "test_run", "run_name should be preserved" - - - - -