OpenAI Function Calling In LangChain

Tutorial 3

Jan Kirenz

OpenAI Function Calling In LangChain

Setup

import os
import openai

from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv()) 
openai.api_key = os.environ['OPENAI_API_KEY']
from typing import List
from pydantic import BaseModel, Field

from langchain.utils.openai_functions import convert_pydantic_to_openai_function
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate

Pydantic Syntax

Basics

  • Pydantic data classes are a blend of Python’s data classes with the validation power of Pydantic.

  • They offer a concise way to define data structures while ensuring that the data adheres to specified types and constraints.

Create class with standard Python

class User:
    def __init__(self, name: str, age: int, email: str):
        self.name = name
        self.age = age
        self.email = email

Creata an instance

foo = User(name="Joe",age=32, email="joe@gmail.com")
foo.name
  • ‘Joe’
foo = User(name="Joe",age="bar", email="joe@gmail.com")
foo.age
  • ‘bar’

Create class with Pydantic

class pUser(BaseModel):
    name: str
    age: int
    email: str

Create instance

foo_p = pUser(name="Jane", age=32, email="jane@gmail.com")
foo_p.name
  • ‘Jane’
foo_p = pUser(name="Jane", age="bar", email="jane@gmail.com")
  • ValidationError: 1 validation error for pUser age value is not a valid integer (type=type_error.integer)

Nest data structures

  • Define class type which includes another object (nest objects)
class Class(BaseModel):
    students: List[pUser]
obj = Class(
    students=[pUser(name="Jane", age=32, email="jane@gmail.com")]
)
obj
  • Class(students=[pUser(name=‘Jane’, age=32, email=‘jane@gmail.com’)])

Pydantic to OpenAI function definition

Weather search function

class WeatherSearch(BaseModel):
    """Call this with an airport code to get the weather at that airport"""
    airport_code: str = Field(description="airport code to get weather for")
  • Pass in class type
weather_function = convert_pydantic_to_openai_function(WeatherSearch)

Inspect class type

weather_function

{‘name’: ‘WeatherSearch’, ‘description’: ‘Call this with an airport code to get the weather at that airport’, ‘parameters’: {‘title’: ‘WeatherSearch’, ‘description’: ‘Call this with an airport code to get the weather at that airport’, ‘type’: ‘object’, ‘properties’: {‘airport_code’: {‘title’: ‘Airport Code’, ‘description’: ‘airport code to get weather for’, ‘type’: ‘string’}}, ‘required’: [‘airport_code’]}}

Combine OpenAI with LEL

Use model directly

model = ChatOpenAI()
model.invoke("what is the weather in SF today?", functions=[weather_function])
  • AIMessage(content=’‘, additional_kwargs={’function_call’: {‘name’: ‘WeatherSearch’, ‘arguments’: ‘{“airport_code”: “SFO”}’}})

Use bind

model_with_function = model.bind(functions=[weather_function])
model_with_function.invoke("what is the weather in sf?")
  • AIMessage(content=’‘, additional_kwargs={’function_call’: {‘name’: ‘WeatherSearch’, ‘arguments’: ‘{“airport_code”: “SFO”}’}})

Forcing it to use a function

model_with_forced_function = model.bind(functions=[weather_function], function_call={"name":"WeatherSearch"})
model_with_forced_function.invoke("what is the weather in sf?")
  • AIMessage(content=’‘, additional_kwargs={’function_call’: {‘name’: ‘WeatherSearch’, ‘arguments’: ‘{“airport_code”: “SFO”}’}})
model_with_forced_function.invoke("hi!")
  • AIMessage(content=’‘, additional_kwargs={’function_call’: {‘name’: ‘WeatherSearch’, ‘arguments’: ‘{“airport_code”: “SFO”}’}})

Using in a chain

Prompt template

We can use this model bound to function in a chain as we normally would

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant"),
    ("user", "{input}")
])

Chain

chain = prompt | model_with_function
chain.invoke({"input": "what is the weather in sf?"})
  • AIMessage(content=’‘, additional_kwargs={’function_call’: {‘name’: ‘WeatherSearch’, ‘arguments’: ‘{“airport_code”: “SFO”}’}})

Using multiple functions

Create ArtistSearch function

Even better, we can pass a set of function and let the LLM decide which to use based on the question context.

class ArtistSearch(BaseModel):
    """Call this to get the names of songs by a particular artist"""
    artist_name: str = Field(description="name of artist to look up")
    n: int = Field(description="number of results")
functions = [
    convert_pydantic_to_openai_function(WeatherSearch),
    convert_pydantic_to_openai_function(ArtistSearch),
]
model_with_functions = model.bind(functions=functions)

Invoke function with weather

model_with_functions.invoke("what is the weather in sf?")
  • AIMessage(content=’‘, additional_kwargs={’function_call’: {‘name’: ‘WeatherSearch’, ‘arguments’: ‘{“airport_code”: “SFO”}’}})

Invoke function with three songs

model_with_functions.invoke("what are three songs by taylor swift?")
  • AIMessage(content=’‘, additional_kwargs={’function_call’: {‘name’: ‘ArtistSearch’, ‘arguments’: ‘{“artist_name”: “taylor swift”,“n”: 3}’}})

Invoke function with hi

model_with_functions.invoke("hi!")
  • AIMessage(content=‘Hello! How can I assist you today?’)

Acknowledgments

What’s next?

Congratulations! You have completed this tutorial 👍

Next, you may want to go back to the lab’s website