Hi and welcome back to part 3! Before we move on let’s take a more in-depth look at the response that the Gemini API returns to us. Go back into the simple_request.py file and change the print statements in the if __name__ == '__main__': block as follows:
if __name__ == "__main__":
query = input("Please ask a question: ")
response = chat_session.send_message(query)
print(f"\033[1;31m Text:\n{response.text}\033[0m")
print(f"\033[1;32m Candidates:\n{response.candidates}\033[0m")
print(f"\033[1;33m Usage metadata:\n{response.usage_metadata}\033[0m")
We’re going to look at the text, candidates, and usage_metadata attributes of the response object here. All the \033[1;31m numbers may look a bit confusing if you’re not familiar with them, but we’re just using them to print color so the output is easy to read. Let’s break it down:
ANSI escape codes are used to colorize the output text in the terminal. Each escape code begins with \033 + [ which is the escape character followed by the open bracket character. This is followed by a sequence of numbers separated by semicolons and ends with the letter m.
The numbers in the sequence represent specific text properties. For instance, 1;31m sets the text to bright red, 1;32m sets the text to bright green, and 1;33m sets the text to bright yellow. The 1 in the sequence stands for bright, and the numbers 31, 32, and 33 represent red, green, and yellow respectively. The escape sequence \033[0m is used to reset the text color back to the default, which is why we use it at the end of each print statement.
Go ahead and run this file and ask a question. I’ll just ask something stupid like “What is a strawberry?” for testing purposes here. You should see colorful text output like this:

We can see that text is just the output answer that we’ve been looking at so far. What is with this candidates thing though? We can see that it has only a single candidate response. I suspect this is a relic of the older versions of Gemini where it would create 3 response outputs and then choose the best one, returning the top response to the end user. You cannot currently ask for more than 1 response from the API, so this is a bit redundant.
We can see that the candidates list has extra info like the role, finish_reason, and that we can also access the safety_ratings, which is the model’s judgment of how safe our question and the response were. In this case, we can see it has rated everything with a probability of NEGLIGIBLE which is the lowest rating.
Finally, in the yellow print block, we have the usage_metadata with the prompt_token_count which is how many tokens our question + system_instructions took up. We also have the candidate_token_count, but as we now know there is only 1 candidate, this is simply the response token count. Finally, we have the total_token_count which goes without saying.
You can use these to programmatically check how ‘dangerous’ your questions are perceived to be or calculate the cost of your requests in real time based on the in- and output token counts.
Adding memory to our chatbot
Now let’s have a look at making this into a proper chat with memory so we can have an actual conversation with Gemini. First, we’ll code up a quick function to get rid of some more repetitive boilerplate code. Create a new file called load_env.py:
π GOOGLE_GEMINI
βοΈ .env
π load_env.py β¨ New file
π simple_request.py
π utils.py
π Pipfile
π Pipfile.lock
And we’ll store the repetitive genai object initialization logic inside:
import os
import google.generativeai as genai
from dotenv import load_dotenv
def configure_genai():
load_dotenv()
genai.configure(api_key=os.environ["GEMINI_API_KEY"])
return genai
This function basically takes the initialization steps from the simple_request.py file and puts them into a function. We also load the .env file here so we can access the GEMINI_API_KEY environment variable. Using the new load_env.py file, we can now simplify our setup in the code.
We’ll leave the simple_request.py file as it is, and get started on a new file instead. Create a new file called simple_chat.py:
π GOOGLE_GEMINI
βοΈ .env
π load_env.py
π simple_chat.py β¨ New file
π simple_request.py
π utils.py
π Pipfile
π Pipfile.lock
In this file we’ll create a chat where we can ask multiple questions and also have Gemini remember the chat history. Open up the simple_chat.py file and let’s get started with the imports:
from load_env import configure_genai from utils import safety_settings genai = configure_genai()
We import our configure_genai and our safety_settings class instance and set up our genai object in a single line of code! Now let’s have some fun. I’m going to give the user a choice of who they want to chat with by asking them two questions:
character = input("What is your favorite movie character? (e.g. Gollum): ")
movie = input("What movie are they from? (e.g. Lord of the Rings): ")
We now have a character and movie of the user’s choice. Let’s define our model just like we did last time:
model = genai.GenerativeModel(
model_name="gemini-1.5-flash",
safety_settings=safety_settings.low,
system_instruction=f"You are helpful and provide good information but you are {character} from {movie}. You will stay in character as {character} no matter what. Make sure you find some way to relate your responses to {character}'s personality or the movie {movie} at least once every response.",
)
You can set the model_name to "gemini-1.5-flash" or "gemini-1.5-pro", whichever you like. As this is a fairly simple request I’ll just use flash for now. We set the safety settings to low, which is extremely easy using the class we made, and then have the system instructions. We’re telling Gemini that it is a character from a movie and it should stay in character, plugging in the name of the character and the movie the user gave us several times.
Turning it into a real conversation
Now let’s define a history list to store our chat history and initialize our chat_session:
history = [] chat_session = model.start_chat(history=history)
Notice that even though this time we will store the chat history we still want to start with an empty history object. Now that we have a chat_session we can send messages to let’s define the chat loop in an if __name__ == "__main__": block at the bottom of the file:
if __name__ == "__main__":
try:
while True:
query = input("\nPlease ask a question or use CTRL+C to exit: ")
response = chat_session.send_message(query)
print(f"\033[1;34m{response.text}\033[0m")
history.append(
{
"role": "user",
"parts": [query],
}
)
history.append(
{
"role": "model",
"parts": [response.text],
}
)
for message in history:
print(f"{message}")
except KeyboardInterrupt:
print("Shutting down...")
Let’s read it from the outside in. We first have a try and except block to catch the KeyboardInterrupt exception when the user presses CTRL+C to exit the chat. Inside the try block we have a while True: loop that will keep running until the user exits the chat. We ask the user for a question and send it to the chat_session to get a response, printing the response in bright blue using the ANSI escape codes we used before.
We now need to update our history with the user’s question and the model’s response. We append a dictionary with the user’s role and the parts of the message to the history list and then do the same for the model’s response. This is just the format that Gemini expects so we stick to it. Finally, we print out the entire chat history after each message so you can see the history list grow as we have a conversation. We can delete this print statement later.
Time to test it out
So go ahead and run the simple_chat.py file. I’m going to tell it I want to chat with Darth Vader from Star Wars:
What is your favorite movie character? (e.g. Gollum): Darth Vader What movie are they from? (e.g. Lord of the
