felixflier03 commited on
Commit
f6476d5
·
verified ·
1 Parent(s): 3812bdb

Update ui_utils.py

Browse files
Files changed (1) hide show
  1. ui_utils.py +178 -446
ui_utils.py CHANGED
@@ -1,466 +1,198 @@
1
- from datetime import datetime
2
  import streamlit as st
3
- import os
4
- import pickle
5
- from typing import Dict, Any
6
- from chatbot_new import IntelligentRAGChatbot, InitializationError, download_vector_store_file
7
- from pathlib import Path
8
 
 
9
 
10
- # Am Anfang der Datei die Konstanten hinzufügen:
11
- BASE_PATH = os.path.dirname(os.path.abspath(__file__))
12
- VECTOR_STORE_PATH = os.path.join(BASE_PATH, "vector_stores")
 
 
 
13
 
14
- def apply_enhanced_custom_css():
15
- st.markdown(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  """
17
- <style>
18
- @import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
19
- /* Color Variables */
20
- :root {
21
- --primary: #06b6d4;
22
- --primary-light: #22d3ee;
23
- --primary-dark: #0891b2;
24
- --background-dark: #0a1930;
25
- --background-light: #1a1f3c;
26
- --surface: rgba(30, 27, 75, 0.5);
27
- --text-primary: #f8fafc;
28
- --text-secondary: #cbd5e1;
29
- }
30
- /* Base Styles & Navigation Bar Fix */
31
- .stApp {
32
- background: linear-gradient(145deg, var(--background-dark) 0%, #164e63 100%) !important;
33
- color: var(--text-primary);
34
- font-family: 'Plus Jakarta Sans', sans-serif;
35
- line-height: 1.7;
36
- }
37
- /* Remove default header */
38
- header[data-testid="stHeader"] {
39
- display: none;
40
- }
41
- /* Clean up sidebar */
42
- section[data-testid="stSidebar"] {
43
- background: linear-gradient(180deg, var(--background-dark) 0%, #164e63 100%) !important;
44
- border-right: 1px solid rgba(6, 182, 212, 0.1);
45
- }
46
- .stSidebar > div:first-child {
47
- padding-top: 2rem;
48
- }
49
- /* Typography */
50
- h1 {
51
- color: var(--text-primary);
52
- text-align: center;
53
- font-size: 2.5rem;
54
- font-weight: 700;
55
- margin-bottom: 2.5rem;
56
- letter-spacing: -0.02em;
57
- background: linear-gradient(135deg, #22d3ee 0%, #06b6d4 50%, #0891b2 100%);
58
- -webkit-background-clip: text;
59
- -webkit-text-fill-color: transparent;
60
- }
61
- /* Chat Messages */
62
- .stChatMessage {
63
- background: rgba(30, 27, 75, 0.3) !important;
64
- backdrop-filter: blur(12px);
65
- border: 1px solid rgba(6, 182, 212, 0.1);
66
- border-radius: 16px;
67
- padding: 1.5rem;
68
- margin: 1.5rem 0;
69
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
70
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
71
- }
72
- /* User and Assistant Messages */
73
- .stChatMessage[data-testid="user-message"],
74
- .stChatMessage[data-testid="assistant-message"] {
75
- color: #ffffff !important; /* Weiß */
76
- }
77
- /* Message Layout */
78
- .stChatMessage[data-testid="user-message"] {
79
- background: rgba(6, 182, 212, 0.1) !important;
80
- margin-left: 12%;
81
- margin-right: 2%;
82
- border-left: 3px solid var(--primary);
83
- }
84
- .stChatMessage[data-testid="assistant-message"] {
85
- background: rgba(10, 25, 48, 0.7) !important;
86
- margin-right: 12%;
87
- margin-left: 2%;
88
- border-right: 3px solid var(--primary);
89
- }
90
- /* Enhanced Input Area */
91
- .stChatInput {
92
- background-color: transparent !important;
93
- }
94
- .stChatInput > div {
95
- background-color: transparent !important;
96
- }
97
- div[data-testid="stChatInput"] {
98
- background-color: transparent !important;
99
- }
100
- .stTextInput > div {
101
- background-color: transparent !important;
102
- }
103
- div[data-testid="textInput"] {
104
- background-color: transparent !important;
105
- }
106
- .stTextInput > div > div {
107
- background-color: transparent !important;
108
- }
109
- .stTextInput > div > div > input {
110
- background: rgba(10, 25, 48, 0.7) !important;
111
- color: var(--text-primary);
112
- border: 1px solid rgba(6, 182, 212, 0.3);
113
- border-radius: 12px;
114
- padding: 1rem 1.5rem;
115
- font-size: 1rem;
116
- font-weight: 500;
117
- transition: all 0.2s ease;
118
- margin-bottom: 1rem;
119
- width: 100%;
120
- }
121
- .stTextInput > div > div > input:focus {
122
- border-color: var(--primary);
123
- box-shadow: 0 0 0 3px rgba(6, 182, 212, 0.15);
124
- }
125
- /* Chat Input Container Fix */
126
- .stChatInputContainer {
127
- background-color: transparent !important;
128
- border: none !important;
129
- padding: 0 !important;
130
- }
131
- section[data-testid="stChatInput"] {
132
- background-color: transparent !important;
133
- border: none !important;
134
- padding: 0 !important;
135
- }
136
- /* Source Details */
137
- .streamlit-expanderHeader {
138
- background: rgba(6, 182, 212, 0.08);
139
- border-radius: 10px;
140
- padding: 1rem;
141
- border: 1px solid rgba(6, 182, 212, 0.15);
142
- transition: all 0.2s ease;
143
- }
144
- /* Copy Button */
145
- button[data-testid="baseButton-secondary"] {
146
- background: rgba(6, 182, 212, 0.1);
147
- color: var(--primary-light);
148
- border: 1px solid rgba(6, 182, 212, 0.2);
149
- border-radius: 8px;
150
- padding: 0.5rem 1rem;
151
- font-weight: 500;
152
- transition: all 0.2s ease;
153
- }
154
- button[data-testid="baseButton-secondary"]:hover {
155
- background: rgba(6, 182, 212, 0.2);
156
- border-color: var(--primary);
157
- }
158
- /* Footer Area */
159
- footer {
160
- visibility: hidden;
161
- }
162
- /* Sidebar Text Size Control */
163
- .stSlider {
164
- padding: 1rem;
165
- }
166
- .stSlider > div > div > div {
167
- background-color: var(--primary) !important;
168
- }
169
- /* Remove Bottom Space */
170
- .main .block-container {
171
- padding-bottom: 2rem;
172
- max-width: 1000px;
173
- }
174
- /* Custom Scrollbar */
175
- ::-webkit-scrollbar {
176
- width: 8px;
177
- height: 8px;
178
- }
179
- ::-webkit-scrollbar-track {
180
- background: rgba(10, 25, 48, 0.1);
181
- }
182
- ::-webkit-scrollbar-thumb {
183
- background: var(--primary);
184
- border-radius: 4px;
185
- }
186
- ::-webkit-scrollbar-thumb:hover {
187
- background: var(--primary-light);
188
- }
189
- /* Light Mode Adjustments */
190
- @media (prefers-color-scheme: light) {
191
- :root {
192
- --background-dark: #ffffff;
193
- --background-light: #f0f0f0;
194
- --text-primary: #000000;
195
- --text-secondary: #555555;
196
- }
197
- .stApp {
198
- background: linear-gradient(145deg, var(--background-light) 0%, #ffffff 100%) !important;
199
- color: var(--text-primary);
200
- }
201
- section[data-testid="stSidebar"] {
202
- background: linear-gradient(180deg, var(--background-light) 0%, #ffffff 100%) !important;
203
- border-right: 1px solid rgba(0, 0, 0, 0.1);
204
- }
205
- .stChatMessage {
206
- background: rgba(255, 255, 255, 0.9) !important;
207
- border: 1px solid rgba(0, 0, 0, 0.1);
208
- }
209
- .stChatMessage[data-testid="user-message"],
210
- .stChatMessage[data-testid="assistant-message"] {
211
- color: #000000 !important; /* Schwarz */
212
- }
213
- .stChatMessage[data-testid="user-message"] {
214
- background: rgba(0, 0, 0, 0.05) !important;
215
- border-left: 3px solid var(--primary);
216
- }
217
- .stChatMessage[data-testid="assistant-message"] {
218
- background: rgba(0, 0, 0, 0.1) !important;
219
- border-right: 3px solid var(--primary);
220
- }
221
- .stTextInput > div > div > input {
222
- background: rgba(255, 255, 255, 0.9) !important;
223
- color: var(--text-primary);
224
- border: 1px solid rgba(0, 0, 0, 0.1);
225
- }
226
- }
227
- </style>
228
- """,
229
- unsafe_allow_html=True
230
- )
231
 
 
 
 
 
 
 
 
 
 
 
 
232
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
233
 
