Spaces:
Runtime error
Runtime error
Syuhra Ganiswari commited on
Commit ·
39ff7ae
1
Parent(s): f86ad4d
update FE
Browse files- .gitignore +2 -0
- app.py +41 -42
- contoh_struktur.json +1 -1
- load_data.py +24 -25
- static/script.js +21 -21
- static/style.css +34 -34
- templates/chat.html +2 -2
- templates/index.html +4 -4
.gitignore
CHANGED
|
@@ -2,7 +2,9 @@
|
|
| 2 |
venv/
|
| 3 |
|
| 4 |
# Database ChromaDB lokal (wajib)
|
|
|
|
| 5 |
my_chroma_db/
|
|
|
|
| 6 |
|
| 7 |
# File rahasia API keys (wajib)
|
| 8 |
.env
|
|
|
|
| 2 |
venv/
|
| 3 |
|
| 4 |
# Database ChromaDB lokal (wajib)
|
| 5 |
+
chroma/
|
| 6 |
my_chroma_db/
|
| 7 |
+
.chroma/
|
| 8 |
|
| 9 |
# File rahasia API keys (wajib)
|
| 10 |
.env
|
app.py
CHANGED
|
@@ -10,12 +10,12 @@ from dotenv import load_dotenv
|
|
| 10 |
from bson.objectid import ObjectId
|
| 11 |
from datetime import datetime
|
| 12 |
|
| 13 |
-
# --- 1.
|
| 14 |
load_dotenv()
|
| 15 |
app = Flask(__name__)
|
| 16 |
CORS(app)
|
| 17 |
|
| 18 |
-
# --- 2.
|
| 19 |
hf_token = os.getenv("HF_TOKEN")
|
| 20 |
hf_client = InferenceClient(
|
| 21 |
"meta-llama/Meta-Llama-3-8B-Instruct",
|
|
@@ -26,20 +26,20 @@ mongo_url = os.getenv("MONGO_URL")
|
|
| 26 |
mongo_client = MongoClient(mongo_url)
|
| 27 |
db = mongo_client.get_database("chatbot_db")
|
| 28 |
chat_history_collection = db.get_collection("conversations")
|
| 29 |
-
print("✅
|
| 30 |
|
| 31 |
-
print("
|
| 32 |
embedding_model = SentenceTransformer('all-MiniLM-L6-v2')
|
| 33 |
-
print("✅
|
| 34 |
|
| 35 |
try:
|
| 36 |
chroma_client = HttpClient(host='localhost', port=8000)
|
| 37 |
knowledge_collection = chroma_client.get_collection(
|
| 38 |
name="website_knowledge"
|
| 39 |
)
|
| 40 |
-
print("✅
|
| 41 |
except Exception as e:
|
| 42 |
-
print(f"❌
|
| 43 |
|
| 44 |
# --- 3. Endpoint Frontend ---
|
| 45 |
# @app.route("/")
|
|
@@ -48,20 +48,20 @@ except Exception as e:
|
|
| 48 |
|
| 49 |
@app.route("/")
|
| 50 |
def home():
|
| 51 |
-
"""
|
| 52 |
return render_template("index.html")
|
| 53 |
|
| 54 |
@app.route("/chat")
|
| 55 |
def chat_page():
|
| 56 |
-
"""
|
| 57 |
return render_template("chat.html")
|
| 58 |
|
| 59 |
-
# --- 4. Endpoint API -
|
| 60 |
@app.route("/api/conversations", methods=["GET"])
|
| 61 |
def get_conversations():
|
| 62 |
user_id = request.args.get("userId")
|
| 63 |
if not user_id:
|
| 64 |
-
return jsonify({"error": "userId
|
| 65 |
|
| 66 |
try:
|
| 67 |
convos = list(chat_history_collection.find(
|
|
@@ -72,58 +72,58 @@ def get_conversations():
|
|
| 72 |
for convo in convos:
|
| 73 |
conversations_list.append({
|
| 74 |
"id": str(convo.get('_id')),
|
| 75 |
-
"title": convo.get("title", "
|
| 76 |
"messages": convo.get("messages", []),
|
| 77 |
"createdAt": convo.get("createdAt")
|
| 78 |
})
|
| 79 |
return jsonify(conversations_list)
|
| 80 |
except Exception as e:
|
| 81 |
print(f"Error di /api/conversations GET: {e}")
|
| 82 |
-
return jsonify({"error": "
|
| 83 |
|
| 84 |
-
# --- 5.
|
| 85 |
@app.route("/api/conversations", methods=["DELETE"])
|
| 86 |
def clear_conversations():
|
| 87 |
"""
|
| 88 |
-
|
| 89 |
"""
|
| 90 |
data = request.json
|
| 91 |
user_id = data.get("userId")
|
| 92 |
if not user_id:
|
| 93 |
-
return jsonify({"error": "userId
|
| 94 |
|
| 95 |
try:
|
| 96 |
-
#
|
| 97 |
result = chat_history_collection.delete_many({"userId": user_id})
|
| 98 |
|
| 99 |
-
print(f"
|
| 100 |
return jsonify({
|
| 101 |
-
"message": "History
|
| 102 |
"deleted_count": result.deleted_count
|
| 103 |
})
|
| 104 |
|
| 105 |
except Exception as e:
|
| 106 |
print(f"Error di /api/conversations DELETE: {e}")
|
| 107 |
-
return jsonify({"error": "
|
| 108 |
|
| 109 |
-
# --- 6. Endpoint API Chat (
|
| 110 |
@app.route("/api/chat", methods = ["POST"])
|
| 111 |
def handle_chat():
|
| 112 |
try:
|
| 113 |
data = request.json
|
| 114 |
user_message = data.get("message")
|
| 115 |
user_id = data.get("userId")
|
| 116 |
-
conversation_id = data.get("conversationId") #
|
| 117 |
|
| 118 |
if not user_message or not user_id:
|
| 119 |
-
return jsonify({"error": "
|
| 120 |
|
| 121 |
history = []
|
| 122 |
user_message_doc = {"role": "user", "content": user_message, "timestamp": datetime.now()}
|
| 123 |
|
| 124 |
-
# --- 1. (CRUD)
|
| 125 |
if conversation_id:
|
| 126 |
-
#
|
| 127 |
current_convo = chat_history_collection.find_one({
|
| 128 |
"_id": ObjectId(conversation_id),
|
| 129 |
"userId": user_id
|
|
@@ -131,18 +131,17 @@ def handle_chat():
|
|
| 131 |
if current_convo:
|
| 132 |
history = current_convo.get("messages", [])[-6:]
|
| 133 |
|
| 134 |
-
# --- 2. (RAG) -
|
| 135 |
-
print(f"
|
| 136 |
query_embedding = embedding_model.encode(user_message).tolist()
|
| 137 |
results = knowledge_collection.query(
|
| 138 |
query_embeddings=[query_embedding],
|
| 139 |
-
n_results=1
|
| 140 |
)
|
| 141 |
context = "\n\n".join(results['documents'][0])
|
| 142 |
print("Konteks ditemukan.")
|
| 143 |
|
| 144 |
-
# --- 3. (RAG)
|
| 145 |
-
# <-- Anda bisa perketat prompt ini nanti
|
| 146 |
system_prompt = """You are a precise assistant. Answer *strictly* and *only* based on the context provided.
|
| 147 |
Do not add any information, pollutants, or applications that are not *explicitly* mentioned in the text.
|
| 148 |
If the context provides conflicting information (like for different products), only use the information from the *single most relevant* chunk."""
|
|
@@ -153,15 +152,15 @@ def handle_chat():
|
|
| 153 |
{"role": "user", "content": user_prompt}
|
| 154 |
]
|
| 155 |
|
| 156 |
-
# --- 4. (RAG)
|
| 157 |
-
print("
|
| 158 |
response = hf_client.chat_completion(messages=messages, max_tokens=250, temperature=0.1)
|
| 159 |
ai_response = response.choices[0].message.content
|
| 160 |
|
| 161 |
-
# --- 5. (CRUD)
|
| 162 |
ai_message_doc = {"role": "assistant", "content": ai_response, "timestamp": datetime.now()}
|
| 163 |
|
| 164 |
-
if conversation_id: #
|
| 165 |
chat_history_collection.update_one(
|
| 166 |
{"_id": ObjectId(conversation_id)},
|
| 167 |
{
|
|
@@ -169,27 +168,27 @@ def handle_chat():
|
|
| 169 |
"$set": {"updatedAt": datetime.now()}
|
| 170 |
}
|
| 171 |
)
|
| 172 |
-
else: #
|
| 173 |
title = user_message[:30] + "..." if len(user_message) > 30 else user_message
|
| 174 |
new_convo_doc = {
|
| 175 |
"userId": user_id,
|
| 176 |
"title": title,
|
| 177 |
-
"messages": [user_message_doc, ai_message_doc], #
|
| 178 |
"createdAt": datetime.now(),
|
| 179 |
"updatedAt": datetime.now()
|
| 180 |
}
|
| 181 |
insert_result = chat_history_collection.insert_one(new_convo_doc)
|
| 182 |
-
conversation_id = insert_result.inserted_id #
|
| 183 |
|
| 184 |
-
print("
|
| 185 |
|
| 186 |
-
# --- 6.
|
| 187 |
return jsonify({"answer": ai_response, "conversationId": str(conversation_id)})
|
| 188 |
|
| 189 |
except Exception as e:
|
| 190 |
print(f"Error di /api/chat: {e}")
|
| 191 |
-
return jsonify({"error": "
|
| 192 |
|
| 193 |
-
# --- 7.
|
| 194 |
if __name__ == "__main__":
|
| 195 |
-
app.run(port=
|
|
|
|
| 10 |
from bson.objectid import ObjectId
|
| 11 |
from datetime import datetime
|
| 12 |
|
| 13 |
+
# --- 1. Load .env and Initialize ---
|
| 14 |
load_dotenv()
|
| 15 |
app = Flask(__name__)
|
| 16 |
CORS(app)
|
| 17 |
|
| 18 |
+
# --- 2. Client Initialization (Global) ---
|
| 19 |
hf_token = os.getenv("HF_TOKEN")
|
| 20 |
hf_client = InferenceClient(
|
| 21 |
"meta-llama/Meta-Llama-3-8B-Instruct",
|
|
|
|
| 26 |
mongo_client = MongoClient(mongo_url)
|
| 27 |
db = mongo_client.get_database("chatbot_db")
|
| 28 |
chat_history_collection = db.get_collection("conversations")
|
| 29 |
+
print("✅ Successfully connected to MongoDB Atlas.")
|
| 30 |
|
| 31 |
+
print("Loading model embedding (this may take a while)...")
|
| 32 |
embedding_model = SentenceTransformer('all-MiniLM-L6-v2')
|
| 33 |
+
print("✅ The embedding model (‘all-MiniLM-L6-v2’) has been successfully loaded.")
|
| 34 |
|
| 35 |
try:
|
| 36 |
chroma_client = HttpClient(host='localhost', port=8000)
|
| 37 |
knowledge_collection = chroma_client.get_collection(
|
| 38 |
name="website_knowledge"
|
| 39 |
)
|
| 40 |
+
print("✅ Successfully connected to ChromaDB (localhost:8000).")
|
| 41 |
except Exception as e:
|
| 42 |
+
print(f"❌ Failed to connect to ChromaDB. Make sure the chroma run... server is running. Error: {e}")
|
| 43 |
|
| 44 |
# --- 3. Endpoint Frontend ---
|
| 45 |
# @app.route("/")
|
|
|
|
| 48 |
|
| 49 |
@app.route("/")
|
| 50 |
def home():
|
| 51 |
+
"""Display the landing page (index.html)"""
|
| 52 |
return render_template("index.html")
|
| 53 |
|
| 54 |
@app.route("/chat")
|
| 55 |
def chat_page():
|
| 56 |
+
"""Display the main chat page (chat.html)"""
|
| 57 |
return render_template("chat.html")
|
| 58 |
|
| 59 |
+
# --- 4. Endpoint API - Take ALL History (untuk F5) ---
|
| 60 |
@app.route("/api/conversations", methods=["GET"])
|
| 61 |
def get_conversations():
|
| 62 |
user_id = request.args.get("userId")
|
| 63 |
if not user_id:
|
| 64 |
+
return jsonify({"error": "userId is required"}), 400
|
| 65 |
|
| 66 |
try:
|
| 67 |
convos = list(chat_history_collection.find(
|
|
|
|
| 72 |
for convo in convos:
|
| 73 |
conversations_list.append({
|
| 74 |
"id": str(convo.get('_id')),
|
| 75 |
+
"title": convo.get("title", "New Chat"),
|
| 76 |
"messages": convo.get("messages", []),
|
| 77 |
"createdAt": convo.get("createdAt")
|
| 78 |
})
|
| 79 |
return jsonify(conversations_list)
|
| 80 |
except Exception as e:
|
| 81 |
print(f"Error di /api/conversations GET: {e}")
|
| 82 |
+
return jsonify({"error": "Failed to retrieve conversation"}), 500
|
| 83 |
|
| 84 |
+
# --- 5. NEW API ENDPOINT (FOR “CLEAR ALL”) ---
|
| 85 |
@app.route("/api/conversations", methods=["DELETE"])
|
| 86 |
def clear_conversations():
|
| 87 |
"""
|
| 88 |
+
Delete ALL conversations for one user ID.
|
| 89 |
"""
|
| 90 |
data = request.json
|
| 91 |
user_id = data.get("userId")
|
| 92 |
if not user_id:
|
| 93 |
+
return jsonify({"error": "userId is required"}), 400
|
| 94 |
|
| 95 |
try:
|
| 96 |
+
# Delete all documents in MongoDB that match the userId
|
| 97 |
result = chat_history_collection.delete_many({"userId": user_id})
|
| 98 |
|
| 99 |
+
print(f"Successfully deleted {result.deleted_count} conversation for userId {user_id}.")
|
| 100 |
return jsonify({
|
| 101 |
+
"message": "History successfully deleted",
|
| 102 |
"deleted_count": result.deleted_count
|
| 103 |
})
|
| 104 |
|
| 105 |
except Exception as e:
|
| 106 |
print(f"Error di /api/conversations DELETE: {e}")
|
| 107 |
+
return jsonify({"error": "Failed to delete history"}), 500
|
| 108 |
|
| 109 |
+
# --- 6. Endpoint API Chat (Main) ---
|
| 110 |
@app.route("/api/chat", methods = ["POST"])
|
| 111 |
def handle_chat():
|
| 112 |
try:
|
| 113 |
data = request.json
|
| 114 |
user_message = data.get("message")
|
| 115 |
user_id = data.get("userId")
|
| 116 |
+
conversation_id = data.get("conversationId") # Active chat ID, can be null
|
| 117 |
|
| 118 |
if not user_message or not user_id:
|
| 119 |
+
return jsonify({"error": "The 'message' dan 'userId' parameters are required."}), 400
|
| 120 |
|
| 121 |
history = []
|
| 122 |
user_message_doc = {"role": "user", "content": user_message, "timestamp": datetime.now()}
|
| 123 |
|
| 124 |
+
# --- 1. (CRUD) Retrieve/Create Conversation ---
|
| 125 |
if conversation_id:
|
| 126 |
+
# This is an existing chat
|
| 127 |
current_convo = chat_history_collection.find_one({
|
| 128 |
"_id": ObjectId(conversation_id),
|
| 129 |
"userId": user_id
|
|
|
|
| 131 |
if current_convo:
|
| 132 |
history = current_convo.get("messages", [])[-6:]
|
| 133 |
|
| 134 |
+
# --- 2. (RAG) - Perform RAG (Same as before) ---
|
| 135 |
+
print(f"Searching for context for: \"{user_message}\"")
|
| 136 |
query_embedding = embedding_model.encode(user_message).tolist()
|
| 137 |
results = knowledge_collection.query(
|
| 138 |
query_embeddings=[query_embedding],
|
| 139 |
+
n_results=1
|
| 140 |
)
|
| 141 |
context = "\n\n".join(results['documents'][0])
|
| 142 |
print("Konteks ditemukan.")
|
| 143 |
|
| 144 |
+
# --- 3. (RAG) For prompt ---
|
|
|
|
| 145 |
system_prompt = """You are a precise assistant. Answer *strictly* and *only* based on the context provided.
|
| 146 |
Do not add any information, pollutants, or applications that are not *explicitly* mentioned in the text.
|
| 147 |
If the context provides conflicting information (like for different products), only use the information from the *single most relevant* chunk."""
|
|
|
|
| 152 |
{"role": "user", "content": user_prompt}
|
| 153 |
]
|
| 154 |
|
| 155 |
+
# --- 4. (RAG) Call the Llama 3 API ---
|
| 156 |
+
print("Calling Hugging Face API...")
|
| 157 |
response = hf_client.chat_completion(messages=messages, max_tokens=250, temperature=0.1)
|
| 158 |
ai_response = response.choices[0].message.content
|
| 159 |
|
| 160 |
+
# --- 5. (CRUD) Save the AI response to MongoDB ---
|
| 161 |
ai_message_doc = {"role": "assistant", "content": ai_response, "timestamp": datetime.now()}
|
| 162 |
|
| 163 |
+
if conversation_id: # Existing chat
|
| 164 |
chat_history_collection.update_one(
|
| 165 |
{"_id": ObjectId(conversation_id)},
|
| 166 |
{
|
|
|
|
| 168 |
"$set": {"updatedAt": datetime.now()}
|
| 169 |
}
|
| 170 |
)
|
| 171 |
+
else: # New chat
|
| 172 |
title = user_message[:30] + "..." if len(user_message) > 30 else user_message
|
| 173 |
new_convo_doc = {
|
| 174 |
"userId": user_id,
|
| 175 |
"title": title,
|
| 176 |
+
"messages": [user_message_doc, ai_message_doc], # Directly append user & AI
|
| 177 |
"createdAt": datetime.now(),
|
| 178 |
"updatedAt": datetime.now()
|
| 179 |
}
|
| 180 |
insert_result = chat_history_collection.insert_one(new_convo_doc)
|
| 181 |
+
conversation_id = insert_result.inserted_id # Get the new _id
|
| 182 |
|
| 183 |
+
print("Conversation successfully saved to MongoDB.")
|
| 184 |
|
| 185 |
+
# --- 6. Send the response ---
|
| 186 |
return jsonify({"answer": ai_response, "conversationId": str(conversation_id)})
|
| 187 |
|
| 188 |
except Exception as e:
|
| 189 |
print(f"Error di /api/chat: {e}")
|
| 190 |
+
return jsonify({"error": "Server error occurred"}), 500
|
| 191 |
|
| 192 |
+
# --- 7. Run the Server ---
|
| 193 |
if __name__ == "__main__":
|
| 194 |
+
app.run(port=5000, debug=True)
|
contoh_struktur.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
| 3 |
"userId": "anon-uuid-12345-abcde", // Anonymous User ID Anda
|
| 4 |
"createdAt": "2025-10-28T12:00:00Z",
|
| 5 |
"updatedAt": "2025-10-28T12:05:00Z",
|
| 6 |
-
"title": "
|
| 7 |
"messages": [
|
| 8 |
{
|
| 9 |
"role": "user",
|
|
|
|
| 3 |
"userId": "anon-uuid-12345-abcde", // Anonymous User ID Anda
|
| 4 |
"createdAt": "2025-10-28T12:00:00Z",
|
| 5 |
"updatedAt": "2025-10-28T12:05:00Z",
|
| 6 |
+
"title": "Conversation about Air Quality", // (Opsional)
|
| 7 |
"messages": [
|
| 8 |
{
|
| 9 |
"role": "user",
|
load_data.py
CHANGED
|
@@ -3,57 +3,56 @@ from sentence_transformers import SentenceTransformer
|
|
| 3 |
from chromadb import HttpClient
|
| 4 |
import sys
|
| 5 |
|
| 6 |
-
# --- 1.
|
| 7 |
-
print("
|
| 8 |
try:
|
| 9 |
-
#
|
| 10 |
client = HttpClient(host='localhost', port=8000)
|
| 11 |
collection = client.get_or_create_collection(name="website_knowledge")
|
| 12 |
-
print("✅
|
| 13 |
except Exception as e:
|
| 14 |
-
print(
|
| 15 |
print(f"Error: {e}")
|
| 16 |
sys.exit(1)
|
| 17 |
|
| 18 |
-
print("
|
| 19 |
model = SentenceTransformer('all-MiniLM-L6-v2')
|
| 20 |
-
print("✅
|
| 21 |
|
| 22 |
-
# --- 2.
|
| 23 |
-
file_path = "knowledge_base_FINAL_COMBINED.csv"
|
| 24 |
try:
|
| 25 |
df = pd.read_csv(file_path)
|
| 26 |
df = df.fillna('')
|
| 27 |
-
print(f"✅
|
| 28 |
except FileNotFoundError:
|
| 29 |
-
print(f"❌ Error: File {file_path}
|
| 30 |
sys.exit(1)
|
| 31 |
|
| 32 |
-
# --- 3.
|
| 33 |
documents = df['chunk_text'].tolist()
|
| 34 |
metadatas = df[['main_topic', 'chunk_title', 'src_url']].to_dict('records')
|
| 35 |
ids = df['chunk_id'].tolist()
|
| 36 |
|
| 37 |
-
# --- 4.
|
| 38 |
-
# app.py
|
| 39 |
-
#
|
| 40 |
-
print(f"
|
| 41 |
embeddings = model.encode(documents, show_progress_bar=True)
|
| 42 |
-
print("✅
|
| 43 |
|
| 44 |
-
# --- 5.
|
| 45 |
-
#
|
| 46 |
try:
|
| 47 |
collection.delete(ids=ids)
|
| 48 |
-
print("
|
| 49 |
except Exception as e:
|
| 50 |
-
print("
|
| 51 |
|
| 52 |
-
print("
|
| 53 |
batch_size = 100
|
| 54 |
for i in range(0, len(ids), batch_size):
|
| 55 |
-
print(f"
|
| 56 |
-
|
| 57 |
collection.add(
|
| 58 |
embeddings=embeddings[i:i+batch_size].tolist(),
|
| 59 |
documents=documents[i:i+batch_size],
|
|
@@ -61,4 +60,4 @@ for i in range(0, len(ids), batch_size):
|
|
| 61 |
ids=ids[i:i+batch_size]
|
| 62 |
)
|
| 63 |
|
| 64 |
-
print(f"🎉
|
|
|
|
| 3 |
from chromadb import HttpClient
|
| 4 |
import sys
|
| 5 |
|
| 6 |
+
# --- 1. Initialize Client & Model ---
|
| 7 |
+
print("Connecting to ChromaDB at localhost:8000...")
|
| 8 |
try:
|
| 9 |
+
# We connect to the same server as app.py
|
| 10 |
client = HttpClient(host='localhost', port=8000)
|
| 11 |
collection = client.get_or_create_collection(name="website_knowledge")
|
| 12 |
+
print("✅ Successfully connected to ChromaDB.")
|
| 13 |
except Exception as e:
|
| 14 |
+
print("❌ FAILED to connect to ChromaDB. Ensure the ChromaDB server is running.")
|
| 15 |
print(f"Error: {e}")
|
| 16 |
sys.exit(1)
|
| 17 |
|
| 18 |
+
print("Loading 'all-MiniLM-L6-v2' embedding model (this might take a moment)...")
|
| 19 |
model = SentenceTransformer('all-MiniLM-L6-v2')
|
| 20 |
+
print("✅ Embedding model successfully loaded.")
|
| 21 |
|
| 22 |
+
# --- 2. Read CSV File ---
|
| 23 |
+
file_path = "knowledge_base_FINAL_COMBINED.csv" # Ensure this file name is correct
|
| 24 |
try:
|
| 25 |
df = pd.read_csv(file_path)
|
| 26 |
df = df.fillna('')
|
| 27 |
+
print(f"✅ Successfully loaded {len(df)} chunks from {file_path}.")
|
| 28 |
except FileNotFoundError:
|
| 29 |
+
print(f"❌ Error: File {file_path} not found.")
|
| 30 |
sys.exit(1)
|
| 31 |
|
| 32 |
+
# --- 3. Prepare Data ---
|
| 33 |
documents = df['chunk_text'].tolist()
|
| 34 |
metadatas = df[['main_topic', 'chunk_title', 'src_url']].to_dict('records')
|
| 35 |
ids = df['chunk_id'].tolist()
|
| 36 |
|
| 37 |
+
# --- 4. CREATE EMBEDDINGS (The Most Important Part) ---
|
| 38 |
+
# app.py creates embeddings 'on-the-fly' for 1 question
|
| 39 |
+
# This script creates embeddings for ALL documents at once
|
| 40 |
+
print(f"Starting embedding process for {len(documents)} documents...")
|
| 41 |
embeddings = model.encode(documents, show_progress_bar=True)
|
| 42 |
+
print("✅ Embeddings complete.")
|
| 43 |
|
| 44 |
+
# --- 5. Add to ChromaDB ---
|
| 45 |
+
# Delete old data (if any) to prevent duplication
|
| 46 |
try:
|
| 47 |
collection.delete(ids=ids)
|
| 48 |
+
print("Old data in the collection successfully deleted.")
|
| 49 |
except Exception as e:
|
| 50 |
+
print("No old data to delete, continuing...")
|
| 51 |
|
| 52 |
+
print("Adding new data to ChromaDB (in batches)...")
|
| 53 |
batch_size = 100
|
| 54 |
for i in range(0, len(ids), batch_size):
|
| 55 |
+
print(f" Adding batch {i//batch_size + 1}...")
|
|
|
|
| 56 |
collection.add(
|
| 57 |
embeddings=embeddings[i:i+batch_size].tolist(),
|
| 58 |
documents=documents[i:i+batch_size],
|
|
|
|
| 60 |
ids=ids[i:i+batch_size]
|
| 61 |
)
|
| 62 |
|
| 63 |
+
print(f"🎉 ALL DONE! {collection.count()} documents successfully stored in ChromaDB.")
|
static/script.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
| 1 |
document.addEventListener("DOMContentLoaded", () => {
|
| 2 |
-
// === 1.
|
| 3 |
const sendButton = document.getElementById("send-button");
|
| 4 |
const chatInput = document.getElementById("chat-input");
|
| 5 |
const historyList = document.getElementById("history-list");
|
| 6 |
const chatLog = document.getElementById("chat-log-area");
|
| 7 |
const newChatButton = document.querySelector(".new-chat-btn");
|
| 8 |
-
const clearAllButton = document.querySelector(".clear-all");
|
| 9 |
|
| 10 |
const emptyChatPlaceholder = `
|
| 11 |
<div class="empty-chat-placeholder">
|
| 12 |
<i class="fa-solid fa-robot"></i>
|
| 13 |
-
<h2>Hello
|
| 14 |
</div>
|
| 15 |
`;
|
| 16 |
|
| 17 |
-
// === 2.
|
| 18 |
let conversations = {};
|
| 19 |
let currentConversationId = null;
|
| 20 |
let currentUserId = getOrCreateUserId();
|
|
@@ -39,46 +39,46 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
| 39 |
}
|
| 40 |
});
|
| 41 |
|
| 42 |
-
// <-- LISTENER
|
| 43 |
clearAllButton.addEventListener("click", (e) => {
|
| 44 |
e.preventDefault();
|
| 45 |
clearAllConversations();
|
| 46 |
});
|
| 47 |
|
| 48 |
-
// === 4.
|
| 49 |
|
| 50 |
/**
|
| 51 |
-
*
|
| 52 |
*/
|
| 53 |
async function clearAllConversations() {
|
| 54 |
-
if (!confirm("
|
| 55 |
return;
|
| 56 |
}
|
| 57 |
|
| 58 |
try {
|
| 59 |
-
const response = await fetch("http://localhost:
|
| 60 |
method: "DELETE",
|
| 61 |
headers: { "Content-Type": "application/json" },
|
| 62 |
-
body: JSON.stringify({ userId: currentUserId }) //
|
| 63 |
});
|
| 64 |
|
| 65 |
if (!response.ok) {
|
| 66 |
-
throw new Error("
|
| 67 |
}
|
| 68 |
|
| 69 |
-
//
|
| 70 |
conversations = {};
|
| 71 |
-
startNewChat(); //
|
| 72 |
|
| 73 |
-
console.log("
|
| 74 |
|
| 75 |
} catch (error) {
|
| 76 |
console.error("Error clearing conversations:", error);
|
| 77 |
-
alert("
|
| 78 |
}
|
| 79 |
}
|
| 80 |
|
| 81 |
-
// ... (
|
| 82 |
|
| 83 |
function getOrCreateUserId() {
|
| 84 |
let userId = localStorage.getItem('anonymousUserId');
|
|
@@ -127,7 +127,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
| 127 |
chatLog.scrollTop = chatLog.scrollHeight;
|
| 128 |
|
| 129 |
try {
|
| 130 |
-
const response = await fetch("http://localhost:
|
| 131 |
method: "POST",
|
| 132 |
headers: { "Content-Type": "application/json" },
|
| 133 |
body: JSON.stringify({
|
|
@@ -159,7 +159,7 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
| 159 |
} catch (error) {
|
| 160 |
console.error("Error sending message:", error);
|
| 161 |
const p = loadingDiv.querySelector(".message-content p");
|
| 162 |
-
p.textContent = "
|
| 163 |
}
|
| 164 |
|
| 165 |
chatLog.scrollTop = chatLog.scrollHeight;
|
|
@@ -229,13 +229,13 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
| 229 |
return messageDiv;
|
| 230 |
}
|
| 231 |
|
| 232 |
-
// ---
|
| 233 |
async function initializeApp() {
|
| 234 |
if (!currentUserId) return;
|
| 235 |
try {
|
| 236 |
-
const response = await fetch(`http://localhost:
|
| 237 |
if (!response.ok) {
|
| 238 |
-
throw new Error("
|
| 239 |
}
|
| 240 |
const data = await response.json();
|
| 241 |
conversations = {};
|
|
|
|
| 1 |
document.addEventListener("DOMContentLoaded", () => {
|
| 2 |
+
// === 1. CATCHING DOM ELEMENTS ===
|
| 3 |
const sendButton = document.getElementById("send-button");
|
| 4 |
const chatInput = document.getElementById("chat-input");
|
| 5 |
const historyList = document.getElementById("history-list");
|
| 6 |
const chatLog = document.getElementById("chat-log-area");
|
| 7 |
const newChatButton = document.querySelector(".new-chat-btn");
|
| 8 |
+
const clearAllButton = document.querySelector(".clear-all");
|
| 9 |
|
| 10 |
const emptyChatPlaceholder = `
|
| 11 |
<div class="empty-chat-placeholder">
|
| 12 |
<i class="fa-solid fa-robot"></i>
|
| 13 |
+
<h2>Hello! I am your Enviro-Edu Assistant. How may I assist you today?</h2>
|
| 14 |
</div>
|
| 15 |
`;
|
| 16 |
|
| 17 |
+
// === 2. APPLICATION MAIN DATA (STATE) ===
|
| 18 |
let conversations = {};
|
| 19 |
let currentConversationId = null;
|
| 20 |
let currentUserId = getOrCreateUserId();
|
|
|
|
| 39 |
}
|
| 40 |
});
|
| 41 |
|
| 42 |
+
// <-- NEW LISTENER FOR “CLEAR ALL” ---
|
| 43 |
clearAllButton.addEventListener("click", (e) => {
|
| 44 |
e.preventDefault();
|
| 45 |
clearAllConversations();
|
| 46 |
});
|
| 47 |
|
| 48 |
+
// === 4. MAIN FUNCTIONS ===
|
| 49 |
|
| 50 |
/**
|
| 51 |
+
* NEW FEATURE: Delete all conversations
|
| 52 |
*/
|
| 53 |
async function clearAllConversations() {
|
| 54 |
+
if (!confirm("Are you sure you want to delete all conversations? This action cannot be undone.")) {
|
| 55 |
return;
|
| 56 |
}
|
| 57 |
|
| 58 |
try {
|
| 59 |
+
const response = await fetch("http://localhost:5000/api/conversations", {
|
| 60 |
method: "DELETE",
|
| 61 |
headers: { "Content-Type": "application/json" },
|
| 62 |
+
body: JSON.stringify({ userId: currentUserId }) // Send userID to be deleted
|
| 63 |
});
|
| 64 |
|
| 65 |
if (!response.ok) {
|
| 66 |
+
throw new Error("Failed to delete history on the server.");
|
| 67 |
}
|
| 68 |
|
| 69 |
+
// If the server is successful, clear the frontend state
|
| 70 |
conversations = {};
|
| 71 |
+
startNewChat(); // This will clear the UI (render history & chat log).
|
| 72 |
|
| 73 |
+
console.log("All conversations have been successfully deleted.");
|
| 74 |
|
| 75 |
} catch (error) {
|
| 76 |
console.error("Error clearing conversations:", error);
|
| 77 |
+
alert("An error occurred while deleting history.");
|
| 78 |
}
|
| 79 |
}
|
| 80 |
|
| 81 |
+
// ... (The functions getOrCreateUserId, startNewChat, sendMessage, etc. remain exactly the same) ...
|
| 82 |
|
| 83 |
function getOrCreateUserId() {
|
| 84 |
let userId = localStorage.getItem('anonymousUserId');
|
|
|
|
| 127 |
chatLog.scrollTop = chatLog.scrollHeight;
|
| 128 |
|
| 129 |
try {
|
| 130 |
+
const response = await fetch("http://localhost:5000/api/chat", {
|
| 131 |
method: "POST",
|
| 132 |
headers: { "Content-Type": "application/json" },
|
| 133 |
body: JSON.stringify({
|
|
|
|
| 159 |
} catch (error) {
|
| 160 |
console.error("Error sending message:", error);
|
| 161 |
const p = loadingDiv.querySelector(".message-content p");
|
| 162 |
+
p.textContent = "Sorry, an error occurred. Please try again.";
|
| 163 |
}
|
| 164 |
|
| 165 |
chatLog.scrollTop = chatLog.scrollHeight;
|
|
|
|
| 229 |
return messageDiv;
|
| 230 |
}
|
| 231 |
|
| 232 |
+
// --- Application Initialization ---
|
| 233 |
async function initializeApp() {
|
| 234 |
if (!currentUserId) return;
|
| 235 |
try {
|
| 236 |
+
const response = await fetch(`http://localhost:5000/api/conversations?userId=${currentUserId}`);
|
| 237 |
if (!response.ok) {
|
| 238 |
+
throw new Error("Failed to load history");
|
| 239 |
}
|
| 240 |
const data = await response.json();
|
| 241 |
conversations = {};
|
static/style.css
CHANGED
|
@@ -1,31 +1,31 @@
|
|
| 1 |
-
/* Reset CSS
|
| 2 |
* {
|
| 3 |
margin: 0;
|
| 4 |
padding: 0;
|
| 5 |
box-sizing: border-box;
|
| 6 |
}
|
| 7 |
|
| 8 |
-
/* 1.
|
| 9 |
body {
|
| 10 |
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
| 11 |
background-color: #f7f7f8;
|
| 12 |
color: #1f2328;
|
| 13 |
/* display: flex; */
|
| 14 |
/* height: 100vh; */
|
| 15 |
-
/* overflow: hidden;
|
| 16 |
}
|
| 17 |
|
| 18 |
-
/* 2.
|
| 19 |
-
/*
|
| 20 |
body:not(.landing-page) {
|
| 21 |
display: flex;
|
| 22 |
height: 100vh;
|
| 23 |
-
overflow: hidden; /*
|
| 24 |
}
|
| 25 |
|
| 26 |
-
/* --- 3. STYLE
|
| 27 |
|
| 28 |
-
.landing-page { /*
|
| 29 |
display: flex;
|
| 30 |
align-items: center;
|
| 31 |
justify-content: center;
|
|
@@ -55,10 +55,10 @@ body:not(.landing-page) {
|
|
| 55 |
font-size: 1.1em;
|
| 56 |
font-weight: bold;
|
| 57 |
border-radius: 8px;
|
| 58 |
-
/*
|
| 59 |
}
|
| 60 |
|
| 61 |
-
/* --- 1. Sidebar (
|
| 62 |
.sidebar {
|
| 63 |
width: 260px;
|
| 64 |
background-color: #ffffff;
|
|
@@ -78,7 +78,7 @@ body:not(.landing-page) {
|
|
| 78 |
.new-chat-btn {
|
| 79 |
flex-grow: 1;
|
| 80 |
padding: 12px;
|
| 81 |
-
background-color: #4a4aef;
|
| 82 |
color: white;
|
| 83 |
border: none;
|
| 84 |
border-radius: 8px;
|
|
@@ -86,7 +86,7 @@ body:not(.landing-page) {
|
|
| 86 |
font-weight: 500;
|
| 87 |
cursor: pointer;
|
| 88 |
text-align: left;
|
| 89 |
-
text-decoration: none; /*
|
| 90 |
}
|
| 91 |
|
| 92 |
.new-chat-btn i {
|
|
@@ -102,8 +102,8 @@ body:not(.landing-page) {
|
|
| 102 |
}
|
| 103 |
|
| 104 |
.chat-history {
|
| 105 |
-
flex-grow: 1; /*
|
| 106 |
-
overflow-y: auto; /* Scroll
|
| 107 |
}
|
| 108 |
|
| 109 |
.history-header {
|
|
@@ -182,9 +182,9 @@ body:not(.landing-page) {
|
|
| 182 |
background-color: #ddd;
|
| 183 |
}
|
| 184 |
|
| 185 |
-
/* --- 2. Chat Area (
|
| 186 |
.chat-area {
|
| 187 |
-
flex-grow: 1; /*
|
| 188 |
display: flex;
|
| 189 |
flex-direction: column;
|
| 190 |
background-color: #ffffff;
|
|
@@ -196,21 +196,21 @@ body:not(.landing-page) {
|
|
| 196 |
border-bottom: 1px solid #e0e0e0;
|
| 197 |
font-size: 1.1em;
|
| 198 |
font-weight: 600;
|
| 199 |
-
flex-shrink: 0; /*
|
| 200 |
}
|
| 201 |
|
| 202 |
.chat-log {
|
| 203 |
-
flex-grow: 1; /*
|
| 204 |
-
overflow-y: auto; /*
|
| 205 |
padding: 24px;
|
| 206 |
display: flex;
|
| 207 |
flex-direction: column;
|
| 208 |
-
gap: 20px; /*
|
| 209 |
}
|
| 210 |
|
| 211 |
-
/* --- STYLE
|
| 212 |
.empty-chat-placeholder {
|
| 213 |
-
flex-grow: 1; /*
|
| 214 |
display: flex;
|
| 215 |
flex-direction: column;
|
| 216 |
align-items: center;
|
|
@@ -232,11 +232,11 @@ body:not(.landing-page) {
|
|
| 232 |
/* ------------------------------- */
|
| 233 |
|
| 234 |
|
| 235 |
-
/* Styling
|
| 236 |
.chat-message {
|
| 237 |
display: flex;
|
| 238 |
gap: 16px;
|
| 239 |
-
max-width: 90%; /*
|
| 240 |
}
|
| 241 |
|
| 242 |
.chat-message .avatar {
|
|
@@ -263,7 +263,7 @@ body:not(.landing-page) {
|
|
| 263 |
padding-top: 8px;
|
| 264 |
}
|
| 265 |
|
| 266 |
-
.message-content strong { /*
|
| 267 |
display: block;
|
| 268 |
margin-bottom: 4px;
|
| 269 |
font-size: 1.05em;
|
|
@@ -275,7 +275,7 @@ body:not(.landing-page) {
|
|
| 275 |
}
|
| 276 |
|
| 277 |
.message-content ol {
|
| 278 |
-
padding-left: 20px; /*
|
| 279 |
margin-top: 10px;
|
| 280 |
}
|
| 281 |
|
|
@@ -283,24 +283,24 @@ body:not(.landing-page) {
|
|
| 283 |
margin-bottom: 8px;
|
| 284 |
}
|
| 285 |
|
| 286 |
-
/*
|
| 287 |
.chat-input-area {
|
| 288 |
padding: 24px;
|
| 289 |
border-top: 1px solid #e0e0e0;
|
| 290 |
background-color: #fff;
|
| 291 |
-
flex-shrink: 0; /*
|
| 292 |
}
|
| 293 |
|
| 294 |
.input-wrapper {
|
| 295 |
display: flex;
|
| 296 |
position: relative;
|
| 297 |
-
max-width: 900px; /*
|
| 298 |
-
margin: 0 auto;
|
| 299 |
}
|
| 300 |
|
| 301 |
.chat-input-area input {
|
| 302 |
flex-grow: 1;
|
| 303 |
-
padding: 16px 50px 16px 20px; /*
|
| 304 |
border: 1px solid #ccc;
|
| 305 |
border-radius: 8px;
|
| 306 |
font-size: 1em;
|
|
@@ -322,7 +322,7 @@ body:not(.landing-page) {
|
|
| 322 |
border: none;
|
| 323 |
width: 40px;
|
| 324 |
height: 40px;
|
| 325 |
-
border-radius: 50%; /*
|
| 326 |
font-size: 1.1em;
|
| 327 |
cursor: pointer;
|
| 328 |
display: flex;
|
|
@@ -330,11 +330,11 @@ body:not(.landing-page) {
|
|
| 330 |
justify-content: center;
|
| 331 |
}
|
| 332 |
|
| 333 |
-
/*
|
| 334 |
.upgrade-tab {
|
| 335 |
position: fixed;
|
| 336 |
top: 50%;
|
| 337 |
-
right: -60px; /*
|
| 338 |
transform: translateY(-50%) rotate(-90deg);
|
| 339 |
background-color: #4a4aef;
|
| 340 |
color: white;
|
|
|
|
| 1 |
+
/* Reset basic CSS */
|
| 2 |
* {
|
| 3 |
margin: 0;
|
| 4 |
padding: 0;
|
| 5 |
box-sizing: border-box;
|
| 6 |
}
|
| 7 |
|
| 8 |
+
/* 1. UNIVERSAL BODY STYLE (Basic) */
|
| 9 |
body {
|
| 10 |
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
| 11 |
background-color: #f7f7f8;
|
| 12 |
color: #1f2328;
|
| 13 |
/* display: flex; */
|
| 14 |
/* height: 100vh; */
|
| 15 |
+
/* overflow: hidden; Preventing body scroll */
|
| 16 |
}
|
| 17 |
|
| 18 |
+
/* 2. SPECIAL BODY STYLE FOR CHAT PAGE */
|
| 19 |
+
/* This selector ensures that the style only applies if the body is NOT a landing page */
|
| 20 |
body:not(.landing-page) {
|
| 21 |
display: flex;
|
| 22 |
height: 100vh;
|
| 23 |
+
overflow: hidden; /* Preventing body scroll */
|
| 24 |
}
|
| 25 |
|
| 26 |
+
/* --- 3. STYLE FOR LANDING PAGE (index.html) --- */
|
| 27 |
|
| 28 |
+
.landing-page { /* Using the class added in index.html */
|
| 29 |
display: flex;
|
| 30 |
align-items: center;
|
| 31 |
justify-content: center;
|
|
|
|
| 55 |
font-size: 1.1em;
|
| 56 |
font-weight: bold;
|
| 57 |
border-radius: 8px;
|
| 58 |
+
/* Add hover or other styles if needed */
|
| 59 |
}
|
| 60 |
|
| 61 |
+
/* --- 1. Sidebar (Left) --- */
|
| 62 |
.sidebar {
|
| 63 |
width: 260px;
|
| 64 |
background-color: #ffffff;
|
|
|
|
| 78 |
.new-chat-btn {
|
| 79 |
flex-grow: 1;
|
| 80 |
padding: 12px;
|
| 81 |
+
background-color: #4a4aef;
|
| 82 |
color: white;
|
| 83 |
border: none;
|
| 84 |
border-radius: 8px;
|
|
|
|
| 86 |
font-weight: 500;
|
| 87 |
cursor: pointer;
|
| 88 |
text-align: left;
|
| 89 |
+
text-decoration: none; /* Added so that the link is not underlined */
|
| 90 |
}
|
| 91 |
|
| 92 |
.new-chat-btn i {
|
|
|
|
| 102 |
}
|
| 103 |
|
| 104 |
.chat-history {
|
| 105 |
+
flex-grow: 1; /* Filling the remaining space with history */
|
| 106 |
+
overflow-y: auto; /* Scroll if history is full */
|
| 107 |
}
|
| 108 |
|
| 109 |
.history-header {
|
|
|
|
| 182 |
background-color: #ddd;
|
| 183 |
}
|
| 184 |
|
| 185 |
+
/* --- 2. Chat Area (Right) --- */
|
| 186 |
.chat-area {
|
| 187 |
+
flex-grow: 1; /* Filling the remaining space */
|
| 188 |
display: flex;
|
| 189 |
flex-direction: column;
|
| 190 |
background-color: #ffffff;
|
|
|
|
| 196 |
border-bottom: 1px solid #e0e0e0;
|
| 197 |
font-size: 1.1em;
|
| 198 |
font-weight: 600;
|
| 199 |
+
flex-shrink: 0; /* Preventing headers from shrinking */
|
| 200 |
}
|
| 201 |
|
| 202 |
.chat-log {
|
| 203 |
+
flex-grow: 1; /* Filling the center space */
|
| 204 |
+
overflow-y: auto; /* This is a scrollable chat area. */
|
| 205 |
padding: 24px;
|
| 206 |
display: flex;
|
| 207 |
flex-direction: column;
|
| 208 |
+
gap: 20px; /* Distance between messages */
|
| 209 |
}
|
| 210 |
|
| 211 |
+
/* --- STYLE FOR EMPTY STATE --- */
|
| 212 |
.empty-chat-placeholder {
|
| 213 |
+
flex-grow: 1; /* Ensuring placeholders fill the chat log space */
|
| 214 |
display: flex;
|
| 215 |
flex-direction: column;
|
| 216 |
align-items: center;
|
|
|
|
| 232 |
/* ------------------------------- */
|
| 233 |
|
| 234 |
|
| 235 |
+
/* Styling for each message (Saved for later when you add a chat) */
|
| 236 |
.chat-message {
|
| 237 |
display: flex;
|
| 238 |
gap: 16px;
|
| 239 |
+
max-width: 90%; /* So that it is not too wide */
|
| 240 |
}
|
| 241 |
|
| 242 |
.chat-message .avatar {
|
|
|
|
| 263 |
padding-top: 8px;
|
| 264 |
}
|
| 265 |
|
| 266 |
+
.message-content strong { /* For title Enviro Education Tools Chatbot */
|
| 267 |
display: block;
|
| 268 |
margin-bottom: 4px;
|
| 269 |
font-size: 1.05em;
|
|
|
|
| 275 |
}
|
| 276 |
|
| 277 |
.message-content ol {
|
| 278 |
+
padding-left: 20px; /* Indentation for ordered lists */
|
| 279 |
margin-top: 10px;
|
| 280 |
}
|
| 281 |
|
|
|
|
| 283 |
margin-bottom: 8px;
|
| 284 |
}
|
| 285 |
|
| 286 |
+
/* Input Area Below */
|
| 287 |
.chat-input-area {
|
| 288 |
padding: 24px;
|
| 289 |
border-top: 1px solid #e0e0e0;
|
| 290 |
background-color: #fff;
|
| 291 |
+
flex-shrink: 0; /* Preventing the input area from shrinking */
|
| 292 |
}
|
| 293 |
|
| 294 |
.input-wrapper {
|
| 295 |
display: flex;
|
| 296 |
position: relative;
|
| 297 |
+
max-width: 900px; /* Limit input width */
|
| 298 |
+
margin: 0 auto;
|
| 299 |
}
|
| 300 |
|
| 301 |
.chat-input-area input {
|
| 302 |
flex-grow: 1;
|
| 303 |
+
padding: 16px 50px 16px 20px; /* Make space for the send button */
|
| 304 |
border: 1px solid #ccc;
|
| 305 |
border-radius: 8px;
|
| 306 |
font-size: 1em;
|
|
|
|
| 322 |
border: none;
|
| 323 |
width: 40px;
|
| 324 |
height: 40px;
|
| 325 |
+
border-radius: 50%; /* Make a circle */
|
| 326 |
font-size: 1.1em;
|
| 327 |
cursor: pointer;
|
| 328 |
display: flex;
|
|
|
|
| 330 |
justify-content: center;
|
| 331 |
}
|
| 332 |
|
| 333 |
+
/* The “Upgrade” tab on the right side */
|
| 334 |
.upgrade-tab {
|
| 335 |
position: fixed;
|
| 336 |
top: 50%;
|
| 337 |
+
right: -60px; /* Adjust the position so that it sticks out slightly. */
|
| 338 |
transform: translateY(-50%) rotate(-90deg);
|
| 339 |
background-color: #4a4aef;
|
| 340 |
color: white;
|
templates/chat.html
CHANGED
|
@@ -47,7 +47,7 @@
|
|
| 47 |
<section class="chat-area">
|
| 48 |
|
| 49 |
<header class="chat-header">
|
| 50 |
-
|
| 51 |
</header>
|
| 52 |
|
| 53 |
<div class="chat-log" id="chat-log-area"> <div class="empty-chat-placeholder">
|
|
@@ -59,7 +59,7 @@
|
|
| 59 |
|
| 60 |
<footer class="chat-input-area">
|
| 61 |
<div class="input-wrapper">
|
| 62 |
-
<input type="text" id="chat-input" placeholder="
|
| 63 |
<button class="send-btn" id="send-button">
|
| 64 |
<i class="fa-solid fa-paper-plane"></i>
|
| 65 |
</button>
|
|
|
|
| 47 |
<section class="chat-area">
|
| 48 |
|
| 49 |
<header class="chat-header">
|
| 50 |
+
Enviro Education Tools Chatbot
|
| 51 |
</header>
|
| 52 |
|
| 53 |
<div class="chat-log" id="chat-log-area"> <div class="empty-chat-placeholder">
|
|
|
|
| 59 |
|
| 60 |
<footer class="chat-input-area">
|
| 61 |
<div class="input-wrapper">
|
| 62 |
+
<input type="text" id="chat-input" placeholder="Type your question...">
|
| 63 |
<button class="send-btn" id="send-button">
|
| 64 |
<i class="fa-solid fa-paper-plane"></i>
|
| 65 |
</button>
|
templates/index.html
CHANGED
|
@@ -25,12 +25,12 @@
|
|
| 25 |
</head>
|
| 26 |
<body class="landing-page">
|
| 27 |
<div class="landing-container">
|
| 28 |
-
<h1>
|
| 29 |
<p>
|
| 30 |
-
|
| 31 |
-
|
| 32 |
</p>
|
| 33 |
-
<a href="/chat" class="start-chat-btn">
|
| 34 |
</div>
|
| 35 |
</body>
|
| 36 |
</html>
|
|
|
|
| 25 |
</head>
|
| 26 |
<body class="landing-page">
|
| 27 |
<div class="landing-container">
|
| 28 |
+
<h1>Welcome to the Enviro Education Chatbot!</h1>
|
| 29 |
<p>
|
| 30 |
+
This AI assistant is trained based on our product data.
|
| 31 |
+
Ask anything about Air, Water, and Soil Quality Testers.
|
| 32 |
</p>
|
| 33 |
+
<a href="/chat" class="start-chat-btn">Start Chat Now</a>
|
| 34 |
</div>
|
| 35 |
</body>
|
| 36 |
</html>
|