Build a Personal AI Study Buddy

Build a Streamlit chat app that uses Gemini AI to explain, quiz, and summarize notes.

Introduction

30 Second Summary

Studying alone can feel like shouting questions into the void. Imagine having a personal tutor available anytime, one that can explain tricky concepts, quiz you on your notes, or summarize pages of material into clear bullet points.

In this project, you will build a personal AI Study Buddy web app using Python and Google's Gemini API. Your study buddy lives in a chat interface where you paste notes or ask questions, and it responds in different study modes to help you learn faster.

What You'll Build

A chat-based web application powered by Streamlit and the Gemini LLM that helps you study any subject through multiple AI-driven modes.

By the end of this project, you'll have:

  • A working Streamlit web app with a real-time chat interface running locally on your machine.
  • Three study modes (Explain, Quiz Me, and Summarize) powered by custom system prompts that shape how the AI responds.
  • Conversation memory and streaming responses so your study buddy remembers context and replies in real-time.
  • Secret Mission: Add a Flashcard Generator mode that creates interactive flip cards from your notes.

Are there any prerequisites?

None. This project walks you through installing every tool from scratch, including Python, your code editor, and the Gemini API key. You just need a Google account (free) to generate your API key.

Before We Start

You're about to build an AI-powered study buddy that can explain concepts, quiz you, and summarize your notes. To make this happen, you'll need a few tools on your machine.

In this step, you'll install Python and Cursor (a code editor), then set up your project folder with an isolated environment for your packages.

In this step, get ready to:
  • Install Python and Cursor on your machine.
  • Create your project folder and virtual environment.
  • Install the required Python packages.
Install Python and Cursor

Python is the programming language your study buddy is built with. You need version 3.10 or higher installed on your machine.

  • Check if Python is already installed by running the version check command for your operating system:

macOS

python3 --version

Windows

python --version

✔️ I see version 3.10 or higher

You're all set. Move on to installing Cursor below.

ⓧ I see an older version

You need Python 3.10 or higher for this project. The packages we use require it.

  • Go to python.org/downloads and download the latest Python 3 release for your operating system.
  • Run the installer. On Windows, make sure to check the box that says Add python.exe to PATH before clicking Install Now.
  • Close and reopen your terminal, then run the version check again to confirm.

Why a newer version?

The google-genai SDK requires Python 3.10 or higher. Older versions will fail during package installation.

ⓧ Command not found

Python isn't installed on your machine yet. Let's fix that.

  • Go to python.org/downloads and download the latest Python 3 release for your operating system.
  • Run the installer. On Windows, make sure to check the box that says Add python.exe to PATH before clicking Install Now.
  • Close and reopen your terminal, then run the version check again to confirm it works.

Now let's install Cursor, the code editor you'll use throughout this project.

  • Go to cursor.com/download in your browser.
  • Click the download button for your operating system (macOS or Windows).
  • Open the downloaded file. On macOS, drag Cursor into your Applications folder. On Windows, run the installer and follow the prompts.
  • Open Cursor. You'll be prompted to sign in or create a free account.

Can't find the download?

Make sure you're at cursor.com/download (not a different site). The page shows download buttons for macOS, Windows, and Linux.

Help me troubleshoot my Cursor installation.

Create the project folder and virtual environment

A virtual environment is an isolated space for your project's packages. It keeps this project's dependencies separate from anything else on your machine.

  • Open your terminal. Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Terminal (macOS) or PowerShell (Windows) and press Enter to open it.
  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir study-buddy
cd study-buddy
  • Confirm the folder exists by running:
ls

The folder will be empty for now. That's expected.

  • Create a virtual environment by running the command for your operating system:

macOS

python3 -m venv venv

Windows

python -m venv venv
  • Activate the virtual environment:

macOS

source venv/bin/activate

Windows

venv\Scripts\activate

You should see (venv) appear at the start of your terminal prompt. This confirms the virtual environment is active.

What is a virtual environment?

Think of it as a sealed box for your project. Any packages you install while it's active only exist inside this box. This prevents conflicts between different projects on your machine.

  • Now open this folder in Cursor. Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Cursor and press Enter to open it.
  • In Cursor, click File in the top menu bar.
  • Click Open Folder.
  • Navigate to your Desktop and select the study-buddy folder, then click Open.

Don't see (venv) in your prompt?

If you switched to Cursor's integrated terminal, you may need to activate the virtual environment again. Open the terminal in Cursor with Ctrl+` and run the activate command for your OS.

Help me troubleshoot my virtual environment.

Install the required packages

Your study buddy needs two main libraries. Streamlit builds the web interface, and google-genai connects to the Gemini API.

  • Make sure your virtual environment is active (you should see (venv) in your terminal prompt).
  • Install the packages by running:
pip install streamlit google-genai python-dotenv

What are these packages?

