├── README.md ├── langchain_model └── __init__.py └── setup.py /README.md: -------------------------------------------------------------------------------- 1 | # LangChain Model 2 | 3 | LangChain (prompts & LLMs) + Pydantic (data models) = LangChain Model 4 | 5 | ## 🚀 Overview 6 | 7 | This is an easy way to populate a Pydantic model using LLMs. 8 | Currently, this relies on OpenAI function calling, so only OpenAI can be used. 9 | There are currently two main use cases for LangChain Models - extraction and generation. 10 | 11 | ## 📄 Installation 12 | `pip install langchain_model` 13 | 14 | ## 💻 Usage 15 | 16 | ### Setup 17 | 18 | First, you need to set your OpenAI key: 19 | 20 | ```python 21 | import os 22 | os.environ["OPENAI_API_KEY"] = 'YOUR_OPENAI_API_KEY' 23 | ``` 24 | 25 | ### Quickstart 26 | 27 | Next, define a Pydantic model with the data schema you want to extract - and add the `langchain_model` decorator to it. 28 | 29 | ```python 30 | from pydantic import BaseModel, Field 31 | from langchain_model import langchain_model 32 | 33 | 34 | @langchain_model 35 | class Person(BaseModel): 36 | name: str = Field(description="The name of the person") 37 | age: int = Field(description="The age of the person") 38 | hobbies: list[str] = Field(description="Hobbies this person enjoys") 39 | ``` 40 | 41 | Now, just pass in text and get back a populated Pydantic model! 42 | 43 | ```python 44 | Person("Jenny (5) likes to play kickball") 45 | ``` 46 | ```text 47 | Person(name='Jenny', age=5, hobbies=['play kickball']) 48 | ``` 49 | 50 | ### LLM Configuration 51 | 52 | By default, this will use the following LLM configuration: 53 | 54 | ```python 55 | ChatOpenAI(model="gpt-3.5-turbo", temperature=0) 56 | ``` 57 | 58 | If you want to use a different configuration, you easily can! 59 | 60 | ```python 61 | from pydantic import BaseModel, Field 62 | from langchain_model import langchain_model 63 | from langchain.chat_models import ChatOpenAI 64 | 65 | 66 | @langchain_model(llm=ChatOpenAI(model="gpt-4", temperature=.5)) 67 | class Person(BaseModel): 68 | name: str = Field(description="The name of the person") 69 | age: int = Field(description="The age of the person") 70 | hobbies: list[str] = Field(description="Hobbies this person enjoys") 71 | ``` 72 | 73 | ### Prompt Configuration 74 | 75 | By default, this will use the following prompt: 76 | 77 | ```python 78 | prompt_msgs = [ 79 | SystemMessage( 80 | content="You are a world class algorithm for extracting information in structured formats." 81 | ), 82 | HumanMessage( 83 | content="Use the given format to extract information from the following input:" 84 | ), 85 | HumanMessagePromptTemplate.from_template("{input}"), 86 | HumanMessage(content="Tips: Make sure to answer in the correct format"), 87 | ] 88 | default_prompt = ChatPromptTemplate(messages=prompt_msgs) 89 | ``` 90 | 91 | If you want to use a different configuration, you easily can! 92 | 93 | ```python 94 | from pydantic import BaseModel, Field 95 | from langchain_model import langchain_model 96 | from langchain.prompts import ChatPromptTemplate 97 | 98 | 99 | prompt = ChatPromptTemplate.from_template("Make up a person that meets this criteria: {criteria}") 100 | @langchain_model(prompt=prompt) 101 | class Person(BaseModel): 102 | name: str = Field(description="The name of the person") 103 | age: int = Field(description="The age of the person") 104 | hobbies: list[str] = Field(description="Hobbies this person enjoys") 105 | ``` 106 | 107 | You can then call this to get data in that format 108 | 109 | ```python 110 | Person("harry potter character") 111 | ``` 112 | ```text 113 | Person(name='Hermione Granger', age=17, hobbies=['reading', 'studying', 'spellcasting']) 114 | ``` 115 | 116 | ## 🔧 Use Cases 117 | 118 | There are currently two main uses for `langchain_models` - extraction and generation. 119 | You can control which one you use by specifying the `mode` parameter. 120 | All this does is fetch the default prompt to use. So if you specify `mode`, you cannot also pass in a prompt. 121 | 122 | ### Extraction 123 | 124 | This means extracting structured information from text. This is default mode of `langchain_model`, 125 | but you can make super sure you're using this mode by using `@langchain_model(mode="extract")`. 126 | 127 | ```python 128 | from pydantic import BaseModel, Field 129 | from langchain_model import langchain_model 130 | 131 | 132 | @langchain_model(mode="extract") 133 | class Person(BaseModel): 134 | name: str = Field(description="The name of the person") 135 | age: int = Field(description="The age of the person") 136 | hobbies: list[str] = Field(description="Hobbies this person enjoys") 137 | 138 | Person("Jenny (5) likes to play kickball") 139 | ``` 140 | 141 | ```text 142 | Person(name='Jenny', age=5, hobbies=['play kickball']) 143 | ``` 144 | 145 | ### Generation 146 | 147 | This means generating structured information based on the user input. 148 | To use this, use `@langchain_model(mode="generate")` 149 | 150 | ```python 151 | from pydantic import BaseModel, Field 152 | from langchain_model import langchain_model 153 | 154 | 155 | @langchain_model(mode="generate") 156 | class Person(BaseModel): 157 | name: str = Field(description="The name of the person") 158 | age: int = Field(description="The age of the person") 159 | hobbies: list[str] = Field(description="Hobbies this person enjoys") 160 | 161 | Person("harry potter character") 162 | ``` 163 | ```text 164 | Person(name='Harry Potter', age=17, hobbies=['Quidditch', 'Defeating Dark wizards', 'Exploring the Wizarding World']) 165 | ``` 166 | -------------------------------------------------------------------------------- /langchain_model/__init__.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, TypeVar, Any, Callable, Type 2 | 3 | from langchain.chains.openai_functions import ( 4 | create_structured_output_chain, 5 | ) 6 | from langchain.chat_models import ChatOpenAI 7 | from langchain.prompts import ChatPromptTemplate, HumanMessagePromptTemplate 8 | from langchain.schema import HumanMessage, SystemMessage, BasePromptTemplate 9 | from pydantic import BaseModel 10 | 11 | 12 | M = TypeVar("M", bound=BaseModel) 13 | 14 | # Taken from LangChain docs, need to validate that this is best. 15 | prompt_msgs = [ 16 | SystemMessage( 17 | content="You are a world class algorithm for extracting information in structured formats." 18 | ), 19 | HumanMessage( 20 | content="Use the given format to extract information from the following input:" 21 | ), 22 | HumanMessagePromptTemplate.from_template("{input}"), 23 | HumanMessage(content="Tips: Make sure to answer in the correct format"), 24 | ] 25 | default_extraction_prompt = ChatPromptTemplate(messages=prompt_msgs) 26 | 27 | generation_prompt_msgs = [ 28 | SystemMessage(content="Generate fake data based on user input."), 29 | HumanMessagePromptTemplate.from_template("{input}"), 30 | ] 31 | default_generation_prompt = ChatPromptTemplate(messages=generation_prompt_msgs) 32 | 33 | 34 | def langchain_model( 35 | _cls=None, 36 | llm: Optional[ChatOpenAI] = None, 37 | prompt: Optional[BasePromptTemplate] = None, 38 | mode: Optional[str] = None, 39 | **kwargs: Any 40 | ) -> Callable[[Type[M]], Type[M]]: 41 | if mode is None: 42 | prompt = prompt or default_extraction_prompt 43 | llm = llm or ChatOpenAI(model="gpt-3.5-turbo", temperature=0) 44 | else: 45 | if prompt is not None: 46 | raise ValueError( 47 | "If prompt is passed in, mode should NOT be (all mode does is set the default prompt)" 48 | ) 49 | if mode == "extract": 50 | prompt = default_extraction_prompt 51 | llm = llm or ChatOpenAI(model="gpt-3.5-turbo", temperature=0) 52 | elif mode == "generate": 53 | prompt = default_generation_prompt 54 | # Set high temperature by default for generate mode. 55 | llm = llm or ChatOpenAI(model="gpt-3.5-turbo", temperature=1) 56 | else: 57 | raise ValueError( 58 | "Mode is not valid, should be one of `extract` or `generate`." 59 | ) 60 | if _cls is None: 61 | 62 | def class_rebuilder(cls): 63 | chain = create_structured_output_chain(cls, llm, prompt, **kwargs) 64 | 65 | class NewClass(cls): 66 | def __init__(self, *args, **kwargs): 67 | super().__init__(**chain.run(args[0]).dict()) 68 | 69 | NewClass.__name__ = cls.__name__ 70 | return NewClass 71 | 72 | return class_rebuilder 73 | else: 74 | chain = create_structured_output_chain(_cls, llm, prompt, **kwargs) 75 | 76 | class NewClass(_cls): 77 | def __init__(self, *args, **kwargs): 78 | super().__init__(**chain.run(args[0]).dict()) 79 | 80 | NewClass.__name__ = _cls.__name__ 81 | return NewClass 82 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name="langchain_model", 5 | version="0.0.6", 6 | description="Populate Pydantic Models with LangChain", 7 | packages=find_packages(), 8 | install_requires=[ 9 | "langchain", 10 | "pydantic<2", 11 | ], 12 | ) 13 | --------------------------------------------------------------------------------