go over how to use Open AI functions with LangChain expression language. We'll also introduce Pydantic, a Python library that makes it easier to construct OpenAI functions. Let's get to it. So what is Pydantic? Pydantic is a data validation library for Python. It makes it really easy to define different schemas. And then it also makes it really easy to export those schemas to JSON. That's going to come in handy because we can use the Pydantic objects to create the Open AI functions descriptions. If you remember, those OpenAI function descriptions were a big blob of JSON with a bunch of different components. And so rather than having to think about all of that, we can use Pydantic. The way that we'll do that is we'll define a Pydantic class. It's very similar to a normal Python class, so you can see them side by side here. And the main difference is rather than having an init method, we're just going to list the attributes and their type under the class definition. We're not actually going to use these classes for anything. We're just going to use it to create that Open AI functions JSON. Let's take a look at what this looks like in practice. First we're going to load in all our normal environment variables. We'll then import a few Pydantic classes as well as some type hints that we'll use later on. We'll start with just a normal Python class. So this is a normal Python class for user. It has an init method and we pass in name, age, and email. And so if we create an instance of this, we can see that we have a normal Python class and we can access those elements accordingly. If we pass in an invalid value for age, so here we have bar, which is a string, and we expect it to be an integer, we can see that it creates it okay. And if we look at the element, we have it accessed. And that's actually not good, because we do want to have some validation that if we say it's an integer, it actually is an integer. Let's now take a look at doing this with Pydantic. We can now have our class inherit from base model and this is imported from Pydantic above. And then we can define our attributes just right under the class definition with various type hints. So we're saying name is a string, age is an integer, email is another string. We can create an object as normal and if we inspect this object we can actually see that it prints out nicely all the different elements and so this is a nice benefit of Pydantic as well. Similarly we can access individual elements like that. Now if we try to pass in an invalid argument for age if we try to pass in a string of value bar, One other thing we can do with Pydantic is we can actually nest these data structures. So here we're going to define a class type and this is a Pydantic model, This is a brief introduction to Pydantic if you want to learn more I'd encourage you to go look at their docs or now is also a good time to play around with it a little bit try out some different type hints see what it ends up looking like. Now we're going to talk about how to use Pydantic to create these Open AI function definitions. So what we're going to do is we're going to create a Pydantic object that we can then cast to that JSON schema that we went over in lesson one. Importantly here the Pydantic object that we create isn't actually going to do anything we're just using it to create this schema. We'll create a class called weather search and this lines up with the function that we created earlier. We're inheriting from base model. You can see also here that we're adding a doc string and we'll see how this gets reflected in later on. And then we're having a single argument called airport code which we type in as a string, Field is a Pydantic module that we can use to set things like the description of various fields. From there we're going to import a function from LangChain. From LangChain utils open AI functions import convert Pydantic to open AI functions. This function is going to do exactly what it sounds like. It's going to convert a Pydantic object to the JSON schema that's expected for an Open AI function. And so if we pass the class in here and notice that we're passing in the name of the class we're not initializing it with an object. We're passing in the class type, we get back weather function. If we look at what weather function is, this is a JSON schema in the same form as what we passed into OpenAI in the first lesson. We can see that it has name weather search, so this is the name of the Python class. We can also see that it has a description, and this is pulled from the doc string here. For parameters, we can see that it has a list of properties, one of which is airport code, or the only one of which is airport code, and this is pulled from the arguments that are listed here. Airport code itself has a description, and that's pulled from the field description here, and then it has a type, which is string. We've made some assumptions about how people would want to create OpenAI functions. So one thing in particular that we've done is we've made this doc string here required, because that gets put in to the description of the function and as we talked about earlier the functions really are prompts and so if you're passing in a function you want to give a good description of what that function is doing and so we've added some checks that enforce that you are passing in the description. So if we have a separate class called weather search 1, Now, however, you'll see that there's no description for the airport code argument. Descriptions to arguments are optional in LangChain. They're not required. Let's now look at combining open AI functions with LangChain Expression Language. So first, let's import the chat model. We'll then create an instance of this model. And for now, we'll just interact with it directly. So specifically what we're going to do is we're going to call invoke on this model. And then we can also pass in keyword arguments. So we'll pass in this weather function that we defined above. And so here, what we get back from the model is going to be a content message that's null. And then in the additional quarks field, we're going to have this function call parameter, which returns a function call with name weather search, and then arguments airport code SFO. So it's using the function appropriately. What we can also do is we can bind the function invocations to the model. And one reason for doing this is so that we could just pass that model plus functions around together and not have to worry about always passing in these function keyword arguments. So here if we do model with function and set that equal to model.bind functions equals weather function. We can now call this model with function directly and we can just pass in, And now we can see that it responds and it's still using the function call and that's because it still knows that the function calls exist because we've bound them to the model like that. We can do the same thing and we can force the model to use a function. And so we can bind the weather function as above, We can now use this model which has the the function bound to it in a chain like we'd normally use a model. And so in order to do that, we're going to import a prompt template. We'll create a really simple prompt template. So we'll do something like chat prompt template from messages. We'll create a really simple system message. And then we'll add our user input here. We'll then create a chain by just piping together prompt, The next step up is we can pass in a list of functions and let the language model decide which one to use based on the question context. Here we're going to create another Pydantic model, this one called artist search, and we're going to have the description and the usage be call this to get the names of songs by a particular artist. We're going to add two arguments, the artist name and then n, which is an integer corresponding to the number of results to look up. We'll then create a new list of functions and this time there's going to be 2, And we're going to create a new object called model with functions a new object called model with functions, And now let's try invoking this with different inputs and see what happens. So first ask it, what is the weather in SF, And again here, we're not forcing it to call a function. So if we just say hi, it should respond with something that doesn't use functions at all. So hello, how can I assist you today? Now is a good point to pause and try this out. Try passing in a few different Pydantic objects to convert Pydantic to Open AI functions, That's it for an overview of using LangChain expression In the next lesson, we're going to talk about one of the main use cases for this type of functionality, tagging and extraction.