streamlit is a Python framework for building interactive web apps with minimal code. It handles all the HTML and JavaScript for you.

google-genai is Google's official SDK for the Gemini API. You'll use it to send prompts and receive AI responses.

python-dotenv loads environment variables from a .env file. This keeps your API key out of your code.

You should see output ending with Successfully installed followed by a list of package names.

  • Create a requirements.txt file to document your project's dependencies. In Cursor, click File then New File.
  • Name the file requirements.txt and add the following content:
streamlit
google-genai
python-dotenv

What is requirements.txt?

This file lists every package your project needs. Anyone who clones your project can run pip install -r requirements.txt to install everything in one go.

  • Save requirements.txt with Cmd+S (macOS) or Ctrl+S (Windows).

✔️ Awesome, I've got everything!

Great. Make sure you've saved your file and your virtual environment is still active.

ⓧ I'd like to double check the full code

streamlit
google-genai
python-dotenv

Seeing a pip error?

If you see a permission error or a message about pip not being found, make sure your virtual environment is active. You should see (venv) at the start of your terminal prompt.

If you see errors about package versions or compatibility, check that your Python version is 3.10 or higher.

Help me fix my pip install error.

Your development environment is ready. Next up, you'll get your Gemini API key so your study buddy can talk to Google's AI.

Set Up Your Development Environment

Your study-buddy folder is open in Cursor and your virtual environment is active. Before you start building the app, there's one more package to add for handling sensitive credentials like API keys.

In this step, you'll install the final dependency and create a requirements file that documents everything your project needs to run.

In this step, get ready to:
  • Install python-dotenv for secure API key management.
  • Create a requirements.txt file to track your project dependencies.
Install the python-dotenv package

Your app will load an API key from a local file instead of hardcoding it into your source code. The python-dotenv package reads variables from a .env file and makes them available to your Python code automatically.

  • Install the package by running this command in your terminal:
pip install python-dotenv

What does this command do?

pip install python-dotenv downloads and installs the python-dotenv library into your active virtual environment. Later, you'll use it to load your Gemini API key from a .env file so the key never appears directly in your code.

  • After the installation finishes, you should see a message ending with Successfully installed python-dotenv in your terminal output.

Seeing a 'pip not found' error?

Make sure your virtual environment is still active. You should see (venv) at the start of your terminal prompt. If it disappeared, reactivate it with venv\Scripts\activate (Windows) or source venv/bin/activate (macOS).

Help me fix my pip install error in my virtual environment

Create a requirements file

A requirements.txt file lists every package your project depends on. Anyone who clones your project can install all dependencies in one command by reading from this file.

  • Create a new file in Cursor by pressing Cmd+N (macOS) or Ctrl+N (Windows).
  • Copy the following code into the file:
streamlit
google-genai
python-dotenv

What does this file do?

Each line names one package your project needs. streamlit is the web framework for your chat interface. google-genai is the official SDK for calling the Gemini API. python-dotenv loads your API key from a local file.

  • Save the file as requirements.txt by pressing Cmd+S (macOS) or Ctrl+S (Windows). Name it requirements.txt and save it inside your study-buddy folder.
  • Confirm the file exists by running:
ls
  • You should see requirements.txt and venv listed in the output.

Don't see requirements.txt in the output?

Make sure you saved the file inside the study-buddy folder, not a parent or subfolder. Check the file tab in Cursor to confirm the path shows study-buddy/requirements.txt.

Help me find where my requirements.txt file was saved

✔️ Awesome, I've got everything!

Great. Double check you've saved your file.

ⓧ I'd like to double check the full code

Here's what your requirements.txt should look like:

streamlit
google-genai
python-dotenv

Your environment is fully set up. Next, you'll grab a free Gemini API key to give your study buddy its brain.

Get Your Gemini API Key

Your development environment is ready with Python, Cursor, and all the packages installed. Now you need a way for your app to talk to Google's AI.

The Gemini API is Google's interface for accessing their AI models. To use it, you need an API key that authenticates your requests. In this step, you will generate a free key and confirm it works.

In this step, get ready to:
  • Create a free Gemini API key in Google AI Studio.
  • Store your key securely using a .env file and .gitignore.
  • Verify the API works by running a quick test script.
Create your API key

Google AI Studio is the free web console where you manage your Gemini API keys. You only need a Google account to get started.

  • Open your browser and go to aistudio.google.com.
  • Sign in with your Google account.
  • Click Create API Key.
  • If prompted to select a project, choose the default project that Google AI Studio provides.
  • Copy the API key that appears. You will need it in the next substep.

What is an API key?

An API key is a unique string that identifies your app when it makes requests to an external service. Think of it like a library card that proves you are allowed to borrow books.

The Gemini API free tier gives you enough tokens for personal study use with no credit card required.

Keep this tab open

Leave Google AI Studio open in a browser tab. If you ever need to view or regenerate your key, you can return to the API Keys page from the left sidebar.

