lss9566 commited on
Commit
1f104d1
Β·
verified Β·
1 Parent(s): 51cce15

Upload 2 files

Browse files
Files changed (2) hide show
  1. gradio_app.py +689 -0
  2. requirements.txt +7 -0
gradio_app.py ADDED
@@ -0,0 +1,689 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from enhanced_rag_system import EnhancedImmunoRAGSystem
3
+ import json
4
+
5
+ # RAG μ‹œμŠ€ν…œ μ΄ˆκΈ°ν™”
6
+ rag_system = None
7
+
8
+ def initialize_rag_system():
9
+ global rag_system
10
+ try:
11
+ rag_system = EnhancedImmunoRAGSystem("merged_qa_dataset.json")
12
+ return "βœ… μ‹œμŠ€ν…œ μ€€λΉ„ μ™„λ£Œ"
13
+ except Exception as e:
14
+ return f"❌ λ‘œλ”© μ‹€νŒ¨: {str(e)}"
15
+
16
+ def answer_question(question, history):
17
+ if rag_system is None:
18
+ return history + [[question, "⚠️ μ‹œμŠ€ν…œμ„ λ¨Όμ € μ΄ˆκΈ°ν™”ν•΄μ£Όμ„Έμš”."]]
19
+
20
+ if not question.strip():
21
+ return history + [[question, "πŸ’­ μ§ˆλ¬Έμ„ μž…λ ₯ν•΄μ£Όμ„Έμš”."]]
22
+
23
+ try:
24
+ result = rag_system.answer_question(question)
25
+
26
+ # κΉ”λ”ν•œ 응닡 포맷
27
+ confidence = result['confidence']
28
+ confidence_bar = "β–ˆ" * int(confidence * 10) + "β–‘" * (10 - int(confidence * 10))
29
+ confidence_color = "🟒" if confidence > 0.7 else "🟑" if confidence > 0.4 else "πŸ”΄"
30
+
31
+ response = f"{result['answer']}\n\n"
32
+ response += f"{confidence_color} **신뒰도** `{confidence:.0%}` {confidence_bar}\n\n"
33
+
34
+ if result['matched_questions'] and confidence > 0.3:
35
+ top_match = result['matched_questions'][0]
36
+ response += f"πŸ’‘ **κ΄€λ ¨ 질문:** {top_match['question']}"
37
+
38
+ return history + [[question, response]]
39
+
40
+ except Exception as e:
41
+ return history + [[question, f"❌ **였λ₯˜**\n```\n{str(e)}\n```"]]
42
+
43
+ def clear_chat():
44
+ return []
45
+
46
+ # μ„Έλ ¨λœ CSS μŠ€νƒ€μΌ
47
+ custom_css = """
48
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
49
+
50
+ :root {
51
+ --primary: #6366f1;
52
+ --primary-light: #818cf8;
53
+ --primary-dark: #4f46e5;
54
+ --secondary: #64748b;
55
+ --accent: #06b6d4;
56
+ --success: #10b981;
57
+ --warning: #f59e0b;
58
+ --error: #ef4444;
59
+ --background: #fafbfc;
60
+ --surface: #ffffff;
61
+ --surface-2: #f8fafc;
62
+ --border: #e1e5e9;
63
+ --border-light: #f1f3f4;
64
+ --text: #1e293b;
65
+ --text-light: #64748b;
66
+ --text-muted: #94a3b8;
67
+ --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
68
+ --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
69
+ --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
70
+ --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
71
+ --radius: 12px;
72
+ --radius-lg: 16px;
73
+ --radius-xl: 20px;
74
+ }
75
+
76
+ /* μ „μ—­ 리셋 및 κΈ°λ³Έ μŠ€νƒ€μΌ */
77
+ * {
78
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif !important;
79
+ box-sizing: border-box;
80
+ }
81
+
82
+ /* μ „μ—­ ν…μŠ€νŠΈ 색상 */
83
+ *:not(.app-header):not(.app-header *):not(.user-message):not(.user-message *) {
84
+ color: var(--text) !important;
85
+ }
86
+
87
+ /* 메인 μ»¨ν…Œμ΄λ„ˆ */
88
+ .gradio-container {
89
+ max-width: 1000px !important;
90
+ margin: 0 auto !important;
91
+ padding: 24px !important;
92
+ background: var(--background) !important;
93
+ min-height: 100vh;
94
+ }
95
+
96
+ /* 헀더 - λ―Έλ‹ˆλ©€ λ””μžμΈ */
97
+ .app-header {
98
+ text-align: center;
99
+ padding: 48px 32px;
100
+ background: linear-gradient(135deg, var(--primary) 0%, var(--primary-light) 100%);
101
+ border-radius: var(--radius-xl);
102
+ margin-bottom: 32px;
103
+ box-shadow: var(--shadow-lg);
104
+ position: relative;
105
+ overflow: hidden;
106
+ }
107
+
108
+ .app-header::before {
109
+ content: '';
110
+ position: absolute;
111
+ top: 0;
112
+ left: 0;
113
+ right: 0;
114
+ bottom: 0;
115
+ background: linear-gradient(135deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.05) 100%);
116
+ pointer-events: none;
117
+ }
118
+
119
+ .app-header * {
120
+ color: white !important;
121
+ position: relative;
122
+ z-index: 1;
123
+ }
124
+
125
+ .app-header h1 {
126
+ font-size: 2.75rem;
127
+ font-weight: 700;
128
+ margin: 0 0 12px 0;
129
+ letter-spacing: -0.02em;
130
+ line-height: 1.1;
131
+ }
132
+
133
+ .app-header p {
134
+ font-size: 1.125rem;
135
+ opacity: 0.9;
136
+ margin: 0;
137
+ font-weight: 400;
138
+ line-height: 1.5;
139
+ }
140
+
141
+ /* μ±„νŒ… μ»¨ν…Œμ΄λ„ˆ - κΉ”λ”ν•œ μΉ΄λ“œ λ””μžμΈ */
142
+ .chat-container {
143
+ background: var(--surface);
144
+ border-radius: var(--radius-xl);
145
+ padding: 0;
146
+ margin-bottom: 24px;
147
+ border: 1px solid var(--border-light);
148
+ box-shadow: var(--shadow-sm);
149
+ overflow: hidden;
150
+ }
151
+
152
+ /* μ±„νŒ…λ°•μŠ€ */
153
+ .chatbot {
154
+ border: none !important;
155
+ border-radius: 0 !important;
156
+ background: transparent !important;
157
+ box-shadow: none !important;
158
+ min-height: 500px !important;
159
+ padding: 24px !important;
160
+ }
161
+
162
+ .chatbot .message {
163
+ border-radius: var(--radius) !important;
164
+ padding: 20px !important;
165
+ margin: 16px 0 !important;
166
+ box-shadow: none !important;
167
+ border: none !important;
168
+ max-width: 85% !important;
169
+ }
170
+
171
+ /* μ‚¬μš©μž λ©”μ‹œμ§€ */
172
+ .chatbot .message.user,
173
+ .user-message {
174
+ background: var(--primary) !important;
175
+ margin-left: auto !important;
176
+ margin-right: 0 !important;
177
+ border-radius: var(--radius) var(--radius) 4px var(--radius) !important;
178
+ }
179
+
180
+ .chatbot .message.user *,
181
+ .user-message * {
182
+ color: white !important;
183
+ }
184
+
185
+ /* 봇 λ©”μ‹œμ§€ */
186
+ .chatbot .message.bot {
187
+ background: var(--surface-2) !important;
188
+ margin-left: 0 !important;
189
+ margin-right: auto !important;
190
+ border: 1px solid var(--border-light) !important;
191
+ border-radius: var(--radius) var(--radius) var(--radius) 4px !important;
192
+ }
193
+
194
+ .chatbot .message.bot * {
195
+ color: var(--text) !important;
196
+ }
197
+
198
+ /* μž…λ ₯ μ˜μ—­ - ν”Œλ‘œνŒ… λ””μžμΈ */
199
+ .input-container {
200
+ background: var(--surface);
201
+ border-radius: var(--radius-lg);
202
+ padding: 20px;
203
+ border: 1px solid var(--border-light);
204
+ box-shadow: var(--shadow-sm);
205
+ margin-top: 24px;
206
+ }
207
+
208
+ .input-row {
209
+ background: transparent;
210
+ border-radius: 0;
211
+ padding: 0;
212
+ border: none;
213
+ box-shadow: none;
214
+ margin: 0;
215
+ }
216
+
217
+ .input-row .textbox,
218
+ .input-row input,
219
+ .input-row textarea {
220
+ border: 1px solid var(--border) !important;
221
+ background: var(--surface) !important;
222
+ border-radius: var(--radius) !important;
223
+ padding: 16px 20px !important;
224
+ font-size: 16px !important;
225
+ line-height: 1.5 !important;
226
+ transition: all 0.2s ease !important;
227
+ color: var(--text) !important;
228
+ box-shadow: var(--shadow-sm) !important;
229
+ }
230
+
231
+ .input-row .textbox::placeholder,
232
+ .input-row input::placeholder,
233
+ .input-row textarea::placeholder {
234
+ color: var(--text-muted) !important;
235
+ opacity: 1 !important;
236
+ }
237
+
238
+ .input-row .textbox:focus,
239
+ .input-row input:focus,
240
+ .input-row textarea:focus {
241
+ border-color: var(--primary) !important;
242
+ box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1) !important;
243
+ outline: none !important;
244
+ }
245
+
246
+ /* λ²„νŠΌ - λͺ¨λ˜ λ””μžμΈ */
247
+ .btn-primary,
248
+ button[variant="primary"] {
249
+ background: var(--primary) !important;
250
+ border: none !important;
251
+ border-radius: var(--radius) !important;
252
+ padding: 16px 24px !important;
253
+ font-weight: 600 !important;
254
+ font-size: 15px !important;
255
+ line-height: 1 !important;
256
+ transition: all 0.2s ease !important;
257
+ box-shadow: var(--shadow-sm) !important;
258
+ color: white !important;
259
+ cursor: pointer !important;
260
+ }
261
+
262
+ .btn-primary:hover,
263
+ button[variant="primary"]:hover {
264
+ background: var(--primary-dark) !important;
265
+ transform: translateY(-1px) !important;
266
+ box-shadow: var(--shadow-md) !important;
267
+ }
268
+
269
+ .btn-secondary {
270
+ background: var(--surface) !important;
271
+ border: 1px solid var(--border) !important;
272
+ border-radius: var(--radius) !important;
273
+ padding: 16px 24px !important;
274
+ font-weight: 500 !important;
275
+ font-size: 15px !important;
276
+ color: var(--text-light) !important;
277
+ transition: all 0.2s ease !important;
278
+ cursor: pointer !important;
279
+ }
280
+
281
+ .btn-secondary:hover {
282
+ border-color: var(--primary) !important;
283
+ color: var(--primary) !important;
284
+ transform: translateY(-1px) !important;
285
+ box-shadow: var(--shadow-sm) !important;
286
+ }
287
+
288
+ /* μ˜ˆμ‹œ 질문 - μΉ΄λ“œ κ·Έλ¦¬λ“œ */
289
+ .examples-section {
290
+ margin: 32px 0;
291
+ }
292
+
293
+ .examples-title {
294
+ font-size: 1.125rem;
295
+ font-weight: 600;
296
+ color: var(--text);
297
+ margin-bottom: 16px;
298
+ text-align: center;
299
+ }
300
+
301
+ .example-grid {
302
+ display: grid;
303
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
304
+ gap: 16px;
305
+ margin-top: 20px;
306
+ }
307
+
308
+ .example-btn {
309
+ background: var(--surface) !important;
310
+ border: 1px solid var(--border-light) !important;
311
+ border-radius: var(--radius) !important;
312
+ padding: 20px !important;
313
+ text-align: left !important;
314
+ font-size: 14px !important;
315
+ font-weight: 500 !important;
316
+ color: var(--text) !important;
317
+ transition: all 0.2s ease !important;
318
+ box-shadow: var(--shadow-sm) !important;
319
+ cursor: pointer !important;
320
+ line-height: 1.4 !important;
321
+ }
322
+
323
+ .example-btn:hover {
324
+ border-color: var(--primary) !important;
325
+ background: var(--surface) !important;
326
+ transform: translateY(-2px) !important;
327
+ box-shadow: var(--shadow-md) !important;
328
+ color: var(--primary) !important;
329
+ }
330
+
331
+ /* νƒ­ - λ―Έλ‹ˆλ©€ λ””μžμΈ */
332
+ .tab-nav {
333
+ background: var(--surface) !important;
334
+ border-radius: var(--radius-lg) !important;
335
+ border: 1px solid var(--border-light) !important;
336
+ overflow: hidden !important;
337
+ box-shadow: var(--shadow-sm) !important;
338
+ margin-bottom: 24px !important;
339
+ }
340
+
341
+ .tab-nav .tab-item {
342
+ padding: 20px 32px !important;
343
+ font-weight: 500 !important;
344
+ font-size: 15px !important;
345
+ transition: all 0.2s ease !important;
346
+ color: var(--text-light) !important;
347
+ border: none !important;
348
+ }
349
+
350
+ .tab-nav .tab-item.selected {
351
+ background: var(--primary) !important;
352
+ color: white !important;
353
+ }
354
+
355
+ .tab-nav .tab-item:hover:not(.selected) {
356
+ background: var(--surface-2) !important;
357
+ color: var(--text) !important;
358
+ }
359
+
360
+ /* μ‹œμŠ€ν…œ 정보 μ„Ήμ…˜ */
361
+ .system-info {
362
+ background: var(--surface);
363
+ border-radius: var(--radius-lg);
364
+ padding: 32px;
365
+ border: 1px solid var(--border-light);
366
+ box-shadow: var(--shadow-sm);
367
+ }
368
+
369
+ /* λ§ˆν¬λ‹€μš΄ μŠ€νƒ€μΌλ§ */
370
+ .markdown,
371
+ .gr-markdown {
372
+ color: var(--text) !important;
373
+ line-height: 1.6;
374
+ }
375
+
376
+ .markdown h1, .markdown h2, .markdown h3,
377
+ .gr-markdown h1, .gr-markdown h2, .gr-markdown h3 {
378
+ color: var(--text) !important;
379
+ font-weight: 600;
380
+ margin-top: 24px;
381
+ margin-bottom: 12px;
382
+ }
383
+
384
+ .markdown h3, .gr-markdown h3 {
385
+ font-size: 1.125rem;
386
+ color: var(--text) !important;
387
+ }
388
+
389
+ .markdown p, .gr-markdown p {
390
+ color: var(--text) !important;
391
+ margin-bottom: 16px;
392
+ }
393
+
394
+ .markdown table, .gr-markdown table {
395
+ border-collapse: collapse;
396
+ width: 100%;
397
+ margin: 20px 0;
398
+ border-radius: var(--radius);
399
+ overflow: hidden;
400
+ box-shadow: var(--shadow-sm);
401
+ }
402
+
403
+ .markdown th, .gr-markdown th {
404
+ background: var(--surface-2) !important;
405
+ color: var(--text) !important;
406
+ font-weight: 600;
407
+ padding: 16px;
408
+ text-align: left;
409
+ border-bottom: 1px solid var(--border);
410
+ }
411
+
412
+ .markdown td, .gr-markdown td {
413
+ color: var(--text) !important;
414
+ padding: 16px;
415
+ border-bottom: 1px solid var(--border-light);
416
+ }
417
+
418
+ .markdown strong, .gr-markdown strong {
419
+ color: var(--text) !important;
420
+ font-weight: 600;
421
+ }
422
+
423
+ .markdown code, .gr-markdown code {
424
+ background: var(--surface-2);
425
+ color: var(--text) !important;
426
+ padding: 2px 6px;
427
+ border-radius: 4px;
428
+ font-size: 0.875em;
429
+ }
430
+
431
+ /* μƒνƒœ ν‘œμ‹œ */
432
+ .status-output {
433
+ background: var(--surface-2);
434
+ border: 1px solid var(--border-light);
435
+ border-radius: var(--radius);
436
+ padding: 16px;
437
+ font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
438
+ font-size: 14px;
439
+ color: var(--text) !important;
440
+ }
441
+
442
+ /* λͺ¨λ°”일 μ΅œμ ν™” */
443
+ @media (max-width: 768px) {
444
+ .gradio-container {
445
+ padding: 16px !important;
446
+ }
447
+
448
+ .app-header {
449
+ padding: 32px 24px !important;
450
+ margin-bottom: 24px !important;
451
+ }
452
+
453
+ .app-header h1 {
454
+ font-size: 2.25rem !important;
455
+ }
456
+
457
+ .chatbot {
458
+ padding: 16px !important;
459
+ min-height: 400px !important;
460
+ }
461
+
462
+ .chatbot .message {
463
+ max-width: 90% !important;
464
+ padding: 16px !important;
465
+ }
466
+
467
+ .input-container {
468
+ padding: 16px !important;
469
+ }
470
+
471
+ .example-grid {
472
+ grid-template-columns: 1fr !important;
473
+ gap: 12px !important;
474
+ }
475
+
476
+ .example-btn {
477
+ padding: 16px !important;
478
+ }
479
+ }
480
+
481
+ /* 닀크λͺ¨λ“œ 지원 */
482
+ @media (prefers-color-scheme: dark) {
483
+ :root {
484
+ --background: #0f172a;
485
+ --surface: #1e293b;
486
+ --surface-2: #334155;
487
+ --border: #475569;
488
+ --border-light: #374151;
489
+ --text: #f1f5f9;
490
+ --text-light: #cbd5e0;
491
+ --text-muted: #94a3b8;
492
+ }
493
+ }
494
+
495
+ /* λΆ€λ“œλŸ¬μš΄ μ• λ‹ˆλ©”μ΄μ…˜ */
496
+ @keyframes slideUp {
497
+ from {
498
+ opacity: 0;
499
+ transform: translateY(20px);
500
+ }
501
+ to {
502
+ opacity: 1;
503
+ transform: translateY(0);
504
+ }
505
+ }
506
+
507
+ .fade-in {
508
+ animation: slideUp 0.4s ease-out;
509
+ }
510
+
511
+ /* μŠ€ν¬λ‘€λ°” μŠ€νƒ€μΌλ§ */
512
+ ::-webkit-scrollbar {
513
+ width: 8px;
514
+ }
515
+
516
+ ::-webkit-scrollbar-track {
517
+ background: var(--surface-2);
518
+ border-radius: 4px;
519
+ }
520
+
521
+ ::-webkit-scrollbar-thumb {
522
+ background: var(--border);
523
+ border-radius: 4px;
524
+ }
525
+
526
+ ::-webkit-scrollbar-thumb:hover {
527
+ background: var(--text-muted);
528
+ }
529
+ """
530
+
531
+ # μ˜ˆμ‹œ μ§ˆλ¬Έλ“€ (더 κ°„κ²°ν•˜κ²Œ)
532
+ example_questions = [
533
+ "HBs Ag μ°Έκ³ μΉ˜λŠ”?",
534
+ "CEA μ •μƒλ²”μœ„λŠ”?",
535
+ "PIVKA 검사 μ›λ¦¬λŠ”?",
536
+ "CA19-9 μ–Έμ œ λ‚˜μ™€μš”?",
537
+ "AFP κΈ°μ€€μΉ˜λŠ”?",
538
+ "PSA 정상값은?"
539
+ ]
540
+
541
+ # Gradio μΈν„°νŽ˜μ΄μŠ€
542
+ with gr.Blocks(
543
+ title="면역검사 AI 챗봇",
544
+ theme=gr.themes.Default(
545
+ primary_hue="blue",
546
+ secondary_hue="slate",
547
+ neutral_hue="slate",
548
+ font=gr.themes.GoogleFont("Inter")
549
+ ),
550
+ css=custom_css
551
+ ) as demo:
552
+
553
+ # 헀더
554
+ gr.HTML("""
555
+ <div class="app-header fade-in">
556
+ <h1>🧬 면역검사 AI</h1>
557
+ <p>μ •ν™•ν•˜κ³  λΉ λ₯΄μ€ 면역검사 정보λ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€</p>
558
+ </div>
559
+ """)
560
+
561
+ with gr.Tabs(elem_classes="tab-nav") as tabs:
562
+
563
+ # μ±„νŒ… νƒ­
564
+ with gr.Tab("πŸ’¬ μ±„νŒ…"):
565
+
566
+ with gr.Group(elem_classes="chat-container"):
567
+ gr.Markdown("πŸ’¬ μ•„λž˜ μ±„νŒ…μ°½μ— μ§ˆλ¬Έμ„ μž…λ ₯ν•΄ μ£Όμ„Έμš”. 처음 λ‘œλ”© μ‹œ λͺ¨λΈ 쀀비에 μ‹œκ°„μ΄ 걸릴 수 μžˆμŠ΅λ‹ˆλ‹€.")
568
+ chatbot = gr.Chatbot(
569
+ height=500,
570
+ )
571
+
572
+ with gr.Row():
573
+ msg = gr.Textbox(
574
+ placeholder="예: HBs Ag μ°Έκ³ μΉ˜λŠ” μ–Όλ§ˆμΈκ°€μš”?",
575
+ container=False,
576
+ scale=4,
577
+ show_label=False,
578
+ elem_classes="textbox"
579
+ )
580
+ with gr.Column(scale=1, min_width=120):
581
+ submit_btn = gr.Button(
582
+ "전솑",
583
+ variant="primary",
584
+ elem_classes="btn-primary"
585
+ )
586
+ clear_btn = gr.Button(
587
+ "μ΄ˆκΈ°ν™”",
588
+ elem_classes="btn-secondary"
589
+ )
590
+
591
+ # μ˜ˆμ‹œ μ§ˆλ¬Έλ“€
592
+ gr.Markdown("### πŸ’‘ λΉ λ₯Έ 질문")
593
+ gr.HTML('<div class="example-grid">')
594
+
595
+ with gr.Row():
596
+ for i, question in enumerate(example_questions):
597
+ if i % 3 == 0 and i > 0:
598
+ gr.HTML('</div><div class="example-grid">')
599
+
600
+ example_btn = gr.Button(
601
+ f"πŸ’­ {question}",
602
+ elem_classes="example-btn"
603
+ )
604
+ example_btn.click(
605
+ lambda x=question: x,
606
+ outputs=msg
607
+ )
608
+
609
+ gr.HTML('</div>')
610
+
611
+ # μ‹œμŠ€ν…œ 정보 νƒ­
612
+ with gr.Tab("βš™οΈ μ‹œμŠ€ν…œ"):
613
+
614
+ with gr.Row():
615
+ init_btn = gr.Button(
616
+ "πŸ”„ μ‹œμŠ€ν…œ μ΄ˆκΈ°ν™”",
617
+ variant="primary",
618
+ elem_classes="btn-primary"
619
+ )
620
+
621
+ init_output = gr.Textbox(
622
+ label="μƒνƒœ",
623
+ interactive=False,
624
+ lines=1
625
+ )
626
+
627
+ # μ‹œμŠ€ν…œ 정보
628
+ gr.Markdown("""
629
+ ### πŸ“‹ μ§€μ›ν•˜λŠ” 검사 ν•­λͺ©
630
+
631
+ | λΆ„λ₯˜ | 검사 ν•­λͺ© |
632
+ |------|----------|
633
+ | **κ°„μ—Ό 검사** | HBs Ag, HBs Ab, HBc Ab, HCV Ab |
634
+ | **μ’…μ–‘ ν‘œμ§€μž** | CEA, AFP, CA19-9, CA125, PSA, PIVKA-II |
635
+ | **호λ₯΄λͺ¬ 검사** | TSH, T3, T4, 인슐린 |
636
+ | **감염 검사** | HIV, 맀독, κ²°ν•΅ |
637
+
638
+ ### 🎯 μ‚¬μš©λ²•
639
+ 1. μžμ—°μ–΄λ‘œ μ§ˆλ¬Έν•˜μ„Έμš” (예: "HBs Ag μ •μƒμΉ˜λŠ”?")
640
+ 2. λ™μ˜μ–΄λ„ μΈμ‹ν•©λ‹ˆλ‹€ (예: "λΉ„ν˜•κ°„μ—Όν•­μ›", "Bν˜•κ°„μ—Όν‘œλ©΄ν•­μ›")
641
+ 3. 신뒰도가 λ†’μ„μˆ˜λ‘ μ •ν™•ν•œ λ‹΅λ³€μž…λ‹ˆλ‹€
642
+ """)
643
+
644
+ # 이벀트 ν•Έλ“€λŸ¬
645
+ msg.submit(
646
+ answer_question,
647
+ [msg, chatbot],
648
+ [chatbot]
649
+ ).then(
650
+ lambda: "",
651
+ outputs=msg
652
+ )
653
+
654
+ submit_btn.click(
655
+ answer_question,
656
+ [msg, chatbot],
657
+ [chatbot]
658
+ ).then(
659
+ lambda: "",
660
+ outputs=msg
661
+ )
662
+
663
+ clear_btn.click(clear_chat, outputs=chatbot)
664
+ init_btn.click(initialize_rag_system, outputs=init_output)
665
+
666
+ # μžλ™ μ΄ˆκΈ°ν™”
667
+ demo.load(initialize_rag_system, outputs=init_output)
668
+
669
+ if __name__ == "__main__":
670
+ import os
671
+ # Hugging Face Spaces/일반 ν™˜κ²½ λͺ¨λ‘ ν˜Έν™˜
672
+ port = int(os.environ.get("PORT", "7860"))
673
+ is_spaces = bool(os.environ.get("SPACE_ID"))
674
+
675
+ if is_spaces:
676
+ # Hugging Face Spaces: share κΈˆμ§€, queue ꢌμž₯
677
+ demo.queue(concurrency_count=3).launch(
678
+ server_name="0.0.0.0",
679
+ server_port=port,
680
+ share=False
681
+ )
682
+ else:
683
+ # 둜컬/기타 PaaS: ν•„μš” μ‹œ share ν—ˆμš©
684
+ demo.launch(
685
+ server_name="0.0.0.0",
686
+ server_port=port,
687
+ share=True,
688
+ show_error=True
689
+ )
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ gradio==4.44.1
2
+ streamlit
3
+ sentence-transformers
4
+ scikit-learn
5
+ numpy
6
+ torch
7
+ transformers