Spaces:
Runtime error
Runtime error
Commit
·
cfa6bee
1
Parent(s):
32f3d95
test
Browse files- Dockerfile +13 -0
- main.py +123 -0
- requirements.txt +10 -0
- templates/index.html +427 -0
Dockerfile
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM python:3.9
|
| 2 |
+
|
| 3 |
+
RUN useradd -m -u 1000 user
|
| 4 |
+
USER user
|
| 5 |
+
ENV PATH="/home/user/.local/bin:$PATH"
|
| 6 |
+
|
| 7 |
+
WORKDIR /app
|
| 8 |
+
|
| 9 |
+
COPY --chown=user ./requirements.txt requirements.txt
|
| 10 |
+
RUN pip install --no-cache-dir --upgrade -r requirements.txt
|
| 11 |
+
|
| 12 |
+
COPY --chown=user . /app
|
| 13 |
+
CMD ["gunicorn", "-b", "0.0.0.0:7860", "main:app"]
|
main.py
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from flask import Flask, render_template, request, jsonify
|
| 3 |
+
from langchain.document_loaders import PyPDFLoader
|
| 4 |
+
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
| 5 |
+
from langchain.embeddings import HuggingFaceEmbeddings
|
| 6 |
+
from langchain.vectorstores import Chroma
|
| 7 |
+
from langchain.chains import ConversationalRetrievalChain
|
| 8 |
+
from langchain.prompts import PromptTemplate
|
| 9 |
+
from langchain.chat_models import ChatOpenAI
|
| 10 |
+
from huggingface_hub import InferenceClient
|
| 11 |
+
|
| 12 |
+
# Ensure HF_TOKEN is set
|
| 13 |
+
HF_TOKEN = os.getenv("HF_TOKEN")
|
| 14 |
+
if not HF_TOKEN:
|
| 15 |
+
raise ValueError("HF_TOKEN environment variable not set.")
|
| 16 |
+
|
| 17 |
+
# Constants
|
| 18 |
+
PERSIST_DIR = "db"
|
| 19 |
+
PDF_DIRECTORY = 'data'
|
| 20 |
+
os.makedirs(PDF_DIRECTORY, exist_ok=True) # Ensure PDF directory exists
|
| 21 |
+
os.makedirs(PERSIST_DIR, exist_ok=True) # Ensure persistence directory exists
|
| 22 |
+
|
| 23 |
+
# Hugging Face Model Setup
|
| 24 |
+
repo_id = "meta-llama/Meta-Llama-3-8B-Instruct"
|
| 25 |
+
llm_client = InferenceClient(
|
| 26 |
+
model=repo_id,
|
| 27 |
+
token=HF_TOKEN,
|
| 28 |
+
)
|
| 29 |
+
|
| 30 |
+
# Embedding Model
|
| 31 |
+
embedding_model = HuggingFaceEmbeddings(model_name="BAAI/bge-small-en-v1.5")
|
| 32 |
+
|
| 33 |
+
# Initialize Flask App
|
| 34 |
+
app = Flask(__name__)
|
| 35 |
+
|
| 36 |
+
# Chat history
|
| 37 |
+
chat_history = []
|
| 38 |
+
|
| 39 |
+
# Load and Prepare Documents
|
| 40 |
+
def data_ingestion_from_directory():
|
| 41 |
+
# Load PDFs
|
| 42 |
+
documents = []
|
| 43 |
+
for filename in os.listdir(PDF_DIRECTORY):
|
| 44 |
+
if filename.endswith('.pdf'):
|
| 45 |
+
loader = PyPDFLoader(os.path.join(PDF_DIRECTORY, filename))
|
| 46 |
+
documents.extend(loader.load())
|
| 47 |
+
|
| 48 |
+
# Split into Chunks
|
| 49 |
+
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
|
| 50 |
+
docs = text_splitter.split_documents(documents)
|
| 51 |
+
|
| 52 |
+
# Create Vector Store
|
| 53 |
+
vectorstore = Chroma.from_documents(docs, embedding=embedding_model, persist_directory=PERSIST_DIR)
|
| 54 |
+
vectorstore.persist()
|
| 55 |
+
|
| 56 |
+
# Handle Queries with LangChain
|
| 57 |
+
def handle_query(query):
|
| 58 |
+
# Reload Vector Store
|
| 59 |
+
vectorstore = Chroma(persist_directory=PERSIST_DIR, embedding_function=embedding_model)
|
| 60 |
+
retriever = vectorstore.as_retriever(search_type="similarity", search_kwargs={"k": 5})
|
| 61 |
+
|
| 62 |
+
# Define Prompt Template
|
| 63 |
+
prompt_template = """
|
| 64 |
+
You are the Taj Hotel chatbot, known as Taj Hotel Helper. Your goal is to provide accurate and professional answers to user queries based on the information available about the Taj Hotel. Always respond clearly and concisely, ideally within 10-15 words. If you don't know the answer, say so politely.
|
| 65 |
+
|
| 66 |
+
Context:
|
| 67 |
+
{context}
|
| 68 |
+
|
| 69 |
+
User's Question:
|
| 70 |
+
{question}
|
| 71 |
+
|
| 72 |
+
Answer:
|
| 73 |
+
"""
|
| 74 |
+
prompt = PromptTemplate(
|
| 75 |
+
input_variables=["context", "question"],
|
| 76 |
+
template=prompt_template
|
| 77 |
+
)
|
| 78 |
+
|
| 79 |
+
# Initialize LLM
|
| 80 |
+
llm = ChatOpenAI(client=llm_client, temperature=0.1, max_tokens=512)
|
| 81 |
+
|
| 82 |
+
# Build Conversational Retrieval Chain
|
| 83 |
+
qa_chain = ConversationalRetrievalChain(
|
| 84 |
+
retriever=retriever,
|
| 85 |
+
llm=llm,
|
| 86 |
+
memory=chat_history,
|
| 87 |
+
prompt_template=prompt,
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
# Execute Query
|
| 91 |
+
response = qa_chain({"question": query})
|
| 92 |
+
return response["answer"]
|
| 93 |
+
|
| 94 |
+
# Data Ingestion
|
| 95 |
+
data_ingestion_from_directory()
|
| 96 |
+
|
| 97 |
+
# Generate Response
|
| 98 |
+
def generate_response(query):
|
| 99 |
+
try:
|
| 100 |
+
response = handle_query(query)
|
| 101 |
+
return response
|
| 102 |
+
except Exception as e:
|
| 103 |
+
return f"Error: {str(e)}"
|
| 104 |
+
|
| 105 |
+
# Routes
|
| 106 |
+
@app.route('/')
|
| 107 |
+
def index():
|
| 108 |
+
return render_template('index.html')
|
| 109 |
+
|
| 110 |
+
@app.route('/chat', methods=['POST'])
|
| 111 |
+
def chat():
|
| 112 |
+
try:
|
| 113 |
+
user_message = request.json.get("message")
|
| 114 |
+
if not user_message:
|
| 115 |
+
return jsonify({"response": "Please say something!"})
|
| 116 |
+
|
| 117 |
+
bot_response = generate_response(user_message)
|
| 118 |
+
return jsonify({"response": bot_response})
|
| 119 |
+
except Exception as e:
|
| 120 |
+
return jsonify({"response": f"An error occurred: {str(e)}"})
|
| 121 |
+
|
| 122 |
+
if __name__ == '__main__':
|
| 123 |
+
app.run(debug=True)
|
requirements.txt
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
gunicorn
|
| 2 |
+
Flask==2.3.3
|
| 3 |
+
langchain==0.0.306
|
| 4 |
+
huggingface_hub==0.19.0
|
| 5 |
+
chromadb==0.4.4
|
| 6 |
+
openai==0.29.0
|
| 7 |
+
PyPDF2==3.0.1
|
| 8 |
+
tiktoken==0.5.0
|
| 9 |
+
torch==2.0.1
|
| 10 |
+
transformers==4.34.0
|
templates/index.html
ADDED
|
@@ -0,0 +1,427 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
|
| 4 |
+
<head>
|
| 5 |
+
<meta charset="UTF-8">
|
| 6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 7 |
+
<title>Advanced Chatbot UI</title>
|
| 8 |
+
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap" rel="stylesheet">
|
| 9 |
+
<style>
|
| 10 |
+
:root {
|
| 11 |
+
--primary-color: #007bff; /* Default blue */
|
| 12 |
+
--bot-message-bg: #f0f2f5; /* Light Gray */
|
| 13 |
+
--user-message-bg: #007bff; /* User Blue */
|
| 14 |
+
--user-message-text: #fff; /* User Message White */
|
| 15 |
+
--accent-color: #6a0dad; /* Default Purple */
|
| 16 |
+
--background-color: #fff; /* Default Background White */
|
| 17 |
+
--shadow-color: rgba(0, 0, 0, 0.2); /* Default Shadow */
|
| 18 |
+
--input-bg: #f0f2f5; /* Default Input Background */
|
| 19 |
+
--input-border: #ccc; /* Default Input Border */
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
/* Themes */
|
| 23 |
+
.theme-calm-azure {
|
| 24 |
+
--background-color: #E3F2FD;
|
| 25 |
+
--bot-message-bg: #BBDEFB;
|
| 26 |
+
--user-message-bg: #2196F3;
|
| 27 |
+
--user-message-text: #FFFFFF;
|
| 28 |
+
--input-bg: #FFFFFF;
|
| 29 |
+
--input-border: #BDBDBD;
|
| 30 |
+
--accent-color: #1976D2;
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
.theme-elegant-charcoal {
|
| 34 |
+
--background-color: #263238;
|
| 35 |
+
--bot-message-bg: #37474F;
|
| 36 |
+
--user-message-bg: #FF5722;
|
| 37 |
+
--user-message-text: #FFFFFF;
|
| 38 |
+
--input-bg: #455A64;
|
| 39 |
+
--input-border: #CFD8DC;
|
| 40 |
+
--accent-color: #FF9800;
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
.theme-fresh-greenery {
|
| 44 |
+
--background-color: #E8F5E9;
|
| 45 |
+
--bot-message-bg: #C8E6C9;
|
| 46 |
+
--user-message-bg: #4CAF50;
|
| 47 |
+
--user-message-text: #FFFFFF;
|
| 48 |
+
--input-bg: #FFFFFF;
|
| 49 |
+
--input-border: #A5D6A7;
|
| 50 |
+
--accent-color: #388E3C;
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
.theme-soft-lavender {
|
| 54 |
+
--background-color: #F3E5F5;
|
| 55 |
+
--bot-message-bg: #E1BEE7;
|
| 56 |
+
--user-message-bg: #9C27B0;
|
| 57 |
+
--user-message-text: #FFFFFF;
|
| 58 |
+
--input-bg: #FFFFFF;
|
| 59 |
+
--input-border: #D1C4E9;
|
| 60 |
+
--accent-color: #7B1FA2;
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
.theme-bright-summer {
|
| 64 |
+
--background-color: #FFEB3B;
|
| 65 |
+
--bot-message-bg: #FFF9C4;
|
| 66 |
+
--user-message-bg: #F44336;
|
| 67 |
+
--user-message-text: #FFFFFF;
|
| 68 |
+
--input-bg: #FFFFFF;
|
| 69 |
+
--input-border: #FBC02D;
|
| 70 |
+
--accent-color: #C62828;
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
* {
|
| 74 |
+
margin: 0;
|
| 75 |
+
padding: 0;
|
| 76 |
+
box-sizing: border-box;
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
body {
|
| 80 |
+
font-family: 'Roboto', sans-serif;
|
| 81 |
+
background-color: var(--background-color);
|
| 82 |
+
transition: background 0.5s ease;
|
| 83 |
+
overflow: hidden; /* Prevent scroll on body */
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
.container {
|
| 87 |
+
display: flex;
|
| 88 |
+
flex-direction: column;
|
| 89 |
+
height: 100vh;
|
| 90 |
+
width: 100%;
|
| 91 |
+
overflow: hidden; /* Prevent scroll on container */
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
/* Header */
|
| 95 |
+
.header {
|
| 96 |
+
background: var(--accent-color);
|
| 97 |
+
color: #fff;
|
| 98 |
+
font-size: 1.5rem;
|
| 99 |
+
font-weight: 700;
|
| 100 |
+
text-align: center;
|
| 101 |
+
padding: 20px;
|
| 102 |
+
box-shadow: 0 4px 8px var(--shadow-color);
|
| 103 |
+
position: relative;
|
| 104 |
+
z-index: 1000;
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
/* Chatbox */
|
| 108 |
+
.chat-box {
|
| 109 |
+
flex: 1;
|
| 110 |
+
display: flex;
|
| 111 |
+
flex-direction: column;
|
| 112 |
+
overflow-y: auto;
|
| 113 |
+
padding: 20px;
|
| 114 |
+
background: linear-gradient(to bottom right, #f5f7fa, #c3cfe2);
|
| 115 |
+
border-radius: 10px;
|
| 116 |
+
margin: 20px;
|
| 117 |
+
box-shadow: 0 4px 8px var(--shadow-color);
|
| 118 |
+
position: relative;
|
| 119 |
+
z-index: 10;
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
.message {
|
| 123 |
+
max-width: 75%;
|
| 124 |
+
padding: 12px 18px;
|
| 125 |
+
border-radius: 20px;
|
| 126 |
+
box-shadow: 0 3px 6px var(--shadow-color);
|
| 127 |
+
margin-bottom: 10px;
|
| 128 |
+
opacity: 0;
|
| 129 |
+
animation: fadeIn 0.3s forwards; /* Changed to forwards for delay effect */
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
.user-message {
|
| 133 |
+
align-self: flex-end;
|
| 134 |
+
background: var(--user-message-bg);
|
| 135 |
+
color: var(--user-message-text);
|
| 136 |
+
border-radius: 15px 20px 20px 20px;
|
| 137 |
+
animation: slideInRight 0.5s forwards; /* Sliding effect on user message */
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
.bot-message {
|
| 141 |
+
align-self: flex-start;
|
| 142 |
+
background: var(--bot-message-bg);
|
| 143 |
+
color: #333;
|
| 144 |
+
border-radius: 20px 15px 20px 20px;
|
| 145 |
+
animation: slideInLeft 0.5s forwards; /* Sliding effect on bot message */
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
/* Footer */
|
| 149 |
+
.footer {
|
| 150 |
+
background: #ffffff;
|
| 151 |
+
padding: 15px;
|
| 152 |
+
display: flex;
|
| 153 |
+
justify-content: center;
|
| 154 |
+
align-items: center;
|
| 155 |
+
box-shadow: 0 -4px 8px var(--shadow-color);
|
| 156 |
+
position: relative;
|
| 157 |
+
z-index: 1000;
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
.footer input[type="text"] {
|
| 161 |
+
width: 75%;
|
| 162 |
+
padding: 15px;
|
| 163 |
+
border: 1px solid var(--input-border);
|
| 164 |
+
border-radius: 20px;
|
| 165 |
+
margin-right: 10px;
|
| 166 |
+
box-shadow: 0 2px 4px var(--shadow-color);
|
| 167 |
+
transition: border 0.3s, box-shadow 0.3s; /* Added shadow transition */
|
| 168 |
+
background-color: var(--input-bg);
|
| 169 |
+
outline: none;
|
| 170 |
+
}
|
| 171 |
+
|
| 172 |
+
.footer input[type="text"]:focus {
|
| 173 |
+
border-color: var(--accent-color);
|
| 174 |
+
box-shadow: 0 0 10px var(--accent-color);
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
button {
|
| 178 |
+
background: var(--accent-color);
|
| 179 |
+
color: #fff;
|
| 180 |
+
border: none;
|
| 181 |
+
padding: 10px 20px;
|
| 182 |
+
border-radius: 20px;
|
| 183 |
+
font-size: 1rem;
|
| 184 |
+
cursor: pointer;
|
| 185 |
+
box-shadow: 0 4px 10px var(--shadow-color);
|
| 186 |
+
transition: background 0.3s ease, transform 0.2s ease, box-shadow 0.2s ease;
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
button:hover {
|
| 190 |
+
background: #4b0082;
|
| 191 |
+
transform: scale(1.05);
|
| 192 |
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5); /* Shadow effect on hover */
|
| 193 |
+
}
|
| 194 |
+
|
| 195 |
+
/* Settings */
|
| 196 |
+
.settings {
|
| 197 |
+
display: flex;
|
| 198 |
+
justify-content: space-between;
|
| 199 |
+
align-items: center;
|
| 200 |
+
padding: 10px;
|
| 201 |
+
background: #f0f2f5;
|
| 202 |
+
box-shadow: 0 2px 5px var(--shadow-color);
|
| 203 |
+
position: relative;
|
| 204 |
+
z-index: 1000;
|
| 205 |
+
}
|
| 206 |
+
|
| 207 |
+
.color-picker,
|
| 208 |
+
.theme-toggle {
|
| 209 |
+
display: flex;
|
| 210 |
+
align-items: center;
|
| 211 |
+
position:relative;
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
.color-circle {
|
| 215 |
+
width: 20px;
|
| 216 |
+
height: 20px;
|
| 217 |
+
border-radius: 50%;
|
| 218 |
+
margin: 0 5px;
|
| 219 |
+
cursor: pointer;
|
| 220 |
+
box-shadow: 0 2px 5px var(--shadow-color);
|
| 221 |
+
transition: transform 0.3s; /* Adding circle hover effect */
|
| 222 |
+
}
|
| 223 |
+
|
| 224 |
+
.color-circle:hover {
|
| 225 |
+
transform: scale(1.2); /* Scale up on hover */
|
| 226 |
+
}
|
| 227 |
+
|
| 228 |
+
/* Animations */
|
| 229 |
+
@keyframes fadeIn {
|
| 230 |
+
from {
|
| 231 |
+
opacity: 0;
|
| 232 |
+
transform: translateY(20px);
|
| 233 |
+
}
|
| 234 |
+
|
| 235 |
+
to {
|
| 236 |
+
opacity: 1;
|
| 237 |
+
transform: translateY(0);
|
| 238 |
+
}
|
| 239 |
+
}
|
| 240 |
+
|
| 241 |
+
@keyframes slideInRight {
|
| 242 |
+
from {
|
| 243 |
+
opacity: 0;
|
| 244 |
+
transform: translateX(100%);
|
| 245 |
+
}
|
| 246 |
+
to {
|
| 247 |
+
opacity: 1;
|
| 248 |
+
transform: translateX(0);
|
| 249 |
+
}
|
| 250 |
+
}
|
| 251 |
+
|
| 252 |
+
@keyframes slideInLeft {
|
| 253 |
+
from {
|
| 254 |
+
opacity: 0;
|
| 255 |
+
transform: translateX(-100%);
|
| 256 |
+
}
|
| 257 |
+
to {
|
| 258 |
+
opacity: 1;
|
| 259 |
+
transform: translateX(0);
|
| 260 |
+
}
|
| 261 |
+
}
|
| 262 |
+
|
| 263 |
+
/* Background animation */
|
| 264 |
+
@keyframes backgroundAnimate {
|
| 265 |
+
0% {
|
| 266 |
+
background-color: rgba(255, 255, 255, 0.05);
|
| 267 |
+
}
|
| 268 |
+
50% {
|
| 269 |
+
background-color: rgba(255, 255, 255, 0.1);
|
| 270 |
+
}
|
| 271 |
+
100% {
|
| 272 |
+
background-color: rgba(255, 255, 255, 0.05);
|
| 273 |
+
}
|
| 274 |
+
}
|
| 275 |
+
|
| 276 |
+
/* VFX related styles */
|
| 277 |
+
.vfx {
|
| 278 |
+
position: absolute;
|
| 279 |
+
top: 0;
|
| 280 |
+
left: 0;
|
| 281 |
+
width: 100%;
|
| 282 |
+
height: 100%;
|
| 283 |
+
pointer-events: none;
|
| 284 |
+
animation: backgroundAnimate 5s infinite; /* Continuously animate background */
|
| 285 |
+
z-index: 0; /* Background layer */
|
| 286 |
+
}
|
| 287 |
+
</style>
|
| 288 |
+
</head>
|
| 289 |
+
|
| 290 |
+
<body>
|
| 291 |
+
<div class="vfx"></div> <!-- Background effects -->
|
| 292 |
+
|
| 293 |
+
<div class="container">
|
| 294 |
+
<!-- Settings -->
|
| 295 |
+
<div class="settings">
|
| 296 |
+
<div class="theme-toggle">
|
| 297 |
+
<label for="theme-select">Select Theme:</label>
|
| 298 |
+
<select id="theme-select">
|
| 299 |
+
<option value="default">Default</option>
|
| 300 |
+
<option value="calm-azure">Calm Azure</option>
|
| 301 |
+
<option value="elegant-charcoal">Elegant Charcoal</option>
|
| 302 |
+
<option value="fresh-greenery">Fresh Greenery</option>
|
| 303 |
+
<option value="soft-lavender">Soft Lavender</option>
|
| 304 |
+
<option value="bright-summer">Bright Summer</option>
|
| 305 |
+
</select>
|
| 306 |
+
</div>
|
| 307 |
+
<div class="color-picker">
|
| 308 |
+
<label>Accent Color:</label>
|
| 309 |
+
<div class="color-circle" style="background-color: #6a0dad;" onclick="changeColor('#6a0dad')"></div>
|
| 310 |
+
<div class="color-circle" style="background-color: #ff4500;" onclick="changeColor('#ff4500')"></div>
|
| 311 |
+
<div class="color-circle" style="background-color: #007bff;" onclick="changeColor('#007bff')"></div>
|
| 312 |
+
<div class="color-circle" style="background-color: #28a745;" onclick="changeColor('#28a745')"></div>
|
| 313 |
+
</div>
|
| 314 |
+
</div>
|
| 315 |
+
|
| 316 |
+
<!-- Header -->
|
| 317 |
+
<div class="header">Advanced Chatbot UI</div>
|
| 318 |
+
|
| 319 |
+
<!-- Chatbox -->
|
| 320 |
+
<div class="chat-box" id="chat-box"></div>
|
| 321 |
+
|
| 322 |
+
<!-- Footer -->
|
| 323 |
+
<div class="footer">
|
| 324 |
+
<input type="text" id="user-input" placeholder="Type your message..." />
|
| 325 |
+
<button id="send-btn">Send</button>
|
| 326 |
+
<button id="voice-btn">🎤 Start Voice Input</button>
|
| 327 |
+
</div>
|
| 328 |
+
</div>
|
| 329 |
+
|
| 330 |
+
<script>
|
| 331 |
+
const chatBox = document.getElementById('chat-box');
|
| 332 |
+
const voiceBtn = document.getElementById('voice-btn');
|
| 333 |
+
const sendBtn = document.getElementById('send-btn');
|
| 334 |
+
const userInput = document.getElementById('user-input');
|
| 335 |
+
const themeSelect = document.getElementById('theme-select');
|
| 336 |
+
|
| 337 |
+
// Add message to chatbox with visual effects
|
| 338 |
+
function addMessage(sender, text) {
|
| 339 |
+
const msgDiv = document.createElement('div');
|
| 340 |
+
msgDiv.classList.add('message', sender);
|
| 341 |
+
msgDiv.textContent = text;
|
| 342 |
+
chatBox.appendChild(msgDiv);
|
| 343 |
+
chatBox.scrollTop = chatBox.scrollHeight;
|
| 344 |
+
}
|
| 345 |
+
|
| 346 |
+
// Speech Recognition Setup
|
| 347 |
+
const recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition)();
|
| 348 |
+
recognition.lang = 'en-US';
|
| 349 |
+
|
| 350 |
+
voiceBtn.addEventListener('click', () => recognition.start());
|
| 351 |
+
|
| 352 |
+
recognition.addEventListener('result', (e) => {
|
| 353 |
+
const transcript = e.results[0][0].transcript;
|
| 354 |
+
addMessage('user-message', transcript);
|
| 355 |
+
sendUserMessage(transcript);
|
| 356 |
+
});
|
| 357 |
+
|
| 358 |
+
// Function to change the accent color
|
| 359 |
+
function changeColor(color) {
|
| 360 |
+
document.documentElement.style.setProperty('--accent-color', color);
|
| 361 |
+
}
|
| 362 |
+
|
| 363 |
+
// Function to change the theme
|
| 364 |
+
function changeTheme(theme) {
|
| 365 |
+
document.documentElement.classList.remove('theme-calm-azure', 'theme-elegant-charcoal', 'theme-fresh-greenery', 'theme-soft-lavender', 'theme-bright-summer');
|
| 366 |
+
if (theme !== 'default') {
|
| 367 |
+
document.documentElement.classList.add('theme-' + theme);
|
| 368 |
+
}
|
| 369 |
+
}
|
| 370 |
+
|
| 371 |
+
// Send user input to backend (placeholder URL)
|
| 372 |
+
function sendUserMessage(message) {
|
| 373 |
+
fetch('/chat', {
|
| 374 |
+
method: 'POST',
|
| 375 |
+
headers: {
|
| 376 |
+
'Content-Type': 'application/json',
|
| 377 |
+
},
|
| 378 |
+
body: JSON.stringify({ message: message }),
|
| 379 |
+
})
|
| 380 |
+
.then(response => response.json())
|
| 381 |
+
.then(data => {
|
| 382 |
+
const botResponse = data.response;
|
| 383 |
+
addMessage('bot-message', botResponse);
|
| 384 |
+
speakResponse(botResponse);
|
| 385 |
+
})
|
| 386 |
+
.catch(error => {
|
| 387 |
+
console.error("Error:", error);
|
| 388 |
+
addMessage('bot-message', "Sorry, I couldn't process that.");
|
| 389 |
+
});
|
| 390 |
+
}
|
| 391 |
+
|
| 392 |
+
// Text-to-Speech Function
|
| 393 |
+
function speakResponse(text) {
|
| 394 |
+
const utterance = new SpeechSynthesisUtterance(text);
|
| 395 |
+
utterance.lang = 'en-US';
|
| 396 |
+
window.speechSynthesis.speak(utterance);
|
| 397 |
+
}
|
| 398 |
+
// Event listeners for buttons
|
| 399 |
+
sendBtn.addEventListener('click', () => {
|
| 400 |
+
const message = userInput.value.trim();
|
| 401 |
+
if (message) {
|
| 402 |
+
addMessage('user-message', message);
|
| 403 |
+
sendUserMessage(message);
|
| 404 |
+
userInput.value = ''; // Clear input field after sending
|
| 405 |
+
}
|
| 406 |
+
});
|
| 407 |
+
|
| 408 |
+
// Handle pressing 'Enter' key for sending messages
|
| 409 |
+
userInput.addEventListener('keypress', (e) => {
|
| 410 |
+
if (e.key === 'Enter') {
|
| 411 |
+
sendBtn.click(); // Trigger button click
|
| 412 |
+
}
|
| 413 |
+
});
|
| 414 |
+
|
| 415 |
+
// Update theme when selected from dropdown
|
| 416 |
+
themeSelect.addEventListener('change', (e) => {
|
| 417 |
+
changeTheme(e.target.value);
|
| 418 |
+
});
|
| 419 |
+
|
| 420 |
+
recognition.addEventListener('error', (event) => {
|
| 421 |
+
console.error("Speech recognition error", event);
|
| 422 |
+
});
|
| 423 |
+
|
| 424 |
+
</script>
|
| 425 |
+
</body>
|
| 426 |
+
|
| 427 |
+
</html>
|