aiqcamp's picture
Update app.py
03f48f0 verified
raw
history blame
10 kB
# 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()