langchain-ai / langserve

LangServe 🦜️🏓
Other
1.89k stars 211 forks source link

Value not declarable with JSON Schema (Pydantic V2) #690

Open JP-Ellis opened 3 months ago

JP-Ellis commented 3 months ago

Summary

I am unable to get LangServe to work nicely with a Pydantic V2 model.

Background

I understand that LangChain and LangServe are undergoing an upgrade from Pydantic V1 to V2. I was under the impression that LangServe supports V2 models, with the one caveat that OpenAPI docs aren't presently generated.

Example

Here is a simple reproducible example of the error, along with all installed dependencies and the error logs. The TL;DR is:

Virtual Environment ```shell ❯ which python /Users/JP-Ellis/tmp/.venv/bin/python ❯ python --version Python 3.12.4 ❯ uv pip install fastapi langchain_core langserve pydantic sse_starlette Resolved 55 packages in 36ms Installed 55 packages in 44ms + annotated-types==0.7.0 + anyio==4.4.0 + attrs==23.2.0 + certifi==2024.6.2 + charset-normalizer==3.3.2 + click==8.1.7 + dnspython==2.6.1 + email-validator==2.1.2 + fastapi==0.111.0 + fastapi-cli==0.0.4 + h11==0.14.0 + httpcore==1.0.5 + httptools==0.6.1 + httpx==0.27.0 + idna==3.7 + jinja2==3.1.4 + jsonpatch==1.33 + jsonpointer==3.0.0 + jsonschema==4.22.0 + jsonschema-specifications==2023.12.1 + langchain-core==0.2.9 + langserve==0.2.2 + langsmith==0.1.81 + markdown-it-py==3.0.0 + markupsafe==2.1.5 + mdurl==0.1.2 + orjson==3.10.5 + packaging==24.1 + pydantic==2.7.4 + pydantic-core==2.18.4 + pygments==2.18.0 + pyproject-toml==0.0.10 + python-dotenv==1.0.1 + python-multipart==0.0.9 + pyyaml==6.0.1 + referencing==0.35.1 + requests==2.32.3 + rich==13.7.1 + rpds-py==0.18.1 + setuptools==70.1.0 + shellingham==1.5.4 + sniffio==1.3.1 + sse-starlette==2.1.2 + starlette==0.37.2 + tenacity==8.4.1 + toml==0.10.2 + typer==0.12.3 + typing-extensions==4.12.2 + ujson==5.10.0 + urllib3==2.2.2 + uvicorn==0.30.1 + uvloop==0.19.0 + watchfiles==0.22.0 + websockets==12.0 + wheel==0.43.0 ```
example.py ```python from fastapi import APIRouter from langchain_core.runnables import Runnable, RunnableConfig from langserve import add_routes from pydantic import BaseModel router = APIRouter() class ExampleInput(BaseModel): foo: str class ExampleOutput(BaseModel): bar: str class ExampleGenerator(Runnable[ExampleInput, ExampleOutput]): def invoke( self, input: ExampleInput, config: RunnableConfig | None = None ) -> ExampleOutput: return ExampleOutput(bar=f"Received foo={input.foo}") add_routes( router, ExampleGenerator(), path="/example", ) ```
Error logs ```text ❯ python example.py Traceback (most recent call last): File "/Users/JP-Ellis/tmp/example.py", line 26, in add_routes( File "/Users/JP-Ellis/tmp/.venv/lib/python3.12/site-packages/langserve/server.py", line 443, in add_routes api_handler = APIHandler( ^^^^^^^^^^^ File "/Users/JP-Ellis/tmp/.venv/lib/python3.12/site-packages/langserve/api_handler.py", line 667, in __init__ input_type_ = _resolve_model( ^^^^^^^^^^^^^^^ File "/Users/JP-Ellis/tmp/.venv/lib/python3.12/site-packages/langserve/api_handler.py", line 338, in _resolve_model hash_ = model.schema_json() ^^^^^^^^^^^^^^^^^^^ File "/Users/JP-Ellis/tmp/.venv/lib/python3.12/site-packages/pydantic/v1/main.py", line 675, in schema_json cls.schema(by_alias=by_alias, ref_template=ref_template), default=pydantic_encoder, **dumps_kwargs ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/JP-Ellis/tmp/.venv/lib/python3.12/site-packages/pydantic/v1/main.py", line 664, in schema s = model_schema(cls, by_alias=by_alias, ref_template=ref_template) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/JP-Ellis/tmp/.venv/lib/python3.12/site-packages/pydantic/v1/schema.py", line 188, in model_schema m_schema, m_definitions, nested_models = model_process_schema( ^^^^^^^^^^^^^^^^^^^^^ File "/Users/JP-Ellis/tmp/.venv/lib/python3.12/site-packages/pydantic/v1/schema.py", line 581, in model_process_schema m_schema, m_definitions, nested_models = model_type_schema( ^^^^^^^^^^^^^^^^^^ File "/Users/JP-Ellis/tmp/.venv/lib/python3.12/site-packages/pydantic/v1/schema.py", line 622, in model_type_schema f_schema, f_definitions, f_nested_models = field_schema( ^^^^^^^^^^^^^ File "/Users/JP-Ellis/tmp/.venv/lib/python3.12/site-packages/pydantic/v1/schema.py", line 255, in field_schema f_schema, f_definitions, f_nested_models = field_type_schema( ^^^^^^^^^^^^^^^^^^ File "/Users/JP-Ellis/tmp/.venv/lib/python3.12/site-packages/pydantic/v1/schema.py", line 527, in field_type_schema f_schema, f_definitions, f_nested_models = field_singleton_schema( ^^^^^^^^^^^^^^^^^^^^^^^ File "/Users/JP-Ellis/tmp/.venv/lib/python3.12/site-packages/pydantic/v1/schema.py", line 951, in field_singleton_schema raise ValueError(f'Value not declarable with JSON Schema, field: {field}') ValueError: Value not declarable with JSON Schema, field: name='__root__' type=Optional[ExampleInput] required=False default=None ```
eyurtsev commented 3 months ago