234
- def add_copy_button(text: str, key: str):
235
- if st.button("📋 Kopieren", key=f"copy_{key}"):
236
- st.session_state[f"copied_{key}"] = True
237
- st.success("Text kopiert!")
238
 
239
- def add_scroll_button():
240
- st.markdown("""
241
- <div class="scroll-button" onclick="window.scrollTo(0,document.body.scrollHeight);">
242
-
243
- </div>
244
- """, unsafe_allow_html=True)
245
 
246
- def add_font_size_control():
247
- st.sidebar.markdown("### Textgröße")
248
- font_size = st.sidebar.slider("Wählen Sie die Textgröße", 12, 24, 16)
249
- st.markdown(f"""
250
- <style>
251
- .stChatMessage p {{
252
- font-size: {font_size}px !important;
253
- }}
254
- </style>
255
- """, unsafe_allow_html=True)
256
 
257
- def check_pickle_files():
258
- files = ['vectorstore.pkl', 'bm25.pkl', 'documents.pkl']
259
- for file in files:
260
- full_path = os.path.join(VECTOR_STORE_PATH, file)
261
- try:
262
- with open(full_path, 'rb') as f:
263
- data = pickle.load(f)
264
- print(f"\nDebug {file}:")
265
- print(f"Typ: {type(data)}")
266
- if hasattr(data, '__len__'):
267
- print(f"Länge: {len(data)}")
268
- except Exception as e:
269
- print(f"Fehler beim Laden von {file}: {str(e)}")
 
 
270
 
