Spaces:
Running
Running
| # 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( | |
| """ | |
| <div style="text-align: center; padding: 20px;"> | |
| <h1 style="font-size: 3em; margin-bottom: 10px;">🦜🔗 AI Chat Assistant</h1> | |
| <h2 style="font-size: 1.5em; opacity: 0.9;">Powered by Devstral-Small with Brave Search Integration</h2> | |
| </div> | |
| """, | |
| 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() |