DjPapzin commited on
Commit
a4a1998
·
1 Parent(s): 2c43989

Undo last commit

Browse files
Files changed (1) hide show
  1. frontend/app.py +368 -91
frontend/app.py CHANGED
@@ -51,117 +51,394 @@ def preprocess_text(text):
51
 
52
  return cleaned_text
53
 
54
- # --- Load CSV files for symptom detection ---
55
- try:
56
- training_data = pd.read_csv("frontend/Symptoms_Detection/training_data.csv")
57
- symptom2disease = pd.read_csv("frontend/Symptoms_Detection/Symptom2Disease.csv")
58
- final_dataset_llms = pd.read_csv("frontend/Symptoms_Detection/final_dataset_llms.csv")
59
- disease_precaution = pd.read_csv("frontend/Symptoms_Detection/disease_precaution.csv", encoding='latin1')
60
- disease_riskFactors = pd.read_csv("frontend/Symptoms_Detection/disease_riskFactors.csv", encoding='latin1')
61
- except FileNotFoundError:
62
- st.error("One or more CSV files for symptom detection not found. Please ensure they are in the correct directory.")
63
-
64
- # --- Functions for symptom detection ---
65
- def precaution(label):
66
- label = str(label)
67
- label = label.lower()
68
- disease_precaution["Disease"] = disease_precaution["Disease"].str.lower()
69
- filtered_precautions = disease_precaution[disease_precaution["Disease"] == label]
70
- precautions = filtered_precautions[["Precaution_1", "Precaution_2", "Precaution_3", "Precaution_4"]]
71
- return precautions.values.tolist()
72
-
73
- def occurance(label):
74
- label = str(label)
75
- label = label.lower()
76
- disease_riskFactors["DNAME"] = disease_riskFactors["DNAME"].str.lower()
77
- filtered_occurrence = disease_riskFactors[disease_riskFactors["DNAME"] == label]
78
- occurrences = filtered_occurrence["OCCUR"].tolist()
79
- return occurrences
80
-
81
- # --- Load the vectorizer and LLM model ---
82
- try:
83
- vectorizer = pd.read_pickle("frontend/Symptoms_Detection/vectorizer.pkl")
84
- model_llm = pd.read_pickle("frontend/Symptoms_Detection/logistic_regression_model.pkl")
85
- except FileNotFoundError:
86
- st.error("Vectorizer or LLM model not found. Please ensure they are in the correct directory.")
87
-
88
- # --- Function to predict symptoms using LLM ---
89
- def predict_symptoms(symptoms):
90
- preprocessed_symptoms = preprocess_text(symptoms)
91
- symptoms_vectorized = vectorizer.transform([preprocessed_symptoms])
92
- prediction = model_llm.predict(symptoms_vectorized)
93
- return prediction[0]
94
-
95
- # --- Rest of your app.py code ---
96
-
97
  st.title("Medi Scape Dashboard")
98
 
99
  # --- Session State Initialization ---
100
- # ... (rest of your session state initialization code)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
 
102
  # --- End of Session State Initialization ---
103
 
104
  # Load the disease classification model
105
- # ... (rest of your disease classification model loading code)
 
 
 
 
106
 
107
  # Sidebar Navigation
108
- # ... (rest of your sidebar navigation code)
 
109
 
110
  # Access secrets using st.secrets
111
- # ... (rest of your secrets access code)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
- # --- Prediction function (using session state) ---
114
- def predict_disease(symptoms):
115
- # ... (your existing predict_disease function)
116
 
117
- # --- New function to analyze X-ray with LLM ---
118
- def analyze_xray_with_llm(predicted_class):
119
- pass # Replace this with the actual logic for this function
120
 
121
- if page == "Home":
122
- # ... (your existing Home page code)
 
 
123
 
124
- elif page == "AI Chatbot Diagnosis":
125
- st.write("Enter your symptoms separated by commas:")
126
- symptoms_input = st.text_area("Symptoms:")
127
 
