[X] I added a very descriptive title to this issue.
[X] I searched the LangChain documentation with the integrated search.
[X] I used the GitHub search to find a similar question and didn't find it.
[X] I am sure that this is a bug in LangChain rather than my code.
[X] The bug is not resolved by updating to the latest stable version of LangChain (or the specific integration package).
Example Code
from langchain.schema.runnable import ConfigurableField, RunnableSerializable
from pydantic import Field, model_validator
class Greeting(RunnableSerializable[str, str]):
greeting: str = "Hello"
private_greeting: str = Field(default=None, exclude=True)
@model_validator(mode="after")
def post_init(self):
if self.private_greeting is None:
self.private_greeting = self.greeting.upper()
return self
def invoke(self, input: str, config=None, **kwargs) -> str:
return self.private_greeting + ", " + input.capitalize() + "!"
r = Greeting().configurable_fields(
greeting=ConfigurableField(id="greeting"),
)
# Actual : HELLO, World!
# Expected: GOODBYE, World!
print(r.invoke("world", config={"configurable": {"greeting": "Goodbye"}}))
Error Message and Stack Trace (if applicable)
No response
Description
The Langchain Serializable is based on BaseModel from Pydantic.
A typical way to define a certain field as private in BaseModel is:
to define it as Field(default=None, exclude=True)(meaning that (a) it's optional in the constructor and (b) its value isn't required to re-create the object from its serialized representation)
initialize the fields in a model_validator.
Such private fields are usually derived from the model public fields.
This is for example how langchain_openai defines client and async_client fields:
Now, configurable_fields method creates RunnableConfigurableFields object, which upon invoking recreates the underlying Runnable with the fields provided in configurable.
Note, that every single model field is passed to the class constructor including Excluded fields.
Consider a BaseModel with a public field A and an excluded field B populated by A(potentially by a complex logic).
The user has only the knowledge of A since it's a part of the public interface. So one tries to configure field A. But given that RunnableConfigurableFields passes both A and B to a new instance, the change of A won't affect B. See the minimal example attached.
This also precludes a user from a straightforward configuring of langchain_openai.ChatOpenAI via api_key: https://github.com/langchain-ai/langserve/issues/652
Since the excluded client-fields are derived from api_key, one has to resort to a cumbersome configuration of these client-fields too.
The proposed solution is to filter out the excluded fields when a new object is created:
if configurable:
init_params = {
k: v
for k, v in self.default.__dict__.items()
- if k in self.default.model_fields
+ if k in self.default.model_fields and not self.default.model_fields[k].exclude
}
return (
self.default.__class__(**{**init_params, **configurable}),
config,
)
System Info
System Information
OS: Darwin
OS Version: Darwin Kernel Version 23.6.0: Mon Jul 29 21:14:30 PDT 2024; root:xnu-10063.141.2~1/RELEASE_ARM64_T6000
Python Version: 3.11.10 (main, Sep 7 2024, 01:03:31) [Clang 15.0.0 (clang-1500.3.9.4)]
Package Information
langchain_core: 0.3.1
langchain: 0.3.0
langchain_community: 0.0.38
langsmith: 0.1.126
langchain_openai: 0.2.0
langchain_text_splitters: 0.3.0
langserve: Installed. No version info available.
Optional packages not installed
langgraph
Other Dependencies
aiohttp: 3.10.5
aiosqlite: Installed. No version info available.
aleph-alpha-client: Installed. No version info available.
anthropic: Installed. No version info available.
arxiv: Installed. No version info available.
assemblyai: Installed. No version info available.
async-timeout: Installed. No version info available.
atlassian-python-api: Installed. No version info available.
azure-ai-documentintelligence: Installed. No version info available.
azure-identity: Installed. No version info available.
azure-search-documents: Installed. No version info available.
beautifulsoup4: Installed. No version info available.
bibtexparser: Installed. No version info available.
cassio: Installed. No version info available.
chardet: Installed. No version info available.
cloudpickle: Installed. No version info available.
cohere: Installed. No version info available.
databricks-vectorsearch: Installed. No version info available.
dataclasses-json: 0.6.7
datasets: Installed. No version info available.
dgml-utils: Installed. No version info available.
elasticsearch: Installed. No version info available.
esprima: Installed. No version info available.
faiss-cpu: Installed. No version info available.
feedparser: Installed. No version info available.
fireworks-ai: Installed. No version info available.
friendli-client: Installed. No version info available.
geopandas: Installed. No version info available.
gitpython: Installed. No version info available.
google-cloud-documentai: Installed. No version info available.
gql: Installed. No version info available.
gradientai: Installed. No version info available.
hdbcli: Installed. No version info available.
hologres-vector: Installed. No version info available.
html2text: Installed. No version info available.
httpx: 0.27.2
httpx-sse: Installed. No version info available.
javelin-sdk: Installed. No version info available.
jinja2: Installed. No version info available.
jq: Installed. No version info available.
jsonpatch: 1.33
jsonschema: Installed. No version info available.
lxml: Installed. No version info available.
markdownify: Installed. No version info available.
motor: Installed. No version info available.
msal: Installed. No version info available.
mwparserfromhell: Installed. No version info available.
mwxml: Installed. No version info available.
newspaper3k: Installed. No version info available.
numexpr: Installed. No version info available.
numpy: 1.26.4
nvidia-riva-client: Installed. No version info available.
oci: Installed. No version info available.
openai: 1.46.0
openapi-pydantic: Installed. No version info available.
oracle-ads: Installed. No version info available.
oracledb: Installed. No version info available.
orjson: 3.10.7
packaging: 23.2
pandas: Installed. No version info available.
pdfminer-six: Installed. No version info available.
pgvector: Installed. No version info available.
praw: Installed. No version info available.
premai: Installed. No version info available.
psychicapi: Installed. No version info available.
py-trello: Installed. No version info available.
pydantic: 2.9.2
pyjwt: Installed. No version info available.
pymupdf: Installed. No version info available.
pypdf: Installed. No version info available.
pypdfium2: Installed. No version info available.
pyspark: Installed. No version info available.
PyYAML: 6.0.2
rank-bm25: Installed. No version info available.
rapidfuzz: Installed. No version info available.
rapidocr-onnxruntime: Installed. No version info available.
rdflib: Installed. No version info available.
requests: 2.32.3
requests-toolbelt: Installed. No version info available.
rspace_client: Installed. No version info available.
scikit-learn: Installed. No version info available.
SQLAlchemy: 2.0.35
sqlite-vss: Installed. No version info available.
streamlit: Installed. No version info available.
sympy: Installed. No version info available.
telethon: Installed. No version info available.
tenacity: 8.5.0
tidb-vector: Installed. No version info available.
tiktoken: 0.7.0
timescale-vector: Installed. No version info available.
tqdm: 4.66.5
tree-sitter: Installed. No version info available.
tree-sitter-languages: Installed. No version info available.
typer: Installed. No version info available.
typing-extensions: 4.12.2
upstash-redis: Installed. No version info available.
vdms: Installed. No version info available.
xata: Installed. No version info available.
xmltodict: Installed. No version info available.
Checked other resources
Example Code
Error Message and Stack Trace (if applicable)
No response
Description
The Langchain
Serializable
is based onBaseModel
from Pydantic.A typical way to define a certain field as private in
BaseModel
is:Field(default=None, exclude=True)
(meaning that (a) it's optional in the constructor and (b) its value isn't required to re-create the object from its serialized representation)model_validator
.Such private fields are usually derived from the model public fields.
This is for example how
langchain_openai
definesclient
andasync_client
fields:https://github.com/langchain-ai/langchain/blob/9404e7af9d8768a7e884dae3e9fafb712eaf4e99/libs/partners/openai/langchain_openai/chat_models/base.py#L378-L381
Now,
configurable_fields
method createsRunnableConfigurableFields
object, which upon invoking recreates the underlyingRunnable
with the fields provided inconfigurable
.https://github.com/langchain-ai/langchain/blob/9404e7af9d8768a7e884dae3e9fafb712eaf4e99/libs/core/langchain_core/runnables/configurable.py#L440-L448
Note, that every single model field is passed to the class constructor including
Exclude
d fields.Consider a
BaseModel
with a public fieldA
and an excluded fieldB
populated byA
(potentially by a complex logic).The user has only the knowledge of
A
since it's a part of the public interface. So one tries to configure fieldA
. But given thatRunnableConfigurableFields
passes bothA
andB
to a new instance, the change ofA
won't affectB
. See the minimal example attached.This also precludes a user from a straightforward configuring of
langchain_openai.ChatOpenAI
viaapi_key
: https://github.com/langchain-ai/langserve/issues/652 Since the excludedclient
-fields are derived fromapi_key
, one has to resort to a cumbersome configuration of theseclient
-fields too.The proposed solution is to filter out the excluded fields when a new object is created:
System Info
System Information
Package Information
Optional packages not installed
Other Dependencies