# 2) The actual app import os from getpass import getpass from openai import OpenAI import gradio as gr import requests import json from datetime import datetime # ——— Configure your OpenRouter key ——— OPENROUTER_API_KEY = os.getenv("OPENROUTER_API_KEY") BSEARCH_API = os.getenv("BSEARCH_API") # Check if the API key was retrieved if not OPENROUTER_API_KEY: print("Error: OPENROUTER_API_KEY not found in environment.") print("Please set your API key in the environment as 'OPENROUTER_API_KEY'.") else: client = OpenAI( base_url="https://openrouter.ai/api/v1", api_key=OPENROUTER_API_KEY, ) # Brave Search function def brave_search(query): """Perform a web search using Brave Search API.""" if not BSEARCH_API: return "Error: BSEARCH_API not found in environment. Please set your Brave Search API key." try: headers = { "Accept": "application/json", "X-Subscription-Token": BSEARCH_API } # Brave Search API endpoint url = "https://api.search.brave.com/res/v1/web/search" params = { "q": query, "count": 5 # Number of results to return } response = requests.get(url, headers=headers, params=params) response.raise_for_status() data = response.json() # Format the search results results = [] if "web" in data and "results" in data["web"]: for idx, result in enumerate(data["web"]["results"][:5], 1): title = result.get("title", "No title") url = result.get("url", "") description = result.get("description", "No description") results.append(f"{idx}. **{title}**\n URL: {url}\n {description}\n") if results: return "🔍 **Web Search Results:**\n\n" + "\n".join(results) else: return "No search results found." except Exception as e: return f"Search error: {str(e)}" def openrouter_chat(user_message, history, use_web_search): """Send user_message and history to mistralai/devstral-small:free and append to history.""" history = history or [] # If web search is enabled, perform search first search_context = "" if use_web_search and user_message.strip(): search_results = brave_search(user_message) search_context = f"\n\n{search_results}\n\nBased on the above search results, please answer the following question:\n" # Add search results to history as a system message history.append(("🔍 Web Search Query", user_message)) history.append(("🌐 Search Results", search_results)) # Build the messages list from the history and the current user message messages_for_api = [] # Add system message if web search was used if search_context: messages_for_api.append({ "role": "system", "content": "You are a helpful assistant. When web search results are provided, incorporate them into your response to give accurate and up-to-date information." }) for human_message, ai_message in history[:-2] if use_web_search else history: # Exclude search entries from API messages if not human_message.startswith("🔍") and not human_message.startswith("🌐"): messages_for_api.append({"role": "user", "content": human_message}) if ai_message is not None: messages_for_api.append({"role": "assistant", "content": ai_message}) # Add the current user message with search context if applicable current_message = search_context + user_message if search_context else user_message messages_for_api.append({"role": "user", "content": current_message}) try: # Call the model with the mistralai/Devstral-Small-2505 for full conversation history resp = client.chat.completions.create( model="mistralai/devstral-small:free", messages=messages_for_api, # you can tweak max_tokens, temperature, etc. here ) bot_reply = resp.choices[0].message.content # Append the user message and bot reply to the history for Gradio display history.append((user_message, bot_reply)) except Exception as e: # Handle potential errors and append an error message to the history history.append((user_message, f"Error: {e}")) return history, "" # Enhanced CSS with gradient background and visual improvements custom_css = """ /* Gradient background */ .gradio-container { background: linear-gradient(135deg, #667eea 0%, #764ba2 25%, #f093fb 50%, #4facfe 75%, #00f2fe 100%); min-height: 100vh; padding: 20px; } /* Main container styling */ .container { max-width: 1200px; margin: 0 auto; } /* Chat container with glassmorphism effect */ #component-0 { background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(10px); border-radius: 20px; border: 1px solid rgba(255, 255, 255, 0.2); box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37); padding: 20px; } /* Chatbot styling */ .chatbot { background: rgba(255, 255, 255, 0.9) !important; border-radius: 15px !important; border: none !important; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1) !important; } /* Message bubbles */ .message { border-radius: 10px !important; padding: 12px 16px !important; margin: 8px 0 !important; } .user { background-color: #667eea !important; color: white !important; margin-left: 20% !important; } .bot { background-color: #f0f0f0 !important; color: #333 !important; margin-right: 20% !important; } /* Input styling */ .textbox { border-radius: 10px !important; border: 2px solid #667eea !important; background: rgba(255, 255, 255, 0.95) !important; font-size: 16px !important; } .textbox:focus { border-color: #764ba2 !important; box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.2) !important; } /* Checkbox styling */ .checkbox-group { background: rgba(255, 255, 255, 0.8) !important; border-radius: 10px !important; padding: 10px !important; margin: 10px 0 !important; } /* Button styling */ button { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; color: white !important; border: none !important; border-radius: 10px !important; padding: 10px 20px !important; font-weight: bold !important; transition: all 0.3s ease !important; } button:hover { transform: translateY(-2px) !important; box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3) !important; } /* Title styling */ h1, h2 { color: white !important; text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3) !important; } /* Markdown styling */ .markdown-text { color: white !important; text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3) !important; } /* Web search results styling */ .message:has(.search-results) { background: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%) !important; border-left: 4px solid #ff6b6b !important; } /* Responsive design */ @media (max-width: 768px) { .user { margin-left: 10% !important; } .bot { margin-right: 10% !important; } } """ with gr.Blocks(css=custom_css, theme=gr.themes.Soft()) as demo: gr.Markdown( """

🦜🔗 AI Chat Assistant

Powered by Devstral-Small with Brave Search Integration

""", elem_id="header" ) with gr.Row(): with gr.Column(scale=4): chatbot = gr.Chatbot( label="💬 Chat History", height=500, elem_classes=["chatbot"], bubble_full_width=False ) with gr.Row(): msg_in = gr.Textbox( placeholder="Type your question here… (Press Enter to send)", label="✍️ Your Message", scale=4, lines=2 ) with gr.Row(): use_web_search = gr.Checkbox( label="🔍 Enable Web Search", value=True, info="Search the web for current information before answering" ) clear_btn = gr.Button("🗑️ Clear Chat", size="sm") with gr.Column(scale=1): gr.Markdown( """ ### 📋 Features - 💬 Real-time chat with AI - 🔍 Web search integration - 📜 Conversation history - 🎨 Beautiful gradient UI ### 🔧 Tips - Enable web search for current events - Ask follow-up questions - Clear chat to start fresh ### ⚡ Shortcuts - Enter: Send message - Shift+Enter: New line """, elem_classes=["markdown-text"] ) # Event handlers msg_in.submit( openrouter_chat, inputs=[msg_in, chatbot, use_web_search], outputs=[chatbot, msg_in] ) clear_btn.click(lambda: ([], ""), outputs=[chatbot, msg_in]) # Add example queries gr.Examples( examples=[ ["What's the latest news about AI?", True], ["Explain quantum computing in simple terms", False], ["What's the weather like today?", True], ["Write a Python function to sort a list", False], ["What are the current stock market trends?", True] ], inputs=[msg_in, use_web_search], label="💡 Example Queries" ) demo.launch()