Apply patch [skip ci]

This commit is contained in:
open-swe[bot]
2025-08-13 15:35:53 +00:00
parent f0e9b01261
commit 1a6e3dff17
2 changed files with 72 additions and 60 deletions

View File

@@ -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)

View File

@@ -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"