271
- def display_processing_status(status: str, progress: float = 0.0):
272
- """Zeigt Verarbeitungsstatus mit Fortschrittsbalken"""
273
- progress_container = st.empty()
274
- with progress_container:
275
- progress_bar = st.progress(progress)
276
- status_text = st.empty()
277
- status_text.write(status)
278
- return progress_container
279
 
280
- def add_modern_feedback(message_key: str):
281
- """Fügt ein modernes, hover-basiertes Feedback-System hinzu"""
282
- st.markdown(f"""
283
- <style>
284
- .feedback-container-{message_key} {{
285
- position: absolute;
286
- right: -60px;
287
- top: 50%;
288
- transform: translateY(-50%);
289
- opacity: 0;
290
- transition: opacity 0.3s ease;
291
- background: rgba(255, 255, 255, 0.1);
292
- padding: 8px;
293
- border-radius: 8px;
294
- display: flex;
295
- flex-direction: column;
296
- gap: 8px;
297
- }}
298
-
299
- .message-container-{message_key}:hover .feedback-container-{message_key} {{
300
- opacity: 1;
301
- }}
302
 
303
- .feedback-button {{
304
- background: transparent;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
305
  border: none;
 
 
 
 
 
 
 
306
  cursor: pointer;
307
- padding: 4px;
308
- transition: transform 0.2s ease;
309
- color: #cbd5e1;
310
- }}
311
-
312
- .feedback-button:hover {{
313
- transform: scale(1.2);
314
- color: #06b6d4;
315
- }}
316
-
317
- .message-container-{message_key} {{
318
- position: relative;
319
- padding-right: 60px;
320
- }}
321
  </style>
322
-
323
- <div class="message-container-{message_key}">
324
- <div class="feedback-container-{message_key}">
325
- <button class="feedback-button" onclick="handleFeedback('positive', '{message_key}')">
326
- 👍
327
- </button>
328
- <button class="feedback-button" onclick="handleFeedback('negative', '{message_key}')">
329
- 👎
330
- </button>
331
- </div>
332
- </div>
333
-
334
  <script>
335
- function handleFeedback(type, key) {{
336
- // Hier könnte man AJAX-Calls für die Feedback-Verarbeitung implementieren
337
- const button = event.target;
338
- button.style.color = '#06b6d4';
339
-
340
- // Feedback in session state speichern
341
- window.parent.postMessage({{
342
- type: 'feedback',
343
- messageKey: key,
344
- feedbackType: type
345
- }}, '*');
346
- }}
347
- </script>
348
- """, unsafe_allow_html=True)
349
 
