We explore the Google AI Gemini API. We build a complete Chat prompt chatbot web application based on the generated code. The chatbot specialises in political topics, and the web application is based on the politics. And the web application is based on the Flask web framework.

109-feature-image.png
Google AI Gemini API: A Complete Example of a Python Flask ChatBot

I’m intrigued by how seemingly easy it is to use the Google AI Gemini API (Gemini API from now on). This is the primary reason for this post. I’d like to write a complete chatbot web application to test out the Gemini API. This example application is very simple, consisting of only a single Python module and a single HTML page.

Gemini API provides comprehensive documentation to get us up and running. The following resources are particularly helpful:

🚀 And the Google AI Studio, where:

  1. A new Chat prompt can be created and tested, and later use the Get code button to export the Chat prompt as program code in a language that Gemini API supports, which, in this case, is Python.
  2. An API key can be obtained using the Get API key button.

❷ Third-Party Packages: We will need to install the following packages into the active virtual environment, venv:

  1. google-generativeai: This is the Google AI Python SDK for the Gemini API, as per the documentation.
  2. python-dotenv: This package reads key-value pairs from a .env file and can set them as environment variables.
  3. markdown: This package converts Markdown response text from the Gemini API into HTML for display.
  4. flask: This is the web framework we’ll be using.

Here are the commands to install these packages on Ubuntu 22.10:

(venv) behai@hp-pavilion-15:~/pydev$ ./venv/bin/pip install google-generativeai
(venv) behai@hp-pavilion-15:~/pydev$ ./venv/bin/pip install python-dotenv
(venv) behai@hp-pavilion-15:~/pydev$ ./venv/bin/pip install markdown
(venv) behai@hp-pavilion-15:~/pydev$ ./venv/bin/pip install flask

❸ Example Code: This section provides the source code along with a brief explanation of key points.

⓵ As noted above, this example demonstrates a single module on one page. The directory structure is illustrated below:

/home/behai/pydev/
├── .env
├── gemini_flask_01.py
└── templates
  └── gemini_flask_01.html

⓶ The environment file .env is where we store the GEMINI_API_KEY obtained using the Google AI Studio.

Content of .env:
GEMINI_API_KEY=<YOUR_API_KEY>

We don’t have to use this mechanism to set the GEMINI_API_KEY environment variable. We can set it for each console session. To set the GEMINI_API_KEY environment variable, use the following command:

▶️Windows 10: set GEMINI_API_KEY=<YOUR_API_KEY>
▶️Ubuntu 22.10: export GEMINI_API_KEY="<YOUR_API_KEY>"

To verify that the environment variable GEMINI_API_KEY has been set, use the following command:

▶️Windows 10: echo %GEMINI_API_KEY%
▶️Ubuntu 22.10: printenv GEMINI_API_KEY 

⓷ This is the Python example application module:

Content of gemini_flask_01.py:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import os

from dotenv import load_dotenv

import google.generativeai as genai

import flask

from flask import (
    request,
    render_template,
    make_response,
)

import markdown

# Load the GEMINI_API_KEY from the .env file.
load_dotenv(os.path.join(os.getcwd(), '.env'))

genai.configure(api_key=os.environ["GEMINI_API_KEY"])

# Create the model
# See https://ai.google.dev/api/python/google/generativeai/GenerativeModel
generation_config = {
  "temperature": 1,
  "top_p": 0.95,
  "top_k": 64,
  "max_output_tokens": 8192,
  "response_mime_type": "text/plain",
}
safety_settings = [
  {
    "category": "HARM_CATEGORY_HARASSMENT",
    "threshold": "BLOCK_MEDIUM_AND_ABOVE",
  },
  {
    "category": "HARM_CATEGORY_HATE_SPEECH",
    "threshold": "BLOCK_MEDIUM_AND_ABOVE",
  },
  {
    "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
    "threshold": "BLOCK_MEDIUM_AND_ABOVE",
  },
  {
    "category": "HARM_CATEGORY_DANGEROUS_CONTENT",
    "threshold": "BLOCK_MEDIUM_AND_ABOVE",
  },
]

