leonsimon23 commited on
Commit
90e3caa
·
verified ·
1 Parent(s): 01ab923

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +43 -56
app.py CHANGED
@@ -32,18 +32,15 @@ def get_extraction_config():
32
  ]
33
  return prompt_description, examples
34
 
35
- # --- 2. Gradio 的核心处理函数 (已更新) ---
36
- # 新增了对 PDF 文件处理的支持
37
  def process_input_and_visualize(input_text, input_file):
38
  """
39
- 接收文本或文件输入,解析内容,调用 LangExtract 处理,并返回结果给 Gradio
40
  """
41
  source_text = ""
42
  # 优先处理上传的文件
43
  if input_file is not None:
44
  try:
45
- # 使用 fitz (PyMuPDF) 打开 PDF 文件
46
- # input_file.name 会提供 Gradio 保存的临时文件路径
47
  with fitz.open(input_file.name) as doc:
48
  for page in doc:
49
  source_text += page.get_text()
@@ -59,40 +56,41 @@ def process_input_and_visualize(input_text, input_file):
59
  # --- LangExtract 核心提取逻辑 ---
60
  prompt, examples = get_extraction_config()
61
 
62
- # --- 模拟结果 (用于演示) ---
63
- from langextract.data import AnnotatedDocument, Extraction, CharInterval
64
- # 这里的模拟数据的位置信息可能与实际PDF文本不符,仅作演示
65
- result = AnnotatedDocument(
66
- text=source_text,
67
- extractions=[
68
- # 注意:这些硬编码的位置仅适用于特定的示例文本
69
- Extraction(extraction_class='药物', extraction_text='Lisinopril', attributes={'medication_group': 'Lisinopril'}, char_interval=CharInterval(start_pos=28, end_pos=38)),
70
- Extraction(extraction_class='药物', extraction_text='Metformin', attributes={'medication_group': 'Metformin'}, char_interval=CharInterval(start_pos=43, end_pos=52)),
71
- ]
72
- )
73
- # --- 模拟结束 ---
74
 
75
- # --- 真实的 LangExtract 调用 (部署时请取消注释) ---
76
- # api_key = os.environ.get("LANGEXTRACT_API_KEY")
77
- # if not api_key:
78
- # raise gr.Error("错误:未设置 LANGEXTRACT_API_KEY。")
79
- # result = lx.extract(
80
- # text_or_documents=source_text,
81
- # prompt_description=prompt,
82
- # examples=examples,
83
- # model_id="gemini-2.5-pro",
84
- # api_key=api_key
85
- # )
 
 
 
 
 
 
86
 
87
  # 1. 准备命名实体识别 (NER) 的高亮文本输出
88
  highlighted_text = []
89
  last_pos = 0
 
90
  sorted_extractions = sorted(result.extractions, key=lambda e: e.char_interval.start_pos)
91
  for entity in sorted_extractions:
92
  start, end = entity.char_interval.start_pos, entity.char_interval.end_pos
93
- highlighted_text.append((source_text[last_pos:start], None))
94
- highlighted_text.append((entity.extraction_text, entity.extraction_class))
95
- last_pos = end
 
 
96
  highlighted_text.append((source_text[last_pos:], None))
97
 
98
  # 2. 准备关系提取 (RE) 的结构化 Markdown 输出
@@ -102,12 +100,15 @@ def process_input_and_visualize(input_text, input_file):
102
  medication_groups.setdefault(group_name, []).append(extraction)
103
 
104
  structured_output = "### 结构化提取结果\n\n"
105
- for med_name, extractions in medication_groups.items():
106
- structured_output += f"#### 药物组: {med_name}\n"
107
- for extraction in sorted(extractions, key=lambda e: e.char_interval.start_pos):
108
- pos_info = f" (位置: {extraction.char_interval.start_pos}-{extraction.char_interval.end_pos})"
109
- structured_output += f"- **{extraction.extraction_class}**: {extraction.extraction_text}{pos_info}\n"
110
- structured_output += "\n"
 
 
 
111
 
112
  # 3. 生成并保存交互式可视化文件
113
  session_id = str(uuid.uuid4())
@@ -127,26 +128,18 @@ def process_input_and_visualize(input_text, input_file):
127
  return highlighted_text, structured_output, html_path, html_path, jsonl_path
128
 
129
 
130
- # --- 3. 创建 Gradio 应用界面 (已更新) ---
131
  with gr.Blocks(theme=gr.themes.Soft(), title="药物信息提取器") as demo:
132
- gr.Markdown("# ⚕️ LangExtract 药物信息提取器")
133
  gr.Markdown("一个基于大型语言模型的智能工具,可从**临床文本**或 **PDF 文件**中自动提取药物、剂量等信息,并进行结构化关联。")
134
 
135
  with gr.Row():
136
- # 左侧为输入区,使用 Tab 切换
137
  with gr.Column(scale=1):
138
  with gr.Tabs():
139
  with gr.TabItem("📄 临床文本输入"):
140
- input_textbox = gr.Textbox(
141
- lines=15,
142
- label="粘贴临床笔记",
143
- placeholder=textwrap.dedent("""请在此处粘贴文本...""")
144
- )
145
  with gr.TabItem("📁 上传PDF文件"):
146
- input_file_uploader = gr.File(
147
- label="选择一个 PDF 文件进行分析",
148
- file_types=['.pdf']
149
- )
150
 
151
  submit_btn = gr.Button("🧠 提取信息", variant="primary")
152
  gr.Examples(
@@ -154,35 +147,29 @@ with gr.Blocks(theme=gr.themes.Soft(), title="药物信息提取器") as demo:
154
  "The patient was prescribed Lisinopril and Metformin last month.\nHe takes the Lisinopril 10mg daily for hypertension, but often misses his Metformin 500mg dose which should be taken twice daily for diabetes.",
155
  "Patient took 400 mg PO Ibuprofen q4h for two days for a headache.",
156
  ],
157
- inputs=input_textbox, # 示例仅与文本框关联
158
  label="示例文本 (点击自动填充到上方文本框)"
159
  )
160
 
161
- # 右侧为输出区
162
  with gr.Column(scale=2):
163
  with gr.Tabs():
164
  with gr.TabItem("📊 总览 (NER & RE)"):
165
  gr.Markdown("### 命名实体识别 (NER) - 文本高亮")
166
  output_highlight = gr.HighlightedText(label="实体高亮显示", color_map={"药物": "#FF6347", "剂量": "#FFA500", "频率": "#32CD32", "病症": "#4169E1"})
167
  output_structured = gr.Markdown(label="结构化关系")
168
-
169
  with gr.TabItem("🌐 交互式可视化"):
170
  gr.Markdown("### 交互式可视化图表")
171
  output_html_viewer = gr.HTML(label="交互式图表 (可缩放和筛选)")
172
-
173
  with gr.TabItem("📁 文件下载"):
174
  gr.Markdown("### 下载提取结果")
175
  download_html = gr.File(label="下载交互式 HTML 文件")
176
  download_jsonl = gr.File(label="下载 JSONL 数据文件")
177
 
178
- # 设置按钮的点击事件
179
- # 输入现在是文本框和文件上传器
180
  submit_btn.click(
181
  fn=process_input_and_visualize,
182
  inputs=[input_textbox, input_file_uploader],
183
  outputs=[output_highlight, output_structured, output_html_viewer, download_html, download_jsonl]
184
  ).then(
185
- # 任务完成后,清空输入框和文件上传器,准备下一次输入
186
  lambda: (None, None),
187
  inputs=None,
188
  outputs=[input_textbox, input_file_uploader]
 
32
  ]
33
  return prompt_description, examples
34
 
35
+ # --- 2. Gradio 的核心处理函数 (生产级版本) ---
 
36
  def process_input_and_visualize(input_text, input_file):
37
  """
38
+ 接收文本或文件输入,解析内容,调用 LangExtract 的高级功能进行处理,并返回结果。
39
  """
40
  source_text = ""
41
  # 优先处理上传的文件
42
  if input_file is not None:
43
  try:
 
 
44
  with fitz.open(input_file.name) as doc:
45
  for page in doc:
46
  source_text += page.get_text()
 
56
  # --- LangExtract 核心提取逻辑 ---
57
  prompt, examples = get_extraction_config()
58
 
59
+ # 从环境变量中获取 API Key ( Hugging Face 中必须设置为 Secret)
60
+ api_key = os.environ.get("LANGEXTRACT_API_KEY")
61
+ if not api_key:
62
+ raise gr.Error("错误:服务器未配置 LANGEXTRACT_API_KEY。请联系管理员在 Hugging Face Space 的 Secrets 中添加它。")
 
 
 
 
 
 
 
 
63
 
64
+ # --- 真实的、带高级参数的 LangExtract 调用 ---
65
+ # 这里我们移除了所有模拟代码,直接调用 API
66
+ try:
67
+ result = lx.extract(
68
+ text_or_documents=source_text,
69
+ prompt_description=prompt,
70
+ examples=examples,
71
+ model_id="gemini-2.5-pro", # 使用强大的模型以获得最佳效果
72
+ api_key=api_key,
73
+ # --- 智能处理长文本的关键参数 ---
74
+ max_workers=10, # 启用并行处理以加速,设置10个工作线程
75
+ extraction_passes=2, # 执行两次提取以提高召回率 (可以设为3以获得更高精度,但会更慢)
76
+ max_char_buffer=1500 # 设置文本块的最大字符数,用于智能分块
77
+ )
78
+ except Exception as e:
79
+ # 捕获API调用或其他处理中可能发生的错误
80
+ raise gr.Error(f"信息提取过程中发生错误: {e}")
81
 
