diff --git a/libs/langchain/langchain/chains/__init__.py b/libs/langchain/langchain/chains/__init__.py index b20d3fecb44..3a34b82ed51 100644 --- a/libs/langchain/langchain/chains/__init__.py +++ b/libs/langchain/langchain/chains/__init__.py @@ -82,6 +82,7 @@ from langchain.chains.router import ( ) from langchain.chains.sequential import SequentialChain, SimpleSequentialChain from langchain.chains.sql_database.query import create_sql_query_chain +from langchain.chains.structured_output import create_structured_output_runnable from langchain.chains.summarize import load_summarize_chain from langchain.chains.transform import TransformChain @@ -145,5 +146,6 @@ __all__ = [ "create_sql_query_chain", "create_retrieval_chain", "create_history_aware_retriever", + "create_structured_output_runnable", "load_summarize_chain", ] diff --git a/libs/langchain/langchain/chains/structured_output/base.py b/libs/langchain/langchain/chains/structured_output/base.py index d825c1fe7c8..965f215ecd3 100644 --- a/libs/langchain/langchain/chains/structured_output/base.py +++ b/libs/langchain/langchain/chains/structured_output/base.py @@ -22,7 +22,7 @@ from langchain.output_parsers.openai_functions import ( def create_openai_fn_runnable( functions: Sequence[Union[Dict[str, Any], Type[BaseModel], Callable]], llm: Runnable, - prompt: BasePromptTemplate, + prompt: Optional[BasePromptTemplate] = None, *, enforce_single_function_usage: bool = True, output_parser: Optional[Union[BaseOutputParser, BaseGenerationOutputParser]] = None, @@ -64,7 +64,6 @@ def create_openai_fn_runnable( from langchain.chains.structured_output import create_openai_fn_runnable from langchain_openai import ChatOpenAI - from langchain_core.prompts import ChatPromptTemplate from langchain_core.pydantic_v1 import BaseModel, Field @@ -85,15 +84,8 @@ def create_openai_fn_runnable( llm = ChatOpenAI(model="gpt-4", temperature=0) - prompt = ChatPromptTemplate.from_messages( - [ - ("system", "You are a world class algorithm for recording entities."), - ("human", "Make calls to the relevant function to record the entities in the following input: {input}"), - ("human", "Tip: Make sure to answer in the correct format"), - ] - ) - chain = create_openai_fn_runnable([RecordPerson, RecordDog], llm, prompt) - chain.invoke({"input": "Harry was a chubby brown beagle who loved chicken"}) + structured_llm = create_openai_fn_runnable([RecordPerson, RecordDog], llm) + structured_llm.invoke("Harry was a chubby brown beagle who loved chicken) # -> RecordDog(name="Harry", color="brown", fav_food="chicken") """ # noqa: E501 if not functions: @@ -103,14 +95,17 @@ def create_openai_fn_runnable( if len(openai_functions) == 1 and enforce_single_function_usage: llm_kwargs["function_call"] = {"name": openai_functions[0]["name"]} output_parser = output_parser or get_openai_output_parser(functions) - return prompt | llm.bind(**llm_kwargs) | output_parser + if prompt: + return prompt | llm.bind(**llm_kwargs) | output_parser + else: + return llm.bind(**llm_kwargs) | output_parser # TODO: implement mode='openai-tools'. def create_structured_output_runnable( output_schema: Union[Dict[str, Any], Type[BaseModel]], llm: Runnable, - prompt: BasePromptTemplate, + prompt: Optional[BasePromptTemplate] = None, *, output_parser: Optional[Union[BaseOutputParser, BaseGenerationOutputParser]] = None, mode: Literal["openai-functions", "openai-json"] = "openai-functions", @@ -152,7 +147,28 @@ def create_structured_output_runnable( from typing import Optional - from langchain.chains.structured_output import create_structured_output_runnable + from langchain.chains import create_structured_output_runnable + from langchain_openai import ChatOpenAI + from langchain_core.pydantic_v1 import BaseModel, Field + + class Dog(BaseModel): + '''Identifying information about a dog.''' + + name: str = Field(..., description="The dog's name") + color: str = Field(..., description="The dog's color") + fav_food: Optional[str] = Field(None, description="The dog's favorite food") + + llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0) + structured_llm = create_structured_output_runnable(Dog, llm, mode="openai-functions") + structured_llm.invoke("Harry was a chubby brown beagle who loved chicken") + # -> Dog(name="Harry", color="brown", fav_food="chicken") + + OpenAI functions with prompt example: + .. code-block:: python + + from typing import Optional + + from langchain.chains import create_structured_output_runnable from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate from langchain_core.pydantic_v1 import BaseModel, Field @@ -165,23 +181,20 @@ def create_structured_output_runnable( fav_food: Optional[str] = Field(None, description="The dog's favorite food") llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0) + structured_llm = create_structured_output_runnable(Dog, llm, mode="openai-functions") + system = '''Extract information about any dogs mentioned in the user input.''' prompt = ChatPromptTemplate.from_messages( - [ - ("system", "You are a world class algorithm for extracting information in structured formats."), - ("human", "Use the given format to extract information from the following input: {input}"), - ("human", "Tip: Make sure to answer in the correct format"), - ] + [("system", system), ("human", "{input}"),] ) - chain = create_structured_output_runnable(Dog, llm, prompt, mode="openai-functions") + chain = prompt | structured_llm chain.invoke({"input": "Harry was a chubby brown beagle who loved chicken"}) # -> Dog(name="Harry", color="brown", fav_food="chicken") - OpenAI json response format example: .. code-block:: python from typing import Optional - from langchain.chains.structured_output import create_structured_output_runnable + from langchain.chains import create_structured_output_runnable from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate from langchain_core.pydantic_v1 import BaseModel, Field @@ -194,32 +207,30 @@ def create_structured_output_runnable( fav_food: Optional[str] = Field(None, description="The dog's favorite food") llm = ChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0) + structured_llm = create_structured_output_runnable(Dog, llm, mode="openai-json") system = '''You are a world class assistant for extracting information in structured JSON formats. \ Extract a valid JSON blob from the user input that matches the following JSON Schema: {output_schema}''' prompt = ChatPromptTemplate.from_messages( - [ - ("system", system), - ("human", "{input}"), - ] + [("system", system), ("human", "{input}"),] ) - chain = create_structured_output_runnable(Dog, llm, prompt, mode="openai-json") + chain = prompt | structured_llm chain.invoke({"input": "Harry was a chubby brown beagle who loved chicken"}) """ # noqa: E501 if mode == "openai-functions": return _create_openai_functions_structured_output_runnable( output_schema, llm, - prompt, + prompt=prompt, output_parser=output_parser, enforce_single_function_usage=enforce_single_function_usage, **kwargs, ) elif mode == "openai-json": return _create_openai_json_runnable( - output_schema, llm, prompt, output_parser=output_parser, **kwargs + output_schema, llm, prompt=prompt, output_parser=output_parser, **kwargs ) else: raise ValueError( @@ -263,7 +274,7 @@ def get_openai_output_parser( def _create_openai_json_runnable( output_schema: Union[Dict[str, Any], Type[BaseModel]], llm: Runnable, - prompt: BasePromptTemplate, + prompt: Optional[BasePromptTemplate] = None, *, output_parser: Optional[Union[BaseOutputParser, BaseGenerationOutputParser]] = None, ) -> Runnable: @@ -277,17 +288,20 @@ def _create_openai_json_runnable( output_parser = output_parser or JsonOutputParser() schema_as_dict = output_schema - if "output_schema" in prompt.input_variables: - prompt = prompt.partial(output_schema=json.dumps(schema_as_dict, indent=2)) - llm = llm.bind(response_format={"type": "json_object"}) - return prompt | llm | output_parser + if prompt: + if "output_schema" in prompt.input_variables: + prompt = prompt.partial(output_schema=json.dumps(schema_as_dict, indent=2)) + + return prompt | llm | output_parser + else: + return llm | output_parser def _create_openai_functions_structured_output_runnable( output_schema: Union[Dict[str, Any], Type[BaseModel]], llm: Runnable, - prompt: BasePromptTemplate, + prompt: Optional[BasePromptTemplate] = None, *, output_parser: Optional[Union[BaseOutputParser, BaseGenerationOutputParser]] = None, **kwargs: Any, @@ -315,7 +329,7 @@ def _create_openai_functions_structured_output_runnable( return create_openai_fn_runnable( [function], llm, - prompt, + prompt=prompt, output_parser=output_parser, **kwargs, ) diff --git a/libs/langchain/tests/unit_tests/chains/test_imports.py b/libs/langchain/tests/unit_tests/chains/test_imports.py index cf76a851b8c..4da79031fe3 100644 --- a/libs/langchain/tests/unit_tests/chains/test_imports.py +++ b/libs/langchain/tests/unit_tests/chains/test_imports.py @@ -62,6 +62,7 @@ EXPECTED_ALL = [ "create_history_aware_retriever", "create_retrieval_chain", "load_summarize_chain", + "create_structured_output_runnable", ]