LLDDWW Claude commited on
Commit
ad98685
ยท
1 Parent(s): 88cbfb6

feat: add professional drug information system

Browse files

Major enhancements:
- Add detailed drug information fields (efficacy, usage_precautions, drug_interactions)
- Update VL model prompts to generate pharmacist-level explanations
- Create dedicated "์•ฝ๋ฌผ ์ƒ์„ธ ์ •๋ณด" tab with structured information
- Improve system prompt to emphasize pharmaceutical expertise
- Reorganize UI with tabs: ์•ฝ๋ฌผ ์ƒ์„ธ ์ •๋ณด, ์‰ฌ์šด ์„ค๋ช…, ํ•œ ์ปท ๋งŒํ™”, ์ผ์ • ์นด๋“œ

Drug information now includes:
- ๐Ÿ” ์ด ์•ฝ์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ? (ํšจ๋Šฅํšจ๊ณผ)
- ๐Ÿ“‹ ์ด ์•ฝ์€ ์–ด๋–ป๊ฒŒ ๋ณต์šฉํ•ฉ๋‹ˆ๊นŒ? (๋ณต์šฉ๋ฒ• ๋ฐ ์ฃผ์˜์‚ฌํ•ญ)
- โš ๏ธ ๋ถ€์ž‘์šฉ
- ๐Ÿ”„ ์•ฝ๋ฌผ ์ƒํ˜ธ์ž‘์šฉ
- โšก ํŠน๋ณ„ ์ฃผ์˜์‚ฌํ•ญ

Example: For Almagate 500mg, it now explains:
- Mechanism: neutralizes gastric acid, reduces pepsin activity
- Instructions: avoid with iron supplements (2hr gap), limit caffeine/spices
- Side effects and contraindications

๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

Files changed (1) hide show
  1. app.py +56 -15
app.py CHANGED
@@ -1,6 +1,7 @@
1
  import json
2
  import os
3
  import re
 
4
  from typing import Any, Dict, List, Optional
5
 
6
  import gradio as gr
@@ -116,6 +117,9 @@ def _sanitize_medication(item: Dict[str, Any]) -> Dict[str, Any]:
116
  "dosage_example": _to_str(item.get("dosage_example")),
117
  "side_effects": _to_str(item.get("side_effects")),
118
  "warnings": _to_str(item.get("warnings")),
 
 
 
119
  }
120
 
121
 
@@ -170,28 +174,30 @@ def analyze_image_with_qwen(image: Image.Image) -> Dict[str, Any]:
170
  " \"raw_text\": \"OCR๋กœ ์ฝ์€ ์ „์ฒด ๋ฌธ์žฅ\",\n"
171
  " \"medications\": [\n"
172
  " {\n"
173
- " \"name\": \"์•ฝ ์ด๋ฆ„\",\n"
174
- " \"dose_per_intake\": \"1ํšŒ ์šฉ๋Ÿ‰ (์˜ˆ: 1์ •, 5mL)\",\n"
175
  " \"times_per_day\": \"ํ•˜๋ฃจ ๋ณต์šฉ ํšŸ์ˆ˜\",\n"
176
  " \"time_slots\": [\"๋ณต์šฉ ์‹œ๊ฐ„๋Œ€\"],\n"
177
- " \"description\": \"์•ฝ ์„ค๋ช…\",\n"
178
- " \"usage_example\": \"๋ณต์šฉ ์˜ˆ์‹œ\",\n"
179
- " \"dosage_example\": \"๋ณต์šฉ ๋ฐฉ๋ฒ• ์˜ˆ์‹œ\",\n"
180
- " \"side_effects\": \"์ฃผ์š” ๋ถ€์ž‘์šฉ\",\n"
181
- " \"warnings\": \"์ฃผ์˜ ๋ฌธ๊ตฌ\"\n"
 
182
  " }\n"
183
  " ],\n"
184
  " \"warnings\": [\"์ „์ฒด ๊ฒฝ๊ณ \"]\n"
185
  "}"
186
  )
187
  user_prompt = (
188
- "์œ„ JSON ์Šคํ‚ค๋งˆ๋ฅผ ๋ฐ˜๋“œ์‹œ ๋”ฐ๋ฅด์„ธ์š”. ๋ชจ๋“  ๊ฐ’์€ ํ•œ๊ตญ์–ด๋กœ ์ž‘์„ฑํ•˜๊ณ , ๋นˆ ์ •๋ณด๋Š” ๋นˆ ๋ฌธ์ž์—ด๋กœ ๋‘์„ธ์š”."
 
189
  )
190
 
191
  messages = [
192
  {
193
  "role": "system",
194
- "content": "๋‹น์‹ ์€ ์•ฝ์‚ฌ ์„ ์ƒ๋‹˜์ž…๋‹ˆ๋‹ค. ์ •ํ™•ํ•˜๊ณ  ์นœ์ ˆํ•˜๊ฒŒ ์ •๋ณด๋ฅผ ์ •๋ฆฌํ•˜์„ธ์š”.",
195
  },
196
  {
197
  "role": "user",
@@ -460,6 +466,7 @@ def run_pipeline(image: Optional[Image.Image], progress=gr.Progress()):
460
  "๐Ÿ“ท ์•ฝ ๋ด‰ํˆฌ ์‚ฌ์ง„์„ ์˜ฌ๋ฆฌ๋ฉด ์ธ์‹์ด ์‹œ์ž‘๋ผ์š”.",
461
  "",
462
  None,
 
463
  )
464
 
465
  progress(0, desc="์•ฝ๋ด‰ํˆฌ ์ด๋ฏธ์ง€ ๋ถ„์„ ์ค‘...")
@@ -473,6 +480,33 @@ def run_pipeline(image: Optional[Image.Image], progress=gr.Progress()):
473
  progress(0.66, desc="์ผ์ • ์นด๋“œ ๋ Œ๋”๋ง ์ค‘...")
474
  card_img = render_card(medications)
475
  csv_row = medications_to_csv(medications)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
  markdown = (
477
  "## ์–ด๋ฅด์‹ ์„ ์œ„ํ•œ ์„ค๋ช…\n"
478
  + (narratives.get("elderly_narrative") or "- ์„ค๋ช…์„ ์ค€๋น„ํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.")
@@ -488,7 +522,7 @@ def run_pipeline(image: Optional[Image.Image], progress=gr.Progress()):
488
  cartoon_image = generate_cartoon_image(narratives.get("image_prompt"))
489
 
490
  progress(1.0, desc="์™„๋ฃŒ!")
491
- return json_text, card_img, csv_row, markdown, warnings_md, raw_text, cartoon_image
492
 
493
 
494
  CUSTOM_CSS = """
@@ -531,18 +565,25 @@ with gr.Blocks(theme=gr.themes.Soft(), css=CUSTOM_CSS) as demo:
531
  btn = gr.Button("์ธ์‹ & ์„ค๋ช… ์ƒ์„ฑ", elem_classes=["primary-btn"])
532
  with gr.Column(scale=6, elem_classes=["glass-panel"]):
533
  gr.Markdown("### 2. ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•˜์„ธ์š”")
534
- explain_md = gr.Markdown("์—ฌ๊ธฐ์— ์•ฝ ์„ค๋ช…์ด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.", elem_classes=["output-card"])
 
 
 
 
 
 
 
 
 
535
  raw_box = gr.Textbox(label="๋ชจ๋ธ์ด ์ฝ์€ ์›๋ฌธ ํ…์ŠคํŠธ", lines=5, interactive=False)
536
- cartoon_img = gr.Image(type="pil", label="ํ•œ ์ปท ๋งŒํ™”")
537
- card_out = gr.Image(type="pil", label="์ผ์ • ์นด๋“œ(๋ฏธ๋ฆฌ๋ณด๊ธฐ)")
538
- csv_box = gr.Textbox(label="CSV(์•ฝ๋ช…,1ํšŒ์šฉ๋Ÿ‰,1์ผํšŸ์ˆ˜,์‹œ๊ฐ„๋Œ€)", lines=2, elem_classes=["csv-box"])
539
  with gr.Accordion("์„ธ๋ถ€ JSON ๊ฒฐ๊ณผ", open=False, elem_classes=["accordion"]):
540
  json_out = gr.Code(label="๋ชจ๋ธ ๋ถ„์„(JSON)")
541
 
542
  btn.click(
543
  run_pipeline,
544
  inputs=img_in,
545
- outputs=[json_out, card_out, csv_box, explain_md, warn_md, raw_box, cartoon_img],
546
  )
547
 
548
  gr.Markdown(
 
1
  import json
2
  import os
3
  import re
4
+ import urllib.parse
5
  from typing import Any, Dict, List, Optional
6
 
7
  import gradio as gr
 
117
  "dosage_example": _to_str(item.get("dosage_example")),
118
  "side_effects": _to_str(item.get("side_effects")),
119
  "warnings": _to_str(item.get("warnings")),
120
+ "efficacy": _to_str(item.get("efficacy")), # ํšจ๋Šฅํšจ๊ณผ
121
+ "usage_precautions": _to_str(item.get("usage_precautions")), # ๋ณต์šฉ ์ฃผ์˜์‚ฌํ•ญ
122
+ "drug_interactions": _to_str(item.get("drug_interactions")), # ์•ฝ๋ฌผ ์ƒํ˜ธ์ž‘์šฉ
123
  }
124
 
125
 
 
174
  " \"raw_text\": \"OCR๋กœ ์ฝ์€ ์ „์ฒด ๋ฌธ์žฅ\",\n"
175
  " \"medications\": [\n"
176
  " {\n"
177
+ " \"name\": \"์•ฝ ์ด๋ฆ„ (์„ฑ๋ถ„๋ช…)\",\n"
178
+ " \"dose_per_intake\": \"1ํšŒ ์šฉ๋Ÿ‰ (์˜ˆ: 1์ •, 500mg, 5mL)\",\n"
179
  " \"times_per_day\": \"ํ•˜๋ฃจ ๋ณต์šฉ ํšŸ์ˆ˜\",\n"
180
  " \"time_slots\": [\"๋ณต์šฉ ์‹œ๊ฐ„๋Œ€\"],\n"
181
+ " \"description\": \"์•ฝ ๊ฐ„๋‹จ ์„ค๋ช…\",\n"
182
+ " \"efficacy\": \"์ด ์•ฝ์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ? (ํšจ๋Šฅํšจ๊ณผ๋ฅผ ์ƒ์„ธํžˆ - ์˜ˆ: ์œ„์‚ฐ์„ ์ค‘ํ™”ํ•˜๊ณ  ํŽฉ์‹ ์˜ ์ž‘์šฉ์„ ๊ฐ์†Œ์‹œ์ผœ ์œ„๋‚ด ํ†ต์ฆ ๋ฐ ๊ถค์–‘ํ˜•์„ฑ์„ ๋ฐฉ์ง€ํ•˜๋Š” ์•ฝ)\",\n"
183
+ " \"usage_precautions\": \"์ด ์•ฝ์€ ์–ด๋–ป๊ฒŒ ๋ณต์šฉํ•ฉ๋‹ˆ๊นŒ? (๋ณต์šฉ ์ฃผ์˜์‚ฌํ•ญ์„ ์ƒ์„ธํžˆ - ์˜ˆ: ์ฒ ๋ถ„์ œ์™€ 2์‹œ๊ฐ„ ๊ฐ„๊ฒฉ ์œ ์ง€, ๊ฐ•ํ•œ ํ–ฅ์‹ ๋ฃŒ/์นดํŽ˜์ธ ์„ญ์ทจ ์ œํ•œ, 2์ฃผ ๋ณต์šฉ ํ›„ ์ฆ์ƒ ๋ฏธ๊ฐœ์„ ์‹œ ์ „๋ฌธ๊ฐ€ ์ƒ๋‹ด)\",\n"
184
+ " \"side_effects\": \"์ฃผ์š” ๋ถ€์ž‘์šฉ (๊ตฌ์ฒด์ ์œผ๋กœ)\",\n"
185
+ " \"drug_interactions\": \"๋‹ค๋ฅธ ์•ฝ๊ณผ์˜ ์ƒํ˜ธ์ž‘์šฉ (๋ณ‘์šฉ ์ฃผ์˜ ์•ฝ๋ฌผ)\",\n"
186
+ " \"warnings\": \"ํŠน๋ณ„ ์ฃผ์˜์‚ฌํ•ญ\"\n"
187
  " }\n"
188
  " ],\n"
189
  " \"warnings\": [\"์ „์ฒด ๊ฒฝ๊ณ \"]\n"
190
  "}"
191
  )
192
  user_prompt = (
193
+ "์œ„ JSON ์Šคํ‚ค๋งˆ๋ฅผ ๋ฐ˜๋“œ์‹œ ๋”ฐ๋ฅด์„ธ์š”. ๋ชจ๋“  ๊ฐ’์€ ํ•œ๊ตญ์–ด๋กœ ์ž‘์„ฑํ•˜๊ณ , ๋นˆ ์ •๋ณด๋Š” ๋นˆ ๋ฌธ์ž์—ด๋กœ ๋‘์„ธ์š”.\n"
194
+ "์•ฝ ์ด๋ฆ„์—์„œ ์„ฑ๋ถ„๋ช…์„ ํŒŒ์•…ํ•˜์—ฌ, ํ•ด๋‹น ์•ฝ์˜ ํšจ๋Šฅํšจ๊ณผ, ๋ณต์šฉ๋ฐฉ๋ฒ•, ์ฃผ์˜์‚ฌํ•ญ์„ ์•ฝ์‚ฌ ์ˆ˜์ค€์œผ๋กœ ์ƒ์„ธํžˆ ์ž‘์„ฑํ•˜์„ธ์š”."
195
  )
196
 
197
  messages = [
198
  {
199
  "role": "system",
200
+ "content": "๋‹น์‹ ์€ ์•ฝ์‚ฌ ์ž๊ฒฉ์ฆ์„ ๋ณด์œ ํ•œ ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค. ์•ฝ๋ฌผ์˜ ํšจ๋Šฅ, ๋ณต์šฉ๋ฒ•, ์ฃผ์˜์‚ฌํ•ญ, ์ƒํ˜ธ์ž‘์šฉ์„ ์ •ํ™•ํ•˜๊ณ  ์ƒ์„ธํ•˜๊ฒŒ ์„ค๋ช…ํ•˜์„ธ์š”.",
201
  },
202
  {
203
  "role": "user",
 
466
  "๐Ÿ“ท ์•ฝ ๋ด‰ํˆฌ ์‚ฌ์ง„์„ ์˜ฌ๋ฆฌ๋ฉด ์ธ์‹์ด ์‹œ์ž‘๋ผ์š”.",
467
  "",
468
  None,
469
+ "์•ฝ๋ฌผ ์ •๋ณด๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.",
470
  )
471
 
472
  progress(0, desc="์•ฝ๋ด‰ํˆฌ ์ด๋ฏธ์ง€ ๋ถ„์„ ์ค‘...")
 
480
  progress(0.66, desc="์ผ์ • ์นด๋“œ ๋ Œ๋”๋ง ์ค‘...")
481
  card_img = render_card(medications)
482
  csv_row = medications_to_csv(medications)
483
+
484
+ # ์•ฝ๋ฌผ ์ƒ์„ธ ์ •๋ณด ๋งˆํฌ๋‹ค์šด ์ƒ์„ฑ
485
+ detailed_info = ""
486
+ if medications:
487
+ detailed_info = "# ๐Ÿ’Š ์•ฝ๋ฌผ ์ƒ์„ธ ์ •๋ณด\n\n"
488
+ for idx, med in enumerate(medications):
489
+ detailed_info += f"## {idx + 1}. {med.get('name', '์•ฝ ์ด๋ฆ„ ๋ฏธํ™•์ธ')}\n\n"
490
+
491
+ if med.get("efficacy"):
492
+ detailed_info += f"### ๐Ÿ” ์ด ์•ฝ์€ ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?\n{med.get('efficacy')}\n\n"
493
+
494
+ if med.get("usage_precautions"):
495
+ detailed_info += f"### ๐Ÿ“‹ ์ด ์•ฝ์€ ์–ด๋–ป๊ฒŒ ๋ณต์šฉํ•ฉ๋‹ˆ๊นŒ?\n{med.get('usage_precautions')}\n\n"
496
+
497
+ if med.get("side_effects"):
498
+ detailed_info += f"### โš ๏ธ ๋ถ€์ž‘์šฉ\n{med.get('side_effects')}\n\n"
499
+
500
+ if med.get("drug_interactions"):
501
+ detailed_info += f"### ๐Ÿ”„ ์•ฝ๋ฌผ ์ƒํ˜ธ์ž‘์šฉ\n{med.get('drug_interactions')}\n\n"
502
+
503
+ if med.get("warnings"):
504
+ detailed_info += f"### โšก ํŠน๋ณ„ ์ฃผ์˜์‚ฌํ•ญ\n{med.get('warnings')}\n\n"
505
+
506
+ detailed_info += "---\n\n"
507
+ else:
508
+ detailed_info = "์•ฝ๋ฌผ ์ •๋ณด๏ฟฝ๏ฟฝ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."
509
+
510
  markdown = (
511
  "## ์–ด๋ฅด์‹ ์„ ์œ„ํ•œ ์„ค๋ช…\n"
512
  + (narratives.get("elderly_narrative") or "- ์„ค๋ช…์„ ์ค€๋น„ํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค.")
 
522
  cartoon_image = generate_cartoon_image(narratives.get("image_prompt"))
523
 
524
  progress(1.0, desc="์™„๋ฃŒ!")
525
+ return json_text, card_img, csv_row, markdown, warnings_md, raw_text, cartoon_image, detailed_info
526
 
527
 
528
  CUSTOM_CSS = """
 
565
  btn = gr.Button("์ธ์‹ & ์„ค๋ช… ์ƒ์„ฑ", elem_classes=["primary-btn"])
566
  with gr.Column(scale=6, elem_classes=["glass-panel"]):
567
  gr.Markdown("### 2. ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•˜์„ธ์š”")
568
+ with gr.Tabs():
569
+ with gr.Tab("๐Ÿ“š ์•ฝ๋ฌผ ์ƒ์„ธ ์ •๋ณด"):
570
+ detailed_info_md = gr.Markdown("์•ฝ๋ฌผ ์ •๋ณด๊ฐ€ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.", elem_classes=["output-card"])
571
+ with gr.Tab("๐Ÿ‘ด๐Ÿ‘ถ ์‰ฌ์šด ์„ค๋ช…"):
572
+ explain_md = gr.Markdown("์—ฌ๊ธฐ์— ์•ฝ ์„ค๋ช…์ด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.", elem_classes=["output-card"])
573
+ with gr.Tab("๐ŸŽจ ํ•œ ์ปท ๋งŒํ™”"):
574
+ cartoon_img = gr.Image(type="pil", label="ํ•œ ์ปท ๋งŒํ™”")
575
+ with gr.Tab("๐Ÿ“… ์ผ์ • ์นด๋“œ"):
576
+ card_out = gr.Image(type="pil", label="์ผ์ • ์นด๋“œ(๋ฏธ๋ฆฌ๋ณด๊ธฐ)")
577
+
578
  raw_box = gr.Textbox(label="๋ชจ๋ธ์ด ์ฝ์€ ์›๋ฌธ ํ…์ŠคํŠธ", lines=5, interactive=False)
579
+ csv_box = gr.Textbox(label="CSV(์•ฝ๋ช…,1ํšŒ์šฉ๋Ÿ‰,1์ผํšŸ์ˆ˜,์‹œ๊ฐ„๋Œ€)", lines=4, elem_classes=["csv-box"])
 
 
580
  with gr.Accordion("์„ธ๋ถ€ JSON ๊ฒฐ๊ณผ", open=False, elem_classes=["accordion"]):
581
  json_out = gr.Code(label="๋ชจ๋ธ ๋ถ„์„(JSON)")
582
 
583
  btn.click(
584
  run_pipeline,
585
  inputs=img_in,
586
+ outputs=[json_out, card_out, csv_box, explain_md, warn_md, raw_box, cartoon_img, detailed_info_md],
587
  )
588
 
589
  gr.Markdown(