350
- def display_metadata(metadata: Dict[str, Any]):
351
- """Erweiterte Metadaten-Anzeige"""
352
- with st.expander("🔍 Qualitätsmetriken", expanded=True):
353
- # Zeige Failed Metrics
354
- if metadata.get('failed_quality_check'):
355
- st.warning("Die Antwort hat die Qualitätsanforderungen nicht erfüllt:")
356
- failed_metrics = metadata.get('failed_metrics', [])
357
- for metric in failed_metrics:
358
- st.error(f"- {metric}")
359
-
360
- # Zeige Qualitätsmetriken
361
- if metadata.get('quality_metrics') and metadata['quality_metrics'].get('metrics'):
362
- metrics = metadata['quality_metrics']['metrics']
363
- cols = st.columns(4)
364
-
365
- def get_color(value: float) -> str:
366
- if value >= 0.8: return "green"
367
- if value >= 0.6: return "orange"
368
- return "red"
369
-
370
- metric_names = {
371
- 'relevance_score': 'Relevanz',
372
- 'completeness_score': 'Vollständigkeit',
373
- 'coherence_score': 'Kohärenz',
374
- 'accuracy_score': 'Genauigkeit'
375
- }
376
-
377
- for col, (metric, display_name) in zip(cols, metric_names.items()):
378
- with col:
379
- value = metrics.get(metric, 0)
380
- st.markdown(f"""
381
- <div style='color: {get_color(value)}'>
382
- {display_name}: {value:.2f}
383
- </div>
384
- """, unsafe_allow_html=True)
385
-
386
- # Zeige Kontext-Abdeckung
387
- if metadata.get('context_coverage') is not None:
388
- st.write(f"Kontext-Abdeckung: {metadata['context_coverage']:.2%}")
389
-
390
- def handle_conversation_flow(st):
391
- """Vereinfachte Konversationssteuerung mit natürlicher Verneinungserkennung"""
392
-
393
- rejection_patterns = [
394
- "nein danke", "keine weiteren fragen", "das war's danke", "das wars danke",
395
- "nein das wäre alles", "das ist alles danke", "danke das wärs",
396
- "nein für heute reicht es", "das reicht danke", "danke das genügt",
397
- "nein das ist alles", "das war alles", "keine fragen mehr", "nein", "nein"
398
- ]
399
-
400
- def is_rejection(text: str) -> bool:
401
- """Überprüft, ob eine Antwort eine höfliche Verneinung enthält"""
402
- text = text.lower().strip()
403
- return any(pattern in text for pattern in rejection_patterns)
404
-
405
- try:
406
- if "messages" in st.session_state and st.session_state.messages:
407
- last_message = st.session_state.messages[-1]
408
- if last_message["role"] == "user":
409
- if is_rejection(last_message["content"]):
410
- farewell_message = (
411
- "Vielen Dank für das Gespräch! Ich hoffe, ich konnte Ihnen weiterhelfen. "
412
- "Falls Sie später weitere Fragen haben, können Sie sich jederzeit wieder an mich wenden."
413
- )
414
- st.session_state.messages.append({
415
- "role": "assistant",
416
- "content": farewell_message
417
- })
418
- st.session_state.conversation_ended = True
419
- return True
420
- else:
421
- # Wichtig: conversation_ended zurücksetzen, wenn KEINE Verneinung
422
- st.session_state.conversation_ended = False
423
 
