In this Python post, I outline some changes to the OpenAI API, and models available, relevant to the first two blogs in this series. I also discuss secure API key management.
Update
A couple of recent changes from OpenAI means that the code presented in blogs 1 and 2 of this series will no longer work.
OpenAI Python library changes
The most important change, introduced in November 2023, is to the Python code required to access the OpenAI API.
First, as OpenAI explains, it’s now necessary to instantiate an API client object, after importing the OpenAI
class from the openai
library. The API key also needs to be passed to this client. The following code achieves this, provided the API key is available as an environment variable called OPENAI_API_KEY
:
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ.get("OPENAI_API_KEY"),
)
I’ll say more about API key management, and storing API keys as environment variables, in the final section below.
Second, API calls are now made using client.chat.completions.create()
. Earlier, for chat completions, I was using openai.ChatCompletion.create()
to query the “gpt-3.5-turbo” and openai.Completion.create()
for the “text-davinci-003” – these are replaced with this new client-object based method.
Finally, the output from the API is a completion object with a similar structure to the output from the API call via the openai.ChatCompletion.create()
method. It is not a dictionary object, however, so values cannot be accessed by providing key-names in square brackets. Instead of response['choices'][0]['message']['content']
, it’s now necessary to use response.choices[0].message.content
.
Other aspects of the API call code are the same as those shown in the classify_sentimentTurbo()
function from Part 1.
To incorporate these changes, classify_sentimentTurbo()
can be rewritten as follows:
def classify_sentimentTurbo(prompt, slice_size=20):
# Initialize list to store predicted sentiments
predicted_sentiments = []
# Extract reviews based on the slice_size
reviews = imdb['train']['review'][:slice_size]
# Iterate over the sliced items
for review in reviews:
# Construct the full prompt
full_prompt = f"{prompt}: {review}\nSentiment:"
# Extract the sentiment label from the output (removing any white space, and ensuring lower case)
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[{'role': 'user', 'content': full_prompt}],
max_tokens=2,
temperature = 0
)
print(response)
# Extract the sentiment label from the output (removing any white space, and ensuring lower case)
predicted_sentiment = response.choices[0].message.content.strip().lower()
# Add the predicted sentiment to the list
predicted_sentiments.append(predicted_sentiment)
# Create a DataFrame from the reviews and predicted sentiment labels
df = pd.DataFrame({
'Review': reviews,
'Sentiment': predicted_sentiments
})
return df
Deprecation of text-davinci-003
The model text-davinci-003
, which had “legacy” status when used in Parts 1 and 2 of this series, was deprecated in January 2024 and is therefore no longer available. OpenAI describes the difference between “legacy” status and “deprecation” as follows:
We use the term “deprecation” to refer to the process of retiring a model or endpoint. When we announce that a model or endpoint is being deprecated, it immediately becomes deprecated. All deprecated models and endpoints will also have a shut down date. At the time of the shut down, the model or endpoint will no longer be accessible.
We use the term “legacy” to refer to models and endpoints that will no longer receive updates. We tag endpoints and models as legacy to signal to developers where we are moving as a platform and that they should likely migrate to newer models or endpoints. You can expect that a legacy model or endpoint will be deprecated at some point in the future.
As demonstrated in Part 2, text-davinci-003
was fairly “quirky” to say the least – in one case changing the sentiment classification of an IMDb movie review due to the presence of the word “either” in a prompt, where this meaning would probably have been inferred from the original prompt. So, it’s perhaps unsurprising that this model has been retired, although this means that my earlier findings are no longer reproducible.
Having said that, this phenomenon is definitely not unique to text-davinci-003
. In a fascinating paper on model evaluation Zheng et al. remark that this issue, which they term “prompt sensitivity”, is observed in many advanced LLMs, is little studied, and “urge the community to place greater emphasis” on researching this issue going forward.1
API key management
There are different ways to store an API key as an environment variable, and some are less secure than others. Using a code chunk in the notebook one is working in to do this (e.g. using os.environ["OPENAI_API_KEY"] = "my-API-key"
) might work, but it could be easy to accidentally share this (e.g. by uploading it to a public repository on GitHub).
A better way, recommended by OpenAI, is to use the python-dotenv library. With this approach, you store your API key in a .env
file in the root of the project you’re working on. The load_dotenv()
function can then be used to read this file, and store the API key as environment variable.
To elaborate, here’s what the content of your .env
file might look like:
OPENAI_API_KEY=sk-abcdefg1234567890
MISTRAL_API_KEY=1234567890abcdefgh
running this code
from dotenv import load_dotenv
load_dotenv()
stores both of these keys as environment variables and makes them accessible via the os.getenv()
function. At this point, for example, print(os.getenv("MISTRAL_API_KEY"))
will print 1234567890abcdefgh
, and the client = OpenAI()
function detailed above will correctly transfer the OpenAI API key to the OpenAI client.
Obviously, if using this approach, it would be wise to ensure that .env
is added to your .gitignore
file if you’re using GitHub.
-
Shen Sheng et al., “GPT-Fathom: Benchmarking Large Language Models to Decipher the Evolutionary Path towards GPT-4 and Beyond”, 2023 (https://arxiv.org/abs/2309.16583v2). ↩︎