mirror of
https://github.com/hwchase17/langchain.git
synced 2026-07-16 00:19:35 +00:00
Apply patch [skip ci]
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user