Antenna — Beautiful, Text-Vision Chat with Gemini Pro — Production ready

#1 of 101 — Awesome GenAI application with Tushar Aggarwal

Image generated & edited by author

Lets Starts

Alright, imagine GeminiPro as the superhero of AI models, developed by Google. It’s like the Tony Stark of generative AI, capable of understanding and generating text, images, and even videos. The GeminiPro API is your gateway to harnessing this power. You can use it to create applications that need to generate content, answer questions, recognize objects, and much more. Think of it as your trusty sidekick that helps you build cool stuff without breaking a sweat.

Here’s a quick peek at what you can do with GeminiPro:

Text Generation: Create responses, summaries, or even code snippets.Image and Video Analysis: Recognize objects, generate captions, and understand content.Multimodal Prompts: Combine text, images, and videos in your prompts for richer interactions.

To get started, you need to grab an API key from Google Cloud, and then you can start making requests to the GeminiPro API. It’s like getting your superhero license!

Get your still free API here : https://aistudio.google.com/app/apikey

What is the difference between Production-ready and Non-production-ready LLM Chatbots?

Ah, the age-old question! Think of it like the difference between a prototype and a finished product. 🛠️

Non-production-ready LLM Chatbots: These are like your weekend DIY projects. They work, but they might be a bit rough around the edges. They can handle basic tasks, but they might not be reliable enough for prime time. Expect occasional hiccups, like forgetting the context of a conversation or giving you a response that makes you go, “Huh?”Production-ready LLM Chatbots: These are the polished, professional versions. They’ve been through rigorous testing, fine-tuning, and optimization. They handle user interactions smoothly, maintain context like a pro, and are robust enough to handle real-world usage. They’re like the difference between a homemade go-kart and a Tesla. 🚗In short, production-ready chatbots are reliable, scalable, and ready to impress your users, with units tests while non-production-ready ones are great for experimentation and learning.

What is Antenna?

Antenna is a beautiful, simple, multimodal assistant that combines text generation with memory and image chat capabilities, all powered by the mighty Gemini Pro (text and visual) engine. And guess what? Every single function and workflow is unit tested to perfection. Need I say more?

This gem is brought to you by the Towards GenAI community, founded by yours truly, Tushar Aggarwal.

Join our awesome community and let’s build the future together!

Towards GenAI

LinkedIn

Youtube Video coming up soon:

Now lets start developing Step by Step —

I used these below code to test in ./tests/Home_test.py

#Import dependencies
import os
import google.generativeai as genai
from dotenv import load_dotenv
import google.generativeai as genai
import logging
from PIL import Image
import streamlit as st

Setting Logging and .env

# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

##################################################################################################
#Environmental variables
load_dotenv()
google_api_key = os.getenv(“GOOGLE_API_KEY”) #For developer testing

Checking if api key loaded successfully with logging info

#Checking if api key loaded successfully with logging info
if google_api_key:
logger.info(“Google API Key loaded successfully.”)
else:
logger.error(“Failed to load Google API Key.”)

Testing Gemini Pro Text Generation with logging info

def model_load_test():
try:
model = genai.GenerativeModel(‘gemini-pro’)
logging.info(“Generative model loaded: %s”, model)

question = input(“Enter the question: “)
logging.info(“User entered question: %s”, question)

if question:
response = model.generate_content(question)
logging.info(“Response generated”)

print(response.text)
logging.info(“Response text printed”)
else:
logging.warning(“No question entered”)
print(“Please enter a valid question.”)

except Exception as e:
logging.error(“An error occurred: %s”, str(e))

model_load_test()

Testing Gemini Pro vision Generation with logging info

def vision_model_load_test():
try:
img = Image.open(‘.src/tushar.png’)
logging.info(“Image loaded successfully: %s”, img.filename)

if img:
vision_model = genai.GenerativeModel(‘gemini-pro-vision’)
logging.info(“Generative model loaded: %s”, vision_model)