LangChain does not currently support pydantic 2 models. If you're using pydantic 2, you need to use the pydantic.v1 namespace

Do this in your code;

from pydantic.v1 import BaseModel
JP-Ellis commented 3 months ago

Is there a tracking issue for when LangServe will support Pydantic 2? Or is there a way to use the pydantic.v1 namespace in Pydantic V2 installations?

I'm asking because I have found that the most recent release of LangChain no longer support Pydantic V1 at all due to this commit which introduced the following change to the langchain-core dependency:

pydantic = [
  { version = ">=1,<3", python =  "<3.12.4" },
  { version = "^2.7.4", python = ">=3.12.4" },
]
eyurtsev commented 3 months ago

Use pydantic.v1 right now with pydantic 2. We first need to migrate langchain to support pydantic 2 proper (rather than from the v1 namespace).

JP-Ellis commented 3 months ago

Unfortunately, using the pydantic.v1 namespace does not work for me at the moment, due to some mix up between V1 and V2 functions deep in dependencies which I would rather avoid monkey-patching. Happy to raise another issue to track that specifically if you want.

eyurtsev commented 3 months ago

Is it coming from langchain dependencies or another dependency?

JP-Ellis commented 2 months ago

In my case, it comes from the underlying FastAPI dependency. I am getting the same error as reported in:

Let me see if I can generate a minimal example within LangChain's context.

EDIT: On investigating the issue, I have confirmed it is identical to the example documented above. This does raise another question though: if FastAPI doesn't support Pydantic V1 models, how is LangServe able to avoid this very issue?

Having said all this, I am happy for you to close this issue (or keep it open for visibility, until Pydantic V2 is fully supported?)

codekiln commented 1 month ago

Just now saw this issue, which is very similar to the one I just filed - https://github.com/langchain-ai/langserve/issues/725. In my case, the only reason I upgraded was that I need to update langchain-core, which was holding back many of my other lang* libraries. In order to do that, I needed to update pydantic to 2.x. I tried converting all models to from langchain.pydantic_v1 import ..., but then I discovered FastAPI was incompatible with having 2.x installed and using 1.x models; specifically, I was getting fastapi basemodel.validate() takes 2 positional arguments but 3 were given in my middleware on every request when I tried that.

eyurtsev commented 1 month ago

Hi this is a documented limitation currently. Please hold off until LangChain 0.3.x. If you want to use pydantic proper 2. You can use pydantic.v1 namespace but FastAPI does not support it.

https://github.com/langchain-ai/langserve?tab=readme-ov-file#pydantic https://github.com/langchain-ai/langserve?tab=readme-ov-file#limitations https://python.langchain.com/v0.2/docs/how_to/pydantic_compatibility/#4-langserve-cannot-generate-openapi-docs-if-running-pydantic-2

Abd-elr4hman commented 1 month ago

how can one help with moving langchain to pydantic v2 ? can you point me towards a good starting point to get started ? @eyurtsev