k96beni commited on
Commit
a1352f4
·
verified ·
1 Parent(s): d593db7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +298 -82
app.py CHANGED
@@ -14,6 +14,8 @@ import uuid
14
  from user_agents import parse as parse_ua
15
  import schedule
16
  import threading
 
 
17
 
18
  # --- Konfiguration ---
19
  CHARGENODE_URL = "https://www.chargenode.eu"
@@ -55,14 +57,42 @@ scheduler = CommitScheduler(
55
 
56
  # --- Globala variabler ---
57
  last_log = None # Sparar loggdata från senaste svar för feedback
 
 
58
 
59
  # --- Förbättrad loggfunktion ---
60
  def safe_append_to_log(log_entry):
61
  """Säker metod för att lägga till loggdata utan att förlora historisk information."""
62
  try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  # Öppna filen i append-läge
64
  with open(log_file_path, "a", encoding="utf-8") as log_file:
65
- log_json = json.dumps(log_entry)
66
  log_file.write(log_json + "\n")
67
  log_file.flush() # Säkerställ att data skrivs till disk omedelbart
68
 
@@ -72,16 +102,22 @@ def safe_append_to_log(log_entry):
72
  except Exception as e:
73
  print(f"Fel vid loggning: {e}")
74
 
75
- # Försök skapa mappen om den inte finns
76
  try:
77
  os.makedirs(os.path.dirname(log_file_path), exist_ok=True)
78
 
79
- # Försök igen
 
 
 
 
 
 
80
  with open(log_file_path, "a", encoding="utf-8") as log_file:
81
- log_json = json.dumps(log_entry)
82
  log_file.write(log_json + "\n")
83
 
84
- print("Loggpost tillagd efter återhämtning")
85
  return True
86
 
87
  except Exception as retry_error:
@@ -156,41 +192,111 @@ def prepare_chunks(text_data):
156
  embedder = None
157
  embeddings = None
158
  index = None
 
 
 
159
 
160
  def initialize_embeddings():
161
  """Initierar SentenceTransformer och FAISS-index vid första anrop."""
162
  global embedder, embeddings, index, chunks, chunk_sources
163
 
164
- if embedder is None:
165
- print("Initierar SentenceTransformer och FAISS-index...")
166
- # Ladda och förbered lokal data
167
- print("Laddar textdata...")
168
- text_data = {"local_files": load_local_files()}
169
- print("Förbereder textsegment...")
170
- chunks, chunk_sources = prepare_chunks(text_data)
171
- print(f"{len(chunks)} segment laddade")
172
-
173
- print("Skapar embeddings...")
174
- embedder = SentenceTransformer('all-MiniLM-L6-v2')
175
- embeddings = embedder.encode(chunks, convert_to_numpy=True)
176
- embeddings /= np.linalg.norm(embeddings, axis=1, keepdims=True)
177
- index = faiss.IndexFlatIP(embeddings.shape[1])
178
- index.add(embeddings)
179
- print("FAISS-index klart")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
  def retrieve_context(query, k=RETRIEVAL_K):
181
  """Hämtar relevant kontext för frågor."""
182
  # Säkerställ att modeller är laddade
183
  initialize_embeddings()
184
 
185
- query_embedding = embedder.encode([query], convert_to_numpy=True)
186
- query_embedding /= np.linalg.norm(query_embedding)
187
- D, I = index.search(query_embedding, k)
188
- retrieved, sources = [], set()
189
- for idx in I[0]:
190
- if idx < len(chunks):
191
- retrieved.append(chunks[idx])
192
- sources.add(chunk_sources[idx])
193
- return " ".join(retrieved), list(sources)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
 
195
  def generate_answer(query):
196
  """Genererar svar baserat på fråga och kontextinformation."""
@@ -271,18 +377,27 @@ def vote(data: gr.LikeData):
271
  data.value innehåller information om meddelandet.
272
  """
273
  feedback_type = "up" if data.liked else "down"
274
- global last_log
 
 
 
 
 
 
275
  log_entry = {
276
  "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
277
  "feedback": feedback_type,
278
- "bot_reply": data.value if not isinstance(data.value, dict) else data.value.get("value")
279
  }
280
- # Om global logdata finns, lägg till ytterligare metadata.
281
- if last_log:
282
- log_entry.update({
283
- "session_id": last_log.get("session_id"),
284
- "user_message": last_log.get("user_message"),
285
- })
 
 
 
286
 
287
  # Använd den förbättrade loggfunktionen
288
  safe_append_to_log(log_entry)
@@ -290,10 +405,16 @@ def vote(data: gr.LikeData):
290
  # Skicka feedback till Slack
291
  try:
292
  if feedback_type == "down": # Skicka bara negativ feedback
 
 
 
 
 
 
293
  feedback_message = f"""
294
  *⚠️ Negativ feedback registrerad*
295
 
296
- *Fråga:* {last_log.get('user_message', 'Okänd fråga')}
297
 
298
  *Svar:* {log_entry.get('bot_reply', 'Okänt svar')[:300]}{'...' if len(log_entry.get('bot_reply', '')) > 300 else ''}
299
  """
@@ -536,26 +657,69 @@ def send_support_to_slack(områdeskod, uttagsnummer, email, chat_history):
536
  # --- Schemaläggning av rapporter ---
537
  def run_scheduler():
538
  """Kör schemaläggaren i en separat tråd med förenklad statusrapportering."""
539
- # Använd den förenklade funktionen för rapportering
540
- schedule.every().day.at("08:00").do(simple_status_report)
541
- schedule.every().day.at("12:00").do(simple_status_report)
542
- schedule.every().day.at("17:00").do(simple_status_report)
543
-
544
- # Veckorapport på måndagar
545
- schedule.every().monday.at("09:00").do(lambda: send_to_slack(
546
- "Veckostatistik",
547
- f"*ChargeNode AI Bot - Veckostatistik*\n\n{json.dumps(generate_monthly_stats(7), indent=2)}",
548
- "#3498db"
549
- ))
550
-
551
- while True:
552
- schedule.run_pending()
553
- time.sleep(60) # Kontrollera varje minut
 
 
 
 
 
 
 
 
 
 
 
 
554
 
555
  # Starta schemaläggaren i en separat tråd
556
  scheduler_thread = threading.Thread(target=run_scheduler, daemon=True)
557
  scheduler_thread.start()
558
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
559
  # Kör en statusrapport vid uppstart för att verifiera att allt fungerar
560
  try:
561
  print("Skickar en inledande statusrapport för att verifiera Slack-integrationen...")
@@ -565,35 +729,74 @@ except Exception as e:
565
 
566
  # Definiera respond och chat-relaterade funktioner före Gradio UI
567
  def respond(message, chat_history, request: gr.Request):
568
- global last_log
 
 
 
 
 
 
 
 
 
 
 
569
  start = time.time()
570
- response = generate_answer(message)
 
 
 
 
 
571
  elapsed = round(time.time() - start, 2)
572
 
573
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
574
  session_id = str(uuid.uuid4())
575
 
576
  # Använd session_id från tidigare logg om det finns
577
- if last_log and 'session_id' in last_log:
578
- session_id = last_log.get('session_id')
579
-
580
- user_id = request.client.host if request else "okänd"
581
-
582
- ua_str = request.headers.get("user-agent", "")
583
- ref = request.headers.get("referer", "")
584
- ip = request.headers.get("x-forwarded-for", user_id).split(",")[0]
585
- ua = parse_ua(ua_str)
586
- browser = f"{ua.browser.family} {ua.browser.version_string}"
587
- osys = f"{ua.os.family} {ua.os.version_string}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
588
 
 
589
  platform = "webb"
590
- if "chargenode.eu" in ref:
591
- platform = "chargenode.eu"
592
- elif "localhost" in ref:
593
- platform = "test"
594
- elif "app" in ref:
595
- platform = "app"
 
 
 
 
596
 
 
597
  log_data = {
598
  "timestamp": timestamp,
599
  "user_id": user_id,
@@ -609,7 +812,10 @@ def respond(message, chat_history, request: gr.Request):
609
 
610
  # Använd den förbättrade loggfunktionen
611
  safe_append_to_log(log_data)
612
- last_log = log_data
 
 
 
613
 
614
  # Skicka varje konversation direkt till Slack
615
  try:
@@ -631,8 +837,17 @@ def respond(message, chat_history, request: gr.Request):
631
  except Exception as e:
632
  print(f"Kunde inte skicka konversation till Slack: {e}")
633
 
634
- chat_history.append({"role": "user", "content": message})
635
- chat_history.append({"role": "assistant", "content": response})
 
 
 
 
 
 
 
 
 
636
 
637
  return "", chat_history
638
 
@@ -791,17 +1006,18 @@ with gr.Blocks(css=custom_css, title="ChargeNode Kundtjänst") as app:
791
 
792
  # Lägg till anpassad JavaScript för att styra scrollning
793
  js_code = """
794
- function scrollToTop() {
795
- const chatContainer = document.querySelector('.gradio-container .message-wrap');
796
- if (chatContainer) {
797
- chatContainer.scrollTop = 0;
 
798
  }
799
  }
800
 
801
  // Kör funktionen när nya meddelanden läggs till
802
  document.addEventListener('DOMNodeInserted', function(event) {
803
  if (event.target.classList && event.target.classList.contains('bot')) {
804
- setTimeout(scrollToTop, 100);
805
  }
806
  });
807
  """
@@ -841,4 +1057,4 @@ with gr.Blocks(css=custom_css, title="ChargeNode Kundtjänst") as app:
841
  )
842
 
843
  if __name__ == "__main__":
844
- app.launch(share=True)
 
14
  from user_agents import parse as parse_ua
15
  import schedule
16
  import threading
17
+ import io
18
+ import atexit
19
 
20
  # --- Konfiguration ---
21
  CHARGENODE_URL = "https://www.chargenode.eu"
 
57
 
58
  # --- Globala variabler ---
59
  last_log = None # Sparar loggdata från senaste svar för feedback
60
+ # Lägg till en lock för att skydda åtkomst till last_log
61
+ last_log_lock = threading.Lock()
62
 
63
  # --- Förbättrad loggfunktion ---
64
  def safe_append_to_log(log_entry):
65
  """Säker metod för att lägga till loggdata utan att förlora historisk information."""
66
  try:
67
+ # Kontrollera att loggmappen finns
68
+ os.makedirs(os.path.dirname(log_file_path), exist_ok=True)
69
+
70
+ # Kontrollera loggfilens storlek
71
+ try:
72
+ log_size = os.path.getsize(log_file_path)
73
+ # Om loggfilen är större än 10MB, rotera den
74
+ if log_size > 10 * 1024 * 1024: # 10MB
75
+ backup_path = f"{log_file_path}.bak"
76
+ if os.path.exists(backup_path):
77
+ os.remove(backup_path)
78
+ os.rename(log_file_path, backup_path)
79
+ print(f"Loggfil roterad: {log_file_path} -> {backup_path}")
80
+ except FileNotFoundError:
81
+ # Filen finns inte än, inget att rotera
82
+ pass
83
+
84
+ # Sanitera loggdata för att undvika potentiella injektioner
85
+ sanitized_entry = {}
86
+ for k, v in log_entry.items():
87
+ if isinstance(v, str):
88
+ # Ta bort null-bytes och begränsa längden
89
+ sanitized_entry[k] = v.replace('\0', '')[:10000] # Begränsa till 10000 tecken
90
+ else:
91
+ sanitized_entry[k] = v
92
+
93
  # Öppna filen i append-läge
94
  with open(log_file_path, "a", encoding="utf-8") as log_file:
95
+ log_json = json.dumps(sanitized_entry)
96
  log_file.write(log_json + "\n")
97
  log_file.flush() # Säkerställ att data skrivs till disk omedelbart
98
 
 
102
  except Exception as e:
103
  print(f"Fel vid loggning: {e}")
104
 
105
+ # Försök skapa mappen om den inte finns (detta bör redan ha gjorts ovan)
106
  try:
107
  os.makedirs(os.path.dirname(log_file_path), exist_ok=True)
108
 
109
+ # Försök igen med minimal loggdata för att undvika fel
110
+ minimal_entry = {
111
+ "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
112
+ "error": f"Fel vid loggning: {str(e)[:200]}",
113
+ "recovery": True
114
+ }
115
+
116
  with open(log_file_path, "a", encoding="utf-8") as log_file:
117
+ log_json = json.dumps(minimal_entry)
118
  log_file.write(log_json + "\n")
119
 
120
+ print("Minimal loggpost tillagd efter återhämtning")
121
  return True
122
 
123
  except Exception as retry_error:
 
192
  embedder = None
193
  embeddings = None
194
  index = None
195
+ chunks = []
196
+ chunk_sources = []
197
+ embedding_lock = threading.Lock()
198
 
199
  def initialize_embeddings():
200
  """Initierar SentenceTransformer och FAISS-index vid första anrop."""
201
  global embedder, embeddings, index, chunks, chunk_sources
202
 
203
+ # Använd en lock för att förhindra att flera trådar initierar samtidigt
204
+ with embedding_lock:
205
+ if embedder is None:
206
+ try:
207
+ print("Initierar SentenceTransformer och FAISS-index...")
208
+ # Ladda och förbered lokal data
209
+ print("Laddar textdata...")
210
+ text_data = {"local_files": load_local_files()}
211
+ print("Förbereder textsegment...")
212
+ chunks, chunk_sources = prepare_chunks(text_data)
213
+ print(f"{len(chunks)} segment laddade")
214
+
215
+ if not chunks:
216
+ print("Varning: Inga textsegment hittades. Kontrollera textdata.")
217
+ # Skapa tomma listor för att undvika fel
218
+ chunks = [""]
219
+ chunk_sources = ["empty"]
220
+
221
+ print("Skapar embeddings...")
222
+ embedder = SentenceTransformer('all-MiniLM-L6-v2')
223
+ embeddings = embedder.encode(chunks, convert_to_numpy=True)
224
+
225
+ # Kontrollera att embeddings inte är tomma
226
+ if embeddings.size > 0:
227
+ embeddings /= np.linalg.norm(embeddings, axis=1, keepdims=True)
228
+ index = faiss.IndexFlatIP(embeddings.shape[1])
229
+ index.add(embeddings)
230
+ print("FAISS-index klart")
231
+ else:
232
+ print("Varning: Tomma embeddings. Skapar ett tomt index.")
233
+ # Skapa ett tomt index med rätt dimensioner
234
+ index = faiss.IndexFlatIP(384) # Standard dimension för all-MiniLM-L6-v2
235
+ except Exception as e:
236
+ print(f"Fel vid initiering av embeddings: {e}")
237
+ # Sätt upp grundläggande värden för att undvika fel
238
+ if embedder is None:
239
+ print("Försöker återhämta från fel...")
240
+ try:
241
+ embedder = SentenceTransformer('all-MiniLM-L6-v2')
242
+ if not chunks:
243
+ chunks = ["Fel vid laddning av data"]
244
+ chunk_sources = ["error"]
245
+ embeddings = embedder.encode(chunks, convert_to_numpy=True)
246
+ index = faiss.IndexFlatIP(embeddings.shape[1])
247
+ index.add(embeddings)
248
+ print("Återhämtning lyckades")
249
+ except Exception as recovery_error:
250
+ print(f"Kunde inte återhämta: {recovery_error}")
251
+ # Sätt upp dummy-värden som sista utväg
252
+ embedder = None
253
+ embeddings = np.zeros((1, 384))
254
+ index = faiss.IndexFlatIP(384)
255
  def retrieve_context(query, k=RETRIEVAL_K):
256
  """Hämtar relevant kontext för frågor."""
257
  # Säkerställ att modeller är laddade
258
  initialize_embeddings()
259
 
260
+ try:
261
+ if embedder is None:
262
+ print("Varning: Embedder är fortfarande None efter initiering")
263
+ return "Kunde inte ladda kontext", ["error"]
264
+
265
+ query_embedding = embedder.encode([query], convert_to_numpy=True)
266
+
267
+ # Kontrollera att embedding inte är tom
268
+ if query_embedding.size == 0:
269
+ print("Varning: Tom query embedding")
270
+ return "Kunde inte bearbeta frågan", ["error"]
271
+
272
+ query_embedding /= np.linalg.norm(query_embedding)
273
+
274
+ # Kontrollera att index finns
275
+ if index is None:
276
+ print("Varning: FAISS-index är None")
277
+ return "Kunde inte söka i kontext", ["error"]
278
+
279
+ # Säkerställ att k inte är större än antalet element i index
280
+ actual_k = min(k, index.ntotal)
281
+ if actual_k < k:
282
+ print(f"Varning: Justerade k från {k} till {actual_k} baserat på index.ntotal")
283
+
284
+ D, I = index.search(query_embedding, actual_k)
285
+
286
+ retrieved, sources = [], set()
287
+ for idx in I[0]:
288
+ if 0 <= idx < len(chunks):
289
+ retrieved.append(chunks[idx])
290
+ sources.add(chunk_sources[idx])
291
+
292
+ if not retrieved:
293
+ print("Varning: Ingen relevant kontext hittades")
294
+ return "Ingen relevant kontext hittades", ["no_context"]
295
+
296
+ return " ".join(retrieved), list(sources)
297
+ except Exception as e:
298
+ print(f"Fel vid hämtning av kontext: {e}")
299
+ return f"Fel vid kontexthämtning: {str(e)[:200]}", ["error"]
300
 
301
  def generate_answer(query):
302
  """Genererar svar baserat på fråga och kontextinformation."""
 
377
  data.value innehåller information om meddelandet.
378
  """
379
  feedback_type = "up" if data.liked else "down"
380
+ global last_log, last_log_lock
381
+
382
+ # Skapa en kopia av data.value för att undvika potentiella race conditions
383
+ bot_reply = data.value if not isinstance(data.value, dict) else data.value.get("value", "")
384
+ if bot_reply is None:
385
+ bot_reply = ""
386
+
387
  log_entry = {
388
  "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
389
  "feedback": feedback_type,
390
+ "bot_reply": bot_reply
391
  }
392
+
393
+ # Använd lock för att säkert komma åt last_log
394
+ with last_log_lock:
395
+ # Om global logdata finns, lägg till ytterligare metadata.
396
+ if last_log:
397
+ log_entry.update({
398
+ "session_id": last_log.get("session_id", "unknown"),
399
+ "user_message": last_log.get("user_message", "Okänd fråga"),
400
+ })
401
 
402
  # Använd den förbättrade loggfunktionen
403
  safe_append_to_log(log_entry)
 
405
  # Skicka feedback till Slack
406
  try:
407
  if feedback_type == "down": # Skicka bara negativ feedback
408
+ # Hämta user_message säkert
409
+ user_message = "Okänd fråga"
410
+ with last_log_lock:
411
+ if last_log:
412
+ user_message = last_log.get("user_message", "Okänd fråga")
413
+
414
  feedback_message = f"""
415
  *⚠️ Negativ feedback registrerad*
416
 
417
+ *Fråga:* {user_message}
418
 
419
  *Svar:* {log_entry.get('bot_reply', 'Okänt svar')[:300]}{'...' if len(log_entry.get('bot_reply', '')) > 300 else ''}
420
  """
 
657
  # --- Schemaläggning av rapporter ---
658
  def run_scheduler():
659
  """Kör schemaläggaren i en separat tråd med förenklad statusrapportering."""
660
+ # Flagga för att kontrollera om tråden ska fortsätta köra
661
+ running = True
662
+
663
+ try:
664
+ # Använd den förenklade funktionen för rapportering
665
+ schedule.every().day.at("08:00").do(simple_status_report)
666
+ schedule.every().day.at("12:00").do(simple_status_report)
667
+ schedule.every().day.at("17:00").do(simple_status_report)
668
+
669
+ # Veckorapport på måndagar
670
+ schedule.every().monday.at("09:00").do(lambda: send_to_slack(
671
+ "Veckostatistik",
672
+ f"*ChargeNode AI Bot - Veckostatistik*\n\n{json.dumps(generate_monthly_stats(7), indent=2)}",
673
+ "#3498db"
674
+ ))
675
+
676
+ while running:
677
+ try:
678
+ schedule.run_pending()
679
+ time.sleep(60) # Kontrollera varje minut
680
+ except Exception as e:
681
+ print(f"Fel i schemaläggaren: {e}")
682
+ time.sleep(300) # Vänta 5 minuter vid fel
683
+ except Exception as e:
684
+ print(f"Kritiskt fel i schemaläggaren: {e}")
685
+ finally:
686
+ print("Schemaläggaren avslutad")
687
 
688
  # Starta schemaläggaren i en separat tråd
689
  scheduler_thread = threading.Thread(target=run_scheduler, daemon=True)
690
  scheduler_thread.start()
691
 
692
+ # Registrera en atexit-funktion för att städa upp vid avslut
693
+
694
+ def cleanup():
695
+ """Städa upp resurser vid avslut."""
696
+ print("Städar upp resurser...")
697
+ # Stäng scheduler om möjligt
698
+ schedule.clear()
699
+
700
+ # Stäng CommitScheduler om den är aktiv
701
+ if 'scheduler' in globals() and scheduler:
702
+ try:
703
+ scheduler.stop()
704
+ print("CommitScheduler stoppad")
705
+ except Exception as e:
706
+ print(f"Kunde inte stoppa CommitScheduler: {e}")
707
+
708
+ # Stäng eventuella öppna filer
709
+ try:
710
+ # Försök att stänga alla öppna filer
711
+ import gc
712
+ for obj in gc.get_objects():
713
+ if isinstance(obj, io.IOBase) and not obj.closed:
714
+ try:
715
+ obj.close()
716
+ except:
717
+ pass
718
+ except Exception as e:
719
+ print(f"Fel vid stängning av filer: {e}")
720
+
721
+ atexit.register(cleanup)
722
+
723
  # Kör en statusrapport vid uppstart för att verifiera att allt fungerar
724
  try:
725
  print("Skickar en inledande statusrapport för att verifiera Slack-integrationen...")
 
729
 
730
  # Definiera respond och chat-relaterade funktioner före Gradio UI
731
  def respond(message, chat_history, request: gr.Request):
732
+ global last_log, last_log_lock
733
+
734
+ # Validera indata
735
+ if not message or not isinstance(message, str):
736
+ print(f"Varning: Ogiltigt meddelande: {type(message)}")
737
+ message = str(message) if message is not None else ""
738
+
739
+ # Begränsa meddelandets längd för att förhindra överbelastning
740
+ if len(message) > 1000:
741
+ message = message[:1000] + "..."
742
+ print("Meddelande trunkerat på grund av längd")
743
+
744
  start = time.time()
745
+ try:
746
+ response = generate_answer(message)
747
+ except Exception as e:
748
+ print(f"Fel vid generering av svar: {e}")
749
+ response = f"Tyvärr uppstod ett tekniskt fel. Vänligen försök igen eller kontakta support@chargenode.eu. Felkod: {str(e)[:50]}"
750
+
751
  elapsed = round(time.time() - start, 2)
752
 
753
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
754
  session_id = str(uuid.uuid4())
755
 
756
  # Använd session_id från tidigare logg om det finns
757
+ with last_log_lock:
758
+ if last_log and 'session_id' in last_log:
759
+ session_id = last_log.get('session_id')
760
+
761
+ # Säker hantering av request-objektet
762
+ user_id = "okänd"
763
+ ua_str = ""
764
+ ref = ""
765
+ ip = ""
766
+
767
+ if request:
768
+ try:
769
+ user_id = request.client.host if hasattr(request.client, 'host') else "okänd"
770
+ ua_str = request.headers.get("user-agent", "") if hasattr(request, 'headers') else ""
771
+ ref = request.headers.get("referer", "") if hasattr(request, 'headers') else ""
772
+ ip = request.headers.get("x-forwarded-for", user_id).split(",")[0] if hasattr(request, 'headers') else user_id
773
+ except Exception as e:
774
+ print(f"Fel vid läsning av request-data: {e}")
775
+
776
+ # Säker parsing av user agent
777
+ try:
778
+ ua = parse_ua(ua_str)
779
+ browser = f"{ua.browser.family} {ua.browser.version_string}"
780
+ osys = f"{ua.os.family} {ua.os.version_string}"
781
+ except Exception as e:
782
+ print(f"Fel vid parsing av user agent: {e}")
783
+ browser = "okänd"
784
+ osys = "okänd"
785
 
786
+ # Bestäm plattform
787
  platform = "webb"
788
+ try:
789
+ if ref:
790
+ if "chargenode.eu" in ref:
791
+ platform = "chargenode.eu"
792
+ elif "localhost" in ref:
793
+ platform = "test"
794
+ elif "app" in ref:
795
+ platform = "app"
796
+ except Exception as e:
797
+ print(f"Fel vid bestämning av plattform: {e}")
798
 
799
+ # Skapa loggdata
800
  log_data = {
801
  "timestamp": timestamp,
802
  "user_id": user_id,
 
812
 
813
  # Använd den förbättrade loggfunktionen
814
  safe_append_to_log(log_data)
815
+
816
+ # Uppdatera last_log säkert
817
+ with last_log_lock:
818
+ last_log = log_data.copy() # Använd en kopia för att undvika race conditions
819
 
820
  # Skicka varje konversation direkt till Slack
821
  try:
 
837
  except Exception as e:
838
  print(f"Kunde inte skicka konversation till Slack: {e}")
839
 
840
+ # Uppdatera chatthistorik
841
+ try:
842
+ chat_history.append({"role": "user", "content": message})
843
+ chat_history.append({"role": "assistant", "content": response})
844
+ except Exception as e:
845
+ print(f"Fel vid uppdatering av chatthistorik: {e}")
846
+ # Försök återställa chatthistoriken om något går fel
847
+ if not chat_history:
848
+ chat_history = []
849
+ chat_history.append({"role": "user", "content": message})
850
+ chat_history.append({"role": "assistant", "content": response})
851
 
852
  return "", chat_history
853
 
 
1006
 
1007
  # Lägg till anpassad JavaScript för att styra scrollning
1008
  js_code = """
1009
+ function scrollToLatestBotMessage() {
1010
+ const latestBotMessage = document.querySelector('.gradio-container .message.bot:last-child');
1011
+ if (latestBotMessage) {
1012
+ // Scrolla så att det senaste botmeddelandet är synligt i toppen av chattrutan
1013
+ latestBotMessage.scrollIntoView({block: 'start', behavior: 'smooth'});
1014
  }
1015
  }
1016
 
1017
  // Kör funktionen när nya meddelanden läggs till
1018
  document.addEventListener('DOMNodeInserted', function(event) {
1019
  if (event.target.classList && event.target.classList.contains('bot')) {
1020
+ setTimeout(scrollToLatestBotMessage, 100);
1021
  }
1022
  });
1023
  """
 
1057
  )
1058
 
1059
  if __name__ == "__main__":
1060
+ app.launch(share=True)