Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RunnableConfigurableFields doesn't exclude excluded fields of the underlying class #26989

Open
5 tasks done
adubovik opened this issue Sep 30, 2024 · 1 comment
Open
5 tasks done
Labels
🤖:bug Related to a bug, vulnerability, unexpected error with an existing feature Ɑ: core Related to langchain-core

Comments

@adubovik
Copy link
Contributor

adubovik commented Sep 30, 2024

Checked other resources

  • I added a very descriptive title to this issue.
  • I searched the LangChain documentation with the integrated search.
  • I used the GitHub search to find a similar question and didn't find it.
  • I am sure that this is a bug in LangChain rather than my code.
  • 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:

  1. 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)
  2. 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:

client: Any = Field(default=None, exclude=True) #: :meta private:
async_client: Any = Field(default=None, exclude=True) #: :meta private:
root_client: Any = Field(default=None, exclude=True) #: :meta private:
root_async_client: Any = Field(default=None, exclude=True) #: :meta private:

Now, configurable_fields method creates RunnableConfigurableFields object, which upon invoking recreates the underlying Runnable with the fields provided in configurable.

init_params = {
k: v
for k, v in self.default.__dict__.items()
if k in self.default.model_fields
}
return (
self.default.__class__(**{**init_params, **configurable}),
config,
)

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: langchain-ai/langserve#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.

@dosubot dosubot bot added Ɑ: core Related to langchain-core 🤖:bug Related to a bug, vulnerability, unexpected error with an existing feature labels Sep 30, 2024
@chunkanglu
Copy link

Hi @adubovik ! Unless if you are in the middle of implementing your proposed solution, I'm planning on looking at this more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🤖:bug Related to a bug, vulnerability, unexpected error with an existing feature Ɑ: core Related to langchain-core
Projects
None yet
Development

No branches or pull requests

2 participants