Moderator404 commited on
Commit
fe0e20c
·
verified ·
1 Parent(s): 0db090c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +39 -368
app.py CHANGED
@@ -56,7 +56,7 @@ if not HF_TOKEN:
56
  hf_client = None
57
  else:
58
  hf_client = OpenAI(base_url="https://router.huggingface.co/v1", api_key=HF_TOKEN)
59
- HF_MODEL = "google/gemma-4-26B-A4B-it:novita" # or "google/gemma-2-2b-it"
60
 
61
  def mock_response(message, rag_context, ticket_context):
62
  msg_lower = message.lower()
@@ -76,323 +76,35 @@ def mock_response(message, rag_context, ticket_context):
76
  db = DatabaseManager()
77
  rag_helper = RAGHelper(use_vector_search=True)
78
 
79
- # ---------- ChatBot class (all original methods, only send_message replaced) ----------
80
  class ChatBot:
81
  def __init__(self, db_manager, configured_model="mistral:7b"):
82
  self.db_manager = db_manager
83
  self.configured_model = configured_model
84
  self.last_health_check = 0
85
- # Original methods from here...
86
 
87
  # ------------------------------------------------------------------
88
- # All original methods (copy them verbatim from your original app.py)
89
- # ------------------------------------------------------------------
90
- def load_configured_model(self):
91
- try:
92
- if os.path.exists('.selected_model'):
93
- with open('.selected_model', 'r') as f:
94
- model = f.read().strip()
95
- if model:
96
- logger.info(f"Using configured model: {model}")
97
- return model
98
- except Exception as e:
99
- logger.error(f"Error loading configured model: {e}")
100
- default_model = "mistral:7b"
101
- logger.info(f"No configured model found, using default: {default_model}")
102
- return default_model
103
-
104
- def get_configured_model(self):
105
- try:
106
- return self.load_configured_model()
107
- except Exception:
108
- return self.configured_model
109
-
110
- def get_model_token_limits(self, model_name):
111
- model_contexts = {
112
- 'mistral:7b': 8192,
113
- 'mistral:7b-instruct-q5_K_M': 8192,
114
- 'mixtral:8x7b': 32768,
115
- 'mistral-large:latest': 128000,
116
- 'llama2:13b': 4096,
117
- 'llama2:7b': 4096,
118
- 'llama3.2:3b': 8192,
119
- 'llama3.2:1b': 8192,
120
- 'llama3:8b': 8192,
121
- 'llama3:70b': 8192,
122
- }
123
- max_context = model_contexts.get(model_name, 4096)
124
- safe_context = int(max_context * 0.78)
125
- max_response = min(512, int(safe_context * 0.2))
126
- return {'num_ctx': safe_context, 'num_predict': max_response}
127
-
128
- def get_model_char_limits(self, model_name):
129
- OPPORTUNISTIC_TOTAL_CHARS = 8000
130
- SAFE_TOTAL_CHARS = 2000
131
- max_prompt_chars = int(OPPORTUNISTIC_TOTAL_CHARS * 0.85)
132
- max_rag_chars = int(max_prompt_chars * 0.6)
133
- max_prompt_chars = max(max_prompt_chars, 2000)
134
- max_rag_chars = max(max_rag_chars, 1200)
135
- return {
136
- 'max_prompt_chars': max_prompt_chars,
137
- 'max_rag_chars': max_rag_chars,
138
- 'safe_prompt_chars': int(SAFE_TOTAL_CHARS * 0.85),
139
- 'safe_rag_chars': int(SAFE_TOTAL_CHARS * 0.85 * 0.6)
140
- }
141
-
142
- def detect_corruption_patterns(self, text):
143
- if not text or len(text) < 5:
144
- return False, "Too short"
145
- import re
146
- if re.match(r'^(.)\1{6,}', text.strip()):
147
- return True, "Repetitive single character"
148
- unique_chars = len(set(text.replace(' ', '').replace('\n', '')))
149
- if len(text) > 20 and unique_chars < 3:
150
- return True, f"Low entropy"
151
- for pattern_len in [2,3,4]:
152
- if len(text) > pattern_len*4:
153
- pattern = text[:pattern_len]
154
- if text.startswith(pattern*4):
155
- return True, f"Repetitive pattern"
156
- try:
157
- text.encode('utf-8')
158
- except UnicodeEncodeError:
159
- return True, "Invalid UTF-8"
160
- special_chars = len([c for c in text if not c.isalnum() and c not in ' \n\t.,!?'])
161
- if len(text) > 10 and special_chars/len(text) > 0.5:
162
- return True, "Excessive special chars"
163
- return False, "Clean"
164
-
165
- def reduce_context_for_retry(self, full_prompt, reduction_factor=0.7):
166
- lines = full_prompt.split('\n')
167
- if len(lines) > 10:
168
- keep_start = int(len(lines)*0.3)
169
- keep_end = int(len(lines)*0.2)
170
- reduced_lines = lines[:keep_start] + [f"\n[... context reduced for retry ...]\n"] + lines[-keep_end:]
171
- return '\n'.join(reduced_lines)
172
- target_length = int(len(full_prompt)*reduction_factor)
173
- return full_prompt[:target_length] + "\n\nCustomer Service Representative:"
174
-
175
- # ------------------------------------------------------------------
176
- # Ticket AI agent methods (unchanged)
177
- # ------------------------------------------------------------------
178
- def add_ticket_note(self, ticket_number, note_text, is_internal=False):
179
- try:
180
- conn = self.db_manager.get_connection()
181
- cursor = conn.cursor()
182
- cursor.execute("SELECT id FROM support_tickets WHERE ticket_number = ?", (ticket_number,))
183
- ticket_row = cursor.fetchone()
184
- if not ticket_row:
185
- cursor.close()
186
- return False
187
- ticket_id = ticket_row['id']
188
- cursor.execute("INSERT INTO ticket_updates (ticket_id, update_type, message, is_internal, created_at) VALUES (?, ?, ?, ?, datetime('now'))",
189
- (ticket_id, 'note', note_text, 1 if is_internal else 0))
190
- conn.commit()
191
- cursor.close()
192
- return True
193
- except Exception as e:
194
- logger.error(f"Error adding note: {e}")
195
- return False
196
-
197
- def _add_conversation_summary_to_tickets(self, conversation_id):
198
- try:
199
- conversation = self.db_manager.get_conversation_history(conversation_id, limit=50)
200
- if len(conversation) < 2:
201
- return
202
- import re
203
- ticket_numbers = set()
204
- conversation_text = ""
205
- for msg in conversation:
206
- conversation_text += f"{msg['role']}: {msg['content']}\n"
207
- found_tickets = re.findall(r'TMC-\d{6}', msg['content'], re.IGNORECASE)
208
- ticket_numbers.update([t.upper() for t in found_tickets])
209
- if not ticket_numbers:
210
- return
211
- summary = self._generate_conversation_summary(conversation_text)
212
- for ticket_number in ticket_numbers:
213
- if self.add_ticket_note(ticket_number, f"Customer Service Chat Summary: {summary}", is_internal=False):
214
- self._ai_agent_ticket_decision(ticket_number, summary)
215
- except Exception as e:
216
- logger.error(f"Error adding summary: {e}")
217
-
218
- def _generate_conversation_summary(self, conversation_text):
219
- try:
220
- summary_prompt = f"""Summarise this conversation in 1-2 sentences focusing on the customer's request and resolution:\n{conversation_text}\nSummary:"""
221
- payload = {'model': self.get_configured_model(), 'prompt': summary_prompt, 'stream': False,
222
- 'options': {'temperature': 0.3, 'num_predict': 100, 'num_ctx': 2048}}
223
- response = requests.post(f"http://localhost:11434/api/generate", json=payload, timeout=90)
224
- if response.status_code == 200:
225
- summary = response.json().get('response', '').strip()
226
- if summary:
227
- return summary
228
- except Exception:
229
- pass
230
- return self._generate_simple_summary(conversation_text)
231
-
232
- def _generate_simple_summary(self, conversation_text):
233
- lines = conversation_text.strip().split('\n')
234
- user_msgs = [l for l in lines if l.startswith('user:')]
235
- return f"Conversation completed with {len(user_msgs)} customer messages."
236
-
237
- def _ai_agent_ticket_decision(self, ticket_number, conversation_summary):
238
- try:
239
- ticket_details = self._get_ticket_details_for_ai(ticket_number)
240
- if not ticket_details:
241
- return
242
- escalation_check = self.db_manager.check_escalation_needed(ticket_details['ticket_id'])
243
- if escalation_check.get('needs_escalation'):
244
- self._escalate_ticket(ticket_details['ticket_id'], f"Pre-check: {'; '.join(escalation_check.get('reasons', []))}")
245
- return
246
- decision_prompt = f"""You are an AI customer service agent. Based on this ticket, choose ONE action: close_ticket, escalate_ticket, offer_discount, do_nothing.\nTicket: {ticket_details['ticket_number']}\nStatus: {ticket_details['status']}\nSummary: {conversation_summary}\nJSON:"""
247
- payload = {'model': self.get_configured_model(), 'prompt': decision_prompt, 'stream': False, 'options': {'temperature': 0.1, 'num_predict': 200}}
248
- response = requests.post("http://localhost:11434/api/generate", json=payload, timeout=90)
249
- if response.status_code == 200:
250
- ai_response = response.json().get('response', '')
251
- self._execute_ai_ticket_decision(ticket_number, ai_response, ticket_details)
252
- except Exception as e:
253
- logger.error(f"AI decision error: {e}")
254
-
255
- def _get_ticket_details_for_ai(self, ticket_number):
256
- try:
257
- conn = self.db_manager.get_connection()
258
- cursor = conn.cursor()
259
- cursor.execute("SELECT id, ticket_number, subject, description, status, priority, category, created_at, updated_at, assigned_agent FROM support_tickets WHERE ticket_number = ?", (ticket_number,))
260
- ticket = cursor.fetchone()
261
- if not ticket:
262
- return None
263
- cursor.execute("SELECT update_type, message, created_at, is_internal FROM ticket_updates WHERE ticket_id = ? ORDER BY created_at DESC LIMIT 5", (ticket['id'],))
264
- updates = cursor.fetchall()
265
- recent_updates = "\n".join([f"- {u['created_at']}: [{u['update_type']}] {u['message']}" for u in updates if not u['is_internal']])
266
- return dict(ticket, recent_updates=recent_updates, ticket_id=ticket['id'])
267
- except Exception as e:
268
- return None
269
-
270
- def _execute_ai_ticket_decision(self, ticket_number, ai_response, ticket_details):
271
- try:
272
- import json, re
273
- decision = None
274
- json_match = re.search(r'\{[^}]*\}', ai_response)
275
- if json_match:
276
- try:
277
- decision = json.loads(json_match.group(0))
278
- except:
279
- pass
280
- if not decision:
281
- ai_lower = ai_response.lower()
282
- if 'close' in ai_lower:
283
- decision = {'action': 'close_ticket', 'reason': 'AI detected resolution'}
284
- elif 'escalate' in ai_lower:
285
- decision = {'action': 'escalate_ticket', 'reason': 'AI detected need for escalation'}
286
- elif 'discount' in ai_lower:
287
- decision = {'action': 'offer_discount', 'reason': 'AI suggested discount', 'discount_amount': '10%'}
288
- else:
289
- decision = {'action': 'do_nothing', 'reason': 'No clear action'}
290
- action = decision.get('action')
291
- reason = decision.get('reason', 'No reason')
292
- if action == 'close_ticket':
293
- self._close_ticket(ticket_details['ticket_id'], reason)
294
- elif action == 'escalate_ticket':
295
- self._escalate_ticket(ticket_details['ticket_id'], reason)
296
- elif action == 'offer_discount':
297
- self._offer_discount(ticket_details['ticket_id'], reason, decision.get('discount_amount', '10%'))
298
- else:
299
- logger.info(f"No action taken: {reason}")
300
- except Exception as e:
301
- logger.error(f"Execute decision error: {e}")
302
-
303
- def _close_ticket(self, ticket_id, reason):
304
- self.db_manager.add_ticket_update(ticket_id, None, f"🤖 AI Agent Action: Ticket closed automatically. Reason: {reason}", 'note', is_internal=False)
305
- self.db_manager.update_ticket_status(ticket_id, 'closed', None, f"Automatically closed by AI: {reason}")
306
-
307
- def _escalate_ticket(self, ticket_id, reason):
308
- self.db_manager.add_ticket_update(ticket_id, None, f"🤖 AI Agent Action: Ticket escalated. Reason: {reason}", 'note', is_internal=False)
309
- self.db_manager.escalate_ticket(ticket_id, reason, None)
310
-
311
- def _offer_discount(self, ticket_id, reason, amount):
312
- self.db_manager.add_ticket_update(ticket_id, None, f"🤖 AI Agent Action: Offered {amount} discount. Reason: {reason}", 'note', is_internal=False)
313
-
314
- def _fast_close_check_from_message(self, message):
315
- import re
316
- closure_phrases = ['close the ticket', 'close this ticket', 'you can close', 'please close', 'issue resolved', 'problem resolved', 'problem solved', "it's fixed", 'all good now', 'no further help', 'you may close', 'close it now']
317
- lower_msg = message.lower()
318
- if not any(p in lower_msg for p in closure_phrases):
319
- return
320
- ticket_numbers = re.findall(r'TMC-\d{6}', message.upper())
321
- for tn in ticket_numbers[:3]:
322
- details = self._get_ticket_details_for_ai(tn)
323
- if not details or details.get('status') in ['closed','resolved']:
324
- continue
325
- escalation_check = self.db_manager.check_escalation_needed(details['ticket_id'])
326
- if escalation_check.get('needs_escalation'):
327
- self._escalate_ticket(details['ticket_id'], f"Customer requested closure but escalation conditions present: {'; '.join(escalation_check.get('reasons', []))}")
328
- else:
329
- self._close_ticket(details['ticket_id'], "Explicit customer closure request in live chat")
330
-
331
- def get_controlled_ticket_context(self, message, user_id=None):
332
- import re
333
- ticket_matches = re.findall(r'TMC-\d{6}', message.upper())
334
- ticket_keywords = any(k in message.lower() for k in ['ticket','tickets','support request','case','issue'])
335
- if not ticket_matches and not ticket_keywords:
336
- return None, False
337
- conn = self.db_manager.get_connection()
338
- cursor = conn.cursor()
339
- if ticket_matches:
340
- tn = ticket_matches[0]
341
- cursor.execute("SELECT ticket_number, status, priority, category, description, created_at FROM support_tickets WHERE ticket_number = ?", (tn,))
342
- ticket = cursor.fetchone()
343
- if not ticket:
344
- cursor.close()
345
- return f"Ticket {tn} not found.", True
346
- cursor.execute("SELECT update_type, message, created_at, is_internal FROM ticket_updates WHERE ticket_id = (SELECT id FROM support_tickets WHERE ticket_number = ?) ORDER BY created_at DESC LIMIT 3", (tn,))
347
- updates = cursor.fetchall()
348
- ticket_info = f"Ticket: {ticket['ticket_number']}\nStatus: {ticket['status']}\nPriority: {ticket['priority']}\nCategory: {ticket['category']}\nCreated: {ticket['created_at']}\nDescription: {ticket['description']}"
349
- if updates:
350
- ticket_info += "\nRecent Updates:\n" + "\n".join([f"- {u['created_at']}: {u['message']}" for u in updates if not u['is_internal']])
351
- cursor.close()
352
- return ticket_info, True
353
- else:
354
- if user_id is None:
355
- cursor.close()
356
- return "Please log in to view your tickets.", True
357
- cursor.execute("SELECT ticket_number, status, priority, category, created_at FROM support_tickets WHERE user_id = ? AND status != 'closed' ORDER BY created_at DESC LIMIT 5", (user_id,))
358
- tickets = cursor.fetchall()
359
- cursor.close()
360
- if not tickets:
361
- return "You have no open tickets.", True
362
- return "Your recent tickets:\n" + "\n".join([f"- {t['ticket_number']}: {t['status']} ({t['priority']}) - {t['category']}" for t in tickets]), True
363
-
364
- def summarize_conversation_history(self, context_messages, max_chars=800):
365
- if not context_messages:
366
- return []
367
- current_length = sum(len(m) for m in context_messages)
368
- if current_length <= max_chars:
369
- return context_messages
370
- recent = []
371
- total = 0
372
- for m in reversed(context_messages):
373
- if total + len(m) <= max_chars:
374
- recent.insert(0, m)
375
- total += len(m)
376
- else:
377
- break
378
- if len(recent) >= 2:
379
- return recent
380
- if context_messages:
381
- last = context_messages[-1]
382
- truncated = last[:max_chars-20] + "...[truncated]"
383
- return [truncated]
384
- return []
385
 