128
- if st.button("Diagnose (Regression Model)"):
129
- if symptoms_input:
130
- # Use your existing regression model for prediction
131
- regression_prediction = predict_disease(symptoms_input) # Assuming this function exists
132
- st.write("## Regression Model Prediction:")
133
- st.write(regression_prediction)
134
  else:
135
- st.write("Please enter your symptoms.")
136
-
137
- if st.button("Diagnose (LLM)"):
138
- if symptoms_input:
139
- # Use the LLM model for prediction
140
- llm_prediction = predict_symptoms(symptoms_input)
141
- st.write("## LLM Prediction:")
142
- st.write(llm_prediction)
143
-
144
- # Display precautions and occurrence
145
- st.write("## Precautions:")
146
- precautions = precaution(llm_prediction)
147
- for precaution_text in precautions:
148
- st.write(precaution_text)
149
-
150
- st.write("## Occurrence:")
151
- occurrences = occurance(llm_prediction)
152
- for occurrence_text in occurrences:
153
- st.write(occurrence_text)
 
 
 
 
 
 
 
154
  else:
155
- st.write("Please enter your symptoms.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
 
157
- elif page == "Drug Identification":
158
- # ... (your existing Drug Identification page code)
159
 
160
- elif page == "Disease Detection":
161
- # ... (your existing Disease Detection page code)
162
 
163
- elif page == "Outbreak Alert":
164
- # ... (your existing Outbreak Alert page code)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
 
166
  # Auto-scroll to the bottom of the chat container
167
- # ... (your existing auto-scroll code)
 
 
 
 
 
 
 
 
 
 
 
51
 
52
  return cleaned_text
53
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  st.title("Medi Scape Dashboard")
55
 
56
  # --- Session State Initialization ---
57
+ if 'disease_model' not in st.session_state:
58
+ try:
59
+ model_path = 'FINAL_MODEL.keras' # Updated path
60
+ print(f"Attempting to load disease model from: {model_path}")
61
+ print(f"Model file exists: {os.path.exists(model_path)}")
62
+ st.session_state.disease_model = tf.keras.models.load_model(model_path)
63
+ print("Disease model loaded successfully!")
64
+ except FileNotFoundError:
65
+ st.error("Disease classification model not found. Please ensure 'FINAL_MODEL.keras' is in the same directory as this app.")
66
+ st.session_state.disease_model = None
67
+
68
+ # Load the vectorizer
69
+ if 'vectorizer' not in st.session_state:
70
+ try:
71
+ vectorizer_path = "vectorizer.pkl"
72
+ print(f"Attempting to load vectorizer from: {vectorizer_path}")
73
+ print(f"Vectorizer file exists: {os.path.exists(vectorizer_path)}")
74
+ st.session_state.vectorizer = pd.read_pickle(vectorizer_path)
75
+ print("Vectorizer loaded successfully!")
76
+ except FileNotFoundError:
77
+ st.error("Vectorizer file not found. Please ensure 'vectorizer.pkl' is in the same directory as this app.")
78
+ st.session_state.vectorizer = None
79
+ except Exception as e:
80
+ st.error(f"An error occurred while loading the vectorizer: {e}")
81
+ st.session_state.vectorizer = None
82
+
83
+ if 'model_llm' not in st.session_state:
84
+ try:
85
+ llm_model_path = "frontend/logistic_regression_model.pkl" # Updated path
86
+ print(f"Attempting to load LLM model from: {llm_model_path}")
87
+ print(f"LLM Model file exists: {os.path.exists(llm_model_path)}")
88
+ st.session_state.model_llm = pd.read_pickle(llm_model_path)
89
+ print("LLM model loaded successfully!")
90
+ except FileNotFoundError:
91
+ st.error("LLM model file not found. Please ensure 'logistic_regression_model.pkl' is in the 'frontend' directory.")
92
+ st.session_state.model_llm = None
93
+ except Exception as e:
94
+ st.error(f"An error occurred while loading the LLM model: {e}")
95
+ st.session_state.model_llm = None
96
 
97
  # --- End of Session State Initialization ---
98
 
99
  # Load the disease classification model
100
+ try:
101
+ disease_model = tf.keras.models.load_model('FINAL_MODEL.keras') # Updated path
102
+ except FileNotFoundError:
103
+ st.error("Disease classification model not found. Please ensure 'FINAL_MODEL.keras' is in the same directory as this app.")
104
+ disease_model = None
105
 
106
  # Sidebar Navigation
107
+ st.sidebar.title("Navigation")
108
+ page = st.sidebar.radio("Go to", ["Home", "AI Chatbot Diagnosis", "Drug Identification", "Disease Detection", "Outbreak Alert"])
109
 
110
  # Access secrets using st.secrets
111
+ if "INFERENCE_API_URL" not in st.secrets or "INFERENCE_API_KEY" not in st.secrets:
112
+ st.error("Please make sure to set your secrets in the Streamlit secrets settings.")
113
+ else:
114
+ # Initialize the Inference Client
115
+ CLIENT = InferenceHTTPClient(
116
+ api_url=st.secrets["INFERENCE_API_URL"],
117
+ api_key=st.secrets["INFERENCE_API_KEY"]
118
+ )
119
+
120
+ # Function to preprocess the image
121
+ def preprocess_image(image_path):
122
+ # Load the image
123
+ image = cv2.imread(image_path)
124
+
125
+ # Convert to grayscale
126
+ gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
127
 
128
+ # Remove noise
129
+ blurred = cv2.GaussianBlur(gray, (5, 5), 0)
 
130
 
131
+ # Thresholding/Binarization
132
+ _, binary = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
 
133
 
134
+ # Dilation and Erosion
135
+ kernel = np.ones((1, 1), np.uint8)
136
+ dilated = cv2.dilate(binary, kernel, iterations=1)
137
+ eroded = cv2.erode(dilated, kernel, iterations=1)
138
 
139
+ # Edge detection
140
+ edges = cv2.Canny(eroded, 100, 200)
 
141
 
142
+ # Deskewing
143
+ coords = np.column_stack(np.where(edges > 0))
144
+ angle = cv2.minAreaRect(coords)[-1]
145
+ if angle < -45:
146
+ angle = -(90 + angle)
 
147
  else:
148
+ angle = -angle
149
+
150
+ (h, w) = edges.shape[:2]
151
+ center = (w // 2, h // 2)
152
+ M = cv2.getRotationMatrix2D(center, angle, 1.0)
153
+ deskewed = cv2.warpAffine(edges, M, (w, h), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)
154
+
155
+ # Find contours
156
+ contours, _ = cv2.findContours(deskewed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
157
+
158
+ # Draw contours on the original image
159
+ contour_image = image.copy()
160
+ cv2.drawContours(contour_image, contours, -1, (0, 255, 0), 2)
161
+
162
+ return contour_image
163
+
164
+ def get_x1(detection):
165
+ return detection.xyxy[0][0]
166
+
167
+ # --- Prediction function (using session state) ---
168
+ def predict_disease(symptoms):
169
+ if st.session_state.vectorizer is not None and st.session_state.model_llm is not None:
170
+ preprocessed_symptoms = preprocess_text(symptoms)
171
+ symptoms_vectorized = st.session_state.vectorizer.transform([preprocessed_symptoms])
172
+ prediction = st.session_state.model_llm.predict(symptoms_vectorized)
173
+ return prediction[0]
174
  else:
175
+ st.error("Unable to make prediction. Vectorizer or LLM model is not loaded.")
176
+ return None
177
+
178
+ # --- New function to analyze X-ray with LLM ---
179
+ def analyze_xray_with_llm(predicted_class):
180
+ prompt = f"""
181
+ Based on a chest X-ray analysis, the predicted condition is {predicted_class}.
182
+ Please provide a concise summary of this condition, including:
183
+ - A brief description of the condition.
184
+ - Common symptoms associated with it.
185
+ - Potential causes.
186
+ - General treatment approaches.
187
+ - Any other relevant information for a patient.
188
+ """
189
+ llm_response = get_ai71_response(prompt)
190
+ st.write("## LLM Analysis of X-ray Results:")
191
+ st.write(llm_response)
192
+
193
+ if page == "Home":
194
+ st.markdown("## Welcome to Medi Scape")
195
+ st.write("Medi Scape is an AI-powered healthcare application designed to streamline the process of understanding and managing medical information. It leverages advanced AI models to provide features such as prescription analysis, disease detection from chest X-rays, and symptom-based diagnosis assistance.")
196
+
197
+ st.markdown("## Features")
198
+ st.write("Medi Scape provides various AI-powered tools for remote healthcare, including:")
199
+ features = [
200
+ "**AI Chatbot Diagnosis:** Interact with an AI chatbot for preliminary diagnosis and medical information.",
201
+ "**Drug Identification:** Upload a prescription image to identify medications and access relevant details.",
202
+ "**Doctor's Handwriting Identification:** Our system can accurately recognize and process doctor's handwriting.",
203
+ "**Disease Detection:** Upload a chest X-ray image to detect potential diseases.",
204
+ "**Outbreak Alert:** Stay informed about potential disease outbreaks in your area."
205
+ ]
206
+ for feature in features:
207
+ st.markdown(f"- {feature}")
208
+
209
+ st.markdown("## How it Works")
210
+ steps = [
211
+ "**Upload:** You can upload a prescription image for drug identification or a chest X-ray image for disease detection.",
212
+ "**Process:** Our AI models will analyze the image and extract relevant information.",
213
+ "**Results:** You will receive identified drug names, uses, side effects, and more, or a potential disease diagnosis."
214
+ ]
215
+ for i, step in enumerate(steps, 1):
216
+ st.markdown(f"{i}. {step}")
217
+
218
+ st.markdown("## Key Features")
219
+ key_features = [
220
+ "**AI-Powered:** Leverages advanced AI models for accurate analysis and diagnosis.",
221
+ "**User-Friendly:** Simple and intuitive interface for easy navigation and interaction.",
222
+ "**Secure:** Your data is protected and handled with confidentiality."
223
+ ]
224
+ for feature in key_features:
225
+ st.markdown(f"- {feature}")
226
+
227
+ st.markdown("Please use the sidebar to navigate to different features.")
228
+
229
+ elif page == "AI Chatbot Diagnosis":
230
+ st.write("Enter your symptoms separated by commas:")
231
+ symptoms_input = st.text_area("Symptoms:")
232
+ if st.button("Diagnose"):
233
+ if symptoms_input:
234
+ # --- Pipeline 1 Implementation ---
235
+ # 1. Symptom Input (already done with st.text_area)
236
+ # 2. Regression Prediction
237
+ regression_prediction = predict_disease(symptoms_input)
238
+
239
+ if regression_prediction is not None:
240
+ # 3. LLM Prompt Enhancement
241
+ prompt = f"""The predicted condition based on a symptom analysis is {regression_prediction}.
242
+ Provide a detailed explanation of this condition, including possible causes, common symptoms,
243
+ and general treatment approaches. Also, suggest when a patient should consult a doctor."""
244
+
245
+ # 4. LLM Output
246
+ llm_response = get_ai71_response(prompt)
247
+
248
+ # 5. Combined Output
249
+ st.write("## Logistic Regression Prediction:")
250
+ st.write(regression_prediction)
251
+
252
+ st.write("## LLM Explanation:")
253
+ st.write(llm_response)
254
+ # --- End of Pipeline 1 Implementation ---
255
+
256
+ else:
257
+ st.write("Please enter your symptoms.")
258
+
259
+ elif page == "Drug Identification":
260
+ st.write("Upload a prescription image for drug identification.")
261
+ uploaded_file = st.file_uploader("Upload prescription", type=["png", "jpg", "jpeg"])
262
+
263
+ if uploaded_file is not None:
264
+ # Display the uploaded image
265
+ image = Image.open(uploaded_file)
266
+ st.image(image, caption="Uploaded Prescription", use_column_width=True)
267
+
268
+ if st.button("Process Prescription"):
269
+ # Save the image to a temporary file
270
+ temp_image_path = "temp_image.jpg"
271
+ image.save(temp_image_path)
272
+
273
+ # Preprocess the image
274
+ preprocessed_image = preprocess_image(temp_image_path)
275
+
276
+ # Perform inference
277
+ result_doch1 = CLIENT.infer(preprocessed_image, model_id="doctor-s-handwriting/1")
278
+
279
+ # Extract labels and detections
280
+ labels = [item["class"] for item in result_doch1["predictions"]]
281
+ detections = sv.Detections.from_inference(result_doch1)
282
+
283
+ # Sort detections and labels
284
+ sorted_indices = sorted(range(len(detections)), key=lambda i: get_x1(detections[i]))
285
+ sorted_detections = [detections[i] for i in sorted_indices]
286
+ sorted_labels = [labels[i] for i in sorted_indices]
287
+
288
+ # Convert list to string
289
+ resulting_string = ''.join(sorted_labels)
290
+
291
+ # Display results
292
+ st.subheader("Processed Prescription")
293
+ fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 6))
294
+
295
+ # Plot bounding boxes
296
+ image_with_boxes = preprocessed_image.copy()
297
+ for detection in sorted_detections:
298
+ x1, y1, x2, y2 = detection.xyxy[0]
299
+ cv2.rectangle(image_with_boxes, (int(x1), int(y1)), (int(x2), int(y2)), (255, 0, 0), 2)
300
+ ax1.imshow(cv2.cvtColor(image_with_boxes, cv2.COLOR_BGR2RGB))
301
+ ax1.set_title("Bounding Boxes")
302
+ ax1.axis('off')
303
+
304
+ # Plot labels
305
+ image_with_labels = preprocessed_image.copy()
306
+ for i, detection in enumerate(sorted_detections):
307
+ x1, y1, x2, y2 = detection.xyxy[0]
308
+ label = sorted_labels[i]
309
+ cv2.putText(image_with_labels, label, (int(x1), int(y1) - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)
310
+ ax2.imshow(cv2.cvtColor(image_with_labels, cv2.COLOR_BGR2RGB))
311
+ ax2.set_title("Labels")
312
+ ax2.axis('off')
313
+
314
+ st.pyplot(fig)
315
+
316
+ st.write("Extracted Text from Prescription:", resulting_string)
317
+
318
+ # Prepare prompt for LLM
319
+ prompt = f"""Analyze the following prescription text:
320
+ {resulting_string}
321
+
322
+ Please provide:
323
+ 1. Identified drug name(s)
324
+ 2. Full name of each identified drug
325
+ 3. Primary uses of each drug
326
+ 4. Common side effects
327
+ 5. Recommended dosage (if identifiable from the text)
328
+ 6. Any warnings or precautions
329
+ 7. Potential interactions with other medications (if multiple drugs are identified)
330
+ 8. Any additional relevant information for the patient
331
+
332
+ If any part of the prescription is unclear or seems incomplete, please mention that and provide information about possible interpretations or matches. Always emphasize the importance of consulting a healthcare professional for accurate interpretation and advice."""
333
+
334
+ # Get LLM response
335
+ llm_response = get_ai71_response(prompt)
336
+
337
+ st.subheader("AI Analysis of the Prescription")
338
+ st.write(llm_response)
339
+
340
+ # Remove the temporary image file
341
+ os.remove(temp_image_path)
342
+
343
+ else:
344
+ st.info("Please upload a prescription image to proceed.")
345
+
346
+ elif page == "Disease Detection":
347
+ st.write("Upload a chest X-ray image for disease detection.")
348
+ uploaded_image = st.file_uploader("Choose an image...", type=["jpg", "jpeg", "png"])
349
+
350
+ if uploaded_image is not None and st.session_state.disease_model is not None:
351
+ # Display the image
352
+ img_opened = Image.open(uploaded_image).convert('RGB')
353
+ image_pred = np.array(img_opened)
354
+ image_pred = cv2.resize(image_pred, (150, 150))
355
+
356
+ # Convert the image to a numpy array
357
+ image_pred = np.array(image_pred)
358
+
359
+ # Rescale the image (if the model was trained with rescaling)
360
+ image_pred = image_pred / 255.0
361
+
362
+ # Add an extra dimension to match the input shape (1, 150, 150, 3)
363
+ image_pred = np.expand_dims(image_pred, axis=0)
364
+
365
+ # Predict using the model
366
+ prediction = st.session_state.disease_model.predict(image_pred)
367
+
368
+ # Get the predicted class
369
+ predicted_ = np.argmax(prediction)
370
+
371
+ # Decode the prediction
372
+ if predicted_ == 0:
373
+ predicted_class = "Covid"
374
+ elif predicted_ == 1:
375
+ predicted_class = "Normal Chest X-ray"
376
+ else:
377
+ predicted_class = "Pneumonia"
378
+
379
+ st.image(image_pred, caption='Input image by user', use_column_width=True)
380
+ st.write("Prediction Classes for different types:")
381
+ st.write("COVID: 0")
382
+ st.write("Normal Chest X-ray: 1")
383
+ st.write("Pneumonia: 2")
384
+ st.write("\n")
385
+ st.write("DETECTED DISEASE DISPLAY")
386
+ st.write(f"Predicted Class : {predicted_}")
387
+ st.write(predicted_class)
388
+
389
+ # Analyze X-ray results with LLM
390
+ analyze_xray_with_llm(predicted_class)
391
+ else:
392
+ st.write("Please upload an image file or ensure the disease model is loaded.")
393
+
394
+ elif page == "Outbreak Alert":
395
+ st.markdown("## **Disease Outbreak News (from WHO)**")
396
+
397
+ # Fetch WHO news page
398
+ url = "https://www.who.int/news-room/events"
399
+ response = requests.get(url)
400
+ response.raise_for_status() # Raise an exception for bad status codes
401
 
402
+ soup = BeautifulSoup(response.content, 'html.parser')
 
403
 
404
+ # Find news articles (adjust selectors if WHO website changes)
405
+ articles = soup.find_all('div', class_='list-view--item')
406
 
407
+ for article in articles[:5]: # Display the top 5 news articles
408
+ title_element = article.find('a', class_='link-container')
409
+ if title_element:
410
+ title = title_element.text.strip()
411
+ link = title_element['href']
412
+ date_element = article.find('span', class_='date')
413
+ date = date_element.text.strip() if date_element else "Date not found"
414
+
415
+ # Format date
416
+ date_parts = date.split()
417
+ if len(date_parts) >= 3:
418
+ try:
419
+ formatted_date = datetime.strptime(date, "%d %B %Y").strftime("%Y-%m-%d")
420
+ except ValueError:
421
+ formatted_date = date # Keep the original date if formatting fails
422
+ else:
423
+ formatted_date = date
424
+
425
+ # Display news item in a card-like container
426
+ with st.container():
427
+ st.markdown(f"**{formatted_date}**")
428
+ st.markdown(f"[{title}]({link})")
429
+ st.markdown("---")
430
+ else:
431
+ st.write("Could not find article details.")
432
 
433
  # Auto-scroll to the bottom of the chat container
434
+ st.markdown(
435
+ """
436
+ <script>
437
+ const chatContainer = document.querySelector('.st-chat-container');
438
+ if (chatContainer) {
439
+ chatContainer.scrollTop = chatContainer.scrollHeight;
440
+ }
441
+ </script>
442
+ """,
443
+ unsafe_allow_html=True,
444
+ )