424
- return False # Diese Zeile ist entscheidend für den weiteren Ablauf
425
-
426
- except Exception as e:
427
- print(f"Fehler in handle_conversation_flow: {str(e)}")
428
- st.session_state.conversation_ended = False # Auch hier im Fehlerfall zurücksetzen
429
- return False
430
-
431
- def initialize_session_state():
432
- print("Initializing session state...")
433
- if "messages" not in st.session_state:
434
- print("Setting up initial message...")
435
- st.session_state.messages = [{
436
- "role": "assistant",
437
- "content": "Hallo! Ich bin Ihr KI-Assistent für Wirtschaftsprüfungsthemen. Wie kann ich Ihnen helfen?"
438
- }]
439
- if "chatbot" not in st.session_state:
440
- print("Setting up chatbot...")
441
- try:
442
- download_vector_store_file()
443
- print("Vector store downloaded...")
444
-
445
- st.session_state.chatbot = IntelligentRAGChatbot(
446
- vectorstore_file=Path(VECTOR_STORE_PATH) / "vectorstore.pkl",
447
- bm25_file=Path(VECTOR_STORE_PATH) / "bm25.pkl"
448
- )
449
- print("Chatbot initialized successfully")
450
- except Exception as e:
451
- print(f"Error during chatbot initialization: {str(e)}")
452
- st.error(f"Fehler bei der Initialisierung des Chatbots: {str(e)}")
453
- st.stop()
454
-
455
- # Neue States für Feedback und Verarbeitung
456
- if "feedback_history" not in st.session_state:
457
- st.session_state.feedback_history = {}
458
- if "processing_status" not in st.session_state:
459
- st.session_state.processing_status = None
460
- if "quality_thresholds" not in st.session_state:
461
- st.session_state.quality_thresholds = {
462
- 'relevance_score': 0.5,
463
- 'completeness_score': 0.001,
464
- 'coherence_score': 0.5,
465
- 'accuracy_score': 0.5
466
- }
 
 
1
  import streamlit as st
2
+ from typing import Dict, Any, List, Union
3
+ import logging
 
 
 
4
 
5
+ logger = logging.getLogger(__name__)
6
 
7
+ def handle_feedback(feedback_type: str, message_key: str):
8
+ """Verarbeitet Feedback-Interaktionen"""
9
+ st.session_state.feedback = {
10
+ 'feedbackType': feedback_type,
11
+ 'messageKey': message_key
12
+ }
13
 