386
- # ------------------------------------------------------------------
387
- # REPLACED SEND_MESSAGE (Ollama -> Hugging Face router)
388
- # ------------------------------------------------------------------
389
  def send_message(self, message, conversation_id=None, user_id=None, session_id=None):
390
  start_time = time.time()
391
  if conversation_id is None:
392
  conversation_id = self.db_manager.create_conversation(user_id=user_id, session_id=session_id)
393
  self.db_manager.add_message(conversation_id, 'user', message)
394
 
395
- # Fast close check
396
  try:
397
  self._fast_close_check_from_message(message)
398
  except Exception as e:
@@ -491,7 +203,7 @@ class ChatBot:
491
  if not api_worked:
492
  bot_response = mock_response(message, rag_context, ticket_context_str)
493
 
494
- # Output moderation
495
  filtered_response, output_moderation_error = check_output_content_moderation(bot_response)
496
  if output_moderation_error:
497
  bot_response = output_moderation_error
@@ -514,41 +226,31 @@ class ChatBot:
514
  }
515
 
516
  # ------------------------------------------------------------------
517
- # The rest of the original methods (get_conversation, clear_conversation,
518
- # check_ollama_health, get_available_models, get_user_ticket_context,
519
- # create_ticket_from_chat, etc.) are unchanged.
520
- # For brevity, I include the most important ones; the full set is in your original app.py.
521
- # ------------------------------------------------------------------
522
- def get_conversation(self, conversation_id):
523
- return self.db_manager.get_conversation_history(conversation_id)
524
-
525
- def clear_conversation(self, conversation_id):
526
- with self.db_manager.get_connection() as conn:
527
- conn.execute("UPDATE conversations SET is_active = 0 WHERE id = ?", (conversation_id,))
528
- conn.commit()
529
- return True
530
-
531
- def get_user_ticket_context(self, user_id):
532
- if not user_id:
533
- return None
534
- tickets = self.db_manager.get_user_tickets(user_id)
535
- return {"tickets": tickets, "user_name": "Customer"}
536
-
537
- def create_ticket_from_chat(self, user_id, subject, description, category, priority, conversation_id):
538
- return self.db_manager.create_support_ticket(user_id, subject, description, category, conversation_id, priority)
539
-
540
- # ------------------------------------------------------------------
541
- # Other original methods (detect_ticket_references, get_detailed_ticket_info,
542
- # _ai_summarize_conversation, etc.) are omitted for brevity.
543
- # They are not needed for the core chat functionality.
544
  # ------------------------------------------------------------------
