# Create a virtual environment
python -m venv ai_app_env # Activate the environment (Mac/Linux)
source ai_app_env/bin/activate # Activate the environment (Windows)
ai_app_env\Scripts\activate # Install dependencies
-weight: 500;">pip -weight: 500;">install gradio google-genai python-dotenv
# Create a virtual environment
python -m venv ai_app_env # Activate the environment (Mac/Linux)
source ai_app_env/bin/activate # Activate the environment (Windows)
ai_app_env\Scripts\activate # Install dependencies
-weight: 500;">pip -weight: 500;">install gradio google-genai python-dotenv
# Create a virtual environment
python -m venv ai_app_env # Activate the environment (Mac/Linux)
source ai_app_env/bin/activate # Activate the environment (Windows)
ai_app_env\Scripts\activate # Install dependencies
-weight: 500;">pip -weight: 500;">install gradio google-genai python-dotenv
GEMINI_API_KEY=your_actual_api_key_here
GEMINI_API_KEY=your_actual_api_key_here
GEMINI_API_KEY=your_actual_api_key_here
import gradio as gr # 1. Define the core Python logic
def greet_user(name): return f"Hello, {name}! Welcome to your first AI app." # 2. Create the Interface
demo = gr.Interface( fn=greet_user, inputs="text", outputs="text", title="Greeting Generator", description="Enter your name to get a custom greeting."
) # 3. Launch the web server
if __name__ == "__main__": demo.launch()
import gradio as gr # 1. Define the core Python logic
def greet_user(name): return f"Hello, {name}! Welcome to your first AI app." # 2. Create the Interface
demo = gr.Interface( fn=greet_user, inputs="text", outputs="text", title="Greeting Generator", description="Enter your name to get a custom greeting."
) # 3. Launch the web server
if __name__ == "__main__": demo.launch()
import gradio as gr # 1. Define the core Python logic
def greet_user(name): return f"Hello, {name}! Welcome to your first AI app." # 2. Create the Interface
demo = gr.Interface( fn=greet_user, inputs="text", outputs="text", title="Greeting Generator", description="Enter your name to get a custom greeting."
) # 3. Launch the web server
if __name__ == "__main__": demo.launch()
python 01_greeting_app.py
python 01_greeting_app.py
python 01_greeting_app.py
import gradio as gr
from dotenv import load_dotenv
from google import genai
from google.genai import types # Load environment variables from the .env file
load_dotenv() # Initialize the Gemini client (it automatically picks up the GEMINI_API_KEY env variable)
client = genai.Client() def chat_with_ai(user_message, history): # Convert Gradio history format to Gemini's expected types.Content format contents = [] for human_text, ai_text in history: contents.append(types.Content(role="user", parts=[ types.Part.from_text(text=human_text)])) contents.append(types.Content(role="model", parts=[ types.Part.from_text(text=ai_text)])) # Append the current user message contents.append(types.Content(role="user", parts=[ types.Part.from_text(text=user_message)])) # Configure system instructions config = types.GenerateContentConfig( system_instruction="You are a helpful engineering assistant." ) # Call the free-tier Gemini model response = client.models.generate_content( model="gemini-2.5-flash", contents=contents, config=config ) return response.text # Create the Chat Interface
demo = gr.ChatInterface( fn=chat_with_ai, title="Gemini Engineering Assistant", description="Ask me anything about Python or system design."
) if __name__ == "__main__": demo.launch()
import gradio as gr
from dotenv import load_dotenv
from google import genai
from google.genai import types # Load environment variables from the .env file
load_dotenv() # Initialize the Gemini client (it automatically picks up the GEMINI_API_KEY env variable)
client = genai.Client() def chat_with_ai(user_message, history): # Convert Gradio history format to Gemini's expected types.Content format contents = [] for human_text, ai_text in history: contents.append(types.Content(role="user", parts=[ types.Part.from_text(text=human_text)])) contents.append(types.Content(role="model", parts=[ types.Part.from_text(text=ai_text)])) # Append the current user message contents.append(types.Content(role="user", parts=[ types.Part.from_text(text=user_message)])) # Configure system instructions config = types.GenerateContentConfig( system_instruction="You are a helpful engineering assistant." ) # Call the free-tier Gemini model response = client.models.generate_content( model="gemini-2.5-flash", contents=contents, config=config ) return response.text # Create the Chat Interface
demo = gr.ChatInterface( fn=chat_with_ai, title="Gemini Engineering Assistant", description="Ask me anything about Python or system design."
) if __name__ == "__main__": demo.launch()
import gradio as gr
from dotenv import load_dotenv
from google import genai
from google.genai import types # Load environment variables from the .env file
load_dotenv() # Initialize the Gemini client (it automatically picks up the GEMINI_API_KEY env variable)
client = genai.Client() def chat_with_ai(user_message, history): # Convert Gradio history format to Gemini's expected types.Content format contents = [] for human_text, ai_text in history: contents.append(types.Content(role="user", parts=[ types.Part.from_text(text=human_text)])) contents.append(types.Content(role="model", parts=[ types.Part.from_text(text=ai_text)])) # Append the current user message contents.append(types.Content(role="user", parts=[ types.Part.from_text(text=user_message)])) # Configure system instructions config = types.GenerateContentConfig( system_instruction="You are a helpful engineering assistant." ) # Call the free-tier Gemini model response = client.models.generate_content( model="gemini-2.5-flash", contents=contents, config=config ) return response.text # Create the Chat Interface
demo = gr.ChatInterface( fn=chat_with_ai, title="Gemini Engineering Assistant", description="Ask me anything about Python or system design."
) if __name__ == "__main__": demo.launch()
import gradio as gr def respond(message, history): # Dummy logic for demonstration return f"I received your message: {message}" with gr.Blocks() as demo: gr.Markdown("# Custom Chatbot Layout") with gr.Row(): with gr.Column(scale=4): chatbot = gr.Chatbot(height=400) msg = gr.Textbox(placeholder="Type a message and press Enter...") with gr.Column(scale=1): clear_btn = gr.Button("Clear Chat") settings = gr.Markdown("### Settings\n(Add dropdowns here later)") # Hidden state to store history (Gradio 6 uses list of dicts natively) state = gr.State([]) # Event wiring def user_turn(user_message, history): # Format the user message exactly how Gradio 6 expects it new_msg = {"role": "user", "content": [ {"type": "text", "text": user_message}]} history.append(new_msg) return "", history, history # Returns: clear textbox, -weight: 500;">update state, -weight: 500;">update chatbot def ai_turn(history): # Extract the user's actual text string from the deeply nested history block user_message = history[-1]["content"][0]["text"] bot_response = respond(user_message, history) new_msg = {"role": "assistant", "content": [ {"type": "text", "text": bot_response}]} history.append(new_msg) return history, history # Returns: -weight: 500;">update state, -weight: 500;">update chatbot # When the user presses Enter in the text box msg.submit(user_turn, [msg, state], [msg, state, chatbot], queue=False).then( ai_turn, state, [state, chatbot] ) # Clear both the hidden state and the visible UI chatbot clear_btn.click(lambda: ([], []), None, [state, chatbot], queue=False) if __name__ == "__main__": demo.launch()
import gradio as gr def respond(message, history): # Dummy logic for demonstration return f"I received your message: {message}" with gr.Blocks() as demo: gr.Markdown("# Custom Chatbot Layout") with gr.Row(): with gr.Column(scale=4): chatbot = gr.Chatbot(height=400) msg = gr.Textbox(placeholder="Type a message and press Enter...") with gr.Column(scale=1): clear_btn = gr.Button("Clear Chat") settings = gr.Markdown("### Settings\n(Add dropdowns here later)") # Hidden state to store history (Gradio 6 uses list of dicts natively) state = gr.State([]) # Event wiring def user_turn(user_message, history): # Format the user message exactly how Gradio 6 expects it new_msg = {"role": "user", "content": [ {"type": "text", "text": user_message}]} history.append(new_msg) return "", history, history # Returns: clear textbox, -weight: 500;">update state, -weight: 500;">update chatbot def ai_turn(history): # Extract the user's actual text string from the deeply nested history block user_message = history[-1]["content"][0]["text"] bot_response = respond(user_message, history) new_msg = {"role": "assistant", "content": [ {"type": "text", "text": bot_response}]} history.append(new_msg) return history, history # Returns: -weight: 500;">update state, -weight: 500;">update chatbot # When the user presses Enter in the text box msg.submit(user_turn, [msg, state], [msg, state, chatbot], queue=False).then( ai_turn, state, [state, chatbot] ) # Clear both the hidden state and the visible UI chatbot clear_btn.click(lambda: ([], []), None, [state, chatbot], queue=False) if __name__ == "__main__": demo.launch()
import gradio as gr def respond(message, history): # Dummy logic for demonstration return f"I received your message: {message}" with gr.Blocks() as demo: gr.Markdown("# Custom Chatbot Layout") with gr.Row(): with gr.Column(scale=4): chatbot = gr.Chatbot(height=400) msg = gr.Textbox(placeholder="Type a message and press Enter...") with gr.Column(scale=1): clear_btn = gr.Button("Clear Chat") settings = gr.Markdown("### Settings\n(Add dropdowns here later)") # Hidden state to store history (Gradio 6 uses list of dicts natively) state = gr.State([]) # Event wiring def user_turn(user_message, history): # Format the user message exactly how Gradio 6 expects it new_msg = {"role": "user", "content": [ {"type": "text", "text": user_message}]} history.append(new_msg) return "", history, history # Returns: clear textbox, -weight: 500;">update state, -weight: 500;">update chatbot def ai_turn(history): # Extract the user's actual text string from the deeply nested history block user_message = history[-1]["content"][0]["text"] bot_response = respond(user_message, history) new_msg = {"role": "assistant", "content": [ {"type": "text", "text": bot_response}]} history.append(new_msg) return history, history # Returns: -weight: 500;">update state, -weight: 500;">update chatbot # When the user presses Enter in the text box msg.submit(user_turn, [msg, state], [msg, state, chatbot], queue=False).then( ai_turn, state, [state, chatbot] ) # Clear both the hidden state and the visible UI chatbot clear_btn.click(lambda: ([], []), None, [state, chatbot], queue=False) if __name__ == "__main__": demo.launch()
import gradio as gr
from dotenv import load_dotenv
from google import genai
from google.genai import types load_dotenv()
client = genai.Client() def stream_chat(message, history): contents = [] # History parsing logic remains identical to step 2 for msg in history: role = "model" if msg["role"] == "assistant" else "user" text_content = "".join( [block["text"] for block in msg["content"] if block["type"] == "text"]) contents.append(types.Content(role=role, parts=[ types.Part.from_text(text=text_content)])) contents.append(types.Content(role="user", parts=[ types.Part.from_text(text=message)])) # Enable streaming in the API call response_stream = client.models.generate_content_stream( model="gemini-3.5-flash", contents=contents ) partial_message = "" for chunk in response_stream: if chunk.text is not None: partial_message += chunk.text # Yielding updates the UI immediately yield partial_message demo = gr.ChatInterface( fn=stream_chat, title="Streaming Gemini Chatbot"
) if __name__ == "__main__": demo.launch()
import gradio as gr
from dotenv import load_dotenv
from google import genai
from google.genai import types load_dotenv()
client = genai.Client() def stream_chat(message, history): contents = [] # History parsing logic remains identical to step 2 for msg in history: role = "model" if msg["role"] == "assistant" else "user" text_content = "".join( [block["text"] for block in msg["content"] if block["type"] == "text"]) contents.append(types.Content(role=role, parts=[ types.Part.from_text(text=text_content)])) contents.append(types.Content(role="user", parts=[ types.Part.from_text(text=message)])) # Enable streaming in the API call response_stream = client.models.generate_content_stream( model="gemini-3.5-flash", contents=contents ) partial_message = "" for chunk in response_stream: if chunk.text is not None: partial_message += chunk.text # Yielding updates the UI immediately yield partial_message demo = gr.ChatInterface( fn=stream_chat, title="Streaming Gemini Chatbot"
) if __name__ == "__main__": demo.launch()
import gradio as gr
from dotenv import load_dotenv
from google import genai
from google.genai import types load_dotenv()
client = genai.Client() def stream_chat(message, history): contents = [] # History parsing logic remains identical to step 2 for msg in history: role = "model" if msg["role"] == "assistant" else "user" text_content = "".join( [block["text"] for block in msg["content"] if block["type"] == "text"]) contents.append(types.Content(role=role, parts=[ types.Part.from_text(text=text_content)])) contents.append(types.Content(role="user", parts=[ types.Part.from_text(text=message)])) # Enable streaming in the API call response_stream = client.models.generate_content_stream( model="gemini-3.5-flash", contents=contents ) partial_message = "" for chunk in response_stream: if chunk.text is not None: partial_message += chunk.text # Yielding updates the UI immediately yield partial_message demo = gr.ChatInterface( fn=stream_chat, title="Streaming Gemini Chatbot"
) if __name__ == "__main__": demo.launch()
import gradio as gr
from dotenv import load_dotenv
from google import genai
from google.genai import types load_dotenv()
client = genai.Client() def analyze_document(file_obj, user_question): # Prevent crashing if the user clicks Ask without a file if file_obj is None: return "Please upload a text file first." # Read the file text with open(file_obj.name, "r", encoding="utf-8") as f: file_content = f.read() # Inject the file contents directly into the system prompt config = types.GenerateContentConfig( system_instruction=f"Use this document context to answer questions:\n\n{file_content}" ) response = client.models.generate_content( model="gemini-3.5-flash", contents=user_question, config=config ) return response.text with gr.Blocks() as demo: gr.Markdown("# Document Q&A App") with gr.Row(): file_input = gr.File(label="Upload a .txt file") question_input = gr.Textbox(label="Ask a question about the file") submit_btn = gr.Button("Ask") gr.Markdown("### AI Answer") # Using gr.Markdown allows Gemini's bold text, lists, and code blocks to render beautifully output_markdown = gr.Markdown(value="Your answer will appear here...") submit_btn.click( fn=analyze_document, inputs=[file_input, question_input], outputs=output_markdown # Send the result straight to the Markdown component ) if __name__ == "__main__": demo.launch()
import gradio as gr
from dotenv import load_dotenv
from google import genai
from google.genai import types load_dotenv()
client = genai.Client() def analyze_document(file_obj, user_question): # Prevent crashing if the user clicks Ask without a file if file_obj is None: return "Please upload a text file first." # Read the file text with open(file_obj.name, "r", encoding="utf-8") as f: file_content = f.read() # Inject the file contents directly into the system prompt config = types.GenerateContentConfig( system_instruction=f"Use this document context to answer questions:\n\n{file_content}" ) response = client.models.generate_content( model="gemini-3.5-flash", contents=user_question, config=config ) return response.text with gr.Blocks() as demo: gr.Markdown("# Document Q&A App") with gr.Row(): file_input = gr.File(label="Upload a .txt file") question_input = gr.Textbox(label="Ask a question about the file") submit_btn = gr.Button("Ask") gr.Markdown("### AI Answer") # Using gr.Markdown allows Gemini's bold text, lists, and code blocks to render beautifully output_markdown = gr.Markdown(value="Your answer will appear here...") submit_btn.click( fn=analyze_document, inputs=[file_input, question_input], outputs=output_markdown # Send the result straight to the Markdown component ) if __name__ == "__main__": demo.launch()
import gradio as gr
from dotenv import load_dotenv
from google import genai
from google.genai import types load_dotenv()
client = genai.Client() def analyze_document(file_obj, user_question): # Prevent crashing if the user clicks Ask without a file if file_obj is None: return "Please upload a text file first." # Read the file text with open(file_obj.name, "r", encoding="utf-8") as f: file_content = f.read() # Inject the file contents directly into the system prompt config = types.GenerateContentConfig( system_instruction=f"Use this document context to answer questions:\n\n{file_content}" ) response = client.models.generate_content( model="gemini-3.5-flash", contents=user_question, config=config ) return response.text with gr.Blocks() as demo: gr.Markdown("# Document Q&A App") with gr.Row(): file_input = gr.File(label="Upload a .txt file") question_input = gr.Textbox(label="Ask a question about the file") submit_btn = gr.Button("Ask") gr.Markdown("### AI Answer") # Using gr.Markdown allows Gemini's bold text, lists, and code blocks to render beautifully output_markdown = gr.Markdown(value="Your answer will appear here...") submit_btn.click( fn=analyze_document, inputs=[file_input, question_input], outputs=output_markdown # Send the result straight to the Markdown component ) if __name__ == "__main__": demo.launch()
demo.launch(share=True)
demo.launch(share=True)
demo.launch(share=True) - The user types a message in the browser.
- The Gradio UI sends that text to your Python backend.
- Your Python function processes the text (calling the Google Gemini API).
- The API returns the response to your function.
- Gradio automatically updates the web UI with the new data. - fn=greet_user: We tell Gradio exactly which Python function to run when a user interacts with the app.
- inputs="text": We tell Gradio what kind of data the function expects. Because we said "text", Gradio automatically renders an HTML text box on the screen.
- outputs="text": We tell Gradio what kind of data the function returns. Gradio renders another text box to display the result.
- demo.launch(): This kicks off a local web server (powered by FastAPI under the hood) and opens the connection to your browser. - fn: The Python function to run.
- inputs: The UI component for the function's arguments.
- outputs: The UI component for the function's return value. - Role Mapping: Gradio calls the AI an "assistant". Gemini calls it a "model". We use a simple inline if statement to translate this.
- Text Extraction: Gradio supports multimodal chat (images and text together). Because of this, it stores message content as a list of blocks. We use a list comprehension ("".join([...])) to sift through the blocks, find the text ones, and combine them into a single string.
- Building Gemini Objects: We wrap our extracted text and role inside types.Content and types.Part.from_text. This is strictly required by the google-genai library. - gr.Textbox: Used for standard text entry or displaying plain text outputs. You can configure it to have multiple lines or placeholder text.
- gr.Chatbot: A specialized display component that renders conversation histories in a familiar text-message bubble format.
- gr.Button: Triggers Python functions when clicked.
- gr.Image: Handles image uploads via drag-and-drop or webcam, and can display images generated by computer vision models.
- gr.File: Allows users to upload documents (PDFs, CSVs, TXT files) and passes the file path directly to your Python script.
- gr.Markdown: Renders formatted text, tables, and links to make your UI look professional.
- gr.State: A hidden component that stores variables (like user session data or complex history) across page refreshes without displaying anything on the screen. - Layout with with statements: gr.Blocks() uses Python's context managers. Everything indented under with gr.Row(): will be placed side-by-side horizontally. Everything indented under with gr.Column(): will be stacked vertically. The scale argument dictates how wide the columns are relative to each other.
- gr.State([]): In standard Python, variables inside functions disappear when the function finishes. gr.State creates a persistent, hidden variable attached to the user's browser session. We use it to store our list of message dictionaries.
- Event Chaining (.then): Look at msg.submit(...). When a user hits Enter, we first run the user_turn function. This grabs the text, updates the state, and immediately clears the input box so it feels snappy. We use .then(...) to immediately trigger the ai_turn function right after.
- Input and Output Arrays: Notice how user_turn returns three things: "", history, history. These map directly to the output array [msg, state, chatbot]. We are telling Gradio: "Set the textbox to an empty string, set the hidden state to the updated history, and set the visual chatbot to the updated history." - generate_content_stream: Instead of calling generate_content (which waits for the whole answer to be ready), we call the streaming version. This returns an iterable stream of small text chunks directly from Google's servers.
- The for loop: We iterate over every chunk that arrives from the stream. We take the new text, append it to our partial_message variable, and then call yield partial_message.
- yield vs return: A return statement ends a function immediately. A yield statement pauses the function, sends the current value to Gradio, updates the frontend UI, and then resumes exactly where it left off. This is the magic that creates the typing effect. - if file_obj is None:: This is a guard clause. If the user hits the "Ask" button before uploading a file, Gradio passes None to our function. If we try to open None, Python will crash. This clause prevents the crash and returns a helpful warning to the user.
- file_obj.name: When a user drops a file into a Gradio interface, Gradio does not pass the raw binary data to your function. Instead, it securely saves the file to a temporary directory on your machine and passes you an object. Calling .name retrieves the absolute file path (e.g., /tmp/gradio/some_file.txt), which we can then open normally using standard Python.
- System Prompt Injection: We use an f-string to literally paste the entire text of the file into the system_instruction. We are essentially telling the AI: "Here is everything you need to know. Now, answer the user's question based only on this."
- Rendering with gr.Markdown: LLMs naturally respond with Markdown formatting (like bold text or bulleted lists). If we use a standard gr.Textbox, the user sees raw asterisks and hashes. By assigning gr.Markdown to our outputs array, Gradio parses the formatting automatically. Mark my words, formatted text is infinitely easier to read. - Blocking the UI: Python runs synchronously by default. If your API call takes 20 seconds, the Gradio UI will freeze for 20 seconds. Use asynchronous functions or generators (yield) to keep the interface responsive.
- Forgetting State: Python variables inside functions reset on every click. If you need to remember data between clicks (like chat history or user choices), you must pass it through a gr.State component or the specialized history arguments.
- Exposing API Keys: Never hardcode your API keys into your Python scripts. Always use the .env approach combined with the python-dotenv package. If you upload a hardcoded key to GitHub, bots will find it in seconds.
- Handling Large Files Badly: Reading massive datasets entirely into memory will crash your app. If a user uploads a 50MB text file, reading it straight into an LLM prompt will trigger context window boundaries or network timeouts. - You want to test an AI idea in an afternoon.
- You need to share a prototype with non-technical stakeholders.
- You are building an internal utility tool for your team.
- You want to host a portfolio project on Hugging Face. - Your app is heavily focused on data science, charts, and dashboards rather than pure inputs and outputs. (Streamlit is another great Python UI tool, optimized slightly more for data visualization). - You are building a production SaaS product.
- You need strict user authentication, database management, and complex state routing.
- You have thousands of users and need to scale microservices.