proofread-sp4-demo / runner.py
dev-strender's picture
deploy: v35 document-level chunking (DP split + parallel chunks), progress view, Supabase logbook
8debb9c verified
Raw
History Blame Contribute Delete
4.44 kB
"""v35 νŒŒμ΄ν”„λΌμΈ(v34 + λ¬Έμ„œ λ‹¨μœ„ chunk)을 ν…μŠ€νŠΈ 1건에 μ‹€ν–‰ν•˜λŠ” ν”„λ‘œκ·Έλž˜λ§€ν‹± λŸ¬λ„ˆ.
ꡐ열 λ‘œμ§μ„ μž¬κ΅¬ν˜„ν•˜μ§€ μ•Šκ³  solar-eval 엔진을 κ·ΈλŒ€λ‘œ import ν•œλ‹€ β€” 데λͺ¨κ°€
λ³΄μ—¬μ£ΌλŠ” λ™μž‘μ΄ 평가 run κ³Ό λ°”μ΄νŠΈ λ‹¨μœ„λ‘œ κ°™μ•„μ•Ό ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€ (사본 λ‘œμ§μ€
λ°˜λ“œμ‹œ κ°ˆλΌμ§„λ‹€). λ‘œμ»¬μ—μ„œλŠ” uv μ›Œν¬μŠ€νŽ˜μ΄μŠ€μ˜ solar_eval 을, HF Space μ—μ„œλŠ”
`build_space.py` κ°€ λ²€λ”λ§ν•œ 사본을 μ“΄λ‹€ (sys.path 상 슀크립트 μ˜†μ΄ 먼저라
Space μ—μ„œλŠ” 벀더 사본이 이긴닀).
config μžμ‚°(νŒŒμ΄ν”„λΌμΈ yamlΒ·ν”„λ‘¬ν”„νŠΈΒ·μΉ˜ν™˜ μ‚¬μ „Β·ν™”μ΄νŠΈλ¦¬μŠ€νŠΈ)은
1. `./assets/` β€” HF Space λ ˆμ΄μ•„μ›ƒ (build_space.py κ°€ 쑰립)
2. `../03-evaluation/` β€” 레포 λ ˆμ΄μ•„μ›ƒ (둜컬 개발)
μˆœμ„œλ‘œ μ°ΎλŠ”λ‹€.
"""
from __future__ import annotations
import asyncio
import time
from collections.abc import Callable
from pathlib import Path
from typing import Any
import yaml
PIPELINE_NAME = (
"pipeline_dev_v35" # = v34 + document_chunking (extends). 두 yaml 이 λͺ¨λ‘ μžˆμ–΄μ•Ό ν•œλ‹€
)
PROMPT_NAME = "prompt_dev_260825_combo"
DEFAULT_MODEL = "solar-pro4"
# μ—”μ§„μ˜ StepCallback κ³Ό 같은 λͺ¨μ–‘: (event, index, total, step_name, doc_chunk)
StepCallback = Callable[[str, int, int, str, tuple[int, int] | None], None]
def find_config_dir() -> Path:
here = Path(__file__).resolve().parent
for cand in (here / "assets", here.parent / "03-evaluation"):
if (cand / "pipelines" / f"{PIPELINE_NAME}.yaml").is_file():
return cand
raise FileNotFoundError(
f"config μžμ‚°μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€ β€” {here}/assets λ˜λŠ” ../03-evaluation 에 "
f"pipelines/{PIPELINE_NAME}.yaml 이 μžˆμ–΄μ•Ό ν•©λ‹ˆλ‹€ (build_space.py μ°Έμ‘°)."
)
def build_pipeline() -> Any:
"""v35 νŒŒμ΄ν”„λΌμΈ μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“ λ‹€. μ•±μ—μ„œ 1회 λ§Œλ“€μ–΄ μž¬μ‚¬μš©ν•œλ‹€."""
import solar_eval.pipelines.steps # noqa: F401 β€” STEP_REGISTRY 등둝
from solar_eval.core.dataset_loader import DatasetLoader
from solar_eval.core.pipeline_compose import compose_pipeline
from solar_eval.models.prompt_version import load_step_prompts
from solar_eval.pipelines.registry import create_pipeline
config_dir = find_config_dir()
pipelines_dir = config_dir / "pipelines"
def load_base(name: str) -> dict[str, Any]:
return yaml.safe_load((pipelines_dir / f"{name}.yaml").read_text())
raw = load_base(PIPELINE_NAME)
composed = compose_pipeline(raw, load_base=load_base)
prompts = load_step_prompts(config_dir / "prompts" / PROMPT_NAME)
return create_pipeline(
"multi_step",
input_fields=["original"],
pipeline_config=composed,
prompts=prompts,
dataset_loader=DatasetLoader(),
config_dir=config_dir,
)
def step_names(pipeline: Any) -> list[str]:
"""UI κ°€ μ§„ν–‰ ν‘œμ‹œλ₯Ό 미리 그릴 수 있게 μŠ€ν… 이름을 μˆœμ„œλŒ€λ‘œ."""
return [step.name for step in pipeline.steps]
async def _run_async(
pipeline: Any, text: str, model: str, on_step: StepCallback | None
) -> dict[str, Any]:
from solar_eval.models.sample import EvalSample
from solar_eval.providers.upstage import UpstageProvider
sample = EvalSample(input={"original": text})
start = time.monotonic()
# 평가 run κ³Ό 같은 쑰건: temp 0.0, reasoning off (SP4 κ΅μ²΄μ•ˆ μ œμ•½)
result = await pipeline.run(
sample,
prompts="",
provider=UpstageProvider(),
model=model,
temperature=0.0,
max_tokens=8000,
reasoning_effort=None,
on_step=on_step,
)
elapsed = time.monotonic() - start
return {
"output": result.output,
"step_outputs": result.artifacts.get("step_outputs", {}),
"usage": result.artifacts.get("usage", {}),
"elapsed_s": elapsed,
"pipeline_key": PIPELINE_NAME,
"prompt_key": PROMPT_NAME,
"model": model,
}
def run_proofread(
pipeline: Any,
text: str,
model: str = DEFAULT_MODEL,
on_step: StepCallback | None = None,
) -> dict[str, Any]:
"""ν…μŠ€νŠΈ 1건 ꡐ열. {output, step_outputs, usage, elapsed_s, pipeline_key, prompt_key, model}.
`on_step` 은 μŠ€ν… μ‹œμž‘/μ’…λ£Œμ™€ LLM μŠ€ν…μ˜ bulk μ§„ν–‰λ§ˆλ‹€ λΆˆλ¦°λ‹€ β€” UI μ§„ν–‰ ν‘œμ‹œμš©.
"""
return asyncio.run(_run_async(pipeline, text, model, on_step))