Spaces:
Sleeping
Sleeping
#!/usr/bin/env python3 | |
""" | |
Виправлений MAI-DX Gradio Interface з сумісністю для нових версій Gradio | |
""" | |
import os | |
import sys | |
import json | |
import time | |
import pandas as pd | |
import gradio as gr | |
from datetime import datetime, timedelta | |
from typing import Dict, List, Tuple, Optional | |
from dataclasses import dataclass, asdict | |
import warnings | |
import threading | |
import queue | |
import io | |
from contextlib import redirect_stdout, redirect_stderr | |
# Налаштування середовища | |
os.environ.update({ | |
"SWARMS_VERBOSITY": "ERROR", | |
"RICH_TRACEBACK": "0", | |
"SWARMS_SHOW_PANEL": "false", | |
"SWARMS_AUTO_PRINT": "false" | |
}) | |
warnings.filterwarnings("ignore") | |
from dotenv import load_dotenv | |
load_dotenv() | |
# Патч Rich formatter | |
def patch_rich_formatter(): | |
try: | |
import swarms.utils.formatter | |
def dummy_print_panel(*args, **kwargs): | |
pass | |
if hasattr(swarms.utils.formatter, 'Formatter'): | |
swarms.utils.formatter.Formatter._print_panel = dummy_print_panel | |
return True | |
except: | |
return False | |
patch_rich_formatter() | |
# Імпорт MAI-DX | |
try: | |
from mai_dx import MaiDxOrchestrator | |
MAI_DX_AVAILABLE = True | |
except ImportError as e: | |
MAI_DX_AVAILABLE = False | |
IMPORT_ERROR = str(e) | |
# Перевірка доступності Plotly | |
try: | |
import plotly.graph_objects as go | |
import plotly.express as px | |
PLOTLY_AVAILABLE = True | |
except ImportError: | |
PLOTLY_AVAILABLE = False | |
print("⚠️ Plotly не встановлено, візуалізації будуть недоступні") | |
# Імпорт логгера (створюємо заглушку якщо немає) | |
try: | |
from agent_conversation_logger import AgentConversationLogger, DiagnosisSession | |
LOGGER_AVAILABLE = True | |
except ImportError: | |
LOGGER_AVAILABLE = False | |
class DiagnosisSession: | |
timestamp: str | |
case_name: str | |
patient_info: str | |
mode: str | |
budget: int | |
diagnosis: str | |
confidence: float | |
cost: float | |
iterations: int | |
duration: float | |
status: str | |
reasoning: str | |
class AgentConversationLogger: | |
def __init__(self, *args, **kwargs): | |
self.conversations = [] | |
def start_conversation(self, *args): | |
return f"mock_{len(self.conversations)}" | |
def log_agent_message(self, *args, **kwargs): | |
pass | |
def end_conversation(self, *args): | |
return f"saved_{len(self.conversations)}" | |
def list_conversations(self): | |
return self.conversations | |
def get_conversation_summary(self): | |
return {"total_conversations": 0, "average_accuracy": 0, "average_cost": 0, "mode_distribution": {}} | |
def export_conversation_report(self, case_id, format): | |
return f"report_{case_id}.{format}" | |
def export_analytics_csv(self): | |
return "analytics.csv" | |
class RealTimeMetrics: | |
"""Метрики в реальному часі""" | |
def __init__(self): | |
self.reset() | |
def reset(self): | |
self.start_time = time.time() | |
self.agents_activity = { | |
'Dr. Hypothesis': 0, | |
'Dr. Test-Chooser': 0, | |
'Dr. Challenger': 0, | |
'Dr. Stewardship': 0, | |
'Dr. Checklist': 0, | |
'Consensus Coordinator': 0, | |
'Gatekeeper': 0, | |
'Judge': 0 | |
} | |
self.cost_progression = [] | |
self.confidence_progression = [] | |
self.decision_timeline = [] | |
def update_agent_activity(self, agent_name: str): | |
if agent_name in self.agents_activity: | |
self.agents_activity[agent_name] += 1 | |
def add_cost_point(self, cost: float): | |
self.cost_progression.append({ | |
'time': time.time() - self.start_time, | |
'cost': cost | |
}) | |
def add_confidence_point(self, confidence: float): | |
self.confidence_progression.append({ | |
'time': time.time() - self.start_time, | |
'confidence': confidence | |
}) | |
class EnhancedMAIDXInterface: | |
"""Покращений інтерфейс з сумісністю для Gradio""" | |
def __init__(self): | |
self.sessions_history = [] | |
self.conversation_logger = AgentConversationLogger("mai_dx_logs") | |
self.current_metrics = RealTimeMetrics() | |
# Розширені тестові кейси | |
self.sample_cases = { | |
"🫀 Кардіологічний (Гострий MI)": { | |
"info": """Пацієнт: 58-річний чоловік, менеджер, гіпертонія в анамнезі | |
Скарги: Гострий роздираючий біль у грудях 3 години, іррадіація в ліву руку | |
Огляд: Блідий, пітливий, АТ 160/90, ЧСС 95 | |
ЕКГ: ST-підйоми у відведеннях II, III, aVF (нижня стінка) | |
Тропонін I: 8.5 нг/мл (норма <0.04) | |
Анамнез: Куріння 30 років, дислипідемія, сімейний анамнез ІХС""", | |
"expected": "Acute inferior myocardial infarction (STEMI)" | |
}, | |
"🧠 Неврологічний (Гострий інсульт)": { | |
"info": """Пацієнтка: 67-річна жінка, раптові неврологічні симптоми | |
Презентація: Раптова слабкість правої сторони 2 години тому | |
Огляд: Свідома, дезорієнтована у часі, правостороннє опущення обличчя | |
Неврологія: Правостороння геміплегія, афазія, девіація очей вліво | |
КТ голови: Гострого крововиливу немає, рання ішемія у лівій МСА | |
NIHSS: 15 балів""", | |
"expected": "Acute ischemic stroke in the left middle cerebral artery basin" | |
}, | |
"🦠 Інфекційний (Сепсис)": { | |
"info": """Пацієнт: 45-річний чоловік з прогресуючою лихоманкою | |
Скарги: Висока температура 39.5°C, озноб, загальна слабкість 3 дні | |
Огляд: Гарячий, тахікардія 120/хв, гіпотензія 85/50 | |
Лабораторно: Лейкоцити 18000, С-реактивний білок 180, прокальцитонін 5.2 | |
Посів крові: Pending, lactate 4.2 ммоль/л | |
Анамнез: Нещодавна стоматологічна процедура""", | |
"expected": "Sepsis with a possible odontogenic source" | |
} | |
} | |
def diagnose_with_enhanced_tracking( | |
self, | |
case_name: str, | |
patient_info: str, | |
mode: str, | |
budget: int, | |
max_iterations: int, | |
model_name: str, | |
expected_diagnosis: str = "", | |
enable_logging: bool = True, | |
progress=gr.Progress() | |
) -> Tuple[str, str, str, Optional[object], Optional[object], str]: | |
"""Діагностика з покращеним відстеженням - ВИПРАВЛЕНА ВЕРСІЯ""" | |
if not MAI_DX_AVAILABLE: | |
return self._format_error(f"❌ MAI-DX недоступний: {IMPORT_ERROR}") | |
if not patient_info.strip(): | |
return self._format_error("❌ Введіть інформацію про пацієнта") | |
# Скидання метрик | |
self.current_metrics.reset() | |
conversation_log = "" | |
case_id = None | |
try: | |
progress(0.1, desc="🚀 Ініціалізація системи...") | |
# Логування початку | |
if enable_logging: | |
case_id = self.conversation_logger.start_conversation( | |
case_name or f"Case_{datetime.now().strftime('%H%M%S')}", | |
patient_info, | |
mode | |
) | |
conversation_log += f"📝 Розпочато логування: {case_id}\n\n" | |
progress(0.2, desc="🤖 Створення AI-панелі лікарів...") | |
# Створення оркестратора | |
orchestrator = MaiDxOrchestrator( | |
model_name=model_name, | |
max_iterations=max_iterations, | |
initial_budget=budget, | |
mode=mode if mode != "ensemble" else "budgeted" | |
) | |
progress(0.3, desc="🔍 Запуск діагностичного процесу...") | |
start_time = time.time() | |
# Симуляція прогресу з реальними етапами | |
diagnostic_stages = [ | |
"🧠 Dr. Hypothesis аналізує симптоми...", | |
"🔬 Dr. Test-Chooser обирає тести...", | |
"🤔 Dr. Challenger перевіряє гіпотези...", | |
"💰 Dr. Stewardship оцінює вартість...", | |
"✅ Dr. Checklist контролює якість...", | |
"🤝 Consensus Coordinator формує рішення..." | |
] | |
for i, stage in enumerate(diagnostic_stages): | |
progress(0.3 + (i * 0.1), desc=stage) | |
time.sleep(0.3) # Короткша затримка | |
progress(0.8, desc="🎯 Формування діагнозу...") | |
# Запуск діагностики з перехопленням виводу | |
with io.StringIO() as captured_output: | |
try: | |
original_stdout = sys.stdout | |
sys.stdout = captured_output | |
if mode == "ensemble": | |
try: | |
result = orchestrator.run_ensemble( | |
initial_case_info=patient_info, | |
full_case_details=patient_info, | |
ground_truth_diagnosis=expected_diagnosis or "Unknown", | |
num_runs=2 | |
) | |
except AttributeError: | |
result = orchestrator.run( | |
initial_case_info=patient_info, | |
full_case_details=patient_info, | |
ground_truth_diagnosis=expected_diagnosis or "Unknown" | |
) | |
else: | |
result = orchestrator.run( | |
initial_case_info=patient_info, | |
full_case_details=patient_info, | |
ground_truth_diagnosis=expected_diagnosis or "Unknown" | |
) | |
finally: | |
sys.stdout = original_stdout | |
captured_text = captured_output.getvalue() | |
duration = time.time() - start_time | |
progress(0.9, desc="📊 Обробка результатів...") | |
# Парсинг логів | |
if enable_logging and captured_text: | |
conversation_log += "🤖 Захоплені логи агентів:\n" | |
conversation_log += "=" * 60 + "\n" | |
conversation_log += captured_text + "\n" | |
conversation_log += "=" * 60 + "\n\n" | |
# Оновлення метрик | |
self._parse_logs_for_metrics(captured_text) | |
# Логування результату | |
if enable_logging: | |
self.conversation_logger.log_agent_message( | |
"Judge", | |
"final_evaluation", | |
result.final_diagnosis, | |
getattr(result, 'accuracy_reasoning', 'Фінальна оцінка'), | |
result.accuracy_score / 5.0, | |
result.total_cost, | |
max_iterations | |
) | |
saved_case_id = self.conversation_logger.end_conversation( | |
result.final_diagnosis, | |
result.accuracy_score, | |
result.total_cost | |
) | |
conversation_log += f"💾 Збережено як: {saved_case_id}\n" | |
# Створення сесії | |
# Виправлений код | |
session = DiagnosisSession( | |
case_id=case_id, # <--- ДОДАЙТЕ ЦЕЙ РЯДОК | |
timestamp=datetime.now().strftime("%Y-%m-%d %H:%M:%S"), | |
case_name=case_name or f"Case_{len(self.sessions_history) + 1}", | |
patient_info=patient_info[:200] + "..." if len(patient_info) > 200 else patient_info, | |
mode=mode, | |
budget=budget, | |
diagnosis=result.final_diagnosis, | |
confidence=result.accuracy_score, | |
cost=result.total_cost, | |
iterations=getattr(result, 'iterations', max_iterations), | |
duration=duration, | |
status="✅ Успішно" if result.accuracy_score >= 3.0 else "⚠️ Потребує перегляду", | |
reasoning=getattr(result, 'accuracy_reasoning', 'Недоступно')[:300] + "..." | |
) | |
self.sessions_history.append(session) | |
progress(1.0, desc="✅ Готово!") | |
# Генерація візуалізацій (безпечно) | |
metrics_plot = self._create_metrics_visualization_safe() | |
agent_plot = self._create_agent_activity_chart_safe() | |
# Генерація всіх результатів | |
return ( | |
self._format_main_result(session, result), | |
self._format_detailed_analysis(session, result), | |
self._generate_enhanced_recommendations(result, expected_diagnosis), | |
metrics_plot, | |
agent_plot, | |
conversation_log | |
) | |
except Exception as e: | |
error_msg = f"❌ Помилка діагностики: {str(e)}" | |
if case_id: | |
error_msg += f"\n🗂️ Case ID: {case_id}" | |
return self._format_error(error_msg) | |
def _create_metrics_visualization_safe(self): | |
"""Безпечне створення візуалізації метрик""" | |
try: | |
if not PLOTLY_AVAILABLE: | |
return None | |
if not self.current_metrics.cost_progression: | |
# Створюємо демо-графік | |
fig = go.Figure() | |
fig.add_trace(go.Scatter( | |
x=[0, 1, 2, 3], | |
y=[0, 300, 600, 900], | |
mode='lines+markers', | |
name='Демо-дані', | |
line=dict(color='#1f77b4', width=3) | |
)) | |
fig.update_layout( | |
title='📈 Прогресія вартості (демо)', | |
xaxis_title='Час (секунди)', | |
yaxis_title='Вартість ($)', | |
template='plotly_white', | |
height=400 | |
) | |
return fig | |
# Реальні дані | |
times = [point['time'] for point in self.current_metrics.cost_progression] | |
costs = [point['cost'] for point in self.current_metrics.cost_progression] | |
fig = go.Figure() | |
fig.add_trace(go.Scatter( | |
x=times, | |
y=costs, | |
mode='lines+markers', | |
name='Накопичена вартість', | |
line=dict(color='#1f77b4', width=3) | |
)) | |
fig.update_layout( | |
title='📈 Прогресія вартості діагностики', | |
xaxis_title='Час (секунди)', | |
yaxis_title='Вартість ($)', | |
template='plotly_white', | |
height=400 | |
) | |
return fig | |
except Exception as e: | |
print(f"Помилка візуалізації метрик: {e}") | |
return None | |
def _create_agent_activity_chart_safe(self): | |
"""Безпечне створення діаграми активності агентів""" | |
try: | |
if not PLOTLY_AVAILABLE: | |
return None | |
# Підготовка даних | |
agents = list(self.current_metrics.agents_activity.keys()) | |
activities = list(self.current_metrics.agents_activity.values()) | |
# Якщо немає активності, створюємо демо | |
if all(activity == 0 for activity in activities): | |
activities = [1, 2, 1, 1, 1, 2, 3, 1] # Демо-дані | |
# Кольори для агентів | |
colors = [ | |
'#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', | |
'#FECA57', '#FF9FF3', '#54A0FF', '#5F27CD' | |
] | |
fig = go.Figure(data=[ | |
go.Bar( | |
x=agents, | |
y=activities, | |
marker_color=colors[:len(agents)], | |
text=activities, | |
textposition='auto' | |
) | |
]) | |
fig.update_layout( | |
title='🤖 Активність ШІ-агентів лікарів', | |
xaxis_title='Агенти', | |
yaxis_title='Кількість взаємодій', | |
template='plotly_white', | |
height=400, | |
showlegend=False | |
) | |
fig.update_xaxes(tickangle=45) | |
return fig | |
except Exception as e: | |
print(f"Помилка графіку агентів: {e}") | |
return None | |
def _parse_logs_for_metrics(self, captured_text: str): | |
"""Парсинг логів для оновлення метрик""" | |
lines = captured_text.split('\n') | |
for line in lines: | |
line = line.strip() | |
if not line: | |
continue | |
# Пошук активності агентів | |
for agent in self.current_metrics.agents_activity.keys(): | |
if agent.lower().replace(" ", "").replace(".", "") in line.lower(): | |
self.current_metrics.update_agent_activity(agent) | |
break | |
# Пошук вартості | |
import re | |
cost_match = re.search(r'\$(\d+(?:\.\d+)?)', line) | |
if cost_match: | |
self.current_metrics.add_cost_point(float(cost_match.group(1))) | |
def _format_main_result(self, session, result): | |
"""Форматування основного результату""" | |
confidence_emoji = "🎯" if session.confidence >= 4.0 else "👍" if session.confidence >= 3.0 else "⚠️" | |
efficiency = ((session.budget - session.cost) / session.budget * 100) | |
return f""" | |
## 🏥 Результати MAI-DX Діагностики | |
### 📋 Основна інформація | |
- **🗂️ Випадок**: {session.case_name} | |
- **⏰ Час**: {session.timestamp} | |
- **🔧 Режим**: {session.mode} | |
- **🤖 Модель**: AI Multi-Agent | |
### {confidence_emoji} Діагностичний висновок | |
**{session.diagnosis}** | |
### 📊 Показники якості | |
- **Точність**: {session.confidence:.1f}/5.0 ⭐ | |
- **Статус**: {session.status} | |
- **Ітерації**: {session.iterations} циклів | |
### 💰 Економічні показники | |
- **Витрачено**: ${session.cost:,.2f} | |
- **Бюджет**: ${session.budget:,} | |
- **Ефективність**: {efficiency:.1f}% бюджету збережено | |
- **Швидкість**: {session.duration:.1f} секунд | |
### 🎖️ Загальна оцінка | |
{self._get_overall_rating(session)} | |
""" | |
def _get_overall_rating(self, session): | |
"""Загальна оцінка ефективності""" | |
efficiency = ((session.budget - session.cost) / session.budget * 100) | |
if session.confidence >= 4.0 and efficiency >= 50: | |
return "🏆 **ВІДМІННО** - Високоточний та економічний діагноз" | |
elif session.confidence >= 3.0 and efficiency >= 30: | |
return "🥈 **ДОБРЕ** - Надійний діагноз з прийнятною вартістю" | |
elif session.confidence >= 2.0: | |
return "🥉 **ЗАДОВІЛЬНО** - Потребує додаткової верифікації" | |
else: | |
return "❌ **ПОТРЕБУЄ ПЕРЕГЛЯДУ** - Низька впевненість" | |
def _format_detailed_analysis(self, session, result): | |
"""Детальний аналіз""" | |
return f""" | |
## 🔬 Детальний клінічний аналіз | |
### 💭 Медичне обґрунтування | |
{session.reasoning} | |
### 📈 Аналіз ефективності | |
- **⚡ Швидкість діагностики**: {session.duration:.1f} сек | |
- **💸 Економічна ефективність**: {((session.budget - session.cost) / session.budget * 100):.1f}% | |
- **🔄 Ітеративна конвергенція**: {session.iterations} циклів | |
- **🎯 Клінічна точність**: {(session.confidence / 5.0 * 100):.1f}% | |
### 🤖 Процес прийняття рішень | |
Система MAI-DX використала **{session.iterations} ітерацій** для досягнення консенсусу між **8 ШІ-агентами лікарів**. | |
### 📊 Порівняння з бенчмарками | |
- **Людські лікарі**: ~20% точність на NEJM CPC | |
- **GPT-4**: ~49% точність | |
- **MAI-DX**: ~85.5% точність ✨ | |
""" | |
def _generate_enhanced_recommendations(self, result, expected_diagnosis): | |
"""Покращені рекомендації""" | |
comparison = "" | |
if expected_diagnosis: | |
comparison = f""" | |
### 🎯 Порівняння з очікуваним діагнозом | |
**Очікувався**: {expected_diagnosis} | |
**Отримано**: {result.final_diagnosis} | |
**Збіг**: {'✅ Так' if expected_diagnosis.lower() in result.final_diagnosis.lower() else '❌ Ні'} | |
""" | |
return f""" | |
## 💡 Клінічні рекомендації | |
### 🏥 Негайні дії | |
- 🔍 **Верифікація діагнозу** з лікарем-спеціалістом | |
- 📋 **Додаткові дослідження** при необхідності | |
- 👨⚕️ **Консультація експерта** для підтвердження | |
### 🔬 Дослідницький потенціал | |
- **📊 Аналіз логів**: Вивчіть деталі роботи агентів | |
- **📈 Валідація**: Перевірте на додаткових випадках | |
- **🤖 Тюнінг моделі**: Оптимізуйте параметри | |
{comparison} | |
### ⚠️ Важливе застереження | |
🔴 **Цей діагноз згенеровано ШІ для дослідницьких цілей та НЕ замінює професійну медичну консультацію.** | |
""" | |
def _format_error(self, error_msg): | |
"""Форматування помилки""" | |
return (error_msg, "", "", None, None, "") | |
def load_sample_case(self, sample_key): | |
"""Завантаження зразкового випадку""" | |
if sample_key and sample_key in self.sample_cases: | |
case_data = self.sample_cases[sample_key] | |
return ( | |
case_data["info"], | |
sample_key, | |
case_data["expected"] | |
) | |
return "", "", "" | |
def get_enhanced_analytics(self): | |
"""Розширена аналітика - ВИПРАВЛЕНА ВЕРСІЯ""" | |
try: | |
conversations = self.conversation_logger.list_conversations() | |
if not conversations: | |
empty_df = pd.DataFrame() | |
return empty_df, "📊 Немає даних для аналізу", [] | |
# Створення DataFrame | |
df = pd.DataFrame(conversations) | |
# Статистика | |
total_cases = len(conversations) | |
avg_accuracy = df['accuracy'].mean() if 'accuracy' in df.columns else 0 | |
avg_cost = df['cost'].mean() if 'cost' in df.columns else 0 | |
summary = f""" | |
## 📊 Розширена аналітика MAI-DX | |
### 🎯 Загальна статистика | |
- **Всього випадків**: {total_cases} | |
- **Середня точність**: {avg_accuracy:.2f}/5.0 ⭐ | |
- **Середня вартість**: ${avg_cost:,.2f} | |
- **Загальна економія**: ${(3000 - avg_cost) * total_cases:,.2f} | |
""" | |
# Створення списку для dropdown | |
case_choices = [] | |
if not df.empty and 'case_name' in df.columns: | |
case_choices = [(f"{row['case_name']} ({row.get('case_id', 'N/A')})", | |
row.get('case_id', f"case_{i}")) | |
for i, (_, row) in enumerate(df.iterrows())] | |
return df, summary, case_choices | |
except Exception as e: | |
return pd.DataFrame(), f"❌ Помилка аналітики: {e}", [] | |
def create_enhanced_gradio_interface(): | |
"""Створення виправленого Gradio інтерфейсу""" | |
interface = EnhancedMAIDXInterface() | |
# Сучасний CSS | |
custom_css = """ | |
.gradio-container { | |
font-family: 'Inter', system-ui, -apple-system, 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; | |
} | |
""" | |
with gr.Blocks( | |
title="🏥 MAI-DX Enhanced Research Platform", | |
theme=gr.themes.Soft( | |
primary_hue="blue", | |
secondary_hue="purple", | |
neutral_hue="slate" | |
), | |
css=custom_css | |
) as demo: | |
# Заголовок | |
gr.HTML(""" | |
<div class="main-header"> | |
<h1>🏥 MAI-DX Enhanced Research Platform</h1> | |
<p>🤖 Платформа для дослідження ШІ-діагностики з детальним логуванням взаємодії агентів</p> | |
<p>📊 Базується на дослідженні Microsoft Research "Sequential Diagnosis with Language Models"</p> | |
</div> | |
""") | |
with gr.Tabs() as tabs: | |
# Основна вкладка діагностики | |
with gr.Tab("🩺 Діагностика з ШІ-агентами", elem_id="diagnosis-tab"): | |
with gr.Row(): | |
# Ліва колонка - введення даних | |
with gr.Column(scale=1): | |
gr.Markdown("### 📝 Клінічний випадок") | |
case_name = gr.Textbox( | |
label="🏷️ Назва випадку", | |
placeholder="Наприклад: Кардіологічний випадок №1", | |
lines=1 | |
) | |
sample_selector = gr.Dropdown( | |
choices=list(interface.sample_cases.keys()), | |
label="🎯 Готові тестові випадки", | |
value=None, | |
interactive=True | |
) | |
patient_info = gr.Textbox( | |
label="👤 Детальна інформація про пацієнта", | |
lines=12, | |
placeholder="Введіть повний опис клінічного випадку:\n- Демографія пацієнта\n- Скарги та анамнез\n- Фізикальне обстеження\n- Лабораторні дані\n- Інструментальні дослідження", | |
interactive=True | |
) | |
expected_diagnosis = gr.Textbox( | |
label="🎯 Очікуваний діагноз (для порівняння)", | |
placeholder="Опціонально: введіть правильний діагноз для оцінки точності", | |
lines=2 | |
) | |
# Права колонка - налаштування | |
with gr.Column(scale=1): | |
gr.Markdown("### ⚙️ Налаштування діагностики") | |
mode_choices_map = { | |
"⚡ Миттєвий (найшвидший, базовий аналіз)": "instant", | |
"❓ Тільки питання (швидко, без тестів)": "question_only", | |
"💰 З бюджетом (збалансовано)": "budgeted", | |
"🔓 Без обмежень (повний аналіз)": "no_budget", | |
"👥 Консенсус (найточніший, повільний)": "ensemble" | |
} | |
# Новий, виправлений код | |
mode = gr.Radio( | |
choices=[ | |
"instant", | |
"question_only", | |
"budgeted", | |
"no_budget", | |
"ensemble" | |
], | |
label="🔧 Режим діагностики", | |
value="budgeted", | |
interactive=True | |
) | |
budget = gr.Slider( | |
minimum=500, | |
maximum=10000, | |
step=500, | |
value=3000, | |
label="💵 Бюджет діагностики ($)", | |
interactive=True | |
) | |
max_iterations = gr.Slider( | |
minimum=1, | |
maximum=15, | |
step=1, | |
value=8, | |
label="🔄 Максимум ітерацій агентів", | |
interactive=True | |
) | |
model_name = gr.Dropdown( | |
choices=[ | |
"gemini/gemini-2.5-flash", | |
"gpt-4", | |
"gpt-4-turbo", | |
"claude-3-5-sonnet", | |
"grok-beta" | |
], | |
label="🤖 LLM Модель", | |
value="gemini/gemini-2.5-flash", | |
interactive=True | |
) | |
enable_logging = gr.Checkbox( | |
label="📝 Детальне логування взаємодії агентів", | |
value=True, | |
interactive=True | |
) | |
gr.Markdown("---") | |
diagnose_btn = gr.Button( | |
"🚀 Запустити діагностику", | |
variant="primary", | |
size="lg", | |
interactive=True | |
) | |
# Завантаження зразків | |
sample_selector.change( | |
interface.load_sample_case, | |
inputs=[sample_selector], | |
outputs=[patient_info, case_name, expected_diagnosis] | |
) | |
# Результати діагностики | |
gr.Markdown("---") | |
gr.Markdown("## 📊 Результати діагностики") | |
with gr.Row(): | |
with gr.Column(scale=2): | |
main_result = gr.Markdown(label="🎯 Основний результат") | |
detailed_analysis = gr.Markdown(label="🔬 Детальний аналіз") | |
with gr.Column(scale=1): | |
recommendations = gr.Markdown(label="💡 Рекомендації") | |
# Візуалізації (якщо Plotly доступний) | |
if PLOTLY_AVAILABLE: | |
with gr.Row(): | |
with gr.Column(): | |
metrics_plot = gr.Plot(label="📈 Метрики діагностики") | |
with gr.Column(): | |
agent_activity_plot = gr.Plot(label="🤖 Активність агентів") | |
else: | |
gr.Markdown("ℹ️ Візуалізації недоступні (Plotly не встановлено)") | |
metrics_plot = gr.Textbox(visible=False) | |
agent_activity_plot = gr.Textbox(visible=False) | |
# Логи бесіди | |
gr.Markdown("### 💬 Логи взаємодії ШІ-агентів") | |
conversation_logs = gr.Textbox( | |
label="Детальні логи бесід між агентами", | |
lines=12, | |
elem_classes=["conversation-log"], | |
interactive=False, | |
show_copy_button=True | |
) | |
# Запуск діагностики | |
diagnose_btn.click( | |
interface.diagnose_with_enhanced_tracking, | |
inputs=[ | |
case_name, patient_info, mode, budget, max_iterations, | |
model_name, expected_diagnosis, enable_logging | |
], | |
outputs=[ | |
main_result, detailed_analysis, recommendations, | |
metrics_plot, agent_activity_plot, conversation_logs | |
] | |
) | |
# Вкладка аналітики | |
with gr.Tab("📊 Розширена аналітика", elem_id="analytics-tab"): | |
gr.Markdown("### 🔍 Аналіз збережених діагностичних сесій") | |
with gr.Row(): | |
refresh_analytics_btn = gr.Button("🔄 Оновити дані", variant="secondary") | |
export_data_btn = gr.Button("📤 Експортувати аналітику", variant="secondary") | |
with gr.Row(): | |
with gr.Column(scale=2): | |
analytics_table = gr.Dataframe( | |
label="📋 Історія діагностичних сесій", | |
interactive=False, | |
wrap=True | |
) | |
with gr.Column(scale=1): | |
analytics_summary = gr.Markdown(label="📊 Статистичний звіт") | |
# Детальний аналіз окремих випадків | |
gr.Markdown("### 🔬 Поглиблений аналіз випадків") | |
with gr.Row(): | |
case_selector = gr.Dropdown( | |
label="🗂️ Оберіть випадок для детального аналізу", | |
choices=[], | |
interactive=True | |
) | |
analyze_case_btn = gr.Button("📋 Створити детальний звіт") | |
analysis_status = gr.Markdown() | |
# Функції аналітики - ВИПРАВЛЕНІ | |
def refresh_analytics(): | |
df, summary, case_choices = interface.get_enhanced_analytics() | |
return df, summary, gr.Dropdown(choices=case_choices) | |
def export_analytics(): | |
try: | |
filename = interface.conversation_logger.export_analytics_csv() | |
return f"✅ Дані експортовано: {filename}" | |
except Exception as e: | |
return f"❌ Помилка експорту: {e}" | |
def analyze_case(case_id): | |
if not case_id: | |
return "⚠️ Оберіть випадок для аналізу" | |
try: | |
report = interface.conversation_logger.export_conversation_report(case_id, 'html') | |
return f"✅ Детальний звіт створено: {report}" | |
except Exception as e: | |
return f"❌ Помилка аналізу: {e}" | |
# Зв'язування функцій | |
refresh_analytics_btn.click( | |
refresh_analytics, | |
outputs=[analytics_table, analytics_summary, case_selector] | |
) | |
export_data_btn.click( | |
export_analytics, | |
outputs=[analysis_status] | |
) | |
analyze_case_btn.click( | |
analyze_case, | |
inputs=[case_selector], | |
outputs=[analysis_status] | |
) | |
# Автооновлення при завантаженні | |
demo.load(refresh_analytics, outputs=[analytics_table, analytics_summary, case_selector]) | |
# Вкладка документації | |
with gr.Tab("📚 Документація та статус", elem_id="docs-tab"): | |
# Статус системи | |
gr.Markdown("### 🔧 Статус системи") | |
status_info = f""" | |
**MAI-DX доступність**: {'✅ Доступний' if MAI_DX_AVAILABLE else '❌ Недоступний'} | |
**Logger доступність**: {'✅ Доступний' if LOGGER_AVAILABLE else '⚠️ Заглушка'} | |
**Plotly візуалізації**: {'✅ Доступні' if PLOTLY_AVAILABLE else '❌ Недоступні'} | |
**Gradio версія**: {gr.__version__} | |
""" | |
gr.Markdown(status_info) | |
if not MAI_DX_AVAILABLE: | |
gr.Markdown(f"**Помилка MAI-DX**: {IMPORT_ERROR}") | |
# Документація | |
gr.Markdown(""" | |
### 📖 Короткий гайд | |
#### 🚀 Швидкий старт: | |
1. Оберіть готовий тестовий випадок або введіть свій | |
2. Налаштуйте режим діагностики (рекомендується "З бюджетом") | |
3. Натисніть "Запустити діагностику" | |
4. Дочекайтеся результатів та аналізуйте логи | |
#### 🔧 Режими роботи: | |
- **⚡ Миттєвий**: Швидкий базовий аналіз (~5 сек) | |
- **❓ Тільки питання**: Без тестів, тільки опитування | |
- **💰 З бюджетом**: Збалансований підхід (рекомендується) | |
- **🔓 Без обмежень**: Максимальна точність | |
- **👥 Консенсус**: Найвища точність, але повільно | |
#### 📊 Аналітика: | |
- Переглядайте історію діагнозів у вкладці "Аналітика" | |
- Експортуйте дані для подальшого аналізу | |
- Створюйте детальні звіти по окремих випадках | |
#### ⚠️ Важливо: | |
- Система призначена для дослідницьких цілей | |
- Не замінює професійну медичну консультацію | |
- При проблемах перевірте статус системи вище | |
""") | |
return demo | |
if __name__ == "__main__": | |
print("🚀 Запуск виправленого MAI-DX Enhanced Research Platform...") | |
print(f"🤖 MAI-DX доступність: {'✅' if MAI_DX_AVAILABLE else '❌'}") | |
print(f"📊 Plotly візуалізації: {'✅' if PLOTLY_AVAILABLE else '❌'}") | |
print(f"📝 Logger: {'✅' if LOGGER_AVAILABLE else '⚠️ заглушка'}") | |
print("🔍 Логи зберігаються у: mai_dx_logs/") | |
demo = create_enhanced_gradio_interface() | |
demo.launch( | |
server_name="0.0.0.0", | |
server_port=7860, | |
share=False, | |
debug=False, # Відключаємо debug для стабільності | |
show_error=True, | |
favicon_path=None, | |
ssl_verify=False | |
) |