Store the key securely

Hardcoding API keys directly in your code is risky. If you ever share or upload your code, the key would be exposed.

  • In Cursor, create a new file called .env in your study-buddy folder by pasting the following:
GEMINI_API_KEY=your-api-key-here
  • Replace your-api-key-here with the actual API key you copied from Google AI Studio.

What does this file do?

The .env file stores environment variables outside your code. Your app reads the key from here at runtime, keeping the secret out of your source files.

  • Create another new file called .gitignore in the same study-buddy folder with this content:
.env
venv/
__pycache__/

Why do I need a .gitignore?

The .gitignore file tells Git which files to never track or upload. By listing .env here, your API key stays on your machine even if you push code to GitHub.

The venv/ and __pycache__/ entries exclude your virtual environment and Python's compiled files, which are large and machine-specific.

  • Save both files.
  • Confirm the files exist by running:
ls -a
  • You should see .env and .gitignore listed alongside your requirements.txt and venv folder.

Don't see the files?

Files starting with a dot are hidden by default. The -a flag in ls -a shows hidden files. If you still don't see them, make sure you saved the files in the study-buddy folder and not a subfolder.

help me find my .env and .gitignore files

Verify the API works

Before building the full app, you will run a quick test to make sure your API key connects to Gemini successfully.

  • Create a new file called test_api.py in your study-buddy folder and paste the following code:
import os
from google import genai

os.environ["GEMINI_API_KEY"] = "your-api-key-here"

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

response = client.models.generate_content(
    model="gemini-3.5-flash",
    contents="Say hello and confirm you're working!"
)

print(response.text)
  • Replace your-api-key-here with the same API key you pasted into your .env file.

What does this code do?

  • from google import genai imports the official Google GenAI SDK you installed earlier.
  • genai.Client(api_key=...) creates a client that authenticates your requests to the Gemini API.
  • client.models.generate_content(...) sends a prompt to the gemini-3.5-flash model and returns the AI's response.
  • print(response.text) prints whatever the model says back to your terminal.

Why not use .env here?

This test script sets the key directly so you can quickly confirm the API works without needing the dotenv loading mechanism. Your full app in the next step will load the key from .env using python-dotenv.

  • Save test_api.py.
  • Run the test script by entering this command in your terminal:
python test_api.py
  • You should see a friendly greeting from the AI printed in your terminal, confirming that your key works and the API is connected.

Seeing an error?

If you see a message containing API key not valid, double-check that you pasted the full key with no extra spaces or missing characters.

If you see ModuleNotFoundError, make sure your virtual environment is still activated. You should see (venv) at the start of your terminal prompt.

help me fix my Gemini API test script

✔️ Awesome, I've got everything!

Great. Double check you have saved all three files: .env, .gitignore, and test_api.py.

ⓧ I'd like to double check the full code

Here are all the files you should have after this step:

GEMINI_API_KEY=your-api-key-here
.env
venv/
__pycache__/
import os
from google import genai

os.environ["GEMINI_API_KEY"] = "your-api-key-here"

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

response = client.models.generate_content(
    model="gemini-3.5-flash",
    contents="Say hello and confirm you're working!"
)

print(response.text)
streamlit
google-genai
python-dotenv

Your API key is working and securely stored. Next up, you will build the chat interface that lets you interact with your AI study buddy in real time.

Build the Chat Interface

Your API key is working and your development environment is fully set up. Now it's time to build the actual interface your study buddy will use to talk with you.

Streamlit makes it easy to build chat-based web apps in pure Python. In this step, you'll create the app's layout, add a study mode selector, and set up conversation memory so your buddy remembers what you've discussed.

In this step, get ready to:
  • Create the Streamlit app with a sidebar and study mode selector.
  • Initialize conversation memory using session state.
  • Display chat messages and add a text input for the user.
Create the Streamlit app layout

Your app needs a main file that Streamlit will run. This file sets up the page, connects to the Gemini API, defines system prompts for each study mode, and builds the sidebar.

  • In Cursor, create a new file called app.py in your study-buddy folder.
  • Add the imports and API client setup by pasting this code:
import os
import streamlit as st
from google import genai
from dotenv import load_dotenv

# Load environment variables from .env file
load_dotenv()

# Create the Gemini API client
client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])

# Define system prompts that shape AI behavior for each study mode
SYSTEM_PROMPTS = {
    "Explain": (
        "You are a friendly and patient study buddy. When the user shares notes "
        "or asks about a topic, explain it clearly using simple language, analogies, "
        "and examples. Break complex ideas into digestible pieces. "
        "Ask if they want you to go deeper on any point."
    ),
    "Quiz Me": (
        "You are a study buddy that creates quiz questions. When the user shares "
        "notes or a topic, generate 3-5 questions to test their understanding. "
        "Mix question types: multiple choice, short answer, and true/false. "
        "After the user answers, tell them if they're correct and explain why."
    ),
    "Summarize": (
        "You are a study buddy that creates concise study notes. When the user "
        "shares content, create a clear summary using bullet points and headers. "
        "Highlight key terms in bold. Keep it brief but comprehensive. "
        "End with 2-3 key takeaways."
    ),
}

