Spaces:
Sleeping
Sleeping
Вимкнено спільний доступ до сервера в run_mai_dx_fixed.py. Оновлено налаштування середовища в updated_mai_dx_interface.py для покращення логування. Внесено зміни в обробку сесій та виводу, включаючи нові методи для формування результатів та аналізу. Виправлено помилки в структурі класів та оновлено документацію.
Browse files- run_mai_dx_fixed.py +1 -1
- updated_mai_dx_interface.py +117 -539
run_mai_dx_fixed.py
CHANGED
@@ -60,7 +60,7 @@ def main():
|
|
60 |
demo.launch(
|
61 |
server_name="0.0.0.0",
|
62 |
server_port=7860,
|
63 |
-
share=
|
64 |
debug=False,
|
65 |
show_error=True,
|
66 |
quiet=False
|
|
|
60 |
demo.launch(
|
61 |
server_name="0.0.0.0",
|
62 |
server_port=7860,
|
63 |
+
share=False,
|
64 |
debug=False,
|
65 |
show_error=True,
|
66 |
quiet=False
|
updated_mai_dx_interface.py
CHANGED
@@ -6,26 +6,23 @@ import os
|
|
6 |
import sys
|
7 |
import json
|
8 |
import time
|
|
|
9 |
import pandas as pd
|
10 |
import gradio as gr
|
11 |
from datetime import datetime
|
12 |
-
from typing import Dict, List, Tuple, Optional
|
13 |
from dataclasses import dataclass, asdict
|
14 |
import warnings
|
15 |
|
16 |
-
# Налаштування середовища
|
17 |
os.environ.update({
|
18 |
-
"SWARMS_VERBOSITY": "ERROR",
|
19 |
-
"
|
20 |
-
"SWARMS_SHOW_PANEL": "false",
|
21 |
-
"SWARMS_AUTO_PRINT": "false"
|
22 |
})
|
23 |
warnings.filterwarnings("ignore")
|
24 |
|
25 |
from dotenv import load_dotenv
|
26 |
load_dotenv()
|
27 |
|
28 |
-
# Імпорт MAI-DX через безпечну обгортку
|
29 |
try:
|
30 |
from mai_dx_wrapper import SafeMaiDxOrchestrator as MaiDxOrchestrator, MAI_DX_AVAILABLE
|
31 |
except ImportError:
|
@@ -36,51 +33,20 @@ except ImportError:
|
|
36 |
MAI_DX_AVAILABLE = False
|
37 |
IMPORT_ERROR = str(e)
|
38 |
|
39 |
-
#
|
40 |
-
from enhanced_mai_dx_logger import MAIDxConversationLogger, DiagnosisSession
|
41 |
|
42 |
-
# Перевірка доступності Plotly
|
43 |
try:
|
44 |
import plotly.graph_objects as go
|
45 |
import plotly.express as px
|
46 |
PLOTLY_AVAILABLE = True
|
47 |
except ImportError:
|
48 |
PLOTLY_AVAILABLE = False
|
49 |
-
print("⚠️ Plotly не встановлено, візуалізації будуть недоступні")
|
50 |
-
|
51 |
-
class RealTimeMetrics:
|
52 |
-
"""Метрики в реальному часі"""
|
53 |
-
def __init__(self):
|
54 |
-
self.reset()
|
55 |
-
|
56 |
-
def reset(self):
|
57 |
-
self.start_time = time.time()
|
58 |
-
self.agents_activity = {
|
59 |
-
'Dr. Hypothesis': 0, 'Dr. Test-Chooser': 0, 'Dr. Challenger': 0,
|
60 |
-
'Dr. Stewardship': 0, 'Dr. Checklist': 0, 'Consensus Coordinator': 0,
|
61 |
-
'Gatekeeper': 0, 'Judge': 0
|
62 |
-
}
|
63 |
-
self.cost_progression = []
|
64 |
-
self.conversation_rounds = []
|
65 |
-
|
66 |
-
def update_agent_activity(self, agent_name: str):
|
67 |
-
if agent_name in self.agents_activity:
|
68 |
-
self.agents_activity[agent_name] += 1
|
69 |
-
|
70 |
-
def add_cost_point(self, cost: float):
|
71 |
-
self.cost_progression.append({'time': time.time() - self.start_time, 'cost': cost})
|
72 |
-
|
73 |
-
def add_conversation_round(self, round_data: dict):
|
74 |
-
self.conversation_rounds.append(round_data)
|
75 |
|
76 |
class UpdatedMAIDXInterface:
|
77 |
-
"""Оновлений інтерфейс з повним логуванням розмов"""
|
78 |
-
|
79 |
def __init__(self):
|
80 |
self.sessions_history = []
|
81 |
self.conversation_logger = MAIDxConversationLogger("mai_dx_logs")
|
82 |
-
self.current_metrics = RealTimeMetrics()
|
83 |
-
|
84 |
self.sample_cases = {
|
85 |
"🫀 Кардіологічний (Гострий MI)": {
|
86 |
"info": "Пацієнт: 58-річний чоловік, менеджер, гіпертонія в анамнезі\nСкарги: Гострий роздираючий біль у грудях 3 години, іррадіація в ліву руку\nОгляд: Блідий, пітливий, АТ 160/90, ЧСС 95\nЕКГ: ST-підйоми у відведеннях II, III, aVF (нижня стінка)\nТропонін I: 8.5 нг/мл (норма <0.04)\nАнамнез: Куріння 30 років, дислипідемія, сімейний анамнез ІХС",
|
@@ -101,127 +67,71 @@ class UpdatedMAIDXInterface:
|
|
101 |
model_name: str, expected_diagnosis: str = "", enable_logging: bool = True,
|
102 |
progress=gr.Progress()
|
103 |
):
|
104 |
-
if not MAI_DX_AVAILABLE:
|
105 |
-
|
106 |
-
|
107 |
-
|
|
|
|
|
|
|
|
|
108 |
|
109 |
-
self.current_metrics.reset()
|
110 |
-
conversation_log = ""
|
111 |
-
structured_conversations = ""
|
112 |
-
case_id = None
|
113 |
-
session: Optional[DiagnosisSession] = None
|
114 |
-
|
115 |
try:
|
116 |
-
progress(0.1, desc="🚀 Ініціалізація...")
|
117 |
-
|
118 |
-
# Створюємо сесію логування, ��кщо увімкнено
|
119 |
-
if enable_logging:
|
120 |
-
# ВИПРАВЛЕННЯ ТУТ: Використовуємо create_session
|
121 |
-
session = self.conversation_logger.create_session(
|
122 |
-
case_name=case_name,
|
123 |
-
patient_info=patient_info,
|
124 |
-
mode=mode,
|
125 |
-
budget=budget
|
126 |
-
)
|
127 |
-
case_id = session.case_id
|
128 |
-
conversation_log += f"📝 Розпочато логування сесії: {case_id}\n\n"
|
129 |
-
|
130 |
progress(0.2, desc="🤖 Створення AI-панелі...")
|
131 |
-
|
132 |
-
# ... решта коду методу залишається без змін ...
|
133 |
-
|
134 |
-
orchestrator = MaiDxOrchestrator(
|
135 |
-
model_name=model_name,
|
136 |
-
max_iterations=max_iterations,
|
137 |
-
initial_budget=budget,
|
138 |
-
mode=mode
|
139 |
-
)
|
140 |
|
141 |
progress(0.3, desc="🔍 Запуск діагностики...")
|
142 |
start_time = time.time()
|
143 |
|
144 |
-
|
145 |
-
|
146 |
-
result = self.conversation_logger.capture_orchestrator_output(
|
147 |
-
case_id=case_id,
|
148 |
-
orchestrator_func=orchestrator.run,
|
149 |
-
initial_case_info=patient_info,
|
150 |
-
full_case_details=patient_info,
|
151 |
-
ground_truth_diagnosis=expected_diagnosis or "Unknown"
|
152 |
-
)
|
153 |
-
else:
|
154 |
-
result = orchestrator.run(
|
155 |
-
initial_case_info=patient_info,
|
156 |
-
full_case_details=patient_info,
|
157 |
-
ground_truth_diagnosis=expected_diagnosis or "Unknown"
|
158 |
-
)
|
159 |
|
|
|
|
|
|
|
|
|
|
|
160 |
duration = time.time() - start_time
|
161 |
-
progress(0.9, desc="📊 Обробка результатів...")
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
cost=result.total_cost,
|
184 |
-
iterations=getattr(result, 'iterations', 0),
|
185 |
-
duration=duration,
|
186 |
-
status="✅ Успішно" if result.accuracy_score >= 3.0 else "⚠️ Потребує перегляду",
|
187 |
-
reasoning=getattr(result, 'accuracy_reasoning', 'N/A')
|
188 |
-
)
|
189 |
-
|
190 |
-
if session:
|
191 |
-
self.sessions_history.append(session)
|
192 |
-
conversation_log += "🤖 Повний сирий вивід системи:\n" + "="*60 + "\n"
|
193 |
-
conversation_log += session.raw_output + "\n" + "="*60 + "\n\n"
|
194 |
-
conversation_log += f"💾 Сесію збережено як: {session.case_id}\n"
|
195 |
-
|
196 |
-
if enable_logging:
|
197 |
-
html_path = self.conversation_logger.export_conversation_html(session.case_id)
|
198 |
-
conversation_log += f"📄 HTML звіт: {html_path}\n"
|
199 |
-
|
200 |
-
structured_conversations = self._format_structured_conversations(session.conversations)
|
201 |
-
|
202 |
-
for conv in session.conversations:
|
203 |
-
self.current_metrics.add_conversation_round({
|
204 |
-
'round': conv.round_number,
|
205 |
-
'agents': len(set(msg.agent_name for msg in conv.messages)),
|
206 |
-
'messages': len(conv.messages),
|
207 |
-
'cost': conv.cost_incurred
|
208 |
-
})
|
209 |
-
for msg in conv.messages:
|
210 |
-
self.current_metrics.update_agent_activity(msg.agent_name)
|
211 |
-
if conv.cost_incurred > 0:
|
212 |
-
self.current_metrics.add_cost_point(conv.cost_incurred)
|
213 |
|
|
|
214 |
progress(1.0, desc="✅ Готово!")
|
215 |
|
216 |
-
|
217 |
-
|
|
|
|
|
|
|
|
|
|
|
218 |
|
219 |
return (
|
220 |
self._format_main_result(session),
|
221 |
self._format_detailed_analysis(session),
|
222 |
self._generate_enhanced_recommendations(result, expected_diagnosis),
|
223 |
-
|
224 |
-
|
225 |
conversation_log,
|
226 |
structured_conversations
|
227 |
)
|
@@ -229,484 +139,152 @@ class UpdatedMAIDXInterface:
|
|
229 |
except Exception as e:
|
230 |
import traceback
|
231 |
traceback.print_exc()
|
232 |
-
|
233 |
-
if case_id:
|
234 |
-
error_msg += f"\n🗂️ ID сесії: {case_id}"
|
235 |
-
return self._format_error(error_msg)
|
236 |
|
237 |
-
def _format_structured_conversations(self,
|
238 |
-
|
239 |
-
conversations = self.conversation_logger.get_session_conversations(case_id)
|
240 |
-
|
241 |
-
if not conversations:
|
242 |
-
return "📭 Розмови не знайдено"
|
243 |
-
|
244 |
output = "## 💬 Структуровані розмови агентів\n\n"
|
245 |
-
|
246 |
for conv in conversations:
|
247 |
output += f"### 🔄 Раунд {conv.round_number}\n"
|
248 |
-
output += f"**Час:** {conv.start_time} - {conv.end_time or 'В процесі'}\n\n"
|
249 |
-
|
250 |
for msg in conv.messages:
|
251 |
-
|
252 |
-
|
253 |
-
'decision': '💡',
|
254 |
-
'input': '❓',
|
255 |
-
'output': '📊'
|
256 |
-
}.get(msg.message_type, '💬')
|
257 |
-
|
258 |
-
output += f"{emoji} **{msg.agent_name}** [{msg.message_type}]\n"
|
259 |
-
output += f"```\n{msg.content[:500]}{'...' if len(msg.content) > 500 else ''}\n```\n\n"
|
260 |
-
|
261 |
-
if conv.decision:
|
262 |
-
output += f"**🎯 Рішення:** {conv.decision}\n"
|
263 |
-
|
264 |
-
if conv.tests_ordered:
|
265 |
-
output += f"**🧪 Замовлені тести:** {', '.join(conv.tests_ordered)}\n"
|
266 |
-
|
267 |
-
if conv.questions_asked:
|
268 |
-
output += f"**❓ Поставлені питання:** {', '.join(conv.questions_asked[:3])}...\n"
|
269 |
-
|
270 |
-
if conv.cost_incurred > 0:
|
271 |
-
output += f"**💰 Вартість раунду:** ${conv.cost_incurred:.2f}\n"
|
272 |
-
|
273 |
-
output += "\n---\n\n"
|
274 |
-
|
275 |
return output
|
276 |
|
277 |
-
def
|
278 |
-
|
279 |
-
if not PLOTLY_AVAILABLE:
|
280 |
-
return None
|
281 |
-
|
282 |
-
if not self.current_metrics.conversation_rounds:
|
283 |
-
return None
|
284 |
-
|
285 |
-
try:
|
286 |
-
# Створюємо subplot з двома графіками
|
287 |
-
from plotly.subplots import make_subplots
|
288 |
-
|
289 |
-
fig = make_subplots(
|
290 |
-
rows=2, cols=1,
|
291 |
-
subplot_titles=('Прогресія вартості', 'Активність по раундах'),
|
292 |
-
vertical_spacing=0.15
|
293 |
-
)
|
294 |
-
|
295 |
-
# Графік вартості
|
296 |
-
if self.current_metrics.cost_progression:
|
297 |
-
fig.add_trace(
|
298 |
-
go.Scatter(
|
299 |
-
x=[p['time'] for p in self.current_metrics.cost_progression],
|
300 |
-
y=[p['cost'] for p in self.current_metrics.cost_progression],
|
301 |
-
mode='lines+markers',
|
302 |
-
name='Вартість',
|
303 |
-
line=dict(color='#3498db', width=3)
|
304 |
-
),
|
305 |
-
row=1, col=1
|
306 |
-
)
|
307 |
-
|
308 |
-
# Графік активності по раундах
|
309 |
-
rounds_data = self.current_metrics.conversation_rounds
|
310 |
-
if rounds_data:
|
311 |
-
fig.add_trace(
|
312 |
-
go.Bar(
|
313 |
-
x=[f"Раунд {r['round']}" for r in rounds_data],
|
314 |
-
y=[r['messages'] for r in rounds_data],
|
315 |
-
name='Кількість повідомлень',
|
316 |
-
marker_color='#2ecc71'
|
317 |
-
),
|
318 |
-
row=2, col=1
|
319 |
-
)
|
320 |
-
|
321 |
-
fig.update_layout(
|
322 |
-
height=600,
|
323 |
-
showlegend=True,
|
324 |
-
template='plotly_white'
|
325 |
-
)
|
326 |
-
|
327 |
-
return fig
|
328 |
-
|
329 |
-
except Exception as e:
|
330 |
-
print(f"Помилка візуалізації: {e}")
|
331 |
-
return None
|
332 |
-
|
333 |
-
def _create_conversation_flow_chart(self):
|
334 |
-
"""Створює діаграму потоку розмов між агентами"""
|
335 |
-
if not PLOTLY_AVAILABLE:
|
336 |
-
return None
|
337 |
-
|
338 |
-
activity_data = {k: v for k, v in self.current_metrics.agents_activity.items() if v > 0}
|
339 |
|
340 |
-
|
341 |
-
|
342 |
-
|
|
|
|
|
|
|
|
|
343 |
try:
|
344 |
-
#
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
'Dr. Challenger': '#f39c12',
|
349 |
-
'Dr. Stewardship': '#27ae60',
|
350 |
-
'Dr. Checklist': '#9b59b6',
|
351 |
-
'Consensus Coordinator': '#34495e',
|
352 |
-
'Gatekeeper': '#16a085',
|
353 |
-
'Judge': '#c0392b'
|
354 |
-
}
|
355 |
-
|
356 |
-
agent_colors = [colors.get(agent, '#95a5a6') for agent in activity_data.keys()]
|
357 |
-
|
358 |
-
fig = go.Figure(go.Bar(
|
359 |
-
x=list(activity_data.keys()),
|
360 |
-
y=list(activity_data.values()),
|
361 |
-
text=list(activity_data.values()),
|
362 |
-
textposition='auto',
|
363 |
-
marker_color=agent_colors
|
364 |
-
))
|
365 |
-
|
366 |
-
fig.update_layout(
|
367 |
-
title='🤖 Активність агентів у діагностичному процесі',
|
368 |
-
xaxis_title='Агенти',
|
369 |
-
yaxis_title='Кількість взаємодій',
|
370 |
-
template='plotly_white',
|
371 |
-
height=400,
|
372 |
-
margin=dict(t=60, b=100)
|
373 |
-
)
|
374 |
-
|
375 |
fig.update_xaxes(tickangle=-45)
|
376 |
-
|
377 |
return fig
|
378 |
-
|
379 |
except Exception as e:
|
380 |
-
print(f"Помилка
|
381 |
return None
|
382 |
|
383 |
-
|
|
|
384 |
efficiency = ((session.budget - session.cost) / session.budget * 100) if session.budget > 0 else 0
|
385 |
confidence_emoji = "🎯" if session.confidence >= 4.0 else "👍" if session.confidence >= 3.0 else "⚠️"
|
386 |
-
|
387 |
return f"""
|
388 |
## 🏥 Результати MAI-DX Діагностики
|
389 |
-
|
390 |
### 📋 Основна інформація
|
391 |
- **🗂️ Випадок**: {session.case_name}
|
392 |
- **🆔 ID сесії**: {session.case_id}
|
393 |
-
- **⏰ Час**: {session.timestamp}
|
394 |
- **🔧 Режим**: {session.mode}
|
395 |
-
|
396 |
### {confidence_emoji} Діагностичний висновок
|
397 |
**{session.diagnosis}**
|
398 |
-
|
399 |
### 📊 Показники якості
|
400 |
- **Точність**: {session.confidence:.1f}/5.0 ⭐
|
401 |
- **Статус**: {session.status}
|
402 |
- **Ітерації**: {session.iterations} циклів
|
403 |
-
- **Кількість агентів**: {len([a for a in self.current_metrics.agents_activity.values() if a > 0])}
|
404 |
-
|
405 |
### 💰 Економічні показники
|
406 |
-
- **Витрачено**: ${session.cost:,.2f}
|
407 |
-
- **Бюджет**: ${session.budget:,}
|
408 |
- **Ефективність**: {efficiency:.1f}% бюджету збережено
|
409 |
-
- **Швидкість**: {session.duration:.1f}
|
410 |
"""
|
411 |
|
412 |
-
def _format_detailed_analysis(self, session):
|
413 |
return f"""
|
414 |
-
## 🔬 Детальний
|
415 |
-
|
416 |
-
### 💭 Медичне обґрунтування
|
417 |
{session.reasoning}
|
418 |
-
|
419 |
### 📂 Файли логів
|
420 |
- JSON: `mai_dx_logs/{session.case_id}.json`
|
421 |
- Raw text: `mai_dx_logs/{session.case_id}_raw.txt`
|
422 |
- HTML звіт: `mai_dx_logs/{session.case_id}_conversation.html`
|
423 |
"""
|
424 |
|
425 |
-
def _generate_enhanced_recommendations(self, result,
|
426 |
comparison = ""
|
427 |
-
if
|
428 |
-
is_match =
|
429 |
comparison = f"""
|
430 |
### 🎯 Порівняння з очікуваним
|
431 |
-
**Очікувався**: {
|
432 |
**Отримано**: {result.final_diagnosis}
|
433 |
**Збіг**: {'✅ Так' if is_match else '❌ Ні'}
|
434 |
"""
|
435 |
-
|
436 |
return f"""
|
437 |
## 💡 Клінічні рекомендації
|
438 |
-
|
439 |
-
|
440 |
-
- 🔍 Верифікувати діагноз з лікарем-спеціалістом
|
441 |
-
- 📋 Розглянути необхідність додаткових досліджень
|
442 |
-
- 📊 Переглянути детальний лог розмов агентів
|
443 |
-
|
444 |
{comparison}
|
445 |
-
|
446 |
### ⚠️ Важливе застереження
|
447 |
🔴 **Цей діагноз згенеровано ШІ і НЕ замінює професійну медичну консультацію.**
|
448 |
"""
|
449 |
|
450 |
-
def _format_error(self,
|
451 |
-
return (
|
452 |
-
|
453 |
-
def load_sample_case(self, sample_key):
|
454 |
-
case_data = self.sample_cases.get(sample_key, {})
|
455 |
-
return case_data.get("info", ""), sample_key, case_data.get("expected", "")
|
456 |
|
|
|
|
|
|
|
|
|
457 |
def get_session_details(self, case_id: str):
|
458 |
-
"
|
459 |
-
conversations = self.conversation_logger.get_session_conversations(case_id)
|
460 |
-
|
461 |
-
if not conversations:
|
462 |
-
return "Сесія не знайдена"
|
463 |
-
|
464 |
-
details = f"## Детальний аналіз сесії {case_id}\n\n"
|
465 |
-
details += f"**Кількість раундів**: {len(conversations)}\n"
|
466 |
-
|
467 |
-
total_messages = sum(len(conv.messages) for conv in conversations)
|
468 |
-
details += f"**Всього повідомлень**: {total_messages}\n\n"
|
469 |
-
|
470 |
-
# Статистика по агентах
|
471 |
-
agent_stats = {}
|
472 |
-
for conv in conversations:
|
473 |
-
for msg in conv.messages:
|
474 |
-
agent_stats[msg.agent_name] = agent_stats.get(msg.agent_name, 0) + 1
|
475 |
-
|
476 |
-
details += "### Статистика по агентах:\n"
|
477 |
-
for agent, count in sorted(agent_stats.items(), key=lambda x: x[1], reverse=True):
|
478 |
-
details += f"- **{agent}**: {count} повідомлень\n"
|
479 |
-
|
480 |
-
return details
|
481 |
|
482 |
def create_updated_gradio_interface():
|
483 |
interface = UpdatedMAIDXInterface()
|
484 |
-
|
485 |
-
|
486 |
-
.gradio-container {
|
487 |
-
font-family: 'Inter', sans-serif;
|
488 |
-
}
|
489 |
-
.main-header {
|
490 |
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
491 |
-
color: white;
|
492 |
-
padding: 2rem;
|
493 |
-
border-radius: 15px;
|
494 |
-
margin-bottom: 2rem;
|
495 |
-
text-align: center;
|
496 |
-
}
|
497 |
-
.conversation-log {
|
498 |
-
font-family: 'Fira Code', monospace;
|
499 |
-
background: #2d3748;
|
500 |
-
color: #e2e8f0;
|
501 |
-
border-radius: 8px;
|
502 |
-
padding: 1rem;
|
503 |
-
max-height: 600px;
|
504 |
-
overflow-y: auto;
|
505 |
-
white-space: pre-wrap;
|
506 |
-
}
|
507 |
-
.structured-conv {
|
508 |
-
background: #f7fafc;
|
509 |
-
border: 1px solid #e2e8f0;
|
510 |
-
border-radius: 8px;
|
511 |
-
padding: 1rem;
|
512 |
-
max-height: 800px;
|
513 |
-
overflow-y: auto;
|
514 |
-
}
|
515 |
-
"""
|
516 |
-
|
517 |
-
with gr.Blocks(
|
518 |
-
title="🏥 MAI-DX Enhanced Platform with Full Logging",
|
519 |
-
theme=gr.themes.Soft(primary_hue="blue"),
|
520 |
-
css=custom_css
|
521 |
-
) as demo:
|
522 |
-
|
523 |
-
gr.HTML("""
|
524 |
-
<div class='main-header'>
|
525 |
-
<h1>🏥 MAI-DX Enhanced Research Platform</h1>
|
526 |
-
<p>🤖 Платформа для ШІ-діагностики з повним логуванням розмов агентів</p>
|
527 |
-
</div>
|
528 |
-
""")
|
529 |
-
|
530 |
with gr.Tabs():
|
531 |
-
with gr.Tab("🩺 Діагностика"
|
532 |
with gr.Row():
|
533 |
with gr.Column(scale=1):
|
534 |
gr.Markdown("### 📝 Клінічний випадок")
|
535 |
-
case_name = gr.Textbox(
|
536 |
-
|
537 |
-
|
538 |
-
)
|
539 |
-
|
540 |
-
sample_selector = gr.Dropdown(
|
541 |
-
choices=list(interface.sample_cases.keys()),
|
542 |
-
label="🎯 Готові тестові випадки",
|
543 |
-
interactive=True
|
544 |
-
)
|
545 |
-
|
546 |
-
patient_info = gr.Textbox(
|
547 |
-
label="👤 Інформація про пацієнта",
|
548 |
-
lines=12,
|
549 |
-
placeholder="Введіть опис клінічного випадку..."
|
550 |
-
)
|
551 |
-
|
552 |
-
expected_diagnosis = gr.Textbox(
|
553 |
-
label="🎯 Очікуваний діагноз (англ.)",
|
554 |
-
lines=2
|
555 |
-
)
|
556 |
-
|
557 |
with gr.Column(scale=1):
|
558 |
gr.Markdown("### ⚙️ Налаштування")
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
)
|
566 |
-
|
567 |
-
budget = gr.Slider(
|
568 |
-
minimum=500,
|
569 |
-
maximum=10000,
|
570 |
-
step=500,
|
571 |
-
value=3000,
|
572 |
-
label="💵 Бюджет ($)",
|
573 |
-
interactive=True
|
574 |
-
)
|
575 |
-
|
576 |
-
max_iterations = gr.Slider(
|
577 |
-
minimum=1,
|
578 |
-
maximum=15,
|
579 |
-
step=1,
|
580 |
-
value=8,
|
581 |
-
label="🔄 Макс. ітерацій",
|
582 |
-
interactive=True
|
583 |
-
)
|
584 |
-
|
585 |
-
model_name = gr.Dropdown(
|
586 |
-
choices=[
|
587 |
-
"gemini/gemini-1.5-flash",
|
588 |
-
"gpt-4",
|
589 |
-
"gpt-4-turbo",
|
590 |
-
"claude-3-5-sonnet",
|
591 |
-
"gpt-4o"
|
592 |
-
],
|
593 |
-
label="🤖 LLM Модель",
|
594 |
-
value="gemini/gemini-1.5-flash",
|
595 |
-
interactive=True
|
596 |
-
)
|
597 |
-
|
598 |
-
enable_logging = gr.Checkbox(
|
599 |
-
label="📝 Повне логування розмов",
|
600 |
-
value=True,
|
601 |
-
interactive=True
|
602 |
-
)
|
603 |
-
|
604 |
-
diagnose_btn = gr.Button(
|
605 |
-
"🚀 Запустити діагностику",
|
606 |
-
variant="primary",
|
607 |
-
size="lg"
|
608 |
-
)
|
609 |
|
610 |
-
|
611 |
-
sample_selector.change(
|
612 |
-
interface.load_sample_case,
|
613 |
-
inputs=sample_selector,
|
614 |
-
outputs=[patient_info, case_name, expected_diagnosis]
|
615 |
-
)
|
616 |
-
|
617 |
-
gr.Markdown("---\n## 📊 Результати діагностики")
|
618 |
|
|
|
619 |
with gr.Row():
|
620 |
with gr.Column(scale=2):
|
621 |
main_result = gr.Markdown()
|
622 |
detailed_analysis = gr.Markdown()
|
623 |
-
|
624 |
with gr.Column(scale=1):
|
625 |
recommendations = gr.Markdown()
|
626 |
|
627 |
-
|
628 |
-
|
629 |
-
with gr.Row():
|
630 |
-
metrics_plot = gr.Plot(label="📈 Метрики процесу")
|
631 |
-
agent_activity_plot = gr.Plot(label="🤖 Активність агентів")
|
632 |
-
else:
|
633 |
-
metrics_plot = gr.Plot(visible=False)
|
634 |
-
agent_activity_plot = gr.Plot(visible=False)
|
635 |
|
636 |
-
# Логи
|
637 |
with gr.Row():
|
638 |
-
|
639 |
-
|
640 |
-
conversation_logs = gr.Textbox(
|
641 |
-
label="Raw logs",
|
642 |
-
lines=15,
|
643 |
-
elem_classes=["conversation-log"],
|
644 |
-
interactive=False,
|
645 |
-
show_copy_button=True
|
646 |
-
)
|
647 |
-
|
648 |
-
with gr.Column():
|
649 |
-
gr.Markdown("### 🗣️ Структуровані розмови агентів")
|
650 |
-
structured_conversations = gr.Markdown(
|
651 |
-
elem_classes=["structured-conv"]
|
652 |
-
)
|
653 |
|
654 |
-
# Підключаємо функцію діагностики
|
655 |
diagnose_btn.click(
|
656 |
interface.diagnose_with_full_logging,
|
657 |
-
inputs=[
|
658 |
-
|
659 |
-
max_iterations, model_name, expected_diagnosis,
|
660 |
-
enable_logging
|
661 |
-
],
|
662 |
-
outputs=[
|
663 |
-
main_result, detailed_analysis, recommendations,
|
664 |
-
metrics_plot, agent_activity_plot,
|
665 |
-
conversation_logs, structured_conversations
|
666 |
-
]
|
667 |
)
|
668 |
|
669 |
-
with gr.Tab("📊 Аналіз логів"
|
670 |
gr.Markdown("### 🔍 Аналіз збережених діагностичних сесій")
|
671 |
-
|
672 |
-
with gr.Row():
|
673 |
-
case_id_input = gr.Textbox(
|
674 |
-
label="🆔 ID сесії",
|
675 |
-
placeholder="Введіть ID сесії для аналізу"
|
676 |
-
)
|
677 |
-
|
678 |
-
analyze_btn = gr.Button("📋 Аналізувати")
|
679 |
-
|
680 |
-
session_details = gr.Markdown()
|
681 |
-
|
682 |
-
# Функція аналізу
|
683 |
-
analyze_btn.click(
|
684 |
-
interface.get_session_details,
|
685 |
-
inputs=[case_id_input],
|
686 |
-
outputs=[session_details]
|
687 |
-
)
|
688 |
-
|
689 |
-
gr.Markdown("### 📁 Експорт даних")
|
690 |
-
|
691 |
-
with gr.Row():
|
692 |
-
export_format = gr.Radio(
|
693 |
-
choices=["JSON", "HTML", "CSV"],
|
694 |
-
label="Формат експорту",
|
695 |
-
value="HTML"
|
696 |
-
)
|
697 |
-
|
698 |
-
export_btn = gr.Button("📤 Експортувати")
|
699 |
-
|
700 |
-
export_status = gr.Markdown()
|
701 |
|
702 |
return demo
|
703 |
|
704 |
if __name__ == "__main__":
|
705 |
-
print("🚀 Запуск MAI-DX Enhanced Platform with Full Agent Conversation Logging...")
|
706 |
demo = create_updated_gradio_interface()
|
707 |
-
demo.launch(
|
708 |
-
server_name="0.0.0.0",
|
709 |
-
server_port=7860,
|
710 |
-
share=True,
|
711 |
-
debug=False
|
712 |
-
)
|
|
|
6 |
import sys
|
7 |
import json
|
8 |
import time
|
9 |
+
import uuid
|
10 |
import pandas as pd
|
11 |
import gradio as gr
|
12 |
from datetime import datetime
|
13 |
+
from typing import Dict, List, Tuple, Optional, Any
|
14 |
from dataclasses import dataclass, asdict
|
15 |
import warnings
|
16 |
|
|
|
17 |
os.environ.update({
|
18 |
+
"SWARMS_VERBOSITY": "ERROR", "RICH_TRACEBACK": "0",
|
19 |
+
"SWARMS_SHOW_PANEL": "true", "SWARMS_AUTO_PRINT": "true"
|
|
|
|
|
20 |
})
|
21 |
warnings.filterwarnings("ignore")
|
22 |
|
23 |
from dotenv import load_dotenv
|
24 |
load_dotenv()
|
25 |
|
|
|
26 |
try:
|
27 |
from mai_dx_wrapper import SafeMaiDxOrchestrator as MaiDxOrchestrator, MAI_DX_AVAILABLE
|
28 |
except ImportError:
|
|
|
33 |
MAI_DX_AVAILABLE = False
|
34 |
IMPORT_ERROR = str(e)
|
35 |
|
36 |
+
# УВАГА: Ми імпортуємо парсер напряму, щоб використовувати його в інтерфейсі
|
37 |
+
from enhanced_mai_dx_logger import MAIDxConversationLogger, DiagnosisSession, AgentConversation, EnhancedOutputCapture
|
38 |
|
|
|
39 |
try:
|
40 |
import plotly.graph_objects as go
|
41 |
import plotly.express as px
|
42 |
PLOTLY_AVAILABLE = True
|
43 |
except ImportError:
|
44 |
PLOTLY_AVAILABLE = False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
|
46 |
class UpdatedMAIDXInterface:
|
|
|
|
|
47 |
def __init__(self):
|
48 |
self.sessions_history = []
|
49 |
self.conversation_logger = MAIDxConversationLogger("mai_dx_logs")
|
|
|
|
|
50 |
self.sample_cases = {
|
51 |
"🫀 Кардіологічний (Гострий MI)": {
|
52 |
"info": "Пацієнт: 58-річний чоловік, менеджер, гіпертонія в анамнезі\nСкарги: Гострий роздираючий біль у грудях 3 години, іррадіація в ліву руку\nОгляд: Блідий, пітливий, АТ 160/90, ЧСС 95\nЕКГ: ST-підйоми у відведеннях II, III, aVF (нижня стінка)\nТропонін I: 8.5 нг/мл (норма <0.04)\nАнамнез: Куріння 30 років, дислипідемія, сімейний анамнез ІХС",
|
|
|
67 |
model_name: str, expected_diagnosis: str = "", enable_logging: bool = True,
|
68 |
progress=gr.Progress()
|
69 |
):
|
70 |
+
if not MAI_DX_AVAILABLE: return self._format_error(f"❌ MAI-DX недоступний: {IMPORT_ERROR}")
|
71 |
+
if not patient_info.strip(): return self._format_error("❌ Введіть інформацію про пацієнта")
|
72 |
+
|
73 |
+
# Створюємо тимчасовий об'єкт сесії. ID буде присвоєно пізніше.
|
74 |
+
session = DiagnosisSession(
|
75 |
+
case_id="temp", timestamp=datetime.now().isoformat(),
|
76 |
+
case_name=case_name, patient_info=patient_info, mode=mode, budget=budget
|
77 |
+
)
|
78 |
|
|
|
|
|
|
|
|
|
|
|
|
|
79 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
80 |
progress(0.2, desc="🤖 Створення AI-панелі...")
|
81 |
+
orchestrator = MaiDxOrchestrator(model_name, max_iterations, budget, mode)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
|
83 |
progress(0.3, desc="🔍 Запуск діагностики...")
|
84 |
start_time = time.time()
|
85 |
|
86 |
+
raw_output = ""
|
87 |
+
result = None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
88 |
|
89 |
+
# Надійне захоплення виводу
|
90 |
+
with EnhancedOutputCapture() as capture:
|
91 |
+
result = orchestrator.run(patient_info, patient_info, expected_diagnosis or "Unknown")
|
92 |
+
raw_output = capture.get_value()
|
93 |
+
|
94 |
duration = time.time() - start_time
|
95 |
+
progress(0.9, desc="📊 Обробка та збереження результатів...")
|
96 |
+
|
97 |
+
# ЗАПОВНЮЄМО ОБ'ЄКТ СЕСІЇ ВСІМА ДАНИМИ
|
98 |
+
session.case_id = f"case_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{uuid.uuid4().hex[:6]}"
|
99 |
+
session.raw_output = raw_output
|
100 |
+
session.diagnosis = getattr(result, 'final_diagnosis', 'N/A')
|
101 |
+
session.confidence = getattr(result, 'accuracy_score', 0.0)
|
102 |
+
session.cost = getattr(result, 'total_cost', 0.0)
|
103 |
+
session.reasoning = getattr(result, 'accuracy_reasoning', 'N/A')
|
104 |
+
session.duration = duration
|
105 |
+
|
106 |
+
# ПАРСИМО RAW_OUTPUT І ЗАПОВНЮЄМО ПОЛЕ 'conversations'
|
107 |
+
# Ми викликаємо метод парсингу з самого логера, але на нашому об'єкті сесії
|
108 |
+
self.conversation_logger._parse_captured_output(session, raw_output)
|
109 |
+
|
110 |
+
session.iterations = len(session.conversations)
|
111 |
+
session.status = "✅ Успішно" if session.confidence >= 3.0 else "⚠️ Потребує перегляду"
|
112 |
+
|
113 |
+
# Зберігаємо вже повністю заповнений об'єкт
|
114 |
+
if enable_logging:
|
115 |
+
self.conversation_logger._save_session_to_file(session)
|
116 |
+
self.conversation_logger.export_conversation_html(session)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
117 |
|
118 |
+
self.sessions_history.append(session)
|
119 |
progress(1.0, desc="✅ Готово!")
|
120 |
|
121 |
+
# Формуємо вивід для Gradio
|
122 |
+
conversation_log = f"📝 Сесія: {session.case_id}\n"
|
123 |
+
conversation_log += "🤖 Повний сирий вивід системи:\n" + "="*60 + "\n"
|
124 |
+
conversation_log += session.raw_output + "\n" + "="*60 + "\n\n"
|
125 |
+
|
126 |
+
structured_conversations = self._format_structured_conversations(session.conversations)
|
127 |
+
agent_plot = self._create_agent_activity_chart(session.conversations)
|
128 |
|
129 |
return (
|
130 |
self._format_main_result(session),
|
131 |
self._format_detailed_analysis(session),
|
132 |
self._generate_enhanced_recommendations(result, expected_diagnosis),
|
133 |
+
agent_plot, # Тільки один графік
|
134 |
+
None, # Другий слот для графіка порожній
|
135 |
conversation_log,
|
136 |
structured_conversations
|
137 |
)
|
|
|
139 |
except Exception as e:
|
140 |
import traceback
|
141 |
traceback.print_exc()
|
142 |
+
return self._format_error(f"❌ Критична помилка: {e}")
|
|
|
|
|
|
|
143 |
|
144 |
+
def _format_structured_conversations(self, conversations: List[AgentConversation]) -> str:
|
145 |
+
if not conversations: return "📭 Розмови не знайдено"
|
|
|
|
|
|
|
|
|
|
|
146 |
output = "## 💬 Структуровані розмови агентів\n\n"
|
|
|
147 |
for conv in conversations:
|
148 |
output += f"### 🔄 Раунд {conv.round_number}\n"
|
|
|
|
|
149 |
for msg in conv.messages:
|
150 |
+
output += f"**{msg.agent_name}** `[{msg.message_type}]`:\n"
|
151 |
+
output += f"```\n{msg.content}\n```\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
152 |
return output
|
153 |
|
154 |
+
def _create_agent_activity_chart(self, conversations: List[AgentConversation]) -> Optional[object]:
|
155 |
+
if not PLOTLY_AVAILABLE or not conversations: return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
156 |
|
157 |
+
agent_activity = {}
|
158 |
+
for conv in conversations:
|
159 |
+
for msg in conv.messages:
|
160 |
+
agent_activity[msg.agent_name] = agent_activity.get(msg.agent_name, 0) + 1
|
161 |
+
|
162 |
+
if not agent_activity: return None
|
163 |
+
|
164 |
try:
|
165 |
+
colors = {'Dr. Hypothesis': '#3498db', 'Dr. Test-Chooser': '#e74c3c', 'Dr. Challenger': '#f39c12', 'Dr. Stewardship': '#27ae60', 'Dr. Checklist': '#9b59b6', 'Consensus Coordinator': '#34495e', 'Gatekeeper': '#16a085', 'Judge': '#c0392b'}
|
166 |
+
agent_colors = [colors.get(agent, '#95a5a6') for agent in agent_activity.keys()]
|
167 |
+
fig = go.Figure(go.Bar(x=list(agent_activity.keys()), y=list(agent_activity.values()), name='Взаємодії', marker_color=agent_colors))
|
168 |
+
fig.update_layout(title='🤖 Активність агентів', height=400, showlegend=False, template='plotly_white')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
169 |
fig.update_xaxes(tickangle=-45)
|
|
|
170 |
return fig
|
|
|
171 |
except Exception as e:
|
172 |
+
print(f"Помилка візуалізації: {e}")
|
173 |
return None
|
174 |
|
175 |
+
# ... (решта методів _format... та load_sample_case залишаються без змін) ...
|
176 |
+
def _format_main_result(self, session: DiagnosisSession) -> str:
|
177 |
efficiency = ((session.budget - session.cost) / session.budget * 100) if session.budget > 0 else 0
|
178 |
confidence_emoji = "🎯" if session.confidence >= 4.0 else "👍" if session.confidence >= 3.0 else "⚠️"
|
|
|
179 |
return f"""
|
180 |
## 🏥 Результати MAI-DX Діагностики
|
|
|
181 |
### 📋 Основна інформація
|
182 |
- **🗂️ Випадок**: {session.case_name}
|
183 |
- **🆔 ID сесії**: {session.case_id}
|
|
|
184 |
- **🔧 Режим**: {session.mode}
|
|
|
185 |
### {confidence_emoji} Діагностичний висновок
|
186 |
**{session.diagnosis}**
|
|
|
187 |
### 📊 Показники якості
|
188 |
- **Точність**: {session.confidence:.1f}/5.0 ⭐
|
189 |
- **Статус**: {session.status}
|
190 |
- **Ітерації**: {session.iterations} циклів
|
|
|
|
|
191 |
### 💰 Економічні показники
|
192 |
+
- **Витрачено**: ${session.cost:,.2f} / ${session.budget:,}
|
|
|
193 |
- **Ефективність**: {efficiency:.1f}% бюджету збережено
|
194 |
+
- **Швидкість**: {session.duration:.1f} сек
|
195 |
"""
|
196 |
|
197 |
+
def _format_detailed_analysis(self, session: DiagnosisSession) -> str:
|
198 |
return f"""
|
199 |
+
## 🔬 Детальний аналіз
|
200 |
+
### 💭 Обґрунтування
|
|
|
201 |
{session.reasoning}
|
|
|
202 |
### 📂 Файли логів
|
203 |
- JSON: `mai_dx_logs/{session.case_id}.json`
|
204 |
- Raw text: `mai_dx_logs/{session.case_id}_raw.txt`
|
205 |
- HTML звіт: `mai_dx_logs/{session.case_id}_conversation.html`
|
206 |
"""
|
207 |
|
208 |
+
def _generate_enhanced_recommendations(self, result: Any, expected: str) -> str:
|
209 |
comparison = ""
|
210 |
+
if expected:
|
211 |
+
is_match = expected.lower() in result.final_diagnosis.lower()
|
212 |
comparison = f"""
|
213 |
### 🎯 Порівняння з очікуваним
|
214 |
+
**Очікувався**: {expected}
|
215 |
**Отримано**: {result.final_diagnosis}
|
216 |
**Збіг**: {'✅ Так' if is_match else '❌ Ні'}
|
217 |
"""
|
|
|
218 |
return f"""
|
219 |
## 💡 Клінічні рекомендації
|
220 |
+
- 🔍 Верифікувати діагноз з лікарем-спеціалістом.
|
221 |
+
- 📊 Переглянути детальний лог розмов агентів.
|
|
|
|
|
|
|
|
|
222 |
{comparison}
|
|
|
223 |
### ⚠️ Важливе застереження
|
224 |
🔴 **Цей діагноз згенеровано ШІ і НЕ замінює професійну медичну консультацію.**
|
225 |
"""
|
226 |
|
227 |
+
def _format_error(self, msg: str) -> Tuple[str, str, str, None, None, str, str]:
|
228 |
+
return (msg, "", "", None, None, msg, "")
|
|
|
|
|
|
|
|
|
229 |
|
230 |
+
def load_sample_case(self, key: str) -> Tuple[str, str, str]:
|
231 |
+
case = self.sample_cases.get(key, {})
|
232 |
+
return case.get("info", ""), key, case.get("expected", "")
|
233 |
+
|
234 |
def get_session_details(self, case_id: str):
|
235 |
+
return f"Аналіз сесії {case_id} ще не реалізовано."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
236 |
|
237 |
def create_updated_gradio_interface():
|
238 |
interface = UpdatedMAIDXInterface()
|
239 |
+
with gr.Blocks(title="🏥 MAI-DX Enhanced Platform", theme=gr.themes.Soft(primary_hue="blue")) as demo:
|
240 |
+
gr.HTML("""<div style='background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 2rem; border-radius: 15px; margin-bottom: 2rem; text-align: center;'><h1>🏥 MAI-DX Enhanced Research Platform</h1></div>""")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
241 |
with gr.Tabs():
|
242 |
+
with gr.Tab("🩺 Діагностика"):
|
243 |
with gr.Row():
|
244 |
with gr.Column(scale=1):
|
245 |
gr.Markdown("### 📝 Клінічний випадок")
|
246 |
+
case_name = gr.Textbox(label="🏷️ Назва випадку", placeholder="Наприклад: Кардіологічний випадок №1")
|
247 |
+
sample_selector = gr.Dropdown(choices=list(interface.sample_cases.keys()), label="🎯 Готові випадки", interactive=True)
|
248 |
+
patient_info = gr.Textbox(label="👤 Інформація про пацієнта", lines=12, placeholder="Введіть опис клінічного випадку...")
|
249 |
+
expected_diagnosis = gr.Textbox(label="🎯 Очік��ваний діагноз (англ.)", lines=2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
250 |
with gr.Column(scale=1):
|
251 |
gr.Markdown("### ⚙️ Налаштування")
|
252 |
+
mode = gr.Radio(["budgeted", "no_budget", "instant", "question_only"], label="🔧 Режим", value="budgeted")
|
253 |
+
budget = gr.Slider(500, 10000, 3000, step=500, label="💵 Бюджет ($)")
|
254 |
+
max_iterations = gr.Slider(1, 15, 8, step=1, label="🔄 Макс. ітерацій")
|
255 |
+
model_name = gr.Dropdown(["gemini/gemini-2.5-flash", "gpt-4o", "claude-3-5-sonnet"], label="🤖 LLM Модель", value="gemini/gemini-2.5-flash")
|
256 |
+
enable_logging = gr.Checkbox(label="📝 Повне логування розмов", value=True)
|
257 |
+
diagnose_btn = gr.Button("🚀 Запустити діагностику", variant="primary", size="lg")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
258 |
|
259 |
+
sample_selector.change(interface.load_sample_case, inputs=sample_selector, outputs=[patient_info, case_name, expected_diagnosis])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
260 |
|
261 |
+
gr.Markdown("---")
|
262 |
with gr.Row():
|
263 |
with gr.Column(scale=2):
|
264 |
main_result = gr.Markdown()
|
265 |
detailed_analysis = gr.Markdown()
|
|
|
266 |
with gr.Column(scale=1):
|
267 |
recommendations = gr.Markdown()
|
268 |
|
269 |
+
metrics_plot = gr.Plot(label="📈 Активність агентів")
|
270 |
+
agent_activity_plot = gr.Plot(visible=False) # Сховано
|
|
|
|
|
|
|
|
|
|
|
|
|
271 |
|
|
|
272 |
with gr.Row():
|
273 |
+
conversation_logs = gr.Textbox(label="💬 Сирий вивід", lines=15, interactive=False, show_copy_button=True)
|
274 |
+
structured_conversations = gr.Markdown(label="🗣️ Структуровані розмови")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
275 |
|
|
|
276 |
diagnose_btn.click(
|
277 |
interface.diagnose_with_full_logging,
|
278 |
+
inputs=[case_name, patient_info, mode, budget, max_iterations, model_name, expected_diagnosis, enable_logging],
|
279 |
+
outputs=[main_result, detailed_analysis, recommendations, metrics_plot, agent_activity_plot, conversation_logs, structured_conversations]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
280 |
)
|
281 |
|
282 |
+
with gr.Tab("📊 Аналіз логів"):
|
283 |
gr.Markdown("### 🔍 Аналіз збережених діагностичних сесій")
|
284 |
+
# ... (код для аналізу логів) ...
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
285 |
|
286 |
return demo
|
287 |
|
288 |
if __name__ == "__main__":
|
|
|
289 |
demo = create_updated_gradio_interface()
|
290 |
+
demo.launch(server_name="0.0.0.0", server_port=7860, share=True)
|
|
|
|
|
|
|
|
|
|