# 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, "" # Clean and professional CSS custom_css = """ /* Clean gradient background */ .gradio-container { background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); min-height: 100vh; padding: 20px; } /* Main container styling */ .container { max-width: 1200px; margin: 0 auto; } /* Chat container with subtle glassmorphism */ #component-0 { background: rgba(255, 255, 255, 0.9); backdrop-filter: blur(10px); border-radius: 15px; border: 1px solid rgba(255, 255, 255, 0.3); box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.1); padding: 20px; } /* Chatbot styling */ .chatbot { background: rgba(255, 255, 255, 0.95) !important; border-radius: 12px !important; border: 1px solid #e0e0e0 !important; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05) !important; } /* Message bubbles */ .message { border-radius: 10px !important; padding: 12px 16px !important; margin: 8px 0 !important; } .user { background-color: #4a5568 !important; color: white !important; margin-left: 20% !important; } .bot { background-color: #f7fafc !important; color: #2d3748 !important; margin-right: 20% !important; border: 1px solid #e2e8f0 !important; } /* Input styling */ .textbox { border-radius: 8px !important; border: 2px solid #e2e8f0 !important; background: white !important; font-size: 16px !important; } .textbox:focus { border-color: #4a5568 !important; box-shadow: 0 0 0 3px rgba(74, 85, 104, 0.1) !important; } /* Checkbox styling */ .checkbox-group { background: rgba(255, 255, 255, 0.8) !important; border-radius: 8px !important; padding: 10px !important; margin: 10px 0 !important; } /* Button styling */ button { background: #4a5568 !important; color: white !important; border: none !important; border-radius: 8px !important; padding: 10px 20px !important; font-weight: 600 !important; transition: all 0.2s ease !important; } button:hover { background: #2d3748 !important; transform: translateY(-1px) !important; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15) !important; } /* Title styling */ h1, h2 { color: #2d3748 !important; } /* Remove excessive shadows and effects */ * { text-shadow: none !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.HTML( """

🤖 AI Chat Assistant : Mistral-Devstral

with Web Search

badge badge badge
""" ) 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 message here...", label="Message", scale=4, lines=1 ) submit_btn = gr.Button("Send", scale=1, variant="primary") with gr.Row(): use_web_search = gr.Checkbox( label="🔍 Enable Web Search", value=True, scale=2 ) clear_btn = gr.Button("Clear Chat", scale=1) # Event handlers def submit_message(msg, history, search): return openrouter_chat(msg, history, search) msg_in.submit( submit_message, inputs=[msg_in, chatbot, use_web_search], outputs=[chatbot, msg_in] ) submit_btn.click( submit_message, 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()