What does this code do?

load_dotenv() reads your .env file and loads GEMINI_API_KEY into your environment variables. The client connects to the Gemini API using that key.

SYSTEM_PROMPTS is a dictionary where each key is a study mode and each value is a detailed instruction that tells the AI how to behave. These are called system prompts because they shape the AI's personality before it ever sees the user's message.

You won't see a change yet. The next chunk builds the visible interface.

  • Below the SYSTEM_PROMPTS dictionary, add the page config and sidebar by pasting this code:
# Configure the Streamlit page
st.set_page_config(page_title="AI Study Buddy", page_icon="\ud83d\udcda")
st.title("\ud83d\udcda AI Study Buddy")

# Build the sidebar with study mode selection
with st.sidebar:
    st.header("Study Mode")
    mode = st.radio(
        "Choose how your buddy helps you:",
        options=["Explain", "Quiz Me", "Summarize"],
        index=0,
    )
    st.divider()

What does this code do?

st.set_page_config sets the browser tab title and icon. st.title adds a large heading to the page.

The with st.sidebar: block places everything inside it in a collapsible side panel. st.radio creates a radio button group so the user can pick one study mode at a time.

  • Save app.py.
  • Launch the app by running this command in your terminal:
streamlit run app.py

A new browser tab opens automatically at localhost:8501. You should see the page title and a sidebar with the three study mode radio buttons.

Don't see the app?

  • Make sure your virtual environment is still activated. You should see (venv) at the start of your terminal prompt.
  • On Windows, if streamlit is not recognized, try python -m streamlit run app.py instead.
  • Check that your .env file is in the same folder as app.py. If the API key isn't found, the app will crash on startup.

Help me troubleshoot why my Streamlit app isn't launching.

Add conversation memory with session state

Streamlit reruns your entire script from top to bottom every time the user interacts with the app. Without session state, all your variables would reset on every click.

  • Below the sidebar code in app.py, add the session state initialization and welcome message by pasting this code:
# Initialize chat history in session state (persists across reruns)
if "messages" not in st.session_state:
    st.session_state.messages = []

# Show a welcome message when there's no chat history yet
if not st.session_state.messages:
    st.chat_message("assistant").write(
        "Hey! I'm your AI Study Buddy \ud83d\udcda\n\n"
        "**Here's how I can help:**\n"
        "- **Explain** - I'll break down concepts in simple terms\n"
        "- **Quiz Me** - I'll test your understanding with questions\n"
        "- **Summarize** - I'll create concise notes from your material\n\n"
        "Paste your notes or ask me anything to get started!"
    )

What is session state?

st.session_state is a dictionary that persists across Streamlit reruns. By storing messages here, your conversation history survives every interaction.

The if "messages" not in st.session_state check ensures the list is only created once. After that, new messages get appended to the existing list.

  • Save app.py and switch back to your browser.
  • The page should automatically reload. You should see a friendly welcome message from the assistant explaining the three study modes.

Not seeing the welcome message?

  • If the page didn't auto-refresh, click **Always rerun** in the top-right banner that says "Source file changed."
  • Make sure the welcome message code is outside the with st.sidebar: block. It should be at the same indentation level as the sidebar block itself.

Help me figure out why my welcome message isn't showing up in my Streamlit app.

Display the chat history and add the input

Now you need a way to display previous messages and let the user type new ones. The history loop renders every stored message, and the chat input gives users a text box to type in.

  • Below the welcome message code in app.py, add the chat history display and input by pasting this code:
# Render all previous messages from session state
for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.write(message["content"])

# Create the chat input box at the bottom of the page
prompt = st.chat_input("Paste your notes or ask a question...")

What does this code do?

The for loop walks through every message in st.session_state.messages and renders it as a chat bubble. Each message has a role (either "user" or "assistant") that determines the bubble's appearance.

st.chat_input places a fixed text input at the bottom of the page. When the user presses Enter, the value is stored in prompt and the app reruns.

  • Save app.py and check your browser.
  • You should see a chat input box at the bottom of the page with the placeholder text "Paste your notes or ask a question..." displayed inside it.

Chat input not visible?

  • Scroll to the bottom of the page. The chat input sticks to the bottom of the viewport.
  • Check for syntax errors in your terminal. If the app crashes, Streamlit shows a red error banner in the browser with the line number.

Help me debug why my chat input isn't appearing in my Streamlit study buddy app.

✔️ Awesome, I've got everything!

Great. Double check you've saved app.py and your browser shows the welcome message plus the chat input at the bottom.

ⓧ I'd like to double check the full code

