Spaces:
Running
Running
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 | |
) | |