mirror of
https://github.com/hwchase17/langchain.git
synced 2025-09-09 15:03:21 +00:00
core,groq,openai,mistralai,robocorp,fireworks,anthropic[patch]: Update BaseModel subclass and instance checks to handle both v1 and proper namespaces (#24417)
After this PR chat models will correctly handle pydantic 2 with bind_tools and with_structured_output. ```python import pydantic print(pydantic.__version__) ``` 2.8.2 ```python from langchain_openai import ChatOpenAI from pydantic import BaseModel, Field class Add(BaseModel): x: int y: int model = ChatOpenAI().bind_tools([Add]) print(model.invoke('2 + 5').tool_calls) model = ChatOpenAI().with_structured_output(Add) print(type(model.invoke('2 + 5'))) ``` ``` [{'name': 'Add', 'args': {'x': 2, 'y': 5}, 'id': 'call_PNUFa4pdfNOYXxIMHc6ps2Do', 'type': 'tool_call'}] <class '__main__.Add'> ``` ```python from langchain_openai import ChatOpenAI from pydantic.v1 import BaseModel, Field class Add(BaseModel): x: int y: int model = ChatOpenAI().bind_tools([Add]) print(model.invoke('2 + 5').tool_calls) model = ChatOpenAI().with_structured_output(Add) print(type(model.invoke('2 + 5'))) ``` ```python [{'name': 'Add', 'args': {'x': 2, 'y': 5}, 'id': 'call_hhiHYP441cp14TtrHKx3Upg0', 'type': 'tool_call'}] <class '__main__.Add'> ``` Addresses issues: https://github.com/langchain-ai/langchain/issues/22782 --------- Co-authored-by: Eugene Yurtsev <eyurtsev@gmail.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
from typing import Any, List, Optional, Type, Union
|
||||
from typing import Any, List, Optional, Type, Union, cast
|
||||
|
||||
from langchain_core.language_models import BaseLanguageModel
|
||||
from langchain_core.messages import HumanMessage, SystemMessage
|
||||
@@ -10,6 +10,7 @@ from langchain_core.output_parsers.openai_functions import (
|
||||
from langchain_core.prompts import PromptTemplate
|
||||
from langchain_core.prompts.chat import ChatPromptTemplate, HumanMessagePromptTemplate
|
||||
from langchain_core.pydantic_v1 import BaseModel, Field
|
||||
from langchain_core.utils.pydantic import is_basemodel_subclass
|
||||
|
||||
from langchain.chains.llm import LLMChain
|
||||
from langchain.chains.openai_functions.utils import get_llm_kwargs
|
||||
@@ -45,7 +46,7 @@ def create_qa_with_structure_chain(
|
||||
|
||||
"""
|
||||
if output_parser == "pydantic":
|
||||
if not (isinstance(schema, type) and issubclass(schema, BaseModel)):
|
||||
if not (isinstance(schema, type) and is_basemodel_subclass(schema)):
|
||||
raise ValueError(
|
||||
"Must provide a pydantic class for schema when output_parser is "
|
||||
"'pydantic'."
|
||||
@@ -60,10 +61,10 @@ def create_qa_with_structure_chain(
|
||||
f"Got unexpected output_parser: {output_parser}. "
|
||||
f"Should be one of `pydantic` or `base`."
|
||||
)
|
||||
if isinstance(schema, type) and issubclass(schema, BaseModel):
|
||||
schema_dict = schema.schema()
|
||||
if isinstance(schema, type) and is_basemodel_subclass(schema):
|
||||
schema_dict = cast(dict, schema.schema())
|
||||
else:
|
||||
schema_dict = schema
|
||||
schema_dict = cast(dict, schema)
|
||||
function = {
|
||||
"name": schema_dict["title"],
|
||||
"description": schema_dict["description"],
|
||||
|
@@ -24,6 +24,7 @@ from langchain_core.utils.function_calling import (
|
||||
convert_to_openai_function,
|
||||
convert_to_openai_tool,
|
||||
)
|
||||
from langchain_core.utils.pydantic import is_basemodel_subclass
|
||||
|
||||
|
||||
@deprecated(
|
||||
@@ -465,7 +466,7 @@ def _get_openai_tool_output_parser(
|
||||
*,
|
||||
first_tool_only: bool = False,
|
||||
) -> Union[BaseOutputParser, BaseGenerationOutputParser]:
|
||||
if isinstance(tool, type) and issubclass(tool, BaseModel):
|
||||
if isinstance(tool, type) and is_basemodel_subclass(tool):
|
||||
output_parser: Union[BaseOutputParser, BaseGenerationOutputParser] = (
|
||||
PydanticToolsParser(tools=[tool], first_tool_only=first_tool_only)
|
||||
)
|
||||
@@ -493,7 +494,7 @@ def get_openai_output_parser(
|
||||
not a Pydantic class, then the output parser will automatically extract
|
||||
only the function arguments and not the function name.
|
||||
"""
|
||||
if isinstance(functions[0], type) and issubclass(functions[0], BaseModel):
|
||||
if isinstance(functions[0], type) and is_basemodel_subclass(functions[0]):
|
||||
if len(functions) > 1:
|
||||
pydantic_schema: Union[Dict, Type[BaseModel]] = {
|
||||
convert_to_openai_function(fn)["name"]: fn for fn in functions
|
||||
@@ -516,7 +517,7 @@ def _create_openai_json_runnable(
|
||||
output_parser: Optional[Union[BaseOutputParser, BaseGenerationOutputParser]] = None,
|
||||
) -> Runnable:
|
||||
""""""
|
||||
if isinstance(output_schema, type) and issubclass(output_schema, BaseModel):
|
||||
if isinstance(output_schema, type) and is_basemodel_subclass(output_schema):
|
||||
output_parser = output_parser or PydanticOutputParser(
|
||||
pydantic_object=output_schema, # type: ignore
|
||||
)
|
||||
|
Reference in New Issue
Block a user