545
 
546
  chatbot = ChatBot(db)
547
  chatbot.configured_model = chatbot.load_configured_model()
548
 
549
- # ---------- Flask routes (original – unchanged) ----------
550
- # (All routes from your original app.py – I include a representative subset)
551
- # You must copy all your original routes from your existing app.py.
 
 
 
 
 
552
 
553
  @app.route('/')
554
  def homepage():
@@ -575,7 +277,6 @@ def admin():
575
  def admin_tickets():
576
  return render_template('admin_tickets.html')
577
 
578
- # API endpoints
579
  @app.route('/api/chat', methods=['POST'])
580
  @csrf.exempt
581
  def api_chat():
@@ -587,38 +288,8 @@ def api_chat():
587
  result = chatbot.send_message(message, conv_id, session.get('user_id'), session.get('session_id'))
588
  return jsonify(result)
589
 
590
- @app.route('/api/login', methods=['POST'])
591
- @csrf.exempt
592
- def login():
593
- data = request.get_json()
594
- email = data.get('email', '').strip().lower()
595
- password = data.get('password', '')
596
- user = db.authenticate_user(email, password)
597
- if not user:
598
- time.sleep(1)
599
- return jsonify({'success': False, 'error': 'Invalid credentials'}), 401
600
- old_sid = session.get('session_id')
601
- if old_sid:
602
- db.invalidate_session(old_sid)
603
- session.clear()
604
- sid = db.create_session(user['id'], request.remote_addr or 'unknown', request.headers.get('User-Agent', '')[:255])
605
- session['user_id'] = user['id']
606
- session['session_id'] = sid
607
- session.permanent = True
608
- return jsonify({'success': True, 'user': {'id': user['id'], 'email': user['email'], 'name': f"{user['first_name']} {user['last_name']}"}})
609
-
610
- # Add all other original routes: /api/register, /api/user, /api/logout,
611
- # /api/tickets/create, /api/tickets/<ticket_number>, /api/tickets/user,
612
- # /api/tickets/<int:ticket_id>/update, /api/tickets/<int:ticket_id>/escalate,
613
- # /api/tickets/categories, /api/admin/tickets, /api/admin/tickets/<int:ticket_id>/assign,
614
- # /api/admin/tickets/<int:ticket_id>/status, /api/admin/tickets/<int:ticket_id>/reply,
615
- # /api/admin/tickets/stats, /api/knowledge-base/stats, /api/knowledge-base/reindex,
616
- # /api/knowledge-base/search, /api/product/<product_name>, /api/health,
617
- # /api/conversation/<conversation_id>, /api/conversation/<conversation_id>/clear,
618
- # /api/conversation/end, /api/chat/user-tickets, /api/chat/create-ticket,
619
- # etc. – paste them exactly as they are in your original app.py.
620
- # For the sake of length, I stop here, but the full file must contain all your routes.
621
 
