DocUA commited on
Commit
d963484
·
1 Parent(s): 0871c81

Додано новий запис до .gitignore для ігнорування логів MAI-DX, імпортовано модуль re для парсингу, оновлено обробку помилок при завантаженні логгера, вдосконалено логування активності агентів та оновлено очікувані результати в тестових випадках.

Browse files
Files changed (2) hide show
  1. .gitignore +2 -0
  2. lmai_dx_interface_log.py +172 -551
.gitignore CHANGED
@@ -166,3 +166,5 @@ cython_debug/
166
  *.env
167
  __pycache__/
168
  *.pyc
 
 
 
166
  *.env
167
  __pycache__/
168
  *.pyc
169
+
170
+ mai_dx_logs/
lmai_dx_interface_log.py CHANGED
@@ -15,6 +15,7 @@ import warnings
15
  import threading
16
  import queue
17
  import io
 
18
  from contextlib import redirect_stdout, redirect_stderr
19
 
20
  # Налаштування середовища
@@ -64,11 +65,13 @@ except ImportError:
64
  try:
65
  from agent_conversation_logger import AgentConversationLogger, DiagnosisSession
66
  LOGGER_AVAILABLE = True
67
- except ImportError:
68
  LOGGER_AVAILABLE = False
 
69
 
70
  @dataclass
71
  class DiagnosisSession:
 
72
  timestamp: str
73
  case_name: str
74
  patient_info: str
@@ -89,12 +92,10 @@ except ImportError:
89
  return f"mock_{len(self.conversations)}"
90
  def log_agent_message(self, *args, **kwargs):
91
  pass
92
- def end_conversation(self, *args):
93
  return f"saved_{len(self.conversations)}"
94
  def list_conversations(self):
95
  return self.conversations
96
- def get_conversation_summary(self):
97
- return {"total_conversations": 0, "average_accuracy": 0, "average_cost": 0, "mode_distribution": {}}
98
  def export_conversation_report(self, case_id, format):
99
  return f"report_{case_id}.{format}"
100
  def export_analytics_csv(self):
@@ -146,7 +147,7 @@ class EnhancedMAIDXInterface:
146
  self.conversation_logger = AgentConversationLogger("mai_dx_logs")
147
  self.current_metrics = RealTimeMetrics()
148
 
