langchain-ai / langchain

🦜🔗 Build context-aware reasoning applications
https://python.langchain.com
MIT License
93.98k stars 15.15k forks source link

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

Open adubovik opened 3 weeks ago

adubovik commented 3 weeks ago

Checked other resources

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:

https://github.com/langchain-ai/langchain/blob/9404e7af9d8768a7e884dae3e9fafb712eaf4e99/libs/partners/openai/langchain_openai/chat_models/base.py#L378-L381

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

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 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.

chunkanglu commented 1 week ago

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