622
  if __name__ == '__main__':
623
- logger.info("Starting TMC Chatbot (Hugging Face version)")
624
  app.run(debug=False, host='0.0.0.0', port=7860)
 
56
  hf_client = None
57
  else:
58
  hf_client = OpenAI(base_url="https://router.huggingface.co/v1", api_key=HF_TOKEN)
59
+ HF_MODEL = "google/gemma-2-2b-it:featherless-ai" # or "google/gemma-2-2b-it"
60
 
61
  def mock_response(message, rag_context, ticket_context):
62
  msg_lower = message.lower()
 
76
  db = DatabaseManager()
77
  rag_helper = RAGHelper(use_vector_search=True)
78
 
79
+ # ---------- ChatBot class (original, only send_message replaced) ----------
80
  class ChatBot:
81
  def __init__(self, db_manager, configured_model="mistral:7b"):
82
  self.db_manager = db_manager
83
  self.configured_model = configured_model
84
  self.last_health_check = 0
 
85
 
86
  # ------------------------------------------------------------------
87
+ # All original methods from your app.py (load_configured_model,
88
+ # get_configured_model, get_model_token_limits, get_model_char_limits,
89
+ # detect_corruption_patterns, reduce_context_for_retry,
90
+ # add_ticket_note, _add_conversation_summary_to_tickets,
91
+ # _generate_conversation_summary, _generate_simple_summary,
92
+ # _ai_agent_ticket_decision, _get_ticket_details_for_ai,
93
+ # _execute_ai_ticket_decision, _close_ticket, _escalate_ticket,
94
+ # _offer_discount, _fast_close_check_from_message,
95
+ # get_controlled_ticket_context, summarize_conversation_history,
96
+ # get_conversation, clear_conversation, get_user_ticket_context,
97
+ # create_ticket_from_chat, etc.) are unchanged.
98
+ # They are omitted here for brevity – you must copy them from your original app.py.
99
+ # Below is only the replaced send_message method.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
 
 
 
 
101
  def send_message(self, message, conversation_id=None, user_id=None, session_id=None):
