The ability to extract insights from user input has always been an excellent use case for OpenAI APIs. I have seen a lot of people using GPT APIs to perform NLP tasks. From Intent Detection, Classification to NER (Named Entity Recognition) it's quite useful, especially for developers who do not have extensive ML backgrounds.
And this new capability of "Function Calling" takes this use case to a whole new level. In this article, we are going to explore how we can use this feature to get real data about a stock in a few simple lines of Python.
Understanding the core problem
Getting Stocks data is not difficult, it's only a matter of simple API calls.
Here are the tricky parts :
Identifying what the user wants to do
Mapping the users intent to the solution function
Extracting data that our function expects
Chaining the prompts together to respond
Before the availability of the function calling feature, we had to juggle all these things which led to complicated flow. Sure frameworks like langchain
can help us simplify this juggling. But the ability of the model itself to do this for us is quite amazing.
I am going to demonstrate this with a simple example :
Answer user's questions about stock prices & other information
Get your API keys
To get the stock price data, we are going to use Polygon,
You can signup there and then access your API keys at: https://polygon.io/dashboard/api-keys
Polygon has a Python client but to keep it more abstract, we are going to use its REST API
If you don't already have your OpenAI API keys, get them from here: https://platform.openai.com/account/api-keys
Let's get into the Code
First, let's install openai
and reuests
!pip install openai
!pip install requests
Now, let's setup some global variables and API keys (OpenAI & Polygon)
import openai
global OPENAI_API_KEY
global POLYGON_API_KEY
OPENAI_API_KEY="Your OpenAI API Key goes here"
POLYGON_API_KEY="Your Polygon API Key goes here"
openai.api_key = OPENAI_API_KEY
Imports, some more global variables and setup code
import datetime
import requests
import json
global today_str
today = datetime.date.today()
today_str = today.strftime('%Y-%m-%d')
Functions to fetch Stocks data
Let's write a function that we can use to get stock prices data from the polygon API
def get_stock_price_by_date(ticker_symbol, date):
url = f"https://api.polygon.io/v1/open-close/{ticker_symbol}/{date}?adjusted=true&apiKey={POLYGON_API_KEY}"
try:
response = requests.request(method='GET', url=url)
response.raise_for_status()
# print(response.text)
return json.dumps(response.text)
except Exception as e:
print(f"An error occurred: {e}")
return None
You can try calling this function with manual parameter input, to make sure it works as expected
print(get_stock_price_by_date(ticker_symbol="GOOGL", date="2023-06-16"))
{
"status": "OK",
"from": "2023-06-15",
"symbol": "GOOGL",
"open": 123.14,
"high": 125.46,
"low": 122.4,
"close": 125.09,
"volume": 3.5146268e+07,
"afterHours": 124.85,
"preMarket": 123.48
}
Let's write another function that fetches the data about stock according to the SEC filing
def get_stock_sec_details(ticker_symbol, date = today_str):
url = f"https://api.polygon.io/v3/reference/tickers/{ticker_symbol}?date={date}&apiKey={POLYGON_API_KEY}"
try:
response = requests.request(method='GET', url=url)
response.raise_for_status()
# print(response.text)
return json.dumps(response.text)
except Exception as e:
print(f"An error occurred: {e}")
return None
We can try calling this function to check how it responds
print(get_stock_sec_details(ticker_symbol="AAPL"))
{
"request_id": "f7d8eb4aac2a75fbb4b8df1f25ab4ead",
"results": {
"ticker": "AAPL",
"name": "Apple Inc.",
"market": "stocks",
"locale": "us",
"primary_exchange": "XNAS",
"type": "CS",
"active": true,
"currency_name": "usd",
"cik": "0000320193",
"composite_figi": "BBG000B9XRY4",
"share_class_figi": "BBG001S5N8V8",
"market_cap": 2.92569585902e+12,
"phone_number": "(408) 996-1010",
"address": {
"address1": "ONE APPLE PARK WAY",
"city": "CUPERTINO",
"state": "CA",
"postal_code": "95014"
},
"description": "Apple designs a wide variety of consumer electronic devices, including smartphones (iPhone), tablets (iPad), PCs (Mac), smartwatches (Apple Watch), and AirPods, among others. In addition, Apple offers its customers a variety of services such as Apple Music, iCloud, Apple Care, Apple TV+, Apple Arcade, Apple Fitness, Apple Card, and Apple Pay, among others. Apple's products include internally developed software and semiconductors, and the firm is well known for its integration of hardware, software, semiconductors, and services. Apple's products are distributed online as well as through company-owned stores and third-party retailers.",
"sic_code": "3571",
"sic_description": "ELECTRONIC COMPUTERS",
"ticker_root": "AAPL",
"homepage_url": "https://www.apple.com",
"total_employees": 164000,
"list_date": "1980-12-12",
"branding": {
"logo_url": "https://api.polygon.io/v1/reference/company-branding/d3d3LmFwcGxlLmNvbQ/images/2023-05-01_logo.svg",
"icon_url": "https://api.polygon.io/v1/reference/company-branding/d3d3LmFwcGxlLmNvbQ/images/2023-05-01_icon.jpeg"
},
"share_class_shares_outstanding": 15728700000,
"weighted_shares_outstanding": 15728702000,
"round_lot": 100
},
"status": "OK"
}
Utilizing Function Calling
Now is the part where we describe our function to OpenAI API
def run_conversation(message):
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo-0613",
messages=[{"role": "user", "content": f"Date today is {today_str}. Provide answer with a date. {message}"}],
functions=[
{
"name": "get_stock_price_by_date",
"description": "Get stock prices by date",
"parameters": {
"type": "object",
"properties": {
"ticker_symbol": {
"type": "string",
"description": "The ticker symbol for the stock listed on NASDAQ exchange",
},
"date": {
"type": "string",
"description": "date string in %Y-%m-%d format"
}
},
"required": ["stock_ticker", "date"],
},
},
{
"name": "get_stock_sec_details",
"description": "Get SEC filing details by stock ticker symbol",
"parameters": {
"type": "object",
"properties": {
"ticker_symbol": {
"type": "string",
"description": "The ticker symbol for the stock listed on NASDAQ exchange",
},
"date": {
"type": "string",
"description": "date string in %Y-%m-%d format"
}
},
"required": ["stock_ticker"],
},
}
],
function_call="auto",
)
There are 2 differences between a standard chat completion call and this one.
Model: To use the function calling feature we have to use the latest models
gpt-3.5-turbo-0613
orgpt-4-0613
(GPT 4)Additional Parameters to describe the function :
functions
&function_call
functions
expect an array of the function descriptions, you can provide more functions to this array and the model will try to detect which function is most suitable for the given user inputfunction_call
to indicate whether to generate a model-facing (auto
response (none
a response that will be fed to the model on subsequent call) or a user-facing response that will be presented to the user
Now based on the model's response we'll call the actual function, here you can check which function was found most suitable for the model
Here is how we can do this
## Continuing the `run_conversation` function
internal_message = response["choices"][0]["message"]
if internal_message.get("function_call"):
function_name = internal_message["function_call"]["name"]
function_args = json.loads(internal_message["function_call"]["arguments"])
function_response = ""
# Printing which function is detected
print(function_name)
# If model detected stock price info is required
if function_name == "get_stock_price_by_date":
function_response = get_stock_price_by_date(
ticker_symbol=function_args.get("ticker_symbol"),
date=function_args.get("date"),
)
# If model detected stock SEC filing info is required
if function_name == "get_stock_sec_details":
function_response = get_stock_sec_details(
ticker_symbol=function_args.get("ticker_symbol")
)
second_response = openai.ChatCompletion.create(
model="gpt-3.5-turbo-0613",
messages=[
{"role": "user", "content": message},
internal_message,
{
"role": "function",
"name": function_name,
"content": function_response,
},
],
)
return second_response
One thing to note here is that the function description (objects within the functions
array) will be counted against input tokens
Alright, let's test
print(run_conversation("tell me apple volume 3 days ago")["choices"][0]["message"]["content"])
get_stock_price_by_date
The volume for Apple (AAPL) three days ago, on June 13, 2023, was approximately 54,867,129 shares.
print(run_conversation("how many outstanding google shares are there")["choices"][0]["message"]["content"])
get_stock_sec_details
As of now, there are approximately 5,874,000,000 outstanding Google shares.
So to conclude, by using the function calling feature -
The model can detect which function should be called to fulfill the user's request
It can extract required info from the user's input and provide us with function arguments
You can find the full source code of this article at - https://github.com/anantrp/ai-experiments/blob/main/openai_function_calling_stock_prices_demo.ipynb
You can read more about Function Calling at - https://platform.openai.com/docs/guides/gpt/function-calling
This can go beyond simple retrieval capabilities and we can build actuator functions (functions that can perform actions) such as sending an email, ordering products, taking action on our custom software systems and much more
With Function Calling capability we have great potential to build the AI agents of tomorrow
I hope you find this valuable, if you did - that's awesome 👌 share it with your folks who'll find it useful too. If you have any suggestions/comments please feel free.
Happy coding!
Top comments (2)
If I understand it correctly, I provide multiple function definitions via the OpenAI API and the completion response returns the function that is most likely to be the function that must run in order to answer the question correctly?
So it’s kind of a way to retrieve data from backend systems that are not necessarily publicly available by OpenAI? But in the end my own program executes the function call and returns the data to OpenAI for the final completion?
Yes Chris, that's exactly correct
The usecase goes beyond data retrival. You can perform actions using this.
In this example I've used only one function detection, but you can in theory loop over the return values of these functions and perform complex tasks
For example, rather than asking for data about Apple stocks, you can sell or buy apple stocks right from the chat interface.