Commit ·
edeea8b
1
Parent(s): eedfaef
Updated
Browse files- app.py +70 -30
- assests/logo.png +0 -0
- requirements.txt +2 -1
- static/main.css +155 -80
- templates/index.html +37 -11
app.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
from flask import Flask, render_template, request, jsonify, make_response
|
| 2 |
-
from langchain_core.prompts import
|
| 3 |
-
from
|
|
|
|
| 4 |
from vector import retriever
|
| 5 |
import markdown
|
| 6 |
import dotenv
|
|
@@ -10,41 +11,77 @@ import uuid
|
|
| 10 |
dotenv.load_dotenv()
|
| 11 |
app = Flask(__name__)
|
| 12 |
|
| 13 |
-
#
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
)
|
| 19 |
|
| 20 |
-
#
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
User question: {question}
|
| 32 |
"""
|
| 33 |
|
| 34 |
-
prompt =
|
| 35 |
-
|
| 36 |
-
# New style: RunnableSequence (prompt | llm)
|
| 37 |
-
chain = prompt | llm
|
| 38 |
|
| 39 |
# In-memory chat history per device
|
| 40 |
chat_sessions = {}
|
| 41 |
|
| 42 |
-
def
|
| 43 |
try:
|
| 44 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
except Exception as e:
|
| 46 |
-
print(f"[ERROR]
|
| 47 |
-
return "
|
| 48 |
|
| 49 |
@app.route("/", methods=["GET"])
|
| 50 |
def index():
|
|
@@ -71,13 +108,16 @@ def ask():
|
|
| 71 |
if not question:
|
| 72 |
return jsonify({"error": "No question provided"}), 400
|
| 73 |
|
| 74 |
-
reviews = str(retriever.invoke(question) or "")
|
| 75 |
-
raw_response =
|
|
|
|
|
|
|
|
|
|
| 76 |
chat_sessions[device_id].append({
|
| 77 |
"user": question,
|
| 78 |
-
"bot":
|
| 79 |
})
|
| 80 |
-
return jsonify({"response":
|
| 81 |
|
| 82 |
@app.route("/clear", methods=["POST"])
|
| 83 |
def clear_chat():
|
|
|
|
| 1 |
from flask import Flask, render_template, request, jsonify, make_response
|
| 2 |
+
from langchain_core.prompts import ChatPromptTemplate
|
| 3 |
+
from langchain_openai import ChatOpenAI
|
| 4 |
+
from langchain_community.tools import DuckDuckGoSearchRun
|
| 5 |
from vector import retriever
|
| 6 |
import markdown
|
| 7 |
import dotenv
|
|
|
|
| 11 |
dotenv.load_dotenv()
|
| 12 |
app = Flask(__name__)
|
| 13 |
|
| 14 |
+
# OpenRouter LLM Configuration
|
| 15 |
+
# Using the secret key name provided by the user
|
| 16 |
+
OPENROUTER_API_KEY = os.getenv("DOMINOS_PIZZA_AI_SUPPORT_OPENROUTER_MODEL_KEY")
|
| 17 |
+
MODEL_NAME = "openai/gpt-oss-120b:free"
|
| 18 |
+
|
| 19 |
+
llm = ChatOpenAI(
|
| 20 |
+
model=MODEL_NAME,
|
| 21 |
+
openai_api_key=OPENROUTER_API_KEY,
|
| 22 |
+
openai_api_base="https://openrouter.ai/api/v1",
|
| 23 |
+
default_headers={
|
| 24 |
+
"HTTP-Referer": "https://www.dominos.ng/", # Site URL for OpenRouter
|
| 25 |
+
"X-Title": "Domino's Pizza AI Support"
|
| 26 |
+
}
|
| 27 |
)
|
| 28 |
|
| 29 |
+
# Web Search Tool
|
| 30 |
+
search = DuckDuckGoSearchRun()
|
| 31 |
+
|
| 32 |
+
# System Prompt
|
| 33 |
+
SYSTEM_PROMPT = """
|
| 34 |
+
You are the official Domino's Pizza AI Support agent for Domino's Pizza Nigeria.
|
| 35 |
+
Your goal is to provide exceptional, professional, and friendly assistance to customers.
|
| 36 |
+
|
| 37 |
+
Guidelines:
|
| 38 |
+
1. Always maintain a helpful and appetizing tone.
|
| 39 |
+
2. Use Domino's branding and language where appropriate.
|
| 40 |
+
3. If you don't know the answer or can't find it in the provided context, state that you'll look it up, and use your web search capabilities (simulated in this flow) to find the most accurate information from https://www.dominos.ng/.
|
| 41 |
+
4. Format your responses beautifully using Markdown:
|
| 42 |
+
- Use bold for emphasis.
|
| 43 |
+
- Use lists for steps or menus.
|
| 44 |
+
- Use headers for structure.
|
| 45 |
+
5. If the answer involves a logo, mention that more information can be found at the link associated with the Domino's logo.
|
| 46 |
+
|
| 47 |
+
Context from Domino's Reviews:
|
| 48 |
+
{reviews}
|
| 49 |
+
|
| 50 |
User question: {question}
|
| 51 |
"""
|
| 52 |
|
| 53 |
+
prompt = ChatPromptTemplate.from_template(SYSTEM_PROMPT)
|
|
|
|
|
|
|
|
|
|
| 54 |
|
| 55 |
# In-memory chat history per device
|
| 56 |
chat_sessions = {}
|
| 57 |
|
| 58 |
+
def get_ai_response(question: str, reviews: str) -> str:
|
| 59 |
try:
|
| 60 |
+
# First attempt with context
|
| 61 |
+
chain = prompt | llm
|
| 62 |
+
response = chain.invoke({"reviews": reviews, "question": question})
|
| 63 |
+
content = response.content
|
| 64 |
+
|
| 65 |
+
# Simple check if AI couldn't answer (heuristics)
|
| 66 |
+
fallback_phrases = ["i don't know", "i am not sure", "i couldn't find", "sorry, i can't"]
|
| 67 |
+
if any(phrase in content.lower() for phrase in fallback_phrases):
|
| 68 |
+
print(f"[INFO] Triggering web search for: {question}")
|
| 69 |
+
search_results = search.run(f"Domino's Pizza Nigeria {question}")
|
| 70 |
+
|
| 71 |
+
# Re-run with search results
|
| 72 |
+
search_prompt = ChatPromptTemplate.from_template(
|
| 73 |
+
"The user asked: {question}\n"
|
| 74 |
+
"I found this information on the web: {search_results}\n"
|
| 75 |
+
"Provide a final answer as Domino's AI Support using this information."
|
| 76 |
+
)
|
| 77 |
+
search_chain = search_prompt | llm
|
| 78 |
+
response = search_chain.invoke({"question": question, "search_results": search_results})
|
| 79 |
+
content = response.content
|
| 80 |
+
|
| 81 |
+
return content
|
| 82 |
except Exception as e:
|
| 83 |
+
print(f"[ERROR] AI Response failed: {e}")
|
| 84 |
+
return "I apologize, but I'm having trouble connecting to the pizza ovens (our servers) right now. Please try again in a moment!"
|
| 85 |
|
| 86 |
@app.route("/", methods=["GET"])
|
| 87 |
def index():
|
|
|
|
| 108 |
if not question:
|
| 109 |
return jsonify({"error": "No question provided"}), 400
|
| 110 |
|
| 111 |
+
reviews = str(retriever.invoke(question) or "No specific reviews found.")
|
| 112 |
+
raw_response = get_ai_response(question, reviews)
|
| 113 |
+
|
| 114 |
+
formatted_response = markdown.markdown(raw_response, extensions=['extra', 'tables'])
|
| 115 |
+
|
| 116 |
chat_sessions[device_id].append({
|
| 117 |
"user": question,
|
| 118 |
+
"bot": formatted_response
|
| 119 |
})
|
| 120 |
+
return jsonify({"response": formatted_response})
|
| 121 |
|
| 122 |
@app.route("/clear", methods=["POST"])
|
| 123 |
def clear_chat():
|
assests/logo.png
ADDED
|
requirements.txt
CHANGED
|
@@ -11,4 +11,5 @@ pandas
|
|
| 11 |
sentence-transformers
|
| 12 |
chromadb
|
| 13 |
langchain-community
|
| 14 |
-
python-
|
|
|
|
|
|
| 11 |
sentence-transformers
|
| 12 |
chromadb
|
| 13 |
langchain-community
|
| 14 |
+
python-dotenvlangchain-openai
|
| 15 |
+
duckduckgo-search
|
static/main.css
CHANGED
|
@@ -1,146 +1,221 @@
|
|
| 1 |
-
/*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
* {
|
| 3 |
margin: 0;
|
| 4 |
padding: 0;
|
| 5 |
box-sizing: border-box;
|
| 6 |
-
font-family: 'Segoe UI
|
| 7 |
}
|
| 8 |
|
| 9 |
-
|
| 10 |
-
height:
|
| 11 |
-
width:
|
| 12 |
-
background:
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
| 14 |
}
|
| 15 |
|
| 16 |
-
/* Chat Container */
|
| 17 |
.chat-container {
|
| 18 |
-
position: fixed; /* Lock in place */
|
| 19 |
-
top: 0;
|
| 20 |
-
left: 0;
|
| 21 |
width: 100%;
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
|
|
|
|
|
|
|
|
|
| 25 |
display: flex;
|
| 26 |
flex-direction: column;
|
| 27 |
-
|
|
|
|
| 28 |
}
|
| 29 |
|
| 30 |
-
/* Chat Header */
|
| 31 |
.chat-header {
|
| 32 |
-
background
|
| 33 |
-
padding:
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
}
|
| 39 |
|
| 40 |
-
/* Chat Box */
|
| 41 |
.chat-box {
|
| 42 |
-
flex: 1;
|
| 43 |
-
padding:
|
|
|
|
| 44 |
display: flex;
|
| 45 |
flex-direction: column;
|
| 46 |
-
gap:
|
| 47 |
-
background
|
| 48 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
}
|
| 50 |
|
| 51 |
-
/* Messages */
|
| 52 |
.message {
|
| 53 |
-
max-width:
|
| 54 |
-
padding:
|
| 55 |
-
border-radius:
|
| 56 |
font-size: 1rem;
|
| 57 |
-
line-height: 1.
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
transform: translateY(20px);
|
| 62 |
-
opacity: 0;
|
| 63 |
-
animation: slide-in 0.3s forwards;
|
| 64 |
}
|
| 65 |
|
| 66 |
-
/* User messages */
|
| 67 |
.message.user {
|
| 68 |
align-self: flex-end;
|
| 69 |
-
background
|
| 70 |
color: white;
|
|
|
|
| 71 |
}
|
| 72 |
|
| 73 |
-
/* Bot messages */
|
| 74 |
.message.bot {
|
| 75 |
align-self: flex-start;
|
| 76 |
-
background
|
| 77 |
-
color: #
|
|
|
|
|
|
|
| 78 |
}
|
| 79 |
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
margin:
|
| 84 |
-
|
| 85 |
}
|
| 86 |
|
| 87 |
-
|
| 88 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
}
|
| 90 |
|
| 91 |
-
/* Chat Input */
|
| 92 |
.chat-input {
|
|
|
|
|
|
|
| 93 |
display: flex;
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
}
|
| 98 |
|
| 99 |
.chat-input textarea {
|
| 100 |
flex: 1;
|
| 101 |
-
|
| 102 |
-
border:
|
| 103 |
-
border-radius:
|
| 104 |
-
|
|
|
|
| 105 |
resize: none;
|
| 106 |
-
|
| 107 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
}
|
| 109 |
|
| 110 |
.chat-input button {
|
| 111 |
-
|
| 112 |
-
margin-left: 10px;
|
| 113 |
-
background-color: #10a37f;
|
| 114 |
color: white;
|
| 115 |
border: none;
|
| 116 |
-
border-radius:
|
|
|
|
|
|
|
| 117 |
cursor: pointer;
|
|
|
|
|
|
|
| 118 |
}
|
| 119 |
|
| 120 |
.chat-input button:hover {
|
| 121 |
-
background
|
|
|
|
| 122 |
}
|
| 123 |
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
}
|
| 134 |
|
| 135 |
/* Animations */
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
100% { transform: rotate(360deg); }
|
| 139 |
}
|
| 140 |
|
| 141 |
-
@keyframes
|
|
|
|
|
|
|
|
|
|
|
|
|
| 142 |
to {
|
| 143 |
-
transform: translateY(0);
|
| 144 |
opacity: 1;
|
|
|
|
| 145 |
}
|
| 146 |
}
|
|
|
|
| 1 |
+
/* Domino's Pizza AI Support - Premium CSS */
|
| 2 |
+
|
| 3 |
+
:root {
|
| 4 |
+
--dominos-blue: #006491;
|
| 5 |
+
--dominos-red: #E31837;
|
| 6 |
+
--dominos-white: #FFFFFF;
|
| 7 |
+
--bg-gradient: linear-gradient(135deg, #006491 0%, #004d70 100%);
|
| 8 |
+
--glass: rgba(255, 255, 255, 0.9);
|
| 9 |
+
--glass-border: rgba(255, 255, 255, 0.2);
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
* {
|
| 13 |
margin: 0;
|
| 14 |
padding: 0;
|
| 15 |
box-sizing: border-box;
|
| 16 |
+
font-family: 'Outfit', 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
| 17 |
}
|
| 18 |
|
| 19 |
+
body {
|
| 20 |
+
height: 100vh;
|
| 21 |
+
width: 100vw;
|
| 22 |
+
background: var(--bg-gradient);
|
| 23 |
+
display: flex;
|
| 24 |
+
align-items: center;
|
| 25 |
+
justify-content: center;
|
| 26 |
+
overflow: hidden;
|
| 27 |
}
|
| 28 |
|
|
|
|
| 29 |
.chat-container {
|
|
|
|
|
|
|
|
|
|
| 30 |
width: 100%;
|
| 31 |
+
max-width: 900px;
|
| 32 |
+
height: 90vh;
|
| 33 |
+
background: var(--glass);
|
| 34 |
+
backdrop-filter: blur(10px);
|
| 35 |
+
border-radius: 24px;
|
| 36 |
+
box-shadow: 0 20px 50px rgba(0,0,0,0.3);
|
| 37 |
display: flex;
|
| 38 |
flex-direction: column;
|
| 39 |
+
overflow: hidden;
|
| 40 |
+
border: 1px solid var(--glass-border);
|
| 41 |
}
|
| 42 |
|
|
|
|
| 43 |
.chat-header {
|
| 44 |
+
background: var(--dominos-white);
|
| 45 |
+
padding: 20px 30px;
|
| 46 |
+
display: flex;
|
| 47 |
+
align-items: center;
|
| 48 |
+
justify-content: space-between;
|
| 49 |
+
border-bottom: 2px solid #eee;
|
| 50 |
+
z-index: 10;
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
.chat-header h2 {
|
| 54 |
+
color: var(--dominos-blue);
|
| 55 |
+
font-weight: 800;
|
| 56 |
+
font-size: 1.5rem;
|
| 57 |
+
display: flex;
|
| 58 |
+
align-items: center;
|
| 59 |
+
gap: 12px;
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
.chat-header img {
|
| 63 |
+
height: 40px;
|
| 64 |
}
|
| 65 |
|
|
|
|
| 66 |
.chat-box {
|
| 67 |
+
flex: 1;
|
| 68 |
+
padding: 30px;
|
| 69 |
+
overflow-y: auto;
|
| 70 |
display: flex;
|
| 71 |
flex-direction: column;
|
| 72 |
+
gap: 20px;
|
| 73 |
+
background: rgba(245, 247, 250, 0.5);
|
| 74 |
+
scroll-behavior: smooth;
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
.chat-box::-webkit-scrollbar {
|
| 78 |
+
width: 6px;
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
.chat-box::-webkit-scrollbar-thumb {
|
| 82 |
+
background: rgba(0,0,0,0.1);
|
| 83 |
+
border-radius: 10px;
|
| 84 |
}
|
| 85 |
|
|
|
|
| 86 |
.message {
|
| 87 |
+
max-width: 75%;
|
| 88 |
+
padding: 16px 20px;
|
| 89 |
+
border-radius: 20px;
|
| 90 |
font-size: 1rem;
|
| 91 |
+
line-height: 1.6;
|
| 92 |
+
position: relative;
|
| 93 |
+
transition: all 0.3s ease;
|
| 94 |
+
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
|
|
|
|
|
|
|
|
|
|
| 95 |
}
|
| 96 |
|
|
|
|
| 97 |
.message.user {
|
| 98 |
align-self: flex-end;
|
| 99 |
+
background: var(--dominos-blue);
|
| 100 |
color: white;
|
| 101 |
+
border-bottom-right-radius: 4px;
|
| 102 |
}
|
| 103 |
|
|
|
|
| 104 |
.message.bot {
|
| 105 |
align-self: flex-start;
|
| 106 |
+
background: white;
|
| 107 |
+
color: #333;
|
| 108 |
+
border-bottom-left-radius: 4px;
|
| 109 |
+
border-left: 4px solid var(--dominos-red);
|
| 110 |
}
|
| 111 |
|
| 112 |
+
.message.bot img.bot-logo {
|
| 113 |
+
width: 24px;
|
| 114 |
+
height: 24px;
|
| 115 |
+
margin-right: 10px;
|
| 116 |
+
vertical-align: middle;
|
| 117 |
}
|
| 118 |
|
| 119 |
+
/* AI Thinking Animation */
|
| 120 |
+
.thinking-container {
|
| 121 |
+
display: flex;
|
| 122 |
+
gap: 4px;
|
| 123 |
+
padding: 10px 0;
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
.thinking-dot {
|
| 127 |
+
width: 8px;
|
| 128 |
+
height: 8px;
|
| 129 |
+
background: var(--dominos-red);
|
| 130 |
+
border-radius: 50%;
|
| 131 |
+
animation: bounce 1.4s infinite ease-in-out both;
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
.thinking-dot:nth-child(1) { animation-delay: -0.32s; }
|
| 135 |
+
.thinking-dot:nth-child(2) { animation-delay: -0.16s; }
|
| 136 |
+
|
| 137 |
+
@keyframes bounce {
|
| 138 |
+
0%, 80%, 100% { transform: scale(0); }
|
| 139 |
+
40% { transform: scale(1); }
|
| 140 |
}
|
| 141 |
|
|
|
|
| 142 |
.chat-input {
|
| 143 |
+
background: white;
|
| 144 |
+
padding: 24px 30px;
|
| 145 |
display: flex;
|
| 146 |
+
gap: 15px;
|
| 147 |
+
align-items: flex-end;
|
| 148 |
+
border-top: 1px solid #eee;
|
| 149 |
}
|
| 150 |
|
| 151 |
.chat-input textarea {
|
| 152 |
flex: 1;
|
| 153 |
+
background: #f8f9fa;
|
| 154 |
+
border: 2px solid #eee;
|
| 155 |
+
border-radius: 16px;
|
| 156 |
+
padding: 14px 20px;
|
| 157 |
+
font-size: 1rem;
|
| 158 |
resize: none;
|
| 159 |
+
max-height: 120px;
|
| 160 |
+
outline: none;
|
| 161 |
+
transition: border-color 0.3s ease;
|
| 162 |
+
}
|
| 163 |
+
|
| 164 |
+
.chat-input textarea:focus {
|
| 165 |
+
border-color: var(--dominos-blue);
|
| 166 |
}
|
| 167 |
|
| 168 |
.chat-input button {
|
| 169 |
+
background: var(--dominos-red);
|
|
|
|
|
|
|
| 170 |
color: white;
|
| 171 |
border: none;
|
| 172 |
+
border-radius: 14px;
|
| 173 |
+
padding: 14px 28px;
|
| 174 |
+
font-weight: 700;
|
| 175 |
cursor: pointer;
|
| 176 |
+
transition: transform 0.2s ease, background 0.2s ease;
|
| 177 |
+
height: 52px;
|
| 178 |
}
|
| 179 |
|
| 180 |
.chat-input button:hover {
|
| 181 |
+
background: #c1142d;
|
| 182 |
+
transform: translateY(-2px);
|
| 183 |
}
|
| 184 |
|
| 185 |
+
.chat-input button:active {
|
| 186 |
+
transform: translateY(0);
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
/* Markdown Styling */
|
| 190 |
+
.message h1, .message h2, .message h3 {
|
| 191 |
+
margin: 10px 0;
|
| 192 |
+
color: var(--dominos-blue);
|
| 193 |
+
}
|
| 194 |
+
|
| 195 |
+
.message ul, .message ol {
|
| 196 |
+
margin-left: 20px;
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
.message li {
|
| 200 |
+
margin-bottom: 8px;
|
| 201 |
+
}
|
| 202 |
+
|
| 203 |
+
.message p {
|
| 204 |
+
margin-bottom: 12px;
|
| 205 |
}
|
| 206 |
|
| 207 |
/* Animations */
|
| 208 |
+
.slide-in-bottom {
|
| 209 |
+
animation: slideIn 0.4s ease-out forwards;
|
|
|
|
| 210 |
}
|
| 211 |
|
| 212 |
+
@keyframes slideIn {
|
| 213 |
+
from {
|
| 214 |
+
opacity: 0;
|
| 215 |
+
transform: translateY(20px);
|
| 216 |
+
}
|
| 217 |
to {
|
|
|
|
| 218 |
opacity: 1;
|
| 219 |
+
transform: translateY(0);
|
| 220 |
}
|
| 221 |
}
|
templates/index.html
CHANGED
|
@@ -3,25 +3,30 @@
|
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8" />
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
| 6 |
-
<title>Pizza AI</title>
|
| 7 |
<link rel="stylesheet" href="{{ url_for('static', filename='main.css') }}" />
|
|
|
|
| 8 |
</head>
|
| 9 |
<body>
|
| 10 |
<div class="chat-container">
|
| 11 |
<div class="chat-header">
|
| 12 |
-
<h2>
|
|
|
|
| 13 |
</div>
|
| 14 |
|
| 15 |
<div class="chat-box" id="chat-box">
|
| 16 |
{% for message in chat_history %}
|
| 17 |
<div class="message user slide-in-bottom">{{ message.user }}</div>
|
| 18 |
-
<div class="message bot slide-in-bottom">
|
|
|
|
|
|
|
|
|
|
| 19 |
{% endfor %}
|
| 20 |
</div>
|
| 21 |
|
| 22 |
<form id="chat-form" class="chat-input" autocomplete="off">
|
| 23 |
-
<textarea name="question" id="input" placeholder="
|
| 24 |
-
<button type="submit">Send</button>
|
| 25 |
</form>
|
| 26 |
</div>
|
| 27 |
|
|
@@ -29,6 +34,7 @@
|
|
| 29 |
const chatForm = document.getElementById('chat-form');
|
| 30 |
const chatBox = document.getElementById('chat-box');
|
| 31 |
const input = document.getElementById('input');
|
|
|
|
| 32 |
|
| 33 |
// Auto-expand textarea
|
| 34 |
input.addEventListener('input', () => {
|
|
@@ -36,6 +42,13 @@
|
|
| 36 |
input.style.height = input.scrollHeight + 'px';
|
| 37 |
});
|
| 38 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
chatForm.addEventListener('submit', async (e) => {
|
| 40 |
e.preventDefault();
|
| 41 |
const question = input.value.trim();
|
|
@@ -51,15 +64,24 @@
|
|
| 51 |
// Clear input immediately
|
| 52 |
input.value = '';
|
| 53 |
input.style.height = 'auto';
|
|
|
|
|
|
|
| 54 |
|
| 55 |
-
// Add bot message with
|
| 56 |
const botMessage = document.createElement('div');
|
| 57 |
botMessage.classList.add('message', 'bot', 'slide-in-bottom');
|
| 58 |
-
botMessage.innerHTML =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
chatBox.appendChild(botMessage);
|
| 60 |
botMessage.scrollIntoView({ block: "start", behavior: "smooth" });
|
| 61 |
|
| 62 |
-
// Send POST request
|
| 63 |
try {
|
| 64 |
const res = await fetch('/ask', {
|
| 65 |
method: 'POST',
|
|
@@ -68,11 +90,15 @@
|
|
| 68 |
});
|
| 69 |
const data = await res.json();
|
| 70 |
|
| 71 |
-
// Replace
|
| 72 |
-
botMessage.innerHTML = data.response || data.error;
|
| 73 |
botMessage.scrollIntoView({ block: "start", behavior: "smooth" });
|
| 74 |
} catch (err) {
|
| 75 |
-
botMessage.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 76 |
}
|
| 77 |
});
|
| 78 |
</script>
|
|
|
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8" />
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
| 6 |
+
<title>Domino's Pizza AI Support</title>
|
| 7 |
<link rel="stylesheet" href="{{ url_for('static', filename='main.css') }}" />
|
| 8 |
+
<link rel="icon" href="{{ url_for('static', filename='assests/logo.png') }}" type="image/png">
|
| 9 |
</head>
|
| 10 |
<body>
|
| 11 |
<div class="chat-container">
|
| 12 |
<div class="chat-header">
|
| 13 |
+
<h2><img src="/assests/logo.png" alt="Domino's Logo"> Domino's AI Support</h2>
|
| 14 |
+
<button onclick="clearChat()" style="background: transparent; border: none; color: var(--dominos-red); cursor: pointer; font-weight: bold;">Clear Chat</button>
|
| 15 |
</div>
|
| 16 |
|
| 17 |
<div class="chat-box" id="chat-box">
|
| 18 |
{% for message in chat_history %}
|
| 19 |
<div class="message user slide-in-bottom">{{ message.user }}</div>
|
| 20 |
+
<div class="message bot slide-in-bottom">
|
| 21 |
+
<img src="/assests/logo.png" class="bot-logo" alt="Domino's">
|
| 22 |
+
{{ message.bot|safe }}
|
| 23 |
+
</div>
|
| 24 |
{% endfor %}
|
| 25 |
</div>
|
| 26 |
|
| 27 |
<form id="chat-form" class="chat-input" autocomplete="off">
|
| 28 |
+
<textarea name="question" id="input" placeholder="How can Domino's help you today?" required></textarea>
|
| 29 |
+
<button type="submit" id="send-btn">Send Order</button>
|
| 30 |
</form>
|
| 31 |
</div>
|
| 32 |
|
|
|
|
| 34 |
const chatForm = document.getElementById('chat-form');
|
| 35 |
const chatBox = document.getElementById('chat-box');
|
| 36 |
const input = document.getElementById('input');
|
| 37 |
+
const sendBtn = document.getElementById('send-btn');
|
| 38 |
|
| 39 |
// Auto-expand textarea
|
| 40 |
input.addEventListener('input', () => {
|
|
|
|
| 42 |
input.style.height = input.scrollHeight + 'px';
|
| 43 |
});
|
| 44 |
|
| 45 |
+
async function clearChat() {
|
| 46 |
+
if (confirm('Are you sure you want to clear the pizza party? (Chat history)')) {
|
| 47 |
+
await fetch('/clear', { method: 'POST' });
|
| 48 |
+
window.location.reload();
|
| 49 |
+
}
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
chatForm.addEventListener('submit', async (e) => {
|
| 53 |
e.preventDefault();
|
| 54 |
const question = input.value.trim();
|
|
|
|
| 64 |
// Clear input immediately
|
| 65 |
input.value = '';
|
| 66 |
input.style.height = 'auto';
|
| 67 |
+
input.disabled = true;
|
| 68 |
+
sendBtn.disabled = true;
|
| 69 |
|
| 70 |
+
// Add bot message with thinking animation
|
| 71 |
const botMessage = document.createElement('div');
|
| 72 |
botMessage.classList.add('message', 'bot', 'slide-in-bottom');
|
| 73 |
+
botMessage.innerHTML = `
|
| 74 |
+
<img src="/assests/logo.png" class="bot-logo" alt="Domino's">
|
| 75 |
+
<div class="thinking-container">
|
| 76 |
+
<div class="thinking-dot"></div>
|
| 77 |
+
<div class="thinking-dot"></div>
|
| 78 |
+
<div class="thinking-dot"></div>
|
| 79 |
+
</div>
|
| 80 |
+
`;
|
| 81 |
chatBox.appendChild(botMessage);
|
| 82 |
botMessage.scrollIntoView({ block: "start", behavior: "smooth" });
|
| 83 |
|
| 84 |
+
// Send POST request
|
| 85 |
try {
|
| 86 |
const res = await fetch('/ask', {
|
| 87 |
method: 'POST',
|
|
|
|
| 90 |
});
|
| 91 |
const data = await res.json();
|
| 92 |
|
| 93 |
+
// Replace thinking animation with bot response
|
| 94 |
+
botMessage.innerHTML = `<img src="/assests/logo.png" class="bot-logo" alt="Domino's"> ${data.response || data.error}`;
|
| 95 |
botMessage.scrollIntoView({ block: "start", behavior: "smooth" });
|
| 96 |
} catch (err) {
|
| 97 |
+
botMessage.innerHTML = `<img src="/assests/logo.png" class="bot-logo" alt="Domino's"> Sorry, something went wrong in the kitchen.`;
|
| 98 |
+
} finally {
|
| 99 |
+
input.disabled = false;
|
| 100 |
+
sendBtn.disabled = false;
|
| 101 |
+
input.focus();
|
| 102 |
}
|
| 103 |
});
|
| 104 |
</script>
|