prompt = “Describe image & What is the color of this image and the color of the text?”
logging.info(“Prompt: %s”, prompt)

response = vision_model.generate_content([prompt, img])
logging.info(“Response generated”)

print(response.text)
logging.info(“Response text printed”)
else:
logging.warning(“Invalid image”)
print(“Please enter a valid question.”)

except FileNotFoundError as e:
logging.error(“Image file not found: %s”, str(e))
except Exception as e:
logging.error(“An error occurred: %s”, str(e))

vision_model_load_test()

Testing Gemini Pro text with chain with logging info

def text_chain_test():
try:
chain_model = genai.GenerativeModel(‘gemini-pro’)
chat = chain_model.start_chat(history=[])
logging.info(“Generative model and chat initialized successfully”)

question1 = input(“Enter the question: “)
logging.info(f”User entered question: {question1}”)
response = chat.send_message(question1, stream=True)
response.resolve()
print(response.text)
logging.info(f”Response generated for question1: {response.text}”)

# Question2
question2 = input(“Enter the additional question here: “)
logging.info(f”User entered additional question: {question2}”)
response = chat.send_message(question2, stream=True)
response.resolve()
print(response.text)
logging.info(f”Response generated for question2: {response.text}”)

# print(chat.history)
return True

except Exception as e:
logging.error(f”An error occurred: {str(e)}”)
return False

text_chain_test()

Testing Gemini Pro text with chain with logging info & Configuring the parameters

def text_chain_test_with_config():
try:
chain_model = genai.GenerativeModel(‘gemini-pro’)
chat = chain_model.start_chat(history=[])
logging.info(“Generative model and chat initialized successfully”)

question1 = input(“Enter the question: “)
logging.info(f”User entered question: {question1}”)
generation_config = genai.types.GenerationConfig(max_output_tokens=25, temperature=0.8)
response = chat.send_message(question1, stream=True, generation_config=generation_config)
response.resolve()
print(response.text)
logging.info(f”Response generated for question1: {response.text}”)

# Question2
question2 = input(“Enter the additional question here: “)
logging.info(f”User entered additional question: {question2}”)
generation_config = genai.types.GenerationConfig(max_output_tokens=251, temperature=0.8)
response = chat.send_message(question2, stream=True, generation_config=generation_config)
response.resolve()
print(response.text)
logging.info(f”Response generated for question2: {response.text}”)

# print(chat.history)
return True

except Exception as e:
logging.error(f”An error occurred: {str(e)}”)
return False

text_chain_test_with_config()

Now that we have tested all function calling, let start building Streamlit application.

Now lets start developing beautiful Streamlit application

FYI, if new to Streamlit checkout my comprehensive application here, share on platforms to help someone else too:

https://medium.com/media/5dc7197faada00345e2f714760005d1c/href

Here is the file structure I usually use for Antenna Streamlit Application

Antenna/
├── src/
│ ├── components/
│ │ ├── __init__.py
│ │ ├── navigation.py
│ ├── style/
│ │ ├── __init__.py
│ │ ├── custom_style.css
| ├── components/
│ │ ├── __init__.py
│ │ ├── navigation.py
│ ├── logo.png
│ │
├── tests/
│ ├── __init__.py
│ ├── Main_tests.py
├── .streamlit/
│ ├── config.toml
├── requirements.txt
├── README.md
├── test.env
├── LICENSE
├── Main.py
└── .gitignore

Let see code of each of file and some explanations:

For ./requirements.txtlangchain
pydantic
openai
google-generativeai
pillow
streamlit
python-dotenv
google.generativeai

2. For ./.streamlit/config.toml

[client]showSidebarNavigation = false
[server]runOnSave = true
[theme]textColor = “#042940”

