From a830b809f39f05026ad32ca1c33c39da7b2bd160 Mon Sep 17 00:00:00 2001 From: William FH <13333726+hinthornw@users.noreply.github.com> Date: Sun, 29 Oct 2023 16:53:01 +0900 Subject: [PATCH] Patch forward ref bug (#12508) Currently this gives a bug: ``` from langchain.schema.runnable import RunnableLambda bound = RunnableLambda(lambda x: x).with_config({"callbacks": []}) # ConfigError: field "callbacks" not yet prepared so type is still a ForwardRef, you might need to call RunnableConfig.update_forward_refs(). ``` Rather than deal with cyclic imports and extra load time, etc., I think it makes sense to just have a separate Callbacks definition here that is a relaxed typehint. --- libs/langchain/langchain/schema/runnable/config.py | 4 ++++ .../unit_tests/schema/runnable/test_runnable.py | 14 +++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/libs/langchain/langchain/schema/runnable/config.py b/libs/langchain/langchain/schema/runnable/config.py index a795e3e3fa..bded08f724 100644 --- a/libs/langchain/langchain/schema/runnable/config.py +++ b/libs/langchain/langchain/schema/runnable/config.py @@ -32,6 +32,10 @@ if TYPE_CHECKING: CallbackManager, CallbackManagerForChainRun, ) +else: + # Pydantic validates through typed dicts, but + # the callbacks need forward refs updated + Callbacks = Optional[Union[List, Any]] class EmptyDict(TypedDict, total=False): diff --git a/libs/langchain/tests/unit_tests/schema/runnable/test_runnable.py b/libs/langchain/tests/unit_tests/schema/runnable/test_runnable.py index c0af8211ab..3db0fbcdd6 100644 --- a/libs/langchain/tests/unit_tests/schema/runnable/test_runnable.py +++ b/libs/langchain/tests/unit_tests/schema/runnable/test_runnable.py @@ -60,7 +60,11 @@ from langchain.schema.runnable import ( RunnableSequence, RunnableWithFallbacks, ) -from langchain.schema.runnable.base import ConfigurableField, RunnableGenerator +from langchain.schema.runnable.base import ( + ConfigurableField, + RunnableBinding, + RunnableGenerator, +) from langchain.schema.runnable.utils import ( ConfigurableFieldMultiOption, ConfigurableFieldSingleOption, @@ -3999,3 +4003,11 @@ async def test_runnable_gen_transform() -> None: assert list(chain.stream(3)) == [1, 2, 3] assert [p async for p in achain.astream(4)] == [1, 2, 3, 4] + + +def test_with_config_callbacks() -> None: + result = RunnableLambda(lambda x: x).with_config({"callbacks": []}) + # Bugfix from version 0.0.325 + # ConfigError: field "callbacks" not yet prepared so type is still a ForwardRef, + # you might need to call RunnableConfig.update_forward_refs(). + assert isinstance(result, RunnableBinding)