149
- # Розширені тестові кейси
150
  self.sample_cases = {
151
  "🫀 Кардіологічний (Гострий MI)": {
152
  "info": """Пацієнт: 58-річний чоловік, менеджер, гіпертонія в анамнезі
@@ -155,7 +156,7 @@ class EnhancedMAIDXInterface:
155
  ЕКГ: ST-підйоми у відведеннях II, III, aVF (нижня стінка)
156
  Тропонін I: 8.5 нг/мл (норма <0.04)
157
  Анамнез: Куріння 30 років, дислипідемія, сімейний анамнез ІХС""",
158
- "expected": "Acute inferior myocardial infarction (STEMI)"
159
  },
160
 
161
  "🧠 Неврологічний (Гострий інсульт)": {
@@ -165,7 +166,7 @@ class EnhancedMAIDXInterface:
165
  Неврологія: Правостороння геміплегія, афазія, девіація очей вліво
166
  КТ голови: Гострого крововиливу немає, рання ішемія у лівій МСА
167
  NIHSS: 15 балів""",
168
- "expected": "Acute ischemic stroke in the left middle cerebral artery basin"
169
  },
170
 
171
  "🦠 Інфекційний (Сепсис)": {
@@ -199,7 +200,6 @@ NIHSS: 15 балів""",
199
  if not patient_info.strip():
200
  return self._format_error("❌ Введіть інформацію про пацієнта")
201
 
202
- # Скидання метрик
203
  self.current_metrics.reset()
204
  conversation_log = ""
205
  case_id = None
@@ -207,7 +207,6 @@ NIHSS: 15 балів""",
207
  try:
208
  progress(0.1, desc="🚀 Ініціалізація системи...")
209
 
210
- # Логування початку
211
  if enable_logging:
212
  case_id = self.conversation_logger.start_conversation(
213
  case_name or f"Case_{datetime.now().strftime('%H%M%S')}",
@@ -218,7 +217,6 @@ NIHSS: 15 балів""",
218
 
219
  progress(0.2, desc="🤖 Створення AI-панелі лікарів...")
220
 
221
- # Створення оркестратора
222
  orchestrator = MaiDxOrchestrator(
223
  model_name=model_name,
224
  max_iterations=max_iterations,
@@ -227,94 +225,60 @@ NIHSS: 15 балів""",
227
  )
228
 
229
  progress(0.3, desc="🔍 Запуск діагностичного процесу...")
230
-
231
  start_time = time.time()
232
 
233
- # Симуляція прогресу з реальними етапами
234
- diagnostic_stages = [
235
- "🧠 Dr. Hypothesis аналізує симптоми...",
236
- "🔬 Dr. Test-Chooser обирає тести...",
237
- "🤔 Dr. Challenger перевіряє гіпотези...",
238
- "💰 Dr. Stewardship оцінює вартість...",
239
- "✅ Dr. Checklist контролює якість...",
240
- "🤝 Consensus Coordinator формує рішення..."
241
- ]
242
-
243
- for i, stage in enumerate(diagnostic_stages):
244
- progress(0.3 + (i * 0.1), desc=stage)
245
- time.sleep(0.3) # Короткша затримка
246
-
247
- progress(0.8, desc="🎯 Формування діагнозу...")
248
-
249
  # Запуск діагностики з перехопленням виводу
250
- with io.StringIO() as captured_output:
251
  try:
252
- original_stdout = sys.stdout
253
- sys.stdout = captured_output
254
-
255
- if mode == "ensemble":
256
- try:
257
- result = orchestrator.run_ensemble(
258
- initial_case_info=patient_info,
259
- full_case_details=patient_info,
260
- ground_truth_diagnosis=expected_diagnosis or "Unknown",
261
- num_runs=2
262
- )
263
- except AttributeError:
264
- result = orchestrator.run(
265
- initial_case_info=patient_info,
266
- full_case_details=patient_info,
267
- ground_truth_diagnosis=expected_diagnosis or "Unknown"
268
- )
269
- else:
270
- result = orchestrator.run(
271
- initial_case_info=patient_info,
272
- full_case_details=patient_info,
273
- ground_truth_diagnosis=expected_diagnosis or "Unknown"
274
- )
275
-
276
  finally:
277
- sys.stdout = original_stdout
278
  captured_text = captured_output.getvalue()
279
-
280
  duration = time.time() - start_time
281
-
282
  progress(0.9, desc="📊 Обробка результатів...")
283
 
284
- # Парсинг логів
285
  if enable_logging and captured_text:
286
- conversation_log += "🤖 Захоплені логи агентів:\n"
287
- conversation_log += "=" * 60 + "\n"
288
- conversation_log += captured_text + "\n"
289
- conversation_log += "=" * 60 + "\n\n"
290
-
291
- # Оновлення метрик
292
- self._parse_logs_for_metrics(captured_text)
293
 
294
- # Логування результату
295
  if enable_logging:
 
296
  self.conversation_logger.log_agent_message(
297
- "Judge",
298
- "final_evaluation",
299
- result.final_diagnosis,
300
- getattr(result, 'accuracy_reasoning', 'Фінальна оцінка'),
301
- result.accuracy_score / 5.0,
302
- result.total_cost,
303
- max_iterations
304
  )
305
 
 
306
  saved_case_id = self.conversation_logger.end_conversation(
307
- result.final_diagnosis,
308
- result.accuracy_score,
309
- result.total_cost
 
 
 
310
  )
311
-
312
- conversation_log += f"💾 Збережено як: {saved_case_id}\n"
313
 
314
- # Створення сесії
315
- # Виправлений код
316
  session = DiagnosisSession(
317
- case_id=case_id, # <--- ДОДАЙТЕ ЦЕЙ РЯДОК
318
  timestamp=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
319
  case_name=case_name or f"Case_{len(self.sessions_history) + 1}",
320
  patient_info=patient_info[:200] + "..." if len(patient_info) > 200 else patient_info,
@@ -328,16 +292,13 @@ NIHSS: 15 балів""",
328
  status="✅ Успішно" if result.accuracy_score >= 3.0 else "⚠️ Потребує перегляду",
329
  reasoning=getattr(result, 'accuracy_reasoning', 'Недоступно')[:300] + "..."
330
  )
331
-
332
  self.sessions_history.append(session)
333
 
334
  progress(1.0, desc="✅ Готово!")
335
 
336
- # Генерація візуалізацій (безпечно)
337
  metrics_plot = self._create_metrics_visualization_safe()
338
  agent_plot = self._create_agent_activity_chart_safe()
339
 
340
- # Генерація всіх результатів
341
  return (
342
  self._format_main_result(session, result),
343
  self._format_detailed_analysis(session, result),
@@ -354,608 +315,271 @@ NIHSS: 15 балів""",
354
  return self._format_error(error_msg)
355
 
356
  def _create_metrics_visualization_safe(self):
357
- """Безпечне створення візуалізації метрик"""
358
  try:
359
- if not PLOTLY_AVAILABLE:
360
- return None
361
-
362
  if not self.current_metrics.cost_progression:
363
- # Створюємо демо-графік
364
- fig = go.Figure()
365
- fig.add_trace(go.Scatter(
366
- x=[0, 1, 2, 3],
367
- y=[0, 300, 600, 900],
368
- mode='lines+markers',
369
- name='Демо-дані',
370
- line=dict(color='#1f77b4', width=3)
371
- ))
372
- fig.update_layout(
373
- title='📈 Прогресія вартості (демо)',
374
- xaxis_title='Час (секунди)',
375
- yaxis_title='Вартість ($)',
376
- template='plotly_white',
377
- height=400
378
- )
379
- return fig
380
 
381
- # Реальні дані
382
- times = [point['time'] for point in self.current_metrics.cost_progression]
383
- costs = [point['cost'] for point in self.current_metrics.cost_progression]
384
-
385
- fig = go.Figure()
386
- fig.add_trace(go.Scatter(
387
- x=times,
388
- y=costs,
389
- mode='lines+markers',
390
- name='Накопичена вартість',
391
- line=dict(color='#1f77b4', width=3)
392
- ))
393
-
394
- fig.update_layout(
395
- title='📈 Прогресія вартості діагностики',
396
- xaxis_title='Час (секунди)',
397
- yaxis_title='Вартість ($)',
398
- template='plotly_white',
399
- height=400
400
- )
401
 
 
 
402
  return fig
403
-
404
  except Exception as e:
405
  print(f"Помилка візуалізації метрик: {e}")
406
  return None
407
 
408
  def _create_agent_activity_chart_safe(self):
409
- """Безпечне створення діаграми активності агентів"""
410
  try:
411
- if not PLOTLY_AVAILABLE:
412
- return None
413
-
414
- # Підготовка даних
415
- agents = list(self.current_metrics.agents_activity.keys())
416
- activities = list(self.current_metrics.agents_activity.values())
417
 
418
- # Якщо немає активності, створюємо демо
419
- if all(activity == 0 for activity in activities):
420
- activities = [1, 2, 1, 1, 1, 2, 3, 1] # Демо-дані
421
-
422
- # Кольори для агентів
423
- colors = [
424
- '#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4',
425
- '#FECA57', '#FF9FF3', '#54A0FF', '#5F27CD'
426
- ]
427
-
428
- fig = go.Figure(data=[
429
- go.Bar(
430
- x=agents,
431
- y=activities,
432
- marker_color=colors[:len(agents)],
433
- text=activities,
434
- textposition='auto'
435
- )
436
- ])
437
-
438
- fig.update_layout(
439
- title='🤖 Активність ШІ-агентів лікарів',
440
- xaxis_title='Агенти',
441
- yaxis_title='Кількість взаємодій',
442
- template='plotly_white',
443
- height=400,
444
- showlegend=False
445
- )
446
 
447
- fig.update_xaxes(tickangle=45)
448
 
 
 
 
449
  return fig
450
-
451
  except Exception as e:
452
  print(f"Помилка графіку агентів: {e}")
453
  return None
454
 
455
- def _parse_logs_for_metrics(self, captured_text: str):
456
- """Парсинг логів для оновлення метрик"""
 
 
457
  lines = captured_text.split('\n')
458
 
 
 
 
 
 
 
459
  for line in lines:
460
  line = line.strip()
461
- if not line:
462
- continue
463
-
464
- # Пошук активності агентів
465
- for agent in self.current_metrics.agents_activity.keys():
466
- if agent.lower().replace(" ", "").replace(".", "") in line.lower():
467
- self.current_metrics.update_agent_activity(agent)
 
 
 
468
  break
469
 
470
- # Пошук вартості
471
- import re
472
- cost_match = re.search(r'\$(\d+(?:\.\d+)?)', line)
 
 
 
 
 
 
 
473
  if cost_match:
474
- self.current_metrics.add_cost_point(float(cost_match.group(1)))
 
475
 
476
  def _format_main_result(self, session, result):
477
  """Форматування основного результату"""
478
  confidence_emoji = "🎯" if session.confidence >= 4.0 else "👍" if session.confidence >= 3.0 else "⚠️"
479
- efficiency = ((session.budget - session.cost) / session.budget * 100)
480
 
481
  return f"""
482
  ## 🏥 Результати MAI-DX Діагностики
483
-
484
  ### 📋 Основна інформація
485
  - **🗂️ Випадок**: {session.case_name}
486
  - **⏰ Час**: {session.timestamp}
487
  - **🔧 Режим**: {session.mode}
488
- - **🤖 Модель**: AI Multi-Agent
489
-
490
  ### {confidence_emoji} Діагностичний висновок
491
  **{session.diagnosis}**
492
-
493
  ### 📊 Показники якості
494
  - **Точність**: {session.confidence:.1f}/5.0 ⭐
495
  - **Статус**: {session.status}
496
- - **Ітерації**: {session.iterations} циклів
497
-
498
  ### 💰 Економічні показники
499
  - **Витрачено**: ${session.cost:,.2f}
500
  - **Бюджет**: ${session.budget:,}
501
  - **Ефективність**: {efficiency:.1f}% бюджету збережено
502
- - **Швидкість**: {session.duration:.1f} секунд
503
-
504
  ### 🎖️ Загальна оцінка
505
  {self._get_overall_rating(session)}
506
  """
507
 
508
  def _get_overall_rating(self, session):
509
  """Загальна оцінка ефективності"""
510
- efficiency = ((session.budget - session.cost) / session.budget * 100)
511
-
512
- if session.confidence >= 4.0 and efficiency >= 50:
513
- return "🏆 **ВІДМІННО** - Високоточний та економічний діагноз"
514
- elif session.confidence >= 3.0 and efficiency >= 30:
515
- return "🥈 **ДОБРЕ** - Надійний діагноз з прийнятною вартістю"
516
- elif session.confidence >= 2.0:
517
- return "🥉 **ЗАДОВІЛЬНО** - Потребує додаткової верифікації"
518
- else:
519
- return "❌ **ПОТРЕБУЄ ПЕРЕГЛЯДУ** - Низька впевненість"
520
 
521
  def _format_detailed_analysis(self, session, result):
522
  """Детальний аналіз"""
523
  return f"""
524
  ## 🔬 Детальний клінічний аналіз
525
-
526
  ### 💭 Медичне обґрунтування
527
  {session.reasoning}
528
-
529
  ### 📈 Аналіз ефективності
530
- - **⚡ Швидкість діагностики**: {session.duration:.1f} сек
531
- - **💸 Економічна ефективність**: {((session.budget - session.cost) / session.budget * 100):.1f}%
532
- - **🔄 Ітеративна конвергенція**: {session.iterations} циклів
533
  - **🎯 Клінічна точність**: {(session.confidence / 5.0 * 100):.1f}%
534
-
535
  ### 🤖 Процес прийняття рішень
536
- Система MAI-DX використала **{session.iterations} ітерацій** для досягнення консенсусу між **8 ШІ-агентами лікарів**.
537
-
538
- ### 📊 Порівняння з бенчмарками
539
- - **Людські лікарі**: ~20% точність на NEJM CPC
540
- - **GPT-4**: ~49% точність
541
- - **MAI-DX**: ~85.5% точність ✨
542
  """
543
 
544
  def _generate_enhanced_recommendations(self, result, expected_diagnosis):
545
  """Покращені рекомендації"""
546
  comparison = ""
547
  if expected_diagnosis:
 
 
548
  comparison = f"""
549
- ### 🎯 Порівняння з очікуваним діагнозом
550
  **Очікувався**: {expected_diagnosis}
551
  **Отримано**: {result.final_diagnosis}
552
- **Збіг**: {'✅ Так' if expected_diagnosis.lower() in result.final_diagnosis.lower() else '❌ Ні'}
553
  """
554
-
555
  return f"""
556
  ## 💡 Клінічні рекомендації
557
-
558
  ### 🏥 Негайні дії
559
- - 🔍 **Верифікація діагнозу** з лікарем-спеціалістом
560
- - 📋 **Додаткові дослідження** при необхідності
561
- - 👨‍⚕️ **Консультація експерта** для підтвердження
562
-
563
  ### 🔬 Дослідницький потенціал
564
- - **📊 Аналіз логів**: Вивчіть деталі роботи агентів
565
- - **📈 Валідація**: Перевірте на додаткових випадках
566
- - **🤖 Тюнінг моделі**: Оптимізуйте параметри
567
-
568
  {comparison}
569
-
570
  ### ⚠️ Важливе застереження
571
- 🔴 **Цей діагноз згенеровано ШІ для дослідницьких цілей та НЕ замінює професійну медичну консультацію.**
572
  """
573
 
574
  def _format_error(self, error_msg):
575
- """Форматування помилки"""
576
  return (error_msg, "", "", None, None, "")
577
 
578
  def load_sample_case(self, sample_key):
579
- """Завантаження зразкового випадку"""
580
  if sample_key and sample_key in self.sample_cases:
581
  case_data = self.sample_cases[sample_key]
582
- return (
583
- case_data["info"],
584
- sample_key,
585
- case_data["expected"]
586
- )
587
  return "", "", ""
588
 
589
  def get_enhanced_analytics(self):
590
- """Розширена аналітика - ВИПРАВЛЕНА ВЕРСІЯ"""
591
  try:
592
  conversations = self.conversation_logger.list_conversations()
593
-
594
  if not conversations:
595
- empty_df = pd.DataFrame()
596
- return empty_df, "📊 Немає даних для аналізу", []
597
 
598
- # Створення DataFrame
599
  df = pd.DataFrame(conversations)
 
600
 
601
- # Статистика
602
- total_cases = len(conversations)
603
- avg_accuracy = df['accuracy'].mean() if 'accuracy' in df.columns else 0
604
- avg_cost = df['cost'].mean() if 'cost' in df.columns else 0
605
 
606
  summary = f"""
607
- ## 📊 Розширена аналітика MAI-DX
608
-
609
  ### 🎯 Загальна статистика
610
  - **Всього випадків**: {total_cases}
611
  - **Середня точність**: {avg_accuracy:.2f}/5.0 ⭐
612
  - **Середня вартість**: ${avg_cost:,.2f}
613
- - **Загальна економія**: ${(3000 - avg_cost) * total_cases:,.2f}
614
  """
615
 
616
- # Створення списку для dropdown
617
- case_choices = []
618
- if not df.empty and 'case_name' in df.columns:
619
- case_choices = [(f"{row['case_name']} ({row.get('case_id', 'N/A')})",
620
- row.get('case_id', f"case_{i}"))
621
- for i, (_, row) in enumerate(df.iterrows())]
622
-
623
- return df, summary, case_choices
624
-
625
  except Exception as e:
626
  return pd.DataFrame(), f"❌ Помилка аналітики: {e}", []
627
 
628
  def create_enhanced_gradio_interface():
629
- """Створення виправленого Gradio інтерфейсу"""
630
-
631
  interface = EnhancedMAIDXInterface()
 
632
 
633
- # Сучасний CSS
634
- custom_css = """
635
- .gradio-container {
636
- font-family: 'Inter', system-ui, -apple-system, sans-serif;
637
- }
638
-
639
- .main-header {
640
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
641
- color: white;
642
- padding: 2rem;
643
- border-radius: 15px;
644
- margin-bottom: 2rem;
645
- text-align: center;
646
- }
647
-
648
- .conversation-log {
649
- font-family: 'Fira Code', monospace;
650
- background: #2d3748;
651
- color: #e2e8f0;
652
- border-radius: 8px;
653
- padding: 1rem;
654
- max-height: 500px;
655
- overflow-y: auto;
656
- white-space: pre-wrap;
657
- }
658
- """
659
-
660
- with gr.Blocks(
661
- title="🏥 MAI-DX Enhanced Research Platform",
662
- theme=gr.themes.Soft(
663
- primary_hue="blue",
664
- secondary_hue="purple",
665
- neutral_hue="slate"
666
- ),
667
- css=custom_css
668
- ) as demo:
669
-
670
- # Заголовок
671
- gr.HTML("""
672
- <div class="main-header">
673
- <h1>🏥 MAI-DX Enhanced Research Platform</h1>
674
- <p>🤖 Платформа для дослідження ШІ-діагностики з детальним логуванням взаємодії агентів</p>
675
- <p>📊 Базується на дослідженні Microsoft Research "Sequential Diagnosis with Language Models"</p>
676
- </div>
677
- """)
678
 
679
- with gr.Tabs() as tabs:
680
-
681
- # Основна вкладка діагностики
682
- with gr.Tab("🩺 Діагностика з ШІ-агентами", elem_id="diagnosis-tab"):
683
-
684
  with gr.Row():
685
- # Ліва колонка - введення даних
686
  with gr.Column(scale=1):
687
  gr.Markdown("### 📝 Клінічний випадок")
688
-
689
- case_name = gr.Textbox(
690
- label="🏷️ Назва випадку",
691
- placeholder="Наприклад: Кардіологічний випадок №1",
692
- lines=1
693
- )
694
-
695
- sample_selector = gr.Dropdown(
696
- choices=list(interface.sample_cases.keys()),
697
- label="🎯 Готові тестові випадки",
698
- value=None,
699
- interactive=True
700
- )
701
-
702
- patient_info = gr.Textbox(
703
- label="👤 Детальна інформація про пацієнта",
704
- lines=12,
705
- placeholder="Введіть повний опис клінічного випадку:\n- Демографія пацієнта\n- Скарги та анамнез\n- Фізикальне обстеження\n- Лабораторні дані\n- Інструментальні дослідження",
706
- interactive=True
707
- )
708
-
709
- expected_diagnosis = gr.Textbox(
710
- label="🎯 Очікуваний діагноз (для порівняння)",
711
- placeholder="Опціонально: введіть правильний діагноз для оцінки точності",
712
- lines=2
713
- )
714
 
715
- # Права колонка - налаштування
716
  with gr.Column(scale=1):
717
- gr.Markdown("### ⚙️ Налаштування діагностики")
718
-
719
- mode_choices_map = {
720
- "⚡ Миттєвий (найшвидший, базовий аналіз)": "instant",
721
- " Тільки питання (швидко, без тестів)": "question_only",
722
- "💰 З бюджетом (збалансовано)": "budgeted",
723
- "🔓 Без обмежень (повний аналіз)": "no_budget",
724
- "👥 Консенсус (найточніший, повільний)": "ensemble"
725
- }
726
-
727
- # Новий, виправлений код
728
- mode = gr.Radio(
729
- choices=[
730
- "instant",
731
- "question_only",
732
- "budgeted",
733
- "no_budget",
734
- "ensemble"
735
- ],
736
- label="🔧 Режим діагностики",
737
- value="budgeted",
738
- interactive=True
739
- )
740
-
741
- budget = gr.Slider(
742
- minimum=500,
743
- maximum=10000,
744
- step=500,
745
- value=3000,
746
- label="💵 Бюджет діагностики ($)",
747
- interactive=True
748
- )
749
-
750
- max_iterations = gr.Slider(
751
- minimum=1,
752
- maximum=15,
753
- step=1,
754
- value=8,
755
- label="🔄 Максимум ітерацій агентів",
756
- interactive=True
757
- )
758
-
759
- model_name = gr.Dropdown(
760
- choices=[
761
- "gemini/gemini-2.5-flash",
762
- "gpt-4",
763
- "gpt-4-turbo",
764
- "claude-3-5-sonnet",
765
- "grok-beta"
766
- ],
767
- label="🤖 LLM Модель",
768
- value="gemini/gemini-2.5-flash",
769
- interactive=True
770
- )
771
-
772
- enable_logging = gr.Checkbox(
773
- label="📝 Детальне логування взаємодії агентів",
774
- value=True,
775
- interactive=True
776
- )
777
-
778
- gr.Markdown("---")
779
-
780
- diagnose_btn = gr.Button(
781
- "🚀 Запустити діагностику",
782
- variant="primary",
783
- size="lg",
784
- interactive=True
785
- )
786
-
787
- # Завантаження зразків
788
- sample_selector.change(
789
- interface.load_sample_case,
790
- inputs=[sample_selector],
791
- outputs=[patient_info, case_name, expected_diagnosis]
792
- )
793
 
794
- # Результати діагностики
795
- gr.Markdown("---")
796
- gr.Markdown("## 📊 Результати діагностики")
797
 
 
798
  with gr.Row():
799
  with gr.Column(scale=2):
800
  main_result = gr.Markdown(label="🎯 Основний результат")
801
  detailed_analysis = gr.Markdown(label="🔬 Детальний аналіз")
802
-
803
  with gr.Column(scale=1):
804
  recommendations = gr.Markdown(label="💡 Рекомендації")
805
 
806
- # Візуалізації (якщо Plotly доступний)
807
- if PLOTLY_AVAILABLE:
808
- with gr.Row():
809
- with gr.Column():
810
- metrics_plot = gr.Plot(label="📈 Метрики діагностики")
811
- with gr.Column():
812
- agent_activity_plot = gr.Plot(label="🤖 Активність агентів")
813
- else:
814
- gr.Markdown("ℹ️ Візуалізації недоступні (Plotly не встановлено)")
815
- metrics_plot = gr.Textbox(visible=False)
816
- agent_activity_plot = gr.Textbox(visible=False)
817
 
818
- # Логи бесіди
819
- gr.Markdown("### 💬 Логи взаємодії ШІ-агентів")
820
- conversation_logs = gr.Textbox(
821
- label="Детальні логи бесід між агентами",
822
- lines=12,
823
- elem_classes=["conversation-log"],
824
- interactive=False,
825
- show_copy_button=True
826
- )
827
 
828
- # Запуск діагностики
829
- diagnose_btn.click(
830
- interface.diagnose_with_enhanced_tracking,
831
- inputs=[
832
- case_name, patient_info, mode, budget, max_iterations,
833
- model_name, expected_diagnosis, enable_logging
834
- ],
835
- outputs=[
836
- main_result, detailed_analysis, recommendations,
837
- metrics_plot, agent_activity_plot, conversation_logs
838
- ]
839
- )
840
 
841
- # Вкладка аналітики
842
- with gr.Tab("📊 Розширена аналітика", elem_id="analytics-tab"):
843
  gr.Markdown("### 🔍 Аналіз збережених діагностичних сесій")
844
-
845
  with gr.Row():
846
- refresh_analytics_btn = gr.Button("🔄 Оновити дані", variant="secondary")
847
- export_data_btn = gr.Button("📤 Експортувати аналітику", variant="secondary")
 
 
848
 
 
849
  with gr.Row():
850
- with gr.Column(scale=2):
851
- analytics_table = gr.Dataframe(
852
- label="📋 Історія діагностичних сесій",
853
- interactive=False,
854
- wrap=True
855
- )
856
-
857
- with gr.Column(scale=1):
858
- analytics_summary = gr.Markdown(label="📊 Статистичний звіт")
859
-
860
- # Детальний аналіз окремих випадків
861
- gr.Markdown("### 🔬 Поглиблений аналіз випадків")
862
-
863
- with gr.Row():
864
- case_selector = gr.Dropdown(
865
- label="🗂️ Оберіть випадок для детального аналізу",
866
- choices=[],
867
- interactive=True
868
- )
869
-
870
- analyze_case_btn = gr.Button("📋 Створити детальний звіт")
871
-
872
  analysis_status = gr.Markdown()
873
 
874
- # Функції аналітики - ВИПРАВЛЕНІ
875
  def refresh_analytics():
876
- df, summary, case_choices = interface.get_enhanced_analytics()
877
- return df, summary, gr.Dropdown(choices=case_choices)
878
-
879
- def export_analytics():
880
- try:
881
- filename = interface.conversation_logger.export_analytics_csv()
882
- return f"✅ Дані експортовано: {filename}"
883
- except Exception as e:
884
- return f"❌ Помилка експорту: {e}"
885
-
886
- def analyze_case(case_id):
887
- if not case_id:
888
- return "⚠️ Оберіть випадок для аналізу"
889
- try:
890
- report = interface.conversation_logger.export_conversation_report(case_id, 'html')
891
- return f"✅ Детальний звіт створено: {report}"
892
- except Exception as e:
893
- return f"❌ Помилка аналізу: {e}"
894
 
895
- # Зв'язування функцій
896
- refresh_analytics_btn.click(
897
- refresh_analytics,
898
- outputs=[analytics_table, analytics_summary, case_selector]
899
- )
900
-
901
- export_data_btn.click(
902
- export_analytics,
903
- outputs=[analysis_status]
904
- )
905
-
906
- analyze_case_btn.click(
907
- analyze_case,
908
- inputs=[case_selector],
909
- outputs=[analysis_status]
910
- )
911
-
912
- # Автооновлення при завантаженні
913
  demo.load(refresh_analytics, outputs=[analytics_table, analytics_summary, case_selector])
914
 
915
- # Вкладка документації
916
- with gr.Tab("📚 Документація та статус", elem_id="docs-tab"):
917
-
918
- # Статус системи
919
- gr.Markdown("### 🔧 Статус системи")
920
-
921
- status_info = f"""
922
- **MAI-DX доступність**: {' Доступний' if MAI_DX_AVAILABLE else '❌ Недоступний'}
923
- **Logger доступність**: {'✅ Доступний' if LOGGER_AVAILABLE else '⚠️ Заглушка'}
924
- **Plotly візуалізації**: {'✅ Доступні' if PLOTLY_AVAILABLE else '❌ Недоступні'}
925
- **Gradio версія**: {gr.__version__}
926
- """
927
-
928
- gr.Markdown(status_info)
929
-
930
- if not MAI_DX_AVAILABLE:
931
- gr.Markdown(f"**Помилка MAI-DX**: {IMPORT_ERROR}")
932
-
933
- # Документація
934
- gr.Markdown("""
935
  ### 📖 Короткий гайд
936
-
937
- #### 🚀 Швидкий старт:
938
- 1. Оберіть готовий тестовий випадок або введіть свій
939
- 2. Налаштуйте режим діагностики (рекомендується бюджетом")
940
- 3. Натисніть "Запустити діагностику"
941
- 4. Дочекайт��ся результатів та аналізуйте логи
942
-
943
- #### 🔧 Режими роботи:
944
- - **⚡ Миттєвий**: Швидкий базовий аналіз (~5 сек)
945
- - **❓ Тільки питання**: Без тестів, тільки опитування
946
- - **💰 З бюджетом**: Збалансований підхід (рекомендується)
947
- - **🔓 Без обмежень**: Максимальна точність
948
- - **👥 Консенсус**: Найвища точність, але повільно
949
-
950
- #### 📊 Аналітика:
951
- - Переглядайте історію діагнозів у вкладці "Аналітика"
952
- - Експортуйте дані для подальшого аналізу
953
- - Створюйте детальні звіти по окремих випадках
954
-
955
- #### ⚠️ Важливо:
956
- - Система призначена для дослідницьких цілей
957
- - Не замінює професійну медичну консультацію
958
- - При проблемах перевірте статус системи вище
959
  """)
960
 
961
  return demo
@@ -972,9 +596,6 @@ if __name__ == "__main__":
972
  demo.launch(
973
  server_name="0.0.0.0",
974
  server_port=7860,
975
- share=False,
976
- debug=False, # Відключаємо debug для стабільності
977
- show_error=True,
978
- favicon_path=None,
979
- ssl_verify=False
980
  )
 
15
  import threading
16
  import queue
17
  import io
18
+ import re # Імпортуємо re для парсингу
19
  from contextlib import redirect_stdout, redirect_stderr
20
 
21
  # Налаштування середовища
 
65
  try:
66
  from agent_conversation_logger import AgentConversationLogger, DiagnosisSession
67
  LOGGER_AVAILABLE = True
68
+ except ImportError as e:
69
  LOGGER_AVAILABLE = False
70
+ print(f"⚠️ Не вдалося завантажити логгер, буде використана заглушка: {e}")
71
 
72
  @dataclass
73
  class DiagnosisSession:
74
+ case_id: str
75
  timestamp: str
76
  case_name: str
77
  patient_info: str
 
92
  return f"mock_{len(self.conversations)}"
93
  def log_agent_message(self, *args, **kwargs):
94
  pass
95
+ def end_conversation(self, *args, **kwargs):
96
  return f"saved_{len(self.conversations)}"
97
  def list_conversations(self):
98
  return self.conversations
 
 
99
  def export_conversation_report(self, case_id, format):
100
  return f"report_{case_id}.{format}"
101
  def export_analytics_csv(self):
 
147
  self.conversation_logger = AgentConversationLogger("mai_dx_logs")
148
  self.current_metrics = RealTimeMetrics()
149
 
150
+ # Розширені тестові кейси (з виправленими очікуваними діагнозами)
151
  self.sample_cases = {
152
  "🫀 Кардіологічний (Гострий MI)": {
153
  "info": """Пацієнт: 58-річний чоловік, менеджер, гіпертонія в анамнезі
 
156
  ЕКГ: ST-підйоми у відведеннях II, III, aVF (нижня стінка)
157
  Тропонін I: 8.5 нг/мл (норма <0.04)
158
  Анамнез: Куріння 30 років, дислипідемія, сімейний анамнез ІХС""",
159
+ "expected": "Acute inferior wall myocardial infarction (STEMI)"
160
  },
161
 
162
  "🧠 Неврологічний (Гострий інсульт)": {
 
166
  Неврологія: Правостороння геміплегія, афазія, девіація очей вліво
167
  КТ голови: Гострого крововиливу немає, рання ішемія у лівій МСА
168
  NIHSS: 15 балів""",
169
+ "expected": "Acute ischemic stroke in the left middle cerebral artery territory"
170
  },
171
 
172
  "🦠 Інфекційний (Сепсис)": {
 
200
  if not patient_info.strip():
201
  return self._format_error("❌ Введіть інформацію про пацієнта")
202
 
 
203
  self.current_metrics.reset()
204
  conversation_log = ""
205
  case_id = None
 
207
  try:
208
  progress(0.1, desc="🚀 Ініціалізація системи...")
209
 
 
210
  if enable_logging:
211
  case_id = self.conversation_logger.start_conversation(
212
  case_name or f"Case_{datetime.now().strftime('%H%M%S')}",
 
217
 
218
  progress(0.2, desc="🤖 Створення AI-панелі лікарів...")
219
 
 
220
  orchestrator = MaiDxOrchestrator(
221
  model_name=model_name,
222
  max_iterations=max_iterations,
 
225
  )
226
 
227
  progress(0.3, desc="🔍 Запуск діагностичного процесу...")
 
228
  start_time = time.time()
229
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  # Запуск діагностики з перехопленням виводу
231
+ with io.StringIO() as captured_output, redirect_stdout(captured_output):
232
  try:
233
+ result = orchestrator.run(
234
+ initial_case_info=patient_info,
235
+ full_case_details=patient_info,
236
+ ground_truth_diagnosis=expected_diagnosis or "Unknown"
237
+ )
238
+ except Exception as e:
239
+ # Спроба обробити помилку і отримати частковий результат
240
+ print(f"\nПОМИЛКА ПІД ЧАС ВИКОНАННЯ: {e}")
241
+ result = getattr(orchestrator, 'result', None)
242
+ if not result:
243
+ raise e from e
 
 
 
 
 
 
 
 
 
 
 
 
 
244
  finally:
 
245
  captured_text = captured_output.getvalue()
246
+
247
  duration = time.time() - start_time
 
248
  progress(0.9, desc="📊 Обробка результатів...")
249
 
 
250
  if enable_logging and captured_text:
251
+ conversation_log += "🤖 Захоплені логи агентів:\n" + "=" * 60 + "\n"
252
+ conversation_log += captured_text + "\n" + "=" * 60 + "\n\n"
253
+ # ВИПРАВЛЕНО: Парсинг логів для метрик та повідомлень
254
+ self._parse_and_log_agent_activity(captured_text)
 
 
 
255
 
 
256
  if enable_logging:
257
+ # Логування фінальної оцінки
258
  self.conversation_logger.log_agent_message(
259
+ agent_name="Judge",
260
+ action="final_evaluation",
261
+ content=result.final_diagnosis,
262
+ reasoning=getattr(result, 'accuracy_reasoning', 'Фінальна оцінка'),
263
+ confidence=result.accuracy_score / 5.0, # Нормалізація до 0-1
264
+ cost=result.total_cost,
265
+ iteration=getattr(result, 'iterations', max_iterations)
266
  )
267
 
268
+ # ВИПРАВЛЕНО: Завершення сесії з усіма даними
269
  saved_case_id = self.conversation_logger.end_conversation(
270
+ final_diagnosis=result.final_diagnosis,
271
+ confidence=result.accuracy_score,
272
+ cost=result.total_cost,
273
+ budget=budget,
274
+ reasoning=getattr(result, 'accuracy_reasoning', 'N/A'),
275
+ iterations=getattr(result, 'iterations', max_iterations)
276
  )
277
+ conversation_log += f"💾 Сесію збережено як: {saved_case_id}\n"
 
278
 
279
+ # Створення об'єкту сесії для відображення
 
280
  session = DiagnosisSession(
281
+ case_id=case_id,
282
  timestamp=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
283
  case_name=case_name or f"Case_{len(self.sessions_history) + 1}",
284
  patient_info=patient_info[:200] + "..." if len(patient_info) > 200 else patient_info,
 
292
  status="✅ Успішно" if result.accuracy_score >= 3.0 else "⚠️ Потребує перегляду",
293
  reasoning=getattr(result, 'accuracy_reasoning', 'Недоступно')[:300] + "..."
294
  )
 
295
  self.sessions_history.append(session)
296
 
297
  progress(1.0, desc="✅ Готово!")
298
 
 
299
  metrics_plot = self._create_metrics_visualization_safe()
300
  agent_plot = self._create_agent_activity_chart_safe()
301
 
 
302
  return (
303
  self._format_main_result(session, result),
304
  self._format_detailed_analysis(session, result),
 
315
  return self._format_error(error_msg)
316
 
317
  def _create_metrics_visualization_safe(self):
 
318
  try:
319
+ if not PLOTLY_AVAILABLE: return None
320
+ fig = go.Figure()
 
321
  if not self.current_metrics.cost_progression:
322
+ return None # Немає даних - не показуємо графік
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
323
 
324
+ times = [p['time'] for p in self.current_metrics.cost_progression]
325
+ costs = [p['cost'] for p in self.current_metrics.cost_progression]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
 
327
+ fig.add_trace(go.Scatter(x=times, y=costs, mode='lines+markers', name='Вартість', line=dict(color='#1f77b4', width=3)))
328
+ fig.update_layout(title='📈 Прогресія вартості діагностики', xaxis_title='Час (секунди)', yaxis_title='Вартість ($)', template='plotly_white', height=400)
329
  return fig
 
330
  except Exception as e:
331
  print(f"Помилка візуалізації метрик: {e}")
332
  return None
333
 
334
  def _create_agent_activity_chart_safe(self):
 
335
  try:
336
+ if not PLOTLY_AVAILABLE: return None
 
 
 
 
 
337
 
338
+ activity_data = {k: v for k, v in self.current_metrics.agents_activity.items() if v > 0}
339
+ if not activity_data: return None
340
+
341
+ agents = list(activity_data.keys())
342
+ activities = list(activity_data.values())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
343
 
344
+ colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FECA57', '#FF9FF3', '#54A0FF', '#5F27CD']
345
 
346
+ fig = go.Figure(data=[go.Bar(x=agents, y=activities, marker_color=colors, text=activities, textposition='auto')])
347
+ fig.update_layout(title='🤖 Активність ШІ-агентів', xaxis_title='Агенти', yaxis_title='Кількість взаємодій', template='plotly_white', height=400, showlegend=False)
348
+ fig.update_xaxes(tickangle=-45)
349
  return fig
 
350
  except Exception as e:
351
  print(f"Помилка графіку агентів: {e}")
352
  return None
353
 
354
+ def _parse_and_log_agent_activity(self, captured_text: str):
355
+ """
356
+ Парсить перехоплений текстовий лог для оновлення метрик та логування повідомлень.
357
+ """
358
  lines = captured_text.split('\n')
359
 
360
+ agent_keywords = {
361
+ "Dr. Hypothesis": "Dr. Hypothesis", "Dr. Test-Chooser": "Dr. Test-Chooser",
362
+ "Dr. Challenger": "Dr. Challenger", "Dr. Stewardship": "Dr. Stewardship",
363
+ "Dr. Checklist": "Dr. Checklist", "Consensus Coordinator": "Consensus Coordinator"
364
+ }
365
+
366
  for line in lines:
367
  line = line.strip()
368
+ if not line: continue
369
+
370
+ # 1. Логування активності агентів
371
+ for keyword, agent_name in agent_keywords.items():
372
+ if keyword in line and "analyzing" in line:
373
+ self.current_metrics.update_agent_activity(agent_name)
374
+ self.conversation_logger.log_agent_message(
375
+ agent_name=agent_name, action="analysis_start", content=line,
376
+ reasoning="Parsed from orchestrator output", confidence=0.0, cost=0.0, iteration=0
377
+ )
378
  break
379
 
380
+ # 2. Логування рішень панелі
381
+ if "Panel decision:" in line:
382
+ self.current_metrics.update_agent_activity("Consensus Coordinator")
383
+ self.conversation_logger.log_agent_message(
384
+ agent_name="Consensus Coordinator", action="panel_decision", content=line.split("Panel decision:")[-1].strip(),
385
+ reasoning="Parsed from orchestrator output", confidence=0.0, cost=0.0, iteration=0
386
+ )
387
+
388
+ # 3. Парсинг вартості
389
+ cost_match = re.search(r'Current cost: \$(\d+(?:\.\d+)?)', line)
390
  if cost_match:
391
+ current_cost = float(cost_match.group(1))
392
+ self.current_metrics.add_cost_point(current_cost)
393
 
394
  def _format_main_result(self, session, result):
395
  """Форматування основного результату"""
396
  confidence_emoji = "🎯" if session.confidence >= 4.0 else "👍" if session.confidence >= 3.0 else "⚠️"
397
+ efficiency = ((session.budget - session.cost) / session.budget * 100) if session.budget > 0 else 0
398
 
399
  return f"""
400
  ## 🏥 Результати MAI-DX Діагностики
 
401
  ### 📋 Основна інформація
402
  - **🗂️ Випадок**: {session.case_name}
403
  - **⏰ Час**: {session.timestamp}
404
  - **🔧 Режим**: {session.mode}
 
 
405
  ### {confidence_emoji} Діагностичний висновок
406
  **{session.diagnosis}**
 
407
  ### 📊 Показники якості
408
  - **Точність**: {session.confidence:.1f}/5.0 ⭐
409
  - **Статус**: {session.status}
410
+ - **Ітерації**: {session.iterations}
 
411
  ### 💰 Економічні показники
412
  - **Витрачено**: ${session.cost:,.2f}
413
  - **Бюджет**: ${session.budget:,}
414
  - **Ефективність**: {efficiency:.1f}% бюджету збережено
415
+ - **Швидкість**: {session.duration:.1f} сек
 
416
  ### 🎖️ Загальна оцінка
417
  {self._get_overall_rating(session)}
418
  """
419
 
420
  def _get_overall_rating(self, session):
421
  """Загальна оцінка ефективності"""
422
+ efficiency = ((session.budget - session.cost) / session.budget * 100) if session.budget > 0 else 0
423
+ if session.confidence >= 4.0 and efficiency >= 50: return "🏆 **ВІДМІННО**"
424
+ if session.confidence >= 3.0 and efficiency >= 20: return "🥈 **ДОБРЕ**"
425
+ if session.confidence >= 2.0: return "🥉 **ЗАДОВІЛЬНО**"
426
+ return "❌ **ПОТРЕБУЄ ПЕРЕГЛЯДУ**"
 
 
 
 
 
427
 
428
  def _format_detailed_analysis(self, session, result):
429
  """Детальний аналіз"""
430
  return f"""
431
  ## 🔬 Детальний клінічний аналіз
 
432
  ### 💭 Медичне обґрунтування
433
  {session.reasoning}
 
434
  ### 📈 Аналіз ефективності
435
+ - **💸 Економічна ефективність**: {((session.budget - session.cost) / session.budget * 100) if session.budget > 0 else 0:.1f}%
 
 
436
  - **🎯 Клінічна точність**: {(session.confidence / 5.0 * 100):.1f}%
 
437
  ### 🤖 Процес прийняття рішень
438
+ Система використала **{session.iterations} ітерацій** для консенсусу між **8 ШІ-агентами**.
 
 
 
 
 
439
  """
440
 
441
  def _generate_enhanced_recommendations(self, result, expected_diagnosis):
442
  """Покращені рекомендації"""
443
  comparison = ""
444
  if expected_diagnosis:
445
+ is_match = expected_diagnosis.lower() in result.final_diagnosis.lower() or \
446
+ result.final_diagnosis.lower() in expected_diagnosis.lower()
447
  comparison = f"""
448
+ ### 🎯 Порівняння з очікуваним
449
  **Очікувався**: {expected_diagnosis}
450
  **Отримано**: {result.final_diagnosis}
451
+ **Збіг**: {'✅ Так' if is_match else '❌ Ні'}
452
  """
 
453
  return f"""
454
  ## 💡 Клінічні рекомендації
 
455
  ### 🏥 Негайні дії
456
+ - 🔍 Верифікація діагнозу з лікарем
457
+ - 📋 Розгляд додаткових досліджень
 
 
458
  ### 🔬 Дослідницький потенціал
459
+ - 📊 Аналіз логів для вивчення роботи агентів
460
+ - 📈 Валідація на додаткових випадках
 
 
461
  {comparison}
 
462
  ### ⚠️ Важливе застереження
463
+ 🔴 **Цей діагноз згенеровано ШІ і НЕ замінює професійну медичну консультацію.**
464
  """
465
 
466
  def _format_error(self, error_msg):
 
467
  return (error_msg, "", "", None, None, "")
468
 
469
  def load_sample_case(self, sample_key):
 
470
  if sample_key and sample_key in self.sample_cases:
471
  case_data = self.sample_cases[sample_key]
472
+ return case_data["info"], sample_key, case_data["expected"]
 
 
 
 
473
  return "", "", ""
474
 
475
  def get_enhanced_analytics(self):
 
476
  try:
477
  conversations = self.conversation_logger.list_conversations()
 
478
  if not conversations:
479
+ return pd.DataFrame(), "📊 Немає даних для аналізу", []
 
480
 
 
481
  df = pd.DataFrame(conversations)
482
+ df = df.reindex(columns=['case_id', 'timestamp', 'case_name', 'mode', 'diagnosis', 'confidence', 'cost', 'budget', 'duration', 'status'])
483
 
484
+ total_cases = len(df)
485
+ avg_accuracy = df['confidence'].mean()
486
+ avg_cost = df['cost'].mean()
 
487
 
488
  summary = f"""
 
 
489
  ### 🎯 Загальна статистика
490
  - **Всього випадків**: {total_cases}
491
  - **Середня точність**: {avg_accuracy:.2f}/5.0 ⭐
492
  - **Середня вартість**: ${avg_cost:,.2f}
 
493
  """
494
 
495
+ case_choices = [(f"{row['case_name']} ({row.get('case_id', 'N/A')})", row.get('case_id')) for _, row in df.iterrows()]
496
+ return df, summary, gr.Dropdown(choices=case_choices)
 
 
 
 
 
 
 
497
  except Exception as e:
498
  return pd.DataFrame(), f"❌ Помилка аналітики: {e}", []
499
 
500
  def create_enhanced_gradio_interface():
 
 
501
  interface = EnhancedMAIDXInterface()
502
+ custom_css = """.gradio-container {font-family: 'Inter', sans-serif;} .main-header {background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 2rem; border-radius: 15px; margin-bottom: 2rem; text-align: center;} .conversation-log {font-family: 'Fira Code', monospace; background: #2d3748; color: #e2e8f0; border-radius: 8px; padding: 1rem; max-height: 500px; overflow-y: auto; white-space: pre-wrap;}"""
503
 
504
+ with gr.Blocks(title="🏥 MAI-DX Enhanced Research Platform", theme=gr.themes.Soft(primary_hue="blue"), css=custom_css) as demo:
505
+ gr.HTML("""<div class="main-header"><h1>🏥 MAI-DX Enhanced Research Platform</h1><p>🤖 Дослідницька платформа для ШІ-діагностики з детальним логуванням</p></div>""")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
506
 
507
+ with gr.Tabs():
508
+ with gr.Tab("🩺 Діагностика", elem_id="diagnosis-tab"):
 
 
 
509
  with gr.Row():
 
510
  with gr.Column(scale=1):
511
  gr.Markdown("### 📝 Клінічний випадок")
512
+ case_name = gr.Textbox(label="🏷️ Назва випадку", placeholder="Наприклад: Кардіологічний випадок №1")
513
+ sample_selector = gr.Dropdown(choices=list(interface.sample_cases.keys()), label="🎯 Готові тестові випадки", interactive=True)
514
+ patient_info = gr.Textbox(label="👤 Інформація про пацієнта", lines=10, placeholder="Введіть опис клінічного випадку...")
515
+ expected_diagnosis = gr.Textbox(label="🎯 Очікуваний діагноз (для порівняння)", lines=2)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
516
 
 
517
  with gr.Column(scale=1):
518
+ gr.Markdown("### ⚙️ Налаштування")
519
+ mode = gr.Radio(choices=["instant", "question_only", "budgeted", "no_budget", "ensemble"], label="🔧 Режим", value="budgeted", interactive=True)
520
+ budget = gr.Slider(minimum=500, maximum=10000, step=500, value=3000, label="💵 Бюджет ($)", interactive=True)
521
+ max_iterations = gr.Slider(minimum=1, maximum=15, step=1, value=8, label="🔄 Макс. ітерацій", interactive=True)
522
+ model_name = gr.Dropdown(choices=["gemini/gemini-1.5-flash", "gpt-4", "gpt-4-turbo"], label="🤖 LLM Модель", value="gemini/gemini-1.5-flash", interactive=True)
523
+ enable_logging = gr.Checkbox(label="📝 Детальне логування", value=True, interactive=True)
524
+ diagnose_btn = gr.Button("🚀 Запустити діагностику", variant="primary", size="lg")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
525
 
526
+ sample_selector.change(interface.load_sample_case, inputs=[sample_selector], outputs=[patient_info, case_name, expected_diagnosis])
 
 
527
 
528
+ gr.Markdown("--- \n ## 📊 Результати діагностики")
529
  with gr.Row():
530
  with gr.Column(scale=2):
531
  main_result = gr.Markdown(label="🎯 Основний результат")
532
  detailed_analysis = gr.Markdown(label="🔬 Детальний аналіз")
 
533
  with gr.Column(scale=1):
534
  recommendations = gr.Markdown(label="💡 Рекомендації")
535
 
536
+ with gr.Row():
537
+ metrics_plot = gr.Plot(label="📈 Метрики діагностики")
538
+ agent_activity_plot = gr.Plot(label="🤖 Активність агентів")
 
 
 
 
 
 
 
 
539
 
540
+ conversation_logs = gr.Textbox(label="💬 Логи взаємодії ШІ-агентів", lines=12, elem_classes=["conversation-log"], interactive=False, show_copy_button=True)
 
 
 
 
 
 
 
 
541
 
542
+ diagnose_btn.click(interface.diagnose_with_enhanced_tracking,
543
+ inputs=[case_name, patient_info, mode, budget, max_iterations, model_name, expected_diagnosis, enable_logging],
544
+ outputs=[main_result, detailed_analysis, recommendations, metrics_plot, agent_activity_plot, conversation_logs])
 
 
 
 
 
 
 
 
 
545
 
546
+ with gr.Tab("📊 Аналітика", elem_id="analytics-tab"):
 
547
  gr.Markdown("### 🔍 Аналіз збережених діагностичних сесій")
 
548
  with gr.Row():
549
+ refresh_analytics_btn = gr.Button("🔄 Оновити")
550
+ export_data_btn = gr.Button("📤 Експортувати CSV")
551
+ analytics_table = gr.Dataframe(label="📋 Історія сесій", interactive=False, wrap=True)
552
+ analytics_summary = gr.Markdown(label="📊 Статистичний звіт")
553
 
554
+ gr.Markdown("### 🔬 Детальний звіт по випадку")
555
  with gr.Row():
556
+ case_selector = gr.Dropdown(label="🗂️ Оберіть випадок", choices=[], interactive=True)
557
+ analyze_case_btn = gr.Button("📋 Створити звіт (HTML)")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
558
  analysis_status = gr.Markdown()
559
 
 
560
  def refresh_analytics():
561
+ df, summary, dd = interface.get_enhanced_analytics()
562
+ return df, summary, dd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
563
 
564
+ refresh_analytics_btn.click(refresh_analytics, outputs=[analytics_table, analytics_summary, case_selector])
565
+ export_data_btn.click(lambda: interface.conversation_logger.export_analytics_csv(), outputs=[analysis_status])
566
+ analyze_case_btn.click(lambda case_id: f"✅ Звіт створено: {interface.conversation_logger.export_conversation_report(case_id, 'html')}" if case_id else "⚠️ Оберіть випадок", inputs=[case_selector], outputs=[analysis_status])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
567
  demo.load(refresh_analytics, outputs=[analytics_table, analytics_summary, case_selector])
568
 
569
+ with gr.Tab("📚 Документація", elem_id="docs-tab"):
570
+ gr.Markdown(f"""
571
+ ### 🔧 Статус системи
572
+ - **MAI-DX доступність**: {'✅' if MAI_DX_AVAILABLE else '❌'}
573
+ - **Logger доступність**: {'✅' if LOGGER_AVAILABLE else '⚠️ Заглушка'}
574
+ - **Plotly візуалізації**: {'✅' if PLOTLY_AVAILABLE else '❌'}
575
+ - **Gradio версія**: {gr.__version__}
576
+ {'**Помилка MAI-DX**: ' + IMPORT_ERROR if not MAI_DX_AVAILABLE else ''}
 
 
 
 
 
 
 
 
 
 
 
 
577
  ### 📖 Короткий гайд
578
+ 1. Оберіть готовий випадок або введіть свій.
579
+ 2. Налаштуйте режим, бюджет та модель.
580
+ 3. Натисніть "Запустити діагностику".
581
+ 4. Аналізуйте результати та логи взаємодії агентів.
582
+ 5. Переходьте на вкладку "Аналітика" для перегляду історії.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
583
  """)
584
 
585
  return demo
 
596
  demo.launch(
597
  server_name="0.0.0.0",
598
  server_port=7860,
599
+ share=True,
600
+ debug=False
 
 
 
601
  )