14
+ def display_chat_message(message: Dict[str, Any], idx: int):
15
+ """Zeigt eine Chat-Nachricht mit korrekt angeordneten Buttons und Metadaten an"""
16
+ with st.chat_message(message["role"]):
17
+ # Hauptinhalt
18
+ st.markdown(message["content"])
19
+
20
+ # Feedback/Copy Buttons für Assistant-Nachrichten
21
+ if message["role"] == "assistant":
22
+ # Buttons nebeneinander in korrekter Reihenfolge
23
+ button_cols = st.columns([0.94, 0.02, 0.02, 0.02]) # Angepasste Spaltenbreiten
24
+
25
+ with button_cols[1]: # Kopier-Button links
26
+ if st.button("📋", key=f'copy_{idx}', help="In Zwischenablage kopieren"):
27
+ st.session_state.to_copy = message["content"]
28
+ with button_cols[2]: # Negatives Feedback rechts
29
+ if st.button("👎", key=f'thumb_down_{idx}', help="Negatives Feedback"):
30
+ handle_feedback('negative', idx)
31
+ with button_cols[3]: # Positives Feedback in der Mitte
32
+ if st.button("👍", key=f'thumb_up_{idx}', help="Positives Feedback"):
33
+ handle_feedback('positive', idx)
34
+
35
+ # Zeige Metadaten direkt nach der Nachricht an
36
+ if "metadata" in message:
37
+ display_metadata(message["metadata"])
38
+
39
+ def display_typical_questions(questions: List[str]):
40
+ """Zeigt typische Fragen als einheitlich gestylte Buttons an"""
41
+ cols = st.columns(len(questions))
42
+ for idx, question in enumerate(questions):
43
+ with cols[idx]:
44
+ # Button mit angepasstem Styling
45
+ if st.button(
46
+ question,
47
+ key=f'question_{idx}',
48
+ help="Klicken Sie um diese Frage zu stellen",
49
+ use_container_width=True,
50
+ # Styling
51
+ type="secondary" # Dies stellt das gewünschte Hover-Verhalten wieder her
52
+ ):
53
+ st.session_state.user_prompt = question
54
+
55
+ def set_chat_input_value(text: str):
56
  """
57
+ Versucht den Wert der Chat-Eingabe zu setzen (veraltet, nicht mehr verwendet)
58
+ """
59
+ logger.warning("set_chat_input_value wird aufgerufen, ist aber veraltet und sollte nicht mehr verwendet werden.")
60
+ if "chat_input" in st.session_state:
61
+ st.session_state.chat_input = text
62
+ else:
63
+ logger.warning("chat_input nicht im Session State gefunden.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
 
65
+ def display_metadata(metadata: Union[Dict[str, Any], str]):
66
+ """Zeigt Metadaten sicher an"""
67
+ try:
68
+ # Überprüfe, ob metadata ein String ist
69
+ if isinstance(metadata, str):
70
+ st.warning(f"Unerwartetes Metadaten-Format: {metadata}")
71
+ return
72
+
73
+ if not isinstance(metadata, dict):
74
+ st.warning(f"Ungültiges Metadaten-Format: {type(metadata)}")
75
+ return
76
 
77
+ with st.expander("🔍 Details", expanded=False):
78
+ # Intent und Konfidenz
79
+ col1, col2 = st.columns(2)
80
+ with col1:
81
+ intent_info = metadata.get('intent', {})
82
+ if isinstance(intent_info, dict):
83
+ st.markdown(f"**Intent:** {intent_info.get('intent', 'unknown')}")
84
+ if metadata.get('subintents'):
85
+ st.markdown("**Sub-Intents:**")
86
+ for subintent in metadata['subintents']:
87
+ st.markdown(f"- {subintent}")
88
+ with col2:
89
+ if isinstance(intent_info, dict):
90
+ confidence = intent_info.get('confidence', 0.0)
91
+ st.markdown(f"**Konfidenz:** {confidence:.2f}")
92
 
93
+ # Multi-Intent Info
94
+ if metadata.get('multi_intent'):
95
+ st.info("Multiple Intents erkannt")
 
96
 
97
+ # Follow-up Info
98
+ if metadata.get('is_follow_up'):
99
+ st.info("Follow-up Frage erkannt")
 
 
 
100
 
101
+ # Erkannte Themen
102
+ if metadata.get('key_topics'):
103
+ st.markdown("**Erkannte Themen:**")
104
+ st.write(", ".join(metadata['key_topics']))
 
 
 
 
 
 
105
 
106
+ # Qualitätsmetriken
107
+ if 'quality_metrics' in metadata:
108
+ metrics = metadata['quality_metrics'].get('metrics', {})
109
+ if metrics:
110
+ st.markdown("### Qualitätsmetriken")
111
+ metric_data = {
112
+ 'Metrik': ['Relevanz', 'Vollständigkeit', 'Kohärenz', 'Genauigkeit'],
113
+ 'Wert': [
114
+ f"{metrics.get('relevance_score', 0.0):.2f}",
115
+ f"{metrics.get('completeness_score', 0.0):.2f}",
116
+ f"{metrics.get('coherence_score', 0.0):.2f}",
117
+ f"{metrics.get('accuracy_score', 0.0):.2f}"
118
+ ]
119
+ }
120
+ st.table(metric_data)
121
 
122
+ # Error Info
123
+ if 'error' in metadata:
124
+ st.error(metadata['error'])
 
 
 
 
 
125
 
126
+ except Exception as e:
127
+ logger.error(f"Fehler bei Metadaten-Anzeige: {str(e)}")
128
+ st.warning("Metadaten konnten nicht angezeigt werden")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
 
130
+ def _get_metric_color(value: float) -> str:
131
+ """Bestimmt die Farbe basierend auf dem Metrik-Wert"""
132
+ if value >= 0.8: return "#00C853" # Grün
133
+ if value >= 0.6: return "#FFB300" # Orange
134
+ return "#FF3D00" # Rot
135
+
136
+ def add_scroll_button():
137
+ """Fügt einen Scroll-nach-unten Button hinzu"""
138
+ # JavaScript und CSS direkt einfügen
139
+ st.components.v1.html(
140
+ """
141
+ <style>
142
+ .scroll-button {
143
+ position: fixed;
144
+ bottom: 20px;
145
+ right: 20px;
146
+ background-color: rgba(6, 182, 212, 0.8);
147
+ color: white;
148
  border: none;
149
+ border-radius: 50%;
150
+ width: 40px;
151
+ height: 40px;
152
+ font-size: 24px;
153
+ display: flex;
154
+ align-items: center;
155
+ justify-content: center;
156
  cursor: pointer;
157
+ transition: opacity 0.3s;
158
+ box-shadow: 0 2px 5px rgba(0,0,0,0.2);
159
+ z-index: 1000; /* Stelle sicher, dass der Button über anderen Elementen liegt */
160
+ }
161
+ .scroll-button:hover {
162
+ opacity: 1;
163
+ background-color: rgba(6, 182, 212, 1);
164
+ }
 
 
 
 
 
 
165
  </style>
 
 
 
 
 
 
 
 
 
 
 
 
166
  <script>
167
+ // Erstelle den Button, wenn das Dokument bereit ist
168
+ document.addEventListener('DOMContentLoaded', function() {
169
+ const scrollButton = document.createElement('div');
170
+ scrollButton.classList.add('scroll-button');
171
+ scrollButton.innerHTML = '<i class="fas fa-arrow-down"></i>';
172
+ document.body.appendChild(scrollButton);
 
 
 
 
 
 
 
 
173
 
174
+ scrollButton.addEventListener('click', () => {
175
+ window.scrollTo(0, document.body.scrollHeight);
176
+ });
177
+ });
178
+ </script>
179
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
180
+ """,
181
+ height=0, # Höhe auf 0 setzen, da wir nur das Skript und CSS einfügen wollen
182
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
 
184
+ def add_font_size_control():
185
+ """Fügt eine Schriftgrößen-Kontrolle hinzu"""
186
+ st.sidebar.markdown("### Textgröße")
187
+ font_size = st.sidebar.slider("Wählen Sie die Textgröße", 12, 24, 16)
188
+ st.markdown(
189
+ f"""
190
+ <style>
191
+ .stChatMessage p {{
192
+ font-size: {font_size}px !important;
193
+ line-height: 1.5;
194
+ }}
195
+ </style>
196
+ """,
197
+ unsafe_allow_html=True
198
+ )