import gradio as gr from openai import OpenAI import requests import json from typing import List, Dict, Optional, Tuple import random class GifChatBot: def __init__(self): self.openai_client = None self.giphy_key = None self.chat_history = [] self.is_initialized = False def setup_keys(self, openai_key: str, giphy_key: str) -> str: """Initialize API clients with user's keys""" try: self.openai_client = OpenAI(api_key=openai_key) self.giphy_key = giphy_key # Test both keys self._test_giphy_key() self._test_openai_key() self.is_initialized = True return "✅ Setup successful! Let's chat!" except Exception as error: self.is_initialized = False return f"❌ Error setting up: {str(error)}" def _test_giphy_key(self): """Test if GIPHY key is valid""" response = requests.get( "https://api.giphy.com/v1/gifs/trending", params={"api_key": self.giphy_key, "limit": 1} ) if response.status_code != 200: raise Exception("Invalid GIPHY API key") def _test_openai_key(self): """Test if OpenAI key is valid""" try: self.openai_client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "test"}], max_tokens=5 ) except Exception: raise Exception("Invalid OpenAI API key") def get_gif(self, search_query: str) -> Optional[str]: """Search GIPHY with natural, contextual queries""" try: # Keep the search query simple and contextual params = { 'api_key': self.giphy_key, 'q': search_query, 'limit': 15, 'rating': 'pg-13', 'bundle': 'messaging_non_clips' } response = requests.get( "https://api.giphy.com/v1/gifs/search", params=params ) if response.status_code == 200: data = response.json() if data["data"]: # Filter for good quality GIFs good_gifs = [ gif for gif in data["data"] if self._is_good_quality_gif(gif) ] if good_gifs: return random.choice(good_gifs)["images"]["original"]["url"] # Fallback to trending if no good matches return self._get_trending_gif() except Exception as error: print(f"GIPHY error: {error}") return None def _is_good_quality_gif(self, gif: Dict) -> bool: """Check if a GIF meets quality criteria""" try: # Avoid very long GIFs if int(gif.get("images", {}).get("original", {}).get("frames", 50)) > 50: return False # Check if size is reasonable (under 2MB) if int(gif.get("images", {}).get("original", {}).get("size", 0)) > 2000000: return False return True except: return True def _get_trending_gif(self) -> Optional[str]: """Get a trending GIF as fallback""" try: response = requests.get( "https://api.giphy.com/v1/gifs/trending", params={ 'api_key': self.giphy_key, 'limit': 25, 'rating': 'pg-13', 'bundle': 'messaging_non_clips' } ) if response.status_code == 200: data = response.json() if data["data"]: return random.choice(data["data"])["images"]["original"]["url"] except Exception as error: print(f"Error getting trending GIF: {error}") return None def reset_chat(self) -> Tuple[str, List[List[str]]]: """Reset the chat history""" self.chat_history = [] return "", [] def chat(self, message: str, history: List[List[str]]) -> Tuple[str, List[List[str]], str]: """Main chat function with natural GIF integration""" if not self.is_initialized: return message, history, "Please set up your API keys first!" if not message.strip(): return message, history, "" try: # System message emphasizing natural GIF search system_message = """You are a supportive, empathetic friend who uses GIFs naturally in conversation. When using GIFs, keep search terms simple and contextual, like humans do: Examples of natural GIF searches: - When someone's hungry: [GIF: hungry] - When comforting someone: [GIF: comforting hug] - When celebrating: [GIF: celebration] - When confused: [GIF: confused] - When agreeing strongly: [GIF: absolutely yes] Keep your responses: 1. Empathetic and supportive 2. Context-aware (reference previous messages naturally) 3. Use GIFs that match the emotional context Use 0-1 GIFs per message unless the context really calls for more. The GIF should enhance your message, not replace it.""" # Prepare conversation history messages = [{"role": "system", "content": system_message}] for chat in history: messages.extend([ {"role": "user", "content": chat[0]}, {"role": "assistant", "content": chat[1]} ]) messages.append({"role": "user", "content": message}) # Get AI response response = self.openai_client.chat.completions.create( model="gpt-4o-mini", messages=messages, temperature=0.9, max_tokens=150 ) # Process response and insert GIFs ai_message = response.choices[0].message.content final_response = "" # Split by GIF markers and process parts = ai_message.split("[GIF:") final_response += parts[0] for part in parts[1:]: gif_desc_end = part.find("]") if gif_desc_end != -1: gif_desc = part[:gif_desc_end].strip() gif_url = self.get_gif(gif_desc) if gif_url: final_response += f"\n![GIF]({gif_url})\n" final_response += part[gif_desc_end + 1:] history.append([message, final_response]) return "", history, "" except Exception as error: error_message = f"Oops! Something went wrong: {str(error)}" return message, history, error_message def create_interface(): """Create the Gradio interface""" bot = GifChatBot() with gr.Blocks(theme=gr.themes.Soft()) as interface: gr.Markdown(""" # 🎭 Friendly Chat Bot with GIFs Chat with an empathetic AI friend who expresses themselves through GIFs! Enter your API keys below to start. """) with gr.Row(): with gr.Column(scale=1): openai_key = gr.Textbox( label="OpenAI API Key", placeholder="sk-...", type="password", scale=2 ) with gr.Column(scale=1): giphy_key = gr.Textbox( label="GIPHY API Key", placeholder="Enter your GIPHY API key", type="password", scale=2 ) setup_button = gr.Button("Set up Keys", variant="primary") setup_status = gr.Textbox(label="Setup Status") chatbot = gr.Chatbot( label="Chat", bubble_full_width=False, show_label=False, height=450 ) with gr.Row(): with gr.Column(scale=4): message_box = gr.Textbox( label="Type your message", placeholder="Say something...", show_label=False, container=False ) with gr.Column(scale=1): clear_button = gr.Button("Clear Chat", variant="secondary") error_box = gr.Textbox(label="Error Messages", visible=True) # Set up event handlers setup_button.click( bot.setup_keys, inputs=[openai_key, giphy_key], outputs=setup_status ) message_box.submit( bot.chat, inputs=[message_box, chatbot], outputs=[message_box, chatbot, error_box] ) clear_button.click( bot.reset_chat, outputs=[message_box, chatbot] ) gr.Markdown(""" ### Tips: - 🤝 Share how you're feeling - the AI will respond empathetically - 💭 The conversation is context-aware - 🎯 GIFs are chosen to match the emotional context - 🔄 Use 'Clear Chat' to start fresh """) return interface if __name__ == "__main__": demo = create_interface() demo.launch()