Spaces:
Running
Running
高橋慧
commited on
Commit
·
c33e580
1
Parent(s):
636cfae
light editon
Browse files- README.md +23 -6
- app.py +122 -283
- appOld3.py +321 -0
- app_fixed.py +321 -0
- requirements.txt +5 -13
- requirements_old3.txt +50 -0
- requirements_spaces.txt +26 -0
README.md
CHANGED
@@ -1,12 +1,29 @@
|
|
1 |
---
|
2 |
title: ClinicalTrialV2
|
3 |
-
emoji:
|
4 |
-
colorFrom:
|
5 |
-
colorTo:
|
6 |
sdk: gradio
|
7 |
-
sdk_version:
|
8 |
-
app_file:
|
9 |
pinned: false
|
|
|
10 |
---
|
11 |
|
12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
---
|
2 |
title: ClinicalTrialV2
|
3 |
+
emoji: 🏥
|
4 |
+
colorFrom: blue
|
5 |
+
colorTo: green
|
6 |
sdk: gradio
|
7 |
+
sdk_version: 4.36.1
|
8 |
+
app_file: app_fixed.py
|
9 |
pinned: false
|
10 |
+
license: mit
|
11 |
---
|
12 |
|
13 |
+
# 臨床試験適格性評価システム
|
14 |
+
|
15 |
+
このアプリケーションは患者情報に基づいて適切な臨床試験を見つけ、AIエージェントが適格性を自動評価するシステムです。
|
16 |
+
|
17 |
+
## 必要な環境変数
|
18 |
+
|
19 |
+
Spacesの設定で以下の環境変数を設定してください:
|
20 |
+
|
21 |
+
- `GROQ_API_KEY`: GroqのAPIキー
|
22 |
+
- `OPENAI_API_KEY`: OpenAIのAPIキー(オプション)
|
23 |
+
|
24 |
+
## 使用方法
|
25 |
+
|
26 |
+
1. 患者の基本情報(年齢、性別、腫瘍タイプ)を入力
|
27 |
+
2. 遺伝子変異情報、測定可能腫瘍の有無を入力
|
28 |
+
3. 「Generate Clinical Trials Data」をクリック
|
29 |
+
4. 結果をフィルタリング・ダウンロード
|
app.py
CHANGED
@@ -1,12 +1,6 @@
|
|
1 |
import gradio as gr
|
2 |
import pandas as pd
|
3 |
-
import time
|
4 |
-
import traceback
|
5 |
import os
|
6 |
-
from OpenAITools.FetchTools import fetch_clinical_trials
|
7 |
-
from langchain_openai import ChatOpenAI
|
8 |
-
from langchain_groq import ChatGroq
|
9 |
-
from OpenAITools.CrinicalTrialTools import SimpleClinicalTrialAgent, GraderAgent, LLMTranslator, generate_ex_question_English
|
10 |
|
11 |
# 環境変数チェック
|
12 |
def check_environment():
|
@@ -19,303 +13,148 @@ def check_environment():
|
|
19 |
if not os.getenv("OPENAI_API_KEY"):
|
20 |
missing_vars.append("OPENAI_API_KEY")
|
21 |
|
22 |
-
|
23 |
-
print(f"⚠️ 環境変数が設定されていません: {', '.join(missing_vars)}")
|
24 |
-
print("一部の機能が制限される可能性があります。")
|
25 |
-
|
26 |
-
return len(missing_vars) == 0
|
27 |
-
|
28 |
-
# 環境変数チェック実行
|
29 |
-
env_ok = check_environment()
|
30 |
-
|
31 |
-
# モデルとエージェントの安全な初期化
|
32 |
-
def safe_init_agents():
|
33 |
-
"""エージェントを安全に初期化"""
|
34 |
-
try:
|
35 |
-
groq = ChatGroq(model_name="llama3-70b-8192", temperature=0)
|
36 |
-
translator = LLMTranslator(groq)
|
37 |
-
criteria_agent = SimpleClinicalTrialAgent(groq)
|
38 |
-
grader_agent = GraderAgent(groq)
|
39 |
-
return translator, criteria_agent, grader_agent
|
40 |
-
except Exception as e:
|
41 |
-
print(f"エージェント初期化エラー: {e}")
|
42 |
-
return None, None, None
|
43 |
-
|
44 |
-
# エージェント初期化
|
45 |
-
translator, CriteriaCheckAgent, grader_agent = safe_init_agents()
|
46 |
-
|
47 |
-
# エラーハンドリング付きでエージェント評価を実行する関数
|
48 |
-
def evaluate_with_retry(agent, criteria, question, max_retries=3):
|
49 |
-
"""エラーハンドリング付きでエージェント評価を実行"""
|
50 |
-
if agent is None:
|
51 |
-
return "評価エラー: エージェントが初期化されていません。API keyを確認してください。"
|
52 |
-
|
53 |
-
for attempt in range(max_retries):
|
54 |
-
try:
|
55 |
-
return agent.evaluate_eligibility(criteria, question)
|
56 |
-
except Exception as e:
|
57 |
-
if "missing variables" in str(e):
|
58 |
-
# プロンプトテンプレートの変数エラーの場合
|
59 |
-
print(f"プロンプトテンプレートエラー (試行 {attempt + 1}/{max_retries}): {e}")
|
60 |
-
return "評価エラー: プロンプトテンプレートの設定に問題があります"
|
61 |
-
elif "no healthy upstream" in str(e) or "InternalServerError" in str(e):
|
62 |
-
# Groqサーバーエラーの場合
|
63 |
-
print(f"Groqサーバーエラー (試行 {attempt + 1}/{max_retries}): {e}")
|
64 |
-
if attempt < max_retries - 1:
|
65 |
-
time.sleep(2) # 2秒待機してリトライ
|
66 |
-
continue
|
67 |
-
else:
|
68 |
-
return "評価エラー: サーバーに接続できませんでした"
|
69 |
-
elif "API key" in str(e) or "authentication" in str(e).lower():
|
70 |
-
return "評価エラー: API keyが無効または設定されていません"
|
71 |
-
else:
|
72 |
-
print(f"予期しないエラー (試行 {attempt + 1}/{max_retries}): {e}")
|
73 |
-
if attempt < max_retries - 1:
|
74 |
-
time.sleep(1)
|
75 |
-
continue
|
76 |
-
else:
|
77 |
-
return f"評価エラー: {str(e)}"
|
78 |
-
return "評価エラー: 最大リトライ回数に達しました"
|
79 |
-
|
80 |
-
def evaluate_grade_with_retry(agent, judgment, max_retries=3):
|
81 |
-
"""エラーハンドリング付きでグレード評価を実行"""
|
82 |
-
if agent is None:
|
83 |
-
return "unclear"
|
84 |
-
|
85 |
-
for attempt in range(max_retries):
|
86 |
-
try:
|
87 |
-
return agent.evaluate_eligibility(judgment)
|
88 |
-
except Exception as e:
|
89 |
-
if "no healthy upstream" in str(e) or "InternalServerError" in str(e):
|
90 |
-
print(f"Groqサーバーエラー (グレード評価 - 試行 {attempt + 1}/{max_retries}): {e}")
|
91 |
-
if attempt < max_retries - 1:
|
92 |
-
time.sleep(2)
|
93 |
-
continue
|
94 |
-
else:
|
95 |
-
return "unclear"
|
96 |
-
elif "API key" in str(e) or "authentication" in str(e).lower():
|
97 |
-
return "unclear"
|
98 |
-
else:
|
99 |
-
print(f"予期しないエラー (グレード評価 - 試行 {attempt + 1}/{max_retries}): {e}")
|
100 |
-
if attempt < max_retries - 1:
|
101 |
-
time.sleep(1)
|
102 |
-
continue
|
103 |
-
else:
|
104 |
-
return "unclear"
|
105 |
-
return "unclear"
|
106 |
|
107 |
-
#
|
108 |
-
def
|
|
|
109 |
try:
|
110 |
# 入力検証
|
111 |
if not all([age, sex, tumor_type]):
|
112 |
-
return pd.DataFrame()
|
113 |
|
114 |
-
#
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
|
132 |
-
|
133 |
-
|
134 |
-
df = fetch_clinical_trials(TumorName)
|
135 |
-
if df.empty:
|
136 |
-
print("臨床試験データが見つかりませんでした")
|
137 |
-
return pd.DataFrame(), pd.DataFrame()
|
138 |
-
except Exception as e:
|
139 |
-
print(f"臨床試験データ取得エラー: {e}")
|
140 |
-
return pd.DataFrame(), pd.DataFrame()
|
141 |
|
142 |
-
df['AgentJudgment'] = None
|
143 |
-
df['AgentGrade'] = None
|
144 |
-
|
145 |
-
# 臨床試験の適格性の評価
|
146 |
-
NCTIDs = list(df['NCTID'])
|
147 |
-
progress = gr.Progress(track_tqdm=True)
|
148 |
-
|
149 |
-
for i, nct_id in enumerate(NCTIDs):
|
150 |
-
try:
|
151 |
-
target_criteria = df.loc[df['NCTID'] == nct_id, 'Eligibility Criteria'].values[0]
|
152 |
-
|
153 |
-
# エラーハンドリング付きで評価実行
|
154 |
-
agent_judgment = evaluate_with_retry(CriteriaCheckAgent, target_criteria, ex_question)
|
155 |
-
agent_grade = evaluate_grade_with_retry(grader_agent, agent_judgment)
|
156 |
-
|
157 |
-
# データフレームの更新
|
158 |
-
df.loc[df['NCTID'] == nct_id, 'AgentJudgment'] = agent_judgment
|
159 |
-
df.loc[df['NCTID'] == nct_id, 'AgentGrade'] = agent_grade
|
160 |
-
|
161 |
-
except Exception as e:
|
162 |
-
print(f"NCTID {nct_id} の評価中にエラー: {e}")
|
163 |
-
df.loc[df['NCTID'] == nct_id, 'AgentJudgment'] = f"エラー: {str(e)}"
|
164 |
-
df.loc[df['NCTID'] == nct_id, 'AgentGrade'] = "unclear"
|
165 |
-
|
166 |
-
progress((i + 1) / len(NCTIDs))
|
167 |
-
|
168 |
-
# 列を指定した順に並び替え
|
169 |
-
columns_order = ['NCTID', 'AgentGrade', 'Title', 'AgentJudgment', 'Japanes Locations',
|
170 |
-
'Primary Completion Date', 'Cancer', 'Summary', 'Eligibility Criteria']
|
171 |
-
|
172 |
-
# 存在する列のみを選択
|
173 |
-
available_columns = [col for col in columns_order if col in df.columns]
|
174 |
-
df = df[available_columns]
|
175 |
-
|
176 |
-
return df, df # フィルタ用と表示用にデータフレームを返す
|
177 |
-
|
178 |
-
except Exception as e:
|
179 |
-
print(f"データフレーム生成中に予期しないエラー: {e}")
|
180 |
-
traceback.print_exc()
|
181 |
-
return pd.DataFrame(), pd.DataFrame()
|
182 |
-
|
183 |
-
# CSVとして保存しダウンロードする関数
|
184 |
-
def download_filtered_csv(df):
|
185 |
-
try:
|
186 |
-
if df is None or len(df) == 0:
|
187 |
-
return None
|
188 |
-
file_path = "filtered_data.csv"
|
189 |
-
df.to_csv(file_path, index=False)
|
190 |
-
return file_path
|
191 |
except Exception as e:
|
192 |
-
print(f"
|
193 |
-
return
|
194 |
|
195 |
-
#
|
196 |
-
def
|
|
|
|
|
|
|
|
|
197 |
try:
|
198 |
-
if
|
199 |
-
return
|
200 |
-
|
201 |
-
df.to_csv(file_path, index=False)
|
202 |
-
return file_path
|
203 |
except Exception as e:
|
204 |
-
print(f"
|
205 |
-
return
|
206 |
|
207 |
# Gradioインターフェースの作成
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
# 環境変数状態の表示
|
212 |
-
if env_ok:
|
213 |
-
gr.Markdown("✅ **ステータス**: 全ての環境変数が設定されています")
|
214 |
-
else:
|
215 |
-
gr.Markdown("⚠️ **注意**: 一部の環境変数が設定されていません。機能が制限される可能性があります。")
|
216 |
|
217 |
-
gr.
|
218 |
-
|
219 |
-
# 各種入力フィールド
|
220 |
-
with gr.Row():
|
221 |
-
with gr.Column():
|
222 |
-
age_input = gr.Textbox(label="Age", placeholder="例: 65", value="")
|
223 |
-
sex_input = gr.Dropdown(choices=["男性", "女性"], label="Sex", value=None)
|
224 |
-
tumor_type_input = gr.Textbox(label="Tumor Type", placeholder="例: gastric cancer", value="")
|
225 |
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
# データフレーム表示エリア
|
232 |
-
dataframe_output = gr.DataFrame(
|
233 |
-
headers=["NCTID", "AgentGrade", "Title", "AgentJudgment", "Status"],
|
234 |
-
datatype=["str", "str", "str", "str", "str"],
|
235 |
-
value=None
|
236 |
-
)
|
237 |
-
|
238 |
-
# 内部状態用の非表示コンポーネント
|
239 |
-
original_df_state = gr.State(value=None)
|
240 |
-
filtered_df_state = gr.State(value=None)
|
241 |
-
|
242 |
-
# ボタン類
|
243 |
-
with gr.Row():
|
244 |
-
generate_button = gr.Button("Generate Clinical Trials Data", variant="primary")
|
245 |
-
|
246 |
-
with gr.Row():
|
247 |
-
yes_button = gr.Button("Show Eligible Trials", variant="secondary")
|
248 |
-
no_button = gr.Button("Show Ineligible Trials", variant="secondary")
|
249 |
-
unclear_button = gr.Button("Show Unclear Trials", variant="secondary")
|
250 |
-
|
251 |
-
with gr.Row():
|
252 |
-
download_filtered_button = gr.Button("Download Filtered Data")
|
253 |
-
download_full_button = gr.Button("Download Full Data")
|
254 |
-
|
255 |
-
# ダウンロードファイル
|
256 |
-
download_filtered_output = gr.File(label="Download Filtered Data", visible=False)
|
257 |
-
download_full_output = gr.File(label="Download Full Data", visible=False)
|
258 |
-
|
259 |
-
# イベントハンドリング
|
260 |
-
def update_dataframe_and_state(age, sex, tumor_type, gene_mutation, measurable, biopsiable):
|
261 |
-
"""データフレーム生成と状態更新"""
|
262 |
-
df, _ = generate_dataframe(age, sex, tumor_type, gene_mutation, measurable, biopsiable)
|
263 |
-
return df, df, df
|
264 |
-
|
265 |
-
def filter_and_update(original_df, grade):
|
266 |
-
"""フィルタリングと表示更新"""
|
267 |
-
if original_df is None or len(original_df) == 0:
|
268 |
-
return original_df, original_df
|
269 |
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
276 |
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
inputs=[age_input, sex_input, tumor_type_input, gene_mutation_input, measurable_input, biopsiable_input],
|
281 |
-
outputs=[dataframe_output, original_df_state, filtered_df_state]
|
282 |
-
)
|
283 |
-
|
284 |
-
yes_button.click(
|
285 |
-
fn=lambda df: filter_and_update(df, "yes"),
|
286 |
-
inputs=[original_df_state],
|
287 |
-
outputs=[dataframe_output, filtered_df_state]
|
288 |
-
)
|
289 |
-
|
290 |
-
no_button.click(
|
291 |
-
fn=lambda df: filter_and_update(df, "no"),
|
292 |
-
inputs=[original_df_state],
|
293 |
-
outputs=[dataframe_output, filtered_df_state]
|
294 |
-
)
|
295 |
-
|
296 |
-
unclear_button.click(
|
297 |
-
fn=lambda df: filter_and_update(df, "unclear"),
|
298 |
-
inputs=[original_df_state],
|
299 |
-
outputs=[dataframe_output, filtered_df_state]
|
300 |
-
)
|
301 |
-
|
302 |
-
download_filtered_button.click(
|
303 |
-
fn=download_filtered_csv,
|
304 |
-
inputs=[filtered_df_state],
|
305 |
-
outputs=[download_filtered_output]
|
306 |
-
)
|
307 |
-
|
308 |
-
download_full_button.click(
|
309 |
-
fn=download_full_csv,
|
310 |
-
inputs=[original_df_state],
|
311 |
-
outputs=[download_full_output]
|
312 |
-
)
|
313 |
-
|
314 |
if __name__ == "__main__":
|
|
|
315 |
demo.launch(
|
316 |
server_name="0.0.0.0",
|
317 |
server_port=7860,
|
318 |
share=False,
|
319 |
-
debug=
|
320 |
show_error=True
|
321 |
-
)
|
|
|
1 |
import gradio as gr
|
2 |
import pandas as pd
|
|
|
|
|
3 |
import os
|
|
|
|
|
|
|
|
|
4 |
|
5 |
# 環境変数チェック
|
6 |
def check_environment():
|
|
|
13 |
if not os.getenv("OPENAI_API_KEY"):
|
14 |
missing_vars.append("OPENAI_API_KEY")
|
15 |
|
16 |
+
return missing_vars
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
|
18 |
+
# 軽量版データ生成関数
|
19 |
+
def generate_sample_dataframe(age, sex, tumor_type, GeneMutation, Meseable, Biopsiable):
|
20 |
+
"""サンプルデータを生成(デバッグ用)"""
|
21 |
try:
|
22 |
# 入力検証
|
23 |
if not all([age, sex, tumor_type]):
|
24 |
+
return pd.DataFrame()
|
25 |
|
26 |
+
# サンプルデータの作成
|
27 |
+
sample_data = {
|
28 |
+
'NCTID': ['NCT12345678', 'NCT87654321', 'NCT11111111'],
|
29 |
+
'AgentGrade': ['yes', 'no', 'unclear'],
|
30 |
+
'Title': [
|
31 |
+
f'Clinical Trial for {tumor_type} in {sex} patients',
|
32 |
+
f'Alternative treatment for {tumor_type}',
|
33 |
+
f'Experimental therapy for {tumor_type} with {GeneMutation}'
|
34 |
+
],
|
35 |
+
'AgentJudgment': [
|
36 |
+
f'{age}歳{sex}の{tumor_type}患者は参加可能です',
|
37 |
+
f'{age}歳{sex}の{tumor_type}患者は参加できません',
|
38 |
+
f'{age}歳{sex}の{tumor_type}患者の参加は不明確です'
|
39 |
+
],
|
40 |
+
'Japanese_Locations': ['Tokyo', 'Osaka', 'Kyoto'],
|
41 |
+
'Cancer': [tumor_type, tumor_type, tumor_type]
|
42 |
+
}
|
43 |
|
44 |
+
df = pd.DataFrame(sample_data)
|
45 |
+
return df
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
except Exception as e:
|
48 |
+
print(f"データフレーム生成エラー: {e}")
|
49 |
+
return pd.DataFrame()
|
50 |
|
51 |
+
# フィルタリング関数
|
52 |
+
def filter_dataframe(df, grade):
|
53 |
+
"""データフレームをフィルタリング"""
|
54 |
+
if df is None or len(df) == 0:
|
55 |
+
return df
|
56 |
+
|
57 |
try:
|
58 |
+
if grade == "all":
|
59 |
+
return df
|
60 |
+
return df[df['AgentGrade'] == grade]
|
|
|
|
|
61 |
except Exception as e:
|
62 |
+
print(f"フィルタリングエラー: {e}")
|
63 |
+
return df
|
64 |
|
65 |
# Gradioインターフェースの作成
|
66 |
+
def create_interface():
|
67 |
+
missing_vars = check_environment()
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
|
69 |
+
with gr.Blocks(title="臨床試験適格性評価(軽量版)", theme=gr.themes.Soft()) as demo:
|
70 |
+
gr.Markdown("## 臨床試験適格性評価インターフェース(デバッグ版)")
|
|
|
|
|
|
|
|
|
|
|
|
|
71 |
|
72 |
+
# 環境変数状態の表示
|
73 |
+
if not missing_vars:
|
74 |
+
gr.Markdown("✅ **ステータス**: 全ての環境変数が設定されています")
|
75 |
+
else:
|
76 |
+
gr.Markdown(f"⚠️ **注意**: 環境変数が不足しています: {', '.join(missing_vars)}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
77 |
|
78 |
+
gr.Markdown("💡 **使用方法**: 患者情報を入力して「Generate Sample Data」をクリックしてください。")
|
79 |
+
|
80 |
+
# 入力フィールド
|
81 |
+
with gr.Row():
|
82 |
+
with gr.Column():
|
83 |
+
age_input = gr.Textbox(label="Age", placeholder="例: 65", value="65")
|
84 |
+
sex_input = gr.Dropdown(choices=["男性", "女性"], label="Sex", value="男性")
|
85 |
+
tumor_type_input = gr.Textbox(label="Tumor Type", placeholder="例: gastric cancer", value="gastric cancer")
|
86 |
+
|
87 |
+
with gr.Column():
|
88 |
+
gene_mutation_input = gr.Textbox(label="Gene Mutation", placeholder="例: HER2", value="HER2")
|
89 |
+
measurable_input = gr.Dropdown(choices=["有り", "無し", "不明"], label="Measurable Tumor", value="有り")
|
90 |
+
biopsiable_input = gr.Dropdown(choices=["有り", "無し", "不明"], label="Biopsiable Tumor", value="有り")
|
91 |
+
|
92 |
+
# データフレーム表示
|
93 |
+
dataframe_output = gr.DataFrame(
|
94 |
+
label="Clinical Trials Results",
|
95 |
+
interactive=False
|
96 |
+
)
|
97 |
+
|
98 |
+
# フィルタボタン
|
99 |
+
with gr.Row():
|
100 |
+
generate_button = gr.Button("Generate Sample Data", variant="primary")
|
101 |
+
all_button = gr.Button("Show All", variant="secondary")
|
102 |
+
yes_button = gr.Button("Show Eligible", variant="secondary")
|
103 |
+
no_button = gr.Button("Show Ineligible", variant="secondary")
|
104 |
+
unclear_button = gr.Button("Show Unclear", variant="secondary")
|
105 |
+
|
106 |
+
# 状態管理
|
107 |
+
df_state = gr.State(value=None)
|
108 |
+
|
109 |
+
# イベントハンドリング
|
110 |
+
def update_dataframe(age, sex, tumor_type, gene_mutation, measurable, biopsiable):
|
111 |
+
df = generate_sample_dataframe(age, sex, tumor_type, gene_mutation, measurable, biopsiable)
|
112 |
+
return df, df
|
113 |
+
|
114 |
+
def filter_and_show(df, grade):
|
115 |
+
filtered_df = filter_dataframe(df, grade)
|
116 |
+
return filtered_df
|
117 |
+
|
118 |
+
# ボタンイベント
|
119 |
+
generate_button.click(
|
120 |
+
fn=update_dataframe,
|
121 |
+
inputs=[age_input, sex_input, tumor_type_input, gene_mutation_input, measurable_input, biopsiable_input],
|
122 |
+
outputs=[dataframe_output, df_state]
|
123 |
+
)
|
124 |
+
|
125 |
+
all_button.click(
|
126 |
+
fn=lambda df: filter_and_show(df, "all"),
|
127 |
+
inputs=[df_state],
|
128 |
+
outputs=[dataframe_output]
|
129 |
+
)
|
130 |
+
|
131 |
+
yes_button.click(
|
132 |
+
fn=lambda df: filter_and_show(df, "yes"),
|
133 |
+
inputs=[df_state],
|
134 |
+
outputs=[dataframe_output]
|
135 |
+
)
|
136 |
+
|
137 |
+
no_button.click(
|
138 |
+
fn=lambda df: filter_and_show(df, "no"),
|
139 |
+
inputs=[df_state],
|
140 |
+
outputs=[dataframe_output]
|
141 |
+
)
|
142 |
+
|
143 |
+
unclear_button.click(
|
144 |
+
fn=lambda df: filter_and_show(df, "unclear"),
|
145 |
+
inputs=[df_state],
|
146 |
+
outputs=[dataframe_output]
|
147 |
+
)
|
148 |
|
149 |
+
return demo
|
150 |
+
|
151 |
+
# アプリケーション起動
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
152 |
if __name__ == "__main__":
|
153 |
+
demo = create_interface()
|
154 |
demo.launch(
|
155 |
server_name="0.0.0.0",
|
156 |
server_port=7860,
|
157 |
share=False,
|
158 |
+
debug=True,
|
159 |
show_error=True
|
160 |
+
)
|
appOld3.py
ADDED
@@ -0,0 +1,321 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import pandas as pd
|
3 |
+
import time
|
4 |
+
import traceback
|
5 |
+
import os
|
6 |
+
from OpenAITools.FetchTools import fetch_clinical_trials
|
7 |
+
from langchain_openai import ChatOpenAI
|
8 |
+
from langchain_groq import ChatGroq
|
9 |
+
from OpenAITools.CrinicalTrialTools import SimpleClinicalTrialAgent, GraderAgent, LLMTranslator, generate_ex_question_English
|
10 |
+
|
11 |
+
# 環境変数チェック
|
12 |
+
def check_environment():
|
13 |
+
"""環境変数をチェックし、不足している場合は警告"""
|
14 |
+
missing_vars = []
|
15 |
+
|
16 |
+
if not os.getenv("GROQ_API_KEY"):
|
17 |
+
missing_vars.append("GROQ_API_KEY")
|
18 |
+
|
19 |
+
if not os.getenv("OPENAI_API_KEY"):
|
20 |
+
missing_vars.append("OPENAI_API_KEY")
|
21 |
+
|
22 |
+
if missing_vars:
|
23 |
+
print(f"⚠️ 環境変数が設定されていません: {', '.join(missing_vars)}")
|
24 |
+
print("一部の機能が制限される可能性があります。")
|
25 |
+
|
26 |
+
return len(missing_vars) == 0
|
27 |
+
|
28 |
+
# 環境変数チェック実行
|
29 |
+
env_ok = check_environment()
|
30 |
+
|
31 |
+
# モデルとエージェントの安全な初期化
|
32 |
+
def safe_init_agents():
|
33 |
+
"""エージェントを安全に初期化"""
|
34 |
+
try:
|
35 |
+
groq = ChatGroq(model_name="llama3-70b-8192", temperature=0)
|
36 |
+
translator = LLMTranslator(groq)
|
37 |
+
criteria_agent = SimpleClinicalTrialAgent(groq)
|
38 |
+
grader_agent = GraderAgent(groq)
|
39 |
+
return translator, criteria_agent, grader_agent
|
40 |
+
except Exception as e:
|
41 |
+
print(f"エージェント初期化エラー: {e}")
|
42 |
+
return None, None, None
|
43 |
+
|
44 |
+
# エージェント初期化
|
45 |
+
translator, CriteriaCheckAgent, grader_agent = safe_init_agents()
|
46 |
+
|
47 |
+
# エラーハンドリング付きでエージェント評価を実行する関数
|
48 |
+
def evaluate_with_retry(agent, criteria, question, max_retries=3):
|
49 |
+
"""エラーハンドリング付きでエージェント評価を実行"""
|
50 |
+
if agent is None:
|
51 |
+
return "評価エラー: エージェントが初期化されていません。API keyを確認してください。"
|
52 |
+
|
53 |
+
for attempt in range(max_retries):
|
54 |
+
try:
|
55 |
+
return agent.evaluate_eligibility(criteria, question)
|
56 |
+
except Exception as e:
|
57 |
+
if "missing variables" in str(e):
|
58 |
+
# プロンプトテンプレートの変数エラーの場合
|
59 |
+
print(f"プロンプトテンプレートエラー (試行 {attempt + 1}/{max_retries}): {e}")
|
60 |
+
return "評価エラー: プロンプトテンプレートの設定に問題があります"
|
61 |
+
elif "no healthy upstream" in str(e) or "InternalServerError" in str(e):
|
62 |
+
# Groqサーバーエラーの場合
|
63 |
+
print(f"Groqサーバーエラー (試行 {attempt + 1}/{max_retries}): {e}")
|
64 |
+
if attempt < max_retries - 1:
|
65 |
+
time.sleep(2) # 2秒待機してリトライ
|
66 |
+
continue
|
67 |
+
else:
|
68 |
+
return "評価エラー: サーバーに接続できませんでした"
|
69 |
+
elif "API key" in str(e) or "authentication" in str(e).lower():
|
70 |
+
return "評価エラー: API keyが無効または設定されていません"
|
71 |
+
else:
|
72 |
+
print(f"予期しないエラー (試行 {attempt + 1}/{max_retries}): {e}")
|
73 |
+
if attempt < max_retries - 1:
|
74 |
+
time.sleep(1)
|
75 |
+
continue
|
76 |
+
else:
|
77 |
+
return f"評価エラー: {str(e)}"
|
78 |
+
return "評価エラー: 最大リトライ回数に達しました"
|
79 |
+
|
80 |
+
def evaluate_grade_with_retry(agent, judgment, max_retries=3):
|
81 |
+
"""エラーハンドリング付きでグレード評価を実行"""
|
82 |
+
if agent is None:
|
83 |
+
return "unclear"
|
84 |
+
|
85 |
+
for attempt in range(max_retries):
|
86 |
+
try:
|
87 |
+
return agent.evaluate_eligibility(judgment)
|
88 |
+
except Exception as e:
|
89 |
+
if "no healthy upstream" in str(e) or "InternalServerError" in str(e):
|
90 |
+
print(f"Groqサーバーエラー (グレード評価 - 試行 {attempt + 1}/{max_retries}): {e}")
|
91 |
+
if attempt < max_retries - 1:
|
92 |
+
time.sleep(2)
|
93 |
+
continue
|
94 |
+
else:
|
95 |
+
return "unclear"
|
96 |
+
elif "API key" in str(e) or "authentication" in str(e).lower():
|
97 |
+
return "unclear"
|
98 |
+
else:
|
99 |
+
print(f"予期しないエラー (グレード評価 - 試行 {attempt + 1}/{max_retries}): {e}")
|
100 |
+
if attempt < max_retries - 1:
|
101 |
+
time.sleep(1)
|
102 |
+
continue
|
103 |
+
else:
|
104 |
+
return "unclear"
|
105 |
+
return "unclear"
|
106 |
+
|
107 |
+
# データフレームを生成する関数
|
108 |
+
def generate_dataframe(age, sex, tumor_type, GeneMutation, Meseable, Biopsiable):
|
109 |
+
try:
|
110 |
+
# 入力検証
|
111 |
+
if not all([age, sex, tumor_type]):
|
112 |
+
return pd.DataFrame(), pd.DataFrame()
|
113 |
+
|
114 |
+
# 日本語の腫瘍タイプを英語に翻訳
|
115 |
+
try:
|
116 |
+
if translator is not None:
|
117 |
+
TumorName = translator.translate(tumor_type)
|
118 |
+
else:
|
119 |
+
print("翻訳エージェントが利用できません。元の値を使用します。")
|
120 |
+
TumorName = tumor_type
|
121 |
+
except Exception as e:
|
122 |
+
print(f"翻訳エラー: {e}")
|
123 |
+
TumorName = tumor_type # 翻訳に失敗した場合は元の値を使用
|
124 |
+
|
125 |
+
# 質問文を生成
|
126 |
+
try:
|
127 |
+
ex_question = generate_ex_question_English(age, sex, TumorName, GeneMutation, Meseable, Biopsiable)
|
128 |
+
except Exception as e:
|
129 |
+
print(f"質問生成エラー: {e}")
|
130 |
+
return pd.DataFrame(), pd.DataFrame()
|
131 |
+
|
132 |
+
# 臨床試験データの取得
|
133 |
+
try:
|
134 |
+
df = fetch_clinical_trials(TumorName)
|
135 |
+
if df.empty:
|
136 |
+
print("臨床試験データが見つかりませんでした")
|
137 |
+
return pd.DataFrame(), pd.DataFrame()
|
138 |
+
except Exception as e:
|
139 |
+
print(f"臨床試験データ取得エラー: {e}")
|
140 |
+
return pd.DataFrame(), pd.DataFrame()
|
141 |
+
|
142 |
+
df['AgentJudgment'] = None
|
143 |
+
df['AgentGrade'] = None
|
144 |
+
|
145 |
+
# 臨床試験の適格性の評価
|
146 |
+
NCTIDs = list(df['NCTID'])
|
147 |
+
progress = gr.Progress(track_tqdm=True)
|
148 |
+
|
149 |
+
for i, nct_id in enumerate(NCTIDs):
|
150 |
+
try:
|
151 |
+
target_criteria = df.loc[df['NCTID'] == nct_id, 'Eligibility Criteria'].values[0]
|
152 |
+
|
153 |
+
# エラーハンドリング付きで評価実行
|
154 |
+
agent_judgment = evaluate_with_retry(CriteriaCheckAgent, target_criteria, ex_question)
|
155 |
+
agent_grade = evaluate_grade_with_retry(grader_agent, agent_judgment)
|
156 |
+
|
157 |
+
# データフレームの更新
|
158 |
+
df.loc[df['NCTID'] == nct_id, 'AgentJudgment'] = agent_judgment
|
159 |
+
df.loc[df['NCTID'] == nct_id, 'AgentGrade'] = agent_grade
|
160 |
+
|
161 |
+
except Exception as e:
|
162 |
+
print(f"NCTID {nct_id} の評価中にエラー: {e}")
|
163 |
+
df.loc[df['NCTID'] == nct_id, 'AgentJudgment'] = f"エラー: {str(e)}"
|
164 |
+
df.loc[df['NCTID'] == nct_id, 'AgentGrade'] = "unclear"
|
165 |
+
|
166 |
+
progress((i + 1) / len(NCTIDs))
|
167 |
+
|
168 |
+
# 列を指定した順に並び替え
|
169 |
+
columns_order = ['NCTID', 'AgentGrade', 'Title', 'AgentJudgment', 'Japanes Locations',
|
170 |
+
'Primary Completion Date', 'Cancer', 'Summary', 'Eligibility Criteria']
|
171 |
+
|
172 |
+
# 存在する列のみを選択
|
173 |
+
available_columns = [col for col in columns_order if col in df.columns]
|
174 |
+
df = df[available_columns]
|
175 |
+
|
176 |
+
return df, df # フィルタ用と表示用にデータフレームを返す
|
177 |
+
|
178 |
+
except Exception as e:
|
179 |
+
print(f"データフレーム生成中に予期しないエラー: {e}")
|
180 |
+
traceback.print_exc()
|
181 |
+
return pd.DataFrame(), pd.DataFrame()
|
182 |
+
|
183 |
+
# CSVとして保存しダウンロードする関数
|
184 |
+
def download_filtered_csv(df):
|
185 |
+
try:
|
186 |
+
if df is None or len(df) == 0:
|
187 |
+
return None
|
188 |
+
file_path = "filtered_data.csv"
|
189 |
+
df.to_csv(file_path, index=False)
|
190 |
+
return file_path
|
191 |
+
except Exception as e:
|
192 |
+
print(f"CSV保存エラー: {e}")
|
193 |
+
return None
|
194 |
+
|
195 |
+
# 全体結果をCSVとして保存しダウンロードする関数
|
196 |
+
def download_full_csv(df):
|
197 |
+
try:
|
198 |
+
if df is None or len(df) == 0:
|
199 |
+
return None
|
200 |
+
file_path = "full_data.csv"
|
201 |
+
df.to_csv(file_path, index=False)
|
202 |
+
return file_path
|
203 |
+
except Exception as e:
|
204 |
+
print(f"CSV保存エラー: {e}")
|
205 |
+
return None
|
206 |
+
|
207 |
+
# Gradioインターフェースの作成
|
208 |
+
with gr.Blocks(title="臨床試験適格性評価", theme=gr.themes.Soft()) as demo:
|
209 |
+
gr.Markdown("## 臨床試験適格性評価インターフェース")
|
210 |
+
|
211 |
+
# 環境変数状態の表示
|
212 |
+
if env_ok:
|
213 |
+
gr.Markdown("✅ **ステータス**: 全ての環境変数が設定されています")
|
214 |
+
else:
|
215 |
+
gr.Markdown("⚠️ **注意**: 一部の環境変数が設定されていません。機能が制限される可能性があります。")
|
216 |
+
|
217 |
+
gr.Markdown("💡 **使用方法**: 患者情報を入力して「Generate Clinical Trials Data」をクリックしてください。")
|
218 |
+
|
219 |
+
# 各種入力フィールド
|
220 |
+
with gr.Row():
|
221 |
+
with gr.Column():
|
222 |
+
age_input = gr.Textbox(label="Age", placeholder="例: 65", value="")
|
223 |
+
sex_input = gr.Dropdown(choices=["男性", "女性"], label="Sex", value=None)
|
224 |
+
tumor_type_input = gr.Textbox(label="Tumor Type", placeholder="例: gastric cancer", value="")
|
225 |
+
|
226 |
+
with gr.Column():
|
227 |
+
gene_mutation_input = gr.Textbox(label="Gene Mutation", placeholder="例: HER2", value="")
|
228 |
+
measurable_input = gr.Dropdown(choices=["有り", "無し", "不明"], label="Measurable Tumor", value=None)
|
229 |
+
biopsiable_input = gr.Dropdown(choices=["有り", "無し", "不明"], label="Biopsiable Tumor", value=None)
|
230 |
+
|
231 |
+
# データフレーム表示エリア
|
232 |
+
dataframe_output = gr.DataFrame(
|
233 |
+
headers=["NCTID", "AgentGrade", "Title", "AgentJudgment", "Status"],
|
234 |
+
datatype=["str", "str", "str", "str", "str"],
|
235 |
+
value=None
|
236 |
+
)
|
237 |
+
|
238 |
+
# 内部状態用の非表示コンポーネント
|
239 |
+
original_df_state = gr.State(value=None)
|
240 |
+
filtered_df_state = gr.State(value=None)
|
241 |
+
|
242 |
+
# ボタン類
|
243 |
+
with gr.Row():
|
244 |
+
generate_button = gr.Button("Generate Clinical Trials Data", variant="primary")
|
245 |
+
|
246 |
+
with gr.Row():
|
247 |
+
yes_button = gr.Button("Show Eligible Trials", variant="secondary")
|
248 |
+
no_button = gr.Button("Show Ineligible Trials", variant="secondary")
|
249 |
+
unclear_button = gr.Button("Show Unclear Trials", variant="secondary")
|
250 |
+
|
251 |
+
with gr.Row():
|
252 |
+
download_filtered_button = gr.Button("Download Filtered Data")
|
253 |
+
download_full_button = gr.Button("Download Full Data")
|
254 |
+
|
255 |
+
# ダウンロードファイル
|
256 |
+
download_filtered_output = gr.File(label="Download Filtered Data", visible=False)
|
257 |
+
download_full_output = gr.File(label="Download Full Data", visible=False)
|
258 |
+
|
259 |
+
# イベントハンドリング
|
260 |
+
def update_dataframe_and_state(age, sex, tumor_type, gene_mutation, measurable, biopsiable):
|
261 |
+
"""データフレーム生成と状態更新"""
|
262 |
+
df, _ = generate_dataframe(age, sex, tumor_type, gene_mutation, measurable, biopsiable)
|
263 |
+
return df, df, df
|
264 |
+
|
265 |
+
def filter_and_update(original_df, grade):
|
266 |
+
"""フィルタリングと表示更新"""
|
267 |
+
if original_df is None or len(original_df) == 0:
|
268 |
+
return original_df, original_df
|
269 |
+
|
270 |
+
try:
|
271 |
+
df_filtered = original_df[original_df['AgentGrade'] == grade]
|
272 |
+
return df_filtered, df_filtered
|
273 |
+
except Exception as e:
|
274 |
+
print(f"フィルタリングエラー: {e}")
|
275 |
+
return original_df, original_df
|
276 |
+
|
277 |
+
# ボタン動作の設定
|
278 |
+
generate_button.click(
|
279 |
+
fn=update_dataframe_and_state,
|
280 |
+
inputs=[age_input, sex_input, tumor_type_input, gene_mutation_input, measurable_input, biopsiable_input],
|
281 |
+
outputs=[dataframe_output, original_df_state, filtered_df_state]
|
282 |
+
)
|
283 |
+
|
284 |
+
yes_button.click(
|
285 |
+
fn=lambda df: filter_and_update(df, "yes"),
|
286 |
+
inputs=[original_df_state],
|
287 |
+
outputs=[dataframe_output, filtered_df_state]
|
288 |
+
)
|
289 |
+
|
290 |
+
no_button.click(
|
291 |
+
fn=lambda df: filter_and_update(df, "no"),
|
292 |
+
inputs=[original_df_state],
|
293 |
+
outputs=[dataframe_output, filtered_df_state]
|
294 |
+
)
|
295 |
+
|
296 |
+
unclear_button.click(
|
297 |
+
fn=lambda df: filter_and_update(df, "unclear"),
|
298 |
+
inputs=[original_df_state],
|
299 |
+
outputs=[dataframe_output, filtered_df_state]
|
300 |
+
)
|
301 |
+
|
302 |
+
download_filtered_button.click(
|
303 |
+
fn=download_filtered_csv,
|
304 |
+
inputs=[filtered_df_state],
|
305 |
+
outputs=[download_filtered_output]
|
306 |
+
)
|
307 |
+
|
308 |
+
download_full_button.click(
|
309 |
+
fn=download_full_csv,
|
310 |
+
inputs=[original_df_state],
|
311 |
+
outputs=[download_full_output]
|
312 |
+
)
|
313 |
+
|
314 |
+
if __name__ == "__main__":
|
315 |
+
demo.launch(
|
316 |
+
server_name="0.0.0.0",
|
317 |
+
server_port=7860,
|
318 |
+
share=False,
|
319 |
+
debug=False,
|
320 |
+
show_error=True
|
321 |
+
)
|
app_fixed.py
ADDED
@@ -0,0 +1,321 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import pandas as pd
|
3 |
+
import time
|
4 |
+
import traceback
|
5 |
+
import os
|
6 |
+
from OpenAITools.FetchTools import fetch_clinical_trials
|
7 |
+
from langchain_openai import ChatOpenAI
|
8 |
+
from langchain_groq import ChatGroq
|
9 |
+
from OpenAITools.CrinicalTrialTools import SimpleClinicalTrialAgent, GraderAgent, LLMTranslator, generate_ex_question_English
|
10 |
+
|
11 |
+
# 環境変数チェック
|
12 |
+
def check_environment():
|
13 |
+
"""環境変数をチェックし、不足している場合は警告"""
|
14 |
+
missing_vars = []
|
15 |
+
|
16 |
+
if not os.getenv("GROQ_API_KEY"):
|
17 |
+
missing_vars.append("GROQ_API_KEY")
|
18 |
+
|
19 |
+
if not os.getenv("OPENAI_API_KEY"):
|
20 |
+
missing_vars.append("OPENAI_API_KEY")
|
21 |
+
|
22 |
+
if missing_vars:
|
23 |
+
print(f"⚠️ 環境変数が設定されていません: {', '.join(missing_vars)}")
|
24 |
+
print("一部の機能が制限される可能性があります。")
|
25 |
+
|
26 |
+
return len(missing_vars) == 0
|
27 |
+
|
28 |
+
# 環境変数チェック実行
|
29 |
+
env_ok = check_environment()
|
30 |
+
|
31 |
+
# モデルとエージェントの安全な初期化
|
32 |
+
def safe_init_agents():
|
33 |
+
"""エージェントを安全に初期化"""
|
34 |
+
try:
|
35 |
+
groq = ChatGroq(model_name="llama3-70b-8192", temperature=0)
|
36 |
+
translator = LLMTranslator(groq)
|
37 |
+
criteria_agent = SimpleClinicalTrialAgent(groq)
|
38 |
+
grader_agent = GraderAgent(groq)
|
39 |
+
return translator, criteria_agent, grader_agent
|
40 |
+
except Exception as e:
|
41 |
+
print(f"エージェント初期化エラー: {e}")
|
42 |
+
return None, None, None
|
43 |
+
|
44 |
+
# エージェント初期化
|
45 |
+
translator, CriteriaCheckAgent, grader_agent = safe_init_agents()
|
46 |
+
|
47 |
+
# エラーハンドリング付きでエージェント評価を実行する関数
|
48 |
+
def evaluate_with_retry(agent, criteria, question, max_retries=3):
|
49 |
+
"""エラーハンドリング付きでエージェント評価を実行"""
|
50 |
+
if agent is None:
|
51 |
+
return "評価エラー: エージェントが初期化されていません。API keyを確認してください。"
|
52 |
+
|
53 |
+
for attempt in range(max_retries):
|
54 |
+
try:
|
55 |
+
return agent.evaluate_eligibility(criteria, question)
|
56 |
+
except Exception as e:
|
57 |
+
if "missing variables" in str(e):
|
58 |
+
# プロンプトテンプレートの変数エラーの場合
|
59 |
+
print(f"プロンプトテンプレートエラー (試行 {attempt + 1}/{max_retries}): {e}")
|
60 |
+
return "評価エラー: プロンプトテンプレートの設定に問題があります"
|
61 |
+
elif "no healthy upstream" in str(e) or "InternalServerError" in str(e):
|
62 |
+
# Groqサーバーエラーの場合
|
63 |
+
print(f"Groqサーバーエラー (試行 {attempt + 1}/{max_retries}): {e}")
|
64 |
+
if attempt < max_retries - 1:
|
65 |
+
time.sleep(2) # 2秒待機してリトライ
|
66 |
+
continue
|
67 |
+
else:
|
68 |
+
return "評価エラー: サーバーに接続できませんでした"
|
69 |
+
elif "API key" in str(e) or "authentication" in str(e).lower():
|
70 |
+
return "評価エラー: API keyが無効または設定されていません"
|
71 |
+
else:
|
72 |
+
print(f"予期しないエラー (試行 {attempt + 1}/{max_retries}): {e}")
|
73 |
+
if attempt < max_retries - 1:
|
74 |
+
time.sleep(1)
|
75 |
+
continue
|
76 |
+
else:
|
77 |
+
return f"評価エラー: {str(e)}"
|
78 |
+
return "評価エラー: 最大リトライ回数に達しました"
|
79 |
+
|
80 |
+
def evaluate_grade_with_retry(agent, judgment, max_retries=3):
|
81 |
+
"""エラーハンドリング付きでグレード評価を実行"""
|
82 |
+
if agent is None:
|
83 |
+
return "unclear"
|
84 |
+
|
85 |
+
for attempt in range(max_retries):
|
86 |
+
try:
|
87 |
+
return agent.evaluate_eligibility(judgment)
|
88 |
+
except Exception as e:
|
89 |
+
if "no healthy upstream" in str(e) or "InternalServerError" in str(e):
|
90 |
+
print(f"Groqサーバーエラー (グレード評価 - 試行 {attempt + 1}/{max_retries}): {e}")
|
91 |
+
if attempt < max_retries - 1:
|
92 |
+
time.sleep(2)
|
93 |
+
continue
|
94 |
+
else:
|
95 |
+
return "unclear"
|
96 |
+
elif "API key" in str(e) or "authentication" in str(e).lower():
|
97 |
+
return "unclear"
|
98 |
+
else:
|
99 |
+
print(f"予期しないエラー (グレード評価 - 試行 {attempt + 1}/{max_retries}): {e}")
|
100 |
+
if attempt < max_retries - 1:
|
101 |
+
time.sleep(1)
|
102 |
+
continue
|
103 |
+
else:
|
104 |
+
return "unclear"
|
105 |
+
return "unclear"
|
106 |
+
|
107 |
+
# データフレームを生成する関数
|
108 |
+
def generate_dataframe(age, sex, tumor_type, GeneMutation, Meseable, Biopsiable):
|
109 |
+
try:
|
110 |
+
# 入力検証
|
111 |
+
if not all([age, sex, tumor_type]):
|
112 |
+
return pd.DataFrame(), pd.DataFrame()
|
113 |
+
|
114 |
+
# 日本語の腫瘍タイプを英語に翻訳
|
115 |
+
try:
|
116 |
+
if translator is not None:
|
117 |
+
TumorName = translator.translate(tumor_type)
|
118 |
+
else:
|
119 |
+
print("翻訳エージェントが利用できません。元の値を使用します。")
|
120 |
+
TumorName = tumor_type
|
121 |
+
except Exception as e:
|
122 |
+
print(f"翻訳エラー: {e}")
|
123 |
+
TumorName = tumor_type # 翻訳に失敗した場合は元の値を使用
|
124 |
+
|
125 |
+
# 質問文を生成
|
126 |
+
try:
|
127 |
+
ex_question = generate_ex_question_English(age, sex, TumorName, GeneMutation, Meseable, Biopsiable)
|
128 |
+
except Exception as e:
|
129 |
+
print(f"質問生成エラー: {e}")
|
130 |
+
return pd.DataFrame(), pd.DataFrame()
|
131 |
+
|
132 |
+
# 臨床試験データの取得
|
133 |
+
try:
|
134 |
+
df = fetch_clinical_trials(TumorName)
|
135 |
+
if df.empty:
|
136 |
+
print("臨床試験データが見つかりませんでした")
|
137 |
+
return pd.DataFrame(), pd.DataFrame()
|
138 |
+
except Exception as e:
|
139 |
+
print(f"臨床試験データ取得エラー: {e}")
|
140 |
+
return pd.DataFrame(), pd.DataFrame()
|
141 |
+
|
142 |
+
df['AgentJudgment'] = None
|
143 |
+
df['AgentGrade'] = None
|
144 |
+
|
145 |
+
# 臨床試験の適格性の評価
|
146 |
+
NCTIDs = list(df['NCTID'])
|
147 |
+
progress = gr.Progress(track_tqdm=True)
|
148 |
+
|
149 |
+
for i, nct_id in enumerate(NCTIDs):
|
150 |
+
try:
|
151 |
+
target_criteria = df.loc[df['NCTID'] == nct_id, 'Eligibility Criteria'].values[0]
|
152 |
+
|
153 |
+
# エラーハンドリング付きで評価実行
|
154 |
+
agent_judgment = evaluate_with_retry(CriteriaCheckAgent, target_criteria, ex_question)
|
155 |
+
agent_grade = evaluate_grade_with_retry(grader_agent, agent_judgment)
|
156 |
+
|
157 |
+
# データフレームの更新
|
158 |
+
df.loc[df['NCTID'] == nct_id, 'AgentJudgment'] = agent_judgment
|
159 |
+
df.loc[df['NCTID'] == nct_id, 'AgentGrade'] = agent_grade
|
160 |
+
|
161 |
+
except Exception as e:
|
162 |
+
print(f"NCTID {nct_id} の評価中にエラー: {e}")
|
163 |
+
df.loc[df['NCTID'] == nct_id, 'AgentJudgment'] = f"エラー: {str(e)}"
|
164 |
+
df.loc[df['NCTID'] == nct_id, 'AgentGrade'] = "unclear"
|
165 |
+
|
166 |
+
progress((i + 1) / len(NCTIDs))
|
167 |
+
|
168 |
+
# 列を指定した順に並び替え
|
169 |
+
columns_order = ['NCTID', 'AgentGrade', 'Title', 'AgentJudgment', 'Japanes Locations',
|
170 |
+
'Primary Completion Date', 'Cancer', 'Summary', 'Eligibility Criteria']
|
171 |
+
|
172 |
+
# 存在する列のみを選択
|
173 |
+
available_columns = [col for col in columns_order if col in df.columns]
|
174 |
+
df = df[available_columns]
|
175 |
+
|
176 |
+
return df, df # フィルタ用と表示用にデータフレームを返す
|
177 |
+
|
178 |
+
except Exception as e:
|
179 |
+
print(f"データフレーム生成中に予期しないエラー: {e}")
|
180 |
+
traceback.print_exc()
|
181 |
+
return pd.DataFrame(), pd.DataFrame()
|
182 |
+
|
183 |
+
# CSVとして保存しダウンロードする関数
|
184 |
+
def download_filtered_csv(df):
|
185 |
+
try:
|
186 |
+
if df is None or len(df) == 0:
|
187 |
+
return None
|
188 |
+
file_path = "filtered_data.csv"
|
189 |
+
df.to_csv(file_path, index=False)
|
190 |
+
return file_path
|
191 |
+
except Exception as e:
|
192 |
+
print(f"CSV保存エラー: {e}")
|
193 |
+
return None
|
194 |
+
|
195 |
+
# 全体結果をCSVとして保存しダウンロードする関数
|
196 |
+
def download_full_csv(df):
|
197 |
+
try:
|
198 |
+
if df is None or len(df) == 0:
|
199 |
+
return None
|
200 |
+
file_path = "full_data.csv"
|
201 |
+
df.to_csv(file_path, index=False)
|
202 |
+
return file_path
|
203 |
+
except Exception as e:
|
204 |
+
print(f"CSV保存エラー: {e}")
|
205 |
+
return None
|
206 |
+
|
207 |
+
# Gradioインターフェースの作成
|
208 |
+
with gr.Blocks(title="臨床試験適格性評価", theme=gr.themes.Soft()) as demo:
|
209 |
+
gr.Markdown("## 臨床試験適格性評価インターフェース")
|
210 |
+
|
211 |
+
# 環境変数状態の表示
|
212 |
+
if env_ok:
|
213 |
+
gr.Markdown("✅ **ステータス**: 全ての環境変数が設定されています")
|
214 |
+
else:
|
215 |
+
gr.Markdown("⚠️ **注意**: 一部の環境変数が設定されていません。機能が制限される可能性があります。")
|
216 |
+
|
217 |
+
gr.Markdown("💡 **使用方法**: 患者情報を入力して「Generate Clinical Trials Data」をクリックしてください。")
|
218 |
+
|
219 |
+
# 各種入力フィールド
|
220 |
+
with gr.Row():
|
221 |
+
with gr.Column():
|
222 |
+
age_input = gr.Textbox(label="Age", placeholder="例: 65", value="")
|
223 |
+
sex_input = gr.Dropdown(choices=["男性", "女性"], label="Sex", value=None)
|
224 |
+
tumor_type_input = gr.Textbox(label="Tumor Type", placeholder="例: gastric cancer", value="")
|
225 |
+
|
226 |
+
with gr.Column():
|
227 |
+
gene_mutation_input = gr.Textbox(label="Gene Mutation", placeholder="例: HER2", value="")
|
228 |
+
measurable_input = gr.Dropdown(choices=["有り", "無し", "不明"], label="Measurable Tumor", value=None)
|
229 |
+
biopsiable_input = gr.Dropdown(choices=["有り", "無し", "不明"], label="Biopsiable Tumor", value=None)
|
230 |
+
|
231 |
+
# データフレーム表示エリア(修正版)
|
232 |
+
dataframe_output = gr.DataFrame(
|
233 |
+
label="Clinical Trials Results",
|
234 |
+
interactive=False,
|
235 |
+
wrap=True
|
236 |
+
)
|
237 |
+
|
238 |
+
# 内部状態用の非表示コンポーネント
|
239 |
+
original_df_state = gr.State(value=None)
|
240 |
+
filtered_df_state = gr.State(value=None)
|
241 |
+
|
242 |
+
# ボタン類
|
243 |
+
with gr.Row():
|
244 |
+
generate_button = gr.Button("Generate Clinical Trials Data", variant="primary")
|
245 |
+
|
246 |
+
with gr.Row():
|
247 |
+
yes_button = gr.Button("Show Eligible Trials", variant="secondary")
|
248 |
+
no_button = gr.Button("Show Ineligible Trials", variant="secondary")
|
249 |
+
unclear_button = gr.Button("Show Unclear Trials", variant="secondary")
|
250 |
+
|
251 |
+
with gr.Row():
|
252 |
+
download_filtered_button = gr.Button("Download Filtered Data")
|
253 |
+
download_full_button = gr.Button("Download Full Data")
|
254 |
+
|
255 |
+
# ダウンロードファイル
|
256 |
+
download_filtered_output = gr.File(label="Download Filtered Data", visible=False)
|
257 |
+
download_full_output = gr.File(label="Download Full Data", visible=False)
|
258 |
+
|
259 |
+
# イベントハンドリング
|
260 |
+
def update_dataframe_and_state(age, sex, tumor_type, gene_mutation, measurable, biopsiable):
|
261 |
+
"""データフレーム生成と状態更新"""
|
262 |
+
df, _ = generate_dataframe(age, sex, tumor_type, gene_mutation, measurable, biopsiable)
|
263 |
+
return df, df, df
|
264 |
+
|
265 |
+
def filter_and_update(original_df, grade):
|
266 |
+
"""フィルタリングと表示更新"""
|
267 |
+
if original_df is None or len(original_df) == 0:
|
268 |
+
return original_df, original_df
|
269 |
+
|
270 |
+
try:
|
271 |
+
df_filtered = original_df[original_df['AgentGrade'] == grade]
|
272 |
+
return df_filtered, df_filtered
|
273 |
+
except Exception as e:
|
274 |
+
print(f"フィルタリングエラー: {e}")
|
275 |
+
return original_df, original_df
|
276 |
+
|
277 |
+
# ボタン動作の設定
|
278 |
+
generate_button.click(
|
279 |
+
fn=update_dataframe_and_state,
|
280 |
+
inputs=[age_input, sex_input, tumor_type_input, gene_mutation_input, measurable_input, biopsiable_input],
|
281 |
+
outputs=[dataframe_output, original_df_state, filtered_df_state]
|
282 |
+
)
|
283 |
+
|
284 |
+
yes_button.click(
|
285 |
+
fn=lambda df: filter_and_update(df, "yes"),
|
286 |
+
inputs=[original_df_state],
|
287 |
+
outputs=[dataframe_output, filtered_df_state]
|
288 |
+
)
|
289 |
+
|
290 |
+
no_button.click(
|
291 |
+
fn=lambda df: filter_and_update(df, "no"),
|
292 |
+
inputs=[original_df_state],
|
293 |
+
outputs=[dataframe_output, filtered_df_state]
|
294 |
+
)
|
295 |
+
|
296 |
+
unclear_button.click(
|
297 |
+
fn=lambda df: filter_and_update(df, "unclear"),
|
298 |
+
inputs=[original_df_state],
|
299 |
+
outputs=[dataframe_output, filtered_df_state]
|
300 |
+
)
|
301 |
+
|
302 |
+
download_filtered_button.click(
|
303 |
+
fn=download_filtered_csv,
|
304 |
+
inputs=[filtered_df_state],
|
305 |
+
outputs=[download_filtered_output]
|
306 |
+
)
|
307 |
+
|
308 |
+
download_full_button.click(
|
309 |
+
fn=download_full_csv,
|
310 |
+
inputs=[original_df_state],
|
311 |
+
outputs=[download_full_output]
|
312 |
+
)
|
313 |
+
|
314 |
+
if __name__ == "__main__":
|
315 |
+
demo.launch(
|
316 |
+
server_name="0.0.0.0",
|
317 |
+
server_port=7860,
|
318 |
+
share=False,
|
319 |
+
debug=False,
|
320 |
+
show_error=True
|
321 |
+
)
|
requirements.txt
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
-
# Core web framework (
|
2 |
-
gradio==4.
|
3 |
|
4 |
# Data processing
|
5 |
pandas>=1.5.0
|
@@ -24,27 +24,19 @@ SQLAlchemy>=2.0.0,<3.0.0
|
|
24 |
# Pydantic (LangChain互換性のため)
|
25 |
pydantic>=2.5.0,<3.0.0
|
26 |
|
27 |
-
# Text processing
|
28 |
tiktoken>=0.4.0
|
29 |
|
30 |
# Utilities
|
31 |
tenacity>=8.0.0
|
32 |
packaging>=23.0.0
|
33 |
|
34 |
-
#
|
35 |
-
# Uncomment the following lines if bio-processing is required
|
36 |
# biopython>=1.83
|
37 |
# bio>=1.7.1
|
38 |
# biothings-client>=0.3.1
|
39 |
# mygene>=3.2.2
|
40 |
|
41 |
-
# Optional: Advanced LangChain features
|
42 |
# langchainhub>=0.1.20
|
43 |
# langgraph>=0.2.30
|
44 |
-
|
45 |
-
# Optional: Additional LLama Index support (if needed)
|
46 |
-
# Uncomment if you're using LlamaIndex in addition to LangChain
|
47 |
-
# llama-index>=0.10.0
|
48 |
-
# llama-index-core>=0.10.0
|
49 |
-
# llama-index-llms-openai>=0.1.20
|
50 |
-
# llama-index-llms-groq>=0.1.4
|
|
|
1 |
+
# Core web framework (安定バージョンに固定)
|
2 |
+
gradio==4.36.1
|
3 |
|
4 |
# Data processing
|
5 |
pandas>=1.5.0
|
|
|
24 |
# Pydantic (LangChain互換性のため)
|
25 |
pydantic>=2.5.0,<3.0.0
|
26 |
|
27 |
+
# Text processing
|
28 |
tiktoken>=0.4.0
|
29 |
|
30 |
# Utilities
|
31 |
tenacity>=8.0.0
|
32 |
packaging>=23.0.0
|
33 |
|
34 |
+
# Bio-related libraries (必要に応じて)
|
|
|
35 |
# biopython>=1.83
|
36 |
# bio>=1.7.1
|
37 |
# biothings-client>=0.3.1
|
38 |
# mygene>=3.2.2
|
39 |
|
40 |
+
# Optional: Advanced LangChain features
|
41 |
# langchainhub>=0.1.20
|
42 |
# langgraph>=0.2.30
|
|
|
|
|
|
|
|
|
|
|
|
|
|
requirements_old3.txt
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Core web framework (固定バージョンで安定性確保)
|
2 |
+
gradio==4.44.0
|
3 |
+
|
4 |
+
# Data processing
|
5 |
+
pandas>=1.5.0
|
6 |
+
numpy>=1.21.0
|
7 |
+
requests>=2.25.0
|
8 |
+
|
9 |
+
# LangChain ecosystem (互換性のあるバージョンに調整)
|
10 |
+
langchain==0.2.16
|
11 |
+
langchain-community==0.2.16
|
12 |
+
langchain-core==0.2.38
|
13 |
+
langchain-openai==0.1.23
|
14 |
+
langchain-groq==0.1.9
|
15 |
+
langchain-text-splitters==0.2.4
|
16 |
+
|
17 |
+
# LLM providers
|
18 |
+
openai>=1.0.0,<2.0.0
|
19 |
+
groq>=0.4.0
|
20 |
+
|
21 |
+
# Database utilities
|
22 |
+
SQLAlchemy>=2.0.0,<3.0.0
|
23 |
+
|
24 |
+
# Pydantic (LangChain互換性のため)
|
25 |
+
pydantic>=2.5.0,<3.0.0
|
26 |
+
|
27 |
+
# Text processing (軽量化)
|
28 |
+
tiktoken>=0.4.0
|
29 |
+
|
30 |
+
# Utilities
|
31 |
+
tenacity>=8.0.0
|
32 |
+
packaging>=23.0.0
|
33 |
+
|
34 |
+
# Optional: Bio-related libraries (if needed for your specific use case)
|
35 |
+
# Uncomment the following lines if bio-processing is required
|
36 |
+
# biopython>=1.83
|
37 |
+
# bio>=1.7.1
|
38 |
+
# biothings-client>=0.3.1
|
39 |
+
# mygene>=3.2.2
|
40 |
+
|
41 |
+
# Optional: Advanced LangChain features (if needed)
|
42 |
+
# langchainhub>=0.1.20
|
43 |
+
# langgraph>=0.2.30
|
44 |
+
|
45 |
+
# Optional: Additional LLama Index support (if needed)
|
46 |
+
# Uncomment if you're using LlamaIndex in addition to LangChain
|
47 |
+
# llama-index>=0.10.0
|
48 |
+
# llama-index-core>=0.10.0
|
49 |
+
# llama-index-llms-openai>=0.1.20
|
50 |
+
# llama-index-llms-groq>=0.1.4
|
requirements_spaces.txt
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Hugging Face Spaces最適化版
|
2 |
+
gradio==4.36.1
|
3 |
+
pandas==1.5.3
|
4 |
+
numpy==1.24.3
|
5 |
+
requests==2.31.0
|
6 |
+
|
7 |
+
# LangChain (軽量版)
|
8 |
+
langchain==0.1.20
|
9 |
+
langchain-community==0.0.38
|
10 |
+
langchain-core==0.1.52
|
11 |
+
langchain-openai==0.1.7
|
12 |
+
langchain-groq==0.1.5
|
13 |
+
|
14 |
+
# LLM providers
|
15 |
+
openai==1.12.0
|
16 |
+
groq==0.4.2
|
17 |
+
|
18 |
+
# Database
|
19 |
+
SQLAlchemy==2.0.23
|
20 |
+
|
21 |
+
# Pydantic
|
22 |
+
pydantic==2.5.3
|
23 |
+
|
24 |
+
# Utilities
|
25 |
+
tiktoken==0.5.2
|
26 |
+
tenacity==8.2.3
|