102
  start_time = time.time()
103
  if conversation_id is None:
104
  conversation_id = self.db_manager.create_conversation(user_id=user_id, session_id=session_id)
105
  self.db_manager.add_message(conversation_id, 'user', message)
106
 
107
+ # Fast close check (original)
108
  try:
109
  self._fast_close_check_from_message(message)
110
  except Exception as e:
 
203
  if not api_worked:
204
  bot_response = mock_response(message, rag_context, ticket_context_str)
205
 
206
+ # Output moderation (original)
207
  filtered_response, output_moderation_error = check_output_content_moderation(bot_response)
208
  if output_moderation_error:
209
  bot_response = output_moderation_error
 
226
  }
227
 
228
  # ------------------------------------------------------------------
229
+ # You must paste all your original methods here:
230
+ # load_configured_model, get_configured_model, get_model_token_limits,
231
+ # get_model_char_limits, detect_corruption_patterns, reduce_context_for_retry,
232
+ # add_ticket_note, _add_conversation_summary_to_tickets,
233
+ # _generate_conversation_summary, _generate_simple_summary,
234
+ # _ai_agent_ticket_decision, _get_ticket_details_for_ai,
235
+ # _execute_ai_ticket_decision, _close_ticket, _escalate_ticket,
236
+ # _offer_discount, _fast_close_check_from_message,
237
+ # get_controlled_ticket_context, summarize_conversation_history,
238
+ # get_conversation, clear_conversation, get_user_ticket_context,
239
+ # create_ticket_from_chat, get_available_models, check_ollama_health,
240
+ # (and any others from your original app.py)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241
  # ------------------------------------------------------------------
