"""조선일보 본문 교열 데모 (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'{before}' ) if after: parts.append(f'{after}') 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'