Here's the complete app.py file so far:

import os
import streamlit as st
from google import genai
from dotenv import load_dotenv

load_dotenv()

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

SYSTEM_PROMPTS = {
    "Explain": (
        "You are a friendly and patient study buddy. When the user shares notes "
        "or asks about a topic, explain it clearly using simple language, analogies, "
        "and examples. Break complex ideas into digestible pieces. "
        "Ask if they want you to go deeper on any point."
    ),
    "Quiz Me": (
        "You are a study buddy that creates quiz questions. When the user shares "
        "notes or a topic, generate 3-5 questions to test their understanding. "
        "Mix question types: multiple choice, short answer, and true/false. "
        "After the user answers, tell them if they're correct and explain why."
    ),
    "Summarize": (
        "You are a study buddy that creates concise study notes. When the user "
        "shares content, create a clear summary using bullet points and headers. "
        "Highlight key terms in bold. Keep it brief but comprehensive. "
        "End with 2-3 key takeaways."
    ),
}

st.set_page_config(page_title="AI Study Buddy", page_icon="\ud83d\udcda")
st.title("\ud83d\udcda AI Study Buddy")

with st.sidebar:
    st.header("Study Mode")
    mode = st.radio(
        "Choose how your buddy helps you:",
        options=["Explain", "Quiz Me", "Summarize"],
        index=0,
    )
    st.divider()

if "messages" not in st.session_state:
    st.session_state.messages = []

if not st.session_state.messages:
    st.chat_message("assistant").write(
        "Hey! I'm your AI Study Buddy \ud83d\udcda\n\n"
        "**Here's how I can help:**\n"
        "- **Explain** - I'll break down concepts in simple terms\n"
        "- **Quiz Me** - I'll test your understanding with questions\n"
        "- **Summarize** - I'll create concise notes from your material\n\n"
        "Paste your notes or ask me anything to get started!"
    )

for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.write(message["content"])

prompt = st.chat_input("Paste your notes or ask a question...")

Your chat interface is ready. Next up, you'll connect the Gemini API so the study buddy actually responds to your messages in each mode.

Connect the Gemini API with Study Modes

Your chat interface can display messages and accept input. Now you need to connect it to the Gemini API so your study buddy can actually respond.

In this step, you'll wire the chat input to the Gemini API. Each time a user sends a message, your app will select the right system prompt, include the conversation history for context, and stream the AI's response in real time.

In this step, get ready to:
  • Understand how system prompts shape AI behavior for each study mode.
  • Build the conversation contents structure for multi-turn context.
  • Stream Gemini's response into the chat interface.
Review your system prompts

Your app.py already contains a SYSTEM_PROMPTS dictionary from the previous step. Each key maps a study mode to a detailed instruction that tells the AI how to behave.

What do the system prompts do?

System prompts are invisible instructions you send to the AI before the user's actual question. They shape the AI's personality and response format without the user seeing them.

  • Explain mode tells the AI to use simple language, analogies, and examples.
  • Quiz Me mode tells the AI to generate test questions and grade the user's answers.
  • Summarize mode tells the AI to produce bullet-point notes with key takeaways.

The same user message produces completely different responses depending on which system prompt you prepend. This is prompt engineering in action.

Handle user input and stream AI responses

Right now your chat input captures text but does nothing with it. You need to replace the last line in app.py with logic that sends the message to Gemini and displays the streamed response.

  • In app.py, scroll to the very last line of the file.
  • Find this line:
prompt = st.chat_input("Paste your notes or ask a question...")
  • Delete that line and replace it with the following code:
if prompt := st.chat_input("Paste your notes or ask a question..."):
    # Save the user's message to conversation history
    st.session_state.messages.append({"role": "user", "content": prompt})
    with st.chat_message("user"):
        st.write(prompt)

    with st.chat_message("assistant"):
        # Select the system prompt based on the current mode
        system_prompt = SYSTEM_PROMPTS[mode]
        # Build the contents list with system prompt as first message
        contents = [{"role": "user", "parts": [{"text": system_prompt}]}]
        contents.append({"role": "model", "parts": [{"text": "Understood! I'll help you study in this mode."}]})

        # Include previous conversation for context
        for msg in st.session_state.messages[:-1]:
            role = "user" if msg["role"] == "user" else "model"
            contents.append({"role": role, "parts": [{"text": msg["content"]}]})

        # Add the current user message
        contents.append({"role": "user", "parts": [{"text": prompt}]})

        # Call Gemini with streaming enabled
        response = client.models.generate_content_stream(
            model="gemini-3.5-flash",
            contents=contents,
        )

        # Stream the response text into the chat bubble
        full_response = st.write_stream(
            chunk.text for chunk in response if chunk.text
        )

    # Save the assistant's complete response to history
    st.session_state.messages.append({"role": "assistant", "content": full_response})

