Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -401,222 +401,306 @@ STRATEGY_INFO = [
|
|
| 401 |
]
|
| 402 |
|
| 403 |
CSS = """
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
|
| 408 |
-
|
| 409 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 410 |
}
|
| 411 |
|
| 412 |
-
/* Gradio 外層容器不要太貼邊 */
|
| 413 |
.gradio-container {
|
| 414 |
-
max-width:
|
| 415 |
-
margin: 0 auto !important;
|
| 416 |
}
|
| 417 |
|
| 418 |
-
|
| 419 |
-
|
| 420 |
-
background: #fbf6ec !important;
|
| 421 |
-
border: 1px solid #cfc3b2 !important;
|
| 422 |
-
border-top: 4px solid #2d6a4f !important;
|
| 423 |
-
border-radius: 14px !important;
|
| 424 |
-
padding: 28px 36px !important;
|
| 425 |
-
margin-bottom: 20px !important;
|
| 426 |
-
box-shadow: 0 2px 10px rgba(31, 27, 22, 0.06) !important;
|
| 427 |
}
|
| 428 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 429 |
.hdr-eyebrow {
|
| 430 |
-
|
| 431 |
-
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
|
| 437 |
}
|
| 438 |
-
|
| 439 |
.hdr-title {
|
| 440 |
-
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
|
|
|
|
|
|
|
| 445 |
}
|
| 446 |
-
|
| 447 |
.hdr-sub {
|
| 448 |
-
|
| 449 |
-
|
| 450 |
-
|
|
|
|
|
|
|
|
|
|
| 451 |
}
|
| 452 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 453 |
.pill {
|
| 454 |
-
display: inline-block
|
| 455 |
-
margin:
|
| 456 |
-
padding:
|
| 457 |
-
border-radius:
|
| 458 |
-
font-size: 11px
|
| 459 |
-
|
| 460 |
-
|
| 461 |
-
border: 1px solid rgba(
|
| 462 |
-
font-weight: 700 !important;
|
| 463 |
}
|
| 464 |
-
|
| 465 |
-
|
| 466 |
-
background:
|
| 467 |
-
color:
|
| 468 |
-
border-color: rgba(184, 122, 26, .35) !important;
|
| 469 |
}
|
| 470 |
|
| 471 |
-
|
| 472 |
-
|
| 473 |
-
|
| 474 |
-
|
| 475 |
-
|
| 476 |
-
|
| 477 |
-
background: #fbf7ee !important;
|
| 478 |
-
border: 1px solid #cfc3b2 !important;
|
| 479 |
-
border-radius: 12px !important;
|
| 480 |
-
color: #1f1b16 !important;
|
| 481 |
}
|
| 482 |
|
| 483 |
-
.
|
| 484 |
-
|
| 485 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 486 |
}
|
| 487 |
|
| 488 |
-
/* API Key 區塊 */
|
| 489 |
#apikey-box {
|
| 490 |
-
background:
|
| 491 |
-
border: 1.5px solid
|
| 492 |
-
border-radius:
|
| 493 |
-
padding:
|
| 494 |
-
|
|
|
|
| 495 |
}
|
| 496 |
|
| 497 |
-
|
| 498 |
-
.
|
| 499 |
-
|
| 500 |
-
|
| 501 |
-
|
| 502 |
-
|
| 503 |
-
|
| 504 |
-
|
| 505 |
-
|
|
|
|
|
|
|
| 506 |
}
|
| 507 |
-
|
| 508 |
-
|
| 509 |
-
|
| 510 |
-
|
| 511 |
-
|
| 512 |
-
.gradio-container span {
|
| 513 |
-
color: #2b241e !important;
|
| 514 |
-
opacity: 1 !important;
|
| 515 |
}
|
|
|
|
|
|
|
|
|
|
| 516 |
|
| 517 |
-
|
| 518 |
-
|
| 519 |
-
|
| 520 |
-
|
| 521 |
-
.gradio-container textarea,
|
| 522 |
-
.gradio-container input {
|
| 523 |
-
background: #3f3a34 !important;
|
| 524 |
-
color: #fffaf0 !important;
|
| 525 |
-
border: 1px solid #6b6258 !important;
|
| 526 |
-
border-radius: 7px !important;
|
| 527 |
-
opacity: 1 !important;
|
| 528 |
-
}
|
| 529 |
|
| 530 |
-
|
| 531 |
-
|
| 532 |
-
color: #c7bfb3 !important;
|
| 533 |
-
opacity: 1 !important;
|
| 534 |
}
|
| 535 |
|
| 536 |
-
|
| 537 |
-
|
| 538 |
-
.gradio-container .prose,
|
| 539 |
-
.gradio-container p,
|
| 540 |
-
.gradio-container div {
|
| 541 |
-
opacity: 1 !important;
|
| 542 |
}
|
| 543 |
|
| 544 |
-
|
| 545 |
-
|
| 546 |
-
display: grid !important;
|
| 547 |
-
grid-template-columns: repeat(4, 1fr) !important;
|
| 548 |
-
gap: 10px !important;
|
| 549 |
-
margin: 10px 0 16px !important;
|
| 550 |
}
|
| 551 |
|
| 552 |
-
.
|
| 553 |
-
|
| 554 |
-
|
| 555 |
-
|
| 556 |
-
padding: 10px 12px !important;
|
| 557 |
-
cursor: pointer !important;
|
| 558 |
-
transition: border-color .15s, box-shadow .15s, background .15s !important;
|
| 559 |
-
text-align: left !important;
|
| 560 |
-
width: 100% !important;
|
| 561 |
-
color: #fffaf0 !important;
|
| 562 |
}
|
| 563 |
|
| 564 |
-
.
|
| 565 |
-
|
| 566 |
-
|
| 567 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 568 |
}
|
| 569 |
|
| 570 |
-
.
|
| 571 |
-
|
| 572 |
-
|
| 573 |
-
|
|
|
|
| 574 |
}
|
| 575 |
|
| 576 |
-
.
|
| 577 |
-
.
|
| 578 |
-
|
| 579 |
-
|
| 580 |
-
|
| 581 |
-
|
| 582 |
-
.gradio-container button {
|
| 583 |
-
font-weight: 800 !important;
|
| 584 |
opacity: 1 !important;
|
| 585 |
}
|
| 586 |
|
| 587 |
-
|
| 588 |
-
background:
|
| 589 |
-
|
| 590 |
-
border:
|
| 591 |
-
|
| 592 |
}
|
| 593 |
|
| 594 |
-
|
| 595 |
-
|
| 596 |
-
|
| 597 |
-
|
| 598 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 599 |
}
|
|
|
|
| 600 |
|
| 601 |
-
|
| 602 |
-
|
| 603 |
-
|
| 604 |
-
|
| 605 |
-
|
| 606 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 607 |
}
|
| 608 |
"""
|
| 609 |
|
| 610 |
HEADER_HTML = """
|
| 611 |
<div id="hdr">
|
| 612 |
-
<div class="hdr-eyebrow">
|
| 613 |
-
<div class="hdr-title">多策略 RAG 文件問答系統</div>
|
| 614 |
-
<div class="hdr-sub">支援 PDF / DOCX 上傳,採用 ChromaDB 持久化向量資料庫與 8 種 RAG 檢索策略</div>
|
| 615 |
-
<div>
|
|
|
|
|
|
|
| 616 |
<span class="pill">▸ Groq API</span>
|
| 617 |
<span class="pill">▸ llama-3.1-8b-instant</span>
|
| 618 |
-
<span class="pill
|
| 619 |
-
<span class="pill
|
| 620 |
<span class="pill">▸ SentenceTransformers</span>
|
| 621 |
</div>
|
| 622 |
</div>
|
|
@@ -700,8 +784,10 @@ def create_interface():
|
|
| 700 |
# ── 左欄 ──────────────────���───────────────
|
| 701 |
with gr.Column(scale=1, min_width=320, elem_classes="card-box"):
|
| 702 |
|
|
|
|
|
|
|
| 703 |
# ★ Step 00:API Key 輸入(新增)
|
| 704 |
-
gr.HTML("<div class='sec-label'>Step 00 ·
|
| 705 |
with gr.Group(elem_id="apikey-box"):
|
| 706 |
api_key_input = gr.Textbox(
|
| 707 |
label="Groq API Key",
|
|
@@ -718,18 +804,18 @@ def create_interface():
|
|
| 718 |
value="✓ API Key 已從環境變數載入" if env_key else "⚠ 尚未設定 API Key",
|
| 719 |
interactive=False,
|
| 720 |
lines=1,
|
| 721 |
-
label="
|
| 722 |
show_label=False,
|
| 723 |
)
|
| 724 |
|
| 725 |
# Step 01:上傳文件
|
| 726 |
-
gr.HTML("<div class='sec-label'>Step 01 ·
|
| 727 |
-
file_input = gr.File(label="PDF / DOCX", file_types=[".pdf", ".docx"])
|
| 728 |
-
load_btn = gr.Button("↑ 載入
|
| 729 |
-
status = gr.Textbox(label="狀態", interactive=False, lines=3)
|
| 730 |
|
| 731 |
# Step 02:RAG 策略
|
| 732 |
-
gr.HTML("<div class='sec-label'>Step 02 · 選擇
|
| 733 |
gr.HTML(build_strategy_menu("semantic"))
|
| 734 |
strategy_input = gr.Textbox(
|
| 735 |
value="semantic",
|
|
@@ -738,26 +824,27 @@ def create_interface():
|
|
| 738 |
visible=False,
|
| 739 |
)
|
| 740 |
strategy_status = gr.Textbox(
|
| 741 |
-
value="✓ 已選擇
|
| 742 |
interactive=False,
|
| 743 |
lines=1,
|
| 744 |
-
label="目前
|
| 745 |
)
|
| 746 |
gr.HTML(STRATEGY_MENU_JS)
|
| 747 |
|
| 748 |
# Step 03:參數
|
| 749 |
-
gr.HTML("<div class='sec-label'>Step 03 ·
|
| 750 |
-
topk = gr.Slider(minimum=1, maximum=10, value=3, step=1, label="Top-K 片段數量")
|
| 751 |
|
| 752 |
# ── 右欄:問答 ────────────────────────────
|
| 753 |
with gr.Column(scale=2, elem_classes="card-box"):
|
| 754 |
-
gr.HTML("<div class='
|
|
|
|
| 755 |
qin = gr.Textbox(
|
| 756 |
label="問題",
|
| 757 |
placeholder="例如:這份文件的核心論點是什麼?",
|
| 758 |
lines=4,
|
| 759 |
)
|
| 760 |
-
ask_btn = gr.Button("提問", variant="primary", size="lg", elem_id="ask-btn")
|
| 761 |
|
| 762 |
gr.HTML("<div class='sec-label'>AI 回答</div>")
|
| 763 |
ans = gr.Textbox(label="AI 回答內容", lines=12, interactive=False)
|
|
|
|
| 401 |
]
|
| 402 |
|
| 403 |
CSS = """
|
| 404 |
+
:root {
|
| 405 |
+
--paper: #f3ecdf;
|
| 406 |
+
--paper-2: #ebe2d1;
|
| 407 |
+
--paper-3: #dcc9a8;
|
| 408 |
+
--ink: #1c2433;
|
| 409 |
+
--ink-soft: #41506a;
|
| 410 |
+
--gold: #b88a42;
|
| 411 |
+
--gold-2: #d1ab63;
|
| 412 |
+
--line: rgba(94, 74, 40, .28);
|
| 413 |
+
--panel: rgba(255, 251, 242, 0.86);
|
| 414 |
+
--panel-strong: rgba(252, 247, 238, 0.96);
|
| 415 |
+
--shadow: 0 16px 40px rgba(29, 28, 26, .12);
|
| 416 |
+
}
|
| 417 |
+
|
| 418 |
+
body, .gradio-container {
|
| 419 |
+
background:
|
| 420 |
+
radial-gradient(circle at top left, rgba(184,138,66,.10), transparent 32%),
|
| 421 |
+
radial-gradient(circle at bottom right, rgba(28,36,51,.08), transparent 30%),
|
| 422 |
+
linear-gradient(180deg, #f6f1e7 0%, #efe7d7 100%) !important;
|
| 423 |
+
color: var(--ink) !important;
|
| 424 |
+
font-family: 'Noto Serif TC', 'Microsoft JhengHei', serif !important;
|
| 425 |
}
|
| 426 |
|
|
|
|
| 427 |
.gradio-container {
|
| 428 |
+
max-width: 1320px !important;
|
|
|
|
| 429 |
}
|
| 430 |
|
| 431 |
+
.contain, .app {
|
| 432 |
+
background: transparent !important;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 433 |
}
|
| 434 |
|
| 435 |
+
#hdr {
|
| 436 |
+
position: relative;
|
| 437 |
+
overflow: hidden;
|
| 438 |
+
background:
|
| 439 |
+
linear-gradient(135deg, rgba(255,249,239,.97), rgba(246,238,224,.94)),
|
| 440 |
+
linear-gradient(90deg, rgba(184,138,66,.08), rgba(28,36,51,.04));
|
| 441 |
+
border: 1px solid rgba(147, 117, 69, .35);
|
| 442 |
+
border-radius: 26px;
|
| 443 |
+
padding: 30px 34px 28px;
|
| 444 |
+
margin-bottom: 18px;
|
| 445 |
+
box-shadow: var(--shadow);
|
| 446 |
+
}
|
| 447 |
+
#hdr::before {
|
| 448 |
+
content: '';
|
| 449 |
+
position: absolute;
|
| 450 |
+
inset: 0;
|
| 451 |
+
pointer-events: none;
|
| 452 |
+
background:
|
| 453 |
+
radial-gradient(circle at 10% 15%, rgba(184,138,66,.13), transparent 16%),
|
| 454 |
+
radial-gradient(circle at 92% 12%, rgba(28,36,51,.12), transparent 20%),
|
| 455 |
+
linear-gradient(90deg, rgba(184,138,66,.22), transparent 20%, transparent 80%, rgba(184,138,66,.22));
|
| 456 |
+
opacity: .65;
|
| 457 |
+
}
|
| 458 |
+
#hdr::after {
|
| 459 |
+
content: '封\A魔';
|
| 460 |
+
white-space: pre;
|
| 461 |
+
position: absolute;
|
| 462 |
+
top: 22px;
|
| 463 |
+
right: 22px;
|
| 464 |
+
color: rgba(184,138,66,.20);
|
| 465 |
+
font-size: 36px;
|
| 466 |
+
line-height: 1.05;
|
| 467 |
+
font-weight: 700;
|
| 468 |
+
letter-spacing: .18em;
|
| 469 |
+
}
|
| 470 |
.hdr-eyebrow {
|
| 471 |
+
position: relative;
|
| 472 |
+
z-index: 1;
|
| 473 |
+
font-size: 11px;
|
| 474 |
+
letter-spacing: .32em;
|
| 475 |
+
color: #8f6d32;
|
| 476 |
+
text-transform: uppercase;
|
| 477 |
+
margin-bottom: 10px;
|
| 478 |
}
|
|
|
|
| 479 |
.hdr-title {
|
| 480 |
+
position: relative;
|
| 481 |
+
z-index: 1;
|
| 482 |
+
font-size: 34px;
|
| 483 |
+
line-height: 1.2;
|
| 484 |
+
font-weight: 800;
|
| 485 |
+
color: #1c2433;
|
| 486 |
+
margin: 0 0 10px;
|
| 487 |
}
|
|
|
|
| 488 |
.hdr-sub {
|
| 489 |
+
position: relative;
|
| 490 |
+
z-index: 1;
|
| 491 |
+
font-size: 15px;
|
| 492 |
+
line-height: 1.85;
|
| 493 |
+
color: #5a5347;
|
| 494 |
+
max-width: 920px;
|
| 495 |
}
|
| 496 |
+
.hdr-quote {
|
| 497 |
+
position: relative;
|
| 498 |
+
z-index: 1;
|
| 499 |
+
margin-top: 14px;
|
| 500 |
+
color: #6b604d;
|
| 501 |
+
font-size: 13px;
|
| 502 |
+
letter-spacing: .08em;
|
| 503 |
+
}
|
| 504 |
+
.hdr-pills { position: relative; z-index: 1; margin-top: 16px; }
|
| 505 |
.pill {
|
| 506 |
+
display: inline-block;
|
| 507 |
+
margin: 8px 8px 0 0;
|
| 508 |
+
padding: 5px 12px;
|
| 509 |
+
border-radius: 999px;
|
| 510 |
+
font-size: 11px;
|
| 511 |
+
color: #7f5a1e;
|
| 512 |
+
background: rgba(245, 233, 205, .95);
|
| 513 |
+
border: 1px solid rgba(184,138,66,.32);
|
|
|
|
| 514 |
}
|
| 515 |
+
.pill-dark {
|
| 516 |
+
color: #e9e7df;
|
| 517 |
+
background: rgba(28,36,51,.92);
|
| 518 |
+
border-color: rgba(28,36,51,.92);
|
|
|
|
| 519 |
}
|
| 520 |
|
| 521 |
+
.card-box {
|
| 522 |
+
background: linear-gradient(180deg, rgba(255, 250, 241, .92), rgba(250, 244, 233, .90)) !important;
|
| 523 |
+
border: 1px solid rgba(147, 117, 69, .26) !important;
|
| 524 |
+
border-radius: 24px !important;
|
| 525 |
+
padding: 20px !important;
|
| 526 |
+
box-shadow: var(--shadow);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 527 |
}
|
| 528 |
|
| 529 |
+
.sec-label {
|
| 530 |
+
display: flex;
|
| 531 |
+
align-items: center;
|
| 532 |
+
gap: 10px;
|
| 533 |
+
font-size: 15px;
|
| 534 |
+
letter-spacing: .22em;
|
| 535 |
+
text-transform: uppercase;
|
| 536 |
+
color: #26324a;
|
| 537 |
+
font-weight: 800;
|
| 538 |
+
margin: 10px 0 14px;
|
| 539 |
+
}
|
| 540 |
+
.sec-label::after {
|
| 541 |
+
content: '';
|
| 542 |
+
flex: 1;
|
| 543 |
+
height: 1px;
|
| 544 |
+
background: linear-gradient(90deg, rgba(184,138,66,.6), rgba(184,138,66,0));
|
| 545 |
+
}
|
| 546 |
+
.sub-note {
|
| 547 |
+
margin: -4px 0 14px;
|
| 548 |
+
color: #7a6a57;
|
| 549 |
+
font-size: 12px;
|
| 550 |
+
line-height: 1.7;
|
| 551 |
}
|
| 552 |
|
|
|
|
| 553 |
#apikey-box {
|
| 554 |
+
background: linear-gradient(180deg, rgba(255,244,219,.80), rgba(255,249,238,.88));
|
| 555 |
+
border: 1.5px solid rgba(190, 135, 33, .85);
|
| 556 |
+
border-radius: 18px;
|
| 557 |
+
padding: 16px 16px 10px;
|
| 558 |
+
box-shadow: inset 0 0 0 1px rgba(255,255,255,.55);
|
| 559 |
+
margin-bottom: 10px;
|
| 560 |
}
|
| 561 |
|
| 562 |
+
.strat-grid { display:grid; grid-template-columns:repeat(4,1fr); gap:12px; margin:12px 0 16px; }
|
| 563 |
+
.strat-card {
|
| 564 |
+
background: linear-gradient(180deg, rgba(255,249,239,.98), rgba(246,238,223,.94));
|
| 565 |
+
border:1px solid rgba(148,120,73,.28);
|
| 566 |
+
border-radius:16px;
|
| 567 |
+
padding:12px 12px 11px;
|
| 568 |
+
cursor:pointer;
|
| 569 |
+
transition:transform .18s ease, border-color .18s ease, box-shadow .18s ease;
|
| 570 |
+
text-align:left;
|
| 571 |
+
width:100%;
|
| 572 |
+
box-shadow: 0 6px 18px rgba(0,0,0,.04);
|
| 573 |
}
|
| 574 |
+
.strat-card:hover { border-color:#b88a42; box-shadow:0 10px 24px rgba(184,138,66,.12); transform: translateY(-2px); }
|
| 575 |
+
.strat-card.active {
|
| 576 |
+
border-color:#b88a42;
|
| 577 |
+
background: linear-gradient(180deg, rgba(255,246,224,.98), rgba(249,238,212,.96));
|
| 578 |
+
box-shadow:0 12px 26px rgba(184,138,66,.16);
|
|
|
|
|
|
|
|
|
|
| 579 |
}
|
| 580 |
+
.strat-icon { font-size:20px; margin-bottom:6px; }
|
| 581 |
+
.strat-name { font-size:14px; font-weight:800; color:#1c2433; margin:0 0 3px; }
|
| 582 |
+
.strat-desc { font-size:11.5px; color:#6d6253; line-height:1.55; }
|
| 583 |
|
| 584 |
+
#ask-btn { background: linear-gradient(180deg, #22314b, #172131) !important; color:#fff !important; border:1px solid #172131 !important; border-radius:12px !important; }
|
| 585 |
+
#ask-btn:hover { filter: brightness(1.08); }
|
| 586 |
+
#apply-key-btn { background: linear-gradient(180deg, #c18a22, #a97012) !important; color:#fff !important; border:1px solid #8e5d11 !important; border-radius:12px !important; }
|
| 587 |
+
#apply-key-btn:hover { filter: brightness(1.06); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 588 |
|
| 589 |
+
button.secondary, .secondary-button {
|
| 590 |
+
border-radius: 12px !important;
|
|
|
|
|
|
|
| 591 |
}
|
| 592 |
|
| 593 |
+
.gr-textbox, .gr-file, .gr-slider, .gr-accordion, .gr-examples {
|
| 594 |
+
--block-background-fill: transparent !important;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 595 |
}
|
| 596 |
|
| 597 |
+
textarea, input, .gradio-container textarea, .gradio-container input {
|
| 598 |
+
font-family: 'Noto Serif TC', 'Microsoft JhengHei', serif !important;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 599 |
}
|
| 600 |
|
| 601 |
+
.gr-textbox label, .gr-file label, .gr-slider label {
|
| 602 |
+
color: #3a2e1b !important;
|
| 603 |
+
font-size: 12px !important;
|
| 604 |
+
letter-spacing: .08em !important;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 605 |
}
|
| 606 |
|
| 607 |
+
.gr-textbox textarea,
|
| 608 |
+
.gr-textbox input,
|
| 609 |
+
textarea.scroll-hide,
|
| 610 |
+
input[type='text'],
|
| 611 |
+
input[type='password'] {
|
| 612 |
+
background: linear-gradient(180deg, rgba(29,38,55,.96), rgba(36,49,70,.94)) !important;
|
| 613 |
+
color: #f6efe3 !important;
|
| 614 |
+
border: 1px solid rgba(164, 137, 94, .55) !important;
|
| 615 |
+
border-radius: 16px !important;
|
| 616 |
+
box-shadow: inset 0 0 0 1px rgba(255,255,255,.03) !important;
|
| 617 |
}
|
| 618 |
|
| 619 |
+
.gr-textbox textarea::placeholder,
|
| 620 |
+
.gr-textbox input::placeholder,
|
| 621 |
+
textarea::placeholder,
|
| 622 |
+
input::placeholder {
|
| 623 |
+
color: #b9b3aa !important;
|
| 624 |
}
|
| 625 |
|
| 626 |
+
.gr-textbox textarea[disabled],
|
| 627 |
+
.gr-textbox input[disabled],
|
| 628 |
+
textarea[disabled],
|
| 629 |
+
input[disabled] {
|
| 630 |
+
background: linear-gradient(180deg, rgba(58,53,47,.96), rgba(70,64,56,.94)) !important;
|
| 631 |
+
color: #f3ebdd !important;
|
|
|
|
|
|
|
| 632 |
opacity: 1 !important;
|
| 633 |
}
|
| 634 |
|
| 635 |
+
.gr-file {
|
| 636 |
+
background: rgba(255, 251, 243, .75) !important;
|
| 637 |
+
border: 1px dashed rgba(147,117,69,.45) !important;
|
| 638 |
+
border-radius: 16px !important;
|
| 639 |
+
padding: 6px !important;
|
| 640 |
}
|
| 641 |
|
| 642 |
+
.gr-accordion {
|
| 643 |
+
border: 1px solid rgba(147,117,69,.30) !important;
|
| 644 |
+
border-radius: 16px !important;
|
| 645 |
+
overflow: hidden !important;
|
| 646 |
+
background: rgba(255,250,242,.78) !important;
|
| 647 |
+
}
|
| 648 |
+
.gr-accordion summary {
|
| 649 |
+
background: linear-gradient(180deg, rgba(248,240,225,.95), rgba(241,232,213,.94)) !important;
|
| 650 |
+
color: #26324a !important;
|
| 651 |
+
font-weight: 700 !important;
|
| 652 |
}
|
| 653 |
+
.gr-accordion .label-wrap span { color: #26324a !important; }
|
| 654 |
|
| 655 |
+
.gr-examples {
|
| 656 |
+
background: rgba(255,251,243,.72) !important;
|
| 657 |
+
border: 1px solid rgba(147,117,69,.25) !important;
|
| 658 |
+
border-radius: 16px !important;
|
| 659 |
+
padding: 10px 12px !important;
|
| 660 |
+
}
|
| 661 |
+
.gr-examples .label-wrap span,
|
| 662 |
+
.gr-examples .label-wrap label {
|
| 663 |
+
color: #7b6238 !important;
|
| 664 |
+
font-weight: 700 !important;
|
| 665 |
+
}
|
| 666 |
+
.gr-examples button {
|
| 667 |
+
background: rgba(255,247,230,.96) !important;
|
| 668 |
+
border: 1px solid rgba(184,138,66,.30) !important;
|
| 669 |
+
color: #4f4334 !important;
|
| 670 |
+
border-radius: 12px !important;
|
| 671 |
+
}
|
| 672 |
+
.gr-examples button:hover {
|
| 673 |
+
border-color: #b88a42 !important;
|
| 674 |
+
color: #26324a !important;
|
| 675 |
+
}
|
| 676 |
+
|
| 677 |
+
input[type='range'] { accent-color: #b88a42 !important; }
|
| 678 |
+
|
| 679 |
+
@media (max-width: 980px) {
|
| 680 |
+
.strat-grid { grid-template-columns: repeat(2, 1fr); }
|
| 681 |
+
.hdr-title { font-size: 28px; }
|
| 682 |
+
#hdr { padding: 24px 22px; }
|
| 683 |
+
}
|
| 684 |
+
@media (max-width: 640px) {
|
| 685 |
+
.strat-grid { grid-template-columns: 1fr; }
|
| 686 |
+
.sec-label { font-size: 13px; letter-spacing: .16em; }
|
| 687 |
+
.hdr-title { font-size: 24px; }
|
| 688 |
+
#hdr::after { display: none; }
|
| 689 |
}
|
| 690 |
"""
|
| 691 |
|
| 692 |
HEADER_HTML = """
|
| 693 |
<div id="hdr">
|
| 694 |
+
<div class="hdr-eyebrow">Arcane Archive · Multi-Strategy Retrieval</div>
|
| 695 |
+
<div class="hdr-title">墨境卷宗|多策略 RAG 文件問答系統</div>
|
| 696 |
+
<div class="hdr-sub">以「古卷、夜藍、鎏金、紙墨」為視覺核心,重塑你的文件問答介面。支援 PDF / DOCX 上傳,採用 ChromaDB 持久化向量資料庫與 8 種 RAG 檢索策略。</div>
|
| 697 |
+
<div class="hdr-quote">殿堂、工廠、流浪船、試煉之地 —— 每一次檢索,都是穿越文件迷霧的航行。</div>
|
| 698 |
+
<div class="hdr-pills">
|
| 699 |
+
<span class="pill pill-dark">▸ 多策略檢索</span>
|
| 700 |
<span class="pill">▸ Groq API</span>
|
| 701 |
<span class="pill">▸ llama-3.1-8b-instant</span>
|
| 702 |
+
<span class="pill">▸ ChromaDB</span>
|
| 703 |
+
<span class="pill">▸ PDF / DOCX</span>
|
| 704 |
<span class="pill">▸ SentenceTransformers</span>
|
| 705 |
</div>
|
| 706 |
</div>
|
|
|
|
| 784 |
# ── 左欄 ──────────────────���───────────────
|
| 785 |
with gr.Column(scale=1, min_width=320, elem_classes="card-box"):
|
| 786 |
|
| 787 |
+
gr.HTML("<div class='sub-note'>以金紋卷軸風格整理左側控制面板:先點亮金鑰,再載入卷宗,最後挑選檢索術式。</div>")
|
| 788 |
+
|
| 789 |
# ★ Step 00:API Key 輸入(新增)
|
| 790 |
+
gr.HTML("<div class='sec-label'>Step 00 · 啟封金鑰</div>")
|
| 791 |
with gr.Group(elem_id="apikey-box"):
|
| 792 |
api_key_input = gr.Textbox(
|
| 793 |
label="Groq API Key",
|
|
|
|
| 804 |
value="✓ API Key 已從環境變數載入" if env_key else "⚠ 尚未設定 API Key",
|
| 805 |
interactive=False,
|
| 806 |
lines=1,
|
| 807 |
+
label="金鑰狀態",
|
| 808 |
show_label=False,
|
| 809 |
)
|
| 810 |
|
| 811 |
# Step 01:上傳文件
|
| 812 |
+
gr.HTML("<div class='sec-label'>Step 01 · 載入卷宗</div>")
|
| 813 |
+
file_input = gr.File(label="上傳卷宗(PDF / DOCX)", file_types=[".pdf", ".docx"])
|
| 814 |
+
load_btn = gr.Button("↑ 載入卷宗")
|
| 815 |
+
status = gr.Textbox(label="卷宗狀態", interactive=False, lines=3)
|
| 816 |
|
| 817 |
# Step 02:RAG 策略
|
| 818 |
+
gr.HTML("<div class='sec-label'>Step 02 · 選擇檢索術式</div>")
|
| 819 |
gr.HTML(build_strategy_menu("semantic"))
|
| 820 |
strategy_input = gr.Textbox(
|
| 821 |
value="semantic",
|
|
|
|
| 824 |
visible=False,
|
| 825 |
)
|
| 826 |
strategy_status = gr.Textbox(
|
| 827 |
+
value="✓ 已選擇術式:語意搜尋",
|
| 828 |
interactive=False,
|
| 829 |
lines=1,
|
| 830 |
+
label="目前術式",
|
| 831 |
)
|
| 832 |
gr.HTML(STRATEGY_MENU_JS)
|
| 833 |
|
| 834 |
# Step 03:參數
|
| 835 |
+
gr.HTML("<div class='sec-label'>Step 03 · 檢索參數</div>")
|
| 836 |
+
topk = gr.Slider(minimum=1, maximum=10, value=3, step=1, label="Top-K 檢索片段數量")
|
| 837 |
|
| 838 |
# ── 右欄:問答 ────────────────────────────
|
| 839 |
with gr.Column(scale=2, elem_classes="card-box"):
|
| 840 |
+
gr.HTML("<div class='sub-note'>右側為提問主場域。介面配色改為夜藍輸入框與紙本質地背景,營造海報式卷宗氛圍。</div>")
|
| 841 |
+
gr.HTML("<div class='sec-label'>Step 04 · 發問</div>")
|
| 842 |
qin = gr.Textbox(
|
| 843 |
label="問題",
|
| 844 |
placeholder="例如:這份文件的核心論點是什麼?",
|
| 845 |
lines=4,
|
| 846 |
)
|
| 847 |
+
ask_btn = gr.Button("開始提問", variant="primary", size="lg", elem_id="ask-btn")
|
| 848 |
|
| 849 |
gr.HTML("<div class='sec-label'>AI 回答</div>")
|
| 850 |
ans = gr.Textbox(label="AI 回答內容", lines=12, interactive=False)
|