mirror of
https://github.com/hwchase17/langchain.git
synced 2026-07-15 07:00:38 +00:00
Apply patch [skip ci]
This commit is contained in:
@@ -156,14 +156,14 @@ def _set_config_context(
|
||||
|
||||
class ConfigContext:
|
||||
"""Context manager for setting child Runnable config + tracing context.
|
||||
|
||||
|
||||
This class implements proper context manager protocol with state tracking
|
||||
to prevent reuse and provide clear error messages.
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, config: RunnableConfig) -> None:
|
||||
"""Initialize the context manager with a config.
|
||||
|
||||
|
||||
Args:
|
||||
config: The config to set.
|
||||
"""
|
||||
@@ -171,24 +171,24 @@ class ConfigContext:
|
||||
self._entered = False
|
||||
self._ctx: Optional[Context] = None
|
||||
self._config_token: Optional[Token[Optional[RunnableConfig]]] = None
|
||||
|
||||
|
||||
def __enter__(self) -> Context:
|
||||
"""Enter the context manager.
|
||||
|
||||
|
||||
Returns:
|
||||
The config context.
|
||||
|
||||
|
||||
Raises:
|
||||
RuntimeError: If the context manager has already been entered.
|
||||
"""
|
||||
if self._entered:
|
||||
raise RuntimeError("Cannot re-enter an already-entered context manager")
|
||||
|
||||
|
||||
self._entered = True
|
||||
self._ctx = copy_context()
|
||||
self._config_token, _ = self._ctx.run(_set_config_context, self.config)
|
||||
return self._ctx
|
||||
|
||||
|
||||
def __exit__(
|
||||
self,
|
||||
exc_type: Optional[type[BaseException]],
|
||||
@@ -196,18 +196,18 @@ class ConfigContext:
|
||||
exc_tb: Optional[object],
|
||||
) -> None:
|
||||
"""Exit the context manager and reset the config.
|
||||
|
||||
|
||||
Args:
|
||||
exc_type: The exception type if an exception occurred.
|
||||
exc_val: The exception value if an exception occurred.
|
||||
exc_tb: The exception traceback if an exception occurred.
|
||||
|
||||
|
||||
Raises:
|
||||
RuntimeError: If the context manager was not entered.
|
||||
"""
|
||||
if not self._entered:
|
||||
raise RuntimeError("Cannot exit context manager that was not entered")
|
||||
|
||||
|
||||
if self._ctx is not None and self._config_token is not None:
|
||||
self._ctx.run(var_child_runnable_config.reset, self._config_token)
|
||||
self._ctx.run(
|
||||
@@ -221,7 +221,7 @@ class ConfigContext:
|
||||
"client": None,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# Mark as exited to prevent reuse
|
||||
self._entered = False
|
||||
|
||||
@@ -669,6 +669,3 @@ async def run_in_executor(
|
||||
)
|
||||
|
||||
return await asyncio.get_running_loop().run_in_executor(executor_or_config, wrapper)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -166,53 +166,54 @@ async def test_run_in_executor() -> None:
|
||||
def test_set_config_context_reuse_raises_error() -> None:
|
||||
"""Test that reusing the same context manager raises RuntimeError."""
|
||||
from langchain_core.runnables.config import set_config_context
|
||||
|
||||
|
||||
config = RunnableConfig(tags=["test"])
|
||||
ctx_manager = set_config_context(config)
|
||||
|
||||
|
||||
# First enter should work
|
||||
with ctx_manager as ctx1:
|
||||
assert ctx1 is not None
|
||||
|
||||
|
||||
# Second enter should raise RuntimeError
|
||||
with pytest.raises(RuntimeError) as exc_info:
|
||||
with ctx_manager as ctx2:
|
||||
pass # Should not reach here
|
||||
|
||||
assert "Cannot re-enter an already-entered context manager" in str(exc_info.value)
|
||||
|
||||
assert "Cannot re-enter an already-entered context manager" in str(
|
||||
exc_info.value
|
||||
)
|
||||
|
||||
|
||||
def test_set_config_context_exit_without_enter() -> None:
|
||||
"""Test that exiting without entering raises RuntimeError."""
|
||||
from langchain_core.runnables.config import set_config_context
|
||||
|
||||
|
||||
config = RunnableConfig(tags=["test"])
|
||||
ctx_manager = set_config_context(config)
|
||||
|
||||
|
||||
# Attempting to exit without entering should raise RuntimeError
|
||||
with pytest.raises(RuntimeError) as exc_info:
|
||||
ctx_manager.__exit__(None, None, None)
|
||||
|
||||
|
||||
assert "Cannot exit context manager that was not entered" in str(exc_info.value)
|
||||
|
||||
|
||||
def test_set_config_context_normal_usage() -> None:
|
||||
"""Test that normal usage still works correctly."""
|
||||
from langchain_core.runnables.config import set_config_context
|
||||
|
||||
|
||||
config = RunnableConfig(
|
||||
tags=["test"],
|
||||
metadata={"key": "value"},
|
||||
configurable={"param": "value"}
|
||||
tags=["test"], metadata={"key": "value"}, configurable={"param": "value"}
|
||||
)
|
||||
|
||||
|
||||
# Normal usage should work without issues
|
||||
with set_config_context(config) as ctx:
|
||||
assert ctx is not None
|
||||
# Context should be a contextvars.Context object
|
||||
from contextvars import Context
|
||||
|
||||
assert isinstance(ctx, Context)
|
||||
|
||||
|
||||
# After exiting, we should be able to create a new context manager
|
||||
# with the same config
|
||||
with set_config_context(config) as ctx2:
|
||||
@@ -223,29 +224,23 @@ def test_set_config_context_normal_usage() -> None:
|
||||
def test_set_config_context_nested_different_instances() -> None:
|
||||
"""Test that using different instances in nested contexts works properly."""
|
||||
from langchain_core.runnables.config import set_config_context
|
||||
|
||||
config1 = RunnableConfig(
|
||||
tags=["outer"],
|
||||
metadata={"level": "outer"}
|
||||
)
|
||||
config2 = RunnableConfig(
|
||||
tags=["inner"],
|
||||
metadata={"level": "inner"}
|
||||
)
|
||||
|
||||
|
||||
config1 = RunnableConfig(tags=["outer"], metadata={"level": "outer"})
|
||||
config2 = RunnableConfig(tags=["inner"], metadata={"level": "inner"})
|
||||
|
||||
# Nested contexts with different instances should work
|
||||
with set_config_context(config1) as ctx1:
|
||||
assert ctx1 is not None
|
||||
|
||||
|
||||
with set_config_context(config2) as ctx2:
|
||||
assert ctx2 is not None
|
||||
# Both contexts should be valid
|
||||
from contextvars import Context
|
||||
|
||||
assert isinstance(ctx1, Context)
|
||||
assert isinstance(ctx2, Context)
|
||||
# They should be different contexts
|
||||
assert ctx1 is not ctx2
|
||||
|
||||
|
||||
# After inner context exits, outer should still be valid
|
||||
assert ctx1 is not None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user