mirror of https://github.com/hwchase17/langchain
core[minor]: support pydantic v2 models in PydanticOutputParser (#18811)
As mentioned in #18322, the current PydanticOutputParser won't work for anyone trying to parse to pydantic v2 models. This PR adds a separate `PydanticV2OutputParser`, as well as a `langchain_core.pydantic_v2` namespace that will fail on import to any projects using pydantic<2. Happy to update the docs for output parsers if this is something we're interesting in adding. On a separate note, I also updated `check_pydantic.sh` to detect pydantic imports with leading whitespace and excluded the internal namespaces. That change can be separated into its own PR if needed. --------- Co-authored-by: Jan Nissen <jan23@gmail.com>pull/18991/head
parent
d0accc3275
commit
2e0ddd6fb8
@ -0,0 +1,72 @@
|
||||
from typing import Literal
|
||||
|
||||
import pydantic # pydantic: ignore
|
||||
import pytest
|
||||
|
||||
from langchain_core.exceptions import OutputParserException
|
||||
from langchain_core.language_models import ParrotFakeChatModel
|
||||
from langchain_core.output_parsers.pydantic import PydanticOutputParser, TBaseModel
|
||||
from langchain_core.prompts.prompt import PromptTemplate
|
||||
from langchain_core.utils.pydantic import PYDANTIC_MAJOR_VERSION
|
||||
|
||||
V1BaseModel = pydantic.BaseModel
|
||||
if PYDANTIC_MAJOR_VERSION == 2:
|
||||
from pydantic.v1 import BaseModel # pydantic: ignore
|
||||
|
||||
V1BaseModel = BaseModel # type: ignore
|
||||
|
||||
|
||||
class ForecastV2(pydantic.BaseModel):
|
||||
temperature: int
|
||||
f_or_c: Literal["F", "C"]
|
||||
forecast: str
|
||||
|
||||
|
||||
class ForecastV1(V1BaseModel):
|
||||
temperature: int
|
||||
f_or_c: Literal["F", "C"]
|
||||
forecast: str
|
||||
|
||||
|
||||
@pytest.mark.parametrize("pydantic_object", [ForecastV2, ForecastV1])
|
||||
def test_pydantic_parser_chaining(
|
||||
pydantic_object: TBaseModel,
|
||||
) -> None:
|
||||
prompt = PromptTemplate(
|
||||
template="""{{
|
||||
"temperature": 20,
|
||||
"f_or_c": "C",
|
||||
"forecast": "Sunny"
|
||||
}}""",
|
||||
input_variables=[],
|
||||
)
|
||||
|
||||
model = ParrotFakeChatModel()
|
||||
|
||||
parser = PydanticOutputParser(pydantic_object=pydantic_object) # type: ignore
|
||||
chain = prompt | model | parser
|
||||
|
||||
res = chain.invoke({})
|
||||
assert type(res) == pydantic_object
|
||||
assert res.f_or_c == "C"
|
||||
assert res.temperature == 20
|
||||
assert res.forecast == "Sunny"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("pydantic_object", [ForecastV2, ForecastV1])
|
||||
def test_pydantic_parser_validation(pydantic_object: TBaseModel) -> None:
|
||||
bad_prompt = PromptTemplate(
|
||||
template="""{{
|
||||
"temperature": "oof",
|
||||
"f_or_c": 1,
|
||||
"forecast": "Sunny"
|
||||
}}""",
|
||||
input_variables=[],
|
||||
)
|
||||
|
||||
model = ParrotFakeChatModel()
|
||||
|
||||
parser = PydanticOutputParser(pydantic_object=pydantic_object) # type: ignore
|
||||
chain = bad_prompt | model | parser
|
||||
with pytest.raises(OutputParserException):
|
||||
chain.invoke({})
|
Loading…
Reference in New Issue