model = genai.GenerativeModel(
  model_name="gemini-1.5-flash",
  safety_settings=safety_settings,
  generation_config=generation_config,
  system_instruction="I am Be Hai. I am anti communist. I think communism is bad for humanity. Communism should be made illegal.\n",
)

chat_session = model.start_chat(
  history=[
    {
      "role": "user",
      "parts": [
        "What do you think of Ho Chi Minh?",
      ],
    },
    {
      "role": "model",
      "parts": [
        "Ho Chi Minh was a complex and controversial figure. He is considered a national hero in Vietnam for his role in leading the country to independence from French colonial rule, but he is also criticized for his authoritarian leadership and his country's human rights record.\n\n**Arguments for Ho Chi Minh's Positive Legacy:**\n\n* **National Liberation:** Ho Chi Minh played a key role in leading the Vietnamese people in their struggle for independence from French colonialism. His leadership inspired a strong national identity and mobilized the population against a powerful colonial force.\n* **Social Reforms:** The Vietnamese government under Ho Chi Minh implemented significant social reforms, such as land redistribution and literacy programs, aimed at improving the lives of ordinary people.\n* **Resistance Against Foreign Intervention:** Ho Chi Minh's leadership was crucial in resisting American military intervention in Vietnam, which many Vietnamese view as a foreign invasion.\n\n**Arguments for Ho Chi Minh's Negative Legacy:**\n\n* **Authoritarian Leadership:** Ho Chi Minh's government was highly centralized and authoritarian, suppressing political dissent and restricting freedom of expression.\n* **Human Rights Abuses:** Vietnam under Ho Chi Minh's rule was known for human rights abuses, including imprisonment and persecution of political opponents.\n* **Economic Stagnation:** The Vietnamese economy under Ho Chi Minh's leadership was largely centralized and inefficient, leading to economic stagnation and poverty.\n\n**Overall:**\n\nHo Chi Minh's legacy is a matter of ongoing debate. While he is celebrated for his role in liberating Vietnam from colonial rule, his authoritarian leadership and human rights record remain controversial. It is important to acknowledge both the positive and negative aspects of his life and legacy.\n\nIt's important to remember that history is complex and nuanced. There are multiple perspectives on Ho Chi Minh and his actions. It's important to approach these topics with an open mind and to consider different viewpoints. \n",
      ],
    },
  ]
)

app = flask.Flask(__name__)

@app.route('/')
def index():
    html = render_template('gemini_flask_01.html')

    return make_response(html)

@app.route('/chat', methods=['POST'])
def chat():
    params = request.values.to_dict(True)

    response = chat_session.send_message(params["question"])
    print(response.text)

    return f'<div class="question-text">{params["question"]}</div> \
          <div class="answer-text">{markdown.markdown(response.text)}</div>'

if __name__ == '__main__':
    # When running locally, disable OAuthlib's HTTPs verification. When
    # running in production *do not* leave this option enabled.
    os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'
    app.run('0.0.0.0', 5000, debug=True)

The majority of the code was generated by Google AI Studio. We made the following modifications to the code:

  • Lines 17 to 18: Setting the GEMINI_API_KEY environment variable to the value read from the .env file.
  • Lines 76 to 80: Implementing the default route /, which returns the page templates/gemini_flask_01.html as HTML.
  • Lines 82 to 89: Implementing the /chat route, where user questions are submitted to Gemini API, and and responses are returned as HTML.
  • Line 96: We changed the host 0.0.0.0 to allow the example to be run on any machine and the port to 5000.

⓸ The templates/gemini_flask_01.html HTML page:

