deedrop1140 commited on
Commit
8826cea
·
verified ·
1 Parent(s): 8421bae

Upload 34 files

Browse files
Dockerfile ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use official Python base image
2
+ FROM python:3.10-slim
3
+
4
+ # Prevent Python from writing .pyc files & enable buffered output
5
+ ENV PYTHONDONTWRITEBYTECODE=1
6
+ ENV PYTHONUNBUFFERED=1
7
+
8
+ # Set working directory
9
+ WORKDIR /app
10
+
11
+ # Install system dependencies (useful for pandas, numpy, etc.)
12
+ RUN apt-get update && apt-get install -y --no-install-recommends \
13
+ build-essential \
14
+ git \
15
+ && rm -rf /var/lib/apt/lists/*
16
+
17
+ # Copy requirement file
18
+ COPY requirements.txt .
19
+
20
+ # Install Python dependencies
21
+ RUN pip install --no-cache-dir -r requirements.txt
22
+
23
+ # Copy application files
24
+ COPY . .
25
+
26
+ # Expose Flask default port
27
+ EXPOSE 5000
28
+
29
+ # Set Flask environment variables
30
+ ENV FLASK_APP=app.py
31
+ ENV FLASK_RUN_HOST=0.0.0.0
32
+
33
+ # Run Flask application
34
+ CMD ["flask", "run"]
app.py ADDED
@@ -0,0 +1,664 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # from flask import Flask, render_template, request, jsonify
2
+ # import joblib
3
+ # import numpy as np
4
+ # from transformers import pipeline, AutoTokenizer, AutoModelForSequenceClassification, AutoModelForSeq2SeqLM
5
+ # from datetime import datetime
6
+
7
+ # app = Flask(__name__)
8
+
9
+ # # -----------------------------
10
+ # # Load Models
11
+ # # -----------------------------
12
+ # print("⏳ Loading models...")
13
+
14
+ # # 1️⃣ Category Classifier
15
+ # clf_model = joblib.load("models/complaint_classifier_rf.pkl")
16
+ # vectorizer = joblib.load("models/tfidf_vectorizer.pkl")
17
+
18
+ # # 2️⃣ Fine-Tuned BERT Sentiment Model
19
+ # sentiment_path = "models/bert_finetuned"
20
+ # sent_tokenizer = AutoTokenizer.from_pretrained(sentiment_path)
21
+ # sent_model = AutoModelForSequenceClassification.from_pretrained(sentiment_path)
22
+ # sentiment_pipe = pipeline("text-classification", model=sent_model, tokenizer=sent_tokenizer)
23
+
24
+ # # 3️⃣ Chatbot (T5)
25
+ # chatbot_path = "models/chatbot_t5"
26
+ # chat_tokenizer = AutoTokenizer.from_pretrained(chatbot_path)
27
+ # chat_model = AutoModelForSeq2SeqLM.from_pretrained(chatbot_path)
28
+ # chatbot = pipeline("text2text-generation", model=chat_model, tokenizer=chat_tokenizer)
29
+
30
+ # print("✅ All models loaded successfully!")
31
+
32
+ # # In-memory sentiment data (for analytics)
33
+ # sentiment_log = {"Positive": 0, "Negative": 0, "Neutral": 0}
34
+ # time_log = []
35
+
36
+ # # -----------------------------
37
+ # # Helper Functions
38
+ # # -----------------------------
39
+ # def predict_category(text):
40
+ # vec = vectorizer.transform([text])
41
+ # return clf_model.predict(vec)[0]
42
+
43
+ # def predict_sentiment(text):
44
+ # result = sentiment_pipe(text, truncation=True, max_length=128)[0]
45
+ # label = result["label"]
46
+ # score = round(result["score"] * 100, 2)
47
+ # if "NEG" in label.upper():
48
+ # sentiment = "Negative"
49
+ # elif "POS" in label.upper():
50
+ # sentiment = "Positive"
51
+ # else:
52
+ # sentiment = "Neutral"
53
+ # return sentiment, score
54
+
55
+ # def chatbot_reply(msg, category, sentiment):
56
+ # prompt = f"User: {msg} | Category: {category} | Sentiment: {sentiment}"
57
+ # reply = chatbot(prompt, max_new_tokens=80, num_return_sequences=1)[0]["generated_text"]
58
+ # return reply
59
+
60
+ # # -----------------------------
61
+ # # Routes
62
+ # # -----------------------------
63
+ # @app.route('/')
64
+ # def home():
65
+ # return render_template('index.html')
66
+
67
+ # @app.route('/analyze', methods=['POST'])
68
+ # def analyze():
69
+ # user_text = request.form['user_input'].strip().lower()
70
+
71
+ # # 🧠 1️⃣ Check for simple greetings or small talk
72
+ # smalltalk_responses = {
73
+ # "hi": "Hey there 👋 How can I help you today?",
74
+ # "hello": "Hello! How’s your day going?",
75
+ # "hey": "Hey! What can I do for you?",
76
+ # "thanks": "You're welcome 😊 Always happy to help!",
77
+ # "thank you": "No problem! Glad to assist 🙌",
78
+ # "good morning": "Good morning ☀️ Ready to get your deliveries moving?",
79
+ # "good evening": "Good evening 🌙 How can I assist?",
80
+ # "bye": "Goodbye 👋 Take care!",
81
+ # }
82
+
83
+ # for phrase, reply in smalltalk_responses.items():
84
+ # if user_text.startswith(phrase):
85
+ # return jsonify({
86
+ # "category": "Small Talk",
87
+ # "sentiment": "Neutral",
88
+ # "confidence": 100,
89
+ # "reply": reply
90
+ # })
91
+
92
+ # # 🧩 2️⃣ Continue with complaint classification
93
+ # category = predict_category(user_text)
94
+ # sentiment, confidence = predict_sentiment(user_text)
95
+ # bot_reply = chatbot_reply(user_text, category, sentiment)
96
+
97
+ # # 🧠 Log analytics
98
+ # sentiment_log[sentiment] += 1
99
+ # time_log.append({
100
+ # "time": datetime.now().strftime("%H:%M:%S"),
101
+ # "sentiment": sentiment
102
+ # })
103
+
104
+ # return jsonify({
105
+ # "category": category,
106
+ # "sentiment": sentiment,
107
+ # "confidence": confidence,
108
+ # "reply": bot_reply
109
+ # })
110
+
111
+ # @app.route('/analytics', methods=['GET'])
112
+ # def analytics():
113
+ # """Return sentiment distribution for chart"""
114
+ # return jsonify({
115
+ # "labels": list(sentiment_log.keys()),
116
+ # "values": list(sentiment_log.values()),
117
+ # "timeline": time_log[-20:]
118
+ # })
119
+
120
+
121
+ # if __name__ == "__main__":
122
+ # app.run(debug=True)
123
+ # app.py -- merged app (auth + DB + ML + chat + debug)
124
+ # app.py -- merged app (auth + DB + ML + chat + debug) - NO SMALL-TALK
125
+ import os
126
+ import random
127
+ import string
128
+ import threading
129
+ from datetime import datetime, timedelta
130
+
131
+ from flask import Flask, render_template, request, redirect, url_for, flash, jsonify
132
+ from flask_sqlalchemy import SQLAlchemy
133
+ from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
134
+ from flask_mail import Mail, Message
135
+ from werkzeug.security import generate_password_hash, check_password_hash
136
+
137
+ # ML imports
138
+ import joblib
139
+ from transformers import pipeline, AutoTokenizer, AutoModelForSequenceClassification, AutoModelForSeq2SeqLM
140
+
141
+ # ---------------------------
142
+ # Config
143
+ # ---------------------------
144
+ BASE_DIR = os.path.dirname(os.path.abspath(__file__))
145
+ DB_PATH = os.path.join(BASE_DIR, "models.db")
146
+
147
+ app = Flask(__name__)
148
+ app.config['SECRET_KEY'] = os.environ.get("FLASK_SECRET", "super-secret-key")
149
+ app.config['SQLALCHEMY_DATABASE_URI'] = f"sqlite:///{DB_PATH}"
150
+ app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
151
+
152
+ # Mail config (dev defaults)
153
+ app.config['MAIL_SERVER'] = os.environ.get("MAIL_SERVER", "localhost")
154
+ app.config['MAIL_PORT'] = int(os.environ.get("MAIL_PORT", 1025))
155
+ app.config['MAIL_USERNAME'] = os.environ.get("MAIL_USERNAME", "")
156
+ app.config['MAIL_PASSWORD'] = os.environ.get("MAIL_PASSWORD", "")
157
+ app.config['MAIL_USE_TLS'] = os.environ.get("MAIL_USE_TLS", "False") == "True"
158
+ app.config['MAIL_USE_SSL'] = os.environ.get("MAIL_USE_SSL", "False") == "True"
159
+ app.config['MAIL_DEFAULT_SENDER'] = os.environ.get("MAIL_DEFAULT_SENDER", "noreply@fastship.ai")
160
+
161
+ # ---------------------------
162
+ # Extensions
163
+ # ---------------------------
164
+ db = SQLAlchemy(app)
165
+ login_manager = LoginManager(app)
166
+ login_manager.login_view = "login"
167
+ mail = Mail(app)
168
+
169
+ # ---------------------------
170
+ # DB Models
171
+ # ---------------------------
172
+ class User(UserMixin, db.Model):
173
+ id = db.Column(db.Integer, primary_key=True)
174
+ fullname = db.Column(db.String(120))
175
+ email = db.Column(db.String(120), unique=True, nullable=False)
176
+ password_hash = db.Column(db.String(256), nullable=False)
177
+ phone = db.Column(db.String(32))
178
+ address = db.Column(db.String(256))
179
+ is_admin = db.Column(db.Boolean, default=False)
180
+ created_at = db.Column(db.DateTime, default=datetime.utcnow)
181
+
182
+ def set_password(self, password):
183
+ self.password_hash = generate_password_hash(password)
184
+
185
+ def check_password(self, password):
186
+ return check_password_hash(self.password_hash, password)
187
+
188
+
189
+ class OTP(db.Model):
190
+ id = db.Column(db.Integer, primary_key=True)
191
+ email = db.Column(db.String(120), nullable=False)
192
+ code = db.Column(db.String(8), nullable=False)
193
+ created_at = db.Column(db.DateTime, default=datetime.utcnow)
194
+ expires_at = db.Column(db.DateTime, nullable=False)
195
+
196
+
197
+ class ChatMessage(db.Model):
198
+ id = db.Column(db.Integer, primary_key=True)
199
+ user_id = db.Column(db.Integer, nullable=True)
200
+ username = db.Column(db.String(120))
201
+ address = db.Column(db.String(256))
202
+ order_id = db.Column(db.String(64), nullable=True)
203
+ message = db.Column(db.Text, nullable=False)
204
+ category = db.Column(db.String(120))
205
+ sentiment = db.Column(db.String(32))
206
+ status = db.Column(db.String(32), default="Pending")
207
+ created_at = db.Column(db.DateTime, default=datetime.utcnow)
208
+
209
+ with app.app_context():
210
+ db.create_all()
211
+
212
+ # ---------------------------
213
+ # Login loader
214
+ # ---------------------------
215
+ @login_manager.user_loader
216
+ def load_user(user_id):
217
+ return User.query.get(int(user_id))
218
+
219
+ # ---------------------------
220
+ # Utilities
221
+ # ---------------------------
222
+ def gen_otp_code(length=6):
223
+ return "".join(random.choices(string.digits, k=length))
224
+
225
+ def send_email(to_email, subject, body):
226
+ """Try to send email. If SMTP not configured, fallback to printing to console."""
227
+ try:
228
+ msg = Message(subject, recipients=[to_email], html=body)
229
+ mail.send(msg)
230
+ app.logger.info("Email sent to %s", to_email)
231
+ return True
232
+ except Exception as e:
233
+ app.logger.warning("Could not send email (fallback). Exception: %s", e)
234
+ print("=== EMAIL Fallback ===")
235
+ print("To:", to_email)
236
+ print("Subject:", subject)
237
+ print(body)
238
+ print("======================")
239
+ return False
240
+
241
+ # ---------------------------
242
+ # ML model loading (attempt) - uses variable names matching your simple script
243
+ # ---------------------------
244
+ models_lock = threading.Lock()
245
+
246
+ clf_model = None
247
+ vectorizer = None
248
+ sentiment_pipe = None
249
+ sent_model = None
250
+ chatbot = None
251
+ chatbot_model = None
252
+
253
+ def load_models():
254
+ global clf_model, vectorizer, sentiment_pipe, sent_model, chatbot, chatbot_model
255
+ app.logger.info("Loading models from models/ ...")
256
+
257
+ # classifier + vectorizer (joblib)
258
+ try:
259
+ clf_model = joblib.load(os.path.join("models", "complaint_classifier_rf.pkl"))
260
+ vectorizer = joblib.load(os.path.join("models", "tfidf_vectorizer.pkl"))
261
+ app.logger.info("Classifier & vectorizer loaded.")
262
+ except Exception as e:
263
+ app.logger.warning("Classifier/vectorizer not loaded: %s", e)
264
+ clf_model = None
265
+ vectorizer = None
266
+
267
+ # sentiment (transformers)
268
+ try:
269
+ sentiment_path = os.path.join("models", "bert_finetuned")
270
+ sent_tokenizer = AutoTokenizer.from_pretrained(sentiment_path)
271
+ sent_model = AutoModelForSequenceClassification.from_pretrained(sentiment_path)
272
+ sentiment_pipe_local = pipeline("text-classification", model=sent_model, tokenizer=sent_tokenizer)
273
+ sentiment_pipe = sentiment_pipe_local
274
+ globals()['sentiment_pipe'] = sentiment_pipe
275
+ globals()['sent_model'] = sent_model
276
+ app.logger.info("Sentiment model loaded.")
277
+ except Exception as e:
278
+ app.logger.warning("Sentiment model not loaded: %s", e)
279
+ globals()['sentiment_pipe'] = None
280
+ globals()['sent_model'] = None
281
+
282
+ # chatbot (t5)
283
+ try:
284
+ chatbot_path = os.path.join("models", "chatbot_t5")
285
+ chat_tokenizer = AutoTokenizer.from_pretrained(chatbot_path)
286
+ chatbot_model_local = AutoModelForSeq2SeqLM.from_pretrained(chatbot_path)
287
+ chatbot_pipe = pipeline("text2text-generation", model=chatbot_model_local, tokenizer=chat_tokenizer)
288
+ globals()['chatbot'] = chatbot_pipe
289
+ globals()['chatbot_model'] = chatbot_model_local
290
+ app.logger.info("Chatbot model loaded.")
291
+ except Exception as e:
292
+ app.logger.warning("Chatbot model not loaded: %s", e)
293
+ globals()['chatbot'] = None
294
+ globals()['chatbot_model'] = None
295
+
296
+ with app.app_context():
297
+ load_models()
298
+
299
+ # -----------------------------
300
+ # Model functions (model-driven, no heuristics / no small-talk)
301
+ # -----------------------------
302
+ def predict_category(text):
303
+ """
304
+ Return predicted category string if model available,
305
+ otherwise return None (so caller returns models_missing).
306
+ """
307
+ if clf_model is None or vectorizer is None:
308
+ return None
309
+ try:
310
+ vec = vectorizer.transform([text])
311
+ return clf_model.predict(vec)[0]
312
+ except Exception as e:
313
+ app.logger.error("Classifier predict error: %s", e)
314
+ return None
315
+
316
+ def predict_sentiment(text):
317
+ """
318
+ Robust model mapping. Returns (sentiment_label, confidence_score) or (None, None) if model missing/error.
319
+ """
320
+ if sentiment_pipe is None:
321
+ return None, None
322
+ try:
323
+ with models_lock:
324
+ out = sentiment_pipe(text, truncation=True, max_length=128)[0]
325
+ raw_label = out.get("label", "")
326
+ score = round(out.get("score", 0.0) * 100, 2)
327
+
328
+ # Map LABEL_0 style to real labels if model.config.id2label exists
329
+ try:
330
+ cfg = getattr(sent_model, "config", None)
331
+ if cfg is not None and hasattr(cfg, "id2label"):
332
+ if raw_label.upper().startswith("LABEL_"):
333
+ try:
334
+ idx = int(raw_label.split("_")[-1])
335
+ mapped = cfg.id2label.get(idx, raw_label)
336
+ raw_label = mapped
337
+ except Exception:
338
+ pass
339
+ except Exception:
340
+ pass
341
+
342
+ rl = raw_label.upper()
343
+ if "NEG" in rl:
344
+ return "Negative", score
345
+ if "POS" in rl:
346
+ return "Positive", score
347
+ if "NEU" in rl:
348
+ return "Neutral", score
349
+
350
+ # default to Neutral to stay model-driven
351
+ return "Neutral", score
352
+
353
+ except Exception as e:
354
+ app.logger.error("Sentiment pipeline error: %s", e)
355
+ return None, None
356
+
357
+ def chatbot_reply(msg, category, sentiment):
358
+ """
359
+ Return chatbot-generated string if model exists, otherwise None.
360
+ """
361
+ if chatbot is None:
362
+ return None
363
+ try:
364
+ prompt = f"User: {msg} | Category: {category} | Sentiment: {sentiment}"
365
+ with models_lock:
366
+ out = chatbot(prompt, max_new_tokens=80, num_return_sequences=1)[0]
367
+ return out.get("generated_text") or out.get("text") or str(out)
368
+ except Exception as e:
369
+ app.logger.error("Chatbot pipeline error: %s", e)
370
+ return None
371
+
372
+ # -----------------------------
373
+ # In-memory analytics (optional)
374
+ # -----------------------------
375
+ sentiment_log = {"Positive": 0, "Negative": 0, "Neutral": 0}
376
+ time_log = []
377
+
378
+ # ---------------------------
379
+ # Debug / Health endpoints
380
+ # ---------------------------
381
+ @app.route('/health')
382
+ def health():
383
+ return jsonify({
384
+ "classifier_loaded": clf_model is not None,
385
+ "vectorizer_loaded": vectorizer is not None,
386
+ "sentiment_loaded": sentiment_pipe is not None,
387
+ "chatbot_loaded": chatbot is not None
388
+ })
389
+
390
+ @app.route('/debug/sentiment', methods=['GET'])
391
+ def debug_sentiment():
392
+ text = request.args.get('text', None)
393
+ if not text:
394
+ return jsonify({"error": "provide ?text=..."}), 400
395
+ pipeline_output = None
396
+ pipeline_error = None
397
+ id2label = None
398
+ if sentiment_pipe is not None:
399
+ try:
400
+ with models_lock:
401
+ pipeline_output = sentiment_pipe(text, truncation=True, max_length=128)
402
+ except Exception as e:
403
+ pipeline_error = str(e)
404
+ try:
405
+ model_obj = sent_model
406
+ if model_obj is not None and hasattr(model_obj.config, "id2label"):
407
+ id2label = dict(model_obj.config.id2label)
408
+ except Exception:
409
+ id2label = None
410
+ return jsonify({
411
+ "text": text,
412
+ "pipeline_output": pipeline_output,
413
+ "pipeline_error": pipeline_error,
414
+ "id2label": id2label
415
+ })
416
+
417
+ # ---------------------------
418
+ # Routes: Auth pages
419
+ # ---------------------------
420
+ @app.route('/')
421
+ def index():
422
+ return redirect(url_for('login'))
423
+
424
+ @app.route('/register', methods=['GET', 'POST'])
425
+ def register():
426
+ if request.method == "POST":
427
+ fullname = request.form.get("fullname", "").strip()
428
+ email = request.form.get("email", "").strip().lower()
429
+ password = request.form.get("password", "")
430
+ phone = request.form.get("phone", "")
431
+ address = request.form.get("address", "")
432
+
433
+ if not fullname or not email or not password:
434
+ flash("Please fill required fields", "warning")
435
+ return redirect(url_for("register"))
436
+
437
+ if User.query.filter_by(email=email).first():
438
+ flash("Email already registered. Try logging in.", "warning")
439
+ return redirect(url_for("login"))
440
+
441
+ user = User(fullname=fullname, email=email, phone=phone, address=address)
442
+ user.set_password(password)
443
+ db.session.add(user)
444
+ db.session.commit()
445
+ flash("Registered successfully. Please log in.", "success")
446
+ return redirect(url_for("login"))
447
+ return render_template("register.html")
448
+
449
+ @app.route('/login', methods=['GET', 'POST'])
450
+ def login():
451
+ if request.method == "POST":
452
+ email = request.form.get("email", "").strip().lower()
453
+ password = request.form.get("password", "")
454
+ user = User.query.filter_by(email=email).first()
455
+ if not user or not user.check_password(password):
456
+ flash("Invalid credentials", "danger")
457
+ return redirect(url_for("login"))
458
+ login_user(user)
459
+ flash("Logged in successfully", "success")
460
+ return redirect(url_for("chat"))
461
+ return render_template("login.html")
462
+
463
+ @app.route('/logout')
464
+ @login_required
465
+ def logout():
466
+ logout_user()
467
+ flash("Logged out", "info")
468
+ return redirect(url_for("login"))
469
+
470
+ # ---------------------------
471
+ # Forgot / Reset
472
+ # ---------------------------
473
+ @app.route('/forgot', methods=['GET', 'POST'])
474
+ def forgot():
475
+ if request.method == "POST":
476
+ email = request.form.get("email", "").strip().lower()
477
+ user = User.query.filter_by(email=email).first()
478
+ if not user:
479
+ flash("Email not registered", "warning")
480
+ return redirect(url_for("forgot"))
481
+ code = gen_otp_code()
482
+ expires = datetime.utcnow() + timedelta(minutes=10)
483
+ otp = OTP(email=email, code=code, expires_at=expires)
484
+ db.session.add(otp)
485
+ db.session.commit()
486
+
487
+ body = f"<p>Your FastShip OTP to reset password is <b>{code}</b>. It expires in 10 minutes.</p>"
488
+ send_email(email, "Your FastShip Password Reset OTP", body)
489
+ flash("OTP sent to your email (check console if SMTP is not configured).", "info")
490
+ return redirect(url_for("reset"))
491
+ return render_template("forgot.html")
492
+
493
+ @app.route('/reset', methods=['GET', 'POST'])
494
+ def reset():
495
+ if request.method == "POST":
496
+ email = request.form.get("email", "").strip().lower()
497
+ code = request.form.get("otp", "").strip()
498
+ new_password = request.form.get("password", "")
499
+
500
+ otp = OTP.query.filter_by(email=email, code=code).order_by(OTP.created_at.desc()).first()
501
+ if not otp:
502
+ flash("Invalid OTP", "danger")
503
+ return redirect(url_for("reset"))
504
+ if datetime.utcnow() > otp.expires_at:
505
+ flash("OTP expired", "danger")
506
+ return redirect(url_for("forgot"))
507
+
508
+ user = User.query.filter_by(email=email).first()
509
+ if not user:
510
+ flash("User not found", "danger")
511
+ return redirect(url_for("register"))
512
+
513
+ user.set_password(new_password)
514
+ db.session.commit()
515
+
516
+ OTP.query.filter_by(email=email).delete()
517
+ db.session.commit()
518
+
519
+ flash("Password reset successful. Please login.", "success")
520
+ return redirect(url_for("login"))
521
+ return render_template("reset.html")
522
+
523
+ # ---------------------------
524
+ # Chat page & analyze (direct model flow, NO small-talk)
525
+ # ---------------------------
526
+ @app.route('/chat', methods=['GET', 'POST'])
527
+ @login_required
528
+ def chat():
529
+ if request.method == "GET":
530
+ return render_template("chat.html", username=current_user.fullname)
531
+
532
+ user_text = request.form.get("user_input", "").strip()
533
+ order_id = request.form.get("order_id", "").strip()
534
+ username = current_user.fullname
535
+ address = current_user.address or ""
536
+
537
+ if not user_text:
538
+ return jsonify({"error": "empty"}), 400
539
+
540
+ category = predict_category(user_text)
541
+ sentiment, confidence = predict_sentiment(user_text)
542
+ bot_reply = chatbot_reply(user_text, category, sentiment)
543
+
544
+ missing = []
545
+ if category is None:
546
+ missing.append("category classifier")
547
+ if sentiment is None:
548
+ missing.append("sentiment model")
549
+ if bot_reply is None:
550
+ missing.append("chatbot model")
551
+ if missing:
552
+ return jsonify({
553
+ "error": "models_missing",
554
+ "message": f"Required model(s) not loaded: {', '.join(missing)}"
555
+ }), 503
556
+
557
+ # Save message
558
+ msg = ChatMessage(user_id=current_user.id, username=username, address=address,
559
+ order_id=order_id, message=user_text, category=category,
560
+ sentiment=sentiment, status="Pending")
561
+ db.session.add(msg)
562
+ db.session.commit()
563
+
564
+ # Escalate if negative
565
+ if sentiment == "Negative":
566
+ if "delivery" in (category or "").lower():
567
+ assigned = "Delivery Team"
568
+ elif "refund" in (category or "").lower():
569
+ assigned = "Finance"
570
+ elif "damage" in (category or "").lower():
571
+ assigned = "Warehouse"
572
+ else:
573
+ assigned = "Customer Care"
574
+ msg.status = f"Escalated to {assigned}"
575
+ db.session.commit()
576
+
577
+ sentiment_log.setdefault(sentiment, 0)
578
+ sentiment_log[sentiment] += 1
579
+ time_log.append({"time": datetime.now().strftime("%H:%M:%S"), "sentiment": sentiment})
580
+
581
+ return jsonify({
582
+ "category": category,
583
+ "sentiment": sentiment,
584
+ "confidence": confidence,
585
+ "reply": bot_reply
586
+ })
587
+
588
+ @app.route('/analyze', methods=['POST'])
589
+ def analyze():
590
+ user_text = request.form.get('user_input', '').strip()
591
+ if not user_text:
592
+ return jsonify({"error": "empty"}), 400
593
+
594
+ category = predict_category(user_text)
595
+ sentiment, confidence = predict_sentiment(user_text)
596
+ reply = chatbot_reply(user_text, category, sentiment)
597
+
598
+ missing = []
599
+ if category is None:
600
+ missing.append("category classifier")
601
+ if sentiment is None:
602
+ missing.append("sentiment model")
603
+ if reply is None:
604
+ missing.append("chatbot model")
605
+ if missing:
606
+ return jsonify({
607
+ "error": "models_missing",
608
+ "message": f"Required model(s) not loaded: {', '.join(missing)}"
609
+ }), 503
610
+
611
+ sentiment_log.setdefault(sentiment, 0)
612
+ sentiment_log[sentiment] += 1
613
+ time_log.append({"time": datetime.now().strftime("%H:%M:%S"), "sentiment": sentiment})
614
+
615
+ return jsonify({
616
+ "category": category,
617
+ "sentiment": sentiment,
618
+ "confidence": confidence,
619
+ "reply": reply
620
+ })
621
+
622
+ # ---------------------------
623
+ # Admin / Dashboard / Analytics
624
+ # ---------------------------
625
+ @app.route('/analytics', methods=['GET'])
626
+ def analytics():
627
+ return jsonify({
628
+ "labels": list(sentiment_log.keys()),
629
+ "values": list(sentiment_log.values()),
630
+ "timeline": time_log[-20:]
631
+ })
632
+
633
+ @app.route('/dashboard')
634
+ @login_required
635
+ def dashboard():
636
+ total = ChatMessage.query.count()
637
+ neg = ChatMessage.query.filter_by(sentiment="Negative").count()
638
+ pos = ChatMessage.query.filter_by(sentiment="Positive").count()
639
+ neu = ChatMessage.query.filter_by(sentiment="Neutral").count()
640
+ return render_template("dashboard.html", total=total, neg=neg, pos=pos, neu=neu)
641
+
642
+ @app.route('/admin/negatives')
643
+ @login_required
644
+ def admin_negatives():
645
+ if not current_user.is_admin:
646
+ flash("Admin access required", "danger")
647
+ return redirect(url_for("chat"))
648
+ negatives = ChatMessage.query.filter_by(sentiment="Negative").order_by(ChatMessage.created_at.desc()).limit(200).all()
649
+ return render_template("admin_negatives.html", negatives=negatives)
650
+
651
+ # ---------------------------
652
+ # Run
653
+ # ---------------------------
654
+ if __name__ == "__main__":
655
+ with app.app_context():
656
+ if User.query.filter_by(is_admin=True).count() == 0:
657
+ admin = User(fullname="Admin", email="admin@fastship.ai", phone="", address="")
658
+ admin.set_password("admin123")
659
+ admin.is_admin = True
660
+ db.session.add(admin)
661
+ db.session.commit()
662
+ print("Admin user created: admin@fastship.ai / admin123")
663
+
664
+ app.run(debug=True)
makecsv.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import os
3
+
4
+ # Define file paths
5
+ raw_file = os.path.join('data', 'complaints_dataset.txt')
6
+ clean_csv = os.path.join('data', 'complaints_dataset.csv')
7
+
8
+ # Load text data
9
+ df = pd.read_csv(raw_file,
10
+ names=['complaint_text', 'category', 'sentiment', 'urgency'])
11
+
12
+ # Clean data
13
+ df['complaint_text'] = df['complaint_text'].astype(str).str.strip()
14
+ df['category'] = df['category'].astype(str).str.title().str.strip()
15
+ df['sentiment'] = df['sentiment'].astype(str).str.capitalize().str.strip()
16
+ df['urgency'] = df['urgency'].astype(str).str.capitalize().str.strip()
17
+
18
+ # Remove blanks & duplicates
19
+ df = df.dropna().drop_duplicates()
20
+
21
+ # Print summary
22
+ print("✅ Dataset cleaned successfully!")
23
+ print("Total Rows:", df.shape[0])
24
+ print("Categories:", df['category'].unique())
25
+ print("Sentiments:", df['sentiment'].unique())
26
+ print("Urgency Levels:", df['urgency'].unique())
27
+
28
+ # Save clean CSV
29
+ df.to_csv(clean_csv, index=False, encoding='utf-8')
30
+ print(f"\n💾 Clean CSV saved to: {clean_csv}")
31
+
32
+
33
+
34
+
models.db ADDED
Binary file (20.5 kB). View file
 
models/bert_finetuned/config.json ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "activation": "gelu",
3
+ "architectures": [
4
+ "DistilBertForSequenceClassification"
5
+ ],
6
+ "attention_dropout": 0.1,
7
+ "dim": 768,
8
+ "dropout": 0.1,
9
+ "dtype": "float32",
10
+ "hidden_dim": 3072,
11
+ "id2label": {
12
+ "0": "High",
13
+ "1": "Mixed",
14
+ "2": "Nan",
15
+ "3": "Negative",
16
+ "4": "Neutral",
17
+ "5": "Positive"
18
+ },
19
+ "initializer_range": 0.02,
20
+ "label2id": {
21
+ "High": 0,
22
+ "Mixed": 1,
23
+ "Nan": 2,
24
+ "Negative": 3,
25
+ "Neutral": 4,
26
+ "Positive": 5
27
+ },
28
+ "max_position_embeddings": 512,
29
+ "model_type": "distilbert",
30
+ "n_heads": 12,
31
+ "n_layers": 6,
32
+ "pad_token_id": 0,
33
+ "problem_type": "single_label_classification",
34
+ "qa_dropout": 0.1,
35
+ "seq_classif_dropout": 0.2,
36
+ "sinusoidal_pos_embds": false,
37
+ "tie_weights_": true,
38
+ "transformers_version": "4.57.1",
39
+ "vocab_size": 30522
40
+ }
models/bert_finetuned/model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f52bec55cee880cd15a3b5d7fd34cdb39fda37687a3ff2effe6fb5ff913066d1
3
+ size 267844872
models/bert_finetuned/special_tokens_map.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "cls_token": "[CLS]",
3
+ "mask_token": "[MASK]",
4
+ "pad_token": "[PAD]",
5
+ "sep_token": "[SEP]",
6
+ "unk_token": "[UNK]"
7
+ }
models/bert_finetuned/tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
models/bert_finetuned/tokenizer_config.json ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "added_tokens_decoder": {
3
+ "0": {
4
+ "content": "[PAD]",
5
+ "lstrip": false,
6
+ "normalized": false,
7
+ "rstrip": false,
8
+ "single_word": false,
9
+ "special": true
10
+ },
11
+ "100": {
12
+ "content": "[UNK]",
13
+ "lstrip": false,
14
+ "normalized": false,
15
+ "rstrip": false,
16
+ "single_word": false,
17
+ "special": true
18
+ },
19
+ "101": {
20
+ "content": "[CLS]",
21
+ "lstrip": false,
22
+ "normalized": false,
23
+ "rstrip": false,
24
+ "single_word": false,
25
+ "special": true
26
+ },
27
+ "102": {
28
+ "content": "[SEP]",
29
+ "lstrip": false,
30
+ "normalized": false,
31
+ "rstrip": false,
32
+ "single_word": false,
33
+ "special": true
34
+ },
35
+ "103": {
36
+ "content": "[MASK]",
37
+ "lstrip": false,
38
+ "normalized": false,
39
+ "rstrip": false,
40
+ "single_word": false,
41
+ "special": true
42
+ }
43
+ },
44
+ "clean_up_tokenization_spaces": false,
45
+ "cls_token": "[CLS]",
46
+ "do_lower_case": true,
47
+ "extra_special_tokens": {},
48
+ "mask_token": "[MASK]",
49
+ "model_max_length": 512,
50
+ "pad_token": "[PAD]",
51
+ "sep_token": "[SEP]",
52
+ "strip_accents": null,
53
+ "tokenize_chinese_chars": true,
54
+ "tokenizer_class": "DistilBertTokenizer",
55
+ "unk_token": "[UNK]"
56
+ }
models/bert_finetuned/training_args.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d8cbecec005080b4bbac951219f279f5417b9bc24736fbc1bb182fa59d8e7abf
3
+ size 5777
models/bert_finetuned/vocab.txt ADDED
The diff for this file is too large to render. See raw diff
 
models/chatbot_t5/config.json ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "T5ForConditionalGeneration"
4
+ ],
5
+ "classifier_dropout": 0.0,
6
+ "d_ff": 2048,
7
+ "d_kv": 64,
8
+ "d_model": 512,
9
+ "decoder_start_token_id": 0,
10
+ "dense_act_fn": "relu",
11
+ "dropout_rate": 0.1,
12
+ "dtype": "float32",
13
+ "eos_token_id": 1,
14
+ "feed_forward_proj": "relu",
15
+ "initializer_factor": 1.0,
16
+ "is_encoder_decoder": true,
17
+ "is_gated_act": false,
18
+ "layer_norm_epsilon": 1e-06,
19
+ "model_type": "t5",
20
+ "n_positions": 512,
21
+ "num_decoder_layers": 6,
22
+ "num_heads": 8,
23
+ "num_layers": 6,
24
+ "output_past": true,
25
+ "pad_token_id": 0,
26
+ "relative_attention_max_distance": 128,
27
+ "relative_attention_num_buckets": 32,
28
+ "task_specific_params": {
29
+ "summarization": {
30
+ "early_stopping": true,
31
+ "length_penalty": 2.0,
32
+ "max_length": 200,
33
+ "min_length": 30,
34
+ "no_repeat_ngram_size": 3,
35
+ "num_beams": 4,
36
+ "prefix": "summarize: "
37
+ },
38
+ "translation_en_to_de": {
39
+ "early_stopping": true,
40
+ "max_length": 300,
41
+ "num_beams": 4,
42
+ "prefix": "translate English to German: "
43
+ },
44
+ "translation_en_to_fr": {
45
+ "early_stopping": true,
46
+ "max_length": 300,
47
+ "num_beams": 4,
48
+ "prefix": "translate English to French: "
49
+ },
50
+ "translation_en_to_ro": {
51
+ "early_stopping": true,
52
+ "max_length": 300,
53
+ "num_beams": 4,
54
+ "prefix": "translate English to Romanian: "
55
+ }
56
+ },
57
+ "transformers_version": "4.57.1",
58
+ "use_cache": true,
59
+ "vocab_size": 32128
60
+ }
models/chatbot_t5/generation_config.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_from_model_config": true,
3
+ "decoder_start_token_id": 0,
4
+ "eos_token_id": [
5
+ 1
6
+ ],
7
+ "pad_token_id": 0,
8
+ "transformers_version": "4.57.1"
9
+ }
models/chatbot_t5/model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f4a4bb4927a4d4fb0ea1af56ebef9466d28864e6dcdfbc41daa28f47e2b3226b
3
+ size 242041896
models/chatbot_t5/special_tokens_map.json ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "additional_special_tokens": [
3
+ "<extra_id_0>",
4
+ "<extra_id_1>",
5
+ "<extra_id_2>",
6
+ "<extra_id_3>",
7
+ "<extra_id_4>",
8
+ "<extra_id_5>",
9
+ "<extra_id_6>",
10
+ "<extra_id_7>",
11
+ "<extra_id_8>",
12
+ "<extra_id_9>",
13
+ "<extra_id_10>",
14
+ "<extra_id_11>",
15
+ "<extra_id_12>",
16
+ "<extra_id_13>",
17
+ "<extra_id_14>",
18
+ "<extra_id_15>",
19
+ "<extra_id_16>",
20
+ "<extra_id_17>",
21
+ "<extra_id_18>",
22
+ "<extra_id_19>",
23
+ "<extra_id_20>",
24
+ "<extra_id_21>",
25
+ "<extra_id_22>",
26
+ "<extra_id_23>",
27
+ "<extra_id_24>",
28
+ "<extra_id_25>",
29
+ "<extra_id_26>",
30
+ "<extra_id_27>",
31
+ "<extra_id_28>",
32
+ "<extra_id_29>",
33
+ "<extra_id_30>",
34
+ "<extra_id_31>",
35
+ "<extra_id_32>",
36
+ "<extra_id_33>",
37
+ "<extra_id_34>",
38
+ "<extra_id_35>",
39
+ "<extra_id_36>",
40
+ "<extra_id_37>",
41
+ "<extra_id_38>",
42
+ "<extra_id_39>",
43
+ "<extra_id_40>",
44
+ "<extra_id_41>",
45
+ "<extra_id_42>",
46
+ "<extra_id_43>",
47
+ "<extra_id_44>",
48
+ "<extra_id_45>",
49
+ "<extra_id_46>",
50
+ "<extra_id_47>",
51
+ "<extra_id_48>",
52
+ "<extra_id_49>",
53
+ "<extra_id_50>",
54
+ "<extra_id_51>",
55
+ "<extra_id_52>",
56
+ "<extra_id_53>",
57
+ "<extra_id_54>",
58
+ "<extra_id_55>",
59
+ "<extra_id_56>",
60
+ "<extra_id_57>",
61
+ "<extra_id_58>",
62
+ "<extra_id_59>",
63
+ "<extra_id_60>",
64
+ "<extra_id_61>",
65
+ "<extra_id_62>",
66
+ "<extra_id_63>",
67
+ "<extra_id_64>",
68
+ "<extra_id_65>",
69
+ "<extra_id_66>",
70
+ "<extra_id_67>",
71
+ "<extra_id_68>",
72
+ "<extra_id_69>",
73
+ "<extra_id_70>",
74
+ "<extra_id_71>",
75
+ "<extra_id_72>",
76
+ "<extra_id_73>",
77
+ "<extra_id_74>",
78
+ "<extra_id_75>",
79
+ "<extra_id_76>",
80
+ "<extra_id_77>",
81
+ "<extra_id_78>",
82
+ "<extra_id_79>",
83
+ "<extra_id_80>",
84
+ "<extra_id_81>",
85
+ "<extra_id_82>",
86
+ "<extra_id_83>",
87
+ "<extra_id_84>",
88
+ "<extra_id_85>",
89
+ "<extra_id_86>",
90
+ "<extra_id_87>",
91
+ "<extra_id_88>",
92
+ "<extra_id_89>",
93
+ "<extra_id_90>",
94
+ "<extra_id_91>",
95
+ "<extra_id_92>",
96
+ "<extra_id_93>",
97
+ "<extra_id_94>",
98
+ "<extra_id_95>",
99
+ "<extra_id_96>",
100
+ "<extra_id_97>",
101
+ "<extra_id_98>",
102
+ "<extra_id_99>"
103
+ ],
104
+ "eos_token": {
105
+ "content": "</s>",
106
+ "lstrip": false,
107
+ "normalized": false,
108
+ "rstrip": false,
109
+ "single_word": false
110
+ },
111
+ "pad_token": {
112
+ "content": "<pad>",
113
+ "lstrip": false,
114
+ "normalized": false,
115
+ "rstrip": false,
116
+ "single_word": false
117
+ },
118
+ "unk_token": {
119
+ "content": "<unk>",
120
+ "lstrip": false,
121
+ "normalized": false,
122
+ "rstrip": false,
123
+ "single_word": false
124
+ }
125
+ }
models/chatbot_t5/spiece.model ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d60acb128cf7b7f2536e8f38a5b18a05535c9e14c7a355904270e15b0945ea86
3
+ size 791656
models/chatbot_t5/tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
models/chatbot_t5/tokenizer_config.json ADDED
@@ -0,0 +1,939 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_prefix_space": null,
3
+ "added_tokens_decoder": {
4
+ "0": {
5
+ "content": "<pad>",
6
+ "lstrip": false,
7
+ "normalized": false,
8
+ "rstrip": false,
9
+ "single_word": false,
10
+ "special": true
11
+ },
12
+ "1": {
13
+ "content": "</s>",
14
+ "lstrip": false,
15
+ "normalized": false,
16
+ "rstrip": false,
17
+ "single_word": false,
18
+ "special": true
19
+ },
20
+ "2": {
21
+ "content": "<unk>",
22
+ "lstrip": false,
23
+ "normalized": false,
24
+ "rstrip": false,
25
+ "single_word": false,
26
+ "special": true
27
+ },
28
+ "32000": {
29
+ "content": "<extra_id_99>",
30
+ "lstrip": false,
31
+ "normalized": false,
32
+ "rstrip": false,
33
+ "single_word": false,
34
+ "special": true
35
+ },
36
+ "32001": {
37
+ "content": "<extra_id_98>",
38
+ "lstrip": false,
39
+ "normalized": false,
40
+ "rstrip": false,
41
+ "single_word": false,
42
+ "special": true
43
+ },
44
+ "32002": {
45
+ "content": "<extra_id_97>",
46
+ "lstrip": false,
47
+ "normalized": false,
48
+ "rstrip": false,
49
+ "single_word": false,
50
+ "special": true
51
+ },
52
+ "32003": {
53
+ "content": "<extra_id_96>",
54
+ "lstrip": false,
55
+ "normalized": false,
56
+ "rstrip": false,
57
+ "single_word": false,
58
+ "special": true
59
+ },
60
+ "32004": {
61
+ "content": "<extra_id_95>",
62
+ "lstrip": false,
63
+ "normalized": false,
64
+ "rstrip": false,
65
+ "single_word": false,
66
+ "special": true
67
+ },
68
+ "32005": {
69
+ "content": "<extra_id_94>",
70
+ "lstrip": false,
71
+ "normalized": false,
72
+ "rstrip": false,
73
+ "single_word": false,
74
+ "special": true
75
+ },
76
+ "32006": {
77
+ "content": "<extra_id_93>",
78
+ "lstrip": false,
79
+ "normalized": false,
80
+ "rstrip": false,
81
+ "single_word": false,
82
+ "special": true
83
+ },
84
+ "32007": {
85
+ "content": "<extra_id_92>",
86
+ "lstrip": false,
87
+ "normalized": false,
88
+ "rstrip": false,
89
+ "single_word": false,
90
+ "special": true
91
+ },
92
+ "32008": {
93
+ "content": "<extra_id_91>",
94
+ "lstrip": false,
95
+ "normalized": false,
96
+ "rstrip": false,
97
+ "single_word": false,
98
+ "special": true
99
+ },
100
+ "32009": {
101
+ "content": "<extra_id_90>",
102
+ "lstrip": false,
103
+ "normalized": false,
104
+ "rstrip": false,
105
+ "single_word": false,
106
+ "special": true
107
+ },
108
+ "32010": {
109
+ "content": "<extra_id_89>",
110
+ "lstrip": false,
111
+ "normalized": false,
112
+ "rstrip": false,
113
+ "single_word": false,
114
+ "special": true
115
+ },
116
+ "32011": {
117
+ "content": "<extra_id_88>",
118
+ "lstrip": false,
119
+ "normalized": false,
120
+ "rstrip": false,
121
+ "single_word": false,
122
+ "special": true
123
+ },
124
+ "32012": {
125
+ "content": "<extra_id_87>",
126
+ "lstrip": false,
127
+ "normalized": false,
128
+ "rstrip": false,
129
+ "single_word": false,
130
+ "special": true
131
+ },
132
+ "32013": {
133
+ "content": "<extra_id_86>",
134
+ "lstrip": false,
135
+ "normalized": false,
136
+ "rstrip": false,
137
+ "single_word": false,
138
+ "special": true
139
+ },
140
+ "32014": {
141
+ "content": "<extra_id_85>",
142
+ "lstrip": false,
143
+ "normalized": false,
144
+ "rstrip": false,
145
+ "single_word": false,
146
+ "special": true
147
+ },
148
+ "32015": {
149
+ "content": "<extra_id_84>",
150
+ "lstrip": false,
151
+ "normalized": false,
152
+ "rstrip": false,
153
+ "single_word": false,
154
+ "special": true
155
+ },
156
+ "32016": {
157
+ "content": "<extra_id_83>",
158
+ "lstrip": false,
159
+ "normalized": false,
160
+ "rstrip": false,
161
+ "single_word": false,
162
+ "special": true
163
+ },
164
+ "32017": {
165
+ "content": "<extra_id_82>",
166
+ "lstrip": false,
167
+ "normalized": false,
168
+ "rstrip": false,
169
+ "single_word": false,
170
+ "special": true
171
+ },
172
+ "32018": {
173
+ "content": "<extra_id_81>",
174
+ "lstrip": false,
175
+ "normalized": false,
176
+ "rstrip": false,
177
+ "single_word": false,
178
+ "special": true
179
+ },
180
+ "32019": {
181
+ "content": "<extra_id_80>",
182
+ "lstrip": false,
183
+ "normalized": false,
184
+ "rstrip": false,
185
+ "single_word": false,
186
+ "special": true
187
+ },
188
+ "32020": {
189
+ "content": "<extra_id_79>",
190
+ "lstrip": false,
191
+ "normalized": false,
192
+ "rstrip": false,
193
+ "single_word": false,
194
+ "special": true
195
+ },
196
+ "32021": {
197
+ "content": "<extra_id_78>",
198
+ "lstrip": false,
199
+ "normalized": false,
200
+ "rstrip": false,
201
+ "single_word": false,
202
+ "special": true
203
+ },
204
+ "32022": {
205
+ "content": "<extra_id_77>",
206
+ "lstrip": false,
207
+ "normalized": false,
208
+ "rstrip": false,
209
+ "single_word": false,
210
+ "special": true
211
+ },
212
+ "32023": {
213
+ "content": "<extra_id_76>",
214
+ "lstrip": false,
215
+ "normalized": false,
216
+ "rstrip": false,
217
+ "single_word": false,
218
+ "special": true
219
+ },
220
+ "32024": {
221
+ "content": "<extra_id_75>",
222
+ "lstrip": false,
223
+ "normalized": false,
224
+ "rstrip": false,
225
+ "single_word": false,
226
+ "special": true
227
+ },
228
+ "32025": {
229
+ "content": "<extra_id_74>",
230
+ "lstrip": false,
231
+ "normalized": false,
232
+ "rstrip": false,
233
+ "single_word": false,
234
+ "special": true
235
+ },
236
+ "32026": {
237
+ "content": "<extra_id_73>",
238
+ "lstrip": false,
239
+ "normalized": false,
240
+ "rstrip": false,
241
+ "single_word": false,
242
+ "special": true
243
+ },
244
+ "32027": {
245
+ "content": "<extra_id_72>",
246
+ "lstrip": false,
247
+ "normalized": false,
248
+ "rstrip": false,
249
+ "single_word": false,
250
+ "special": true
251
+ },
252
+ "32028": {
253
+ "content": "<extra_id_71>",
254
+ "lstrip": false,
255
+ "normalized": false,
256
+ "rstrip": false,
257
+ "single_word": false,
258
+ "special": true
259
+ },
260
+ "32029": {
261
+ "content": "<extra_id_70>",
262
+ "lstrip": false,
263
+ "normalized": false,
264
+ "rstrip": false,
265
+ "single_word": false,
266
+ "special": true
267
+ },
268
+ "32030": {
269
+ "content": "<extra_id_69>",
270
+ "lstrip": false,
271
+ "normalized": false,
272
+ "rstrip": false,
273
+ "single_word": false,
274
+ "special": true
275
+ },
276
+ "32031": {
277
+ "content": "<extra_id_68>",
278
+ "lstrip": false,
279
+ "normalized": false,
280
+ "rstrip": false,
281
+ "single_word": false,
282
+ "special": true
283
+ },
284
+ "32032": {
285
+ "content": "<extra_id_67>",
286
+ "lstrip": false,
287
+ "normalized": false,
288
+ "rstrip": false,
289
+ "single_word": false,
290
+ "special": true
291
+ },
292
+ "32033": {
293
+ "content": "<extra_id_66>",
294
+ "lstrip": false,
295
+ "normalized": false,
296
+ "rstrip": false,
297
+ "single_word": false,
298
+ "special": true
299
+ },
300
+ "32034": {
301
+ "content": "<extra_id_65>",
302
+ "lstrip": false,
303
+ "normalized": false,
304
+ "rstrip": false,
305
+ "single_word": false,
306
+ "special": true
307
+ },
308
+ "32035": {
309
+ "content": "<extra_id_64>",
310
+ "lstrip": false,
311
+ "normalized": false,
312
+ "rstrip": false,
313
+ "single_word": false,
314
+ "special": true
315
+ },
316
+ "32036": {
317
+ "content": "<extra_id_63>",
318
+ "lstrip": false,
319
+ "normalized": false,
320
+ "rstrip": false,
321
+ "single_word": false,
322
+ "special": true
323
+ },
324
+ "32037": {
325
+ "content": "<extra_id_62>",
326
+ "lstrip": false,
327
+ "normalized": false,
328
+ "rstrip": false,
329
+ "single_word": false,
330
+ "special": true
331
+ },
332
+ "32038": {
333
+ "content": "<extra_id_61>",
334
+ "lstrip": false,
335
+ "normalized": false,
336
+ "rstrip": false,
337
+ "single_word": false,
338
+ "special": true
339
+ },
340
+ "32039": {
341
+ "content": "<extra_id_60>",
342
+ "lstrip": false,
343
+ "normalized": false,
344
+ "rstrip": false,
345
+ "single_word": false,
346
+ "special": true
347
+ },
348
+ "32040": {
349
+ "content": "<extra_id_59>",
350
+ "lstrip": false,
351
+ "normalized": false,
352
+ "rstrip": false,
353
+ "single_word": false,
354
+ "special": true
355
+ },
356
+ "32041": {
357
+ "content": "<extra_id_58>",
358
+ "lstrip": false,
359
+ "normalized": false,
360
+ "rstrip": false,
361
+ "single_word": false,
362
+ "special": true
363
+ },
364
+ "32042": {
365
+ "content": "<extra_id_57>",
366
+ "lstrip": false,
367
+ "normalized": false,
368
+ "rstrip": false,
369
+ "single_word": false,
370
+ "special": true
371
+ },
372
+ "32043": {
373
+ "content": "<extra_id_56>",
374
+ "lstrip": false,
375
+ "normalized": false,
376
+ "rstrip": false,
377
+ "single_word": false,
378
+ "special": true
379
+ },
380
+ "32044": {
381
+ "content": "<extra_id_55>",
382
+ "lstrip": false,
383
+ "normalized": false,
384
+ "rstrip": false,
385
+ "single_word": false,
386
+ "special": true
387
+ },
388
+ "32045": {
389
+ "content": "<extra_id_54>",
390
+ "lstrip": false,
391
+ "normalized": false,
392
+ "rstrip": false,
393
+ "single_word": false,
394
+ "special": true
395
+ },
396
+ "32046": {
397
+ "content": "<extra_id_53>",
398
+ "lstrip": false,
399
+ "normalized": false,
400
+ "rstrip": false,
401
+ "single_word": false,
402
+ "special": true
403
+ },
404
+ "32047": {
405
+ "content": "<extra_id_52>",
406
+ "lstrip": false,
407
+ "normalized": false,
408
+ "rstrip": false,
409
+ "single_word": false,
410
+ "special": true
411
+ },
412
+ "32048": {
413
+ "content": "<extra_id_51>",
414
+ "lstrip": false,
415
+ "normalized": false,
416
+ "rstrip": false,
417
+ "single_word": false,
418
+ "special": true
419
+ },
420
+ "32049": {
421
+ "content": "<extra_id_50>",
422
+ "lstrip": false,
423
+ "normalized": false,
424
+ "rstrip": false,
425
+ "single_word": false,
426
+ "special": true
427
+ },
428
+ "32050": {
429
+ "content": "<extra_id_49>",
430
+ "lstrip": false,
431
+ "normalized": false,
432
+ "rstrip": false,
433
+ "single_word": false,
434
+ "special": true
435
+ },
436
+ "32051": {
437
+ "content": "<extra_id_48>",
438
+ "lstrip": false,
439
+ "normalized": false,
440
+ "rstrip": false,
441
+ "single_word": false,
442
+ "special": true
443
+ },
444
+ "32052": {
445
+ "content": "<extra_id_47>",
446
+ "lstrip": false,
447
+ "normalized": false,
448
+ "rstrip": false,
449
+ "single_word": false,
450
+ "special": true
451
+ },
452
+ "32053": {
453
+ "content": "<extra_id_46>",
454
+ "lstrip": false,
455
+ "normalized": false,
456
+ "rstrip": false,
457
+ "single_word": false,
458
+ "special": true
459
+ },
460
+ "32054": {
461
+ "content": "<extra_id_45>",
462
+ "lstrip": false,
463
+ "normalized": false,
464
+ "rstrip": false,
465
+ "single_word": false,
466
+ "special": true
467
+ },
468
+ "32055": {
469
+ "content": "<extra_id_44>",
470
+ "lstrip": false,
471
+ "normalized": false,
472
+ "rstrip": false,
473
+ "single_word": false,
474
+ "special": true
475
+ },
476
+ "32056": {
477
+ "content": "<extra_id_43>",
478
+ "lstrip": false,
479
+ "normalized": false,
480
+ "rstrip": false,
481
+ "single_word": false,
482
+ "special": true
483
+ },
484
+ "32057": {
485
+ "content": "<extra_id_42>",
486
+ "lstrip": false,
487
+ "normalized": false,
488
+ "rstrip": false,
489
+ "single_word": false,
490
+ "special": true
491
+ },
492
+ "32058": {
493
+ "content": "<extra_id_41>",
494
+ "lstrip": false,
495
+ "normalized": false,
496
+ "rstrip": false,
497
+ "single_word": false,
498
+ "special": true
499
+ },
500
+ "32059": {
501
+ "content": "<extra_id_40>",
502
+ "lstrip": false,
503
+ "normalized": false,
504
+ "rstrip": false,
505
+ "single_word": false,
506
+ "special": true
507
+ },
508
+ "32060": {
509
+ "content": "<extra_id_39>",
510
+ "lstrip": false,
511
+ "normalized": false,
512
+ "rstrip": false,
513
+ "single_word": false,
514
+ "special": true
515
+ },
516
+ "32061": {
517
+ "content": "<extra_id_38>",
518
+ "lstrip": false,
519
+ "normalized": false,
520
+ "rstrip": false,
521
+ "single_word": false,
522
+ "special": true
523
+ },
524
+ "32062": {
525
+ "content": "<extra_id_37>",
526
+ "lstrip": false,
527
+ "normalized": false,
528
+ "rstrip": false,
529
+ "single_word": false,
530
+ "special": true
531
+ },
532
+ "32063": {
533
+ "content": "<extra_id_36>",
534
+ "lstrip": false,
535
+ "normalized": false,
536
+ "rstrip": false,
537
+ "single_word": false,
538
+ "special": true
539
+ },
540
+ "32064": {
541
+ "content": "<extra_id_35>",
542
+ "lstrip": false,
543
+ "normalized": false,
544
+ "rstrip": false,
545
+ "single_word": false,
546
+ "special": true
547
+ },
548
+ "32065": {
549
+ "content": "<extra_id_34>",
550
+ "lstrip": false,
551
+ "normalized": false,
552
+ "rstrip": false,
553
+ "single_word": false,
554
+ "special": true
555
+ },
556
+ "32066": {
557
+ "content": "<extra_id_33>",
558
+ "lstrip": false,
559
+ "normalized": false,
560
+ "rstrip": false,
561
+ "single_word": false,
562
+ "special": true
563
+ },
564
+ "32067": {
565
+ "content": "<extra_id_32>",
566
+ "lstrip": false,
567
+ "normalized": false,
568
+ "rstrip": false,
569
+ "single_word": false,
570
+ "special": true
571
+ },
572
+ "32068": {
573
+ "content": "<extra_id_31>",
574
+ "lstrip": false,
575
+ "normalized": false,
576
+ "rstrip": false,
577
+ "single_word": false,
578
+ "special": true
579
+ },
580
+ "32069": {
581
+ "content": "<extra_id_30>",
582
+ "lstrip": false,
583
+ "normalized": false,
584
+ "rstrip": false,
585
+ "single_word": false,
586
+ "special": true
587
+ },
588
+ "32070": {
589
+ "content": "<extra_id_29>",
590
+ "lstrip": false,
591
+ "normalized": false,
592
+ "rstrip": false,
593
+ "single_word": false,
594
+ "special": true
595
+ },
596
+ "32071": {
597
+ "content": "<extra_id_28>",
598
+ "lstrip": false,
599
+ "normalized": false,
600
+ "rstrip": false,
601
+ "single_word": false,
602
+ "special": true
603
+ },
604
+ "32072": {
605
+ "content": "<extra_id_27>",
606
+ "lstrip": false,
607
+ "normalized": false,
608
+ "rstrip": false,
609
+ "single_word": false,
610
+ "special": true
611
+ },
612
+ "32073": {
613
+ "content": "<extra_id_26>",
614
+ "lstrip": false,
615
+ "normalized": false,
616
+ "rstrip": false,
617
+ "single_word": false,
618
+ "special": true
619
+ },
620
+ "32074": {
621
+ "content": "<extra_id_25>",
622
+ "lstrip": false,
623
+ "normalized": false,
624
+ "rstrip": false,
625
+ "single_word": false,
626
+ "special": true
627
+ },
628
+ "32075": {
629
+ "content": "<extra_id_24>",
630
+ "lstrip": false,
631
+ "normalized": false,
632
+ "rstrip": false,
633
+ "single_word": false,
634
+ "special": true
635
+ },
636
+ "32076": {
637
+ "content": "<extra_id_23>",
638
+ "lstrip": false,
639
+ "normalized": false,
640
+ "rstrip": false,
641
+ "single_word": false,
642
+ "special": true
643
+ },
644
+ "32077": {
645
+ "content": "<extra_id_22>",
646
+ "lstrip": false,
647
+ "normalized": false,
648
+ "rstrip": false,
649
+ "single_word": false,
650
+ "special": true
651
+ },
652
+ "32078": {
653
+ "content": "<extra_id_21>",
654
+ "lstrip": false,
655
+ "normalized": false,
656
+ "rstrip": false,
657
+ "single_word": false,
658
+ "special": true
659
+ },
660
+ "32079": {
661
+ "content": "<extra_id_20>",
662
+ "lstrip": false,
663
+ "normalized": false,
664
+ "rstrip": false,
665
+ "single_word": false,
666
+ "special": true
667
+ },
668
+ "32080": {
669
+ "content": "<extra_id_19>",
670
+ "lstrip": false,
671
+ "normalized": false,
672
+ "rstrip": false,
673
+ "single_word": false,
674
+ "special": true
675
+ },
676
+ "32081": {
677
+ "content": "<extra_id_18>",
678
+ "lstrip": false,
679
+ "normalized": false,
680
+ "rstrip": false,
681
+ "single_word": false,
682
+ "special": true
683
+ },
684
+ "32082": {
685
+ "content": "<extra_id_17>",
686
+ "lstrip": false,
687
+ "normalized": false,
688
+ "rstrip": false,
689
+ "single_word": false,
690
+ "special": true
691
+ },
692
+ "32083": {
693
+ "content": "<extra_id_16>",
694
+ "lstrip": false,
695
+ "normalized": false,
696
+ "rstrip": false,
697
+ "single_word": false,
698
+ "special": true
699
+ },
700
+ "32084": {
701
+ "content": "<extra_id_15>",
702
+ "lstrip": false,
703
+ "normalized": false,
704
+ "rstrip": false,
705
+ "single_word": false,
706
+ "special": true
707
+ },
708
+ "32085": {
709
+ "content": "<extra_id_14>",
710
+ "lstrip": false,
711
+ "normalized": false,
712
+ "rstrip": false,
713
+ "single_word": false,
714
+ "special": true
715
+ },
716
+ "32086": {
717
+ "content": "<extra_id_13>",
718
+ "lstrip": false,
719
+ "normalized": false,
720
+ "rstrip": false,
721
+ "single_word": false,
722
+ "special": true
723
+ },
724
+ "32087": {
725
+ "content": "<extra_id_12>",
726
+ "lstrip": false,
727
+ "normalized": false,
728
+ "rstrip": false,
729
+ "single_word": false,
730
+ "special": true
731
+ },
732
+ "32088": {
733
+ "content": "<extra_id_11>",
734
+ "lstrip": false,
735
+ "normalized": false,
736
+ "rstrip": false,
737
+ "single_word": false,
738
+ "special": true
739
+ },
740
+ "32089": {
741
+ "content": "<extra_id_10>",
742
+ "lstrip": false,
743
+ "normalized": false,
744
+ "rstrip": false,
745
+ "single_word": false,
746
+ "special": true
747
+ },
748
+ "32090": {
749
+ "content": "<extra_id_9>",
750
+ "lstrip": false,
751
+ "normalized": false,
752
+ "rstrip": false,
753
+ "single_word": false,
754
+ "special": true
755
+ },
756
+ "32091": {
757
+ "content": "<extra_id_8>",
758
+ "lstrip": false,
759
+ "normalized": false,
760
+ "rstrip": false,
761
+ "single_word": false,
762
+ "special": true
763
+ },
764
+ "32092": {
765
+ "content": "<extra_id_7>",
766
+ "lstrip": false,
767
+ "normalized": false,
768
+ "rstrip": false,
769
+ "single_word": false,
770
+ "special": true
771
+ },
772
+ "32093": {
773
+ "content": "<extra_id_6>",
774
+ "lstrip": false,
775
+ "normalized": false,
776
+ "rstrip": false,
777
+ "single_word": false,
778
+ "special": true
779
+ },
780
+ "32094": {
781
+ "content": "<extra_id_5>",
782
+ "lstrip": false,
783
+ "normalized": false,
784
+ "rstrip": false,
785
+ "single_word": false,
786
+ "special": true
787
+ },
788
+ "32095": {
789
+ "content": "<extra_id_4>",
790
+ "lstrip": false,
791
+ "normalized": false,
792
+ "rstrip": false,
793
+ "single_word": false,
794
+ "special": true
795
+ },
796
+ "32096": {
797
+ "content": "<extra_id_3>",
798
+ "lstrip": false,
799
+ "normalized": false,
800
+ "rstrip": false,
801
+ "single_word": false,
802
+ "special": true
803
+ },
804
+ "32097": {
805
+ "content": "<extra_id_2>",
806
+ "lstrip": false,
807
+ "normalized": false,
808
+ "rstrip": false,
809
+ "single_word": false,
810
+ "special": true
811
+ },
812
+ "32098": {
813
+ "content": "<extra_id_1>",
814
+ "lstrip": false,
815
+ "normalized": false,
816
+ "rstrip": false,
817
+ "single_word": false,
818
+ "special": true
819
+ },
820
+ "32099": {
821
+ "content": "<extra_id_0>",
822
+ "lstrip": false,
823
+ "normalized": false,
824
+ "rstrip": false,
825
+ "single_word": false,
826
+ "special": true
827
+ }
828
+ },
829
+ "additional_special_tokens": [
830
+ "<extra_id_0>",
831
+ "<extra_id_1>",
832
+ "<extra_id_2>",
833
+ "<extra_id_3>",
834
+ "<extra_id_4>",
835
+ "<extra_id_5>",
836
+ "<extra_id_6>",
837
+ "<extra_id_7>",
838
+ "<extra_id_8>",
839
+ "<extra_id_9>",
840
+ "<extra_id_10>",
841
+ "<extra_id_11>",
842
+ "<extra_id_12>",
843
+ "<extra_id_13>",
844
+ "<extra_id_14>",
845
+ "<extra_id_15>",
846
+ "<extra_id_16>",
847
+ "<extra_id_17>",
848
+ "<extra_id_18>",
849
+ "<extra_id_19>",
850
+ "<extra_id_20>",
851
+ "<extra_id_21>",
852
+ "<extra_id_22>",
853
+ "<extra_id_23>",
854
+ "<extra_id_24>",
855
+ "<extra_id_25>",
856
+ "<extra_id_26>",
857
+ "<extra_id_27>",
858
+ "<extra_id_28>",
859
+ "<extra_id_29>",
860
+ "<extra_id_30>",
861
+ "<extra_id_31>",
862
+ "<extra_id_32>",
863
+ "<extra_id_33>",
864
+ "<extra_id_34>",
865
+ "<extra_id_35>",
866
+ "<extra_id_36>",
867
+ "<extra_id_37>",
868
+ "<extra_id_38>",
869
+ "<extra_id_39>",
870
+ "<extra_id_40>",
871
+ "<extra_id_41>",
872
+ "<extra_id_42>",
873
+ "<extra_id_43>",
874
+ "<extra_id_44>",
875
+ "<extra_id_45>",
876
+ "<extra_id_46>",
877
+ "<extra_id_47>",
878
+ "<extra_id_48>",
879
+ "<extra_id_49>",
880
+ "<extra_id_50>",
881
+ "<extra_id_51>",
882
+ "<extra_id_52>",
883
+ "<extra_id_53>",
884
+ "<extra_id_54>",
885
+ "<extra_id_55>",
886
+ "<extra_id_56>",
887
+ "<extra_id_57>",
888
+ "<extra_id_58>",
889
+ "<extra_id_59>",
890
+ "<extra_id_60>",
891
+ "<extra_id_61>",
892
+ "<extra_id_62>",
893
+ "<extra_id_63>",
894
+ "<extra_id_64>",
895
+ "<extra_id_65>",
896
+ "<extra_id_66>",
897
+ "<extra_id_67>",
898
+ "<extra_id_68>",
899
+ "<extra_id_69>",
900
+ "<extra_id_70>",
901
+ "<extra_id_71>",
902
+ "<extra_id_72>",
903
+ "<extra_id_73>",
904
+ "<extra_id_74>",
905
+ "<extra_id_75>",
906
+ "<extra_id_76>",
907
+ "<extra_id_77>",
908
+ "<extra_id_78>",
909
+ "<extra_id_79>",
910
+ "<extra_id_80>",
911
+ "<extra_id_81>",
912
+ "<extra_id_82>",
913
+ "<extra_id_83>",
914
+ "<extra_id_84>",
915
+ "<extra_id_85>",
916
+ "<extra_id_86>",
917
+ "<extra_id_87>",
918
+ "<extra_id_88>",
919
+ "<extra_id_89>",
920
+ "<extra_id_90>",
921
+ "<extra_id_91>",
922
+ "<extra_id_92>",
923
+ "<extra_id_93>",
924
+ "<extra_id_94>",
925
+ "<extra_id_95>",
926
+ "<extra_id_96>",
927
+ "<extra_id_97>",
928
+ "<extra_id_98>",
929
+ "<extra_id_99>"
930
+ ],
931
+ "clean_up_tokenization_spaces": true,
932
+ "eos_token": "</s>",
933
+ "extra_ids": 100,
934
+ "extra_special_tokens": {},
935
+ "model_max_length": 512,
936
+ "pad_token": "<pad>",
937
+ "tokenizer_class": "T5Tokenizer",
938
+ "unk_token": "<unk>"
939
+ }
models/chatbot_t5/training_args.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:42fb2a22064282e8f09b4610ba008dd3138e1acc9ae501500af8225913ec9bdd
3
+ size 5777
models/complaint_classifier_rf.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0d2c197298f8d1bfa907523c9155f4631e6055333948eb3b6a6e520aa8e2ad08
3
+ size 38277841
models/tfidf_vectorizer.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0f484985441c6e095fe89402b015faac5c278dc224b7140cde747954e5ee2201
3
+ size 51265
proejectremember_andnote.txt ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ if you converting data into csv but if have code have 4 colum and i some line you hacve 5 it give error
2
+
3
+
4
+ Ahh — nice, bro. That error happens because XGBoost doesn’t accept sparse matrices (like TF-IDF) in its native DMatrix unless you convert them properly.
5
+
6
+ logistic ,svm,xboost is good in classification but in good sentiment so thaat why onaa try bert
7
+
8
+ Encoding UTF-8 in Python is the process of converting human-readable text into a format that computers can store and transmit as raw data (bytes), specifically using the rules of the UTF-8 system.
9
+ Think of it like translating a message into a secret code that a computer understands.
10
+
11
+
12
+ Imagine you are telling a story. When you get to the very end, you might say "The End."
13
+ The <|endoftext|> token is exactly like "The End" for a computer model.
14
+ What it means:
15
+ For the computer: "I am finished writing now."
16
+ During training: It helped the computer learn where one story stops and another starts.
17
+ When you get an answer: If the computer decides to use this token, its software stops the response immediately.
18
+ It's a secret signal, hidden from you, that tells the AI to stop talking and give you the final answer.
19
+
20
+
21
+ also used
22
+
23
+ def detect_mixed(text, sentiment):
24
+ pos_words = ["good","great","amazing","excellent","perfect","love"]
25
+ neg_words = ["delay","broken","bad","terrible","refund","lost","not"]
26
+ pos = any(w in text.lower() for w in pos_words)
27
+ neg = any(w in text.lower() for w in neg_words)
28
+
29
+ if pos and neg:
30
+ return "Mixed"
31
+ return sentiment
32
+ result = bert_sentiment(text)[0]
33
+ sentiment = detect_mixed(text, result['label'])
34
+
35
+ def polarity_override(text, sentiment):
36
+ text = text.lower()
37
+ negative_keywords = ["delay", "late", "scammed", "broken", "refund not", "not received", "missing", "cancelled"]
38
+ positive_keywords = ["delivered", "resolved", "refunded", "on time", "working", "successfully"]
39
+
40
+ if any(word in text for word in negative_keywords):
41
+ return "Negative"
42
+ elif any(word in text for word in positive_keywords):
43
+ return "Positive"
44
+ return sentiment
45
+
46
+
47
+ if length problem apper try to fix bt poolingg=max_length
48
+
49
+
50
+ you can seee model diffent after chaing epoch
51
+ by this
52
+
53
+ import pandas as pd
54
+ import matplotlib.pyplot as plt
55
+
56
+ # Load training history from Trainer's log
57
+ logs = pd.DataFrame(trainer.state.log_history)
58
+
59
+ # Keep only relevant columns
60
+ logs = logs.dropna(subset=['loss','eval_loss'], how='all')
61
+
62
+ plt.figure(figsize=(8,5))
63
+ plt.plot(logs["epoch"], logs["loss"], label="Training Loss")
64
+ if "eval_loss" in logs.columns:
65
+ plt.plot(logs["epoch"], logs["eval_loss"], label="Validation Loss")
66
+ plt.xlabel("Epoch")
67
+ plt.ylabel("Loss")
68
+ plt.title("Training vs Validation Loss")
69
+ plt.legend()
70
+ plt.grid(True)
71
+ plt.show()
72
+
73
+
74
+
75
+
76
+ for tranfomer errors
77
+
78
+ from transformers import AutoTokenizer, AutoModelForSeq2SeqLM, TrainingArguments, Trainer
79
+ import numpy as np
80
+
81
+ model_name = "t5-small"
82
+ tokenizer = AutoTokenizer.from_pretrained(model_name)
83
+ model = AutoModelForSeq2SeqLM.from_pretrained(model_name)
84
+
85
+ training_args = TrainingArguments(
86
+ output_dir="./results",
87
+ num_train_epochs=3,
88
+ per_device_train_batch_size=8,
89
+ per_device_eval_batch_size=8,
90
+ learning_rate=2e-4,
91
+ weight_decay=0.01,
92
+ eval_strategy="epoch", # <- use eval_strategy (not evaluation_strategy)
93
+ save_strategy="epoch", # save checkpoints every epoch
94
+ logging_strategy="steps", # or "epoch"
95
+ logging_steps=100,
96
+ logging_dir="./logs",
97
+ report_to="none"
98
+ )
99
+
100
+ trainer = Trainer(
101
+ model=model,
102
+ args=training_args,
103
+ train_dataset=tokenized_train,
104
+ eval_dataset=tokenized_test,
105
+ tokenizer=tokenizer,
106
+ compute_metrics=compute_metrics,
107
+ # predict_with_generate=True # enable if you want generated outputs for seq2seq eval
108
+ )
109
+
110
+ trainer.train()
111
+
requirements.txt ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Flask==3.0.2
2
+ transformers==4.45.1
3
+ torch
4
+ scikit-learn
5
+ joblib
6
+ numpy
7
+ pandas
8
+
9
+ Flask-Login==0.6.2
10
+ Flask-SQLAlchemy==3.0.3
11
+ Flask-Mail==0.9.1
12
+ transformers==4.45.1
13
+ torch
14
+ joblib
15
+ numpy
16
+ pandas
17
+ sentencepiece
static/script.js ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ const chatForm = document.getElementById("chat-form");
2
+ const userInput = document.getElementById("user-input");
3
+ const chatBox = document.getElementById("chat-box");
4
+ const orderIdEl = document.getElementById("order_id");
5
+ const categoryEl = document.getElementById("category");
6
+ const sentimentEl = document.getElementById("sentiment");
7
+ const negativeBox = document.getElementById("negative-box");
8
+
9
+ let sentimentChart = null;
10
+
11
+ // 🧠 Append User Message
12
+ function appendUser(text) {
13
+ chatBox.innerHTML += `<p class="user-msg">🧑 You: ${text}</p>`;
14
+ chatBox.scrollTop = chatBox.scrollHeight;
15
+ }
16
+
17
+ // 🤖 Append Bot Message
18
+ function appendBot(text) {
19
+ chatBox.innerHTML += `<p class="bot-msg">🤖 Bot: ${text}</p>`;
20
+ chatBox.scrollTop = chatBox.scrollHeight;
21
+ }
22
+
23
+ // ⭐ Send chat message
24
+ chatForm.addEventListener("submit", async (e) => {
25
+ e.preventDefault();
26
+
27
+ const text = userInput.value.trim();
28
+ const order_id = orderIdEl.value.trim();
29
+ if (!text) return;
30
+
31
+ appendUser(text);
32
+ userInput.value = "";
33
+
34
+ try {
35
+ const res = await fetch("/chat", {
36
+ method: "POST",
37
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
38
+ body: new URLSearchParams({ user_input: text, order_id })
39
+ });
40
+
41
+ const data = await res.json();
42
+
43
+ if (data.error) {
44
+ appendBot("Sorry, could not process your message.");
45
+ return;
46
+ }
47
+
48
+ appendBot(data.reply);
49
+
50
+ categoryEl.textContent = data.category || "—";
51
+ sentimentEl.textContent = `${data.sentiment || "—"} (${data.confidence || 0}%)`;
52
+
53
+ await updateAnalytics();
54
+ await updateNegatives();
55
+ } catch (err) {
56
+ console.error(err);
57
+ appendBot("Network error — try again.");
58
+ }
59
+ });
60
+
61
+ // 📊 Update Analytics Charts
62
+ async function updateAnalytics() {
63
+ const res = await fetch("/analytics");
64
+ const data = await res.json();
65
+
66
+ const ctx = document.getElementById("sentimentChart");
67
+
68
+ if (sentimentChart) sentimentChart.destroy();
69
+
70
+ sentimentChart = new Chart(ctx, {
71
+ type: "pie",
72
+ data: {
73
+ labels: data.labels,
74
+ datasets: [{
75
+ data: data.values,
76
+ backgroundColor: ["#2ecc71", "#e74c3c", "#f1c40f"]
77
+ }]
78
+ }
79
+ });
80
+ }
81
+
82
+ // ⚠️ Update Negative Messages Box
83
+ async function updateNegatives() {
84
+ if (!negativeBox) return; // Safety check
85
+
86
+ const res = await fetch("/admin/negatives-data");
87
+ const data = await res.json();
88
+
89
+ negativeBox.innerHTML = data.length === 0
90
+ ? "<p>No negative messages yet.</p>"
91
+ : data.map(msg =>
92
+ `<p class="neg-item">⚠️ <b>${msg.username}</b>: ${msg.message}</p>`
93
+ ).join("");
94
+ }
95
+
96
+ // auto-refresh negative messages
97
+ setInterval(updateNegatives, 8000);
static/style.css ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root{--bg:#f4f7fb;--card:#fff;--accent:#27ae60;--muted:#6b7280}
2
+ *{box-sizing:border-box}
3
+ body{font-family:Inter,system-ui,Arial,sans-serif;background:var(--bg);margin:0;color:#111}
4
+ .topbar{display:flex;justify-content:space-between;align-items:center;padding:12px 20px;background:linear-gradient(90deg,#ffffff,#fbfdfe);border-bottom:1px solid #eee}
5
+ .brand{font-weight:700}
6
+ .nav a{margin-left:12px;text-decoration:none;color:var(--muted)}
7
+ .container{max-width:1100px;margin:22px auto;padding:12px}
8
+ .chat-grid{display:grid;grid-template-columns:1fr 360px;gap:20px}
9
+ .chat-panel{background:var(--card);border-radius:12px;padding:14px;box-shadow:0 8px 24px rgba(16,24,40,0.06)}
10
+ .side-panel{background:var(--card);border-radius:12px;padding:14px;box-shadow:0 8px 24px rgba(16,24,40,0.06)}
11
+ .chat-box{height:360px;overflow:auto;border:1px solid #eef2f7;border-radius:8px;padding:12px;background:#fcfeff}
12
+ .user-msg{text-align:right;margin:8px 0;color:#0f172a}
13
+ .bot-msg{text-align:left;margin:8px 0;color:#064e3b}
14
+ .input-row{display:flex;gap:8px;margin-top:10px}
15
+ #user-input{flex:1;padding:10px;border:1px solid #e6eef6;border-radius:8px}
16
+ #order_id{width:100%;padding:8px;margin-top:8px;border-radius:8px;border:1px solid #e6eef6}
17
+ #send-btn{background:var(--accent);color:#fff;border:none;padding:10px 14px;border-radius:8px;cursor:pointer}
18
+ .meta{display:flex;gap:14px;margin-top:12px;color:var(--muted)}
19
+ .negative-box{height:180px;overflow:auto;border:1px solid #fdecea;background:#fff5f5;padding:8px;border-radius:8px}
20
+ .negative-entry{border-bottom:1px dashed #ffdede;padding:8px;margin-bottom:6px}
21
+ .flashes .flash{padding:10px;border-radius:6px;margin-bottom:8px}
22
+ .flash.success{background:#e6ffed;color:#0b6a3a}
23
+ .flash.warning{background:#fff7e6;color:#7a4d00}
24
+ .flash.danger{background:#ffe6e6;color:#912d2d}
25
+ .footer{padding:12px;text-align:center;color:var(--muted);font-size:13px;margin-top:18px}
26
+ .home-grid{display:flex;gap:20px}
27
+ .home-left{flex:1}
28
+ .home-right{width:420px}
29
+ .btn{display:inline-block;padding:10px 14px;background:var(--accent);color:#fff;border-radius:8px;text-decoration:none}
30
+ .btn.ghost{background:transparent;color:var(--accent);border:1px solid var(--accent)}
31
+ @media(max-width:900px){.chat-grid{grid-template-columns:1fr}.home-grid{flex-direction:column}}
templates/admin_negatives.html ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+ {% block content %}
3
+ <h2>Negative Messages</h2>
4
+ <table class="table">
5
+ <thead>
6
+ <tr><th>Time</th><th>User</th><th>Order ID</th><th>Message</th><th>Category</th><th>Status</th></tr>
7
+ </thead>
8
+ <tbody>
9
+ {% for m in negatives %}
10
+ <tr>
11
+ <td>{{ m.created_at }}</td>
12
+ <td>{{ m.username }}</td>
13
+ <td>{{ m.order_id }}</td>
14
+ <td>{{ m.message }}</td>
15
+ <td>{{ m.category }}</td>
16
+ <td>{{ m.status }}</td>
17
+ </tr>
18
+ {% endfor %}
19
+ </tbody>
20
+ </table>
21
+ {% endblock %}
templates/base.html ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
6
+ <title>FastShip AI</title>
7
+ <link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
8
+ <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
9
+ </head>
10
+ <body>
11
+ <header class="topbar">
12
+ <div class="brand">🚚 FastShip AI</div>
13
+ <nav class="nav">
14
+ {% if current_user.is_authenticated %}
15
+ <span class="user">Hello, {{ current_user.fullname }}</span>
16
+ <a href="{{ url_for('dashboard') }}">Dashboard</a>
17
+ {% if current_user.is_admin %}
18
+ <a href="{{ url_for('admin_negatives') }}">Admin</a>
19
+ {% endif %}
20
+ <a href="{{ url_for('logout') }}">Logout</a>
21
+ {% else %}
22
+ <a href="{{ url_for('login') }}">Login</a>
23
+ <a href="{{ url_for('register') }}">Register</a>
24
+ {% endif %}
25
+ </nav>
26
+ </header>
27
+
28
+
29
+ <main class="main">
30
+ <div class="container">
31
+ {% with messages = get_flashed_messages(with_categories=true) %}
32
+ {% if messages %}
33
+ <div class="flashes">
34
+ {% for category, msg in messages %}
35
+ <div class="flash {{ category|lower }}">{{ msg }}</div>
36
+ {% endfor %}
37
+ </div>
38
+ {% endif %}
39
+ {% endwith %}
40
+ {% block content %}{% endblock %}
41
+ </div>
42
+ </main>
43
+
44
+
45
+ <footer class="footer">© FastShip AI</footer>
46
+ </body>
47
+ </html>
templates/chat.html ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+ {% block content %}
3
+ <div class="chat-grid">
4
+ <section class="chat-panel">
5
+ <h2>Chat</h2>
6
+ <div id="chat-box" class="chat-box" aria-live="polite"></div>
7
+
8
+
9
+ <form id="chat-form" class="chat-form" autocomplete="off">
10
+ <input id="order_id" name="order_id" placeholder="Order ID (optional)" />
11
+ <div class="input-row">
12
+ <input id="user-input" name="user_input" placeholder="Type your message..." />
13
+ <button id="send-btn" type="submit">Send</button>
14
+ </div>
15
+ </form>
16
+
17
+
18
+ <div class="meta">
19
+ <div><strong>Category:</strong> <span id="category">—</span></div>
20
+ <div><strong>Sentiment:</strong> <span id="sentiment">—</span></div>
21
+ </div>
22
+ </section>
23
+
24
+
25
+ <aside class="side-panel">
26
+ <h3>Live Analytics</h3>
27
+ <canvas id="sentimentChart" height="140"></canvas>
28
+
29
+
30
+ <h3>Recent Negatives</h3>
31
+ <div id="negative-box" class="negative-box"></div>
32
+ </aside>
33
+ </div>
34
+
35
+
36
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
37
+ <script src="{{ url_for('static', filename='script.js') }}"></script>
38
+ {% endblock %}
templates/dashboard.html ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+ {% block content %}
3
+ <h2>Dashboard</h2>
4
+ <p>Total messages: {{ total }}</p>
5
+ <p>Positive: {{ pos }} | Neutral: {{ neu }} | Negative: {{ neg }}</p>
6
+ <p><a href="{{ url_for('admin_negatives') }}">View negative messages (admin)</a></p>
7
+ {% endblock %}
templates/forgot.html ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+ {% block content %}
3
+ <h2>Forgot Password</h2>
4
+ <form method="post">
5
+ <label>Enter your registered email</label><br/>
6
+ <input name="email" type="email" required><br/>
7
+ <button type="submit">Send OTP</button>
8
+ </form>
9
+ {% endblock %}
templates/index.html ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+ {% block content %}
3
+ <div class="home-grid">
4
+ <div class="home-left">
5
+ <h2>🤖 FastShip AI Assistant</h2>
6
+ <p>Login to chat with the assistant or test the demo below.</p>
7
+ <a class="btn" href="{{ url_for('login') }}">Login</a>
8
+ <a class="btn ghost" href="{{ url_for('register') }}">Register</a>
9
+ </div>
10
+ <div class="home-right">
11
+ <h3>Live Demo</h3>
12
+ <div id="chat-box-mini" class="chat-box-mini"></div>
13
+ <div class="input-area">
14
+ <input id="user-input-mini" type="text" placeholder="Try: "Where's my order?"" />
15
+ <button id="send-mini" class="btn">Send</button>
16
+ </div>
17
+ </div>
18
+ </div>
19
+ <script>
20
+ // tiny demo handler calling /analyze
21
+ document.getElementById('send-mini').addEventListener('click', async function(){
22
+ const t = document.getElementById('user-input-mini').value.trim();
23
+ if(!t) return;
24
+ const r = await fetch('/analyze', {method:'POST', headers:{'Content-Type':'application/x-www-form-urlencoded'}, body: new URLSearchParams({user_input:t})});
25
+ const js = await r.json();
26
+ const el = document.getElementById('chat-box-mini');
27
+ el.innerHTML += `<div><b>You:</b> ${t}</div><div><b>Bot:</b> ${js.reply}</div>`;
28
+ });
29
+ </script>
30
+ {% endblock %}
templates/login.html ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+ {% block content %}
3
+
4
+ <style>
5
+ .auth-wrapper {
6
+ min-height: 80vh;
7
+ display: flex;
8
+ justify-content: center;
9
+ align-items: center;
10
+ background: linear-gradient(135deg, #4a90e2, #7b4397);
11
+ padding: 20px;
12
+ }
13
+
14
+ .auth-card {
15
+ width: 100%;
16
+ max-width: 420px;
17
+ background: #ffffff;
18
+ border-radius: 12px;
19
+ box-shadow: 0 10px 30px rgba(0,0,0,0.15);
20
+ padding: 26px 22px 28px;
21
+ box-sizing: border-box;
22
+ }
23
+
24
+ .auth-title {
25
+ text-align: center;
26
+ margin-bottom: 8px;
27
+ font-size: 24px;
28
+ color: #333;
29
+ }
30
+
31
+ .auth-subtitle {
32
+ text-align: center;
33
+ font-size: 13px;
34
+ color: #777;
35
+ margin-bottom: 20px;
36
+ }
37
+
38
+ .auth-form label {
39
+ font-size: 14px;
40
+ color: #444;
41
+ }
42
+
43
+ .auth-form input {
44
+ width: 100%;
45
+ padding: 10px;
46
+ margin: 6px 0 16px;
47
+ border-radius: 6px;
48
+ border: 1px solid #ccc;
49
+ font-size: 14px;
50
+ outline: none;
51
+ box-sizing: border-box;
52
+ }
53
+
54
+ .auth-form input:focus {
55
+ border-color: #4a90e2;
56
+ box-shadow: 0 0 0 2px rgba(74,144,226,0.2);
57
+ }
58
+
59
+ .auth-btn {
60
+ width: 100%;
61
+ padding: 10px;
62
+ border-radius: 6px;
63
+ border: none;
64
+ background: #4a90e2;
65
+ color: #fff;
66
+ font-size: 15px;
67
+ font-weight: 600;
68
+ cursor: pointer;
69
+ margin-top: 4px;
70
+ }
71
+
72
+ .auth-btn:hover {
73
+ background: #3a7bd5;
74
+ }
75
+
76
+ .auth-extra {
77
+ margin-top: 12px;
78
+ font-size: 13px;
79
+ text-align: center;
80
+ }
81
+
82
+ .auth-extra a {
83
+ color: #4a90e2;
84
+ text-decoration: none;
85
+ font-weight: 600;
86
+ }
87
+
88
+ .auth-extra a:hover {
89
+ text-decoration: underline;
90
+ }
91
+
92
+ @media (max-width: 480px) {
93
+ .auth-card {
94
+ padding: 22px 16px 24px;
95
+ }
96
+ }
97
+ </style>
98
+
99
+ <div class="auth-wrapper">
100
+ <div class="auth-card">
101
+ <h2 class="auth-title">Login</h2>
102
+ <p class="auth-subtitle">Welcome back! Please enter your details.</p>
103
+
104
+ <form method="post" class="auth-form">
105
+ <label>Email</label><br/>
106
+ <input name="email" type="email" required><br/>
107
+
108
+ <label>Password</label><br/>
109
+ <input name="password" type="password" required><br/>
110
+
111
+ <button type="submit" class="auth-btn">Login</button>
112
+ </form>
113
+
114
+ <p class="auth-extra">
115
+ <a href="{{ url_for('forgot') }}">Forgot password?</a>
116
+ </p>
117
+ </div>
118
+ </div>
119
+
120
+ {% endblock %}
templates/register.html ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+ {% block content %}
3
+
4
+ <style>
5
+ .auth-wrapper {
6
+ min-height: 80vh;
7
+ display: flex;
8
+ justify-content: center;
9
+ align-items: center;
10
+ background: linear-gradient(135deg, #4a90e2, #7b4397);
11
+ padding: 20px;
12
+ }
13
+
14
+ .auth-card {
15
+ width: 100%;
16
+ max-width: 480px;
17
+ background: #ffffff;
18
+ border-radius: 12px;
19
+ box-shadow: 0 10px 30px rgba(0,0,0,0.15);
20
+ padding: 26px 22px 28px;
21
+ box-sizing: border-box;
22
+ }
23
+
24
+ .auth-title {
25
+ text-align: center;
26
+ margin-bottom: 8px;
27
+ font-size: 24px;
28
+ color: #333;
29
+ }
30
+
31
+ .auth-subtitle {
32
+ text-align: center;
33
+ font-size: 13px;
34
+ color: #777;
35
+ margin-bottom: 20px;
36
+ }
37
+
38
+ .auth-form label {
39
+ font-size: 14px;
40
+ color: #444;
41
+ }
42
+
43
+ .auth-form input,
44
+ .auth-form textarea {
45
+ width: 100%;
46
+ padding: 10px;
47
+ margin: 6px 0 16px;
48
+ border-radius: 6px;
49
+ border: 1px solid #ccc;
50
+ font-size: 14px;
51
+ outline: none;
52
+ box-sizing: border-box;
53
+ resize: vertical;
54
+ }
55
+
56
+ .auth-form input:focus,
57
+ .auth-form textarea:focus {
58
+ border-color: #4a90e2;
59
+ box-shadow: 0 0 0 2px rgba(74,144,226,0.2);
60
+ }
61
+
62
+ .auth-btn {
63
+ width: 100%;
64
+ padding: 10px;
65
+ border-radius: 6px;
66
+ border: none;
67
+ background: #4a90e2;
68
+ color: #fff;
69
+ font-size: 15px;
70
+ font-weight: 600;
71
+ cursor: pointer;
72
+ margin-top: 4px;
73
+ }
74
+
75
+ .auth-btn:hover {
76
+ background: #3a7bd5;
77
+ }
78
+
79
+ @media (max-width: 480px) {
80
+ .auth-card {
81
+ padding: 22px 16px 24px;
82
+ }
83
+ }
84
+ </style>
85
+
86
+ <div class="auth-wrapper">
87
+ <div class="auth-card">
88
+ <h2 class="auth-title">Register</h2>
89
+ <p class="auth-subtitle">Create your account to get started.</p>
90
+
91
+ <form method="post" class="auth-form">
92
+ <label>Full name</label><br/>
93
+ <input name="fullname" required><br/>
94
+
95
+ <label>Email</label><br/>
96
+ <input name="email" type="email" required><br/>
97
+
98
+ <label>Password</label><br/>
99
+ <input name="password" type="password" required><br/>
100
+
101
+ <label>Phone</label><br/>
102
+ <input name="phone"><br/>
103
+
104
+ <label>Address</label><br/>
105
+ <textarea name="address" rows="3"></textarea><br/>
106
+
107
+ <button type="submit" class="auth-btn">Register</button>
108
+ </form>
109
+ </div>
110
+ </div>
111
+
112
+ {% endblock %}
templates/reset.html ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {% extends "base.html" %}
2
+ {% block content %}
3
+ <h2>Reset Password</h2>
4
+ <form method="post">
5
+ <label>Email</label><br/><input name="email" type="email" required><br/>
6
+ <label>OTP (check email)</label><br/><input name="otp" required><br/>
7
+ <label>New password</label><br/><input name="password" type="password" required><br/>
8
+ <button type="submit">Reset Password</button>
9
+ </form>
10
+ {% endblock %}