proofread-demo / app.py
dev-strender's picture
Replace v24-era demo with v34 pipeline demo (engine-vendored bundle)
9c84f9d verified
Raw History Blame
4.79 kB
"""์กฐ์„ ์ผ๋ณด ๋ณธ๋ฌธ ๊ต์—ด ๋ฐ๋ชจ (Streamlit / HF Space).
v34 ํŒŒ์ดํ”„๋ผ์ธ(prod 7-์Šคํ… + ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ ๋ณต์› + ์œจ/๋ฅ  ๊ทœ์น™) ร— combo ํ”„๋กฌํ”„ํŠธ๋ฅผ
solar-eval ์—”์ง„ ๊ทธ๋Œ€๋กœ ์‹คํ–‰ํ•œ๋‹ค. ๋กœ์ง์€ ์ „๋ถ€ runner.py โ€” ์ด ํŒŒ์ผ์€ UI ๋งŒ ๋‹ด๋‹น.
"""
from __future__ import annotations
import difflib
import html
import os
import runner
import streamlit as st
SAMPLE_TEXT = (
"์ •๋ถ€์˜ ๊ฐ•๋ ฅํ•œ ๋ถ€๋™์‚ฐ ๊ทœ์ œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ์ˆ˜๋„๊ถŒ ์•„ํŒŒํŠธ ๋งค๋งค ๊ฑฐ๋ž˜ ๋น„๋ฅ ์€ "
"์˜คํžˆ๋ ค ์ฆ๊ฐ€์„ธ๋ฅผ ๋ณด์˜€๋‹ค. ์‹œ์ค‘ ์€ํ–‰์˜ ์ฃผํƒ ๋‹ด๋ณด ๋Œ€์ถœ ๊ธˆ๋ฆฌ๊ฐ€ ๋‚ฎ์•„์ง€๋ฉด์„œ "
"์‹ค์ˆ˜์š”์ž์˜ ๊ตฌ๋งค ์‹ฌ๋ฆฌ๊ฐ€ ๋˜์‚ด์•„๋‚ฌ๊ธฐ ๋•Œ๋ฌธ์ด๋ผ๋Š” ๋ถ„์„์ด ๋‚˜์˜จ๋‹ค."
)
LLM_STEPS = {"proofread", "basic_correction", "context_correction", "style_correction"}
def diff_html(src: str, dst: str) -> tuple[str, int]:
"""์–ด์ ˆ ๋‹จ์œ„ diff ๋ฅผ HTML ๋กœ. (๋งˆํฌ์—…, ๋ฐ”๋€ ์„ธ๊ทธ๋จผํŠธ ์ˆ˜) ๋ฐ˜ํ™˜."""
a, b = src.split(), dst.split()
sm = difflib.SequenceMatcher(a=a, b=b, autojunk=False)
parts: list[str] = []
n_changed = 0
for op, i1, i2, j1, j2 in sm.get_opcodes():
before, after = html.escape(" ".join(a[i1:i2])), html.escape(" ".join(b[j1:j2]))
if op == "equal":
parts.append(after)
continue
n_changed += 1
if before:
parts.append(
f'<span style="background:rgba(230,80,80,.18);'
f'text-decoration:line-through">{before}</span>'
)
if after:
parts.append(f'<span style="background:rgba(60,170,90,.22)">{after}</span>')
return " ".join(parts), n_changed
@st.cache_resource
def get_pipeline():
return runner.build_pipeline()
st.set_page_config(page_title="์กฐ์„ ์ผ๋ณด ๋ณธ๋ฌธ ๊ต์—ด ๋ฐ๋ชจ", page_icon="๐Ÿ“ฐ", layout="wide")
st.title("๐Ÿ“ฐ ์กฐ์„ ์ผ๋ณด ๋ณธ๋ฌธ ๊ต์—ด ๋ฐ๋ชจ")
st.caption(
"pipeline_dev_v34 (FT ๊ต์—ด + Solar 3-์Šคํ… + ๊ฒฐ์ •๋ก  ๊ทœ์น™: ์–ดํœ˜ ์น˜ํ™˜ ยท ๋ณตํ•ฉ์–ด "
"ํ™”์ดํŠธ๋ฆฌ์ŠคํŠธ ๋ณต์› ยท ์œจ/๋ฅ  ๋ฐ›์นจ ๊ทœ์น™) ร— prompt_dev_260825_combo โ€” "
"temperature 0.0 ยท reasoning off. ํ‰๊ฐ€ ํ•˜๋„ค์Šค์™€ ๊ฐ™์€ ์—”์ง„์„ ๊ทธ๋Œ€๋กœ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค."
)
with st.sidebar:
st.subheader("์„ค์ •")
if not os.environ.get("UPSTAGE_API_KEY"):
key = st.text_input(
"UPSTAGE_API_KEY",
type="password",
help="HF Space ์—์„œ๋Š” Settings โ†’ Secrets ๋กœ ์„ค์ •ํ•˜์„ธ์š”.",
)
if key:
os.environ["UPSTAGE_API_KEY"] = key
model = st.selectbox("๋ชจ๋ธ (step1โ€“3 ์ฃผ์ž…)", ["solar-pro4", "solar-pro2"], index=0)
st.caption("FT ๊ต์—ด ์Šคํ…์€ ํŒŒ์ดํ”„๋ผ์ธ์— ๊ณ ์ •๋œ ft:solar-news-correction ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.")
text = st.text_area("๊ธฐ์‚ฌ ๋ณธ๋ฌธ", value=SAMPLE_TEXT, height=180, max_chars=4000)
if st.button("๊ต์—ด ์‹คํ–‰", type="primary", disabled=not text.strip()):
if not os.environ.get("UPSTAGE_API_KEY"):
st.error(
"UPSTAGE_API_KEY ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค โ€” ์‚ฌ์ด๋“œ๋ฐ”์— ์ž…๋ ฅํ•˜๊ฑฐ๋‚˜ Space Secrets ์— ์„ค์ •ํ•˜์„ธ์š”."
)
st.stop()
with st.spinner("๊ต์—ด ์ค‘์ž…๋‹ˆ๋‹คโ€ฆ (ํŒŒ์ดํ”„๋ผ์ธ ์ตœ์•… ์‹ค์ธก 37.5์ดˆ, ๋ณดํ†ต 10์ดˆ ๋‚ด)"):
try:
result = runner.run_proofread(get_pipeline(), text.strip(), model=model)
except Exception as e: # noqa: BLE001 โ€” ๋ฐ๋ชจ ํ‘œ๋ฉด์—์„œ๋Š” ์›์ธ ์š”์•ฝ๋งŒ ๋ณด์—ฌ์ค€๋‹ค
st.error(f"์‹คํ–‰ ์‹คํŒจ: {type(e).__name__}: {e}")
st.stop()
markup, n_changed = diff_html(text.strip(), result["output"])
usage = result.get("usage") or {}
c1, c2, c3 = st.columns(3)
c1.metric("์†Œ์š” ์‹œ๊ฐ„", f"{result['elapsed_s']:.1f}s")
c2.metric("๋ฐ”๋€ ๊ณณ", f"{n_changed}๊ณณ")
c3.metric("ํ† ํฐ (์ž…๋ ฅ+์ถœ๋ ฅ)", f"{usage.get('total_tokens', 0):,}")
st.subheader("๊ต์ • ๊ฒฐ๊ณผ (์›๋ฌธ ๋Œ€๋น„ ๋ณ€๊ฒฝ ํ‘œ์‹œ)")
st.markdown(
f'<div style="line-height:1.9;font-size:1.02rem">{markup}</div>',
unsafe_allow_html=True,
)
with st.expander("๊ต์ • ์ „๋ฌธ (๋ณต์‚ฌ์šฉ)"):
st.code(result["output"], language=None)
with st.expander("ํŒŒ์ดํ”„๋ผ์ธ ์Šคํ…๋ณ„ ์ถœ๋ ฅ"):
prev = text.strip()
for name, snapshot in result["step_outputs"].items():
kind = "LLM" if name in LLM_STEPS else "rule"
changed = snapshot != prev
st.markdown(f"**{name}** ยท {kind} ยท {'๋ณ€๊ฒฝํ•จ' if changed else '๋ณ€๊ฒฝ ์—†์Œ'}")
if changed:
step_markup, _ = diff_html(prev, snapshot)
st.markdown(
f'<div style="line-height:1.8;font-size:.92rem;opacity:.9">{step_markup}</div>',
unsafe_allow_html=True,
)
prev = snapshot
st.divider()