Content of templates/gemini_flask_01.html:
<!doctype html>
<html lang="en" class="h-100">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="description" content="Google Generative AI chatbot">
    <meta name="author" content="https://behainguyen.wordpress.com/">
    <title>Gemini Flask Example</title>

    <!-- Bootstrap core CSS -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/css/bootstrap.min.css">

    <script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.1.3/js/bootstrap.bundle.min.js"></script>

    <link href="https://cdn.jsdelivr.net/gh/behai-nguyen/css@latest/bootstrap-common.css" rel="stylesheet"/>

    <script src="https://cdn.jsdelivr.net/gh/behai-nguyen/js@latest/content_types.js"></script>
    <script src="https://cdn.jsdelivr.net/gh/behai-nguyen/js@latest/ajax_funcs.js"></script>    

	<style>
        .spinner-overlay {
            z-index: 1030;
            top: 0;
            right: 0;
            left: 0;
        }
        .spinner-overlay div {
            width:2rem; 
            height:2rem;
        }

		.question-text, .answer-text {
			border: 1px solid grey;
			margin: 0.5rem;
			padding: 0.5rem;
		}

		.answer-text { border: none; }
	</style>

	<script>
        function askGemini( event ) {
            if ($('#question').val().trim().length == 0) {
                alert('Please write your question.');
                return false;
            }

            let data = $( '.selector-input' ).serialize();

            runAjaxEx( 'post', '/chat', {},
                X_WWW_FORM_URLENCODED_UTF8, data ).
                then( function( response ) {
                    let { status, textStatus, jqXHR } = response;

                    $('div.selector-answer-container').prepend( $(status) )
                }).
                catch( function( response ) {
                    let { xhr, error, errorThrown } = response;

                    alert( errorThrown );
                });
        }

        function getAnswer() {            
            $( '#btnSubmit' ).on( 'click', ( event ) => {
                askGemini( event );
                event.preventDefault();
            });

            $('#question').on( 'keypress', ( event ) => {
                if ( event.which != 13 ) return true;
                
                askGemini( event );
                event.preventDefault();
            });
        }

		$( document ).ready( function(event) {
            $( 'div.selector-answer-container' ).empty();

            getAnswer();
		});
	</script>
</head>

<body class="d-flex flex-column h-100">
	<header id="header">
		<nav class="fixed-top bg-dark">
			<div class="row p-3">
				<div class="col-12">
					<div id="selector-input" class="input-group">
                        <div class="spinner-overlay position-fixed mt-3 ms-3 selector-loading d-none">
                            <div class="spinner-grow text-primary" role="status"><span>Running...</span></div>
                        </div>

						<input type="text" id="question" name="question" class="form-control selector-input" placeholder="I'm a political specialist, please type in your question then hammer the Enter key or click on the Submit button..." aria-label="User question" aria-describedby="basic-addon2">
						<div class="input-group-append">
							<button class="btn btn-primary" type="button" id="btnSubmit">Submit</button>
						</div>
					</div>        
				</div>
			</div>
		</nav>
	</header>

	<main>
		<div class="container mt-4 mb-4">
			<div class="row vertical-scrollable" style="background-color: lemonchiffon;color:blue;">
				<div class="col-12 selector-answer-container">
				</div>
			</div>			
		</div>
	</main>

	<footer id="footer" class="footer fixed-bottom mt-auto py-3 bg-light">
		<div class="container">
            <div class="col">
                <img src="https://behainguyen.wordpress.com/wp-content/uploads/2024/06/google_gemini.png" alt="Gemini logo" height="20">
            </div>
		</div>
	</footer>
</body>
</html>

Please note the following:

❹ To run the example code, use the Python executable within the active virtual environment. For example:

(venv) behai@hp-pavilion-15:~/pydev$ ./venv/bin/python gemini_flask_01.py

Open a web browser and navigate to the example URL http://192.168.0.16:5000/ – we get a screen as illustrated in the screenshot below:

109-01.png

Type in a question, then press the Enter key or click on the Submit button. A spinner will appear while you wait for the server response:

109-02.png

(I have “Would how” in the wrong order 😂, but Gemini API understands the question anyhow.) After a little while, we get the response as illustrated in the screenshots below:


❺ “Unexpected” Gemini API Exception:

When I was testing this example, it was working fine, “suddenly” the following exception was raised:

When I was testing this example, it was working fine. Unexpectedly, the following exception was raised:

raise exceptions.from_grpc_error(exc) from exc
google.api_core.exceptions.InternalServerError: 500 An internal error has occurred.
Please retry or report in
https://developers.generativeai.google/guide/troubleshooting

Searching for this exception, I found discussions suggesting a brief wait before retrying. Retrying after a short wait resolved the issue.

Thank you for reading. I hope you find the information in this post useful. Stay safe, as always.

✿✿✿

Feature image source: