Spaces:
Running
Running
File size: 12,992 Bytes
49e7846 4bfa09f 49e7846 4bfa09f 49e7846 4bfa09f cac1418 4bfa09f 49e7846 cac1418 4bfa09f cac1418 4bfa09f 618375e 4bfa09f 618375e 4bfa09f 618375e 4bfa09f 618375e 4bfa09f 49e7846 4bfa09f 49e7846 4bfa09f 49e7846 4bfa09f 49e7846 4bfa09f 49e7846 b55b713 4bfa09f 49e7846 4bfa09f 49e7846 b55b713 4bfa09f 618375e 4bfa09f b55b713 4bfa09f b55b713 4bfa09f b55b713 4bfa09f b55b713 4bfa09f b55b713 4bfa09f 618375e 4bfa09f 618375e 49e7846 b55b713 4bfa09f 618375e 4bfa09f 618375e 4bfa09f 618375e 4bfa09f 618375e b55b713 4bfa09f 618375e 4bfa09f 618375e 4bfa09f 618375e 4bfa09f 618375e 4bfa09f 618375e b55b713 4bfa09f 618375e b55b713 4bfa09f 618375e 4bfa09f 618375e 49e7846 618375e 49e7846 4bfa09f b55b713 4bfa09f 618375e 4bfa09f b55b713 4bfa09f 618375e 4bfa09f 618375e 4bfa09f 618375e 4bfa09f 618375e b55b713 49e7846 b55b713 4bfa09f 618375e 49e7846 b55b713 4bfa09f 618375e 4bfa09f 618375e 49e7846 4bfa09f 618375e 4bfa09f cac1418 4bfa09f cac1418 |
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 |
import gradio as gr
import threading
import os
import requests
import string
import time
from pydub import AudioSegment
from nltk.tokenize import word_tokenize
import nltk
from nltk.corpus import words, stopwords
from dotenv import load_dotenv
# Download resource NLTK (hanya sekali)
nltk.download('punkt')
nltk.download('words')
nltk.download('stopwords')
load_dotenv()
API_TRANSCRIBE = os.getenv("API_TRANSCRIBE")
API_TEXT = os.getenv("API_TEXT")
english_words = set(words.words())
indonesian_stopwords = set(stopwords.words('indonesian'))
def load_indonesian_wordlist(filepath='wordlist.lst'):
try:
with open(filepath, encoding='utf-8') as f:
return set(line.strip().lower() for line in f if line.strip())
except UnicodeDecodeError:
try:
with open(filepath, encoding='latin-1') as f:
return set(line.strip().lower() for line in f if line.strip())
except Exception:
return set()
except Exception:
return set()
indonesian_words = load_indonesian_wordlist()
valid_words = english_words.union(indonesian_words)
def contains_medical_terms_auto_threshold(text, medical_words):
tokens = word_tokenize(text.lower())
tokens = [w.strip(string.punctuation) for w in tokens if w.isalpha()]
if not tokens:
return False
medical_count = sum(1 for w in tokens if w in medical_words)
ratio = medical_count / len(tokens)
threshold = 0.4 if len(tokens) <= 5 else 0.1
return ratio >= threshold
medical_words = load_indonesian_wordlist('wordlist.lst')
MAX_DURATION_SECONDS = 600
def validate_audio_duration(audio_file):
try:
audio = AudioSegment.from_file(audio_file)
duration_sec = len(audio) / 1000.0
if duration_sec > MAX_DURATION_SECONDS:
return False, duration_sec
return True, duration_sec
except Exception as e:
return False, -1
def start_recording():
"""Function yang dipanggil ketika tombol record ditekan"""
print("ποΈ Recording started...")
return "π΄ Recording..."
def stop_recording(audio):
"""Function yang dipanggil ketika recording selesai"""
if audio is not None:
print("β
Recording completed!")
return "β
Ready to process"
else:
print("β No audio recorded")
return "βͺ Ready to record"
def test_microphone():
"""Function untuk test microphone"""
print("π§ Testing microphone...")
return "π§ Testing microphone... Silakan coba record lagi"
def reset_recording_status():
"""Function untuk reset status recording"""
return "βͺ Ready to record"
def handle_audio(audio_file):
"""Handle audio processing - returns (validation_message, transcript, soap, tags)"""
if audio_file is None:
return "β Tidak ada file audio", "", "", ""
valid, duration = validate_audio_duration(audio_file)
if not valid:
if duration == -1:
msg = "β οΈ Gagal memproses file audio."
else:
msg = f"β οΈ Durasi rekaman terlalu panjang ({duration:.1f}s). Maksimal {MAX_DURATION_SECONDS}s."
return msg, "", "", ""
try:
with open(audio_file, "rb") as f:
files = {"audio": f}
response = requests.post(API_TRANSCRIBE, files=files)
result = response.json()
transcription = result.get("transcription", "")
soap_content = result.get("soap_content", "")
tags_content = result.get("tags_content", "")
if not transcription and not soap_content and not tags_content:
return "β οΈ Tidak ada hasil dari proses audio", "", "", ""
return "", transcription, soap_content, tags_content
except Exception as e:
return f"β Error processing audio: {str(e)}", "", "", ""
def handle_text(dialogue):
"""Handle text processing - returns (validation_message, transcript, soap, tags)"""
if not dialogue.strip():
return "β οΈ Teks tidak boleh kosong", "", "", ""
if not contains_medical_terms_auto_threshold(dialogue, medical_words):
return "β οΈ Teks tidak mengandung istilah medis yang cukup untuk diproses.", "", "", ""
try:
response = requests.post(API_TEXT, json={"dialogue": dialogue})
result = response.json()
soap_content = result.get("soap_content", "")
tags_content = result.get("tags_content", "")
if not soap_content and not tags_content:
return "β οΈ Tidak ada hasil dari proses teks", "", "", ""
return "", dialogue, soap_content, tags_content
except Exception as e:
return f"β Error processing text: {str(e)}", "", "", ""
def toggle_inputs_with_refresh(choice):
# Tampilkan input dan validasi yang sesuai, sembunyikan lainnya
return (
gr.update(visible=(choice == "Upload Audio"), value=None), # audio upload
gr.update(visible=(choice == "Realtime Recording"), value=None), # audio record
gr.update(visible=(choice == "Input Teks"), value=""), # text input
gr.update(visible=(choice == "Upload Audio")), # validasi upload
gr.update(visible=(choice == "Realtime Recording")), # validasi realtime
gr.update(visible=(choice == "Input Teks")), # validasi teks
gr.update(value=""), # transcript
gr.update(value=""), # soap
gr.update(value=""), # tags
)
def clear_all_data():
return (
gr.update(value=None), # audio_upload
gr.update(value=None), # audio_record
gr.update(value=""), # text_input
gr.update(value=""), # validation_upload
gr.update(value=""), # validation_realtime
gr.update(value=""), # validation_text
gr.update(value="βͺ Ready to record"), # recording_status
gr.update(value=""), # transcript_output
gr.update(value=""), # soap_output
gr.update(value=""), # tags_output
)
def process_data(choice, audio_upload, audio_record, text_input):
"""
Process data based on choice and return results in correct order:
Returns: (validation_upload, validation_realtime, validation_text, transcript, soap, tags)
"""
if choice == "Upload Audio":
# Process upload audio
validation_msg, transcript, soap, tags = handle_audio(audio_upload)
return (
validation_msg, # validation_upload
"", # validation_realtime (empty)
"", # validation_text (empty)
transcript, # transcript_output
soap, # soap_output
tags # tags_output
)
elif choice == "Realtime Recording":
# Process realtime recording
validation_msg, transcript, soap, tags = handle_audio(audio_record)
return (
"", # validation_upload (empty)
validation_msg, # validation_realtime
"", # validation_text (empty)
transcript, # transcript_output
soap, # soap_output
tags # tags_output
)
elif choice == "Input Teks":
# Process text input
validation_msg, transcript, soap, tags = handle_text(text_input)
return (
"", # validation_upload (empty)
"", # validation_realtime (empty)
validation_msg, # validation_text
transcript, # transcript_output (will be same as input for text)
soap, # soap_output
tags # tags_output
)
else:
# Default case - clear all
return ("", "", "", "", "", "")
# Buat interface dengan tampilan default Gradio
with gr.Blocks(title="π©Ί SOAP AI") as app:
# Header
gr.Markdown("# ποΈ SOAP AI - Medical Transcription & Analysis")
gr.Markdown("Aplikasi untuk mengkonversi percakapan dokter-pasien menjadi format SOAP")
with gr.Row():
with gr.Column(scale=8):
input_choice = gr.Dropdown(
choices=["Upload Audio", "Realtime Recording", "Input Teks"],
value="Realtime Recording",
label="π― Pilih Metode Input"
)
with gr.Column(scale=2):
clear_button = gr.Button("ποΈ Clear", variant="secondary")
# Input Section - Upload Audio
with gr.Group(visible=False) as upload_audio_group:
gr.Markdown("### π Upload Audio File")
audio_upload = gr.Audio(
sources=["upload"],
label="π Upload File Audio",
type="filepath"
)
# Input Section - Record Audio
with gr.Group(visible=True) as record_audio_group:
gr.Markdown("### π΅ Record Your Audio")
recording_status = gr.Textbox(
value="βͺ Ready to record",
interactive=False,
show_label=False,
lines=1
)
audio_record = gr.Audio(
sources=["microphone"],
label="ποΈ Realtime Recording",
type="filepath"
)
# Input Section - Text Input
with gr.Group(visible=False) as text_input_group:
gr.Markdown("### π Input Teks")
text_input = gr.Textbox(
label="π Masukkan Percakapan Dokter-Pasien",
lines=6,
placeholder="Ketik percakapan antara dokter dan pasien di sini..."
)
# Validation Section
validation_upload = gr.Textbox(
label="β οΈ Validasi Upload Audio",
lines=1,
interactive=False,
visible=False
)
validation_realtime = gr.Textbox(
label="β οΈ Validasi Realtime Recording",
lines=1,
interactive=False,
visible=True
)
validation_text = gr.Textbox(
label="β οΈ Validasi Input Teks",
lines=1,
interactive=False,
visible=False
)
# Process Button
process_button = gr.Button("π Proses ke SOAP", variant="primary", size="lg")
# Output Section
gr.Markdown("## π Hasil Analisis")
transcript_output = gr.Textbox(
label="π Hasil Transkripsi",
lines=4
)
soap_output = gr.Textbox(
label="π Ringkasan SOAP",
lines=8
)
tags_output = gr.Textbox(
label="π·οΈ Medical Tags",
lines=6
)
# Event handlers untuk toggle inputs
input_choice.change(
fn=lambda choice: (
gr.update(visible=(choice == "Upload Audio")),
gr.update(visible=(choice == "Realtime Recording")),
gr.update(visible=(choice == "Input Teks")),
gr.update(visible=(choice == "Upload Audio")),
gr.update(visible=(choice == "Realtime Recording")),
gr.update(visible=(choice == "Input Teks")),
gr.update(value=""),
gr.update(value=""),
gr.update(value="")
),
inputs=input_choice,
outputs=[
upload_audio_group,
record_audio_group,
text_input_group,
validation_upload,
validation_realtime,
validation_text,
transcript_output,
soap_output,
tags_output
]
)
# Event handlers untuk recording
audio_record.start_recording(
fn=start_recording,
outputs=recording_status
)
audio_record.stop_recording(
fn=stop_recording,
inputs=audio_record,
outputs=recording_status
)
clear_button.click(
fn=clear_all_data,
outputs=[
audio_upload,
audio_record,
text_input,
validation_upload,
validation_realtime,
validation_text,
recording_status,
transcript_output,
soap_output,
tags_output
]
)
process_button.click(
fn=process_data,
inputs=[input_choice, audio_upload, audio_record, text_input],
outputs=[
validation_upload,
validation_realtime,
validation_text,
transcript_output,
soap_output,
tags_output
],
show_progress="minimal"
)
# Startup information
if __name__ == "__main__":
print("π Starting SOAP AI Application...")
print("π Setup Instructions:")
print("1. Install dependencies: pip install gradio pydub nltk requests python-dotenv")
print("2. Make sure wordlist.lst file is available")
print("3. Set up your .env file with API_TRANSCRIBE and API_TEXT")
print()
print("\nπ Application will start at: http://localhost:7860")
print("ποΈ Make sure to allow microphone access when using Realtime Recording!")
print()
app.launch() |