82
  # 1. 准备命名实体识别 (NER) 的高亮文本输出
83
  highlighted_text = []
84
  last_pos = 0
85
+ # 确保实体按位置排序,以便正确高亮显示
86
  sorted_extractions = sorted(result.extractions, key=lambda e: e.char_interval.start_pos)
87
  for entity in sorted_extractions:
88
  start, end = entity.char_interval.start_pos, entity.char_interval.end_pos
89
+ # 检查位置是否有效,防止解析错误
90
+ if start >= last_pos and end <= len(source_text):
91
+ highlighted_text.append((source_text[last_pos:start], None))
92
+ highlighted_text.append((entity.extraction_text, entity.extraction_class))
93
+ last_pos = end
94
  highlighted_text.append((source_text[last_pos:], None))
95
 
96
  # 2. 准备关系提取 (RE) 的结构化 Markdown 输出
 
100
  medication_groups.setdefault(group_name, []).append(extraction)
101
 
102
  structured_output = "### 结构化提取结果\n\n"
103
+ if not medication_groups:
104
+ structured_output += "未提取到任何药物信息。"
105
+ else:
106
+ for med_name, extractions in medication_groups.items():
107
+ structured_output += f"#### 药物组: {med_name}\n"
108
+ for extraction in sorted(extractions, key=lambda e: e.char_interval.start_pos):
109
+ pos_info = f" (位置: {extraction.char_interval.start_pos}-{extraction.char_interval.end_pos})"
110
+ structured_output += f"- **{extraction.extraction_class}**: {extraction.extraction_text}{pos_info}\n"
111
+ structured_output += "\n"
112
 
113
  # 3. 生成并保存交互式可视化文件
114
  session_id = str(uuid.uuid4())
 
128
  return highlighted_text, structured_output, html_path, html_path, jsonl_path
129
 
130
 
131
+ # --- 3. 创建 Gradio 应用界面 (保持不变) ---
132
  with gr.Blocks(theme=gr.themes.Soft(), title="药物信息提取器") as demo:
133
+ gr.Markdown("# ⚕️ 基于大模型的药物信息提取器")
134
  gr.Markdown("一个基于大型语言模型的智能工具,可从**临床文本**或 **PDF 文件**中自动提取药物、剂量等信息,并进行结构化关联。")
135
 
136
  with gr.Row():
 
137
  with gr.Column(scale=1):
138
  with gr.Tabs():
139
  with gr.TabItem("📄 临床文本输入"):
140
+ input_textbox = gr.Textbox(lines=15, label="粘贴临床笔记", placeholder="请在此处粘贴文本...")
 
 
 
 
141
  with gr.TabItem("📁 上传PDF文件"):
142
+ input_file_uploader = gr.File(label="选择一个 PDF 文件进行分析", file_types=['.pdf'])
 
 
 
143
 
144
  submit_btn = gr.Button("🧠 提取信息", variant="primary")
145
  gr.Examples(
 
147
  "The patient was prescribed Lisinopril and Metformin last month.\nHe takes the Lisinopril 10mg daily for hypertension, but often misses his Metformin 500mg dose which should be taken twice daily for diabetes.",
148
  "Patient took 400 mg PO Ibuprofen q4h for two days for a headache.",
149
  ],
150
+ inputs=input_textbox,
151
  label="示例文本 (点击自动填充到上方文本框)"
152
  )
153
 
 
154
  with gr.Column(scale=2):
155
  with gr.Tabs():
156
  with gr.TabItem("📊 总览 (NER & RE)"):
157
  gr.Markdown("### 命名实体识别 (NER) - 文本高亮")
158
  output_highlight = gr.HighlightedText(label="实体高亮显示", color_map={"药物": "#FF6347", "剂量": "#FFA500", "频率": "#32CD32", "病症": "#4169E1"})
159
  output_structured = gr.Markdown(label="结构化关系")
 
160
  with gr.TabItem("🌐 交互式可视化"):
161
  gr.Markdown("### 交互式可视化图表")
162
  output_html_viewer = gr.HTML(label="交互式图表 (可缩放和筛选)")
 
163
  with gr.TabItem("📁 文件下载"):
164
  gr.Markdown("### 下载提取结果")
165
  download_html = gr.File(label="下载交互式 HTML 文件")
166
  download_jsonl = gr.File(label="下载 JSONL 数据文件")
167
 
 
 
168
  submit_btn.click(
169
  fn=process_input_and_visualize,
170
  inputs=[input_textbox, input_file_uploader],
171
  outputs=[output_highlight, output_structured, output_html_viewer, download_html, download_jsonl]
172
  ).then(
 
173
  lambda: (None, None),
174
  inputs=None,
175
  outputs=[input_textbox, input_file_uploader]