import os import gradio as gr from gradio import ChatMessage from typing import Iterator import google.generativeai as genai import time # Import time module for potential debugging/delay # get Gemini API Key from the environ variable GEMINI_API_KEY = os.getenv("GEMINI_API_KEY") genai.configure(api_key=GEMINI_API_KEY) # we will be using the Gemini 2.0 Flash model with Thinking capabilities model = genai.GenerativeModel("gemini-2.0-flash-thinking-exp-1219") def format_chat_history(messages: list) -> list: """ Formats the chat history into a structure Gemini can understand """ formatted_history = [] for message in messages: # Skip thinking messages (messages with metadata) if not (message.get("role") == "assistant" and "metadata" in message): formatted_history.append({ "role": "user" if message.get("role") == "user" else "assistant", "parts": [message.get("content", "")] }) return formatted_history def stream_gemini_response(message_input: str|gr.File, messages: list) -> Iterator[list]: """ Streams thoughts and response with conversation history support, handling text or file input. """ user_message = "" input_file = None if isinstance(message_input, str): user_message = message_input print(f"\n=== New Request (Text) ===") print(f"User message: {user_message}") if not user_message.strip(): # Robust check: if text message is empty or whitespace messages.append(ChatMessage(role="assistant", content="Please provide a non-empty text message or upload a file.")) # More specific message yield messages return elif isinstance(message_input, gr.File): #gr.File directly should be used with newer gradio versions (v4+) input_file = message_input.name # Access the temporary file path file_type = message_input.original_name.split('.')[-1].lower() #Get original filename's extension print(f"\n=== New Request (File) ===") print(f"File uploaded: {input_file}, type: {file_type}") try: with open(input_file, "rb") as f: #Open file in binary mode for universal handling file_data = f.read() if file_type in ['png', 'jpg', 'jpeg', 'gif']: #Example Image Types - expand as needed user_message = {"inline_data": {"mime_type": f"image/{file_type}", "data": file_data}} #Prepare image part for Gemini elif file_type == 'csv': user_message = {"inline_data": {"mime_type": "text/csv", "data": file_data}} #Prepare csv part except Exception as e: print(f"Error reading file: {e}") messages.append(ChatMessage(role="assistant", content=f"Error reading file: {e}")) yield messages return else: messages.append(ChatMessage(role="assistant", content="Sorry, I cannot understand this input format. Please use text or upload a valid file.")) # More informative error yield messages return try: # Format chat history for Gemini chat_history = format_chat_history(messages) # Initialize Gemini chat chat = model.start_chat(history=chat_history) response = chat.send_message(user_message, stream=True) #Send the message part as is # Initialize buffers and flags - same as before thought_buffer = "" response_buffer = "" thinking_complete = False # Add initial thinking message - same as before messages.append( ChatMessage( role="assistant", content="", metadata={"title": "⚙️ Thinking: *The thoughts produced by the model are experimental"} ) ) for chunk in response: #streaming logic - same as before parts = chunk.candidates[0].content.parts current_chunk = parts[0].text if len(parts) == 2 and not thinking_complete: # Complete thought and start response thought_buffer += current_chunk print(f"\n=== Complete Thought ===\n{thought_buffer}") messages[-1] = ChatMessage( role="assistant", content=thought_buffer, metadata={"title": "⚙️ Thinking: *The thoughts produced by the model are experimental"} ) yield messages # Start response response_buffer = parts[1].text print(f"\n=== Starting Response ===\n{response_buffer}") messages.append( ChatMessage( role="assistant", content=response_buffer ) ) thinking_complete = True elif thinking_complete: # Stream response response_buffer += current_chunk print(f"\n=== Response Chunk ===\n{current_chunk}") messages[-1] = ChatMessage( role="assistant", content=response_buffer ) else: # Stream thinking thought_buffer += current_chunk print(f"\n=== Thinking Chunk ===\n{current_chunk}") messages[-1] = ChatMessage( role="assistant", content=thought_buffer, metadata={"title": "⚙️ Thinking: *The thoughts produced by the model are experimental"} ) #time.sleep(0.05) #Optional: Uncomment this line to add a slight delay for debugging/visualization of streaming. Remove for final version yield messages print(f"\n=== Final Response ===\n{response_buffer}") except Exception as e: print(f"\n=== Error ===\n{str(e)}") messages.append( ChatMessage( role="assistant", content=f"I apologize, but I encountered an error: {str(e)}" ) ) yield messages def user_message(message_text, file_upload, history: list) -> tuple[str, None, list]: """Adds user message to chat history""" msg = message_text if message_text else file_upload history.append(ChatMessage(role="user", content=msg if isinstance(msg, str) else msg.name)) #Store message or filename in history. return "", None, history #clear both input fields # Create the Gradio interface with gr.Blocks(theme=gr.themes.Soft(primary_hue="teal", secondary_hue="slate", neutral_hue="neutral")) as demo: gr.Markdown("# Gemini 2.0 Flash 'Thinking' Chatbot 💭") chatbot = gr.Chatbot( type="messages", label="Gemini2.0 'Thinking' Chatbot (Streaming Output)", #Label now indicates streaming render_markdown=True, scale=1, avatar_images=(None,"https://lh3.googleusercontent.com/oxz0sUBF0iYoN4VvhqWTmux-cxfD1rxuYkuFEfm1SFaseXEsjjE4Je_C_V3UQPuJ87sImQK3HfQ3RXiaRnQetjaZbjJJUkiPL5jFJ1WRl5FKJZYibUA=w214-h214-n-nu") ) with gr.Row(equal_height=True): input_box = gr.Textbox( lines=1, label="Chat Message", placeholder="Type your message here...", scale=3 ) file_upload = gr.File(label="Upload File", file_types=["image", ".csv"], scale=2) # Allow image and CSV files clear_button = gr.Button("Clear Chat", scale=1) # Add example prompts example_prompts = [ ["Write a short poem about the sunset."], ["Explain the theory of relativity in simple terms."], ["If a train leaves Chicago at 6am traveling at 60mph, and another train leaves New York at 8am traveling at 80mph, at what time will they meet?"], ["Summarize the plot of Hamlet."], ["Write a haiku about a cat."] ] gr.Examples( examples=example_prompts, inputs=[input_box], label="Examples: Get Gemini to show its thinking process with these prompts!", examples_per_page=5 # Adjust as needed ) # Set up event handlers msg_store = gr.State("") # Store for preserving user message input_box.submit( user_message, inputs=[input_box, file_upload, chatbot], outputs=[input_box, file_upload, chatbot], queue=False ).then( stream_gemini_response, inputs=[input_box, chatbot], # Input either from text box or file, logic inside stream_gemini_response outputs=chatbot ) file_upload.upload( user_message, inputs=[input_box, file_upload, chatbot], # even textbox is input here so clearing both will work outputs=[input_box, file_upload, chatbot], queue=False ).then( stream_gemini_response, inputs=[file_upload, chatbot], # Input is now the uploaded file. outputs=chatbot ) clear_button.click( lambda: ([], "", ""), outputs=[chatbot, input_box, msg_store], queue=False ) gr.Markdown( # Description moved to the bottom """


--- ### About this Chatbot This chatbot demonstrates the experimental 'thinking' capability of the **Gemini 2.0 Flash** model. You can observe the model's thought process as it generates responses, displayed with the "⚙️ Thinking" prefix. **Try out the example prompts below to see Gemini in action!** **Key Features:** * Powered by Google's **Gemini 2.0 Flash** model. * Shows the model's **thoughts** before the final answer (experimental feature). * Supports **conversation history** for multi-turn chats. * Supports **Image and CSV file uploads** for analysis. * Uses **streaming** for a more interactive experience. **Instructions:** 1. Type your message in the input box or Upload a file below. 2. Press Enter/Submit or Upload to send. 3. Observe the chatbot's "Thinking" process followed by the final response. 4. Use the "Clear Chat" button to start a new conversation. *Please note*: The 'thinking' feature is experimental and the quality of thoughts may vary. File analysis capabilities may be limited depending on the model's experimental features. """ ) # Launch the interface if __name__ == "__main__": demo.launch(debug=True)