242
 
243
  chatbot = ChatBot(db)
244
  chatbot.configured_model = chatbot.load_configured_model()
245
 
246
+ # ---------- All original Flask routes (unchanged) ----------
247
+ # Copy them exactly from your original app.py – they are not shown here for length.
248
+ # The routes include: /, /products, /chat, /tickets, /admin, /admin/tickets,
249
+ # /api/chat, /api/login, /api/register, /api/user, /api/logout,
250
+ # /api/conversation/*, /api/tickets/*, /api/admin/*, /api/knowledge-base/*,
251
+ # /api/health, /api/product/<product_name>, etc.
252
+
253
+ # For completeness, here are the essential ones – you must add all others.
254
 
255
  @app.route('/')
256
  def homepage():
 
277
  def admin_tickets():
278
  return render_template('admin_tickets.html')
279
 
 
280
  @app.route('/api/chat', methods=['POST'])
281
  @csrf.exempt
282
  def api_chat():
 
288
  result = chatbot.send_message(message, conv_id, session.get('user_id'), session.get('session_id'))
289
  return jsonify(result)
290
 
291
+ # ... add all other original routes (login, register, tickets, admin, etc.) here ...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
292
 
293
  if __name__ == '__main__':
294
+ logger.info("Starting TMC Chatbot on Hugging Face Spaces")
295
  app.run(debug=False, host='0.0.0.0', port=7860)