What does this code do?

  • The := walrus operator assigns the chat input to prompt AND checks if it's truthy in one line. The block only runs when the user submits a message.
  • The system prompt is injected as a fake first exchange (user sends the instruction, model acknowledges it). This teaches the AI its role without polluting the visible chat.
  • Previous messages from st.session_state.messages[:-1] are included so the AI remembers the full conversation. The [:-1] excludes the current message since it gets appended separately.
  • generate_content_stream returns chunks of text as the AI generates them. Wrapping this in st.write_stream displays each chunk with a typewriter effect and returns the full combined text.
  • Save app.py.

Seeing a syntax error?

  • Make sure you deleted the old prompt = st.chat_input(...) line completely. Having both the old line and the new if prompt := block will cause a duplicate widget error.
  • Check that the indentation is consistent. Everything inside the if prompt := block should be indented by 4 spaces.
  • Verify you have the closing parenthesis on the st.write_stream( call. The generator expression inside needs to be wrapped properly.

Why isn't my chat responding after I type a message?

Run and test your study modes

Time to see your study buddy in action. You'll launch the app and test each mode to confirm the AI responds differently based on your selection.

  • Launch the app by running this command in your terminal:
streamlit run app.py
  • Your browser opens automatically to the app. You should see the welcome message from your AI Study Buddy.
  • Make sure Explain is selected in the sidebar.
  • Type What is photosynthesis? in the chat input and press Enter.
  • Watch the response stream in character by character with a typewriter effect.
  • Switch to Quiz Me in the sidebar and send the same question. The AI should generate quiz questions instead of explanations.
  • Switch to Summarize and try pasting a paragraph of notes. You should get a bullet-point summary.

What should I see?

Each mode produces a visibly different style of response from the exact same input. The text streams in smoothly, and your previous messages remain visible in the chat history above.

Getting an API error?

  • Check that your .env file contains a valid GEMINI_API_KEY value. If you still have the placeholder text your-api-key-here, replace it with your actual key from Google AI Studio.
  • If you see a rate limit error, wait a minute and try again. The free tier has usage limits but they refresh quickly.
  • Make sure your virtual environment is active (you should see (venv) in your terminal prompt).

My Gemini API call is failing with an error. How do I fix it?

✔️ Awesome, I've got everything!

Great. Double check you've saved app.py and that the app is responding to your messages.

ⓧ I'd like to double check the full code

Here is the complete app.py file as it should look at the end of this step:

import os
import streamlit as st
from google import genai
from dotenv import load_dotenv

load_dotenv()

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

SYSTEM_PROMPTS = {
    "Explain": (
        "You are a friendly and patient study buddy. When the user shares notes "
        "or asks about a topic, explain it clearly using simple language, analogies, "
        "and examples. Break complex ideas into digestible pieces. "
        "Ask if they want you to go deeper on any point."
    ),
    "Quiz Me": (
        "You are a study buddy that creates quiz questions. When the user shares "
        "notes or a topic, generate 3-5 questions to test their understanding. "
        "Mix question types: multiple choice, short answer, and true/false. "
        "After the user answers, tell them if they're correct and explain why."
    ),
    "Summarize": (
        "You are a study buddy that creates concise study notes. When the user "
        "shares content, create a clear summary using bullet points and headers. "
        "Highlight key terms in bold. Keep it brief but comprehensive. "
        "End with 2-3 key takeaways."
    ),
}

st.set_page_config(page_title="AI Study Buddy", page_icon="\ud83d\udcda")
st.title("\ud83d\udcda AI Study Buddy")

with st.sidebar:
    st.header("Study Mode")
    mode = st.radio(
        "Choose how your buddy helps you:",
        options=["Explain", "Quiz Me", "Summarize"],
        index=0,
    )
    st.divider()

if "messages" not in st.session_state:
    st.session_state.messages = []

if not st.session_state.messages:
    st.chat_message("assistant").write(
        "Hey! I'm your AI Study Buddy \ud83d\udcda\n\n"
        "**Here's how I can help:**\n"
        "- **Explain** - I'll break down concepts in simple terms\n"
        "- **Quiz Me** - I'll test your understanding with questions\n"
        "- **Summarize** - I'll create concise notes from your material\n\n"
        "Paste your notes or ask me anything to get started!"
    )

for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.write(message["content"])

if prompt := st.chat_input("Paste your notes or ask a question..."):
    st.session_state.messages.append({"role": "user", "content": prompt})
    with st.chat_message("user"):
        st.write(prompt)

    with st.chat_message("assistant"):
        system_prompt = SYSTEM_PROMPTS[mode]
        contents = [{"role": "user", "parts": [{"text": system_prompt}]}]
        contents.append({"role": "model", "parts": [{"text": "Understood! I'll help you study in this mode."}]})

        for msg in st.session_state.messages[:-1]:
            role = "user" if msg["role"] == "user" else "model"
            contents.append({"role": role, "parts": [{"text": msg["content"]}]})

        contents.append({"role": "user", "parts": [{"text": prompt}]})

        response = client.models.generate_content_stream(
            model="gemini-3.5-flash",
            contents=contents,
        )

        full_response = st.write_stream(
            chunk.text for chunk in response if chunk.text
        )

    st.session_state.messages.append({"role": "assistant", "content": full_response})

Your study buddy is alive and responding. Next up, you'll add finishing touches like a clear chat button and a friendly welcome message to make the experience feel polished.

Add Polish and Controls

Your study buddy can already chat, remember conversations, and switch between three modes. That covers the core functionality.

But a good app also needs small quality-of-life features. In this step, you'll add a button to clear the chat history and verify that the welcome message guides new users. Then you'll launch the finished app and test everything end to end.

In this step, get ready to:
  • Add a Clear Chat button that resets the conversation.
  • Verify the welcome message displays when the chat is empty.
  • Launch and test the complete app.
Add a clear chat button

As you test different study modes, your conversation history piles up. A Clear Chat button lets you start fresh without reloading the page.

  • In app.py, find the sidebar block. Look for this line inside the with st.sidebar: section:
    st.divider()
  • Add the Clear Chat button directly below st.divider(), still inside the with st.sidebar: block, by copying this code:
    if st.button("🗑️ Clear Chat"):
        st.session_state.messages = []
        st.rerun()

What does this code do?

  • st.button("🗑️ Clear Chat") renders a clickable button in the sidebar. It returns True only on the click that triggers it.
  • st.session_state.messages = [] wipes the conversation history by resetting the list to empty.
  • st.rerun() forces Streamlit to re-execute the script from top to bottom, so the UI immediately reflects the cleared state.
  • Save app.py.

Seeing an indentation error?

The if st.button block must be indented one level inside the with st.sidebar: block (4 spaces). If it's at the top level, the button won't appear in the sidebar.

Help me fix my Clear Chat button indentation in app.py

Verify the welcome message

When a new user opens the app (or after clearing the chat), they should see a friendly introduction. Your code already handles this.

  • In app.py, find this block below the session state initialization:
if not st.session_state.messages:
    st.chat_message("assistant").write(
        "Hey! I'm your AI Study Buddy 📚\n\n"
        "**Here's how I can help:**\n"
        "- **Explain** - I'll break down concepts in simple terms\n"
        "- **Quiz Me** - I'll test your understanding with questions\n"
        "- **Summarize** - I'll create concise notes from your material\n\n"
        "Paste your notes or ask me anything to get started!"
    )

How does the welcome message work?

The condition if not st.session_state.messages checks whether the messages list is empty. An empty list evaluates to False in Python, so not [] is True.

This means the welcome message only shows when there is no conversation history. As soon as the user sends their first message, it disappears. Clicking Clear Chat empties the list, which brings it back.

Run and test your app

Time to see everything working together. You'll launch the Streamlit dev server and test each study mode.

  • Launch your app by running this command in the terminal:
streamlit run app.py

Your browser should open automatically to a local URL (typically http://localhost:8501). You should see the welcome message and the sidebar with your mode selector and Clear Chat button.

  • Test the Explain mode by pasting some study notes or typing a question like "What is photosynthesis?" into the chat input.
  • Switch to Quiz Me in the sidebar and send another message. Verify you get quiz-style questions back.
  • Switch to Summarize and paste in a longer block of text. Verify you get bullet-point notes.
  • Send a follow-up message in the same mode. The AI should remember your earlier messages in the same session.
  • Click Clear Chat in the sidebar. The conversation should disappear and the welcome message should reappear.

App not loading or showing errors?

  • Make sure your virtual environment is activated. You should see (venv) at the start of your terminal prompt.
  • Check that your .env file is in the same folder as app.py and contains your real API key (not the placeholder text).
  • If you see a ModuleNotFoundError, re-run pip install streamlit google-genai python-dotenv with your virtual environment active.

Help me debug my Streamlit study buddy app that won't start

✔️ Awesome, I've got everything!

Great. Double check you've saved app.py and your app is running smoothly.

ⓧ I'd like to double check the full code

Here's the complete app.py for comparison:

import os
import streamlit as st
from google import genai
from dotenv import load_dotenv

load_dotenv()

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

SYSTEM_PROMPTS = {
    "Explain": (
        "You are a friendly and patient study buddy. When the user shares notes "
        "or asks about a topic, explain it clearly using simple language, analogies, "
        "and examples. Break complex ideas into digestible pieces. "
        "Ask if they want you to go deeper on any point."
    ),
    "Quiz Me": (
        "You are a study buddy that creates quiz questions. When the user shares "
        "notes or a topic, generate 3-5 questions to test their understanding. "
        "Mix question types: multiple choice, short answer, and true/false. "
        "After the user answers, tell them if they're correct and explain why."
    ),
    "Summarize": (
        "You are a study buddy that creates concise study notes. When the user "
        "shares content, create a clear summary using bullet points and headers. "
        "Highlight key terms in bold. Keep it brief but comprehensive. "
        "End with 2-3 key takeaways."
    ),
}

st.set_page_config(page_title="AI Study Buddy", page_icon="📚")
st.title("📚 AI Study Buddy")

with st.sidebar:
    st.header("Study Mode")
    mode = st.radio(
        "Choose how your buddy helps you:",
        options=["Explain", "Quiz Me", "Summarize"],
        index=0,
    )
    st.divider()
    if st.button("🗑️ Clear Chat"):
        st.session_state.messages = []
        st.rerun()

if "messages" not in st.session_state:
    st.session_state.messages = []

if not st.session_state.messages:
    st.chat_message("assistant").write(
        "Hey! I'm your AI Study Buddy 📚\n\n"
        "**Here's how I can help:**\n"
        "- **Explain** - I'll break down concepts in simple terms\n"
        "- **Quiz Me** - I'll test your understanding with questions\n"
        "- **Summarize** - I'll create concise notes from your material\n\n"
        "Paste your notes or ask me anything to get started!"
    )

for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.write(message["content"])

if prompt := st.chat_input("Paste your notes or ask a question..."):
    st.session_state.messages.append({"role": "user", "content": prompt})
    with st.chat_message("user"):
        st.write(prompt)

    with st.chat_message("assistant"):
        system_prompt = SYSTEM_PROMPTS[mode]
        contents = [{"role": "user", "parts": [{"text": system_prompt}]}]
        contents.append({"role": "model", "parts": [{"text": "Understood! I'll help you study in this mode."}]})

        for msg in st.session_state.messages[:-1]:
            role = "user" if msg["role"] == "user" else "model"
            contents.append({"role": role, "parts": [{"text": msg["content"]}]})

        contents.append({"role": "user", "parts": [{"text": prompt}]})

        response = client.models.generate_content_stream(
            model="gemini-3.5-flash",
            contents=contents,
        )

        full_response = st.write_stream(
            chunk.text for chunk in response if chunk.text
        )

    st.session_state.messages.append({"role": "assistant", "content": full_response})

Your AI Study Buddy is complete. You have a working app with three study modes, conversation memory, streaming responses, and user-friendly controls. Next up, tackle the Secret Mission to add interactive flashcard generation.

Secret mission

Add a Flashcard Generator Mode

Your study buddy already explains, quizzes, and summarizes. But what if it could also turn your notes into interactive flashcards you can flip through? In this mission, you'll add a fourth mode that parses AI output into clickable cards.

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally and uses the Gemini API free tier, so there are no ongoing costs. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.

Resources you used:

  • The study-buddy project folder on your Desktop (contains all project files, virtual environment, and dependencies).
  • A Gemini API key stored in your .env file and registered in Google AI Studio.
  • A Streamlit dev server running in your terminal.

Keep everything running

No action needed. Choose this if you're still actively building or experimenting with your study buddy.

  • Your Streamlit app will keep running in the terminal until you close it or press Ctrl+C.
  • Your Gemini API key remains active but costs nothing on the free tier.
  • Your virtual environment stays on disk inside the study-buddy folder.

Pause - I'll come back to this later

Stop the running process to free up your terminal, but keep all files for later.

  • In the terminal where Streamlit is running, press Ctrl+C to stop the dev server.
  • Deactivate your virtual environment by running:
deactivate
  • Your project files and API key remain intact. When you return, reactivate the virtual environment and run streamlit run app.py to pick up where you left off.

Delete - I don't want to use this again

Remove all project resources and revoke your API key to start completely fresh.

  • In the terminal where Streamlit is running, press Ctrl+C to stop the dev server.
  • Deactivate your virtual environment by running:
deactivate
  • Delete the project folder by running:

macOS

cd ~/Desktop
rm -rf study-buddy

Windows

cd ~\Desktop
rmdir /s /q study-buddy
  • Delete your Gemini API key from Google AI Studio:
  • Open the Google Cloud API Credentials page.
  • Find the API key you created for this project and click the Actions icon (three dots).
  • Select Delete API key.
  • In the Delete credential modal, click Delete to confirm.

Nice Work!

Nice Work!

You just built a personal AI study buddy from scratch. You now have a working web app that uses Google's Gemini API to help you learn any subject through multiple study modes.

Here's what you accomplished:

  • Built a Streamlit web app with a real-time chat interface and sidebar controls.
  • Integrated the Google Gemini API using the official Python SDK and engineered system prompts that shape AI behavior for different study tasks.
  • Implemented conversation memory using session state and added streaming responses for a smooth, interactive experience.
  • Created an interactive flashcard generator that parses AI output into expandable flip cards.

Ready to quiz yourself?