Spaces:
Sleeping
Sleeping
File size: 5,249 Bytes
c421c84 61d7017 c421c84 61d7017 20a14d9 61d7017 c421c84 61d7017 c421c84 d518386 c421c84 61d7017 c421c84 d518386 61d7017 d518386 61d7017 c421c84 61d7017 c421c84 61d7017 20a14d9 c421c84 20a14d9 61d7017 c421c84 d518386 c421c84 61d7017 c421c84 61d7017 c421c84 61d7017 c421c84 61d7017 c421c84 61d7017 c421c84 61d7017 c421c84 61d7017 c421c84 d518386 c421c84 61d7017 20a14d9 a0be335 61d7017 c421c84 61d7017 c421c84 61d7017 c421c84 61d7017 c421c84 61d7017 c421c84 61d7017 c421c84 d518386 c421c84 d518386 61d7017 c421c84 61d7017 c421c84 61d7017 c421c84 61d7017 c421c84 61d7017 | 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 | """Gradio UI for single-output proofreading feedback.
Flow:
1. User enters source text and clicks "교정 실행".
2. The configured pipeline runs.
3. Output + diff are shown.
4. User picks Good / Not Bad / Critical (+ optional comment).
5. On submit: rating is saved to Supabase ratings table.
"""
from __future__ import annotations
import time
from typing import Any
import gradio as gr
from diff_utils import highlight_diff
from pipelines import run_pipeline
from . import db
PipelineConfig = tuple[str, str, str] # (pipeline_key, model, prompt_key)
def _format_summary(counts: dict[str, int]) -> str:
total = sum(counts.values())
if total == 0:
return "아직 피드백이 없습니다."
g = counts.get("good", 0)
n = counts.get("not_bad", 0)
c = counts.get("critical", 0)
return f"**총 피드백**: {total}\n\n- Good: **{g}**\n- Not Bad: **{n}**\n- Critical: **{c}**"
def build_feedback_tab(
client: Any,
vocabulary: list[dict],
pipeline_config: PipelineConfig,
elem_id_prefix: str = "compare",
) -> None:
"""Build the single-pipeline feedback UI. Call inside a gr.Blocks/Tab.
Args:
elem_id_prefix: Unique prefix for component elem_ids. Required
when building this UI multiple times in the same Blocks (e.g.
for two tabs) — Gradio errors on duplicate elem_ids.
"""
pipeline_key, model, prompt_key = pipeline_config
pipeline_run_id_state = gr.State(None)
input_text = gr.Textbox(
label="원문 입력",
lines=8,
placeholder="교정할 텍스트를 입력하세요.",
)
run_btn = gr.Button(
"교정 실행 (⌘+Enter / Ctrl+Enter)",
variant="primary",
elem_id=f"{elem_id_prefix}-run-btn",
)
status = gr.Markdown("")
output = gr.Textbox(label="교정 결과", lines=12, interactive=False)
diff_html = gr.HTML(label="원문 대비 diff")
gr.Markdown("### 피드백")
with gr.Row():
rate_good = gr.Button("👍 Good", variant="primary")
rate_notbad = gr.Button("🆗 Not Bad")
rate_critical = gr.Button("🚨 Critical", variant="stop")
comment = gr.Textbox(label="코멘트 (선택)", lines=2)
rating_status = gr.Markdown("")
summary_md = gr.Markdown("")
def _on_run(text: str):
if not text or not text.strip():
return (
gr.update(value="입력 텍스트가 비어있습니다."),
gr.update(value=""),
gr.update(value=""),
None,
gr.update(value=""),
)
if client is None:
return (
gr.update(value="UPSTAGE_API_KEY 미설정."),
gr.update(value=""),
gr.update(value=""),
None,
gr.update(value=""),
)
start = time.time()
try:
result = run_pipeline(text, pipeline_key, model, prompt_key, client, vocabulary)
except Exception as exc:
return (
gr.update(value=f"에러: {exc}"),
gr.update(value=""),
gr.update(value=""),
None,
gr.update(value=""),
)
elapsed = time.time() - start
out_text = result.get("output", "")
article_id = db.save_article(text)
run_id = db.save_pipeline_run(
article_id,
pipeline_key=pipeline_key,
prompt_key=prompt_key,
model=model,
output=out_text,
processing_time_s=float(elapsed),
)
return (
gr.update(value=f"완료 · {elapsed:.1f}s"),
gr.update(value=out_text),
gr.update(value=highlight_diff(text, out_text)),
run_id,
gr.update(value=""),
)
run_btn.click(
_on_run,
inputs=[input_text],
outputs=[status, output, diff_html, pipeline_run_id_state, rating_status],
)
def _make_rating_handler(rating: str):
def handler(run_id, comment_text):
saved = db.save_rating(run_id, rating, comment_text)
note = (
"✅ 피드백 저장됨"
if saved
else "⚠️ 저장되지 않았습니다 (먼저 교정 실행 후 피드백을 남겨주세요)"
)
summary = _format_summary(db.fetch_rating_counts())
return gr.update(value=note), gr.update(value=summary)
return handler
rate_good.click(
_make_rating_handler("good"),
inputs=[pipeline_run_id_state, comment],
outputs=[rating_status, summary_md],
)
rate_notbad.click(
_make_rating_handler("not_bad"),
inputs=[pipeline_run_id_state, comment],
outputs=[rating_status, summary_md],
)
rate_critical.click(
_make_rating_handler("critical"),
inputs=[pipeline_run_id_state, comment],
outputs=[rating_status, summary_md],
)
refresh_btn = gr.Button("집계 새로고침", size="sm")
refresh_btn.click(
lambda: gr.update(value=_format_summary(db.fetch_rating_counts())),
outputs=[summary_md],
)
|