Spaces:
Running
Running
File size: 29,079 Bytes
022ace2 5e2744f 636cfae c308431 022ace2 5e2744f c308431 5e2744f c308431 5e2744f 636cfae eaf91d7 636cfae eaf91d7 636cfae 5e2744f eaf91d7 5e2744f eaf91d7 7ca4926 5e2744f c8ef2b0 c308431 d08530d c308431 d08530d c308431 d08530d 5e2744f d08530d 5e2744f d08530d 5e2744f d08530d 5e2744f d08530d 5e2744f d08530d 5e2744f c308431 d08530d c308431 d08530d c308431 d08530d c308431 d08530d c308431 d08530d c308431 d08530d c308431 d08530d c308431 d08530d c308431 d08530d c308431 c8ef2b0 d08530d 5e2744f d08530d 5e2744f d08530d 5e2744f d08530d 5e2744f d08530d 5e2744f d08530d 5e2744f d08530d 7ca4926 d08530d 636cfae d08530d 636cfae d08530d 5e2744f d08530d 636cfae d08530d 636cfae d08530d c7eeac3 c308431 eaf91d7 c8ef2b0 eaf91d7 c308431 eaf91d7 c308431 d08530d 022ace2 5e2744f c8ef2b0 636cfae c308431 5e2744f c308431 eaf91d7 c8ef2b0 eaf91d7 c8ef2b0 c308431 5e2744f c308431 5e2744f c308431 5e2744f 636cfae 5e2744f d08530d 5e2744f d08530d 5e2744f eaf91d7 c8ef2b0 eaf91d7 c308431 c8ef2b0 c33e580 c8ef2b0 5e2744f d08530d 5e2744f d08530d c33e580 5e2744f d08530d 5e2744f eaf91d7 c8ef2b0 eaf91d7 c8ef2b0 c308431 d08530d 5e2744f d08530d 2670252 d08530d c8ef2b0 5e2744f d08530d 2670252 d08530d 5e2744f d08530d 5e2744f d08530d 5e2744f d08530d 5e2744f d08530d 022ace2 d08530d 5e2744f d08530d 5e2744f d08530d 5e2744f d08530d 5e2744f d08530d 5e2744f d08530d 5e2744f d08530d 5e2744f d08530d 5e2744f 2670252 5e2744f c308431 eaf91d7 c8ef2b0 eaf91d7 c33e580 022ace2 5e2744f |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 |
import gradio as gr
import pandas as pd
import time
import traceback
import os
import requests
# 完全版のimportエラー対策(段階的フォールバック)
LANGCHAIN_AVAILABLE = False
FULL_VERSION = False
try:
from langchain_groq import ChatGroq
from langchain_openai import ChatOpenAI
LANGCHAIN_AVAILABLE = True
print("✅ LangChain基本ライブラリが利用可能です")
except ImportError as e:
print(f"⚠️ LangChain基本ライブラリが利用できません: {e}")
try:
from OpenAITools.FetchTools import fetch_clinical_trials
from OpenAITools.CrinicalTrialTools import SimpleClinicalTrialAgent, GraderAgent, LLMTranslator, generate_ex_question_English
if LANGCHAIN_AVAILABLE:
FULL_VERSION = True
print("✅ 完全版モジュールが正常にロードされました")
except ImportError as e:
print(f"⚠️ 完全版モジュールのインポートに失敗: {e}")
print("軽量版モードで動作します")
# 環境変数チェック
def check_environment():
"""環境変数をチェックし、不足している場合は警告"""
missing_vars = []
# GROQ_API_KEYは必須
groq_key_available = bool(os.getenv("GROQ_API_KEY"))
if not groq_key_available:
missing_vars.append("GROQ_API_KEY")
# OPENAI_API_KEYはオプション
openai_key_available = bool(os.getenv("OPENAI_API_KEY"))
if not openai_key_available:
print("ℹ️ OPENAI_API_KEY が設定されていません(オプション機能)")
if missing_vars:
print(f"⚠️ 必須環境変数が設定されていません: {', '.join(missing_vars)}")
print("AI評価機能が制限される可能性があります。")
# GROQ_API_KEYがあれば完全版として動作可能
return groq_key_available
# 環境変数チェック実行
env_ok = check_environment()
# モデルとエージェントの安全な初期化
def safe_init_agents():
"""エージェントを安全に初期化"""
if not FULL_VERSION:
return None, None, None
try:
groq = ChatGroq(model_name="llama3-70b-8192", temperature=0)
translator = LLMTranslator(groq)
criteria_agent = SimpleClinicalTrialAgent(groq)
grader_agent = GraderAgent(groq)
print("✅ AIエージェントが正常に初期化されました")
return translator, criteria_agent, grader_agent
except Exception as e:
print(f"❌ エージェント初期化エラー: {e}")
return None, None, None
# エージェント初期化
translator, CriteriaCheckAgent, grader_agent = safe_init_agents()
# エラーハンドリング付きでエージェント評価を実行する関数
def evaluate_with_retry(agent, criteria, question, max_retries=3):
"""エラーハンドリング付きでエージェント評価を実行"""
if agent is None:
return "評価エラー: エージェントが初期化されていません。API keyを確認してください。"
for attempt in range(max_retries):
try:
return agent.evaluate_eligibility(criteria, question)
except Exception as e:
if "missing variables" in str(e):
print(f"プロンプトテンプレートエラー (試行 {attempt + 1}/{max_retries}): {e}")
return "評価エラー: プロンプトテンプレートの設定に問題があります"
elif "no healthy upstream" in str(e) or "InternalServerError" in str(e):
print(f"Groqサーバーエラー (試行 {attempt + 1}/{max_retries}): {e}")
if attempt < max_retries - 1:
time.sleep(2)
continue
else:
return "評価エラー: サーバーに接続できませんでした"
elif "API key" in str(e) or "authentication" in str(e).lower():
return "評価エラー: API keyが無効または設定されていません"
else:
print(f"予期しないエラー (試行 {attempt + 1}/{max_retries}): {e}")
if attempt < max_retries - 1:
time.sleep(1)
continue
else:
return f"評価エラー: {str(e)}"
return "評価エラー: 最大リトライ回数に達しました"
def evaluate_grade_with_retry(agent, judgment, max_retries=3):
"""エラーハンドリング付きでグレード評価を実行"""
if agent is None:
return "unclear"
for attempt in range(max_retries):
try:
return agent.evaluate_eligibility(judgment)
except Exception as e:
if "no healthy upstream" in str(e) or "InternalServerError" in str(e):
print(f"Groqサーバーエラー (グレード評価 - 試行 {attempt + 1}/{max_retries}): {e}")
if attempt < max_retries - 1:
time.sleep(2)
continue
else:
return "unclear"
elif "API key" in str(e) or "authentication" in str(e).lower():
return "unclear"
else:
print(f"予期しないエラー (グレード評価 - 試行 {attempt + 1}/{max_retries}): {e}")
if attempt < max_retries - 1:
time.sleep(1)
continue
else:
return "unclear"
return "unclear"
# 基本的なClinicalTrials.gov API呼び出し(軽量版)
def fetch_clinical_trials_basic(cancer_name):
"""基本的な臨床試験データ取得(requestsのみ使用)"""
try:
search_expr = f"{cancer_name} SEARCH[Location](AREA[LocationCountry]Japan AND AREA[LocationStatus]Recruiting)"
base_url = "https://clinicaltrials.gov/api/v2/studies"
params = {
"query.titles": search_expr,
"pageSize": 20 # 軽量版では20件に制限
}
print(f"基本API呼び出し: {cancer_name}")
response = requests.get(base_url, params=params)
if response.status_code == 200:
data = response.json()
studies = data.get('studies', [])
data_list = []
for study in studies:
nctId = study['protocolSection']['identificationModule'].get('nctId', 'Unknown')
title = study['protocolSection']['identificationModule'].get('briefTitle', 'no title')
conditions = ', '.join(study['protocolSection']['conditionsModule'].get('conditions', ['No conditions listed']))
summary = study['protocolSection']['descriptionModule'].get('briefSummary', 'no summary')
# 場所情報の抽出
locations_list = study['protocolSection'].get('contactsLocationsModule', {}).get('locations', [])
japan_locations = []
for location in locations_list:
if location.get('country') == 'Japan':
city = location.get('city', 'Unknown City')
japan_locations.append(city)
primaryCompletionDate = study['protocolSection']['statusModule'].get('primaryCompletionDateStruct', {}).get('date', 'Unknown Date')
eligibilityCriteria = study['protocolSection']['eligibilityModule'].get('eligibilityCriteria', 'Unknown')
data_list.append({
"NCTID": nctId,
"Title": title,
"Primary Completion Date": primaryCompletionDate,
"Cancer": conditions,
"Summary": summary,
"Japanes Locations": ', '.join(set(japan_locations)) if japan_locations else "No Japan locations",
"Eligibility Criteria": eligibilityCriteria
})
return data_list
else:
print(f"API呼び出し失敗: {response.status_code}")
return []
except Exception as e:
print(f"基本API呼び出しエラー: {e}")
return []
# 軽量版データ生成関数
def generate_sample_data(age, sex, tumor_type, GeneMutation, Meseable, Biopsiable):
"""サンプルデータを生成(辞書のリスト形式)"""
try:
if not all([age, sex, tumor_type]):
return []
sample_data = [
{
"NCTID": "NCT12345678",
"AgentGrade": "yes",
"Title": f"Clinical Trial for {tumor_type} in {sex} patients",
"AgentJudgment": f"{age}歳{sex}の{tumor_type}患者は参加可能です。詳細な検査結果により最終判断が必要です。",
"Japanes Locations": "Tokyo, Osaka",
"Primary Completion Date": "2025-12-31",
"Cancer": tumor_type,
"Summary": f"Phase II study evaluating new treatment for {tumor_type}",
"Eligibility Criteria": f"Age 18-75, confirmed {tumor_type}, adequate organ function"
},
{
"NCTID": "NCT87654321",
"AgentGrade": "no",
"Title": f"Alternative treatment for {tumor_type}",
"AgentJudgment": f"{age}歳{sex}の{tumor_type}患者は年齢制限により参加できません。",
"Japanes Locations": "Kyoto, Fukuoka",
"Primary Completion Date": "2026-06-30",
"Cancer": tumor_type,
"Summary": f"Comparative study of standard vs experimental therapy for {tumor_type}",
"Eligibility Criteria": f"Age 20-65, {tumor_type} with specific mutations, ECOG 0-1"
},
{
"NCTID": "NCT11111111",
"AgentGrade": "unclear",
"Title": f"Experimental therapy for {tumor_type} with {GeneMutation}",
"AgentJudgment": f"{age}歳{sex}の{tumor_type}患者の参加は追加情報が必要で不明確です。",
"Japanes Locations": "Nagoya, Sendai",
"Primary Completion Date": "2025-09-15",
"Cancer": tumor_type,
"Summary": f"Early phase trial testing combination therapy for {tumor_type}",
"Eligibility Criteria": f"Age 18-80, advanced {tumor_type}, previous treatment failure"
}
]
return sample_data
except Exception as e:
print(f"サンプルデータ生成エラー: {e}")
return []
# 基本版データ生成関数(ClinicalTrials.gov API使用、AI評価なし)
def generate_basic_data(age, sex, tumor_type, GeneMutation, Meseable, Biopsiable):
"""基本版のデータ生成(API使用、AI評価なし)"""
try:
if not all([age, sex, tumor_type]):
return []
# 実際のAPI呼び出し
data_list = fetch_clinical_trials_basic(tumor_type)
if not data_list:
print("臨床試験データが見つかりませんでした")
return []
# AI評価なしのプレースホルダーを追加
for item in data_list:
item['AgentJudgment'] = f'基本版:{age}歳{sex}の{tumor_type}患者への詳細評価にはAI機能が必要です'
item['AgentGrade'] = 'unclear'
print(f"基本版評価完了。結果: {len(data_list)} 件")
return data_list
except Exception as e:
print(f"基本版データ生成中に予期しないエラー: {e}")
traceback.print_exc()
return []
# 完全版データ生成関数(AI評価付き)
def generate_full_data(age, sex, tumor_type, GeneMutation, Meseable, Biopsiable):
"""完全版のデータ生成(実際のAPI使用 + AI評価)"""
try:
if not all([age, sex, tumor_type]):
return []
# 日本語の腫瘍タイプを英語に翻訳
try:
if translator is not None:
TumorName = translator.translate(tumor_type)
print(f"腫瘍タイプ翻訳: {tumor_type} → {TumorName}")
else:
print("翻訳エージェントが利用できません。元の値を使用します。")
TumorName = tumor_type
except Exception as e:
print(f"翻訳エラー: {e}")
TumorName = tumor_type
# 質問文を生成
try:
ex_question = generate_ex_question_English(age, sex, TumorName, GeneMutation, Meseable, Biopsiable)
print(f"生成された質問: {ex_question}")
except Exception as e:
print(f"質問生成エラー: {e}")
return []
# 臨床試験データの取得
try:
print(f"臨床試験データを検索中: {TumorName}")
df = fetch_clinical_trials(TumorName)
if df.empty:
print("臨床試験データが見つかりませんでした")
return []
print(f"取得した臨床試験数: {len(df)}")
# DataFrameを辞書のリストに変換
data_list = df.to_dict('records')
except Exception as e:
print(f"臨床試験データ取得エラー: {e}")
return []
# AI評価の実行(最大10件まで)
evaluation_limit = min(len(data_list), 10)
print(f"AI評価実行: {evaluation_limit} 件を処理します")
for i, item in enumerate(data_list[:evaluation_limit]):
try:
print(f"評価中 ({i+1}/{evaluation_limit}): {item['NCTID']}")
target_criteria = item['Eligibility Criteria']
# エラーハンドリング付きで評価実行
agent_judgment = evaluate_with_retry(CriteriaCheckAgent, target_criteria, ex_question)
agent_grade = evaluate_grade_with_retry(grader_agent, agent_judgment)
# データの更新
item['AgentJudgment'] = agent_judgment
item['AgentGrade'] = agent_grade
except Exception as e:
print(f"NCTID {item['NCTID']} の評価中にエラー: {e}")
item['AgentJudgment'] = f"エラー: {str(e)}"
item['AgentGrade'] = "unclear"
# 評価されなかった残りのアイテムにはプレースホルダーを設定
for item in data_list[evaluation_limit:]:
item['AgentJudgment'] = f"完全版:{age}歳{sex}の{tumor_type}患者(評価制限により未処理)"
item['AgentGrade'] = "unclear"
print(f"完全版評価完了。結果: {len(data_list)} 件(うち{evaluation_limit}件をAI評価)")
return data_list
except Exception as e:
print(f"完全版データ生成中に予期しないエラー: {e}")
traceback.print_exc()
return []
# HTMLテーブル生成関数
def create_html_table(data, show_grade=True):
"""データをHTMLテーブルに変換"""
if not data:
return "<div style='text-align: center; padding: 20px; color: #666;'>📄 データがありません</div>"
# CSS スタイル
table_style = """
<style>
.clinical-table {
width: 100%;
border-collapse: collapse;
margin: 10px 0;
font-family: Arial, sans-serif;
font-size: 14px;
}
.clinical-table th {
background-color: #f8f9fa;
border: 1px solid #dee2e6;
padding: 12px 8px;
text-align: left;
font-weight: bold;
color: #495057;
}
.clinical-table td {
border: 1px solid #dee2e6;
padding: 10px 8px;
vertical-align: top;
}
.grade-yes { background-color: #d4edda; }
.grade-no { background-color: #f8d7da; }
.grade-unclear { background-color: #fff3cd; }
.clinical-table tr:hover {
background-color: #f5f5f5;
}
.nctid-link {
color: #007bff;
text-decoration: none;
font-weight: bold;
}
.nctid-link:hover {
text-decoration: underline;
}
.title-cell {
max-width: 300px;
word-wrap: break-word;
}
.criteria-cell {
max-width: 400px;
word-wrap: break-word;
font-size: 12px;
}
</style>
"""
# テーブルヘッダー
html = table_style + '<table class="clinical-table">'
html += '<tr>'
html += '<th>NCTID</th>'
if show_grade:
html += '<th>Grade</th>'
html += '<th>Title</th>'
if show_grade:
html += '<th>AI Judgment</th>'
html += '<th>Japanese Locations</th>'
html += '<th>Completion Date</th>'
html += '<th>Cancer Type</th>'
html += '</tr>'
# データ行
for item in data:
grade = item.get('AgentGrade', 'unclear')
grade_class = f"grade-{grade}" if show_grade else ""
html += f'<tr class="{grade_class}">'
# NCTID(リンク付き)
nctid = item.get('NCTID', '')
html += f'<td><a href="https://clinicaltrials.gov/ct2/show/{nctid}" target="_blank" class="nctid-link">{nctid}</a></td>'
# Grade
if show_grade:
grade_emoji = {'yes': '✅', 'no': '❌', 'unclear': '❓'}.get(grade, '❓')
html += f'<td style="text-align: center;">{grade_emoji} {grade}</td>'
# Title
title = item.get('Title', '').strip()
html += f'<td class="title-cell">{title}</td>'
# AI Judgment
if show_grade:
judgment = item.get('AgentJudgment', '').strip()
html += f'<td class="criteria-cell">{judgment}</td>'
# Japanese Locations
locations = item.get('Japanes Locations', '').strip()
html += f'<td>{locations}</td>'
# Completion Date
completion_date = item.get('Primary Completion Date', '').strip()
html += f'<td>{completion_date}</td>'
# Cancer Type
cancer = item.get('Cancer', '').strip()
html += f'<td>{cancer}</td>'
html += '</tr>'
html += '</table>'
# 統計情報
if show_grade:
total = len(data)
yes_count = len([item for item in data if item.get('AgentGrade') == 'yes'])
no_count = len([item for item in data if item.get('AgentGrade') == 'no'])
unclear_count = len([item for item in data if item.get('AgentGrade') == 'unclear'])
stats_html = f"""
<div style='margin: 10px 0; padding: 10px; background-color: #f8f9fa; border-radius: 5px; font-size: 14px;'>
<strong>📊 統計:</strong>
合計 {total} 件 |
✅ 適格 {yes_count} 件 |
❌ 不適格 {no_count} 件 |
❓ 要検討 {unclear_count} 件
</div>
"""
html = stats_html + html
return html
# フィルタリング関数
def filter_data(data, grade):
"""データをフィルタリング"""
if not data:
return []
try:
if grade == "all":
return data
return [item for item in data if item.get('AgentGrade') == grade]
except Exception as e:
print(f"フィルタリングエラー: {e}")
return data
# システム状態の確認
def get_system_status():
"""システムの現在の状態を確認"""
groq_available = bool(os.getenv("GROQ_API_KEY"))
if FULL_VERSION and groq_available:
return "🟢 完全版", "リアルタイム検索 + AI適格性評価が利用可能です"
elif FULL_VERSION and not groq_available:
return "🟡 完全版(制限)", "GROQ_API_KEYが必要です(Settings → Variables and secrets で設定)"
elif LANGCHAIN_AVAILABLE:
return "🟡 基本版", "ClinicalTrials.gov検索のみ利用可能(AI評価にはGROQ_API_KEY必要)"
else:
return "🔴 軽量版", "サンプルデータのみ表示"
# CSV エクスポート関数
def export_to_csv(data):
"""データをCSVファイルとしてエクスポート"""
try:
if not data:
return None
# DataFrame に変換
df = pd.DataFrame(data)
# ファイルパス
file_path = "clinical_trials_data.csv"
df.to_csv(file_path, index=False, encoding='utf-8-sig')
return file_path
except Exception as e:
print(f"CSV エクスポートエラー: {e}")
return None
# Gradioインターフェースの作成
with gr.Blocks(title="臨床試験適格性評価", theme=gr.themes.Soft()) as demo:
gr.Markdown("## 🏥 臨床試験適格性評価インターフェース(完全版)")
# システム状態表示
status_level, status_message = get_system_status()
gr.Markdown(f"**システム状態**: {status_level} - {status_message}")
# 機能説明
groq_available = bool(os.getenv("GROQ_API_KEY"))
if FULL_VERSION and groq_available:
gr.Markdown("🚀 **利用可能機能**: ClinicalTrials.gov リアルタイム検索 + AI適格性評価 + データエクスポート")
gr.Markdown("🤖 **AI機能**: Groq Llama3-70B による自動適格性判断 + 3段階グレード評価")
elif FULL_VERSION:
gr.Markdown("🔧 **利用可能機能**: リアルタイム検索 + 基本評価(AI機能にはGROQ_API_KEY必要)")
gr.Markdown("⚠️ **API設定が必要**: Settings → Variables and secrets で GROQ_API_KEY を設定してください")
elif LANGCHAIN_AVAILABLE:
gr.Markdown("🔧 **利用可能機能**: ClinicalTrials.gov検索 + 基本評価 + データエクスポート")
else:
gr.Markdown("📋 **利用可能機能**: サンプルデータ表示 + フィルタリング")
gr.Markdown("💡 **使用方法**: 患者情報を入力してボタンをクリックしてください。")
# 各種入力フィールド
with gr.Row():
with gr.Column():
age_input = gr.Textbox(label="Age", placeholder="例: 65", value="65")
sex_input = gr.Dropdown(choices=["男性", "女性"], label="Sex", value="男性")
tumor_type_input = gr.Textbox(label="Tumor Type", placeholder="例: gastric cancer", value="gastric cancer")
with gr.Column():
gene_mutation_input = gr.Textbox(label="Gene Mutation", placeholder="例: HER2", value="HER2")
measurable_input = gr.Dropdown(choices=["有り", "無し", "不明"], label="Measurable Tumor", value="有り")
biopsiable_input = gr.Dropdown(choices=["有り", "無し", "不明"], label="Biopsiable Tumor", value="有り")
# 結果表示エリア(HTMLテーブル)
results_html = gr.HTML(label="Clinical Trials Results")
# 内部状態用
data_state = gr.State(value=[])
# ボタン類
with gr.Row():
if FULL_VERSION and groq_available:
generate_button = gr.Button("🤖 AI適格性評価付き検索(完全版)", variant="primary")
elif FULL_VERSION:
generate_button = gr.Button("🔍 リアルタイム検索(GROQ_API_KEY設定後にAI評価有効)", variant="primary")
elif LANGCHAIN_AVAILABLE:
generate_button = gr.Button("📡 ClinicalTrials.gov検索(基本版)", variant="primary")
else:
generate_button = gr.Button("📋 サンプルデータ表示", variant="primary")
with gr.Row():
yes_button = gr.Button("✅ Show Eligible Trials", variant="secondary")
no_button = gr.Button("❌ Show Ineligible Trials", variant="secondary")
unclear_button = gr.Button("❓ Show Unclear Trials", variant="secondary")
all_button = gr.Button("📊 Show All Trials", variant="secondary")
with gr.Row():
download_button = gr.Button("💾 Download CSV")
# ダウンロードファイル
download_output = gr.File(label="Download CSV", visible=False)
# プログレス表示
progress_text = gr.Textbox(label="Processing Status", value="Ready", interactive=False)
# イベントハンドリング
def update_data_and_display(age, sex, tumor_type, gene_mutation, measurable, biopsiable):
"""データ生成と表示更新"""
try:
groq_available = bool(os.getenv("GROQ_API_KEY"))
if FULL_VERSION and groq_available:
progress_msg = "🤖 AI適格性評価付きで実際の臨床試験データを検索中..."
data = generate_full_data(age, sex, tumor_type, gene_mutation, measurable, biopsiable)
elif FULL_VERSION:
progress_msg = "🔍 実際の臨床試験データを検索中(AI評価にはGROQ_API_KEY必要)..."
data = generate_basic_data(age, sex, tumor_type, gene_mutation, measurable, biopsiable)
elif LANGCHAIN_AVAILABLE:
progress_msg = "📡 ClinicalTrials.govから基本データを検索中..."
data = generate_basic_data(age, sex, tumor_type, gene_mutation, measurable, biopsiable)
else:
progress_msg = "📋 サンプルデータを生成中..."
data = generate_sample_data(age, sex, tumor_type, gene_mutation, measurable, biopsiable)
if data:
html_table = create_html_table(data)
final_progress = f"✅ 完了: {len(data)} 件の臨床試験が見つかりました"
if FULL_VERSION and env_ok:
ai_count = len([item for item in data if 'エラー' not in item.get('AgentJudgment', '')])
final_progress += f"(うち最大10件をAI評価済み)"
else:
html_table = "<div style='text-align: center; padding: 20px; color: #666;'>⚠️ 該当する臨床試験が見つかりませんでした</div>"
final_progress = "⚠️ 該当する臨床試験が見つかりませんでした"
return html_table, data, final_progress
except Exception as e:
error_msg = f"❌ エラー: {str(e)}"
error_html = f"<div style='text-align: center; padding: 20px; color: #d32f2f;'>{error_msg}</div>"
print(f"データ更新エラー: {e}")
return error_html, [], error_msg
def filter_and_show(data, grade):
"""フィルタリングと表示更新"""
try:
filtered_data = filter_data(data, grade)
html_table = create_html_table(filtered_data)
return html_table
except Exception as e:
print(f"フィルタリングエラー: {e}")
return create_html_table(data)
def download_csv(data):
"""CSV ダウンロード処理"""
try:
if not data:
return None
return export_to_csv(data)
except Exception as e:
print(f"ダウンロードエラー: {e}")
return None
# ボタンイベント
generate_button.click(
fn=update_data_and_display,
inputs=[age_input, sex_input, tumor_type_input, gene_mutation_input, measurable_input, biopsiable_input],
outputs=[results_html, data_state, progress_text]
)
yes_button.click(
fn=lambda data: filter_and_show(data, "yes"),
inputs=[data_state],
outputs=[results_html]
)
no_button.click(
fn=lambda data: filter_and_show(data, "no"),
inputs=[data_state],
outputs=[results_html]
)
unclear_button.click(
fn=lambda data: filter_and_show(data, "unclear"),
inputs=[data_state],
outputs=[results_html]
)
all_button.click(
fn=lambda data: filter_and_show(data, "all"),
inputs=[data_state],
outputs=[results_html]
)
download_button.click(
fn=download_csv,
inputs=[data_state],
outputs=[download_output]
)
# フッター情報
gr.Markdown("---")
with gr.Row():
groq_available_footer = bool(os.getenv("GROQ_API_KEY"))
gr.Markdown("🔬 **技術情報**: ClinicalTrials.gov API + LangChain + Groq Llama3-70B")
gr.Markdown("📝 **AI機能状況**: " + ("AI評価機能有効" if (FULL_VERSION and groq_available_footer) else "GROQ_API_KEY設定後にAI機能有効化"))
if __name__ == "__main__":
demo.launch(
server_name="0.0.0.0",
server_port=7860,
share=False,
debug=False,
show_error=True
)
|