3. For ./.env (# optional, for developing tests)

GOOGLE_API_KEY=”Your_Gemini_Key”

Now lets start with ./main.py

##© 2024 Tushar Aggarwal. All rights reserved.(https://tushar-aggarwal.com)
##Antenna[Towards-GenAI] (https://github.com/Towards-GenAI)
##################################################################################################
#Importing dependencies
import os
import google.generativeai as genai
from dotenv import load_dotenv
import logging
from PIL import Image
import sys
from pathlib import Path
script_dir = Path(__file__).resolve().parent
project_root = script_dir.parent
sys.path.append(str(project_root))
import warnings
warnings.filterwarnings(“ignore”)
import streamlit as st
import json
#From src
from src.components.navigation import *
##################################################################################################

First things first, we need to import all the cool stuff we’re going to use in our script. Think of this as gathering all your superhero gadgets before heading out to save the world.

os: This module helps us interact with the operating system. It’s like our backstage pass to the system.google.generativeai as genai: This is our gateway to the Gemini Pro API, the brain behind our AI magic.load_dotenv: From the dotenv package, this helps us load environment variables from a .env file. Think of it as our secret vault for sensitive info.logging: For keeping track of what’s happening in our script. It’s like our personal diary, but for code.PIL.Image: This is for handling images. We can open, manipulate, and save images with this.sys: Provides access to some variables used or maintained by the interpreter and to functions that interact strongly with the interpreter.pathlib.Path: A modern way to handle file paths. It’s like our GPS for navigating the file system.warnings: This module lets us control warning messages. We’re telling it to ignore all warnings because we’re brave like that.streamlit as st: Streamlit is our tool for creating interactive web apps. It’s like turning our script into a live show!json: For handling JSON data. It’s like our translator for data exchange.

Setting Up Paths

Next, we set up some paths to make sure our script knows where it’s running from and where to find other files. It’s like setting up our base of operations.

script_dir = Path(__file__).resolve().parent
project_root = script_dir.parent
sys.path.append(str(project_root))script_dir: This gets the directory where our script is located.project_root: This gets the parent directory of our script directory, which is usually the root of our project.sys.path.append(str(project_root)): This adds our project root to the system path, so we can import modules from there. It’s like adding a new shortcut to our map.

Ignoring Warnings

We’re telling Python to ignore all warnings because we’re confident our code is flawless. Or maybe we just don’t want to see those pesky messages.

warnings.filterwarnings(“ignore”)

Importing from src

Finally, we import some custom components from our src directory. These are like our custom-built gadgets for the mission. 🛠️

#From src
from src.components.navigation import *from src.components.navigation import *: This imports everything from the navigation module in the src.components package. It’s like saying, “Bring me all the tools from the navigation toolbox!”

Configuring Logging

First up, we’re setting up logging. Think of this as installing security cameras in our codebase to keep an eye on what’s happening

##################################################################################################
# Configure logging
logging.basicConfig(level=logging.INFO, format=’%(asctime)s – %(levelname)s – %(message)s’)
logger = logging.getLogger(__name__)

##################################################################################################
#Environmental variables
# load_dotenv()

# google_api_key = os.getenv(“GOOGLE_API_KEY”)
##################################################################################################logging.basicConfig(…): This configures the logging system. We’re setting the logging level to INFO, which means we’ll get all the important messages but skip the boring debug stuff. The format specifies how our log messages will look, including the timestamp, the severity level, and the actual message. It’s like setting up a fancy template for our logs.logger = logging.getLogger(__name__): This creates a logger object for our script. We can use this logger to log messages throughout our code. It’s like having a dedicated scribe to note down everything important.

Environmental Variables

Next, we’re dealing with environmental variables. These are like secret codes that our script needs to function properly.

load_dotenv(): This function loads environment variables from a .env file. It’s currently commented out, but when active, it’s like opening our secret vault to access the keys.google_api_key = os.getenv(“GOOGLE_API_KEY”): This line fetches the GOOGLE_API_KEY from the environment variables. It’s also commented out, but when active, it’s like retrieving our secret API key to unlock the powers of Google’s services.

Chat History Management

Alright, now we’re diving into the world of chat history. Think of this as our chatbot’s memory bank, where it stores all the conversations it has had.

Loading Chat History

First up, we have a function to load the chat history from a file. This is like our chatbot waking up and remembering all the chats it had before.

##################################################################################################

# Load chat history from file if it exists
def load_chat_history():
if os.path.exists(“chat_history.json”):
with open(“chat_history.json”, “r”) as f:
return json.load(f)
return []

# Save chat history to file
def save_chat_history(chat_history):
with open(“chat_history.json”, “w”) as f:
json.dump(chat_history, f)
##################################################################################################if os.path.exists(“chat_history.json”):: This checks if the chat_history.json file exists. It’s like peeking into the fridge to see if there’s any leftover pizza.with open(“chat_history.json”, “r”) as f:: If the file exists, we open it in read mode. It’s like opening that pizza box.return json.load(f): We load the chat history from the file using json.load(). It’s like taking out the pizza and getting ready to eat.return []: If the file doesn’t exist, we return an empty list. It’s like saying, “No pizza today, let’s order a new one.”

Saving Chat History

Next, we have a function to save the chat history to a file. This is like our chatbot writing down everything it talked about before going to sleep.

with open(“chat_history.json”, “w”) as f:: We open the chat_history.json file in write mode. If the file doesn’t exist, it will be created. It’s like getting a new notebook to jot down our thoughts.json.dump(chat_history, f): We save the chat history to the file using json.dump(). It’s like writing down all the juicy details of our day.

Antenna Application Flow

Alright, now we’re setting up the main flow of our Antenna application. Think of this as rolling out the red carpet for our users.

Page Configuration

First, we configure the page settings. This is like setting up the stage for our grand performance.

##################################################################################################
# Antenna Application flow
page_config(“Antenna”, “♊”, “wide”)
custom_style()

st.title(“♊Antenna♊”)
st.markdown(”’
<style>
div.block-container{padding-top:0px;}
font-family: ‘Roboto’, sans-serif; /* Add Roboto font */
color: blue; /* Make the text blue */
</style>
”’,
unsafe_allow_html=True)
st.markdown(
“””
### A Text & Image Chatbot by [Towards-GenAI](https://github.com/Towards-GenAI)
“””
)page_config(“Antenna”, “♊”, “wide”): This function sets the page title to “Antenna”, uses the Gemini symbol (♊) as the favicon, and sets the layout to “wide”. It’s like giving our app a stylish header and a spacious layout.custom_style(): This function applies custom styles to our app. It’s like adding some flair to our stage setup.

Setting Up the Title and Styles

Next, we set up the title and some custom styles for our app. This is like putting up the marquee and decorating the stage.

st.title(“♊Antenna♊”): This sets the title of our app to “♊Antenna♊”. It’s like putting up a big, bold sign that says, “Welcome to Antenna!”st.markdown(…): This block of code injects some custom CSS styles into our app. We’re setting the padding, changing the font to Roboto, and making the text blue. It’s like giving our app a fresh, modern look.

Adding a Markdown Section

We add a markdown section to introduce our chatbot. This is like giving a brief introduction to our audience about what the show is all about.

st.markdown(…): This adds a markdown section with a heading that introduces our chatbot and provides a link to the Towards-GenAI GitHub page. It’s like saying, “Hey, check out this awesome chatbot we built!”

Now Main() function:

#Whole code
def main():

st.sidebar.image(‘./src/logo.png’)
with st.sidebar.expander(“Google API Key please”):
google_api_key = st.text_input(“Google API Key”, key=”google_api_key”, type=”password”)

if google_api_key:
logger.info(“Google API Key loaded successfully.”)
else:
st.info(“Enter the Google API Key to continue”)
st.stop()

genai.configure(api_key=google_api_key)

# Model selector
with st.sidebar:
option = st.selectbox(‘Model’, (‘gemini-pro’, ‘gemini-pro-vision’))

if ‘model’ not in st.session_state or st.session_state.model != option:
st.session_state.chat = genai.GenerativeModel(option).start_chat(history=[])
st.session_state.model = option

st.write(“Adjust Parameters Here:”)
temperature = st.number_input(“Temperature”, min_value=0.0, max_value=1.0, value=0.5, step=0.01)
max_token = st.number_input(“Maximum Output Token”, min_value=0, value=251, max_value=1500)
gen_config = genai.types.GenerationConfig(max_output_tokens=max_token, temperature=temperature)

st.divider()

if st.button(“Clear Chat History”):
st.session_state.messages.clear()
save_chat_history([])

st.divider()

upload_image = st.file_uploader(“Upload Your Image Here”, accept_multiple_files=False, type=[‘jpg’, ‘png’])

if upload_image:
image = Image.open(upload_image)

st.divider()

footer()

Lets dive into each step

Main Function

Alright, now we’re diving into the heart of our Antenna application with the main() function. This is where all the magic happens!

Sidebar Setup

First, we set up the sidebar. Think of this as the control panel for our app, where users can input their Google API key and select models.

def main():
st.sidebar.image(‘./src/logo.png’)
with st.sidebar.expander(“Google API Key please”):
google_api_key = st.text_input(“Google API Key”, key=”google_api_key”, type=”password”)st.sidebar.image(‘./src/logo.png’): This displays our logo in the sidebar. It’s like putting up our brand’s flag.with st.sidebar.expander(“Google API Key please”):: This creates an expandable section in the sidebar for entering the Google API key. It’s like a secret compartment for our key.google_api_key = st.text_input(“Google API Key”, key=”google_api_key”, type=”password”): This creates a text input field for the Google API key, masked as a password. It’s like entering the secret code to unlock the vault.

API Key Handling

Next, we handle the Google API key. If the key is provided, we proceed; otherwise, we prompt the user to enter it. It’s like checking if we have the key before opening the door.

if google_api_key:
logger.info(“Google API Key loaded successfully.”)
else:
st.info(“Enter the Google API Key to continue”)
st.stop()if google_api_key:: Checks if the API key is provided.logger.info(“Google API Key loaded successfully.”): Logs a success message if the key is provided. It’s like saying, “Key accepted, welcome aboard!”st.info(“Enter the Google API Key to continue”): Displays an info message prompting the user to enter the key.st.stop(): Stops the script if the key is not provided. It’s like saying, “No key, no entry!”

Configuring the API

We configure the Gemini Pro API with the provided key. It’s like powering up our AI engine.

genai.configure(api_key=google_api_key)

Model Selector

Next, we set up the model selector in the sidebar. This allows users to choose between different models. It’s like picking your favorite superhero suit.

with st.sidebar:
option = st.selectbox(‘Model’, (‘gemini-pro’, ‘gemini-pro-vision’))
if ‘model’ not in st.session_state or st.session_state.model != option:
st.session_state.chat = genai.GenerativeModel(option).start_chat(history=[])
st.session_state.model = optionoption = st.selectbox(‘Model’, (‘gemini-pro’, ‘gemini-pro-vision’)): Creates a dropdown to select the model.if ‘model’ not in st.session_state or st.session_state.model != option:: Checks if the selected model is different from the current one.st.session_state.chat = genai.GenerativeModel(option).start_chat(history=[]): Starts a new chat session with the selected model.st.session_state.model = option: Updates the session state with the selected model.

Adjusting Parameters

We provide options to adjust parameters like temperature and maximum output tokens. It’s like fine-tuning our superhero suit for optimal performance.

st.write(“Adjust Parameters Here:”)
temperature = st.number_input(“Temperature”, min_value=0.0, max_value=1.0, value=0.5, step=0.01)
max_token = st.number_input(“Maximum Output Token”, min_value=0, value=251, max_value=1500)
gen_config = genai.types.GenerationConfig(max_output_tokens=max_token, temperature=temperature)st.write(“Adjust Parameters Here:”): Adds a section header for parameter adjustments.temperature = st.number_input(“Temperature”, min_value=0.0, max_value=1.0, value=0.5, step=0.01): Creates a number input for temperature.max_token = st.number_input(“Maximum Output Token”, min_value=0, value=251, max_value=1500): Creates a number input for maximum output tokens.gen_config = genai.types.GenerationConfig(max_output_tokens=max_token, temperature=temperature): Configures the generation settings.

Clearing Chat History

We add a button to clear the chat history. It’s like hitting the reset button to start fresh.

st.divider()
if st.button(“Clear Chat History”):
st.session_state.messages.clear()
save_chat_history([])st.divider(): Adds a visual divider.if st.button(“Clear Chat History”):: Checks if the clear chat history button is clicked.st.session_state.messages.clear(): Clears the chat history in the session state.save_chat_history([]): Saves an empty chat history to the file.

Image Upload

We provide an option to upload an image. It’s like adding a visual element to our chat. 📸

st.divider()

upload_image = st.file_uploader(“Upload Your Image Here”, accept_multiple_files=False, type=[‘jpg’, ‘png’])

if upload_image:
image = Image.open(upload_image)st.divider(): Adds a visual divider.upload_image = st.file_uploader(“Upload Your Image Here”, accept_multiple_files=False, type=[‘jpg’, ‘png’]): Creates a file uploader for images.if upload_image:: Checks if an image is uploaded.image = Image.open(upload_image): Opens the uploaded image.

Footer

Finally, we add a footer to our app. It’s like signing off with a flourish.

st.divider()

footer()st.divider(): Adds a visual divider.footer(): Calls the footer function to display the footer.

Now Chat History and session

#inside def main(): continue
# Load chat history
if “messages” not in st.session_state:
st.session_state[“messages”] = load_chat_history()

# Display chat messages
for msg in st.session_state.messages:
st.chat_message(msg[“role”]).write(msg[“content”])

# Handle user input
if upload_image:
if option == “gemini-pro”:
st.info(“Please switch to the Gemini Pro Vision model to use image input.”)
st.stop()
prompt = st.chat_input(placeholder=”I’m Antenna, what’s on your mind?”)
if prompt:
st.session_state.messages.append({“role”: “user”, “content”: prompt})
st.chat_message(“user”).write(prompt)
response = st.session_state.chat.send_message([prompt, image], stream=True, generation_config=gen_config)
response.resolve()
msg = response.text

st.session_state.chat = genai.GenerativeModel(option).start_chat(history=[])
st.session_state.messages.append({“role”: “assistant”, “content”: msg})

st.image(image, width=300)
st.chat_message(“assistant”).write(msg)
else:
prompt = st.chat_input(placeholder=”I’m Antenna, what’s on your mind?”)
if prompt:
st.session_state.messages.append({“role”: “user”, “content”: prompt})
st.chat_message(“user”).write(prompt)

response = st.session_state.chat.send_message(prompt, stream=True, generation_config=gen_config)
response.resolve()
msg = response.text
st.session_state.messages.append({“role”: “assistant”, “content”: msg})
st.chat_message(“assistant”).write(msg)

# Saving chat history
save_chat_history(st.session_state.messages)

if __name__ == “__main__”:
main()

Lets dive into each step

Chat History and User Interaction

Alright, now we’re getting into the nitty-gritty of handling chat history and user input. This is where our chatbot really comes to life!

Loading Chat History

First, we load the chat history. This is like our chatbot waking up and remembering all the conversations it had before.

if “messages” not in st.session_state:
st.session_state[“messages”] = load_chat_history()if “messages” not in st.session_state:: Checks if the chat messages are already in the session state.st.session_state[“messages”] = load_chat_history(): If not, it loads the chat history from the file. It’s like saying, “Hey, remember all those chats we had?”

Displaying Chat Messages

Next, we display the chat messages. This is like putting up a replay of all the previous conversations.

for msg in st.session_state.messages:
st.chat_message(msg[“role”]).write(msg[“content”])for msg in st.session_state.messages:: Loops through each message in the chat history.st.chat_message(msg[“role”]).write(msg[“content”]): Displays the message based on its role (user or assistant). It’s like showing who said what in the chat.

Handling User Input

Now, we handle user input. This is where the real-time interaction happens.

With Image Upload

If an image is uploaded, we handle it differently. It’s like adding a visual element to our chat.

if upload_image:
if option == “gemini-pro”:
st.info(“Please switch to the Gemini Pro Vision model to use image input.”)
st.stop()
prompt = st.chat_input(placeholder=”I’m Antenna, what’s on your mind?”)
if prompt:
st.session_state.messages.append({“role”: “user”, “content”: prompt})
st.chat_message(“user”).write(prompt)
response = st.session_state.chat.send_message([prompt, image], stream=True, generation_config=gen_config)
response.resolve()
msg = response.text
st.session_state.chat = genai.GenerativeModel(option).start_chat(history=[])
st.session_state.messages.append({“role”: “assistant”, “content”: msg})

st.image(image, width=300)
st.chat_message(“assistant”).write(msg)if upload_image:: Checks if an image is uploaded.if option == “gemini-pro”:: If the selected model is not suitable for image input, it prompts the user to switch models.prompt = st.chat_input(placeholder=”I’m Antenna, what’s on your mind?”): Creates an input field for the user’s text prompt.if prompt:: If the user enters a prompt:st.session_state.messages.append({“role”: “user”, “content”: prompt}): Adds the user’s message to the chat history.st.chat_message(“user”).write(prompt): Displays the user’s message.response = st.session_state.chat.send_message([prompt, image], stream=True, generation_config=gen_config): Sends the user’s message and the image to the chatbot.response.resolve(): Resolves the response.msg = response.text: Gets the response text.st.session_state.chat = genai.GenerativeModel(option).start_chat(history=[]): Starts a new chat session.st.session_state.messages.append({“role”: “assistant”, “content”: msg}): Adds the assistant’s message to the chat history.st.image(image, width=300): Displays the uploaded image.st.chat_message(“assistant”).write(msg): Displays the assistant’s message.

Without Image Upload

If no image is uploaded, we handle the text input normally. It’s like a regular chat session.

else:
prompt = st.chat_input(placeholder=”I’m Antenna, what’s on your mind?”)
if prompt:
st.session_state.messages.append({“role”: “user”, “content”: prompt})
st.chat_message(“user”).write(prompt)

response = st.session_state.chat.send_message(prompt, stream=True, generation_config=gen_config)
response.resolve()
msg = response.text
st.session_state.messages.append({“role”: “assistant”, “content”: msg})
st.chat_message(“assistant”).write(msg)else:: If no image is uploaded:prompt = st.chat_input(placeholder=”I’m Antenna, what’s on your mind?”): Creates an input field for the user’s text prompt.if prompt:: If the user enters a prompt:st.session_state.messages.append({“role”: “user”, “content”: prompt}): Adds the user’s message to the chat history.st.chat_message(“user”).write(prompt): Displays the user’s message.response = st.session_state.chat.send_message(prompt, stream=True, generation_config=gen_config): Sends the user’s message to the chatbot.response.resolve(): Resolves the response.msg = response.text: Gets the response text.st.session_state.messages.append({“role”: “assistant”, “content”: msg}): Adds the assistant’s message to the chat history.st.chat_message(“assistant”).write(msg): Displays the assistant’s message.

Saving Chat History

Finally, we save the chat history. This is like our chatbot writing down everything it talked about before going to sleep.

save_chat_history(st.session_state.messages)save_chat_history(st.session_state.messages): Saves the current chat history to a file.

Entry Point

Alright, now we’re at the grand finale of our script. This is where we tell Python to start the show!

Main Function Call

This little snippet is the entry point of our application. It’s like flipping the switch to turn everything on.

if __name__ == “__main__”:
main()if __name__ == “__main__”:: This line checks if the script is being run directly (not imported as a module). It’s like asking, “Are we the main act or just a guest star?”main(): If we are the main act, it calls the main() function to start the application. It’s like saying, “Let’s get this party started!”

Now streamlit run Home.py to see the magic

streamlit run Home.py

Here is a demo gif of Antenna

Image/Gif recorded by Author

Checkout now cloud deployed application

https://medium.com/media/cd3bac73bde2164d121fb6a4aa16830a/href

Now lets see best Practices for Developing LLM Chatbots with Streamlit and GeminiPro

Alright, let’s get down to business. Here are some best practices to make your LLM chatbot shine like a diamond in the rough:

Define Clear Goals: Know what you want your chatbot to do. Whether it’s answering customer queries or providing tech support, having clear goals helps you tailor the chatbot’s training and responses.Collect Quality Training Data: Gather a diverse dataset that includes examples relevant to your chatbot’s tasks. This could be customer queries, industry-specific jargon, or even memes if your chatbot needs to be hip and trendy.Choose the Right Model Size: Balance performance with your available computing resources. Bigger models are powerful but can be resource hogs. Think of it like choosing between a sports car and a fuel-efficient sedan.Fine-tuning: Use techniques like transfer learning and prompt engineering to make your chatbot smarter. Tailor it to your specific use case by providing it with relevant data and adjusting parameters like learning rates. It’s like giving your chatbot a personal trainer.Test and Evaluate: Put your chatbot through its paces with rigorous testing. Use new, unseen data to ensure its responses are accurate and relevant. It’s like a final exam for your chatbot.Use Streamlit for the UI: Streamlit is your best friend for building a user-friendly interface. It’s like the front desk of your chatbot, capturing user input and displaying responses. Plus, it’s super easy to set up and customize.Maintain Chat History: Use Streamlit’s session state to keep track of the conversation history. This helps your chatbot maintain context and provide coherent responses. It’s like giving your chatbot a memory.Deploy and Monitor: Once your chatbot is ready, deploy it using Streamlit Cloud or any other hosting service. Keep an eye on its performance and gather user feedback to make continuous improvements. It’s like being a proud parent watching your kid grow.

And there you have it! With these best practices, you’ll be well on your way to creating an awesome LLM chatbot with Streamlit and GeminiPro called Atenna
Happy coding! 🚀 Feel free to reach out if you have any more questions or need further assistance. Happy chatbot building!

FYI this is just beginner Chabot , already I have developed 100+ more complicated and full RAG capabilities, Agents and more advanced tech-stack, follow and subscriber to get notified as soon as they are published!

Checkout some of my popular medium articles here:101 Python Automation Scripts: Streamlining Tasks and Boosting Productivity(Part 1)Streamlit Magic Cheat Sheets(17000 lines of Codes)

Antenna — Beautiful, Text-Vision Chat with Gemini Pro — Production ready was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.

​ Level Up Coding – Medium

about Infinite Loop Digital

We support businesses by identifying requirements and helping clients integrate AI seamlessly into their operations.

Gartner
Gartner Digital Workplace Summit Generative Al

GenAI sessions:

  • 4 Use Cases for Generative AI and ChatGPT in the Digital Workplace
  • How the Power of Generative AI Will Transform Knowledge Management
  • The Perils and Promises of Microsoft 365 Copilot
  • How to Be the Generative AI Champion Your CIO and Organization Need
  • How to Shift Organizational Culture Today to Embrace Generative AI Tomorrow
  • Mitigate the Risks of Generative AI by Enhancing Your Information Governance
  • Cultivate Essential Skills for Collaborating With Artificial Intelligence
  • Ask the Expert: Microsoft 365 Copilot
  • Generative AI Across Digital Workplace Markets
10 – 11 June 2024

London, U.K.