|
|
from flask import Flask, render_template_string, request, jsonify |
|
|
from chatbot_memory import get_response |
|
|
import os |
|
|
|
|
|
app = Flask(__name__) |
|
|
|
|
|
|
|
|
def get_html_template(): |
|
|
|
|
|
|
|
|
return """<!DOCTYPE html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="UTF-8" /> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
|
|
<title>Voyager – Your AI Travel Companion</title> |
|
|
<style> |
|
|
/* RESET & BASE STYLE */ |
|
|
* { |
|
|
margin: 0; |
|
|
padding: 0; |
|
|
box-sizing: border-box; |
|
|
} |
|
|
|
|
|
body { |
|
|
font-family: 'Poppins', 'Segoe UI', sans-serif; |
|
|
background: radial-gradient(circle at top, #111827, #0a0f16); |
|
|
color: #fff; |
|
|
height: 100vh; |
|
|
display: flex; |
|
|
justify-content: center; |
|
|
align-items: center; |
|
|
overflow: hidden; |
|
|
} |
|
|
|
|
|
/* BACKGROUND MOTION EFFECT */ |
|
|
.background { |
|
|
position: absolute; |
|
|
width: 100%; |
|
|
height: 100%; |
|
|
overflow: hidden; |
|
|
z-index: 0; |
|
|
} |
|
|
|
|
|
.gradient-circle { |
|
|
position: absolute; |
|
|
border-radius: 50%; |
|
|
filter: blur(150px); |
|
|
opacity: 0.5; |
|
|
animation: float 10s ease-in-out infinite alternate; |
|
|
} |
|
|
|
|
|
.circle1 { |
|
|
background: #4fa2f0; |
|
|
width: 400px; |
|
|
height: 400px; |
|
|
top: -100px; |
|
|
left: -100px; |
|
|
} |
|
|
|
|
|
.circle2 { |
|
|
background: #00c6ff; |
|
|
width: 500px; |
|
|
height: 500px; |
|
|
bottom: -200px; |
|
|
right: -150px; |
|
|
animation-delay: 2s; |
|
|
} |
|
|
|
|
|
@keyframes float { |
|
|
0% { |
|
|
transform: translateY(0); |
|
|
} |
|
|
100% { |
|
|
transform: translateY(40px); |
|
|
} |
|
|
} |
|
|
|
|
|
/* CHAT CONTAINER */ |
|
|
.chat-container { |
|
|
position: relative; |
|
|
width: 90%; |
|
|
max-width: 900px; |
|
|
height: 85vh; |
|
|
display: flex; |
|
|
flex-direction: column; |
|
|
backdrop-filter: blur(20px); |
|
|
background: rgba(255, 255, 255, 0.1); |
|
|
border-radius: 25px; |
|
|
border: 1px solid rgba(255, 255, 255, 0.2); |
|
|
box-shadow: 0 0 40px rgba(79, 162, 240, 0.2); |
|
|
z-index: 1; |
|
|
overflow: hidden; |
|
|
} |
|
|
|
|
|
/* HEADER */ |
|
|
.chat-header { |
|
|
background: linear-gradient(135deg, rgba(79, 162, 240, 0.25), rgba(79, 162, 240, 0.15)); //header background |
|
|
padding: 25px; |
|
|
text-align: center; |
|
|
color: #fff; |
|
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1); |
|
|
} |
|
|
|
|
|
.chat-header h1 { |
|
|
font-size: 2rem; |
|
|
font-weight: 700; |
|
|
letter-spacing: 1px; |
|
|
} |
|
|
|
|
|
.chat-header h2 { |
|
|
font-size: 1.1rem; |
|
|
font-weight: 400; |
|
|
opacity: 0.8; |
|
|
} |
|
|
|
|
|
/* MESSAGES */ |
|
|
.chat-messages { |
|
|
flex: 1; |
|
|
padding: 25px; |
|
|
overflow-y: auto; |
|
|
display: flex; |
|
|
flex-direction: column; |
|
|
gap: 20px; |
|
|
scroll-behavior: smooth; |
|
|
} |
|
|
|
|
|
.message { |
|
|
display: flex; |
|
|
align-items: flex-start; |
|
|
gap: 12px; |
|
|
max-width: 80%; |
|
|
animation: fadeIn 0.4s ease-out; |
|
|
} |
|
|
|
|
|
.message.user { |
|
|
align-self: flex-end; |
|
|
flex-direction: row-reverse; |
|
|
} |
|
|
|
|
|
.message.bot { |
|
|
align-self: flex-start; |
|
|
} |
|
|
|
|
|
.avatar { |
|
|
width: 45px; |
|
|
height: 45px; |
|
|
border-radius: 50%; |
|
|
display: flex; |
|
|
justify-content: center; |
|
|
align-items: center; |
|
|
font-weight: bold; |
|
|
color: white; |
|
|
box-shadow: 0 0 15px rgba(255, 255, 255, 0.1); |
|
|
} |
|
|
|
|
|
.avatar.user { |
|
|
background: linear-gradient(135deg, #4fa2f0, #16425b); |
|
|
} |
|
|
|
|
|
.avatar.bot { |
|
|
background: linear-gradient(135deg, #00c6ff, #0072ff); |
|
|
} |
|
|
|
|
|
.message-content { |
|
|
padding: 15px 20px; |
|
|
border-radius: 18px; |
|
|
line-height: 1.6; |
|
|
font-size: 0.95rem; |
|
|
word-wrap: break-word; |
|
|
backdrop-filter: blur(10px); |
|
|
} |
|
|
|
|
|
.message.user .message-content { |
|
|
background: linear-gradient(135deg, #16425b, #4fa2f0); |
|
|
color: #fff; |
|
|
box-shadow: 0 0 15px rgba(79, 162, 240, 0.4); |
|
|
} |
|
|
|
|
|
.message.bot .message-content { |
|
|
background: rgba(255, 255, 255, 0.1); |
|
|
color: #e2e8f0; |
|
|
border: 1px solid rgba(255, 255, 255, 0.1); |
|
|
} |
|
|
|
|
|
/* INPUT AREA */ |
|
|
.chat-input-container { |
|
|
padding: 20px 25px; |
|
|
background: rgba(255, 255, 255, 0.08); |
|
|
border-top: 1px solid rgba(255, 255, 255, 0.1); |
|
|
display: flex; |
|
|
gap: 10px; |
|
|
align-items: center; |
|
|
} |
|
|
|
|
|
.chat-input { |
|
|
flex: 1; |
|
|
background: rgba(255, 255, 255, 0.15); |
|
|
color: #fff; |
|
|
padding: 14px 18px; |
|
|
border: none; |
|
|
border-radius: 25px; |
|
|
font-size: 1rem; |
|
|
outline: none; |
|
|
resize: none; |
|
|
min-height: 24px; |
|
|
transition: background 0.3s ease; |
|
|
} |
|
|
|
|
|
.chat-input::placeholder { |
|
|
color: #cbd5e1; |
|
|
} |
|
|
|
|
|
.chat-input:focus { |
|
|
background: rgba(255, 255, 255, 0.25); |
|
|
} |
|
|
|
|
|
.send-button { |
|
|
width: 50px; |
|
|
height: 50px; |
|
|
border: none; |
|
|
border-radius: 50%; |
|
|
background: linear-gradient(135deg, #00c6ff, #0072ff); |
|
|
color: white; |
|
|
font-size: 1.4rem; |
|
|
cursor: pointer; |
|
|
transition: transform 0.2s ease, box-shadow 0.2s ease; |
|
|
} |
|
|
|
|
|
.send-button:hover { |
|
|
transform: translateY(-3px); |
|
|
box-shadow: 0 0 20px rgba(0, 198, 255, 0.5); |
|
|
} |
|
|
|
|
|
.typing-indicator { |
|
|
display: none; |
|
|
align-items: center; |
|
|
gap: 8px; |
|
|
color: #a0aec0; |
|
|
font-size: 0.9rem; |
|
|
font-style: italic; |
|
|
} |
|
|
|
|
|
.typing-dots { |
|
|
display: flex; |
|
|
gap: 4px; |
|
|
} |
|
|
|
|
|
.typing-dots span { |
|
|
width: 6px; |
|
|
height: 6px; |
|
|
background: #4fa2f0; |
|
|
border-radius: 50%; |
|
|
animation: bounce 1.4s infinite ease-in-out; |
|
|
} |
|
|
|
|
|
.typing-dots span:nth-child(1) { |
|
|
animation-delay: -0.32s; |
|
|
} |
|
|
|
|
|
.typing-dots span:nth-child(2) { |
|
|
animation-delay: -0.16s; |
|
|
} |
|
|
|
|
|
.welcome-message { |
|
|
text-align: center; |
|
|
color: #e2e8f0; |
|
|
opacity: 0.9; |
|
|
line-height: 1.7; |
|
|
margin-top: 60px; |
|
|
font-size: 1.1rem; |
|
|
} |
|
|
|
|
|
.welcome-message h2 { |
|
|
color: #fff; |
|
|
margin-bottom: 12px; |
|
|
font-size: 1.4rem; |
|
|
letter-spacing: 0.5px; |
|
|
} |
|
|
|
|
|
@keyframes fadeIn { |
|
|
from { |
|
|
opacity: 0; |
|
|
transform: translateY(10px); |
|
|
} |
|
|
to { |
|
|
opacity: 1; |
|
|
transform: translateY(0); |
|
|
} |
|
|
} |
|
|
|
|
|
@keyframes bounce { |
|
|
0%, 80%, 100% { |
|
|
transform: scale(0); |
|
|
} |
|
|
40% { |
|
|
transform: scale(1); |
|
|
} |
|
|
} |
|
|
|
|
|
@media (max-width: 768px) { |
|
|
.chat-container { |
|
|
height: 100vh; |
|
|
border-radius: 0; |
|
|
} |
|
|
.chat-header h1 { |
|
|
font-size: 1.6rem; |
|
|
} |
|
|
.message { |
|
|
max-width: 90%; |
|
|
} |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
|
|
|
<body> |
|
|
<div class="background"> |
|
|
<div class="gradient-circle circle1"></div> |
|
|
<div class="gradient-circle circle2"></div> |
|
|
</div> |
|
|
|
|
|
<div class="chat-container"> |
|
|
<div class="chat-header"> |
|
|
<h1>🌍 Voyager</h1> |
|
|
<h2>Your AI Travel Companion</h2> |
|
|
</div> |
|
|
|
|
|
<div class="chat-messages" id="chatMessages"> |
|
|
<div class="welcome-message"> |
|
|
<h2>Welcome to Voyager!</h2> |
|
|
<p>Plan your next adventure effortlessly. |
|
|
Just tell me your destination! ✈️</p> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="typing-indicator" id="typingIndicator"> |
|
|
<span>Voyager is thinking</span> |
|
|
<div class="typing-dots"> |
|
|
<span></span><span></span><span></span> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="chat-input-container"> |
|
|
<textarea |
|
|
class="chat-input" |
|
|
id="chatInput" |
|
|
placeholder="Type your travel idea..." |
|
|
rows="1" |
|
|
></textarea> |
|
|
<button class="send-button" id="sendButton" onclick="sendMessage()">➤</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
let chatHistory = []; |
|
|
|
|
|
const chatInput = document.getElementById('chatInput'); |
|
|
chatInput.addEventListener('input', function() { |
|
|
this.style.height = 'auto'; |
|
|
this.style.height = Math.min(this.scrollHeight, 120) + 'px'; |
|
|
}); |
|
|
|
|
|
chatInput.addEventListener('keydown', function(e) { |
|
|
if (e.key === 'Enter' && !e.shiftKey) { |
|
|
e.preventDefault(); |
|
|
sendMessage(); |
|
|
} |
|
|
}); |
|
|
|
|
|
function addMessage(content, isUser = false) { |
|
|
const messagesContainer = document.getElementById('chatMessages'); |
|
|
const welcomeMessage = messagesContainer.querySelector('.welcome-message'); |
|
|
if (welcomeMessage && isUser) welcomeMessage.remove(); |
|
|
|
|
|
const messageDiv = document.createElement('div'); |
|
|
messageDiv.className = `message ${isUser ? 'user' : 'bot'}`; |
|
|
messageDiv.innerHTML = ` |
|
|
<div class="avatar ${isUser ? 'user' : 'bot'}">${isUser ? 'U' : '🤖'}</div> |
|
|
<div class="message-content">${content}</div> |
|
|
`; |
|
|
messagesContainer.appendChild(messageDiv); |
|
|
messagesContainer.scrollTop = messagesContainer.scrollHeight; |
|
|
return messageDiv; |
|
|
} |
|
|
|
|
|
function showTypingIndicator() { |
|
|
document.getElementById('typingIndicator').style.display = 'flex'; |
|
|
document.getElementById('chatMessages').scrollTop = |
|
|
document.getElementById('chatMessages').scrollHeight; |
|
|
} |
|
|
|
|
|
function hideTypingIndicator() { |
|
|
document.getElementById('typingIndicator').style.display = 'none'; |
|
|
} |
|
|
|
|
|
async function sendMessage() { |
|
|
const input = document.getElementById('chatInput'); |
|
|
const sendButton = document.getElementById('sendButton'); |
|
|
const message = input.value.trim(); |
|
|
if (!message) return; |
|
|
|
|
|
addMessage(message, true); |
|
|
input.value = ''; |
|
|
input.style.height = 'auto'; |
|
|
sendButton.disabled = true; |
|
|
showTypingIndicator(); |
|
|
|
|
|
try { |
|
|
const response = await fetch('/chat', { |
|
|
method: 'POST', |
|
|
headers: { 'Content-Type': 'application/json' }, |
|
|
body: JSON.stringify({ message, history: chatHistory }), |
|
|
}); |
|
|
|
|
|
if (!response.ok) throw new Error('Network error'); |
|
|
const data = await response.json(); |
|
|
addMessage(data.response); |
|
|
|
|
|
chatHistory.push({ role: 'user', content: message }); |
|
|
chatHistory.push({ role: 'assistant', content: data.response }); |
|
|
} catch (error) { |
|
|
console.error(error); |
|
|
addMessage('⚠️ Oops! Something went wrong. Please try again.'); |
|
|
} finally { |
|
|
hideTypingIndicator(); |
|
|
sendButton.disabled = false; |
|
|
input.focus(); |
|
|
} |
|
|
} |
|
|
|
|
|
window.addEventListener('load', () => chatInput.focus()); |
|
|
</script> |
|
|
</body> |
|
|
</html> |
|
|
""" |
|
|
|
|
|
@app.route('/') |
|
|
def home(): |
|
|
return render_template_string(get_html_template()) |
|
|
|
|
|
@app.route('/chat', methods=['POST']) |
|
|
def chat(): |
|
|
try: |
|
|
data = request.get_json() |
|
|
message = data.get('message', '') |
|
|
history = data.get('history', []) |
|
|
|
|
|
|
|
|
response = get_response(message, history) |
|
|
response = response.replace('\n', '<br>') |
|
|
|
|
|
return jsonify({'response': response}) |
|
|
|
|
|
except Exception as e: |
|
|
print(f"Error: {e}") |
|
|
return jsonify({'error': 'Something went wrong'}), 500 |
|
|
|
|
|
if __name__ == "__main__": |
|
|
app.run(debug=True, host='0.0.0.0', port=7860) |