Spaces:
Running
Running
feat: switch demo to FT + Opus article review (pipeline_exp_261021_ft_hybrid)
Browse filesThis view is limited to 50 files because it contains too many changes. ย See raw diff
- README.md +7 -12
- __pycache__/logbook.cpython-312.pyc +0 -0
- __pycache__/runner.cpython-312.pyc +0 -0
- app.py +52 -94
- assets/pipelines/pipeline_dev_v40.yaml +0 -71
- assets/pipelines/pipeline_dev_v44.yaml +0 -6
- assets/pipelines/pipeline_exp_261021_ft_hybrid.yaml +66 -0
- assets/prompts/prompt_dev_260917_vsplit/step1.txt +0 -85
- assets/prompts/prompt_dev_260917_vsplit/step2a.txt +0 -36
- assets/prompts/prompt_dev_260917_vsplit/step2b.txt +0 -33
- assets/prompts/prompt_exp_261021_ft_hybrid/ft_system.txt +1 -0
- assets/prompts/prompt_exp_261021_ft_hybrid/review.txt +41 -0
- assets/prompts/prompt_exp_261021_ft_hybrid/review_sp4.txt +38 -0
- assets/resources/compound_whitelist.txt +0 -3136
- assets/resources/{vocabulary_v2.csv โ vocabulary.csv} +1 -71
- runner.py +64 -76
- solar_eval/__pycache__/__init__.cpython-312.pyc +0 -0
- solar_eval/__pycache__/_version.cpython-312.pyc +0 -0
- solar_eval/__pycache__/config.cpython-312.pyc +0 -0
- solar_eval/cli/commands/compare.py +160 -57
- solar_eval/cli/commands/failure_keys.py +143 -0
- solar_eval/cli/commands/run_eval.py +340 -0
- solar_eval/cli/commands/run_meta.py +538 -0
- solar_eval/cli/commands/run_progress.py +230 -0
- solar_eval/cli/commands/run_resume.py +298 -0
- solar_eval/cli/commands/runs.py +228 -775
- solar_eval/cli/commands/score.py +187 -0
- solar_eval/cli/commands/sync.py +5 -4
- solar_eval/cli/config.py +143 -8
- solar_eval/cli/formatters.py +88 -44
- solar_eval/cli/main.py +3 -0
- solar_eval/config.py +3 -0
- solar_eval/core/__pycache__/__init__.cpython-312.pyc +0 -0
- solar_eval/core/__pycache__/dataset_loader.cpython-312.pyc +0 -0
- solar_eval/core/__pycache__/pipeline_compose.cpython-312.pyc +0 -0
- solar_eval/core/dataset_loader.py +51 -10
- solar_eval/core/dataset_prep.py +169 -0
- solar_eval/core/fingerprint.py +19 -7
- solar_eval/core/pipeline_factory.py +54 -0
- solar_eval/core/row_filter.py +288 -0
- solar_eval/core/run_records.py +205 -0
- solar_eval/core/runner.py +301 -309
- solar_eval/core/snapshot/__init__.py +400 -0
- solar_eval/core/snapshot/dataset_block.py +179 -0
- solar_eval/core/snapshot/environment.py +210 -0
- solar_eval/core/snapshot/impl_hash.py +365 -0
- solar_eval/core/snapshot/reproducibility.py +149 -0
- solar_eval/core/snapshot/resources.py +78 -0
- solar_eval/core/task_discovery.py +1 -1
- solar_eval/evaluators/base.py +19 -1
README.md
CHANGED
|
@@ -10,19 +10,14 @@ pinned: false
|
|
| 10 |
|
| 11 |
# ์กฐ์ ์ผ๋ณด ๋ณธ๋ฌธ ๊ต์ด ๋ฐ๋ชจ
|
| 12 |
|
| 13 |
-
|
| 14 |
-
์คํํฉ๋๋ค โ ๋ฐ๋ชจ๊ฐ ๋ณด์ฌ์ฃผ๋ ๊ฒฐ๊ณผ๋ ํ๊ฐ run ๊ณผ ๊ฐ์ ๊ฒฝ๋ก์์ ๋์ต๋๋ค.
|
| 15 |
|
| 16 |
```
|
| 17 |
-
[๊ท์น] ์ฌ์ ์นํ
|
| 18 |
-
[LLM] FT ๊ต์ด (ft:solar-news-correction)
|
| 19 |
-
[LLM]
|
| 20 |
-
[
|
| 21 |
-
[LLM] ๋ฌธ๋งฅ ๊ต์ ยท ๋ฌธ๋ฒ
|
| 22 |
-
[๊ท์น] ์ฌ์ ์ฌ์ ์ฉ ยท ๋ณตํฉ์ด ๋ณต์ ยท ์จ/๋ฅ ๊ท์น ยท ํ์ฒ๋ฆฌ ยท ์กฐ์ฌ ๊ต์ ยท ์คํ ๋๋๋ฆผ
|
| 23 |
```
|
| 24 |
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
**์คํ ์ ํ์**: Space Settings โ Secrets ์ `UPSTAGE_API_KEY` ๋ฅผ ๋ฃ์ผ์ธ์
|
| 28 |
-
(์์ผ๋ฉด ์ฌ์ด๋๋ฐ ์
๋ ฅ์ ์ฌ์ฉํฉ๋๋ค).
|
|
|
|
| 10 |
|
| 11 |
# ์กฐ์ ์ผ๋ณด ๋ณธ๋ฌธ ๊ต์ด ๋ฐ๋ชจ
|
| 12 |
|
| 13 |
+
FT ๊ต์ด + Opus ๊ธฐ์ฌ ๊ฒํ ๊ตฌ์ฑ(`pipeline_exp_261021_ft_hybrid`) ๋ฐ๋ชจ์
๋๋ค. ํ๊ฐ ํ๋ค์ค์ ๊ฐ์ ์์ง ์ฝ๋๋ฅผ ์คํํฉ๋๋ค.
|
|
|
|
| 14 |
|
| 15 |
```
|
| 16 |
+
[๊ท์น] ์ฉ์ด ์ฌ์ ์นํ
|
| 17 |
+
[LLM] FT ๊ต์ด (ft:solar-news-correction, ๋ฌธ๋จ ๋ฌถ์๋ณ ๋์ ํธ์ถ)
|
| 18 |
+
[LLM] ๊ธฐ์ฌ ์ ์ฒด ๊ฒํ 1ํ (Claude Opus): FT ์ค๊ต์ ๋๋๋ฆผ + ์ฌ์คยท์ซ์ยท๋ฌธ๋งฅ ๊ต์ ๋ชฉ๋ก
|
| 19 |
+
[๊ท์น] ๋๋๋ฆผยท๊ต์ ์ ์ฉ ยท ๋ฐ์ดํ ๋ฐฉํฅ ยท ์๋ฌธ ๋ฐ์ดํ ๋ณด์กด ยท ์จ/๋ฅ ยท ํ์ฒ๋ฆฌ
|
|
|
|
|
|
|
| 20 |
```
|
| 21 |
|
| 22 |
+
**์คํ ์ ํ์**: Space Settings โ Secrets ์ `UPSTAGE_API_KEY` ์ `TIMELY_API_KEY` ๋ฅผ ๋ฃ์ผ์ธ์
|
| 23 |
+
(์์ผ๋ฉด ์ฌ์ด๋๋ฐ ์
๋ ฅ์ ์ฌ์ฉํฉ๋๋ค). ๊ธฐ์ฌ ๊ฒํ ๋จ๊ณ๋ Timely ๋ผ์ฐํฐ๋ฅผ ๊ฑฐ์ณ Claude Opus ๋ฅผ ๋ถ๋ฆ
๋๋ค.
|
|
|
|
|
|
__pycache__/logbook.cpython-312.pyc
ADDED
|
Binary file (4.65 kB). View file
|
|
|
__pycache__/runner.cpython-312.pyc
ADDED
|
Binary file (7.71 kB). View file
|
|
|
app.py
CHANGED
|
@@ -1,12 +1,9 @@
|
|
| 1 |
"""์กฐ์ ์ผ๋ณด ๋ณธ๋ฌธ ๊ต์ด ๋ฐ๋ชจ (Streamlit / HF Space).
|
| 2 |
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
์ ๋ถ runner.py ์ด๊ณ ์ด ํ์ผ์ UI ๋ง ๋ด๋นํ๋ค. ์ฌ์ฉ์ ์
๋ ฅ๊ณผ ๊ฒฐ๊ณผ๋ logbook.py ๊ฐ
|
| 6 |
-
Supabase ์ ๋จ๊ธด๋ค
|
| 7 |
-
|
| 8 |
-
๋ชจ๋ธ ์
๋ ํฐ๋ ๋์ง ์๋๋ค โ ํ์ดํ๋ผ์ธยทํ๋กฌํํธยท๋ชจ๋ธ์ ํ ๋ฌถ์์ด๊ณ , ๋ชจ๋ธ๋ง
|
| 9 |
-
๊ฐ์๋ผ์ฐ๋ฉด ๋ค๋ฅธ ๊ตฌ์ฑ์ด ๋๋ค (runner.CONFIGS ์ฃผ์ ์ฐธ์กฐ).
|
| 10 |
"""
|
| 11 |
|
| 12 |
from __future__ import annotations
|
|
@@ -24,33 +21,29 @@ PANE_STYLE = (
|
|
| 24 |
"padding:14px 16px;line-height:1.9;font-size:1.02rem;min-height:120px"
|
| 25 |
)
|
| 26 |
|
| 27 |
-
# ํฉ์ฑ ์๋ฌธ
|
| 28 |
-
# ๋น๋ฅ /๊ฐฑ์ ๋ฅ /์ฐ์ฒด๋ฅ (์ฌ์ ยท์จ๋ฅ ๊ท์น) ยท
|
| 29 |
SAMPLE_TEXT = (
|
| 30 |
"โถ์ ๋ถ์ ๊ฐ๋ ฅํ ๋ถ๋์ฐ ๊ท์ ์๋ ๋ถ๊ตฌํ๊ณ ์๋๊ถ ์ํํธ ๋งค๋งค ๊ฑฐ๋ ๋น๋ฅ ์ "
|
| 31 |
"์คํ๋ ค ์ฆ๊ฐ์ธ๋ฅผ ๋ณด์๋ค. ์์ค ์ํ์ ์ฃผํ ๋ด๋ณด ๋์ถ ๊ธ๋ฆฌ๊ฐ ๋ฎ์์ง๋ฉด์ "
|
| 32 |
"์ค์์์์ ๊ตฌ๋งค ์ฌ๋ฆฌ๊ฐ ๋์ด์๋ฌ๊ธฐ ๋๋ฌธ์ด๋ผ๋ ๋ถ์์ด ๋์จ๋ค. "
|
| 33 |
"๊ตญํ ๋ถ ๊ด๊ณ์๋ โ๋์ถ ๊ฐฑ์ ๋ฅ ๊ณผ ์ฐ์ฒด๋ฅ ์ ํจ๊ป ๋ด์ผ ํ๋คโ๋ฉฐ โ๋น๋ถ๊ฐ ์ด๋ฐ "
|
| 34 |
-
"ํ๋ฆ์ด ์ด์ด์ง ๊ฒโ์ด๋ผ๊ณ ๋งํ๋ค.
|
| 35 |
-
"
|
| 36 |
)
|
| 37 |
|
| 38 |
-
# ์คํ
์ด๋ฆ โ ์ฌ์ฉ์์๊ฒ ๋ณด์ฌ์ค ๋ผ๋ฒจ
|
| 39 |
-
# basic_correction ์ ๋ผ๋ฒจ์ด ๊ตฌ์ฑ๋ง๋ค ๋ค๋ฅด๋ค โ v44 ์์๋ ์์ฑ์ด ์๋๋ผ FT ๊ฒฐ๊ณผ ํ์ ์ด๋ค.
|
| 40 |
STEP_LABELS = {
|
| 41 |
"vocabulary_sub_pre": "์ฉ์ด ์ฌ์ ์นํ",
|
| 42 |
-
"proofread": "1์ฐจ ๊ต์ด (๊ต์ด ์ ์ฉ FT ๋ชจ๋ธ)",
|
| 43 |
-
"
|
| 44 |
-
"
|
| 45 |
-
"
|
| 46 |
-
"
|
| 47 |
-
"
|
| 48 |
-
"
|
| 49 |
-
"compound_whitelist_restore": "๋ณตํฉ์ด ํ์ดํธ๋ฆฌ์คํธ ๋ณต์",
|
| 50 |
"yul_ryul_normalize": "์จ/๋ฅ ๊ท์น ์ ์ฉ",
|
| 51 |
"post_process": "ํ์ฒ๋ฆฌ (๋จ์ยทํตํยท์ผํ)",
|
| 52 |
-
"josa_fix": "์กฐ์ฌ ๊ต์ (๊ธฐํธยท์ซ์ยท๋จ์ ๋ค)",
|
| 53 |
-
"diff_revert": "์คํ ๋๋๋ฆผ (๊ธ๋ฆฌํยท๊ตฌ์กฐ๋ฌธ์ยท์ผํ)",
|
| 54 |
}
|
| 55 |
|
| 56 |
|
|
@@ -76,64 +69,32 @@ def diff_panes(src: str, dst: str) -> tuple[str, str, int]:
|
|
| 76 |
|
| 77 |
|
| 78 |
class ProgressView:
|
| 79 |
-
"""์คํ
์งํ์ st.status ์์ ๊ทธ๋ฆฐ๋ค
|
| 80 |
-
|
| 81 |
-
๋ฌธ์ ๋จ์ chunk ๋ชจ๋์์๋ chunk ๋ค์ด **๋ณ๋ ฌ**๋ก ๋์ ์ด๋ฒคํธ๊ฐ ์์ฌ ๋ค์ด์ค๋ฏ๋ก, chunk ๋ง๋ค ์ค์ ํ๋์ฉ
|
| 82 |
-
๋๊ณ (๋ช ๋จ๊ณ๊น์ง ์๋) ์ ์ฒด ์งํ ๋ฐ๋ (๋๋ ๋จ๊ณ ์) / (chunk ์ ร ๋จ๊ณ ์) ๋ก ์ก๋๋ค.
|
| 83 |
-
"""
|
| 84 |
|
| 85 |
def __init__(self, names: list[str]) -> None:
|
| 86 |
self.names = names
|
| 87 |
-
self.
|
| 88 |
-
self.n_chunks = 1
|
| 89 |
-
self.done: dict[int, int] = {1: 0} # chunk ๋ฒํธ โ ๋๋ ๋จ๊ณ ์
|
| 90 |
-
self.current: dict[int, str] = {} # chunk ๋ฒํธ ๏ฟฝ๏ฟฝ ์งํ ์ค ๋จ๊ณ ๋ผ๋ฒจ
|
| 91 |
self.status = st.status("๊ต์ด ์ค์
๋๋คโฆ", expanded=True)
|
| 92 |
-
self.bar = self.status.progress(0.0, text=f"0 / {
|
| 93 |
self.lines = self.status.empty()
|
| 94 |
self._render()
|
| 95 |
|
| 96 |
-
def on_step(self, event: str, index: int, total: int, name: str
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
self.done.setdefault(ci, 0)
|
| 101 |
-
label = STEP_LABELS.get(name, name)
|
| 102 |
-
if event == "start":
|
| 103 |
-
self.current[ci] = label
|
| 104 |
-
elif event == "chunk":
|
| 105 |
-
self.current[ci] = f"{label} ยท ์กฐ๊ฐ {index}/{total}"
|
| 106 |
-
elif event == "done":
|
| 107 |
-
self.done[ci] = index
|
| 108 |
-
self.current.pop(ci, None)
|
| 109 |
-
finished = sum(self.done.values())
|
| 110 |
-
grand = self.n_chunks * self.total_steps
|
| 111 |
-
unit = "๋จ๊ณ" if self.n_chunks == 1 else f"๋จ๊ณ (๋ฌธ์ ์กฐ๊ฐ {self.n_chunks}๊ฐ ๋ณ๋ ฌ)"
|
| 112 |
-
self.bar.progress(min(finished / grand, 1.0), text=f"{finished} / {grand} {unit}")
|
| 113 |
self._render()
|
| 114 |
|
| 115 |
def _render(self) -> None:
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
else:
|
| 126 |
-
rows.append(f"โซ๏ธ {label}")
|
| 127 |
-
else:
|
| 128 |
-
rows = []
|
| 129 |
-
for ci in range(1, self.n_chunks + 1):
|
| 130 |
-
done = self.done.get(ci, 0)
|
| 131 |
-
if done >= self.total_steps:
|
| 132 |
-
rows.append(f"โ
์กฐ๊ฐ {ci}: ์๋ฃ")
|
| 133 |
-
elif ci in self.current:
|
| 134 |
-
rows.append(f"โณ ์กฐ๊ฐ {ci}: **{self.current[ci]}** ({done}/{self.total_steps})")
|
| 135 |
-
else:
|
| 136 |
-
rows.append(f"โซ๏ธ ์กฐ๊ฐ {ci}: ๋๊ธฐ")
|
| 137 |
self.lines.markdown(" \n".join(rows))
|
| 138 |
|
| 139 |
def finish(self, elapsed_s: float) -> None:
|
|
@@ -144,49 +105,47 @@ class ProgressView:
|
|
| 144 |
|
| 145 |
|
| 146 |
@st.cache_resource(show_spinner="ํ์ดํ๋ผ์ธ ์ค๋น ์คโฆ")
|
| 147 |
-
def get_pipeline(
|
| 148 |
-
|
| 149 |
-
return runner.build_pipeline(label)
|
| 150 |
|
| 151 |
|
| 152 |
st.set_page_config(page_title="์กฐ์ ์ผ๋ณด ๋ณธ๋ฌธ ๊ต์ด ๋ฐ๋ชจ", page_icon="๐ฐ", layout="wide")
|
| 153 |
st.title("๐ฐ ์กฐ์ ์ผ๋ณด ๋ณธ๋ฌธ ๊ต์ด ๋ฐ๋ชจ")
|
| 154 |
-
st.caption("
|
| 155 |
|
| 156 |
with st.sidebar:
|
| 157 |
st.subheader("์ค์ ")
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
label = runner.DEFAULT_LABEL
|
| 167 |
-
cfg = runner.BY_LABEL[label]
|
| 168 |
st.markdown(f"**{cfg.label}**")
|
| 169 |
st.caption(cfg.note)
|
| 170 |
-
st.markdown(f"-
|
| 171 |
|
| 172 |
-
text = st.text_area("๊ธฐ์ฌ ๋ณธ๋ฌธ", value=SAMPLE_TEXT, height=320, max_chars=
|
| 173 |
|
| 174 |
if st.button("๊ต์ด ์คํ", type="primary", disabled=not text.strip()):
|
| 175 |
-
if not os.environ.get(
|
|
|
|
| 176 |
st.error(
|
| 177 |
-
"
|
| 178 |
)
|
| 179 |
st.stop()
|
| 180 |
|
| 181 |
source = text.strip()
|
| 182 |
-
pipeline = get_pipeline(
|
| 183 |
-
# ์
๋ ฅ์ ์คํ ์ ์ ๋จ๊ธด๋ค
|
| 184 |
article_id = logbook.save_article(source)
|
| 185 |
|
| 186 |
progress = ProgressView(runner.step_names(pipeline))
|
| 187 |
try:
|
| 188 |
-
result = runner.run_proofread(pipeline, source,
|
| 189 |
-
except Exception as e: # noqa: BLE001
|
| 190 |
progress.fail()
|
| 191 |
st.error(f"์คํ ์คํจ: {type(e).__name__}: {e}")
|
| 192 |
st.stop()
|
|
@@ -204,7 +163,6 @@ if st.button("๊ต์ด ์คํ", type="primary", disabled=not text.strip()):
|
|
| 204 |
st.caption(f"๊ธฐ๋ก ์คํจ (๊ฒฐ๊ณผ์๋ ์ํฅ ์์): {logbook.last_error()}")
|
| 205 |
|
| 206 |
left, right, _n_changed = diff_panes(source, result["output"])
|
| 207 |
-
|
| 208 |
c1, c2 = st.columns(2, gap="medium")
|
| 209 |
with c1:
|
| 210 |
st.subheader("์๋ฌธ")
|
|
|
|
| 1 |
"""์กฐ์ ์ผ๋ณด ๋ณธ๋ฌธ ๊ต์ด ๋ฐ๋ชจ (Streamlit / HF Space).
|
| 2 |
|
| 3 |
+
FT ๊ต์ด + Opus ๊ธฐ์ฌ ๊ฒํ ๊ตฌ์ฑ์ solar-eval ์์ง ๊ทธ๋๋ก ์คํํ๋ค - FT ๊ต์ด ๋ชจ๋ธ์ด ๋ฌธ๋จ ๋ฌถ์๋ง๋ค 1์ฐจ ๊ต์ดํ๊ณ ,
|
| 4 |
+
Claude Opus ๊ฐ ๊ธฐ์ฌ ์ ์ฒด๋ฅผ ํ ๋ฒ ๋ณด๊ณ FT ์ค๊ต์ ์ ๋๋๋ฆฌ๋ฉฐ ์ฌ์คยท์ซ์ยท๋ฌธ๋งฅ ์ค๋ฅ๋ฅผ ๊ณ ์น ๋ค ๊ฒฐ์ ๋ก ๊ท์น์ ๊ฑด๋ค.
|
| 5 |
+
๊ตฌ์ฑ ์ ์์ ์คํ์ ์ ๋ถ runner.py ์ด๊ณ ์ด ํ์ผ์ UI ๋ง ๋ด๋นํ๋ค. ์ฌ์ฉ์ ์
๋ ฅ๊ณผ ๊ฒฐ๊ณผ๋ logbook.py ๊ฐ
|
| 6 |
+
Supabase ์ ๋จ๊ธด๋ค(์ํฌ๋ฆฟ์ด ์์ผ๋ฉด ์กฐ์ฉํ ๊ฑด๋๋).
|
|
|
|
|
|
|
|
|
|
| 7 |
"""
|
| 8 |
|
| 9 |
from __future__ import annotations
|
|
|
|
| 21 |
"padding:14px 16px;line-height:1.9;font-size:1.02rem;min-height:120px"
|
| 22 |
)
|
| 23 |
|
| 24 |
+
# ํฉ์ฑ ์๋ฌธ - ํ๊ฐ์
gold ๋ ์ฐ์ง ์๋๋ค(์ ์ถ ๋ฐฉ์ง). ๊ฐ ์ธต์ ํ์ ์ ์ผ๋ถ๋ฌ ์ฌ์ด ๋ ๋ฌธ์ฅ์ด๋ค:
|
| 25 |
+
# ๋น๋ฅ /๊ฐฑ์ ๋ฅ /์ฐ์ฒด๋ฅ (์ฌ์ ยท์จ๋ฅ ๊ท์น) ยท ์๋ฌธํ ์ด๋ฏธ ยท 160ใ์ผ๋ก(์กฐ์ฌ) ยท ๊ณง์ ๋ฐ์ดํ(์๋ฌธ ๋ฐ์ดํ ๋ณด์กด).
|
| 26 |
SAMPLE_TEXT = (
|
| 27 |
"โถ์ ๋ถ์ ๊ฐ๋ ฅํ ๋ถ๋์ฐ ๊ท์ ์๋ ๋ถ๊ตฌํ๊ณ ์๋๊ถ ์ํํธ ๋งค๋งค ๊ฑฐ๋ ๋น๋ฅ ์ "
|
| 28 |
"์คํ๋ ค ์ฆ๊ฐ์ธ๋ฅผ ๋ณด์๋ค. ์์ค ์ํ์ ์ฃผํ ๋ด๋ณด ๋์ถ ๊ธ๋ฆฌ๊ฐ ๋ฎ์์ง๋ฉด์ "
|
| 29 |
"์ค์์์์ ๊ตฌ๋งค ์ฌ๋ฆฌ๊ฐ ๋์ด์๋ฌ๊ธฐ ๋๋ฌธ์ด๋ผ๋ ๋ถ์์ด ๋์จ๋ค. "
|
| 30 |
"๊ตญํ ๋ถ ๊ด๊ณ์๋ โ๋์ถ ๊ฐฑ์ ๋ฅ ๊ณผ ์ฐ์ฒด๋ฅ ์ ํจ๊ป ๋ด์ผ ํ๋คโ๋ฉฐ โ๋น๋ถ๊ฐ ์ด๋ฐ "
|
| 31 |
+
"ํ๋ฆ์ด ์ด์ด์ง ๊ฒโ์ด๋ผ๊ณ ๋งํ๋ค. ์
๊ณ์์ 'K-์ฃผ๊ฑฐ' ๋ชจ๋ธ์ด ํด์ธ์์๋ ํตํ ์ง "
|
| 32 |
+
"์ฃผ๋ชฉํ๊ณ ์๋ค. ์ ๊ท ๊ด์ญ์ฒ ๋๋ ์ต๊ณ ์์ 160ใ์ผ๋ก ์ค๊ณ๋๋ค."
|
| 33 |
)
|
| 34 |
|
| 35 |
+
# ์คํ
์ด๋ฆ โ ์ฌ์ฉ์์๊ฒ ๋ณด์ฌ์ค ๋ผ๋ฒจ (๋ชจ๋ฅด๋ ์คํ
์ ์ด๋ฆ ๊ทธ๋๋ก)
|
|
|
|
| 36 |
STEP_LABELS = {
|
| 37 |
"vocabulary_sub_pre": "์ฉ์ด ์ฌ์ ์นํ",
|
| 38 |
+
"proofread": "1์ฐจ ๊ต์ด (๊ต์ด ์ ์ฉ FT ๋ชจ๋ธ, ๋ฌธ๋จ๋ณ)",
|
| 39 |
+
"ft_hunks": "FT ์์ ๋ชฉ๋ก ์ ๋ฆฌ",
|
| 40 |
+
"review": "๊ธฐ์ฌ ์ ์ฒด ๊ฒํ (Claude Opus 1ํ)",
|
| 41 |
+
"revert_ft": "FT ์ค๊ต์ ๋๋๋ฆผ",
|
| 42 |
+
"apply_review": "์ฌ์คยท์ซ์ยท๋ฌธ๋งฅ ๊ต์ ์ ์ฉ",
|
| 43 |
+
"quote_direction": "๋ฐ์ดํ ๋ฐฉํฅ ์ ๋ฆฌ",
|
| 44 |
+
"quote_preserve": "์๋ฌธ ๋ฐ์ดํ ๋ณด์กด",
|
|
|
|
| 45 |
"yul_ryul_normalize": "์จ/๋ฅ ๊ท์น ์ ์ฉ",
|
| 46 |
"post_process": "ํ์ฒ๋ฆฌ (๋จ์ยทํตํยท์ผํ)",
|
|
|
|
|
|
|
| 47 |
}
|
| 48 |
|
| 49 |
|
|
|
|
| 69 |
|
| 70 |
|
| 71 |
class ProgressView:
|
| 72 |
+
"""์คํ
์งํ์ st.status ์์ ๊ทธ๋ฆฐ๋ค - ๋จ๊ณ๋ณ ์ถ๋ ฅ์ ๋ณด์ฌ์ฃผ์ง ์๊ณ '์ด๋๊น์ง ์๋' ๋ง."""
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
|
| 74 |
def __init__(self, names: list[str]) -> None:
|
| 75 |
self.names = names
|
| 76 |
+
self.done = 0
|
|
|
|
|
|
|
|
|
|
| 77 |
self.status = st.status("๊ต์ด ์ค์
๋๋คโฆ", expanded=True)
|
| 78 |
+
self.bar = self.status.progress(0.0, text=f"0 / {len(names)} ๋จ๊ณ")
|
| 79 |
self.lines = self.status.empty()
|
| 80 |
self._render()
|
| 81 |
|
| 82 |
+
def on_step(self, event: str, index: int, total: int, name: str) -> None:
|
| 83 |
+
if event == "done":
|
| 84 |
+
self.done = index
|
| 85 |
+
self.bar.progress(min(self.done / total, 1.0), text=f"{self.done} / {total} ๋จ๊ณ")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
self._render()
|
| 87 |
|
| 88 |
def _render(self) -> None:
|
| 89 |
+
rows = []
|
| 90 |
+
for i, name in enumerate(self.names, 1):
|
| 91 |
+
label = STEP_LABELS.get(name, name)
|
| 92 |
+
if i <= self.done:
|
| 93 |
+
rows.append(f"โ
{label}")
|
| 94 |
+
elif i == self.done + 1:
|
| 95 |
+
rows.append(f"โณ **{label}**")
|
| 96 |
+
else:
|
| 97 |
+
rows.append(f"โซ๏ธ {label}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
self.lines.markdown(" \n".join(rows))
|
| 99 |
|
| 100 |
def finish(self, elapsed_s: float) -> None:
|
|
|
|
| 105 |
|
| 106 |
|
| 107 |
@st.cache_resource(show_spinner="ํ์ดํ๋ผ์ธ ์ค๋น ์คโฆ")
|
| 108 |
+
def get_pipeline():
|
| 109 |
+
return runner.build_pipeline()
|
|
|
|
| 110 |
|
| 111 |
|
| 112 |
st.set_page_config(page_title="์กฐ์ ์ผ๋ณด ๋ณธ๋ฌธ ๊ต์ด ๋ฐ๋ชจ", page_icon="๐ฐ", layout="wide")
|
| 113 |
st.title("๐ฐ ์กฐ์ ์ผ๋ณด ๋ณธ๋ฌธ ๊ต์ด ๋ฐ๋ชจ")
|
| 114 |
+
st.caption(f"{runner.CONFIG.label} ยท ํ๊ฐ ํ๋ค์ค์ ๊ฐ์ ์์ง ์ฝ๋")
|
| 115 |
|
| 116 |
with st.sidebar:
|
| 117 |
st.subheader("์ค์ ")
|
| 118 |
+
for env_key in runner.REQUIRED_KEYS:
|
| 119 |
+
if not os.environ.get(env_key):
|
| 120 |
+
value = st.text_input(
|
| 121 |
+
env_key, type="password", help="HF Space ์์๋ Settings โ Secrets ๋ก ์ค์ ํ์ธ์."
|
| 122 |
+
)
|
| 123 |
+
if value:
|
| 124 |
+
os.environ[env_key] = value
|
| 125 |
+
cfg = runner.CONFIG
|
|
|
|
|
|
|
| 126 |
st.markdown(f"**{cfg.label}**")
|
| 127 |
st.caption(cfg.note)
|
| 128 |
+
st.markdown(f"- ํ์ดํ๋ผ์ธ `{cfg.pipeline}`\n- ํ๋กฌํํธ `{cfg.prompt}`")
|
| 129 |
|
| 130 |
+
text = st.text_area("๊ธฐ์ฌ ๋ณธ๋ฌธ", value=SAMPLE_TEXT, height=320, max_chars=12000)
|
| 131 |
|
| 132 |
if st.button("๊ต์ด ์คํ", type="primary", disabled=not text.strip()):
|
| 133 |
+
missing = [k for k in runner.REQUIRED_KEYS if not os.environ.get(k)]
|
| 134 |
+
if missing:
|
| 135 |
st.error(
|
| 136 |
+
f"{', '.join(missing)} ๊ฐ ํ์ํฉ๋๋ค - ์ฌ์ด๋๋ฐ์ ์
๋ ฅํ๊ฑฐ๋ Space Secrets ์ ์ค์ ํ์ธ์."
|
| 137 |
)
|
| 138 |
st.stop()
|
| 139 |
|
| 140 |
source = text.strip()
|
| 141 |
+
pipeline = get_pipeline()
|
| 142 |
+
# ์
๋ ฅ์ ์คํ ์ ์ ๋จ๊ธด๋ค - ์คํ์ด ์คํจํด๋ ๋ฌด์์ด ๋ค์ด์๋์ง๋ ์ ์ ์๊ฒ
|
| 143 |
article_id = logbook.save_article(source)
|
| 144 |
|
| 145 |
progress = ProgressView(runner.step_names(pipeline))
|
| 146 |
try:
|
| 147 |
+
result = runner.run_proofread(pipeline, source, on_step=progress.on_step)
|
| 148 |
+
except Exception as e: # noqa: BLE001 - ๋ฐ๋ชจ ํ๋ฉด์์๋ ์์ธ ์์ฝ๋ง ๋ณด์ฌ์ค๋ค
|
| 149 |
progress.fail()
|
| 150 |
st.error(f"์คํ ์คํจ: {type(e).__name__}: {e}")
|
| 151 |
st.stop()
|
|
|
|
| 163 |
st.caption(f"๊ธฐ๋ก ์คํจ (๊ฒฐ๊ณผ์๋ ์ํฅ ์์): {logbook.last_error()}")
|
| 164 |
|
| 165 |
left, right, _n_changed = diff_panes(source, result["output"])
|
|
|
|
| 166 |
c1, c2 = st.columns(2, gap="medium")
|
| 167 |
with c1:
|
| 168 |
st.subheader("์๋ฌธ")
|
assets/pipelines/pipeline_dev_v40.yaml
DELETED
|
@@ -1,71 +0,0 @@
|
|
| 1 |
-
name: pipeline_dev_v40
|
| 2 |
-
description: "v38(C1) + ๊ท์น ์ธต 3์ข
(2026-09-18 ๋ผ์ด๋): โ vocabulary_v2.csv โ ํ์ค์ด/์ฌ์ด์์ท/์ธ๋์ด ํผ๋ 71์ ๋ฑ์ฌ (C3 ์คํจ๋ก ์๊ฑฐ๋ 'LLM ๊ธฐ์ด๊ต์ด ์ถ๊ฐ'์ ๊ท์น ๋์ฒด), โก josa_fix โ ๊ธฐํธยท์ซ์ยท๋จ์ ๋ค ์กฐ์ฌ ๊ฒฐ์ ๋ก ๊ต์ (ํ์
gold 15๊ฑด ์ค ๋ชจ๋ธ ์ ์ค 1๊ฑด์ด๋ ํด๋์ค), โข diff_revert โ ๊ธ๋ฆฌํ ๋ณํยท์ผํ ์ฝ์
ยท๊ธ์ง ์นํยท๊ฒฉ์ ์ ํ ์คํ ๋๋๋ฆผ (paragraph ์คํ 410๊ฑด ์ค ~100๊ฑด์ด ์ด ํด๋์ค). ํ์
์ฌ์(replay) ์ค์ธก: paragraph +4.71 / ci-v0 +3.67 / ci-v1 +1.78, rep-small 3๋ฐ๋ณต ์ฌ์ +2.47. ์คํ
์ถ๊ฐ๋ผ extends ๋ถ๊ฐ, ์ ์ฒด ํ์ผ."
|
| 3 |
-
text_split_size: 500
|
| 4 |
-
steps:
|
| 5 |
-
- name: vocabulary_sub_pre
|
| 6 |
-
type: rule
|
| 7 |
-
rule_type: vocabulary_sub
|
| 8 |
-
config:
|
| 9 |
-
vocab_dataset: resources/vocabulary_v2.csv
|
| 10 |
-
|
| 11 |
-
- name: proofread
|
| 12 |
-
type: llm
|
| 13 |
-
model: ft:solar-news-correction
|
| 14 |
-
prompt_key: proofread
|
| 15 |
-
max_tokens: 2000
|
| 16 |
-
|
| 17 |
-
- name: basic_correction
|
| 18 |
-
type: llm
|
| 19 |
-
prompt_key: step1
|
| 20 |
-
user_template: |
|
| 21 |
-
<์๋ฌธ>
|
| 22 |
-
{steps.vocabulary_sub_pre}
|
| 23 |
-
</์๋ฌธ>
|
| 24 |
-
|
| 25 |
-
<๊ต์ด_๋ชจ๋ธ_์์ ๊ฒฐ๊ณผ>
|
| 26 |
-
{current}
|
| 27 |
-
</๊ต์ด_๋ชจ๋ธ_์์ ๊ฒฐ๊ณผ>
|
| 28 |
-
|
| 29 |
-
- name: context_vocab
|
| 30 |
-
type: llm
|
| 31 |
-
prompt_key: step2a
|
| 32 |
-
|
| 33 |
-
- name: context_grammar
|
| 34 |
-
type: llm
|
| 35 |
-
prompt_key: step2b
|
| 36 |
-
|
| 37 |
-
- name: vocabulary_reapply
|
| 38 |
-
type: rule
|
| 39 |
-
config:
|
| 40 |
-
vocab_dataset: resources/vocabulary_v2.csv
|
| 41 |
-
|
| 42 |
-
- name: compound_whitelist_restore
|
| 43 |
-
type: rule
|
| 44 |
-
config:
|
| 45 |
-
whitelist_path: resources/compound_whitelist.txt
|
| 46 |
-
|
| 47 |
-
- name: yul_ryul_normalize
|
| 48 |
-
type: rule
|
| 49 |
-
config: {}
|
| 50 |
-
|
| 51 |
-
- name: post_process
|
| 52 |
-
type: rule
|
| 53 |
-
config:
|
| 54 |
-
pronoun_replacements:
|
| 55 |
-
์ด์ฌ๋ช
๋ํ: ์ด์ฌ๋ช
๋ํต๋ น
|
| 56 |
-
currency_compact: true
|
| 57 |
-
comma_removal: true
|
| 58 |
-
unit_unicode: true
|
| 59 |
-
|
| 60 |
-
- name: josa_fix
|
| 61 |
-
type: rule
|
| 62 |
-
config: {}
|
| 63 |
-
|
| 64 |
-
- name: diff_revert
|
| 65 |
-
type: rule
|
| 66 |
-
config:
|
| 67 |
-
glyph_restore: true
|
| 68 |
-
comma_insertion_revert: true
|
| 69 |
-
forbidden_insertions:
|
| 70 |
-
- ์ฒญ์๋
|
| 71 |
-
register_guard: true
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assets/pipelines/pipeline_dev_v44.yaml
DELETED
|
@@ -1,6 +0,0 @@
|
|
| 1 |
-
name: pipeline_dev_v44
|
| 2 |
-
description: "v41 + ๋ฌธ์ ๋จ์ chunk โ ๋ฐ๋ชจ(HF Space) ํ๋ฉด์ฉ. ๋ด์ฉ์ `pipeline_dev_v41` ๊ณผ ๋์ผํ๊ณ `document_chunking: true` ํ๋๋ง ๋ค๋ฅด๋ค. **base ๊ฐ v40 ์ธ ์ด์ **: ์์์ 1๋จ๊ณ๋ก ์ ํ๋ผ ์์ด(core/pipeline_compose.py) v41(= v40 + punct ํ๋๊ทธ)์ ๋ค์ extends ํ ์ ์๋ค โ ๊ทธ๋์ v41 ์ด ์น๋ ํ๋๊ทธ๋ฅผ ์ฌ๊ธฐ์ ํจ๊ป ์ ์ธํ๋ค. ๋ ํ์ผ์ diff_revert ์ค์ ์ด ๊ฐ๋ผ์ง๋ฉด ์ ๋๋ฏ๋ก v41 ์ ๊ณ ์น ๋ ์ด ํ์ผ๋ ๊ฐ์ด ๋ณธ๋ค. chunk ๊ทผ๊ฑฐ: v35(= v34 + chunk)๊ฐ long-article ๋งค์นญ ฮF1 โ0.9 โ +3.5 (2026-09-10, `#260910_long_article_regression_set`). ๋จ v41 ๊ณ์ด์์์ ๊ธด ๊ธฐ์ฌ ๊ฒ์ฆ์ ์์ง ์๋ค(long-article ์
๋ฏธํ๋ณด)."
|
| 3 |
-
extends: pipeline_dev_v40
|
| 4 |
-
overrides:
|
| 5 |
-
steps.diff_revert.config.punct_deletion_revert: true
|
| 6 |
-
document_chunking: true
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assets/pipelines/pipeline_exp_261021_ft_hybrid.yaml
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: pipeline_exp_261021_ft_hybrid
|
| 2 |
+
description: 'FT ๋ฌธ๋จ ์ธต + ๊ธฐ์ฌ ์ธต 1ํ (2026-10-21) - ํ๊ธฐ ์ฌ์ ์นํ ๋ค ft:solar-news-correction ์ ํ์ต ์์คํ
ํ๋กฌํํธ(ft_system)๋ก ์ค๋ฐ๊ฟ 500์ ๋ฌถ์๋ง๋ค ๋์์ ๋ถ๋ฅด๊ณ (๋ฌถ์ ๋์ 8), ์๋ฌธ ๋๋น ๋ณ๊ฒฝ ๋ชฉ๋ก(์ฌ์ ์นํ ํฌํจ, ๋์ด์ฐ๊ธฐ๋ง ๋ค๋ฅธ ๊ฒ์ ๋บ)๊ณผ FT ๊ต์ ๋ณธ ์ ๋ฌธ์ ๊ธฐ์ฌ ์ธต 1ํ(review: FTยท์ฌ์ ์ฌ๊ณ ๋๋๋ฆผ ๋ฒํธ + FT ๊ฐ ๋ชป ๋ณด๋ ์ฌ์คยท์ซ์ยท๋ฌธ๋งฅ ๋ฑ๋ง ๊ต์ ๋ชฉ๋ก)์ ์ค๋ค. ๊ฒฐ์ ๋ก ๋๋๋ฆผยท์ ์ฉ ๋ค ๋ฐ์ดํ ๋ฐฉํฅยท์จ/๋ฅ ยทํ์ฒ๋ฆฌ ๊ท์น. ๊ธฐ๋ณธ ๊ธฐ์ฌ ์ธต์ Opus, Solar ๋ง์ด๋ฉด --set steps.review.model=solar-pro4 --set steps.review.prompt_key=review_sp4. ์ธก์ ยท๋ณ์ด ๊ธฐ๋ก์ insights/261021_ft_hybrid_paragraph_article.md.'
|
| 3 |
+
text_split_size: 500
|
| 4 |
+
text_split_mode: lines
|
| 5 |
+
steps:
|
| 6 |
+
- name: vocabulary_sub_pre
|
| 7 |
+
type: rule
|
| 8 |
+
rule_type: vocabulary_sub
|
| 9 |
+
config:
|
| 10 |
+
vocab_dataset: resources/vocabulary.csv
|
| 11 |
+
- name: proofread
|
| 12 |
+
type: llm
|
| 13 |
+
model: ft:solar-news-correction
|
| 14 |
+
prompt_key: ft_system
|
| 15 |
+
max_tokens: 2000
|
| 16 |
+
temperature: 0.0
|
| 17 |
+
bulk_concurrency: 8
|
| 18 |
+
- name: ft_hunks
|
| 19 |
+
type: diff_hunks
|
| 20 |
+
config:
|
| 21 |
+
key: ft_diff
|
| 22 |
+
context: 20
|
| 23 |
+
max_hunks: 80
|
| 24 |
+
skip_spacing: true
|
| 25 |
+
- name: review
|
| 26 |
+
type: llm
|
| 27 |
+
model: timely:claude-opus-5-5
|
| 28 |
+
prompt_key: review
|
| 29 |
+
output_mode: json
|
| 30 |
+
max_tokens: 4000
|
| 31 |
+
user_template: |
|
| 32 |
+
<FT_๋ณ๊ฒฝ_๋ชฉ๋ก>
|
| 33 |
+
{steps.ft_diff}
|
| 34 |
+
</FT_๋ณ๊ฒฝ_๋ชฉ๋ก>
|
| 35 |
+
|
| 36 |
+
<๋ณธ๋ฌธ>
|
| 37 |
+
{current}
|
| 38 |
+
</๋ณธ๋ฌธ>
|
| 39 |
+
- name: revert_ft
|
| 40 |
+
type: revert_hunks
|
| 41 |
+
config:
|
| 42 |
+
hunks_from: ft_hunks
|
| 43 |
+
from_step: review
|
| 44 |
+
- name: apply_review
|
| 45 |
+
type: apply_corrections
|
| 46 |
+
config:
|
| 47 |
+
from_step: review
|
| 48 |
+
max_items: 12
|
| 49 |
+
max_shrink: 0.6
|
| 50 |
+
- name: quote_direction
|
| 51 |
+
type: rule
|
| 52 |
+
config: {}
|
| 53 |
+
- name: quote_preserve
|
| 54 |
+
type: rule
|
| 55 |
+
config: {}
|
| 56 |
+
- name: yul_ryul_normalize
|
| 57 |
+
type: rule
|
| 58 |
+
config: {}
|
| 59 |
+
- name: post_process
|
| 60 |
+
type: rule
|
| 61 |
+
config:
|
| 62 |
+
pronoun_replacements:
|
| 63 |
+
์ด์ฌ๋ช
๋ํ: ์ด์ฌ๋ช
๋ํต๋ น
|
| 64 |
+
currency_compact: true
|
| 65 |
+
comma_removal: true
|
| 66 |
+
unit_unicode: true
|
assets/prompts/prompt_dev_260917_vsplit/step1.txt
DELETED
|
@@ -1,85 +0,0 @@
|
|
| 1 |
-
๐จ๐จ๐จ **์ต์์ ํต์ฌ ์ง์์ฌํญ (PRIME DIRECTIVE)** ๐จ๐จ๐จ
|
| 2 |
-
**์ฌ์ค ๋ณด์กด์ด ์ต์ฐ์ ์ด๋ค.** `<์๋ฌธ>` ์ด ๊ฐ๋ฆฌํค๋ ์ธ๋ฌผยท๊ธฐ๊ดยท์ง์ฑ
ยท์ซ์ยท๋ ์งยท์ฌ๊ฑด์ ๋์ ์ง์๊ณผ ๋ค๋ฅด๋๋ผ๋ ์ ๋ ๋ค๋ฅธ ๋์์ผ๋ก ๋ฐ๊พธ์ง ์๋๋ค. ์ด๋ฅผ ์๋ฐํ๋ ๊ฒ์ด ๊ฐ์ฅ ์ค๋ํ ์ค๋ฅ๋ค.
|
| 3 |
-
|
| 4 |
-
## Task
|
| 5 |
-
๋๋ ํ๊ตญ์ด ๋ด์ค ๊ต์ด์ **๊ฒ์ฆ์(verifier)** ๋ค. ๋ ํ
์คํธ๋ฅผ ๋ฐ๋๋ค:
|
| 6 |
-
- `<์๋ฌธ>`: ๊ต์ด๋์ง ์์ ์๋ณธ. **๋ชจ๋ ์ฌ์ค ์ ๋ณด(์ด๋ฆ, ์ง์ฑ
, ์ซ์ ๋ฑ)์ ๊ธฐ์ค.**
|
| 7 |
-
- `<๊ต์ด_๋ชจ๋ธ_์์ ๊ฒฐ๊ณผ>`: 1์ฐจ ๊ต์ด ๋ชจ๋ธ์ด ์์ ํ ๊ฒฐ๊ณผ. ์ฌ๋ฐ๋ฅธ ๊ต์ ๊ณผ ์๋ชป๋ ์์ ์ด ์์ฌ ์์ ์ ์๋ค.
|
| 8 |
-
|
| 9 |
-
๋์ ์๋ฌด๋ **์๋ก ๊ต์ ํ๋ ๊ฒ์ด ์๋๋ผ, 1์ฐจ ๊ต์ด์ ์์ ์ ํ๋์ฉ ํ์ ํ๋ ๊ฒ**์ด๋ค.
|
| 10 |
-
๋ค์ 3๋จ๊ณ๋ฅผ ์์๋๋ก ์ํํ๋ผ:
|
| 11 |
-
|
| 12 |
-
1. **์ฐจ์ด ๋์ด**: `<์๋ฌธ>` ๊ณผ `<๊ต์ด_๋ชจ๋ธ_์์ ๊ฒฐ๊ณผ>` ๋ฅผ ์ด์ ๋จ์๋ก ๋์กฐํด, 1์ฐจ ๊ต์ด์ด ๋ฐ๊พผ ์ง์ ์ **๋น ์ง์์ด** ๋์ดํ๋ค. ํ์: `'์๋ฌธ ํํ' -> '์์ ํํ'`
|
| 13 |
-
2. **ํ์ **: ๊ฐ ์ฐจ์ด์ ๋ํด `keep`(์ฌ๋ฐ๋ฅธ ๊ต์ , ์ ์ง) ๋๋ `revert`(์๋ชป๋ ์์ , ์๋ฌธ ๋ณต์) ๋ฅผ ํ์ ํ๊ณ ๊ทผ๊ฑฐ๋ฅผ ํ ์ค๋ก ์ ๋๋ค.
|
| 14 |
-
3. **์ต์ข
์กฐ๋ฆฝ**: `keep` ํ์ ๋ง ๋ฐ์ํ๊ณ `revert` ํ์ ์ ์๋ฌธ ํํ์ผ๋ก ๋๋๋ฆฐ ์ต์ข
ํ
์คํธ๋ฅผ ๋ง๋ ๋ค.
|
| 15 |
-
|
| 16 |
-
**์ ๊ต์ ์ ์ถ๊ฐํ๋ ๊ฒ์ ๋์ ์๋ฌด๊ฐ ์๋๋ค.** 1์ฐจ ๊ต์ด์ด ๋์น ์ค๋ฅ๋ ์ดํ ๋จ๊ณ๊ฐ ์ฒ๋ฆฌํ๋ค. ์ฐจ์ด๊ฐ ํ๋๋ ์์ผ๋ฉด ์
๋ ฅ์ ๊ทธ๋๋ก ์ถ๋ ฅํ๋ค.
|
| 17 |
-
|
| 18 |
-
## ํ์ ๊ธฐ์ค
|
| 19 |
-
|
| 20 |
-
**keep (์ฌ๋ฐ๋ฅธ ๊ต์ โ ์ ์ง):**
|
| 21 |
-
- ๋ง์ถค๋ฒยท์คํ์ ๊ต์ (์: `๊ถ์์ฃผ์` -> `๊ถ์์ฃผ์`, `๊ธฐ๋ ท๋ค` -> `๊ธฐ๋ ธ๋ค`)
|
| 22 |
-
- ์ฌ์ด์์ท ๊ต์ (์: `์ฝ์์ผ` -> `์ฝง์์ผ`, `๋๊ฐ` -> `๋๊ฐ` โ ๋ฃ์ ๊ณณ๊ณผ ๋บ ๊ณณ ๋ชจ๋)
|
| 23 |
-
- ์กฐ์ฌยท์ด๋ฏธ ๊ต์ (์: `1๋ฅผ` -> `1์`, `~ํ๋ค๊ณ ํ๋ค` ์ ํธ์ ๊ต์ )
|
| 24 |
-
- ๋ช
๋ฐฑํ ๋์ด์ฐ๊ธฐ ์ค๋ฅ ๊ต์
|
| 25 |
-
- ๊ฐ์ ๋์์ ํ๊ธฐ ์ค๋ฅ ๊ต์ (์: `์ค๋์นด์` -> `์คํค๋์`, `์ฅ๊ฒ` -> `์
๊ฒ` โ ๋์์ด ๊ฐ๊ณ ํ๊ธฐ๋ง ํ๋ ธ์ ๋)
|
| 26 |
-
|
| 27 |
-
**revert (์๋ชป๋ ์์ โ ์๋ฌธ ๋ณต์):**
|
| 28 |
-
- **๋์ ๊ต์ฒด**: ์ธ๋ฌผยท๊ธฐ๊ดยท์ง์ฑ
ยท์ฌ๊ฑด์ ๋ค๋ฅธ ๋์์ผ๋ก ๋ฐ๊พผ ๊ฒ (์: `์ด์ฌ๋ช
๋ํต๋ น` -> `์ค์์ด ๋ํต๋ น`, `๊ธฐ์๋ค ์ด๋ฆฌ` -> `๋ค์นด์ด์น ์ด๋ฆฌ`). ๋์ ์ง์์ ์๋ฌธ์ด ํ๋ ค ๋ณด์ฌ๋ **๋ฌด์กฐ๊ฑด revert**.
|
| 29 |
-
- **์ซ์ยท๋ ์งยท๋จ์ ๊ฐ ๋ณ๊ฒฝ**: ์์น ์์ฒด๋ฅผ ๋ฐ๊พผ ๊ฒ (ํ๊ธฐ ์ ๊ทํ๊ฐ ์๋๋ผ ๊ฐ์ด ๋ฌ๋ผ์ง ๊ฒฝ์ฐ)
|
| 30 |
-
- **๋์์ด ์นํยท์ค๋ฌธ**: ์๋ฌธ ํํ๋ ํ์ค์ด์ด๊ณ ๋ป์ด ํตํ๋๋ฐ ๋ค๋ฅธ ํํ์ผ๋ก ๋ฐ๊พผ ๊ฒ (์: `์ฌ์ฐจ` -> `๋ค์`)
|
| 31 |
-
- **๋ฌธ์ฅ ์์ถยท๋ด์ฉ ์ญ์ ยท๋ด์ฉ ์ถ๊ฐ**: ์๋ฌธ์ ์๋ ๋ด์ฉ์ด ์ฌ๋ผ์ง๊ฑฐ๋ ์๋ ๋ด์ฉ(๊ดํธ ๋ถ์ฐ ํฌํจ)์ด ์๊ธด ๊ฒ
|
| 32 |
-
- **ํ์ฉ๋๋ ํ๊ธฐ๋ฅผ ๋ฐ๊พผ ๊ฒ**: ๋ถ์ฌ ์/๋์ด ์์ด ๋ชจ๋ ํ์ฉ๋๋ ๋ณตํฉ์ด์ ์ฌ์กฐ์
|
| 33 |
-
- **๋ฌธ๋จยท๋ฌธ์ฅ ๊ตฌ์กฐ ๋ณ๊ฒฝ**: `<paragraph_separator>` ๋ ์ค๋ฐ๊ฟ์ ์ถ๊ฐยท์ญ์ ยท์ด๋
|
| 34 |
-
|
| 35 |
-
ํ๋จ์ด ํ์คํ์ง ์์ผ๋ฉด **revert** ํ๋ค โ ์๋ฌธ ์ ์ง๊ฐ ๊ธฐ๋ณธ๊ฐ์ด๋ค.
|
| 36 |
-
|
| 37 |
-
## ์ถ๋ ฅ ํ์
|
| 38 |
-
|
| 39 |
-
๋จผ์ ํ์ ๋ชฉ๋ก์ ์์ ํ
์คํธ๋ก ์ ์ ๋ค, **๋ง์ง๋ง์ JSON ํ๋**๋ฅผ ์ถ๋ ฅํ๋ค.
|
| 40 |
-
- `output`: ์ต์ข
ํ
์คํธ ์ ์ฒด. `<paragraph_separator>` ์ ์ค๋ฐ๊ฟ ๊ตฌ์กฐ๋ ์๋ฌธ๊ณผ 100% ๋์ผํ๊ฒ ์ ์ง (`[IMG:~~]` ํ๊ทธ ํฌํจ).
|
| 41 |
-
- `explanation`: revert ํ ํญ๋ชฉ ์์ฝ. ์์ผ๋ฉด `"๋ชจ๋ ์์ ์ ์ง"`. ์ค๋ฐ๊ฟ์ `\n` ์ผ๋ก ํ๊ธฐ.
|
| 42 |
-
|
| 43 |
-
## ์์
|
| 44 |
-
|
| 45 |
-
**Input:**
|
| 46 |
-
```xml
|
| 47 |
-
<์๋ฌธ>
|
| 48 |
-
์ค์์ด ๋ํต๋ น์ ์ด๋ ์ ์ค์ ๋ชจ์ฌ ํ์๋ฅผ ๊ธฐ๋ ท๋ค.
|
| 49 |
-
</์๋ฌธ>
|
| 50 |
-
<๊ต์ด_๋ชจ๋ธ_์์ ๊ฒฐ๊ณผ>
|
| 51 |
-
์ด์ฌ๋ช
๋ํต๋ น์ ์ด๋ ์ ์ค์ ๋ชจ์ฌ ํ์๋ฅผ ๊ธฐ๋ ธ๋ค.
|
| 52 |
-
</๊ต์ด_๋ชจ๋ธ_์์ ๊ฒฐ๊ณผ>
|
| 53 |
-
```
|
| 54 |
-
|
| 55 |
-
**CORRECT EXAMPLE:**
|
| 56 |
-
์ฐจ์ด ํ์ :
|
| 57 |
-
1. '์ค์์ด ๋ํต๋ น์' -> '์ด์ฌ๋ช
๋ํต๋ น์' : revert (๋์ ๊ต์ฒด โ ์๋ฌธ์ ์ธ๋ฌผ์ ๋ค๋ฅธ ์ธ๋ฌผ๋ก ๋ณ๊ฒฝ)
|
| 58 |
-
2. '์ ์ค์' -> '์ ์ค์' : keep (์คํ์ ๊ต์ )
|
| 59 |
-
3. '๊ธฐ๋ ท๋ค' -> '๊ธฐ๋ ธ๋ค' : keep (๋ง์ถค๋ฒ ๊ต์ )
|
| 60 |
-
|
| 61 |
-
{"output": "์ค์์ด ๋ํต๋ น์ ์ด๋ ์ ์ค์ ๋ชจ์ฌ ํ์๋ฅผ ๊ธฐ๋ ธ๋ค.", "explanation": "'์ด์ฌ๋ช
๋ํต๋ น์' -> '์ค์์ด ๋ํต๋ น์' (๋์ ๊ต์ฒด ๋ณต์)"}
|
| 62 |
-
|
| 63 |
-
**Input:**
|
| 64 |
-
```xml
|
| 65 |
-
<์๋ฌธ>
|
| 66 |
-
๊ทธ๋ ์ ์ ์์ ๊ถ์์ฃผ์์ ๋ง์ ์ฌ์ฐจ ํฌ์ํ๋ค.
|
| 67 |
-
</์๋ฌธ>
|
| 68 |
-
<๊ต์ด_๋ชจ๋ธ_์์ ๊ฒฐ๊ณผ>
|
| 69 |
-
๊ทธ๋ ์ ์ ์์ ๊ถ์์ฃผ์์ ๋ง์ ๋ค์ ํฌ์ํ๋ค.
|
| 70 |
-
</๊ต์ด_๋ชจ๋ธ_์์ ๊ฒฐ๊ณผ>
|
| 71 |
-
```
|
| 72 |
-
|
| 73 |
-
**CORRECT EXAMPLE:**
|
| 74 |
-
์ฐจ์ด ํ์ :
|
| 75 |
-
1. '์ฌ์ฐจ' -> '๋ค์' : revert (๋์์ด ์นํ โ ์๋ฌธ ํํ๋ ํ์ค์ด)
|
| 76 |
-
|
| 77 |
-
{"output": "๊ทธ๋ ์ ์ ์์ ๊ถ์์ฃผ์์ ๋ง์ ์ฌ์ฐจ ํฌ์ํ๋ค.", "explanation": "'๋ค์' -> '์ฌ์ฐจ' (๋์์ด ์นํ ๋ณต์)"}
|
| 78 |
-
|
| 79 |
-
------------------------------------
|
| 80 |
-
|
| 81 |
-
โ ๏ธ ์ต์ข
ํ์ธ:
|
| 82 |
-
- ํ์ ๋ชฉ๋ก ๋ค์, ๋ง์ง๋ง์ JSON ํ ๊ฐ๋ง ์ถ๋ ฅํ๋ค (JSON ๋ค์ ๋ค๋ฅธ ํ
์คํธ ๊ธ์ง)
|
| 83 |
-
- `output` ์ `<paragraph_separator>` ๊ฐ์ยท์์นยท์ค๋ฐ๊ฟ์ ์๋ฌธ๊ณผ 100% ๋์ผ
|
| 84 |
-
- ์ฐจ์ด๊ฐ ์์ผ๋ฉด: `{"output": "[์
๋ ฅ ๊ทธ๋๋ก]", "explanation": "์์ ๋ ๋ด๏ฟฝ๏ฟฝ๏ฟฝ์ด ์์ต๋๋ค."}`
|
| 85 |
-
- ์ ๊ต์ ์ถ๊ฐ ๊ธ์ง โ ๋์ดยทํ์ ยท์กฐ๋ฆฝ๋ง ํ๋ค
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assets/prompts/prompt_dev_260917_vsplit/step2a.txt
DELETED
|
@@ -1,36 +0,0 @@
|
|
| 1 |
-
๐จ๐จ๐จ **์ต์์ ํต์ฌ ์ง์์ฌํญ (PRIME DIRECTIVE)** ๐จ๐จ๐จ
|
| 2 |
-
1. **์ฌ์ค ์ ๋ณด ์์ ์ ๋ ๊ธ์ง**: ์ธ๋ฌผ, ์ง์ฑ
, ์ซ์, ๊ณ ์ ๋ช
์ฌ๋ ๋์ ์ง์๊ณผ ๋ค๋ฅด๋๋ผ๋ ์ ๋ ๋ฐ๊พธ์ง ์๋๋ค. (์
๋ ฅ `์ด์ฌ๋ช
๋ํต๋ น` โ ์ถ๋ ฅ `์ด์ฌ๋ช
๋ํต๋ น`)
|
| 3 |
-
2. **๊ตฌ์กฐ ์ ๋ ์ ์ง**: ๋ฌธ๋จ ์ยท๋ฌธ์ฅ ์์ยท์ค๋ฐ๊ฟยท`<paragraph_separator>`ยท`[IMG:~~]` ํ๊ทธ๋ฅผ ๊ทธ๋๋ก ์ ์งํ๋ค. ๋ด์ฉ ์ถ๊ฐ(๊ดํธ ๋ถ์ฐ ํฌํจ)ยท์ญ์ ๊ธ์ง.
|
| 4 |
-
3. **์์ ํ์ง ์๋ ๊ฒ์ด ๊ธฐ๋ณธ๊ฐ์ด๋ค**: ๋์์ด ์นํ, ๋ฌธ์ฅ ์์ถ, ๋์ด์ฐ๊ธฐ ์ฌ์กฐ์ , ํํ ๋ค๋ฌ๊ธฐ๋ ์ ๋ถ ๊ธ์ง. ์๋ ์๋ฌด์ ํด๋นํ๋ ๊ต์ ๋ง ํ๋ค.
|
| 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 |
-
JSON ํ๋๋ง ์ถ๋ ฅํ๋ค (๋งํฌ๋ค์ด ์ฝ๋ ๋ธ๋กยท์ค๋ช
๋ฌธ ๊ธ์ง):
|
| 30 |
-
{"output": "[๊ต์ ์๋ฃ๋ ์ ์ฒด ํ
์คํธ - ์์ ์์ด๋ ์
๋ ฅ ์ ์ฒด ํฌํจ, <paragraph_separator>ยท์ค๋ฐ๊ฟ์ ์
๋ ฅ๊ณผ 100% ๋์ผ]", "explanation": "['๊ธฐ์กด' -> '์์ ' (์ด์ )' ํ์, ์ฌ๋ฌ ๊ฐ๋ \\n ๊ตฌ๋ถ, ์์ผ๋ฉด '์์ ๋ ๋ด์ฉ์ด ์์ต๋๋ค.']"}
|
| 31 |
-
|
| 32 |
-
**Input:** ์ฌ๊ณ ์ฌํ๊ฐ ๊ฒ์ก์ ์ ์์ด ์ปค์ง์ ์๋ ๊ณคํน์ ์น๋ ๋ค.
|
| 33 |
-
**CORRECT:** {"output": "์ฌ๊ณ ์ฌํ๊ฐ ๊ฑท์ก์ ์ ์์ด ์ปค์ง์ ์๋ ๊ณค์์ ์น๋ ๋ค.", "explanation": "'๊ฒ์ก์' -> '๊ฑท์ก์' (์ค์ ๋ณต์)\n'๊ณคํน์' -> '๊ณค์์' (์น๋ฅด๋ ๊ฒ์ '๊ณค์')"}
|
| 34 |
-
|
| 35 |
-
**Input:** Kํ ๊ทธ๋ฃน์ด ์ปด๋ฐฑํ๋ค.
|
| 36 |
-
**CORRECT:** {"output": "Kํ ๊ทธ๋ฃน์ด ์ปด๋ฐฑํ๋ค.", "explanation": "์์ ๋ ๋ด์ฉ์ด ์์ต๋๋ค."}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assets/prompts/prompt_dev_260917_vsplit/step2b.txt
DELETED
|
@@ -1,33 +0,0 @@
|
|
| 1 |
-
๐จ๐จ๐จ **์ต์์ ํต์ฌ ์ง์์ฌํญ (PRIME DIRECTIVE)** ๐จ๐จ๐จ
|
| 2 |
-
1. **์ฌ์ค ์ ๋ณด ์์ ์ ๋ ๊ธ์ง**: ์ธ๋ฌผ, ์ง์ฑ
, ์ซ์, ๊ณ ์ ๋ช
์ฌ๋ ๋์ ์ง์๊ณผ ๋ค๋ฅด๋๋ผ๋ ์ ๋ ๋ฐ๊พธ์ง ์๋๋ค. (์
๋ ฅ `์ด์ฌ๋ช
๋ํต๋ น` โ ์ถ๋ ฅ `์ด์ฌ๋ช
๋ํต๋ น`)
|
| 3 |
-
2. **๊ตฌ์กฐ ์ ๋ ์ ์ง**: ๋ฌธ๋จ ์ยท๋ฌธ์ฅ ์์ยท์ค๋ฐ๊ฟยท`<paragraph_separator>`ยท`[IMG:~~]` ํ๊ทธ๋ฅผ ๊ทธ๋๋ก ์ ์งํ๋ค. ๋ด์ฉ ์ถ๊ฐ(๊ดํธ ๋ถ์ฐ ํฌํจ)ยท์ญ์ ๊ธ์ง.
|
| 4 |
-
3. **์์ ํ์ง ์๋ ๊ฒ์ด ๊ธฐ๋ณธ๊ฐ์ด๋ค**: ๋์์ด ์นํ, ๋ฌธ์ฅ ์์ถ, ์ด์ ์ฌ๋ฐฐ์น, ํํ ๋ค๋ฌ๊ธฐ, ๋์ด์ฐ๊ธฐ ์ฌ์กฐ์ ์ ์ ๋ถ ๊ธ์ง. ์๋ ์๋ฌด์ ํด๋นํ๋ ๊ต์ ๋ง ํ๋ค.
|
| 5 |
-
|
| 6 |
-
# ์ญํ
|
| 7 |
-
๋น์ ์ ๋ด์ค ๊ธฐ์ฌ ๊ต์ด๊ฐ ์ค **๋ฌธ๋ฒ ํธ์ ์ ๋ด**์
๋๋ค. ๊ธฐ์ด ๊ต์ด์ด ๋๋ ํ
์คํธ์์ ๋ค์ ๋ค ๊ฐ์ง๋ง ์ฐพ์ต๋๋ค:
|
| 8 |
-
|
| 9 |
-
1. **์กฐ์ฌ ์ค๋ฅยท๋๋ฝ**: ๋ฐ์นจ์ ๋ง์ง ์๋ ์กฐ์ฌ, ๋ฌธ๋งฅ์ ์ ๋ง๋ ์กฐ์ฌ, ๋น ์ง ์กฐ์ฌ
|
| 10 |
-
- ์: `1๋ฅผ` โ `1์` / `ํ๋ฅด๊ทธ์ฌ์ด ์์ ์์ถ ์ฌ์ฅ๋ถ` โ `ํ๋ฅด๊ทธ์ฌ์ ์์ ์์ถ ์ฌ์ฅ๋ถ` (์์ ๊ด๊ณ)
|
| 11 |
-
2. **์ด๋ฏธยท์ฉ์ธ ํ์ฉ ์ค๋ฅ**: ์๋ชป๋ ํ์ฉํ, ๊ฒน์น ์ด๋ฏธ
|
| 12 |
-
- ์: `๊ฐ๊ฒ ๋ค"๋ฉฐ๋ฉฐ` โ `๊ฐ๊ฒ ๋ค"๋ฉฐ` / `์ฌํ์` โ `์ฌํผ์` / `๋ชจ์๋ผ์ ํ๋ค` โ `๋ชจ์๋ผ์ง ์๋ค`
|
| 13 |
-
3. **๋ฌธ์ฅ ์ฑ๋ถ ํธ์ ์ค๋ฅ**: ์ฃผ์ด-์์ ์ด, ๋ชฉ์ ์ด-์์ ์ด๊ฐ ์ด๊ธ๋๋ ๊ฒฝ์ฐ โ **์ด๊ธ๋ ์ฑ๋ถ๋ง ์ต์ ์์ **
|
| 14 |
-
- ์: `์ด๋ป๊ฒ ๊ด๋ฆฌํ๋๊ฐ ์ค์ํ๋ค` โ `์ด๋ป๊ฒ ๊ด๋ฆฌํ๋์ง๊ฐ ์ค์ํ๋ค`
|
| 15 |
-
4. **๋จ์ ๋๋ฝ**: ์์น ๋ค์ ๋จ์๊ฐ ๋น ์ง ๊ฒฝ์ฐ โ ๋ฌธ๋งฅ์ ๋ช
์๋ ๋จ์๋ง ๋ณด์ถฉ
|
| 16 |
-
- ์: `์์ฅ ๊ท๋ชจ๋ 1์กฐ7000์ต์ด๋ค` โ `์์ฅ ๊ท๋ชจ๋ 1์กฐ7000์ต์์ด๋ค` (์ ๋ฌธ๋งฅ์ด ๊ธ์ก์ผ ๋)
|
| 17 |
-
|
| 18 |
-
**์ฌ๋/ํผ๋ ํผ์ฉ**์ ๋ช
๋ฐฑํ ๋ฌธ๋ฒ์ด ํ๋ฆฐ ๊ฒฝ์ฐ๋ง ๊ณ ์นฉ๋๋ค (์: `๋ณด์ฌ์ง๋ค` โ `๋ณด์ด๋ค`). ๋ ๋ค ํ์ฉ๋๋ ํํ์ ๊ทธ๋๋ก ๋ก๋๋ค.
|
| 19 |
-
|
| 20 |
-
# ๋ฐ๊พธ๋ฉด ์ ๋๋ ์ (์ ์ง)
|
| 21 |
-
- ๋ป์ด ํตํ๋ ๋ฌธ์ฅ์ ์ด์ โ ๊ทธ๋๋ก ์ ์ง (์ฌ๋ฐฐ์น๋ ๋์ ์๋ฌด๊ฐ ์๋๋ค)
|
| 22 |
-
- '~์๋ ๋ถ๊ตฌํ๊ณ ', ๋ฒ์ญํฌ๋ก ๋ณด์ด๋ ํํ โ ๋ฌธ๋ฒ์ด ํ๋ฆฌ์ง ์์์ผ๋ฉด ์ ์ง
|
| 23 |
-
- ๋์ด์ฐ๊ธฐยท์ฌ์ด์์ทยท๋ง์ถค๋ฒ โ ์ด ๋จ๊ณ์ ๋์์ด ์๋๋ค. ์๋์ง ๋ง๋ผ
|
| 24 |
-
|
| 25 |
-
# ์ถ๋ ฅ ํ์
|
| 26 |
-
JSON ํ๋๋ง ์ถ๋ ฅํ๋ค (๋งํฌ๋ค์ด ์ฝ๋ ๋ธ๋กยท์ค๋ช
๋ฌธ ๊ธ์ง):
|
| 27 |
-
{"output": "[๊ต์ ์๋ฃ๋ ์ ์ฒด ํ
์คํธ - ์์ ์์ด๋ ์
๋ ฅ ์ ์ฒด ํฌํจ, <paragraph_separator>ยท์ค๋ฐ๊ฟ์ ์
๋ ฅ๊ณผ 100% ๋์ผ]", "explanation": "['๊ธฐ์กด' -> '์์ ' (์ด์ )' ํ์, ์ฌ๋ฌ ๊ฐ๋ \\n ๊ตฌ๋ถ, ์์ผ๋ฉด '์์ ๋ ๋ด์ฉ์ด ์์ต๋๋ค.']"}
|
| 28 |
-
|
| 29 |
-
**Input:** ๋ฒ์์ ํ์ 11๊ฐ์ ๋ํด ์ ์ฃ ํ๊ฒฐ์ ๋ด๋ ธ๊ณ ์กฐ๊ตญ์ "๊ฐ์๋ฐญ๊ธธ์ ๊ฐ๊ฒ ๋ค"๋ฉฐ๋ฉฐ ํญ์ํ๋ค.
|
| 30 |
-
**CORRECT:** {"output": "๋ฒ์์ ํ์ 11๊ฐ์ ๋ํด ์ ์ฃ ํ๊ฒฐ์ ๋ด๋ ธ๊ณ ์กฐ๊ตญ์ \"๊ฐ์๋ฐญ๊ธธ์ ๊ฐ๊ฒ ๋ค\"๋ฉฐ ํญ์ํ๋ค.", "explanation": "'๊ฐ๊ฒ ๋ค\"๋ฉฐ๋ฉฐ' -> '๊ฐ๊ฒ ๋ค\"๋ฉฐ' (๊ฒน์น ์ด๋ฏธ ๊ต์ )"}
|
| 31 |
-
|
| 32 |
-
**Input:** Kํ ๊ทธ๋ฃน์ด ์ปด๋ฐฑํ๋ค.
|
| 33 |
-
**CORRECT:** {"output": "Kํ ๊ทธ๋ฃน์ด ์ปด๋ฐฑํ๋ค.", "explanation": "์์ ๋ ๋ด์ฉ์ด ์์ต๋๋ค."}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assets/prompts/prompt_exp_261021_ft_hybrid/ft_system.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
์
๋ ฅ๋ ๋ฌธ์์ ๋ํ ๊ต์ด ๊ฒฐ๊ณผ๋ฅผ ์์ฑํด ์ฃผ์ธ์.
|
assets/prompts/prompt_exp_261021_ft_hybrid/review.txt
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
๋๋ ํ๊ตญ ์ ๋ฌธ์ฌ ์ด๋ฌธํ์ **์ต์ข
๊ต์ด ๋ฐ์คํฌ**๋ค. 1์ฐจ ๊ต์ด ๋ชจ๋ธ(FT)์ด ๊ธฐ์ฌ๋ฅผ ๋ฌธ๋จ(500์)์ฉ ๋๋ ์ด๋ฏธ ๊ณ ์ณค๋ค. ๋๋ ๊ธฐ์ฌ ์ ์ฒด๋ฅผ ํ ๋ฒ์ ๋ณด๊ณ ๋ ๊ฐ์ง ์ผ์ ํ๋ค.
|
| 2 |
+
(A) FT ๊ฐ ๊ณ ์น ์๋ฆฌ ๊ฐ์ด๋ฐ **FT ๊ฐ ์๋ชป ๊ณ ์น ๊ฒ๋ง** ๊ณจ๋ผ ๋๋๋ฆฐ๋ค. (B) FT ๊ฐ ๊ตฌ์กฐ์ ์ผ๋ก **๋ชป ๋ณด๋ ์ค๋ฅ**๋ฅผ ์ฐพ์ ์งง์ ๊ต์ ํญ๋ชฉ์ผ๋ก ๋ธ๋ค. ๋ณธ๋ฌธ์ ๋ค์ ์ฐ์ง ์๋๋ค.
|
| 3 |
+
|
| 4 |
+
## FT ๋ฅผ ์๊ณ ์์ํ๋ค
|
| 5 |
+
- FT ๋ ๊ท๋ฒ ๊ต์ด์ ์ํ๋ค: ๋ง์ถค๋ฒยท์คํ์, ์กฐ์ฌ, ์ด๋ฏธยทํ์ฉ, ์ฃผ์ ํธ์ยท๋น๋ฌธ, ์ฌ์ด์์ท, ๋์ด์ฐ๊ธฐ, ๊ฒน์น ๋ง ์ญ์ , ๋จ์ยท๋ฌธ์ฅ๋ถํธ ํ๊ธฐ. ์กฐ์ ์ผ๋ณด ๊ด๋ก(`N๊ฐ๊ตญ`โ`N๊ตญ`, `์ํผ`โ`์ํผ`, ๊ณง์ ๋ฐ์ดํโ๋ฅ๊ทผ ๋ฐ์ดํ)๋ FT ๊ฐ ๋ฐ๋ฅธ๋ค. **์ด ์ข
๋ฅ์ FT ๋ณ๊ฒฝ์ ๋๋๋ฆฌ์ง ์๋๋ค.** ๋ค๊ฐ ๋ณด๊ธฐ์ ๋ค๋ฅธ ํํ๋ ๊ฐ๋ฅํ๋๋ผ๋ ๊ทธ๋๋ก ๋๋ค.
|
| 6 |
+
- ๋จ, ์ ๊ท๋ฒ ๊ต์ ์ **์ธ์ฉ๋ฌธ ๋ฐ, ์ผ๋ฐ ๋ฑ๋ง**์ ๋ํ ๊ฒ์ด๋ค. FT ๋ ๋ฐ์ดํ ์ ๋ง, ๊ณ ์ ๋ช
์ฌ ์ฒ ์, ์งง์ ๊ดํ์ดยท์กฐ์ฌ ํ๋์ ๋ด๊ธด ๋ป์ ๊ท๋ฒ ๊ต์ ์ธ ์ฒ ๋ฐ๊พธ๋ ์ฌ๊ณ ๋ฅผ ๋ธ๋ค. ๋ฌธ๋จ ๋ฐ์ ๋ชป ๋ณด๊ณ ๊ธฐ์ฌ ์์ฑ ์์ ๋ ๋ชจ๋ฅธ๋ค. (A)์์ ๋๋๋ฆด ๊ฒ์ ์๋ ์ฌ๊ณ ๋ฟ์ด๋ค.
|
| 7 |
+
- FT ๋ ๊ธฐ์ฌ ์ ์ฒด์ ์ฌ์คยท์ซ์ยท์ด๋ฆ ๊ด๊ณ์ ๋ฌธ๋งฅ์ ๋ฑ๋ง ํผ๋์ ๊ฑฐ์ ๊ณ ์น์ง ๋ชปํ๋ค โ (B)์์ ๋ค๊ฐ ๋งก๋๋ค.
|
| 8 |
+
|
| 9 |
+
## (A) ๋๋๋ฆด FT ๋ณ๊ฒฝ โ `<FT_๋ณ๊ฒฝ_๋ชฉ๋ก>` ์ ๋ฒํธ ์ค ์๋์ ํด๋นํ๋ ๊ฒ
|
| 10 |
+
ํ์ ์์: โ ๋ฐ๋ ์๋ฆฌ๊ฐ ๋ฐ์ดํ ์์ธ๊ฐ โ 3๋ฒ. โก ๊ณ ์ ๋ช
์ฌ์ธ๊ฐ โ 2๋ฒ. โข ์ง์์ง๊ฑฐ๋ ๋ํด์ง ๋ง์ด ๋ป์ ๊ฐ๋๊ฐ โ 4ยท5๋ฒ. โฃ ๋จ์ ๊ฒ์ด ๋ง์ถค๋ฒยท์กฐ์ฌยท์ด๋ฏธยทํธ์ ์ ๋ฆฌ๋ฉด ๋๋ค.
|
| 11 |
+
1. **์ซ์ ๋ฐ๊ฟ**: ์ฐ๋ยท๋ ์งยท๊ธ์กยท๋น์จยท์์ยท์ธ์ ๋ฑ์ ์ซ์(0~9)๊ฐ ๋ค๋ฅธ ์ซ์๋ก ๋ฐ๋. ์: `2026`โ`2023`, `20๊ณณ`โ`18๊ณณ`. (๊ฐ์ ๊ฐ๊ณ ํ๊ธฐ๋ง ๋ฐ๋ ๊ฒ, `30001๋ช
`โ`3001๋ช
`์ฒ๋ผ ์๋ฆฟ์๊ฐ ๊ฒน์น ์คํ, **๊ธฐ์ฌ ์ ๋ค๋ฅธ ์ซ์๋ก ๊ฒ์ฐํ๋ฉด FT ์ชฝ ๊ฐ์ด ๋ง๋ ๊ฒ**(ํฉยท์ฐจยท๋น์จ), ๊ธฐ์ฌ ๋ค๋ฅธ ๊ณณ ์ซ์์ ๋ง์ถ ๊ฒ์ ๋๋ค.)
|
| 12 |
+
2. **๊ณ ์ ๋ช
์ฌ ๋ฐ๊ฟ**: ์ธ๋ช
ยท์ง๋ช
ยท๊ธฐ๊ด๋ช
ยทํ์ฌ๋ช
ยท์ํ๋ช
ยท์งํจ์ด ๋ค๋ฅธ ์ด๋ฆ์ด ๋๊ฑฐ๋ ํ ๊ธ์๋ผ๋ ๋ฐ๋, ์์ ์ด๋, ์ ์ ๋ช
์นญ์ ์ผ๋ถ๋ฅผ ์๋ผ๋(`๊ฒฝ์ฑ์ ๊ตญ๋ํ`โ`๊ฒฝ์ฑ์ ๋`). **์ํธยท๋ธ๋๋ยท์ ํ๋ช
ยท์์ค๋ช
ยทํ์ฌ ์ด๋ฆ์ ์ธ๋์ด ํ๊ธฐ๋ฒ๊ณผ ๋ฌ๋ผ๋ ๊ทธ ์ด๋ฆ์ ํ๊ธฐ๊ฐ ์ ๋ต์ด๋ค**: `๋๋ ์ฅฌ๋ฅด`โ`๋๋ ์ฃผ๋ฅด`, `K-ํ ํ์คํฐ๋ฒ`์ ํ์ดํ ์ญ์ ๊ฐ์ ํ๊ธฐ '๊ต์ '์ ๋๋๋ฆฐ๋ค. ์ธ๊ตญ ์ธ๋ช
ยท์ง๋ช
์ ํ ๊ธ์ ๋ฐ๊ฟ๋ ๋๋๋ฆฐ๋ค. (๊ธฐ์ฌ ์ ๋ค๋ฅธ ๊ณณ ํ๊ธฐ์ ๋ง์ถ ๊ฒ, ์๋ ๊ธ์ ์กฐํฉ ๊ฐ์ ๋ช
๋ฐฑํ ์คํ ๊ต์ ์ ๋๋ค.)
|
| 13 |
+
3. **์ธ์ฉ ํผ์**: ํฐ๋ฐ์ดํยท์์๋ฐ์ดํ ์ ๋ณ๊ฒฝ์ **์๋ฌธ ๊ทธ๋๋ก ํ์๊ฐ ๊ทธ๋ ๊ฒ ๋งํ์ ๋ฒํ๊ฐ**๋ก ๊ฐ๋ฅธ๋ค.
|
| 14 |
+
- ๋๋๋ฆผ โ ๋งํฌ ์ ๋ฆฌ: ์๋ฌธ์ด ๊ตฌ์ด๋ก ์์ฐ์ค๋ฝ๊ณ ๋ป์ด ํตํ๋๋ฐ ๋ค๋ฌ์ ๊ฒ. ์ถ์ฝํ ํ๊ธฐ(`โ๊ทธ๊ฑด ์๋๋คโ`โ`โ๊ทธ๊ฒ์ ์๋๋คโ`, `โ์ ํ๋คโ`โ`โํ์ง ์์๋คโ`), ๋ณต์ `๋ค` ์ญ์ (`โ์ ์๋ค์ดโ`โ`โ์ ์๊ฐโ`), ๊ตฌ์ด ์กฐ์ฌ ๋ฐ๊พธ๊ธฐ(`โ๋์ํํ
โ`โ`โ๋์์๊ฒโ`), ์ฝ์ดยท์ค์๋ง ํ๊ธฐ, ๋ฑ๋งยท์ด์ ์ทจํฅ, ๊ธ์๋ฅผ ๋ฃ๊ณ ๋บ โ ๊ท๋ฒ์ ๋ ๋ซ๋๋ผ๋ ๋๋๋ฆฐ๋ค. ์ธ์ฉ๋ฌธยท์ธ์ฉ ๋ค ์ ์ญ์ , `โ๋ผ๋ฉฐ`ยท`โ๋ผ๊ณ `์ `๋ผ` ์ญ์ ๋ ๋๋๋ฆฐ๋ค.
|
| 15 |
+
- ๋๋ค โ ์ค๋ฅ ๊ต์ : ์๋ฌธ ๊ทธ๋๋ก๋ ๋ฌธ๋งฅ์ ์ ๋ง๋ ๋ช
๋ฐฑํ ์ค๋ฅ๋ฅผ ๊ณ ์น ๊ฒ. ์ฌ์ ์ ์๋ ์คํ์, ํ ๊ธ์๊ฐ ๋ฌ๋ผ ๋ป์ด ์๋ฑํด์ง ๋ฑ๋ง(`โ์๊ฒฌ์ ์๋ ตํ๊ฒ ๋คโ`โ`โ์๋ ดํ๊ฒ ๋คโ`), ์ธ์ฉ ๋ ์ด๋ฏธ๊ฐ ๋ฌธ์ฅ๊ณผ ์ ๋ง๋ ๊ฒ(`โ๋๋ ค์ผ ํ์โ๊ณ `โ`โ๋๋ ค์ผ ํ๋คโ๊ณ `).
|
| 16 |
+
4. **๋ป ์๋ ๋ง ์ญ์ **: ์ด์ ยท๊ตฌยท์ ยท๋ฌธ์ฅยท๊ดํธ ์ค๋ช
ยท์ฌ์ง ์ค๋ช
์ด ์ฌ๋ผ์ง(`์ผ์ฑ์ ์๊ฐ`โ`โ
`). ์งง์ ๋ง๋ ๋ป์ด ์์ผ๋ฉด ๋๋๋ฆฐ๋ค: ์์ ๊ดํ์ด(`์ค๋`ยท`์ง๋`ยท`์ฝ`), ๋ณต์ `๋ค`(`์ด๋ค ์ฌ์`โ`์ด ์ฌ์`), ๊ธฐ๊ฐ `๊ฐ`(`5๋
๊ฐ์`โ`5๋
์`), ๋ ์ง ๋ฒ์์ `์ผ`(`28์ผ~๋ค์ ๋ฌ 2์ผ`), `์ยท๊ตฐ` ๊ฐ์ ๊ฐ์ด๋์ (`์๊ตฐ`์ผ๋ก ๋ถ์ด ๋ป์ด ํ๋ ค์ง), `์์`ยท`์ธ`์ฒ๋ผ ์ง์ฐ๋ฉด ๋น๋ฌธ์ด ๋๋ ๋ง, ์ซ์ ์ฒ ๋จ์ ์ผํ(`152,000์`), ๋์ด ์ฌ์ด ์ผํ. `๊ฐ`๋ฅผ ์ง์ `12๊ตฐ`ยท`20ํญ`์ฒ๋ผ ์์ยท๋ถ๋๋ช
์ผ๋ก ์ฝํ๊ฒ ๋ ๊ฒ๋ ๋๋๋ฆฐ๋ค(`๊ฐ๊ตญ`โ`๊ตญ`์ ๊ด๋ก๋ผ ๋๋ค). (๋ฐ๋ก ์๋ค์ ๋๊ฐ์ ๋ง์ด ๊ฒน์ณ ํ๋๋ฅผ ์ง์ด ๊ฒ์ ๋๋ค.)
|
| 17 |
+
5. **์ถ๊ฐยท๋ป ๋ฐ๊ฟ**: ์๋ฌธ์ ์๋ ์ ๋ณดยท๋ฌธ์ฅยท`(์ฌ์ง)`์ด ๋ถ์, ๋ง์นจํ ์๋ ์ค๊ฐ์ ๋ชฉ ์ค์ `์ด๋ค.` ๊ฐ์ ์์ ์ด๋ฅผ ๋ง๋ถ์, ๋ง๋ ๋ฑ๋ง์ ๋ป์ด ๋ค๋ฅธ ๋ฑ๋ง๋ก ๋ฐ๊ฟ(`์ฐ๊ตฌ๊ฐ๋ฐ๋น๋ฅผ`โ`R&D ๋น์ฉ์`, `์ด๋ฌ`โ`๋ค์ ๋ฌ`, `๋ฐฉ๋ฌธ ๊ณ๊ธฐ`โ`๋ฐฉ๋ฌธ์ฐจ`์ฒ๋ผ ๊ณ๊ธฐ๊ฐ ๋ชฉ์ ์ผ๋ก ๋ฐ๋). ์กฐ์ฌ ๊ต์ฒด๋ ์๋ฌธ์ด ๋ง๊ณ ๋ป์ด ๋ฐ๋๋ฉด ๋๋๋ฆฐ๋ค: ์ฒจ๊ฐ์ `๋`โ`๋`, ์ฌ๋ยท๊ทธ๋ฃน ๋ค `์๊ฒ`โ`์`, ์ฃผ์ด `์ด`โ๋ชฉ์ ์ด `์`๋ก ์์ ์ด ํธ์์ด ๊นจ์ง. (์๋ฌธ ์กฐ์ฌ๊ฐ ํ๋ฆฐ ๊ฒ์ ๊ณ ์น ๊ฒ โ `์ ๋ถ์๊ฒ`โ`์ ๋ถ์`, `ํ์๋ค์ด ์ฑ
์ด`โ`์ฑ
์` โ ์ ๋๋ค.)
|
| 18 |
+
6. **์ธ๋์ด ์ญ๊ต์ **: ํ์ค ์ธ๋์ด ํ๊ธฐ๋ฅผ ๋นํ์ค์ผ๋ก ๋ฐ๊ฟ. ์: `๋ก๋ด`โ`๋ก๋ณดํธ`, `๋์งํธ`โ`๋์งํ`, `๋ฆฌ๋์ญ`โ`๋ฆฌ๋์ฝ`, `์ผ์`โ`์์`(๋์๋ฆฌ), `ํจ๋`โ`ํจ๋ฌ`. (๋นํ์ค์ ํ์ค์ผ๋ก ๊ณ ์น ๊ฒ, ์กฐ์ ์ผ๋ณด ์ฌ์ ์นํ์ ๋๋ค.)
|
| 19 |
+
7. **์ด๋**: ๊ฐ์ ๋ง์ด ํ ๊ณณ์์ ์ง์์ง๊ณ ๋ค๋ฅธ ๊ณณ์ ์๊ธด ์ โ ๋ ๋ฒํธ๋ฅผ ํจ๊ป ๋ธ๋ค.
|
| 20 |
+
8. **ํ๊ธฐ ์ฌ์ ์นํ์ ๋ฌธ๋งฅ ์ค์ ์ฉ**: ๋ชฉ๋ก์๋ ์กฐ์ ์ผ๋ณด ํ๊ธฐ ์ฌ์ ์ ๊ธฐ๊ณ ์นํ(์: `๋ํต๋ น์ค`โ`์ฒญ์๋`, `ํ๊ฒฝ๋ถ`โ`๊ธฐํ์๋์งํ๊ฒฝ๋ถ`, `์ํผ`โ`์ํผ`)๋ ์์ฌ ์๋ค. ์ฌ์ ์นํ์ ์กฐ์ ์ผ๋ณด ํ๊ธฐ ๊ท์น์ด๋ฏ๋ก ๋๋, **๊ณผ๊ฑฐ ์์ ์ ๋น์ ์ด๋ฆ**(์: ์ค์์ด ์ ๋ถ '๋ํต๋ น์ค', ๋ฌธ์ฌ์ธ ์ ๋ถ 'ํ๊ฒฝ๋ถ ๋ธ๋๋ฆฌ์คํธ'), **์ด๋ฏธ ์นํ๋ ์ด๋ฆ์ ๋ ๋ถ์ ๊ฒ**(`๊ธฐํ์๋์ง๊ธฐํ์๋์งํ๊ฒฝ๋ถ`), **๋ฐฉ์กยท์ํยท์ํ์ ๊ณ ์ ์ ๋ชฉ**(์: '์ํผ๋งจ์ด ๋์์๋ค') ์์ด๋ฉด ๋๋๋ฆฐ๋ค.
|
| 21 |
+
์ธ์ฉ๋ฌธ ๋ฐ ์ผ๋ฐ ๋ฑ๋ง์ ๋ง์ถค๋ฒยท์กฐ์ฌยท์ด๋ฏธ ๊ต์ ์ด ์ ๋งคํ๋ฉด ๋์ด๋ผ. ๋ฐ์ดํ ์ ๋งํฌ ์ ๋ฆฌยท๊ณ ์ ๋ช
์ฌยท๋ป ์๋ ๋ง ์ญ์ ๊ฐ ์ ๋งคํ๋ฉด ์๋ฌธ ์ชฝ(๋๋๋ฆผ)์ ํํ๋ค. ๋๋๋ฆด ๋ฒํธ๋ ๋ณดํต ๋ชฉ๋ก์ 15% ์ดํ์ด๊ณ 0๊ฐ๋ ํํ๋ค.
|
| 22 |
+
|
| 23 |
+
## (B) ๊ธฐ์ฌ ์ ์ฒด์์ ์๋ก ๊ณ ์น ๊ฒ โ `<๋ณธ๋ฌธ>`(FT ๊ต์ ๋ณธ)์ ์ฒ์๋ถํฐ ๋๊น์ง ํ์ผ๋ฉฐ ์๋ ํ์ ์ ํ๋์ฉ ํ์ธํ๋ค
|
| 24 |
+
**FT ๊ฐ ๊ณ ์น ์๋ฆฌ๋ ๋ค์ ๊ณ ์น์ง ์๋๋ค**: `<FT_๋ณ๊ฒฝ_๋ชฉ๋ก>` ์ ใ๋ฐ๋ ๋คใ ๊ตฌ๊ฐ(๋๋๋ฆฌ์ง ์์ ๋ฒํธ)์ FT ๊ฐ ์ด๋ฏธ ํ๋จํ ์๋ฆฌ๋ค. ๊ทธ ๊ตฌ๊ฐ์ `original` ์ ๋ฃ์ง ์๋๋ค. ๊ทธ ์๋ฆฌ๊ฐ ํ๋ ธ๋ค๊ณ ๋ณด๋ฉด (A)์์ ๋๋๋ฆด์ง๋ง ์ ํ๋ค.
|
| 25 |
+
1. **๊ธฐ์ฌ ์ ๋ถ์ผ์น**(inconsistency): ๊ฐ์ ์ธ๋ฌผยท๊ธฐ๊ดยท์ฌ๊ฑด์ ์ด๋ฆยท์งํจยท์ซ์ยท๋ ์ง๊ฐ ๊ธฐ์ฌ ์ ๋ค๋ฅธ ๊ณณ(์ ๋ชฉยท์ฌ์ง ์ค๋ช
ํฌํจ)๊ณผ ์ด๊ธ๋จ. ๋ ์ฌ๋ฌ ๋ฒ ๋์ค๊ฑฐ๋ ํ๋ค์ยท์ ์ ๋ช
์นญ์ด ์ ํ ์ชฝ์ ๋ง์ถ๋ค.
|
| 26 |
+
2. **์ด๋ฆยท๋ช
์นญยท์ฌ์ค**(fact): ๊ณ ์ ๋ช
์ฌ๊ฐ ๋์ฌ ๋๋ง๋ค ๊ธ์๋ฅผ ํ์ธํ๋ค.
|
| 27 |
+
- ์ธ๊ตญ ์ง๋ช
ยท์ธ๋ช
ยท๋จ์ฒดยท์ ๋์ ํ์ค ํ๊ธ ํ๊ธฐ์์ ๊ธ์๊ฐ ํ๋ฆผ. ์: `์ค์นธ๋๋๋น์`โ`์ค์นธ๋๋๋น์`, `๋ฒ ๋ค์ฃผ์๋ผ`โ`๋ฒ ๋ค์์๋ผ`.
|
| 28 |
+
- ํ์ฌยท์์์ยท์ํยท๋ฒ๋ฅ ยท๊ตญ์ ๊ธฐ๊ตฌ์ ๊ณต์ ๋ช
์นญ์์ ๊ธ์๊ฐ ๋น ์ง๊ฑฐ๋ ๋ฐ๋. ์: `๊ตญ์ ํตํ๊ธฐ๊ตฌ(IMF)`โ`๊ตญ์ ํตํ๊ธฐ๊ธ(IMF)`.
|
| 29 |
+
- ์ฝ์ด์ ํ์ด๊ฐ ์๋ก ๋ฐ๋์๊ฑฐ๋ ์ง์ด ์ด๊ธ๋จ. ์: `๊ตญ๋ฏผ์ด์๋(GDP)`โ`๊ตญ๋ฏผ์ด์๋(GNI)`.
|
| 30 |
+
- ๋๋ฆฌ ์๋ ค์ง ์ธ๋ฌผ ์ด๋ฆ์ ๊ธ์ ์์ยทํ ๊ธ์ ์ค๊ธฐ, ์ญ์ฌ์ ์ฌ๊ฑด์ ์ฐ๋. ์: `ํ๊ตญ์ ์์ด ์ผ์ด๋ 1953๋
`โ`1950๋
`.
|
| 31 |
+
๋จ, ํ์ฌยท์์ ยท์์คยท์ํ์ ์ํธ๋ ์ธ๋์ด ํ๊ธฐ๋ฒ๊ณผ ๋ฌ๋ผ๋ ๊ณต์ ํ๊ธฐ์ผ ์ ์์ผ๋ ๋๋ค. ์ต๊ทผ ์ธ์ฌยท์ง์ฑ
ยท์์น๋ ๋ค ์ง์์ด ๋ก์์ ์ ์์ผ๋ ๊ธฐ์ฌ ์ ๊ทผ๊ฑฐ๊ฐ ์์ผ๋ฉด ๋๋ค.
|
| 32 |
+
3. **๊ฒ์ฐ**(calc): ๊ธฐ์ฌ ์ ์ซ์๋ผ๋ฆฌ ์ด์ด์ง๋ ๊ฐ์ ์ค์ ๋ก ๊ณ์ฐํ๋ค โ ๋น์จยท์ฆ๊ฐ๋ฅ ยทํฉ๊ณยท์ฐจ์ด, ๋ ๋น์จ์ ์ฐจ(%ํฌ์ธํธ), ๊ธ์กยท๋จ์ ํ์ฐ์ ์๋ฆฟ์(10๋ฐฐยท100๋ฐฐ ์ด๊ธ๋จ), ๋ ์ง์ ์์ผ, ๊ธฐ์ฌ ์ ๋ ์์ ์ผ๋ก ์ ํด์ง๋ ๊ธฐ๊ฐยท๋์ด. `A์์ B๋ก ๋์๋ค`์ฒ๋ผ ๋น๊ตํ๋ ๋ ๊ฐ์ ๋จ์๊ฐ ๋ค๋ฅด๋ฉด(๋ช
โ%) ๊ธฐ์ฌ ์์ ๋ง๋ ๊ฐ์ผ๋ก ๋ง์ถ๋ค. ๊ณ์ฐ์ ๋ ๋ฒ ํด์ ๊ฐ์ ๋๋ง ๊ณ ์น๊ณ , ๊ธฐ์ฌ ๋ฐ ๊ธฐ์ค ์์ (์์ฑ์ผยท'์ฌํด')์ ๊ธฐ๋๋ ๊ฐ์ ๊ณ ์น์ง ์๋๋ค.
|
| 33 |
+
4. **๋ฌธ๋งฅ์ ํ๋ฆฐ ๋ฑ๋ง**(word): ๋์์ด์์ดยท๋น์ทํ ์๋ฆฌยทํ์์ดยท๋์ฌ ํผ๋์ผ๋ก ๋ฌธ๋งฅ์ ์ ๋ง๋ ๋ฑ๋ง. ๋ฌธ์ฅ๋ง๋ค ์์ ์ด๊ฐ ์ฃผ์ดยท๋ชฉ์ ์ด์ ๋ป์ด ๋ง๋์ง ๋ณธ๋ค. ์: `๊ฒฐ์ฌ`โ`๊ฒฐ์ `, `ํ์ฐธ`โ`ํ์ฐฝ`, `์ง์`โ`์งํฅ`, `์ ๋ต์ ๋ง์ถ๋ค`โ`๋งํ๋ค`, `๋์ฒด์ `โ`๋์ฒด์ฌ`.
|
| 34 |
+
5. **๋จ์ ๊ท๋ฒ ์ค๋ฅ**(grammar): FT ๊ฐ ๋์น ์กฐ์ฌยท์ด๋ฏธยทํธ์ยท๋น๋ฌธยท๋ง์ถค๋ฒ ์ค๋ฅ ๊ฐ์ด๋ฐ **ํ๋ ธ๋ค๊ณ ๋จ์ ํ ์ ์๋ ๊ฒ**. ์ซ์ยท์๋ฌธ ๋ค ์กฐ์ฌ๋ ์ฝ๋ ์๋ฆฌ์ ๋ง์ถ๋ค(์: `์ฝ๋ 9์`โ`9๋ฅผ`). ํ๋ ๊ธ์ ์คํ๋ก ๋ณด์ด๋ฉด ๋ฌธ์ฅ์ ๋ค์ ์ฐ์ง ๋ง๊ณ ๋ป์ ๊ฐ์ฅ ๊ฐ๊น์ด ํํ๋ก ์ต์ํ๋ง ๊ณ ์น๋ค.
|
| 35 |
+
๊ณ ์น์ง ๋ง ๊ฒ: ๋์ด์ฐ๊ธฐ๋ง์ ์ฐจ์ด, ๋ฐ์ดํ ๋ฐฉํฅ(๋ค ๊ท์น์ด ๋งก๋๋ค), ๋ฌธ์ฒดยท์ด์ยทํํ ์ทจํฅ, ์ธ์ฉ๋ฌธ ์ ๋งํฌ, ๋ฌธ๋จยท์ค๋ฐ๊ฟ, **`evidence` ์ ๊ธฐ์ฌ ์ ๋ค๋ฅธ ๊ณณยท๊ณ์ฐยทํ์คํ ์์ ์ค ํ๋๋ก ๊ทผ๊ฑฐ๋ฅผ ๋ ์ ์๋ ์ซ์ยท์ด๋ฆยท๋ฑ๋ง ๋ณ๊ฒฝ**.
|
| 36 |
+
|
| 37 |
+
## ์ถ๋ ฅ โ JSON ํ ๊ฐ์ฒด๋ง, ๋ค๋ฅธ ๋งยท์ฝ๋ํ์ค ์์ด
|
| 38 |
+
{"revert_why": {"<๋ฒํธ>": "์ซ์|๊ณ ์ ๋ช
์ฌ|์ธ์ฉ|์ญ์ |์ถ๊ฐ|์ธ๋์ด|์ด๋|์ฌ์ "}, "revert": [<๋ฒํธ>...], "corrections": [{"original": "<๋ณธ๋ฌธ์ ๊ธ์ ๊ทธ๋๋ก ์๋ 2~30์ ๊ตฌ๊ฐ>", "corrected": "<๊ทธ ๊ตฌ๊ฐ์ ๊ณ ์น ๊ฒ>", "category": "inconsistency|fact|calc|word|grammar", "evidence": "<๊ทผ๊ฑฐ ํ ์ค>"}]}
|
| 39 |
+
- `revert` ์ ๋ฒํธ๋ `revert_why` ์ ์ฌ์ ๋ฅผ ์ ์ ๋ฒํธ์ ๊ฐ๋ค. `<FT_๋ณ๊ฒฝ_๋ชฉ๋ก>` ์ ์๋ ๋ฒํธ๋ง ์ด๋ค.
|
| 40 |
+
- `original` ์ `<๋ณธ๋ฌธ>` ์์ ๊ทธ๋๋ก ๋ณต์ฌํ๊ณ , ๋ณธ๋ฌธ์์ ํ ๋ฒ๋ง ๋์ค๋๋ก ์๋ค ๊ธ์๋ฅผ ๋ฃ๋๋ค. `original` ๊ณผ `corrected` ๋ ๋ฌ๋ผ์ผ ํ๋ค. ์ต๋ 12๊ฐ.
|
| 41 |
+
- ๋ ๋ค ์์ผ๋ฉด {"revert_why": {}, "revert": [], "corrections": []}.
|
assets/prompts/prompt_exp_261021_ft_hybrid/review_sp4.txt
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
๋๋ ์ ๋ฌธ ๊ต์ด ๋ฐ์คํฌ๋ค. 1์ฐจ ๊ต์ด ๋ชจ๋ธ(FT)์ด ๊ธฐ์ฌ๋ฅผ ์ด๋ฏธ ๊ณ ์ณค๋ค. FT ๋ ๋ง์ถค๋ฒยท์คํ์ยท์กฐ์ฌยท์ด๋ฏธยท๋น๋ฌธ ๊ต์ ์ ์์ฃผ ์ํ๋ค. ๋๋ ์๋ ๋ ์ผ๋ง ํ๋ค. ๋ณธ๋ฌธ์ ๋ค์ ์ฐ์ง ์๋๋ค.
|
| 2 |
+
(A) `<FT_๋ณ๊ฒฝ_๋ชฉ๋ก>` ์์ FT ๊ฐ ์ฌ๊ณ ๋ฅผ ๋ธ ๋ฒํธ๋ง ๊ณจ๋ผ ๋๋๋ฆฐ๋ค. **FT ๋ณ๊ฒฝ์ ๊ธฐ๋ณธ์ผ๋ก ๋ชจ๋ ์ ์งํ๋ค.**
|
| 3 |
+
(B) `<๋ณธ๋ฌธ>` ์์ ๊ธฐ์ฌ ์์ ๊ทผ๊ฑฐ๊ฐ ๊ธ์๋ก ์๋ ์ค๋ฅ๋ง ์ฐพ์ ๊ณ ์น๋ค.
|
| 4 |
+
|
| 5 |
+
## (A) ๋๋๋ฆผ ์ ์ฐจ โ ๋ชฉ๋ก์ ๊ฐ ๋ฒํธ๋ง๋ค 1โ5 ์์๋ก ํ์ธํ๋ค
|
| 6 |
+
๋ชฉ๋ก ํ ์ค์ `#๋ฒํธ โฆ์๋ฌธ๋งฅใ๋ฐ๋๊ธฐ ์ ใโใ๋ฐ๋ ๋คใ๋ท๋ฌธ๋งฅโฆ` ์ด๋ค(โ
=๋น ๋ฌธ์์ด, โ=์ค๋ฐ๊ฟ).
|
| 7 |
+
1. ์ค๋ฐ๊ฟ(โ)ยท๋ฌธ์ฅ๋ถํธยท๋ฐ์ดํ ๋ชจ์ยท๋์ด์ฐ๊ธฐ๋ง ๋ฐ๋์์ผ๋ฉด โ ์ ์ง. ๋ค์ ๋ฒํธ๋ก.
|
| 8 |
+
2. ์ซ์ ํ์ธ: ใ๋ฐ๋๊ธฐ ์ ใ๊ณผ ใ๋ฐ๋ ๋คใ์ ์๋ผ๋น์ ์ซ์๋ฅผ ์ฐจ๋ก๋ก ์ ์ด ๋น๊ตํ๋ค. ์ซ์๊ฐ ๋ค๋ฅด๋ฉด(์: `2025๋
`โ`2022๋
`, `30๊ณณ`โ`28๊ณณ`, `7๋ฒ์งธ`โ`6๋ฒ์งธ`) โ ๋๋๋ฆผ(์ซ์). ๋จ ๋ค์์ ์ ์ง: ์ซ์๊ฐ ๊ฐ๊ณ ๋จ์ ๊ธ์(๋งยท์ตยท์กฐยท์)๋ง ์ ๋ฆฌ๋จ, `19987๋
`โ`1987๋
`์ฒ๋ผ ๊ฒน์น ์๋ฆฟ์ ์คํ ์ ๋ฆฌ, ๋น์จ ๊ฐ์ด ๋ฐ๋์๋๋ฐ ๊ธฐ์ฌ ์ ๋ ์ซ์๋ก ์ง์ ๋๋ ๋ณด๋ ใ๋ฐ๋ ๋คใ ๊ฐ์ด ๋์ด.
|
| 9 |
+
3. ์ด๋ฆ ํ์ธ: ์ฌ๋ยท๊ธฐ๊ดยท์ง๋ช
ยทํ์ฌยท์ํ ์ด๋ฆ์ด ๋ฐ๋์์ผ๋ฉด, `<๋ณธ๋ฌธ>` ์ ๊ฒ์ํ๋ค.
|
| 10 |
+
- ใ๋ฐ๋๊ธฐ ์ ใ ์ด๋ฆ์ด ๋ณธ๋ฌธ ๋ค๋ฅธ ๊ณณ์ ๊ทธ๋๋ก ๋์ค๋ฉด โ ๋๋๋ฆผ(๊ณ ์ ๋ช
์ฌ). ์: ๋ณธ๋ฌธ์ 'ํ์'์ด ์ฌ๋ฌ ๋ฒ ์๋๋ฐ ํ ๊ณณ์ด `ํ์`โ`ํ์ธ`.
|
| 11 |
+
- ์์ ํ ๋ค๋ฅธ ์ด๋ฆ(๋ ๊ธ์ ์ด์ ๋ค๋ฆ)์ด๋ ๋ค๋ฅธ ์งํจ์ผ๋ก ๋ฐ๋์๊ฑฐ๋, ์ด๋ฆยท์งํจ ์์๊ฐ ๋ฐ๋์์ผ๋ฉด โ ๋๋๋ฆผ(๊ณ ์ ๋ช
์ฌ).
|
| 12 |
+
- ๊ทธ ๋ฐ(ํ ๊ธ์ ์คํ ์ ๋ฆฌ, ์ธ๋์ด ํ๊ธฐ ์ ๋ฆฌ `๋๋ ๋`โ`๋๋๋`๋ฅ, ใ๋ฐ๋ ๋คใ ์ด๋ฆ์ด ๋ณธ๋ฌธ ๋ค๋ฅธ ๊ณณ์ ์์) โ ์ ์ง.
|
| 13 |
+
4. ์คํ์ ํ์ธ: ใ๋ฐ๋๊ธฐ ์ ใ์ด ํ๋ฆฐ ๋ง์ด๋ฉด โ **์ ์ง**. ํ๋ฆฐ ๋ง์ด๋ ์ฌ์ ์ ์๋ ๋งยท๊ธ์ ๋น ์งยท๊ธ์ ๊ฒน์นจยทํ๋ฆฐ ์กฐ์ฌยทํ๋ฆฐ ์ด๋ฏธ๋ค. ์: `์ญํ`โ`์ญํ `, `ํ์ฌ์`โ`ํ์ฌ๋ฅผ`, `๋์`โ`๋ผ์`, `ํ์์๋๋ค`โ`ํ์์ต๋๋ค`, `์๋ง๋`โ`์๋ง์`, ๋ฌธ๋งฅ์ ๋ค๋ฅธ ๋ฑ๋ง ์คํ `๊ฒฐ์ฌ ๋๊ธ`โ`๊ฒฐ์ ๋๊ธ`. ์ด๋ฐ ๊ฒ์ ์ธ์ฉ๋ฌธ ์์ด๋ ์ด๋ฆ์ฒ๋ผ ๋ณด์ด๋ ๋๋๋ฆฌ์ง ์๋๋ค.
|
| 14 |
+
5. ใ๋ฐ๋๊ธฐ ์ ใ์ด ๋ง๋ ๋ง์ธ๋ฐ FT ๊ฐ ๋ฐ๊พผ ๊ฒฝ์ฐ๋ง ์๋๋ฅผ ๋ณธ๋ค. ํ๋์ **๋ถ๋ช
ํ** ๋ง์ ๋๋ง ๋๋๋ฆฐ๋ค.
|
| 15 |
+
- ์ธ์ฉ: โ โ ๋ โ โ ์์ ๋ง๋ ๋ง์ ๋ค๋ฅธ ๋งยท์ด๋ฏธ๋ก ๋ฐ๊พธ๊ฑฐ๋ ์ง์. ์: โ์ ๊ฐ ํ ๊ฑธ๋ก ์๋๋คโ์ `๊ฑธ๋ก`โ`๊ฒ์ผ๋ก`, โ์ฌ๋๋ค์ด ๋ง์์ฃ โ์ `๋ค์ด`โ`์ด`. โ ๋๋๋ฆผ(์ธ์ฉ)
|
| 16 |
+
- ์ญ์ : ๋ป ์๋ ์ด์ ยท๊ตฌยท๋ฌธ์ฅยท๊ดํธ ์ค๋ช
์ด โ
์ด ๋จ. ์: `์ค๋ 15์ผ`์ `์ค๋`โโ
, `ํ๋์ฐจ๊ฐ`โโ
. ๋ฐ๋ก ์์ ๊ฐ์ ๋ง์ด ๊ฒน์ณ ํ๋๋ฅผ ์ง์ด ๊ฒ์ ์ ์ง. โ ๋๋๋ฆผ(์ญ์ )
|
| 17 |
+
- ์ถ๊ฐยท๋ฐ๊ฟ ์ฐ๊ธฐ: ์๋ฌธ์ ์๋ ์ ๋ณด๊ฐ ๋ถ์, ๋๋ ๋ง๋ ๋ฑ๋ง์ด ๋ป์ด ๋ค๋ฅธ ๋ฑ๋ง๋ก ๋ฐ๋. ์: `์ง๋์ฃผ`โ`์ด๋ฒ ์ฃผ`, `์์ถ์ก์ด`โ`์์
์ก์ด`, `์ธ๊ฑด๋น๋ฅผ`โ`HR ๋น์ฉ์`. ๊ฐ์ ๋ป์ ๋ค๋ฅธ ํํ(`์ด์ธ`โ`์ด ๋ฐ์`, `๋์ด`โ`๋ผ`)์ ์ ์ง. โ ๋๋๋ฆผ(์ถ๊ฐ)
|
| 18 |
+
- ์ด๋: ๊ฐ์ ๋ง์ด ํ ๋ฒํธ์์ ์ง์์ง๊ณ ๋ค๋ฅธ ๋ฒํธ์์ ์๊น โ ๋ ๋ฒํธ ๋ชจ๋ ๋๋๋ฆผ(์ด๋).
|
| 19 |
+
- ์ฌ์ : ํ๊ธฐ ์ฌ์ ๊ธฐ๊ณ ์นํ(`๋ํต๋ น์ค`โ`์ฒญ์๋`, `ํ๊ฒฝ๋ถ`โ`๊ธฐํ์๋์งํ๊ฒฝ๋ถ`, `์ํผ`โ`์ํผ` ๋ฑ)์ ์กฐ์ ์ผ๋ณด ๊ท์น์ด๋ผ ์ ์งํ๋ค. ๋จ ๊ณผ๊ฑฐ ์ ๋ถยท๊ณผ๊ฑฐ ์ฌ๊ฑด์ ๋น์ ์ด๋ฆ(์: ์ง๋ ์ ๋ถ '๋ํต๋ น์ค' ์ธ์ฌ, ์ ๋ถ์ฒ ์์ ์ฌ๊ฑด๋ช
), ์ด๋ฏธ ์นํ๋ ์ด๋ฆ์ ๋ ๋ถ์(`๊ธฐํ์๋์ง๊ธฐํ์๋์งํ๊ฒฝ๋ถ`), ๋ฐฉ์กยท๋
ธ๋ยท์ํ ๊ณ ์ ์ ๋ชฉ ์์ด๋ฉด โ ๋๋๋ฆผ(์ฌ์ ).
|
| 20 |
+
ํ๋จ์ด ์ ๋งคํ๋ฉด ์ ์งํ๋ค. ๋๋๋ฆด ๋ฒํธ๋ ๊ธฐ์ฌ ํ๋์ ๋ณดํต 0~2๊ฐ๋ค. ๋ชฉ๋ก์ ๋๋ถ๋ถ์ 4๋ฒ(์คํ์ ์ ๋ฆฌ)์ ํด๋นํ๋ ์ ์งํด์ผ ํ๋ค.
|
| 21 |
+
|
| 22 |
+
## (B) ์ ๊ต์ โ ์๋ ๋ค ๊ฐ์ง๋ง, ๊ทผ๊ฑฐ๊ฐ `<๋ณธ๋ฌธ>` ์์ ๊ธ์๋ก ์์ ๋๋ง
|
| 23 |
+
1. inconsistency(๊ธฐ์ฌ ์ ๋ถ์ผ์น): ๊ฐ์ ์ธ๋ฌผยท๊ธฐ๊ดยท์ฌ๊ฑด์ ์ด๋ฆยท์งํจยท์ซ์ยท๋ ์ง๊ฐ ๋ณธ๋ฌธ ๋ ๊ณณ์์ ๋ค๋ฅด๊ฒ ์ ํ. ๋ ์ฌ๋ฌ ๋ฒ ๋์ค๊ฑฐ๋ ํ๋ค์์ด ์ ํ ์ชฝ์ ๋ง์ถฐ ์ ์ ์ชฝ์ ๊ณ ์น๋ค. evidence ์ ๋ ๊ณณ์ ๊ธ์๋ฅผ ๋ชจ๋ ์ด๋ค.
|
| 24 |
+
2. calc(๊ฒ์ฐ): ๋ณธ๋ฌธ์ ๋ ์ซ์๋ก ๊ณ์ฐ๋๋ ๋น์จยท์ฆ๊ฐ๋ฅ ยทํฉ๊ณยท์ฐจ์ด๊ฐ ํ๋ฆผ. ๊ณ์ฐ์ ๋ ๋ฒ ํด์ ๊ฐ์ ๋๋ง ๊ณ ์น๊ณ , evidence ์ ์์ ์ด๋ค(์: "42รท350=12.0%"). ๊ดํธ ์ ํ์ฐ์ก, 'N๋
๋ง'์ฒ๋ผ ์ค๋ ๋ ์ง๊ฐ ํ์ํ ๊ฐ์ ๊ณ ์น์ง ์๋๋ค.
|
| 25 |
+
3. word(๋ฌธ๋งฅ ๋ฑ๋ง): ๋น์ทํ ์๋ฆฌยทํ์์ด ํผ๋์ผ๋ก ๋ฌธ๋งฅ์ ์ ๋ง๋ ๋ฑ๋ง. ์: ๋ ๋ด๋ ๋ฌธ๋งฅ์ `๊ฒฐ์ฌ`โ`๊ฒฐ์ `, `์ง์ํด์ผ ํ ๋ชฉํ`โ`์งํฅํด์ผ ํ ๋ชฉํ`, `์ฒด์ ๋น`โ`์ฒด์ฌ๋น`.
|
| 26 |
+
4. grammar(๋จ์ ์คํ์): FT ๊ฐ ๋์น ์คํ์ยทํ๋ฆฐ ์กฐ์ฌยทํ๋ฆฐ ์ด๋ฏธ ๊ฐ์ด๋ฐ ํ๋ ธ๋ค๊ณ ๋จ์ ํ ์ ์๋ ๊ฒ. ์: `์ ๋ถ์`โ`์ ๋ถ๋`, `ํ๊ฒ ์๋๋ค`โ`ํ๊ฒ ์ต๋๋ค`.
|
| 27 |
+
ํ์ง ๋ง ๊ฒ: ๋ค ์ง์๋ง์ผ๋ก ์ด๋ฆยท์งํจยท์ซ์ยท์ฐ๋ยท๋ ์ง๋ฅผ ๋ฐ๊พธ๊ธฐ(๊ธฐ์ฌ ์ ๊ทผ๊ฑฐ๊ฐ ์์ผ๋ฉด ๋ง๋ค๊ณ ๋ณธ๋ค), ๊ธฐ๊ดยท๋จ์ฒด ์ด๋ฆ ๋ชฉ๋ก์ ๋๋ฆฌ๊ธฐ, ๋์ด์ฐ๊ธฐ, ๋ฌธ์ฒดยท์ด์, ์ธ์ฉ๋ฌธ ๋งํฌ, FT ๋ณ๊ฒฝ์ ๋๋๋ฆฌ๋ ๊ต์ (๋๋๋ฆผ์ (A)๋ก๋ง).
|
| 28 |
+
|
| 29 |
+
### corrections ํญ๋ชฉ ์ฐ๋ ๋ฒ
|
| 30 |
+
- original: `<๋ณธ๋ฌธ>` ์์ ๊ธ์๋ฅผ **๊ทธ๋๋ก ๋ณต์ฌ**ํ๋ค. `<FT_๋ณ๊ฒฝ_๋ชฉ๋ก>` ์ ๊ธ์๋ ๋ค๊ฐ ๊ธฐ์ตํ๋ ๊ธ์๋ฅผ ์ฐ์ง ์๋๋ค. ๊ณ ์น ๋ฑ๋ง๊ณผ ์๋ค ํ๋ ์ด์ , 4~25์. ๋ฌธ์ฅ ์ ์ฒด๋ฅผ ๋ฃ์ง ์๋๋ค. ๋ณธ๋ฌธ์์ ํ ๋ฒ๋ง ๋์ค๋๋ก ํ๋ค.
|
| 31 |
+
- corrected: original ์์ ํ๋ฆฐ ๊ธ์๋ง ๋ฐ๊พผ ๊ฒ. ๋๋จธ์ง ๊ธ์๋ ๊ทธ๋๋ก ๋๋ค.
|
| 32 |
+
- ๋ค ์ฐ๊ณ ํ์ธ: original ๊ณผ corrected ๊ฐ ๊ฐ์ผ๋ฉด ๊ทธ ํญ๋ชฉ์ ์ง์ด๋ค. original ์ด ๋ณธ๋ฌธ์ ์์ผ๋ฉด ์ง์ด๋ค. ์ต๋ 6๊ฐ. ์์ผ๋ฉด ๋น ๋ฐฐ์ด์ด ์ ์์ด๋ค.
|
| 33 |
+
|
| 34 |
+
## ์ถ๋ ฅ โ JSON ํ ๊ฐ์ฒด๋ง, ๋ค๋ฅธ ๋งยท์ฝ๋ํ์ค ์์ด
|
| 35 |
+
{"revert_why": {"<๋ฒํธ>": "์ซ์|๊ณ ์ ๋ช
์ฌ|์ธ์ฉ|์ญ์ |์ถ๊ฐ|์ด๋|์ฌ์ "}, "revert": [<๋ฒํธ>...], "corrections": [{"original": "<๋ณธ๋ฌธ ๋ณต์ฌ ๊ตฌ๊ฐ>", "corrected": "<๊ณ ์น ๊ตฌ๊ฐ>", "category": "inconsistency|calc|word|grammar", "evidence": "<๊ทผ๊ฑฐ ํ ์ค>"}]}
|
| 36 |
+
- `revert` ์ ๋ฒํธ์ `revert_why` ์ ๋ฒํธ๋ ๊ฐ๋ค. ๋ชฉ๋ก์ ์๋ ๋ฒํธ๋ง ์ด๋ค.
|
| 37 |
+
- ์: {"revert_why": {"3": "์ซ์"}, "revert": [3], "corrections": [{"original": "๊น๋ฏผ์ ๋ํ๋ ์ด๋ ", "corrected": "๊น๋ฏผ์ค ๋ํ๋ ์ด๋ ", "category": "inconsistency", "evidence": "๋ณธ๋ฌธ ๋ค๋ฅธ ๋ ๊ณณ์ '๊น๋ฏผ์ค ๋ํ'"}]}
|
| 38 |
+
- ๋ ๋ค ์์ผ๋ฉด {"revert_why": {}, "revert": [], "corrections": []}.
|
assets/resources/compound_whitelist.txt
DELETED
|
@@ -1,3136 +0,0 @@
|
|
| 1 |
-
์ผ์ฑ์ ์ 34
|
| 2 |
-
์์คํน๊ฒ 25
|
| 3 |
-
๋๋ฌธ์ด๋ค 20
|
| 4 |
-
๊ตญ๋ฏผ์ํ 19
|
| 5 |
-
๊ธฐ๋กํ๋ค 15
|
| 6 |
-
์์ค์ํ 15
|
| 7 |
-
ํ๋์ฐจ๊ทธ๋ฃน 14
|
| 8 |
-
๊ณํ์ด๋ค 14
|
| 9 |
-
์๋ ค์ก๋ค 13
|
| 10 |
-
๋ํ๋ฌ๋ค 13
|
| 11 |
-
ํกํ๋ฐ์ฅ 13
|
| 12 |
-
์ฐ๋ฆฌ๋๋ผ 12
|
| 13 |
-
์ฑ๊ฐํฌ๋ฅด 12
|
| 14 |
-
๊ณผ์ ์์ 12
|
| 15 |
-
๋์์ผ๋ก 11
|
| 16 |
-
์์ ์ด๋ค 11
|
| 17 |
-
์ธํฐ๋ท์ํ 11
|
| 18 |
-
ํ๋ก๊ทธ๋จ 10
|
| 19 |
-
์์ํ๋ค 10
|
| 20 |
-
์กฐ๊ตญํ์ ๋น 10
|
| 21 |
-
์ํฉ์์ 9
|
| 22 |
-
์ฃผ์ฅํ๋ค 8
|
| 23 |
-
๋๋ถ์ด๋ฏผ์ฃผ๋น 8
|
| 24 |
-
๊ฒ์ด๋ผ๊ณ 7
|
| 25 |
-
ํ๋ฆฌ์ผ์ธ 7
|
| 26 |
-
์ง๊ณ๋๋ค 7
|
| 27 |
-
์๋ ๋ฅด๊ธฐ 7
|
| 28 |
-
์ฆ๊ฐํ๋ค 7
|
| 29 |
-
๊ตญ์ ๊ฐ์ฌ 7
|
| 30 |
-
๋ถ์์ด๋ค 7
|
| 31 |
-
์ํฉ์ด๋ค 7
|
| 32 |
-
์๋์ ์ผ๋ก 7
|
| 33 |
-
๋ํต๋ น์ค 7
|
| 34 |
-
์ํ์ด์ ์น์ฌ์ 7
|
| 35 |
-
๋
ธ๋ฒจ๋ฌธํ์ 7
|
| 36 |
-
๋ฌธํํ๋ก ๊ฐ 7
|
| 37 |
-
์์๋ฃ์จ 7
|
| 38 |
-
์์ธ์์ 6
|
| 39 |
-
์ค๋งํธํฐ 6
|
| 40 |
-
ํฌ์์๋ค 6
|
| 41 |
-
์ค์ฌ์ผ๋ก 6
|
| 42 |
-
๋ง๋ค์๋ค 6
|
| 43 |
-
๋ฐํํ๋ค 6
|
| 44 |
-
์ฐ๊ธ์ ์ถ 6
|
| 45 |
-
๋ฏธ๋์ดํ
6
|
| 46 |
-
์ ๊ทน์ ์ผ๋ก 6
|
| 47 |
-
์ฌ๋ก ์กฐ์ฌ 6
|
| 48 |
-
ํ ์ค์ฆ๊ถ 6
|
| 49 |
-
๋ ์ง๋์ค 6
|
| 50 |
-
ํ๋๋ฆฌ์ค 6
|
| 51 |
-
์บ์์ค์ํฐ 6
|
| 52 |
-
์ธ๋๋ฒ์ธ 6
|
| 53 |
-
์์
์ด์ต 5
|
| 54 |
-
์ง์ ํ๋ค 5
|
| 55 |
-
๋ง๋ค์ด์ง 5
|
| 56 |
-
์ฌ์ด์์ 5
|
| 57 |
-
์ ๋ง์ด๋ค 5
|
| 58 |
-
์๋ด๋ฒ์ค 5
|
| 59 |
-
ํ๋ ์ด์คํ 5
|
| 60 |
-
์ง๊ธ๊น์ง 5
|
| 61 |
-
์์ฒ์ง์ 5
|
| 62 |
-
์ผ๊ฐ์์ 5
|
| 63 |
-
์์ผ๋์นด๋ 5
|
| 64 |
-
ํฌ์คํธ์์ฆ 5
|
| 65 |
-
ํ๋ฆฌ๋ฐ๊ฒ๋จ 5
|
| 66 |
-
์ดํฌ์คํธ๋กํผ 5
|
| 67 |
-
ํ์ฑ๋์๋ณธ 5
|
| 68 |
-
์ด์ด์ก๋ค 4
|
| 69 |
-
๋ฌธ์จ์๊ฒ 4
|
| 70 |
-
๋ฒํผ์ผ๋ก 4
|
| 71 |
-
๋ณด๋ํ๋ค 4
|
| 72 |
-
์ดํดํ์ง 4
|
| 73 |
-
์ํ์์ 4
|
| 74 |
-
ํ์ด๋ธ๋ฆฌ๋ 4
|
| 75 |
-
๊ธฐ์ค์ผ๋ก 4
|
| 76 |
-
์ค๋ช
ํ๋ค 4
|
| 77 |
-
๋ํ์์ 4
|
| 78 |
-
๋ด๋
๋ถํฐ 4
|
| 79 |
-
์งํํ๋ค 4
|
| 80 |
-
์ฝ๋ ์ค์ค 4
|
| 81 |
-
๋ฐฉ์์ด๋ค 4
|
| 82 |
-
๊ณจํํด๋ฝ 4
|
| 83 |
-
์กฐ์ฌ๋๋ค 4
|
| 84 |
-
ํ์ฌ๋น์ค 4
|
| 85 |
-
์๊ฐ์ด์ก 4
|
| 86 |
-
์ด๋์๋ค 4
|
| 87 |
-
์ถ์งํ๊ณ 4
|
| 88 |
-
์ ํ๋ฒ๋ค 4
|
| 89 |
-
๊ตญ๋ด์์ 4
|
| 90 |
-
๋์ด๋๊ณ 4
|
| 91 |
-
์ ์๋์ง 4
|
| 92 |
-
๋์์ด๊ณต๋ 4
|
| 93 |
-
์ ๋ฌธ๊ฐ๋ค 4
|
| 94 |
-
์์ธ ํ์ด๋จธ๋ณ 4
|
| 95 |
-
์ฐจ์งํ๋ค 4
|
| 96 |
-
๊ณต๋์ฃผํ 4
|
| 97 |
-
์ฌ์ ์ ๊ฒ 4
|
| 98 |
-
์๊ด๊ตฐ์ 4
|
| 99 |
-
์ปค๋ฎค๋ํฐ 4
|
| 100 |
-
์๋ฑํฌ๋กํฐ๋ฆฌ 4
|
| 101 |
-
๋์์ด๋ 4
|
| 102 |
-
๋ทํ๋ฆญ์ค 4
|
| 103 |
-
๋ด์
๋๋ฆฌ๊ทธ 4
|
| 104 |
-
์๋ฉ๋ฆฌ์นธ๋ฆฌ๊ทธ 4
|
| 105 |
-
ํ์ด๊ฑฐ์ค 4
|
| 106 |
-
๋คํธ์ํฌ 4
|
| 107 |
-
์นด์นด์ค๊ฒ์์ฆ 4
|
| 108 |
-
๋ฐฐ๋๋ฏผํด 4
|
| 109 |
-
์ํ
์ค์ 4
|
| 110 |
-
๋ฌด๋์ก๋ค 4
|
| 111 |
-
๋นํธ์ฝ์ธ 4
|
| 112 |
-
์ฅ์คํผ๋๋ 4
|
| 113 |
-
์์ธ๋๋ณ์ 4
|
| 114 |
-
๋ถ์ฐ๋๋ณ์ 4
|
| 115 |
-
์๋ฒ๊ฒฝ๊ธฐ 4
|
| 116 |
-
๋ ์ด์ปค์ค 4
|
| 117 |
-
๋ฌด์๋ณด๋ค 3
|
| 118 |
-
๋ง๊ฐํ๋ค 3
|
| 119 |
-
๋ชจ๊ฑด์คํ ๋ฆฌ 3
|
| 120 |
-
์ดํ์๋ 3
|
| 121 |
-
๊ฐ๋ฅํ๋ค 3
|
| 122 |
-
์
๊ณ์์ 3
|
| 123 |
-
ํฐ์น์คํฌ๋ฆฐ 3
|
| 124 |
-
์ ๊ณตํ๊ณ 3
|
| 125 |
-
์ค๋ฆฌ์ฝ๋ฐธ๋ฆฌ 3
|
| 126 |
-
์คํํธ์
3
|
| 127 |
-
ํ๋ก๋ฆฌ๋ค์ฃผ 3
|
| 128 |
-
๊ธฐ์ํ๊ฒฌ 3
|
| 129 |
-
์ค์ํ๋ค๊ณ 3
|
| 130 |
-
์ด์ค๋ผ์ 3
|
| 131 |
-
๋์ค์ปค๋ฒ๋ฆฌ 3
|
| 132 |
-
๊ฒํ ํ๋ค 3
|
| 133 |
-
๋ฐฉ์์ผ๋ก 3
|
| 134 |
-
์
์ฅ์์ 3
|
| 135 |
-
ํ๋ก์ถ๊ตฌ 3
|
| 136 |
-
๊ฒฝ๊ธฐ์์ 3
|
| 137 |
-
๋ฒ์ด๋์ง 3
|
| 138 |
-
๋ฅด๋
ธ์ฝ๋ฆฌ์ 3
|
| 139 |
-
์ค๋ฌธ์กฐ์ฌ 3
|
| 140 |
-
๊ณต๊ฐํ๋ค 3
|
| 141 |
-
ํ๋จํ๋ค 3
|
| 142 |
-
ํํ์ด์ง 3
|
| 143 |
-
ํ์ฉํ๊ณ 3
|
| 144 |
-
์์๋๋ค 3
|
| 145 |
-
์ฃผ์์์ฅ 3
|
| 146 |
-
ํ๊ตญ์์ 3
|
| 147 |
-
๋ฅ๋ง์ธ๋ 3
|
| 148 |
-
์ฐฝ์
ํ๋ค 3
|
| 149 |
-
์ํํด๋ 3
|
| 150 |
-
์ฒญ์ฝํต์ฅ 3
|
| 151 |
-
์์์ผ๋ก 3
|
| 152 |
-
๋ค์ด๋ดค๋ค 3
|
| 153 |
-
์ ํด์ก๋ค 3
|
| 154 |
-
์ง๊ฒ๋ค๋ฆฌ 3
|
| 155 |
-
์ ํต์
๊ณ 3
|
| 156 |
-
์ ๊ตญ์์ 3
|
| 157 |
-
๊ฒํ ํ๊ณ 3
|
| 158 |
-
๋ฏธ๊ตญ์์ 3
|
| 159 |
-
๊ทธ๋ฌ๋ฉด์ 3
|
| 160 |
-
์ ์ฉ๋๋ค 3
|
| 161 |
-
์ ๊ธฐํ๋ค 3
|
| 162 |
-
์์ฌ์๊ตฌ์ 3
|
| 163 |
-
์ ์น๊ถ์์ 3
|
| 164 |
-
์๋ด๋ํ 3
|
| 165 |
-
ํ์ํ๋ค๊ณ 3
|
| 166 |
-
๊ท์ ํ๊ณ 3
|
| 167 |
-
๋ด๋ถ์์ 3
|
| 168 |
-
์์ด๋์ด 3
|
| 169 |
-
์งํ์ ์ 3
|
| 170 |
-
๋ง์์๋ก 3
|
| 171 |
-
์ง์ฅ์ธ๋ค 3
|
| 172 |
-
์ฒ์์ด๋ค 3
|
| 173 |
-
๊ณ์ํด์ 3
|
| 174 |
-
์์ด์๋ค 3
|
| 175 |
-
์๊ด์์ 3
|
| 176 |
-
ํธ์ฐํน๋ณด 3
|
| 177 |
-
๋์ด๋ฌ๋ค 3
|
| 178 |
-
๋ช
ํ๊ท ์จ 3
|
| 179 |
-
๊ณต๊ฐํ์ง 3
|
| 180 |
-
์ด์ด์ง๊ณ 3
|
| 181 |
-
๋ฌธ์ ์๋ค 3
|
| 182 |
-
์ํด๋ฐฐ์ 3
|
| 183 |
-
๋ง์ด๋์ค 3
|
| 184 |
-
ํ๋๊ฑด์ค 3
|
| 185 |
-
์๋์์ด๊ณ 3
|
| 186 |
-
์ผ๋ง๋ชจํ 3
|
| 187 |
-
์์๋
ธ๋ถ 3
|
| 188 |
-
์๋ฅด๋๋ฐ์ค 3
|
| 189 |
-
์ ๋ฐํฌ์ 3
|
| 190 |
-
๋ฌด์ค์ ์ผ๋ก 3
|
| 191 |
-
์ญ์ ์นํ๋ค 3
|
| 192 |
-
ํด๋ฆฌ๋ธ๋๋ 3
|
| 193 |
-
๊ฐ๋์ธ์ค 3
|
| 194 |
-
๋ํธ๋ก์ดํธ 3
|
| 195 |
-
์ด์น๋ง๋ํต๋ น๊ธฐ๋
๊ด 3
|
| 196 |
-
์์ ๋ช
๋ น 3
|
| 197 |
-
์งํ์ ์ง 3
|
| 198 |
-
๋ธ๋ฃธ๋ฒ๊ทธ 3
|
| 199 |
-
์์ฅ์์ 3
|
| 200 |
-
์์ฅ์ผ๋ก 3
|
| 201 |
-
์์ค์ด๋ค 3
|
| 202 |
-
์ต๊ณ ์ธ๋ฏผํ์ 3
|
| 203 |
-
์ฃผ์ํฌ์์๊ธ 3
|
| 204 |
-
์์ ์ผ๋ก 3
|
| 205 |
-
ํ๊ตญ์๋ฆฌ์ฆ 3
|
| 206 |
-
๋ ์นด๋ก์ด 3
|
| 207 |
-
๋ชจ์ต์ผ๋ก 3
|
| 208 |
-
ํ๊ตญ๋ฌธํ๋ฒ์ญ์ 3
|
| 209 |
-
๋ฌธํํ๋ก 3
|
| 210 |
-
์์ค์ํ๋ค 3
|
| 211 |
-
์ธํฐ๋ท์ํ๋ค 3
|
| 212 |
-
๊ทธ๋ ๋ค๊ณ 3
|
| 213 |
-
๋ผ์ดํ์นํ 3
|
| 214 |
-
์ค๋ฆฌ์ฌ์ค 3
|
| 215 |
-
๊ณผํ์๋ค 3
|
| 216 |
-
๊ธฐ์์ฌํค 3
|
| 217 |
-
๊ฒ์ด์๋ค 3
|
| 218 |
-
์์ด์์ผ๋ก 3
|
| 219 |
-
๋ง๋ฆฌ์ฅ์ฑ 3
|
| 220 |
-
์ฅํธ์์ค 3
|
| 221 |
-
ํ์ธ๋๋ค 3
|
| 222 |
-
์ค์์๋ฆผ๋ณธ 3
|
| 223 |
-
๋ํ์๋ค 3
|
| 224 |
-
์คํฐ๋กํผ 3
|
| 225 |
-
๋ฐฉ์นจ์ด๋ค 2
|
| 226 |
-
๊ฐ๋ฐํ๊ณ 2
|
| 227 |
-
๋ฐฐํฐ๋ฆฌํฉ 2
|
| 228 |
-
์ง์์ ์ผ๋ก 2
|
| 229 |
-
๋ฐ์ ์์ผ 2
|
| 230 |
-
์ฒ์์ผ๋ก 2
|
| 231 |
-
์กฐ์ ํ๋ค 2
|
| 232 |
-
๊ณต์ํ๋ค 2
|
| 233 |
-
๋ชฉํ์ฃผ๊ฐ 2
|
| 234 |
-
์ ์งํฌ์์ฆ๊ถ 2
|
| 235 |
-
์ด์ด์ง๋ฉฐ 2
|
| 236 |
-
์๊ฐํ์ง 2
|
| 237 |
-
์์ฃผ์ด์ 2
|
| 238 |
-
ํ์
๋๋ค 2
|
| 239 |
-
์ฒด๋ฉ์ผ๋ก 2
|
| 240 |
-
์ฐจ๋์ผ๋ก 2
|
| 241 |
-
์ถ๊ฐ๋๋ค 2
|
| 242 |
-
ํน์ง์ด๋ค 2
|
| 243 |
-
๊ฐ์ ์ ํ 2
|
| 244 |
-
์ ํ์์ 2
|
| 245 |
-
ํดํฌ์ค๋ฐ๊ฒ 2
|
| 246 |
-
๋ด๋์ผ๋ฉฐ 2
|
| 247 |
-
์ ์งํ๊ธฐ 2
|
| 248 |
-
์ผ๋ฐ์ ์ผ๋ก 2
|
| 249 |
-
๋์
๋๋ค 2
|
| 250 |
-
๋๋ก์์ 2
|
| 251 |
-
์กฐ์ฌ์์ 2
|
| 252 |
-
์ ๋๋ผ๊ณ 2
|
| 253 |
-
๊ฑฑ์ ํด์ผ 2
|
| 254 |
-
๋ชป์ง์๊ฒ 2
|
| 255 |
-
ํ๋ก๋ฆฌ๋ค 2
|
| 256 |
-
ํ์ฆ๋ฒ๋ฌ 2
|
| 257 |
-
์ ์ธ์์ 2
|
| 258 |
-
์ฃผ๋ฏผ์๊ฒ 2
|
| 259 |
-
๋์ง๊ธฐ๊ฒ 2
|
| 260 |
-
์ฃผ๋ํ๊ณ 2
|
| 261 |
-
๋ธ๋ ํธ์ 2
|
| 262 |
-
๊ณผ๋ํ๋ค 2
|
| 263 |
-
๋ถ์ํ๋ค 2
|
| 264 |
-
์ถ์ํ๋ค 2
|
| 265 |
-
๋ฒ์ค์ค์์ฐจ๋ก 2
|
| 266 |
-
์ง๋์น๊ฒ 2
|
| 267 |
-
๋์์ด๋ค 2
|
| 268 |
-
์ด์์ผ๋ก 2
|
| 269 |
-
๋จ์ด์ก๋ค 2
|
| 270 |
-
ํฌ๋ฅดํฌ๊ฐ 2
|
| 271 |
-
ํฌ์
ํ๋ค 2
|
| 272 |
-
๋ฝ์๋ด๋ฉฐ 2
|
| 273 |
-
๋ฐ์ด์ฌ๋๋ค 2
|
| 274 |
-
์ฌ์๊ธ๋๋ 2
|
| 275 |
-
ํฌ๋ค์๊ฒ 2
|
| 276 |
-
๋๋ฌ๋๊ธฐ 2
|
| 277 |
-
๋ฐฉํฅ์ผ๋ก 2
|
| 278 |
-
์ ๋ณด์๋ค 2
|
| 279 |
-
๊ตฌ๋งคํ๊ณ 2
|
| 280 |
-
๋ถ์ํ๊ณ 2
|
| 281 |
-
ํ๋ฒ์ฌํ๊ด 2
|
| 282 |
-
์งํ๋์ง 2
|
| 283 |
-
๊ณต๊ฐ๋๋ค 2
|
| 284 |
-
ํ์ดํธํ๋ธ 2
|
| 285 |
-
ํ๋ฃจํ๋ฃจ 2
|
| 286 |
-
๋ด์์์ 2
|
| 287 |
-
์ ๊ตญ์ ์ผ๋ก 2
|
| 288 |
-
์์ํ๋ 2
|
| 289 |
-
ํ์ํ๋ค 2
|
| 290 |
-
์๋
๋ณด๋ค 2
|
| 291 |
-
๋ง์๋๋ฐ 2
|
| 292 |
-
์๋ณธ์์ฅ 2
|
| 293 |
-
ํ์ง๊น์ง 2
|
| 294 |
-
์ ์ํ๋ค๊ณ 2
|
| 295 |
-
๊ฒ์์์ 2
|
| 296 |
-
ํ์ด๋ฌ๋ค 2
|
| 297 |
-
๋ฑ๊ทนํ๋ค 2
|
| 298 |
-
์บ ๋ธ๋ฆฌ์ง 2
|
| 299 |
-
์คํ๋์ค 2
|
| 300 |
-
๋น์ฒจ๋๊ธฐ 2
|
| 301 |
-
๋ฌธํ์ฒด์ก๊ด๊ด๋ถ 2
|
| 302 |
-
์ฑํฌ๋กฑ์ผ๋ก 2
|
| 303 |
-
๋
ธ๋ฒจ์ํ์ 2
|
| 304 |
-
๋
ธ๋ฒจ์์ํ 2
|
| 305 |
-
๊ฒฐ์ ์ ์ธ 2
|
| 306 |
-
์ฐ๊ตฌ์๋ค 2
|
| 307 |
-
๋ฐํ์ก๋ค 2
|
| 308 |
-
์กฐ์ ์ผ๋ณด 2
|
| 309 |
-
์ค๋งํธ์ธ์ปด 2
|
| 310 |
-
๊ณต๊ฐ๋๋ค 2
|
| 311 |
-
์
๋ก๋๋๋ค 2
|
| 312 |
-
์ฐ๊ธ์๋ น 2
|
| 313 |
-
ํ๋ช
์ผ๋ก 2
|
| 314 |
-
๊ธฐ์ค๊ธ๋ฆฌ 2
|
| 315 |
-
์ง์
ํ๋ค 2
|
| 316 |
-
์ค๋
๋๋๊ณค 2
|
| 317 |
-
์ฑํํ๋ค 2
|
| 318 |
-
ํ๋ช
๋ณดํธ 2
|
| 319 |
-
ํฐ๋จ๋ ธ๋ค 2
|
| 320 |
-
์๋ ๋๋น์ 2
|
| 321 |
-
์ด์ด์ง๋ฉด์ 2
|
| 322 |
-
๊ฒฝ์์์ 2
|
| 323 |
-
๋ณด์ด๋ฉด์ 2
|
| 324 |
-
๋ํ๋งํธ 2
|
| 325 |
-
๋ถ์ ์๋ค 2
|
| 326 |
-
์๋น์๋ค 2
|
| 327 |
-
๋ถ์๊ธฐ๋ค 2
|
| 328 |
-
๋ณธ๊ฒฉ์ ์ผ๋ก 2
|
| 329 |
-
์ ์ด๋ค์ 2
|
| 330 |
-
ํ๋์ฐจ์ด์ฆ 2
|
| 331 |
-
๊ธ์ฆํ๋ค 2
|
| 332 |
-
์ฃผ๋ ฅํ๊ณ 2
|
| 333 |
-
๊ณผ์ธ๋น๊ตญ 2
|
| 334 |
-
์ ์ฉํ๋ฉด 2
|
| 335 |
-
ํด์ธ์์ 2
|
| 336 |
-
๊ธฐํ์ฌ์ ๋ถ 2
|
| 337 |
-
์ด์ฉ์๋ค 2
|
| 338 |
-
์ฑ๋ฅ๊ฐ๋ค 2
|
| 339 |
-
๋๋ฌธ์ด๋ผ 2
|
| 340 |
-
์์์ํํ 2
|
| 341 |
-
๋ถ์ผ์์ 2
|
| 342 |
-
์์์์ 2
|
| 343 |
-
์งํํ๋ค 2
|
| 344 |
-
ํ๋ผ์คํฑ 2
|
| 345 |
-
์์์ ๊ธฐ์ฐจ 2
|
| 346 |
-
๊ณต๋์ผ๋ก 2
|
| 347 |
-
ํฌ์ํ๊ณ 2
|
| 348 |
-
์ฆ์์์ 2
|
| 349 |
-
์ฌ๋ชจํ๋ 2
|
| 350 |
-
์ ์ถํ๋ค 2
|
| 351 |
-
๊ฒฐ์ ํ๋ค 2
|
| 352 |
-
๋ณด๋์๋ฃ 2
|
| 353 |
-
๋ณด๊ดํ๋ 2
|
| 354 |
-
๊ท๋ช
ํ์๋ฉฐ 2
|
| 355 |
-
ํน๊ฒ๋ฒ์ 2
|
| 356 |
-
์ฆ์ธ๊ฐ์ ๋ฒ 2
|
| 357 |
-
์์คํน๊ฒ์ผ๋ก 2
|
| 358 |
-
์์ฌํ๊ฒ 2
|
| 359 |
-
์ฌ๋ฐ์ํ 2
|
| 360 |
-
ํ๋ณดํ๋ ค 2
|
| 361 |
-
์ฒ๋ฆฌํ๋ค 2
|
| 362 |
-
๋ฌด๋ ฅํํ 2
|
| 363 |
-
์ด๋์ด๋ผ 2
|
| 364 |
-
์ฅ๋ดํ๊ธฐ 2
|
| 365 |
-
์ํฉ์์ 2
|
| 366 |
-
๋
ธ๋ฆผ์๋ก 2
|
| 367 |
-
์ฌ๋ฒ๋ฆฌ์คํฌ 2
|
| 368 |
-
์กํ๋ฉด์ 2
|
| 369 |
-
์ ๊ธฐ๋๊ณ 2
|
| 370 |
-
๋ฏผ์ฃผ๋น์ผ๋ก์ 2
|
| 371 |
-
๊ฒฐ์งํ๊ณ 2
|
| 372 |
-
๋์ด์ฌ๋ ค์ผ 2
|
| 373 |
-
๋ฐ๋ณต๋๋ฉฐ 2
|
| 374 |
-
์์คํน๊ฒ๋ฒ 2
|
| 375 |
-
ํ๋จํ๊ฑฐ๋ 2
|
| 376 |
-
๋ฐ์ํ๋ค 2
|
| 377 |
-
๋ฒ์ํ์ ์ฒ 2
|
| 378 |
-
๋ํ๋ณํํ์ฅ 2
|
| 379 |
-
์ถ์ฒํ๋๋ก 2
|
| 380 |
-
๋ฐ์ํ๋ 2
|
| 381 |
-
๋ช
์ํ๋ 2
|
| 382 |
-
๊ฑฐ๋ก ๋๋ค 2
|
| 383 |
-
๋ง๋ฅ๋จ๋ฆด 2
|
| 384 |
-
์ ์งํ๊ณ 2
|
| 385 |
-
์ฐ๊ตฌ๊ฐ๋ฐ 2
|
| 386 |
-
์ฌ๊ฑด์์ 2
|
| 387 |
-
์ฐ๊ตฌ์์ 2
|
| 388 |
-
์ ๋ต์ผ๋ก 2
|
| 389 |
-
๊ฐ์กฐํ๋ค 2
|
| 390 |
-
๊ธฐ๋ฐ์ผ๋ก 2
|
| 391 |
-
์ง์ํ๊ณ 2
|
| 392 |
-
์น์ดํด์ง๋ฉด์ 2
|
| 393 |
-
๊ธ์ฆํ๊ณ 2
|
| 394 |
-
์์ง๋์ด 2
|
| 395 |
-
๊ตฌ์ถํ๊ณ 2
|
| 396 |
-
๊ธฐ๋กํ๋๋ฐ 2
|
| 397 |
-
ํด๋นํ๋ค 2
|
| 398 |
-
ํฐ๋ฐ์ธํ 2
|
| 399 |
-
ํ๋ก์ ํธ 2
|
| 400 |
-
ํ์ง๊ด๋ฆฌ๋จ 2
|
| 401 |
-
์์น์ ์ผ๋ก 2
|
| 402 |
-
๋ํ์
์ฒด 2
|
| 403 |
-
๊ฑด์ค์ฌ๋ค 2
|
| 404 |
-
๋ชปํ๋๋ก 2
|
| 405 |
-
์ ์ํ์ 2
|
| 406 |
-
์ธ๊ณ์์ 2
|
| 407 |
-
๊ตญ์ ์ฌํ 2
|
| 408 |
-
์ด์ ์์ 2
|
| 409 |
-
๋ฆฌ์ผ๋ฏธํฐ 2
|
| 410 |
-
์ ๊ฑฐ์์ 2
|
| 411 |
-
์๊ด์ผ๋ก 2
|
| 412 |
-
๋์๋ค๋๋ฉฐ 2
|
| 413 |
-
์ฌ์ด์์ 2
|
| 414 |
-
๋น์ ๋๋ 2
|
| 415 |
-
์กธ์
ํ๋ค 2
|
| 416 |
-
์ง์คํธ์ฐ 2
|
| 417 |
-
ํญ๋ฐ์ ์ผ๋ก 2
|
| 418 |
-
์์๋ด๊ธฐ 2
|
| 419 |
-
๋์ด๋์ง 2
|
| 420 |
-
๋ด๋ค๋ดค๋ค 2
|
| 421 |
-
๊ฐํ์ ๋น 2
|
| 422 |
-
๊ณผ์ํ๊ณ 2
|
| 423 |
-
๊ตญํ์์ 2
|
| 424 |
-
๋ณด๊ถ์ ๊ฑฐ 2
|
| 425 |
-
์
์ฅ์ด๋ค 2
|
| 426 |
-
์ ์น์ธ๋ค 2
|
| 427 |
-
๋ค์ด๊ฐ๋ค 2
|
| 428 |
-
๋ง๋ค์ด์ก๋ค 2
|
| 429 |
-
์์๋ค๊ณ 2
|
| 430 |
-
์๋ํ๊ฑฐ๋ 2
|
| 431 |
-
๋๋ถ๊น์น 2
|
| 432 |
-
๊ตํต์ฌ๊ณ 2
|
| 433 |
-
๊ณ ๊ฐ์ฐจ๋ 2
|
| 434 |
-
์ ํ์ผ๋ก 2
|
| 435 |
-
์ธํ
๋ฆฌ์ด 2
|
| 436 |
-
๊ตญ๋ด์์ 2
|
| 437 |
-
์ค์ํ๊ฒ 2
|
| 438 |
-
์ฌ๋ฌ๋ชจ๋ก 2
|
| 439 |
-
์ ํ๋ฒํธ 2
|
| 440 |
-
์๋์ ์ธ 2
|
| 441 |
-
๋ช
์จ์๊ฒ 2
|
| 442 |
-
์ฐจ๊ทผ์ฐจ๊ทผ 2
|
| 443 |
-
ํ์ต๋๋ค 2
|
| 444 |
-
๋ฆฌ๋๋ผ๋ฉด 2
|
| 445 |
-
๋ฒ์์์ 2
|
| 446 |
-
์์ ์ด๋ผ๊ณ 2
|
| 447 |
-
ํ๋ผ์์ค 2
|
| 448 |
-
ํ๋ฝํ๋ค 2
|
| 449 |
-
ํ๊ตญ๊ฑฐ๋์ 2
|
| 450 |
-
์ค์ํ์ฃผ 2
|
| 451 |
-
ํ๋ก๋ฐฐ๊ตฌ 2
|
| 452 |
-
๋ฒ์ด๋ฌ๋ค 2
|
| 453 |
-
๋์ ์ผ๋ก 2
|
| 454 |
-
๋ฐ๋์๋ค 2
|
| 455 |
-
์์ฆ๋ถํฐ 2
|
| 456 |
-
์ ๊ท๋ฆฌ๊ทธ 2
|
| 457 |
-
ํ์ดํ๋ 2
|
| 458 |
-
๋ฉ์ด์ ๋ฆฌ๊ทธ 2
|
| 459 |
-
์๋ฐฉ์์ 2
|
| 460 |
-
์ ์ํ๋ค 2
|
| 461 |
-
์๊ฐ์ด์๋ค 2
|
| 462 |
-
๋ค์ง์๋ค 2
|
| 463 |
-
๋ค๋ฅด๋น์ 2
|
| 464 |
-
ํ๋ผ๋ธํผ์ 2
|
| 465 |
-
๋ธ๋ฃจ์ด์ค 2
|
| 466 |
-
๋ํ๋ฏผ๊ตญ 2
|
| 467 |
-
์์จ์ํํธ 2
|
| 468 |
-
๊ธฐ๋กํ๋ฉฐ 2
|
| 469 |
-
์ด์ด๊ฐ๋ค 2
|
| 470 |
-
์๊ณ ๋ฆฌ์ฆ 2
|
| 471 |
-
์
๊ณ์์ 2
|
| 472 |
-
์ง๋ํด์ 2
|
| 473 |
-
๋
ผ์ํ๊ธฐ 2
|
| 474 |
-
์๊ณ ํ๋ค 2
|
| 475 |
-
๊ตญ๋ฌด์์์ฅ 2
|
| 476 |
-
๊ณต๊ฐ์ ์ผ๋ก 2
|
| 477 |
-
์ฆ๊ถํฌ์์๊ธ 2
|
| 478 |
-
๋จํธ์ด๋ 2
|
| 479 |
-
ํด๋์ ํ 2
|
| 480 |
-
ํ๋ก์ผ๊ตฌ 2
|
| 481 |
-
์์์์ 2
|
| 482 |
-
์ ์ํ๋ก 2
|
| 483 |
-
์๋ฆฌ์์ด์ 2
|
| 484 |
-
์์์๋ํญ๊ณต 2
|
| 485 |
-
๊ฐ๋ฆฌํจ๋ค 2
|
| 486 |
-
์ง์คํ๋ค 2
|
| 487 |
-
๋ํ๋ฐฐ๋๋ฏผํดํํ 2
|
| 488 |
-
์ฒ์ํ์ด 2
|
| 489 |
-
์ํ์ด๋ผ 2
|
| 490 |
-
ํ๊ฐํ๋ค 2
|
| 491 |
-
์์ค์ผ๋ก 2
|
| 492 |
-
๋์ค๋ฌธํ 2
|
| 493 |
-
์ ์ฉ๋์ถ 2
|
| 494 |
-
์ฃผํ๋ด๋ณด๋์ถ 2
|
| 495 |
-
์ฒ์ฐ๊ฐ์ค 2
|
| 496 |
-
๋ถ๋ฐ์ค๋ฆฌ๊ฐ 2
|
| 497 |
-
๋ฏผ์ฃผ์ฃผ์ 2
|
| 498 |
-
์์๋๋ค 2
|
| 499 |
-
๋ฏธ์ธ์ ๋ฅ 2
|
| 500 |
-
์ฑํผ์ธ์ญ 2
|
| 501 |
-
์๋์๋ฆฌ์ฆ 2
|
| 502 |
-
์นด๋ฅผ๋ก์ค 2
|
| 503 |
-
์์คํ
๋ฒ ์ฆ 2
|
| 504 |
-
๋ ๋์ญ์ค 2
|
| 505 |
-
ํฐ๋จ๋ฆฌ๋ฉฐ 2
|
| 506 |
-
๊ด์ ์์ 2
|
| 507 |
-
๋์์ฐ๋ค 2
|
| 508 |
-
๋๋ผํ๋ผ 2
|
| 509 |
-
์ธํฐ๋ด์
๋ 2
|
| 510 |
-
๋งจ๋ถ์ปค์ 2
|
| 511 |
-
๋ฉ๋์น์ 2
|
| 512 |
-
๋ง๋ ์ด์์ 2
|
| 513 |
-
๋ธ๋ฃจ๋์ด 2
|
| 514 |
-
์ธ๊ณ์ ์ธ 2
|
| 515 |
-
ํฌ์ํด์ผ 2
|
| 516 |
-
์ฆ๊ฐํ๋ค 2
|
| 517 |
-
๋์์ง๊ณ 2
|
| 518 |
-
์น์๋ฒ์ง 2
|
| 519 |
-
๊ณต๋ถํด์ผ 2
|
| 520 |
-
ํ์ดํธ์ง๋ก 2
|
| 521 |
-
๋ฟ๋ฆฌ์ณค๋ค 2
|
| 522 |
-
์ฐ์นํ๊ณ 2
|
| 523 |
-
๋๋ผ์ด๋ธ์ท 2
|
| 524 |
-
์งํค๋๋ 2
|
| 525 |
-
ํด๊ฒฐํ๋ค 2
|
| 526 |
-
์ก์๋น๊ฒจ 2
|
| 527 |
-
ํ๊ท ์์ฑ
์ 2
|
| 528 |
-
๊ฐ์ ธ์๋ค 2
|
| 529 |
-
๊ฐ๋ฅด์์ 2
|
| 530 |
-
์ํ๋์์ค์ฝ 2
|
| 531 |
-
์ ๊ท์์ฆ 2
|
| 532 |
-
์ ํ๋ํ 2
|
| 533 |
-
๋
์ผ์ด์ ์๋ฒ์์ํ 2
|
| 534 |
-
ํ๋ค๋๋ก 2
|
| 535 |
-
์๋ฌ๋ ธ๋ค 2
|
| 536 |
-
๋์ฝ๋ฐ๋ฅด 2
|
| 537 |
-
์ ๋ฅํ๊ฒ 2
|
| 538 |
-
์ค์นํ๋ค 2
|
| 539 |
-
๋น์ ๋๋ฉฐ 2
|
| 540 |
-
์ด์๋ฌธํ์ 2
|
| 541 |
-
๊ด์ฃผ๋ฏผ์ฃผํ์ด๋ 2
|
| 542 |
-
๋ฌธ์ฅ์ผ๋ก 2
|
| 543 |
-
ํธ๋ผ์ฐ๋ง 2
|
| 544 |
-
๋น์์ค์ฅ 2
|
| 545 |
-
๋ถ์ฐ์์ 2
|
| 546 |
-
ํ๋๊ฐ๋ น 2
|
| 547 |
-
์ฑ๊ณตํ๋ค 2
|
| 548 |
-
์ค๋ง์ค๋ฌ์ด 2
|
| 549 |
-
๋น๊ตํ๋ฉด 2
|
| 550 |
-
์ค์ ์์ผ๋ก 2
|
| 551 |
-
๊ตญ๊ฐ์ ์ฐ์ผ๋ก 2
|
| 552 |
-
๋ถํ์ฐ๊ตฌ์ 2
|
| 553 |
-
ํ๋ถ๋ฏผ๋ค 2
|
| 554 |
-
์ด์ฐธ๋ชจ๋ถ 2
|
| 555 |
-
ํ๋ซํผ๋ค 2
|
| 556 |
-
์ฟ ํก์ด์ธ 2
|
| 557 |
-
์๋ฏธ๋
ธ์ฐ๋ค 2
|
| 558 |
-
์๋ฏธ๋
ธ์ฐ 2
|
| 559 |
-
๊ฐ๋ฐํ๋ค 2
|
| 560 |
-
์ด์ฐํํ์ 2
|
| 561 |
-
ํ์ ๊ธฐ์
2
|
| 562 |
-
ํ๊ท ๊ธฐ์จ 2
|
| 563 |
-
๋จ์ด์ง์ง 2
|
| 564 |
-
์ต๊ณ ๊ธฐ์จ 2
|
| 565 |
-
์งํ์์์ฅ 2
|
| 566 |
-
ํ๋ํ๊ณ 1
|
| 567 |
-
์์ฌ์ ๊ฒ 1
|
| 568 |
-
๋ฌด์๋ณด์ฆ๊ธฐ๊ฐ 1
|
| 569 |
-
๋ฒ์ธ์ฐจ๋ก 1
|
| 570 |
-
์ ๊ธฐ์ฐจ์ 1
|
| 571 |
-
ํต๋ณด๋์ง 1
|
| 572 |
-
๋ฑ๋กํ๋ฉด 1
|
| 573 |
-
์ง์
ํ๊ธฐ 1
|
| 574 |
-
๋ฐ์ํ๋ฉด 1
|
| 575 |
-
ํฌ์
๋๋ค 1
|
| 576 |
-
์๋ฐฉ์ฒญ์์ 1
|
| 577 |
-
์์ํ๊ณ 1
|
| 578 |
-
ํน์๊ตฌ์กฐ๋ 1
|
| 579 |
-
๊ธฐ์ฆํ๋ค 1
|
| 580 |
-
์์์ผ๋ก 1
|
| 581 |
-
์์ฌํ๊ณ 1
|
| 582 |
-
๋ํ์ฃผ์ธ 1
|
| 583 |
-
์ ํํฌ์์ฆ๊ถ 1
|
| 584 |
-
ํด๋น๋์ง 1
|
| 585 |
-
์์ธ๋๊ณ 1
|
| 586 |
-
์๋งค๋ํ์ง 1
|
| 587 |
-
์๋งค์ํ๋ค 1
|
| 588 |
-
๊ณ ๋์ญํญ 1
|
| 589 |
-
๊ทธ๋ ์ด๋ 1
|
| 590 |
-
์ฆ๊ถ์ฌ๋ค 1
|
| 591 |
-
์ง๊ณ๋๋ค๊ณ 1
|
| 592 |
-
ํํํ๋ค 1
|
| 593 |
-
ํ๋์ฐจ์ฆ๊ถ 1
|
| 594 |
-
๋ฐํ์์ 1
|
| 595 |
-
์ง์ผ์ง์ง 1
|
| 596 |
-
๋น๋ฉ๋ชจ๋ฆฌ 1
|
| 597 |
-
๋น์ฉ์ผ๋ก 1
|
| 598 |
-
์ฐ๋ง๊น์ง 1
|
| 599 |
-
๊ทผ์ ํ๋ฉฐ 1
|
| 600 |
-
ํํํ๊ณ 1
|
| 601 |
-
๋๊ธฐ๋ณด๋ค 1
|
| 602 |
-
์์นํ๋๋ก 1
|
| 603 |
-
์ด์
์ด๋ผ 1
|
| 604 |
-
๋ง์ถ๋ฉด์ 1
|
| 605 |
-
๏ฟฝ๏ฟฝ๋นกํด์ง 1
|
| 606 |
-
์์ํ์ง 1
|
| 607 |
-
์ค๋งํธํฐ์ฉ 1
|
| 608 |
-
๊ธฐ๋ํ๊ธฐ 1
|
| 609 |
-
์ด๋ ค์์ก๋ค๊ณ 1
|
| 610 |
-
์ถ๊ฒฉ์ด๋ค 1
|
| 611 |
-
์ฑ๊ณตํ๋ 1
|
| 612 |
-
์คํจํ๋ 1
|
| 613 |
-
์๋น๋์ 1
|
| 614 |
-
ํต๊ณผํ๋ฉด 1
|
| 615 |
-
์ ์๋ฏธํ 1
|
| 616 |
-
๋จ์ด์ง๊ฒ 1
|
| 617 |
-
์ํ์๋ 1
|
| 618 |
-
๋ชจ๋ธ์ด๋ค 1
|
| 619 |
-
๊ฐ์ธ์ ์ผ๋ก 1
|
| 620 |
-
์ฌ์ฉํ๊ธฐ 1
|
| 621 |
-
์๋์ฐจ๋ฑ๋ก์๋ถ 1
|
| 622 |
-
์์ ์๋ 1
|
| 623 |
-
์๋ฐ์ผ๋ก 1
|
| 624 |
-
์ ์ฃผ์์ 1
|
| 625 |
-
์๋ํ๋ค 1
|
| 626 |
-
๋ช
์์๋ค 1
|
| 627 |
-
์ด์ ๋๊ณ 1
|
| 628 |
-
๋ฌธ์จ์์ 1
|
| 629 |
-
๋ํต๋ น์ผ๋ก 1
|
| 630 |
-
์ด์ ๋๋ค 1
|
| 631 |
-
์ด๋ฉด๋๋ก 1
|
| 632 |
-
๋์์๋ค 1
|
| 633 |
-
ํด๋ฐํคํธํ
1
|
| 634 |
-
์ด์ ํ๋ 1
|
| 635 |
-
๋ณ๊ฒฝํ๋ค 1
|
| 636 |
-
๋ค๋ฐ๋ผ์ค๋ 1
|
| 637 |
-
ํผํด์์ธ 1
|
| 638 |
-
ํ์ค์์ฝ์ฌ๋๋ 1
|
| 639 |
-
์์ค์ด์๋ค 1
|
| 640 |
-
์นด๋ฉ๋ผ์ฉ 1
|
| 641 |
-
์ธ์ด์ฃผ๋ฉด 1
|
| 642 |
-
์ถ๊ฐํ๋๋ฐ 1
|
| 643 |
-
์ฌ๋ผ์ก๋ 1
|
| 644 |
-
๋์์ค๊ณ 1
|
| 645 |
-
๋๋์๊ฐ๊ณ 1
|
| 646 |
-
์ ํ๋ง์ 1
|
| 647 |
-
๊ฐ์ ธ์ค๊ณ 1
|
| 648 |
-
๊ธฐ์ฌ์์ 1
|
| 649 |
-
์ธ์ด๋๊ธฐ๋ 1
|
| 650 |
-
๋์คํ๋ ์ด 1
|
| 651 |
-
์ฅ์ฐฉํ๋ 1
|
| 652 |
-
๋ฑ์ฅํ๊ณ 1
|
| 653 |
-
์ฃผ๋ชฉ๋ฐ๊ณ 1
|
| 654 |
-
๋์
ํ๊ฒ ๋ค๊ณ 1
|
| 655 |
-
์ฐจ์ข
์์ 1
|
| 656 |
-
ํ๊ทํ๋ค 1
|
| 657 |
-
์ฌ๋ฆฌ๊ฑฐ๋ 1
|
| 658 |
-
ํค๋ฆฌํฐ์ง 1
|
| 659 |
-
๋ธ๋ ์ดํฌ 1
|
| 660 |
-
์ถ๊ฐ๋๊ณ 1
|
| 661 |
-
์ ๋ณด์ด๋ฉฐ 1
|
| 662 |
-
์ถ๊ฐํ๋ค 1
|
| 663 |
-
์ ์ฅํด๋ 1
|
| 664 |
-
์คํฑ์์น 1
|
| 665 |
-
๋จ์ถํค๋ก 1
|
| 666 |
-
์ฅ์ ์ด๋ค 1
|
| 667 |
-
๋๊ธฐ๊ฒ๋ 1
|
| 668 |
-
๊ฐ์ง๋๊ณ 1
|
| 669 |
-
์ฃผ์ํด์ผ 1
|
| 670 |
-
ํ๋ฉด์์ 1
|
| 671 |
-
ํ๋ก๊ทธ๋จ์ธ 1
|
| 672 |
-
๋ฐ์ผ๋ ค๋ฉด 1
|
| 673 |
-
์ฑ
์์์ธ 1
|
| 674 |
-
์์ด๋ฒ๋ฆฌ 1
|
| 675 |
-
๋ํ์์ค์ 1
|
| 676 |
-
์ ์กฐ์
์ฒด 1
|
| 677 |
-
ํฐ์น์คํฌ๋ฆฐ์ผ๋ก 1
|
| 678 |
-
์ฐ๋ง์ผ๋ก 1
|
| 679 |
-
๋ง์ฐฌ๊ฐ์ง๋ค 1
|
| 680 |
-
๊ฐ์ค๋ ์ธ์ง 1
|
| 681 |
-
๋์ผํ๊ฒ 1
|
| 682 |
-
ํ๋ฌ๋์น๋ฉด 1
|
| 683 |
-
์๋ํ์ง 1
|
| 684 |
-
ํธ๋ ๋์ 1
|
| 685 |
-
๋งฅ๋ฝ์ด๋ค 1
|
| 686 |
-
ํ๋ธ๋ฆฟ์ฉ 1
|
| 687 |
-
์ง๋์ผ๋ก 1
|
| 688 |
-
ํ์ฌํ๊ณ 1
|
| 689 |
-
๊ฐ๋ฅด์น๋ฉฐ 1
|
| 690 |
-
์ฒ์๋ต์ด์ด์ 1
|
| 691 |
-
์์ํ๊ธฐ 1
|
| 692 |
-
์ค๋ช
ํ์ 1
|
| 693 |
-
๊ทธ์ ์์ผ 1
|
| 694 |
-
๋๋์๋ค 1
|
| 695 |
-
์ง์ ๋๋ค 1
|
| 696 |
-
๋ชปํ๋ค๊ณ 1
|
| 697 |
-
์ฝ๋ฏธ๋๊ฐ 1
|
| 698 |
-
๋ฒ์ด์ง๋ค 1
|
| 699 |
-
์ค๋ฌธ์์ 1
|
| 700 |
-
๋๋ตํ๋ค 1
|
| 701 |
-
๊ฑฑ์ ํ๊ธฐ 1
|
| 702 |
-
์ง๋ฌธ์ด๋ 1
|
| 703 |
-
์ดํดํ ๊น 1
|
| 704 |
-
์ง๊ฒฝ์ด๋ผ๊ณ 1
|
| 705 |
-
ํ์
ํ๋ค 1
|
| 706 |
-
์์๋ฃ๊ณ 1
|
| 707 |
-
๋๋์ธ๋ค 1
|
| 708 |
-
ํ์์ด์ด๊ธฐ 1
|
| 709 |
-
์ดํด๋ ฅ์ธ 1
|
| 710 |
-
๋ฉ๋ฆฌํ๊ณ 1
|
| 711 |
-
์ฝ์ผ๋ผ๊ณ 1
|
| 712 |
-
์ถ์์ ์ธ 1
|
| 713 |
-
๋ชจ๋ฅด๊ณ ์ 1
|
| 714 |
-
์ธ์ด์ฒ ํ์์ธ 1
|
| 715 |
-
๋์ด์ง๋ค 1
|
| 716 |
-
์ธ์์ผ๋ก 1
|
| 717 |
-
์ฌ๋งํ๋ค๊ณ 1
|
| 718 |
-
์ ๊ธฐ์์ผ๋ก 1
|
| 719 |
-
์ฝํ๋์ง 1
|
| 720 |
-
๋ฐํด์ผ๋ก 1
|
| 721 |
-
๋์๋ค๊ณ 1
|
| 722 |
-
๋๋ญ๊ฐ์ง 1
|
| 723 |
-
์ถ์ ๋๋ค 1
|
| 724 |
-
๊ตฌ์ถํ๋ค 1
|
| 725 |
-
ํฌํ์์ 1
|
| 726 |
-
๊ตฌ์กฐ๋จ์ฒด 1
|
| 727 |
-
๊ตฌ์กฐํ๋ค 1
|
| 728 |
-
๊ตฌ์กฐ๋์ 1
|
| 729 |
-
๋จ์ด์ก๊ณ 1
|
| 730 |
-
์ฝํ๋๋ค 1
|
| 731 |
-
์ง์ญ์์ 1
|
| 732 |
-
๋์์์ผ๋ก 1
|
| 733 |
-
๋น ์ ธ๋๊ฐ๋ค 1
|
| 734 |
-
๋ดํ์์ 1
|
| 735 |
-
๋ถ์ํ๊ฒ 1
|
| 736 |
-
๋ฌด๋ชจํ๊ณ 1
|
| 737 |
-
๋ฌด์ฑ
์ํ๋ฉฐ 1
|
| 738 |
-
๋นํํ๋ค 1
|
| 739 |
-
ํผ๋จ๋ฆฌ๊ณ 1
|
| 740 |
-
๋น๋ํ๋ฉด์ 1
|
| 741 |
-
๋ด์ค๋ ํฐ 1
|
| 742 |
-
๊ตฌ๋
ํ๊ธฐ 1
|
| 743 |
-
๋์ค๋ฉด์ 1
|
| 744 |
-
๊ธ๋ฑํ๋ค 1
|
| 745 |
-
์๋ถํ
์ฌ์ค์ฐ 1
|
| 746 |
-
๊ฑฐ๋์ผ๋ณด๋ค 1
|
| 747 |
-
์ ๋ ๋ณด๋ค 1
|
| 748 |
-
๋ณด๋ณต์ผ๋ก 1
|
| 749 |
-
๋์๋ฉด์ 1
|
| 750 |
-
๋ถ์ถ๊ฒผ๋ค 1
|
| 751 |
-
์ง์ค๋ฐ๋ 1
|
| 752 |
-
์คํ์ฐ๋
ธ๋ณด 1
|
| 753 |
-
์ ๋๋ฆฌ์คํธ 1
|
| 754 |
-
์ ๋ฆฌํ๋ฉด์ 1
|
| 755 |
-
์ฐ์ ๊ตญ์ธ 1
|
| 756 |
-
์ฌ๊ท์ด๋๋๋ก๋ฒ์ฝ๋ฆฌ์ 1
|
| 757 |
-
์ธ์ ๋์ 1
|
| 758 |
-
ํ๋ฐฉํ๋ค 1
|
| 759 |
-
๋ฆฌํด๋ผ์ธ 1
|
| 760 |
-
์๋น์ทํ์ง 1
|
| 761 |
-
ํ์ฉํ๋ค 1
|
| 762 |
-
์๋ฉ๊ณต๊ฐ 1
|
| 763 |
-
์คํ๋ก๋ 1
|
| 764 |
-
๊ฐ์ ์ด๋ค 1
|
| 765 |
-
์์คํ์
1
|
| 766 |
-
ํธ์ํ๊ฒ 1
|
| 767 |
-
๋
ธ๋ฉด์์ 1
|
| 768 |
-
์์ฌ๊น์ง 1
|
| 769 |
-
์์ ํ๊ฒ 1
|
| 770 |
-
ํ๋งค๋๋ค 1
|
| 771 |
-
๊ฐํธํ๋ค 1
|
| 772 |
-
์ค๊ณต์์ 1
|
| 773 |
-
๋์
ํ๋ฉด์ 1
|
| 774 |
-
์ฌ์ค๊ณํด 1
|
| 775 |
-
์์ธ์๋ฒ์ค์ด์ก์ฌ์
์กฐํฉ 1
|
| 776 |
-
๋
ธ์ ์ฒด๊ณ 1
|
| 777 |
-
๋ฐ์ฃผํ๋ค 1
|
| 778 |
-
์ํํ๋ค 1
|
| 779 |
-
์ ์ค๋๋๋ฐ 1
|
| 780 |
-
๊ทธ๋๋ก๋ผ 1
|
| 781 |
-
์๋ก์ ์ฌ์ 1
|
| 782 |
-
๊ฐํตํ๊ณ 1
|
| 783 |
-
์ฐ์ฅ๋๋ค 1
|
| 784 |
-
์ด๊ณณ์ ๊ณณ 1
|
| 785 |
-
ํต๊ณผํ๋ค 1
|
| 786 |
-
๊ตฌ๋ถ๊ตฌ๋ถํ 1
|
| 787 |
-
๋
ธ์ ์ผ๋ก 1
|
| 788 |
-
๊ณ ๋๊ฐ์ผ์ง๊ตฌ 1
|
| 789 |
-
๊ทธ์๋๋ฐ 1
|
| 790 |
-
ํธ๋ฆฌํด์ง 1
|
| 791 |
-
์์ํ๋ค 1
|
| 792 |
-
์์ธ์๋ด 1
|
| 793 |
-
๋ฐ๋์๋๋ฐ 1
|
| 794 |
-
๋ฐ์ํ์ง 1
|
| 795 |
-
๋ฐฑ์ง์์ 1
|
| 796 |
-
ํจ์จ์ ์ด๋ผ๊ณ 1
|
| 797 |
-
๊ฐ๋ฑ๋๋ค 1
|
| 798 |
-
์์
ํ๋ค 1
|
| 799 |
-
์ฝ๋ฆฐ์น์์ค 1
|
| 800 |
-
์ถ์ ์ผ๋ก 1
|
| 801 |
-
๋ฆฌ๊ทธ์์ 1
|
| 802 |
-
๋์ด์ฌ๋ ธ๋ค 1
|
| 803 |
-
๊ตฌ๋จ์ด๋ผ 1
|
| 804 |
-
๋๋ํ์ง 1
|
| 805 |
-
์คํธ๋ผ์ด์ปค 1
|
| 806 |
-
๋์ ํฌ๋ก 1
|
| 807 |
-
์ฝ๋ฆฌ์์ปต 1
|
| 808 |
-
์ฌ๋ ค๋์๋ค 1
|
| 809 |
-
๋๊ตฌ์์ 1
|
| 810 |
-
๋ ์ ๋๋ค 1
|
| 811 |
-
์ด๋ ต๊ฒ ์ง 1
|
| 812 |
-
์ธ์ง์ผ์๊ฒ 1
|
| 813 |
-
ํ๋ฅดํค์ 1
|
| 814 |
-
์ ์ ํ๋ค 1
|
| 815 |
-
ํ์ฝํ๊ณ 1
|
| 816 |
-
์ฌ์ ํ๋ค 1
|
| 817 |
-
๋ฌด๋์๋ค 1
|
| 818 |
-
ํ์ฉํ๋ฉด์ 1
|
| 819 |
-
์ ์ฒด์ ๋ช
1
|
| 820 |
-
ํ์ฉํด๋ณด๊ฒ ๋ค 1
|
| 821 |
-
ํ๊ณ ๋ค์ด 1
|
| 822 |
-
์ ํํ๊ณ 1
|
| 823 |
-
์ฌ์ญ์ ์น 1
|
| 824 |
-
๋ง๋ฝํ๋ค 1
|
| 825 |
-
์๋์ผ๋ก 1
|
| 826 |
-
๋จ์ด์ง๊ณ 1
|
| 827 |
-
์ด๋ป๊ฒ๋ 1
|
| 828 |
-
๋งค์ง์ํค๋ฉฐ 1
|
| 829 |
-
์ฐ๋ฆฌ์๊ฒ 1
|
| 830 |
-
ํ๋ณตํ๊ฒ 1
|
| 831 |
-
๊ฒฐ์น์ ์ด๋ 1
|
| 832 |
-
๋ํ๋ถํฐ 1
|
| 833 |
-
์ํํ๋ค๊ณ 1
|
| 834 |
-
์นด๋ฉ๋ผ๋ก 1
|
| 835 |
-
์
์ฒด์ ์ผ๋ก 1
|
| 836 |
-
์ฌ๊ตฌ์ฑํ 1
|
| 837 |
-
์ค๋ฐ๋ถํฐ 1
|
| 838 |
-
์ต์ํ๋ค 1
|
| 839 |
-
๊ทน๋ํํ 1
|
| 840 |
-
๋ํ๊น์ง 1
|
| 841 |
-
๊ณ ์ํ๋ค 1
|
| 842 |
-
์ ํ์ ์ผ๋ก 1
|
| 843 |
-
ํ์ฉํ๋ค 1
|
| 844 |
-
์์ ๋ถํฐ 1
|
| 845 |
-
๊ฒฐ์น๊น์ง 1
|
| 846 |
-
๋์ ํ๋ค 1
|
| 847 |
-
์ฌํํํ 1
|
| 848 |
-
์ธ์ ๊น์ง 1
|
| 849 |
-
์ฌ์ฉ๋๋ค 1
|
| 850 |
-
์คํ์์ 1
|
| 851 |
-
์ด์ํ๋ค 1
|
| 852 |
-
์ฝํธ์์ 1
|
| 853 |
-
์ค์น๋์ด 1
|
| 854 |
-
์ฐธ๊ณ ์ฉ์ผ๋ก 1
|
| 855 |
-
์งํํ๊ณ 1
|
| 856 |
-
๋ฆฌ๊ทธ์์ 1
|
| 857 |
-
์์คํ
์ผ๋ก 1
|
| 858 |
-
์คํ์ฌ์ด๋ 1
|
| 859 |
-
์ก์๋ด๊ณ 1
|
| 860 |
-
๋ฆฌํ๋ ์ด 1
|
| 861 |
-
ํ๋ฐ์ด๋ 1
|
| 862 |
-
ํ๋
์์ 1
|
| 863 |
-
์ฌํด๋ถํฐ 1
|
| 864 |
-
์๋น์ค์ผํฐ 1
|
| 865 |
-
๊ฐ์ตํ๊ณ 1
|
| 866 |
-
๋ง์กฑํ๋ค๊ณ 1
|
| 867 |
-
์๋ตํ๊ณ 1
|
| 868 |
-
์๋ตํ๋ค 1
|
| 869 |
-
์ฐจ๋์์ 1
|
| 870 |
-
๋ง์กฑ์ค๋ฌ์ด 1
|
| 871 |
-
๊ธฐ๋ฅ์ผ๋ก 1
|
| 872 |
-
์กฐ์ฑํ๊ธฐ 1
|
| 873 |
-
ํ์ฌํ๋ค 1
|
| 874 |
-
๊ฐ์งํ๊ณ 1
|
| 875 |
-
๋ฐ์ด๋๊ณ 1
|
| 876 |
-
์ฐ์ํ๋ค 1
|
| 877 |
-
์์ค์ด๊ณ 1
|
| 878 |
-
๊ตฌ๊ฐ์์ 1
|
| 879 |
-
๊ณ ์๋๋ก๋ 1
|
| 880 |
-
์ฃผ๋ง๋ง๋ค 1
|
| 881 |
-
๋ฐฉ์กํต์ ์์์ฅ 1
|
| 882 |
-
๋ง๋ฌด๋ฆฌํ๋ฉด์ 1
|
| 883 |
-
ํ์ฌ์์ฅ 1
|
| 884 |
-
์ ์ถํด์ผ 1
|
| 885 |
-
๋๋ฆฝ์ผ๋ก 1
|
| 886 |
-
์ถ์ํด์ผ 1
|
| 887 |
-
์ด๊ฒ ๋ค๊ณ 1
|
| 888 |
-
๋ฐํ๋ฉด์ 1
|
| 889 |
-
ํ๋ฒ์ฌํ์๋ฒ 1
|
| 890 |
-
์์๋ด์ผ 1
|
| 891 |
-
์ ์ถํ์ง 1
|
| 892 |
-
๊ฒํ ํด๋ณด๋ผ 1
|
| 893 |
-
์ถ์ ์ด๋ค 1
|
| 894 |
-
์์
ํ๋ 1
|
| 895 |
-
๋ง๋ฅด์ธ์ 1
|
| 896 |
-
ํฉ๋ฟ๋ฆฌ๊ณ 1
|
| 897 |
-
์ํ์ด๋ค 1
|
| 898 |
-
์๋ฆฌ์ฆ๋ก 1
|
| 899 |
-
์๊ฐ์ผ๋ก 1
|
| 900 |
-
์ ์๋๋ค 1
|
| 901 |
-
๋ฐํ๋๋ค 1
|
| 902 |
-
์ค๋ธ๋ฆฌ์คํธ 1
|
| 903 |
-
์ํํ์ธ 1
|
| 904 |
-
์์ ๊ฐ๋
1
|
| 905 |
-
์งํํ๋ 1
|
| 906 |
-
์ฐจ๋ณ์ด๋ผ๊ณ 1
|
| 907 |
-
์ ํํ์ง 1
|
| 908 |
-
๊ถ๊ณ ํ๋ค 1
|
| 909 |
-
์๋ด์๊ฒ 1
|
| 910 |
-
์ฌ์ฃผ๋ ค๊ณ 1
|
| 911 |
-
๋จ์ฑ์๊ฒ 1
|
| 912 |
-
๊ฑฐ๋ถํ๋ค๊ณ 1
|
| 913 |
-
์ ๊ธฐํ๊ณ 1
|
| 914 |
-
๊ฐ์
์๊ฒฉ 1
|
| 915 |
-
๋ช
์ํ๊ณ 1
|
| 916 |
-
ํ์ด๋ผ๊ณ 1
|
| 917 |
-
์ ํํ๋ค 1
|
| 918 |
-
์ฌ์ฑ์ฉ์ผ๋ก 1
|
| 919 |
-
๋ถ์กฑํ๋ค๊ณ 1
|
| 920 |
-
์ค๋ฆฝ๋๋ 1
|
| 921 |
-
๋ค์์์ 1
|
| 922 |
-
๊ณ ๋ คํ๋๋ผ 1
|
| 923 |
-
์ถ์ง๋๋ฉด 1
|
| 924 |
-
๊ฐ์ ์ฃผ๋ถ 1
|
| 925 |
-
๋ฐ๊ฐ์์ผ๋ก 1
|
| 926 |
-
์๋ ๋ฅด๊ธฐ์ฑ 1
|
| 927 |
-
์ฃผ๋ฒ์ผ๋ก 1
|
| 928 |
-
๋ถํฌํ๋ฉฐ 1
|
| 929 |
-
์ฃผ๋ณ์ด๋ 1
|
| 930 |
-
์ฐ์ฑ
ํ๋ฉด 1
|
| 931 |
-
์ฌํด์ง๋ค 1
|
| 932 |
-
์์ฆ๋ง๋ค 1
|
| 933 |
-
ํ์์ด๊ธฐ 1
|
| 934 |
-
๊ธด์ฅํ๋ฉฐ 1
|
| 935 |
-
์๋ฌ๋ฆฌ๊ณ 1
|
| 936 |
-
ํ์๊น์ง 1
|
| 937 |
-
์ผ์ฑ์์ธ๋ณ์ 1
|
| 938 |
-
์๋ ๋ฅด๊ธฐ๋ด 1
|
| 939 |
-
๊ฑด์กฐํด์ง๊ณ 1
|
| 940 |
-
์ ์ ํด์ง๊ธฐ 1
|
| 941 |
-
๊ฑด๊ฐ๋ณดํ์ฌ์ฌํ๊ฐ์ 1
|
| 942 |
-
์ถ์ฐ๋๋ค 1
|
| 943 |
-
์์ด๋์ฑ 1
|
| 944 |
-
๋ณํ๋ฉด์ 1
|
| 945 |
-
์ ์ ํ๊ณ 1
|
| 946 |
-
๊ณผ๋ฏผ๋ฐ์์ผ๋ก 1
|
| 947 |
-
์ฌ์ฑ๊ธฐ๋ 1
|
| 948 |
-
๊ฑด์กฐํด์ง๊ธฐ 1
|
| 949 |
-
์ง์๋๋ฉด์ 1
|
| 950 |
-
์์ธ์ผ๋ก 1
|
| 951 |
-
์์ฉํ๋ฉด์ 1
|
| 952 |
-
๋ง์์ก๋ค 1
|
| 953 |
-
์์๋๋ 1
|
| 954 |
-
์ง๋ณ๊ด๋ฆฌ์ฒญ 1
|
| 955 |
-
์ง๋ณ์ด๋ 1
|
| 956 |
-
์ฏ์ฏ๊ฐ๋ฌด์๋ณ 1
|
| 957 |
-
๊ธธ์ด์ง๋ฏ๋ก 1
|
| 958 |
-
๊ฐ์๊น์ง 1
|
| 959 |
-
ํ๋ฐญ์ด๋ 1
|
| 960 |
-
์์ํ๋ค 1
|
| 961 |
-
์๊ฒผ์ผ๋ฉด 1
|
| 962 |
-
๊ณ ๋ ค๋๊ตฌ๋ก๋ณ์ 1
|
| 963 |
-
์์ํด์ง๋ฉด์ 1
|
| 964 |
-
๋ฐ์ ํด์ง๋ฉด์ 1
|
| 965 |
-
์ ํํ๋๋ฐ 1
|
| 966 |
-
์ ํํ๋ 1
|
| 967 |
-
๋ง์ฑ์งํ์๋ค 1
|
| 968 |
-
๊ธ์ตํฌ์์๋์ธ 1
|
| 969 |
-
์ ์ํด์ผ 1
|
| 970 |
-
๊ฒ์ด์์ง 1
|
| 971 |
-
๊ณ ๋ คํ๋ค 1
|
| 972 |
-
์ ์๊น์ง 1
|
| 973 |
-
์ํํ์ 1
|
| 974 |
-
์ ์ํ๊ณ 1
|
| 975 |
-
์ ์ง์ง์ 1
|
| 976 |
-
ํธ์
์ด๋ผ๋ ์ง 1
|
| 977 |
-
ํ๋จํ์ 1
|
| 978 |
-
์ ์ํ์ 1
|
| 979 |
-
์ ์ํ๋ 1
|
| 980 |
-
์๋๋ค๋ฉด 1
|
| 981 |
-
๋ง์ฐฌ๊ฐ์ง๋ผ 1
|
| 982 |
-
์ผ๊ฐ์์ 1
|
| 983 |
-
๊ฐ๋ก ์๋ฐํ๊ธฐ๋ณด๋ค 1
|
| 984 |
-
ํ์งํ์ 1
|
| 985 |
-
์ ์ํ๋ค๋ฉด 1
|
| 986 |
-
์ง๊ถํด์ 1
|
| 987 |
-
์ํํด์ผ 1
|
| 988 |
-
์์ ์ด๋ 1
|
| 989 |
-
๋ฏธ๋ค์ก๊ณ 1
|
| 990 |
-
์์ด์์ 1
|
| 991 |
-
์์ํ๊ธฐ๋ก 1
|
| 992 |
-
๊ธฐ์ธ์ด์ก๋๋ฐ 1
|
| 993 |
-
๋ฌด๋๋จ๋ฆด 1
|
| 994 |
-
์ ์คํด์ผ 1
|
| 995 |
-
๋ถ๋ด์ค๋ฝ๋ค 1
|
| 996 |
-
ํคํ๋ก์ค๊ณ 1
|
| 997 |
-
๊ทธ๋ฆฌ์ค์ธ 1
|
| 998 |
-
์ฑ๊ฐํฌ๋ฅด์ธ 1
|
| 999 |
-
์์ํ๊ณ 1
|
| 1000 |
-
๋ณด๋๊ฒ์ 1
|
| 1001 |
-
๋ฐ์ด๋๋ค 1
|
| 1002 |
-
์๊ธ์ผ๋ก 1
|
| 1003 |
-
๊ตฌ์
ํ๊ณ 1
|
| 1004 |
-
ํ๋ก๊ทธ๋๋ฐ 1
|
| 1005 |
-
๋ํ์์ 1
|
| 1006 |
-
์ปดํจํฐ๊ณผํ 1
|
| 1007 |
-
์ ๋๋ฒ์ํฐ 1
|
| 1008 |
-
์ ๊ฒฝ๊ณผํ 1
|
| 1009 |
-
๋ฐ์ฌํ์ 1
|
| 1010 |
-
๊ธฐ์ต์์ค์ฆ 1
|
| 1011 |
-
์ฌ์ด์ธ์ค์ง 1
|
| 1012 |
-
๋๋๊ฒ์ 1
|
| 1013 |
-
์ฌ๋ฆผํผ์๋์ธ 1
|
| 1014 |
-
์ฌ๋ฆผํผ์๋ 1
|
| 1015 |
-
๋ฐ๋์์ 1
|
| 1016 |
-
๋ฌด์คํํ 1
|
| 1017 |
-
์๋ ์ด๋ง 1
|
| 1018 |
-
์คํํธ์
์ธ 1
|
| 1019 |
-
์ธ์ํ์ง 1
|
| 1020 |
-
์ํ์ฒ๋ผ 1
|
| 1021 |
-
๋ฌด์ฃผํ์ผ๋ก 1
|
| 1022 |
-
๊ฐ๋จ๊ถ์์ 1
|
| 1023 |
-
์ปคํธ๋ผ์ธ 1
|
| 1024 |
-
๊ฐ์ ๋ณด๋ค 1
|
| 1025 |
-
๋ฆฌ์ผํฌ๋ฐ์ด 1
|
| 1026 |
-
๋ง์ ์ด๋ฉฐ 1
|
| 1027 |
-
๋ถ์๊ฐ์กฑ 1
|
| 1028 |
-
์ฐ์ ๋๋ค 1
|
| 1029 |
-
์๋์๋ค 1
|
| 1030 |
-
๊ณต๊ณต๊ธฐ๊ด 1
|
| 1031 |
-
๊ธฐ๊ด์ฅ์๊ฒ 1
|
| 1032 |
-
๊ทผ์ ๋์ง 1
|
| 1033 |
-
ํ๊ตญ๋๋ฐ๋ฌธ์ ์๋ฐฉ์น์ ์ 1
|
| 1034 |
-
๋๋ฌ๋ฌ๋ค 1
|
| 1035 |
-
๋ฌธํ์ฒด์ก๊ด๊ด์์ํ 1
|
| 1036 |
-
ํด์๋๋ค 1
|
| 1037 |
-
๋ช
๋ชฉ์ผ๋ก 1
|
| 1038 |
-
๋ฒ๋ฅ ์๋ฌธ 1
|
| 1039 |
-
๊ตญ๊ฐ์์ 1
|
| 1040 |
-
์นด์ง๋
ธ๊ณต๊ธฐ์
์ธ 1
|
| 1041 |
-
์ง์ ๋์์ 1
|
| 1042 |
-
๊ฐ๋ฅํ๋๋ผ 1
|
| 1043 |
-
ํด์๋์๋๋ฐ 1
|
| 1044 |
-
์ธํฌ์์ 1
|
| 1045 |
-
๋์๊ฐ๋ค 1
|
| 1046 |
-
์ด๋ค์ก๋ค 1
|
| 1047 |
-
์นด๋กค๋ฆฐ์ค์นด 1
|
| 1048 |
-
์๋ฆฌ์ํ์ 1
|
| 1049 |
-
์์์๋ก 1
|
| 1050 |
-
์๋ฌผํ์์ธ 1
|
| 1051 |
-
๋งค์ฌ์ถ์ธ์ธ 1
|
| 1052 |
-
๋น๋ฐ์ด์ ๋ก 1
|
| 1053 |
-
๋ฐ๊ฒฌํ์ฌ 1
|
| 1054 |
-
๊ธฐ์ฌํ๊ธฐ 1
|
| 1055 |
-
์์ํ๊ฒ 1
|
| 1056 |
-
์์์์ธ 1
|
| 1057 |
-
์ฐ๋ธ๋ก์ค 1
|
| 1058 |
-
๋ฏธ์๋ฌผ์ธ 1
|
| 1059 |
-
์์๊ผฌ๋ง์ ์ถฉ 1
|
| 1060 |
-
๋ฐ๊ฒฌํ๋ค 1
|
| 1061 |
-
๋น์ ์์ ์ผ๋ก 1
|
| 1062 |
-
์๋๋๋ฉด 1
|
| 1063 |
-
์๊ฐ๋ฉด์ญ 1
|
| 1064 |
-
๋ฐ๊ฒฌ์ผ๋ก 1
|
| 1065 |
-
์ดํดํ๊ฒ 1
|
| 1066 |
-
์กฐ์ ์ด๋ผ 1
|
| 1067 |
-
๋ฌผ๋ฆฌํ์ 1
|
| 1068 |
-
๋ฐํ๋๋ฉฐ 1
|
| 1069 |
-
๊ฒฝ์ ํ์์ผ๋ก 1
|
| 1070 |
-
์ ์
ํฌ์์๋ค 1
|
| 1071 |
-
๊ฐ์ฐ์์ 1
|
| 1072 |
-
๋ถํฉ๋ฆฌํ 1
|
| 1073 |
-
์ฃผ์์ผ๋ก 1
|
| 1074 |
-
๋ฌด์์ธ์ง 1
|
| 1075 |
-
๋ค์ด๋ณด์ 1
|
| 1076 |
-
ํ์์ผ์ธ 1
|
| 1077 |
-
ํด์ง๊ธ์ฌ 1
|
| 1078 |
-
์ฐ๊ธ์ผ๋ก 1
|
| 1079 |
-
์๋์ธ๋ฒ์ 1
|
| 1080 |
-
์ฐ๊ธ์๋ น์ก 1
|
| 1081 |
-
ํด์ง์๋์ธ์จ 1
|
| 1082 |
-
๊ณผ์ธํ๊ณ 1
|
| 1083 |
-
๋์ด๊ฐ๋ฉด์๋ถํฐ 1
|
| 1084 |
-
๊ณผ์ธํ๋ค 1
|
| 1085 |
-
์ ๋ฆฌํ์ง 1
|
| 1086 |
-
๋ฏธ๋์์
1
|
| 1087 |
-
ํฌ์์์ฐ๊ธ์ผํฐ 1
|
| 1088 |
-
๋ถ์์ง์์๋ฐฐ์ํฐ 1
|
| 1089 |
-
์์์ด๋ค 1
|
| 1090 |
-
๋ด๋ค๋ณด๋ฉด์ 1
|
| 1091 |
-
์์ง์ฌ์ 1
|
| 1092 |
-
ํ๋ฆฌ์ฆํฌ์์๋ฌธ 1
|
| 1093 |
-
๋จํํ๋ฉด์ 1
|
| 1094 |
-
๋ณธ๊ฒฉ์ ์ธ 1
|
| 1095 |
-
์์ฐ์์ฅ 1
|
| 1096 |
-
์ ๋งํ ์ง 1
|
| 1097 |
-
๊ตญ๋ฏผ์ฐ๊ธ๊ณต๋จ 1
|
| 1098 |
-
๊ธฐ๊ธ์ด์ฉ๋ณธ๋ถ 1
|
| 1099 |
-
ํฌ์์ด์ฉํ์ฅ 1
|
| 1100 |
-
๊ตญ๋ฏผ์ํ 1
|
| 1101 |
-
์์์ด์ฝ๋
ธ๋ฏธ์คํธ 1
|
| 1102 |
-
์ ๋ฌธ๊ฐ๋ค 1
|
| 1103 |
-
์ ๊ณต์ ์ผ๋ก 1
|
| 1104 |
-
๋ชจ๋ฐ์ผ์ฉ 1
|
| 1105 |
-
์ถ์๋๊ณ 1
|
| 1106 |
-
์์ดํฐ์ฉ 1
|
| 1107 |
-
๋์ด์ฌ๋ฆฐ 1
|
| 1108 |
-
ํ์ฌ๋๋ฉด์ 1
|
| 1109 |
-
์ ์ ๋ ฅ์ผ๋ก 1
|
| 1110 |
-
์ค์ํด์ก๋ค 1
|
| 1111 |
-
๊ฐ์กฐํ๊ณ 1
|
| 1112 |
-
์ ๋ ฅํจ์จ 1
|
| 1113 |
-
ํฅ์๋๋ค 1
|
| 1114 |
-
์ฝ์ ์ผ๋ก 1
|
| 1115 |
-
๊ฑฐ๋ก ๋๋ 1
|
| 1116 |
-
๊ฐํ๋๋ค 1
|
| 1117 |
-
์ ๋ง๋๋ค 1
|
| 1118 |
-
์ ์ธํ๊ณ 1
|
| 1119 |
-
์๋๋ก์ด๋ 1
|
| 1120 |
-
์ค๋งํธํฐ๋ค 1
|
| 1121 |
-
ํฅ์๋๋ฉด์ 1
|
| 1122 |
-
๋จ์ด์ง๋ฉด 1
|
| 1123 |
-
๊ณ ๊ฐ์ฌ๋ค 1
|
| 1124 |
-
๋ค์ด๊ฐ๋ฉด์ 1
|
| 1125 |
-
์ข์์ง๊ณ 1
|
| 1126 |
-
๊ณ ๋ถ๊ฐ๊ฐ์น 1
|
| 1127 |
-
์ง์คํ๋ฉด์ 1
|
| 1128 |
-
์๊ฐ์์ 1
|
| 1129 |
-
๋๊ฐ๋ด๊ธฐ๋ก 1
|
| 1130 |
-
๊ตญ๊ฐ๋ํํ 1
|
| 1131 |
-
๋ค๋๋๋ 1
|
| 1132 |
-
์ธ๋ฅด๋น์ 1
|
| 1133 |
-
์ฆ๋ฒ ์ฆ๋ค 1
|
| 1134 |
-
ํ์๋
ธ๋ฅดํธ๋ก 1
|
| 1135 |
-
์ฑํผ์ธ์ค๋ฆฌ๊ทธ 1
|
| 1136 |
-
๋ฏธ๋ํ๋๋ก 1
|
| 1137 |
-
๋งนํ์ฝํ๋ฉฐ 1
|
| 1138 |
-
๋ถ๋ฐ์ค๋ฆฌ 1
|
| 1139 |
-
ํ๋ํฌํธ๋ฅดํธ 1
|
| 1140 |
-
์ธก๋ฉด์์ 1
|
| 1141 |
-
์ค๋ฅธ๋ฐ๋ก 1
|
| 1142 |
-
๋ง๋ฌด๋ฆฌํด 1
|
| 1143 |
-
์ฃผ๋์ค๋ฅด 1
|
| 1144 |
-
ํ์ฉํ๋ฉฐ 1
|
| 1145 |
-
๋ฏธ๋ํ๋ 1
|
| 1146 |
-
๋ถ์์ผ๋ก 1
|
| 1147 |
-
๋น ์ง๋ฉด์ 1
|
| 1148 |
-
์์ํ์ผ๋ก 1
|
| 1149 |
-
ํํธ๋๋ก 1
|
| 1150 |
-
์๋น์๋ก 1
|
| 1151 |
-
์ด๋ค๋๋ฐ 1
|
| 1152 |
-
๋ํํ์ 1
|
| 1153 |
-
ํฌํจ๋๋ค 1
|
| 1154 |
-
์
์ฑํ๋ค 1
|
| 1155 |
-
์ค๋์์ 1
|
| 1156 |
-
์๋ฅด๋จ์ผ๋ก 1
|
| 1157 |
-
ํฉ๋ฅํ๋ค 1
|
| 1158 |
-
๊ฐ์ฒ์ ์ธ 1
|
| 1159 |
-
๋ง์์ด์๋ค 1
|
| 1160 |
-
์๋์ผ๋ก 1
|
| 1161 |
-
๊ทผ์ฒ์์ 1
|
| 1162 |
-
์ผํ๊ฐ๋ค๋ก 1
|
| 1163 |
-
์ ์ ์ํ 1
|
| 1164 |
-
๊ณ์ฐ๋๋ง๋ค 1
|
| 1165 |
-
๊ธฐ๋ค๋ ธ๋ค 1
|
| 1166 |
-
๋น์๊ธฐ๋ก 1
|
| 1167 |
-
๊ณตํด์ผ๋ก 1
|
| 1168 |
-
์ง์ ๋๊ณ 1
|
| 1169 |
-
๋ํ๋งํธ๋ง๋ค 1
|
| 1170 |
-
๋ถ์ ์๊ณ 1
|
| 1171 |
-
์ฌํ์ง๋ก 1
|
| 1172 |
-
๋งค์ง๋๋ค 1
|
| 1173 |
-
์ด์๋์ง 1
|
| 1174 |
-
์ง์ํ๊ธฐ 1
|
| 1175 |
-
์์๊ณตํด์ผ 1
|
| 1176 |
-
์๋น์์ฅ 1
|
| 1177 |
-
ํ๋ณต๋ ์ง 1
|
| 1178 |
-
์ ํต์
์ฒด์์ 1
|
| 1179 |
-
ํ๋ชฉ์์ 1
|
| 1180 |
-
๋จ์ด์ง๋ฉด์ 1
|
| 1181 |
-
์ฅ๋งํ๋ ค 1
|
| 1182 |
-
๋กฏ๋ฐ๋ฐฑํ์ 1
|
| 1183 |
-
ํด์ธ์ฌํ 1
|
| 1184 |
-
๋๋ถ์ด๋ผ 1
|
| 1185 |
-
์ ์ธ๊ณ๋ฐฑํ์ 1
|
| 1186 |
-
๋์ด๋ฌ๋ค๊ณ 1
|
| 1187 |
-
์ฆ์์ํ 1
|
| 1188 |
-
๋ผ์ง๊ณ ๊ธฐ 1
|
| 1189 |
-
์ฌํ๊ฐ๋ค๋ก 1
|
| 1190 |
-
์ ์ ์ฃผ์ 1
|
| 1191 |
-
์๊ทํฌ์ 1
|
| 1192 |
-
๊ธฐ๊ฐ๋ณด๋ค 1
|
| 1193 |
-
๋ถ๊ฝ์ถ์ 1
|
| 1194 |
-
๋ชฐ๋ฆฌ๋ฉด์ 1
|
| 1195 |
-
์์ธ์ธ๊ณ๋ถ๊ฝ๏ฟฝ๏ฟฝ๏ฟฝ์ 1
|
| 1196 |
-
ํ๊ฐ๊ณต์ 1
|
| 1197 |
-
๋์ด๊ณต์ 1
|
| 1198 |
-
ํ ์ธํ์ฌ 1
|
| 1199 |
-
๋งค์ฅ์์ 1
|
| 1200 |
-
์งํํ๊ณ 1
|
| 1201 |
-
๋กฏ๋ฐ๋งํธ 1
|
| 1202 |
-
์ค์ธ๋จธ์ค์บฃ 1
|
| 1203 |
-
์ ๋ ดํ๊ฒ 1
|
| 1204 |
-
๋จน๊ฑฐ๋ฆฌ๋ค 1
|
| 1205 |
-
๊ฐ์ตํ๋ค 1
|
| 1206 |
-
๋ด์ผํ๋ 1
|
| 1207 |
-
์ ํ๋ธ๋ก 1
|
| 1208 |
-
๋ฐ๋ผ๋๋ค 1
|
| 1209 |
-
์ ์ถํ์ง 1
|
| 1210 |
-
๊ฒฝ๊ณ ๊น์ง 1
|
| 1211 |
-
๋ณด๋๋ค๊ณ 1
|
| 1212 |
-
์์ฒญ์๋ค 1
|
| 1213 |
-
์์ต์์ 1
|
| 1214 |
-
๊ธฐ์ฌ๋ถ์ผ๋ก 1
|
| 1215 |
-
๊ธ์ก๋งํผ 1
|
| 1216 |
-
์
์ฅ์์ 1
|
| 1217 |
-
์ธ๋ฌด๋น๊ตญ 1
|
| 1218 |
-
๋ฒ์ธ์ด๋ผ 1
|
| 1219 |
-
์ง์ํ๊ธฐ 1
|
| 1220 |
-
์ค์ง์ ์ผ๋ก 1
|
| 1221 |
-
ํ๊ตญ์ฌ๋ฌด๊ด๋ฆฌํํ 1
|
| 1222 |
-
๋๋ฑํ๊ฒ 1
|
| 1223 |
-
๊ณผ์ธํด์ผ 1
|
| 1224 |
-
๊ตญ์ธ๊น์ง 1
|
| 1225 |
-
๋
ผ์์กฐ์ฐจ 1
|
| 1226 |
-
์์ฒ์ง์ํ๊ธฐ 1
|
| 1227 |
-
๊ฐ์ ํ๋ฉด์ 1
|
| 1228 |
-
์ ์ฑ
ํ๋ ฅ 1
|
| 1229 |
-
๋ฐ์ํ๊ณ 1
|
| 1230 |
-
๋ฉ๋ถํ๊ฒ 1
|
| 1231 |
-
์กฐ์ธ์กฐ์ฝ 1
|
| 1232 |
-
๋ฐ์ ธ๋ณด๊ฒ ๋ค 1
|
| 1233 |
-
์
์ฅ์ด์๋ค 1
|
| 1234 |
-
๋ค์ด์คฌ๋ค 1
|
| 1235 |
-
์์ฒญ์๋ค๋ก 1
|
| 1236 |
-
๊ฐ๋ฅํด์ผ 1
|
| 1237 |
-
๊ตฌ๊ธ์ฝ๋ฆฌ์ 1
|
| 1238 |
-
๋๋๋์จ 1
|
| 1239 |
-
์ ๋ฌดํ๋ค 1
|
| 1240 |
-
์๋์ธ๋ฒ 1
|
| 1241 |
-
๊ท์ ํ์ง 1
|
| 1242 |
-
๊ท์ ํ๋๋ผ 1
|
| 1243 |
-
์์ฒ์๋ 1
|
| 1244 |
-
์ดํํ์ง 1
|
| 1245 |
-
๊ฐ์ํ๋๋ฉด์ 1
|
| 1246 |
-
๊ตญ์ธ์ฒญ์ผ๋ก 1
|
| 1247 |
-
์ข
ํฉ์๋์ธ 1
|
| 1248 |
-
๋ฒ์ด๋ค์ธ 1
|
| 1249 |
-
์์ฒญ์๊ฐ 1
|
| 1250 |
-
๋ค๋ฐฉ์ด๋ 1
|
| 1251 |
-
๊ฐ์ ธ๊ฐ๊ฒ 1
|
| 1252 |
-
๊ฐ์ ธ์ค๋ค 1
|
| 1253 |
-
๋นผ๊ณกํ๊ฒ 1
|
| 1254 |
-
์ต๊ด์ด์๋ค 1
|
| 1255 |
-
์ทจ๋ฏธ์ํ์ด๋ผ๊ณ 1
|
| 1256 |
-
์ ํ์ด์์ด์ 1
|
| 1257 |
-
์์งํ๋ค 1
|
| 1258 |
-
์์ฐ์ค๋ฝ๊ฒ 1
|
| 1259 |
-
๋์ต๋๋ค 1
|
| 1260 |
-
์ดํ๋ผ๊ณ 1
|
| 1261 |
-
๊ธฐ์ตํ๋ค 1
|
| 1262 |
-
๋ค์ฑ๋ก์ด 1
|
| 1263 |
-
๋ ๋ค์ฉํ๊ฒ 1
|
| 1264 |
-
๋ง์๊ฑฐ๋ 1
|
| 1265 |
-
๊ณ ํํ์จ 1
|
| 1266 |
-
์ฌ์ฉํ๊ฑฐ๋ 1
|
| 1267 |
-
์ฌ๋์๊ฒ 1
|
| 1268 |
-
์ธ์๊น์ง 1
|
| 1269 |
-
ํ๊ณ ํ๋ค 1
|
| 1270 |
-
์๋น์ด๋ 1
|
| 1271 |
-
์ด๊ดํ๊ณ 1
|
| 1272 |
-
์๋ ๋ก๊ทธ 1
|
| 1273 |
-
ํ๋์๋์ฐจ๊ทธ๋ฃน 1
|
| 1274 |
-
๋์๋ํ 1
|
| 1275 |
-
์ ๋ถ๊น์ง 1
|
| 1276 |
-
๋น์ฆ๋์ค 1
|
| 1277 |
-
์ฒด๊ฒฐํ๋ค๊ณ 1
|
| 1278 |
-
๊ณต๊ณผ๋ํ 1
|
| 1279 |
-
์ฐ๊ตฌ์ค์ฌ 1
|
| 1280 |
-
๋ํ์ด๋ค 1
|
| 1281 |
-
๋ฐ์ ์ฌ์
1
|
| 1282 |
-
์ฐ๋ ๊ธฐ๋ 1
|
| 1283 |
-
๋ชจ๋น๋ฆฌํฐ 1
|
| 1284 |
-
๋ฐธ๋ฅ์ฒด์ธ 1
|
| 1285 |
-
์ค๋ฆฝํ๊ธฐ๋ก 1
|
| 1286 |
-
ํต์์ฐ์
๋ถ 1
|
| 1287 |
-
์กฐ์ง์ผ๋ก 1
|
| 1288 |
-
์ฐ๊ตฌ๊ธฐ๊ด 1
|
| 1289 |
-
๋ก๋ณดํฑ์ค 1
|
| 1290 |
-
ํ์ ์ ์กฐ 1
|
| 1291 |
-
๊ตญ๊ฐ์ฐ๊ตฌํ๋ 1
|
| 1292 |
-
๋ค๊ฐ์์ง 1
|
| 1293 |
-
๋ถํฌ๋ช
ํด 1
|
| 1294 |
-
์ตํ์๊ถ 1
|
| 1295 |
-
๊ธํฌ์ธ๋ 1
|
| 1296 |
-
๊ธ์ต์ํ 1
|
| 1297 |
-
์๋์๋ 1
|
| 1298 |
-
๋์
๋๋ฉด 1
|
| 1299 |
-
์ด๋ ค์์ง 1
|
| 1300 |
-
๋น ์ ธ๋๊ฐ๋ฉด์ 1
|
| 1301 |
-
ํ๊ตญ๊ธฐ์
๊ฑฐ๋ฒ๋์คํฌ๋ผ 1
|
| 1302 |
-
๊ฒฐ๊ณผ์ ์ผ๋ก 1
|
| 1303 |
-
๋น ์ ธ๋๊ฐ 1
|
| 1304 |
-
๊ด์ธกํ๊ณ 1
|
| 1305 |
-
๋ถ์๊ฐ์ธ 1
|
| 1306 |
-
ํฌ์์๋ค์๊ฒ 1
|
| 1307 |
-
ํฌ๋ง์ด์ 1
|
| 1308 |
-
๊ธํฌ์ธ๋ก 1
|
| 1309 |
-
๊ธ์ต์๋์ข
ํฉ๊ณผ์ธ๋ก 1
|
| 1310 |
-
์ํ์ผ๋ก 1
|
| 1311 |
-
๋ฎ์์ง๋ค 1
|
| 1312 |
-
๋ฒ์ ์ฌ๋ฒ์์ํ 1
|
| 1313 |
-
์ฆ์ธ์ผ๋ก 1
|
| 1314 |
-
์ํธ์ผํฐ 1
|
| 1315 |
-
๋์์์๋ฌธํ์ผํฐ 1
|
| 1316 |
-
์๊ตฌํ๊ธฐ๋ก 1
|
| 1317 |
-
๋ฒ์ฌ์์์ฅ์ธ 1
|
| 1318 |
-
์์ฌํ์
1
|
| 1319 |
-
๋ํ๋ช
๋ น์ฅ 1
|
| 1320 |
-
๋ฐฉ์๊น์ง 1
|
| 1321 |
-
์๋ด์ด์ 1
|
| 1322 |
-
๋ ์ฌ๋๋ค 1
|
| 1323 |
-
์ฝ์์ด์ 1
|
| 1324 |
-
์ ์ํ๋ฉฐ 1
|
| 1325 |
-
์ธ์ ๋๋ค๋ฉด์ 1
|
| 1326 |
-
๋ถ์ธํ๊ณ 1
|
| 1327 |
-
๊ตญ๋ฉด์ผ๋ก 1
|
| 1328 |
-
์์์ด๋ค 1
|
| 1329 |
-
๋ํ์ค์ํ 1
|
| 1330 |
-
์ ๋ฐ๋์ 1
|
| 1331 |
-
์ฃผ์๊ฑฐ๋ 1
|
| 1332 |
-
์ ๋ฐ๋์ง 1
|
| 1333 |
-
์ด๋ค์ง์ง 1
|
| 1334 |
-
์ดํด๋ณด๊ณ 1
|
| 1335 |
-
๋ฐ์ํ์ 1
|
| 1336 |
-
์ฒ๋ฆฌํ์ง 1
|
| 1337 |
-
๊ฐ๋ก๋งํ๋ค 1
|
| 1338 |
-
๋์ด์ฌ๋ฆฌ๋ ค 1
|
| 1339 |
-
๊ณต์ง์ ๊ฑฐ๋ฒ 1
|
| 1340 |
-
๋ค๊ฐ์ค๋ฉด์ 1
|
| 1341 |
-
์๋ฐํ๊ธฐ 1
|
| 1342 |
-
์ฌ๋ฐ์ํ๋ 1
|
| 1343 |
-
๊ฒ๋ค์ด๋ค 1
|
| 1344 |
-
์๊ฒฐํ๋ฉด 1
|
| 1345 |
-
์ ์ ํ์ง 1
|
| 1346 |
-
์์๋๊ฑฐ๋ 1
|
| 1347 |
-
์์๋๋ 1
|
| 1348 |
-
๊ตญํ๊ท์น 1
|
| 1349 |
-
๊ตฌ์ฑํด์ผ 1
|
| 1350 |
-
์
๋ง๋๋ก 1
|
| 1351 |
-
์๋ช
ํ๊ธฐ 1
|
| 1352 |
-
๋ฐ๊พธ๊ฒ ๋ค 1
|
| 1353 |
-
๋์จํด์ง 1
|
| 1354 |
-
๊ฒฐ๊ณผ๋ผ๊ณ 1
|
| 1355 |
-
๋ด๋ถ์ ์ผ๋ก 1
|
| 1356 |
-
์ฌํํ๋ค 1
|
| 1357 |
-
๊ณต๊ณต์ฐํ 1
|
| 1358 |
-
์๋์ด์์ง 1
|
| 1359 |
-
์
์ด์ง๊ณ 1
|
| 1360 |
-
์ ์ผํ๊ฒ 1
|
| 1361 |
-
์ฐ์ ์์๊ณ 1
|
| 1362 |
-
์ ์กฐ์
์ธ 1
|
| 1363 |
-
์ฒญ๋
์๊ฒ 1
|
| 1364 |
-
ํ๊ณผ๊น์ง 1
|
| 1365 |
-
์ ์๊ณตํ 1
|
| 1366 |
-
์ ๊ณต์๋ค 1
|
| 1367 |
-
ํ๊ต์์ 1
|
| 1368 |
-
๋๊ฒฐ๋๋ฉด์ 1
|
| 1369 |
-
๋ฐ๋ผ์ก๊ฒ ๋ค๊ณ 1
|
| 1370 |
-
์กด์ฌํ์ง 1
|
| 1371 |
-
์ต์์ธต๋ถ 1
|
| 1372 |
-
์ฅ์
ํ๊ณ 1
|
| 1373 |
-
ํฌ์๋ณด๋ค 1
|
| 1374 |
-
๋ฐ๋์๊ณ 1
|
| 1375 |
-
์์ง์๋ค 1
|
| 1376 |
-
ํด๊ฒฐํ ์ง 1
|
| 1377 |
-
์์๋๋ฐ 1
|
| 1378 |
-
์๋ชป์ธ์ง 1
|
| 1379 |
-
์์ถํด๋ด์ 1
|
| 1380 |
-
์ด๊ธฐ์ฃผ์ 1
|
| 1381 |
-
ํธ์ด๋จ๋ค 1
|
| 1382 |
-
์ผ์ฑ๊ทธ๋ฃน 1
|
| 1383 |
-
์ถ์ํ๊ณ 1
|
| 1384 |
-
์๊ตฌํ๊ณ 1
|
| 1385 |
-
๋ถ๋ด๋ถ๋ด 1
|
| 1386 |
-
์์คํ
๋ฐ๋์ฒด 1
|
| 1387 |
-
๊ฒฝ์์ฌ์ธ 1
|
| 1388 |
-
์๊ทนํ๊ณ 1
|
| 1389 |
-
๋์จํด์ก๋ค 1
|
| 1390 |
-
๊ณผ๋ํ๊ฒ 1
|
| 1391 |
-
๋ฌด์์น์ฐจํ๋ฉฐ 1
|
| 1392 |
-
๊ฒฝ์์์ธ 1
|
| 1393 |
-
์๊ฐ์ด์ก์ผ๋ก 1
|
| 1394 |
-
๋์ด์ฐ๋ค 1
|
| 1395 |
-
์ฐฝ์
์์ธ 1
|
| 1396 |
-
์น๋ถ์ผ๋ก 1
|
| 1397 |
-
ํ์ด๋๋ฆฌ 1
|
| 1398 |
-
์ ๋
ํ๋ค 1
|
| 1399 |
-
๋นผ๋ฎ์๋ค 1
|
| 1400 |
-
์ฌ๊ณ์์ 1
|
| 1401 |
-
์ ์ ์ผ๋ก 1
|
| 1402 |
-
์ถ์ํ๊ณ 1
|
| 1403 |
-
์ค์ํ๋ค 1
|
| 1404 |
-
ํ๋ฆผ๋ํ๊ต 1
|
| 1405 |
-
๋ํ์ฑ์ฌ๋ณ์ 1
|
| 1406 |
-
๋ํ์ ์ธ 1
|
| 1407 |
-
ํ๊ฐํ๋ฉฐ 1
|
| 1408 |
-
์ธ์ง๊ธฐ๋ฅ๊ณผ 1
|
| 1409 |
-
์ธ์ง๊ธฐ๋ฅ 1
|
| 1410 |
-
๋งฅ๋ฝ์ ๋ณด 1
|
| 1411 |
-
ํ๊ด์งํ 1
|
| 1412 |
-
์ ์ฒดํ๋ 1
|
| 1413 |
-
์ง๋จ๋ณด๋ค 1
|
| 1414 |
-
์ ์ ์์ธ 1
|
| 1415 |
-
๋ฒ์ด์ก๋ค 1
|
| 1416 |
-
ํ๋ฆผ๋๋ํ์ฑ์ฌ๋ณ์ 1
|
| 1417 |
-
์ ์ ๊ฑด๊ฐ์ํ 1
|
| 1418 |
-
์ด์งํ๊ณ 1
|
| 1419 |
-
๊ธ์ ์ ์ธ 1
|
| 1420 |
-
์๋์ค์ฝ์๋ํ 1
|
| 1421 |
-
์ฌ๋๋ณด๋ค 1
|
| 1422 |
-
์๋ฐ๋ก์ด๋ 1
|
| 1423 |
-
๋ชฉ์ ์์ 1
|
| 1424 |
-
์๋ต์๋ค 1
|
| 1425 |
-
์ฐ๋ น๋ณ๋ก 1
|
| 1426 |
-
๋ค๊ฐ์ฌ์๋ก 1
|
| 1427 |
-
๊ธฐํผ์๋ค๋ณด๋ค 1
|
| 1428 |
-
๋๋นํด์ผ 1
|
| 1429 |
-
๊ฒฐ๊ณผ์์ 1
|
| 1430 |
-
์ง์ฅ์ธ๋ณด๋ค 1
|
| 1431 |
-
์ ์ถํ๊ณ 1
|
| 1432 |
-
์ฒด๊ณ์ ์ธ 1
|
| 1433 |
-
๋ฏธ์น๋ค๊ณ 1
|
| 1434 |
-
๋จ๊ธฐ์ ์ธ 1
|
| 1435 |
-
์ง์คํด์ 1
|
| 1436 |
-
์ง์ ์ด๋ค 1
|
| 1437 |
-
๋ฉ์
ํ๋ค 1
|
| 1438 |
-
์ธ์ก๊ณต์ 1
|
| 1439 |
-
์ค๊ตญ์ผ๋ก 1
|
| 1440 |
-
ํ๋ณดํ๊ธฐ 1
|
| 1441 |
-
์ค์ํฉ๋๋ค 1
|
| 1442 |
-
๊ธฐ์ ์ ์ธ 1
|
| 1443 |
-
์คํ์์ค 1
|
| 1444 |
-
๋ณธ์ฌ์์ 1
|
| 1445 |
-
๋ถ์ฌ์ฅ์ผ๋ก 1
|
| 1446 |
-
๊ทผ๋ฌดํ๊ณ 1
|
| 1447 |
-
๋ง์ดํฌ๋ก์ํํธ 1
|
| 1448 |
-
๊ณผํ์๋ก 1
|
| 1449 |
-
์ ๋ํ๊ณ 1
|
| 1450 |
-
์คํ์์ค๋ก 1
|
| 1451 |
-
ํ์ ์ ์ธ 1
|
| 1452 |
-
ํ๋์ค๋ฟ 1
|
| 1453 |
-
์ถ์ ๋ค๋ก 1
|
| 1454 |
-
์ ์์ด๋ค 1
|
| 1455 |
-
์ถฉ๋ถํ์ง 1
|
| 1456 |
-
์กฐ์ง์๊ฒ 1
|
| 1457 |
-
์น์ดํด์ง๊ณ 1
|
| 1458 |
-
ํจ์จ์ ์ธ 1
|
| 1459 |
-
์ํคํ
์ฒ 1
|
| 1460 |
-
์์์ผ๋ก 1
|
| 1461 |
-
๊ฐ๋ฐํด์ผ 1
|
| 1462 |
-
์ ๊ณต์๋ค๊ณผ 1
|
| 1463 |
-
ํํธ๋์ญ 1
|
| 1464 |
-
์ ๋ฝ์ผ๋ก 1
|
| 1465 |
-
ํ๋ํด๋๊ฐ๋ค 1
|
| 1466 |
-
๊ฒฝ์ ์์ 1
|
| 1467 |
-
๋งค์ถ์์ 1
|
| 1468 |
-
์ฐจ์งํ๋ค 1
|
| 1469 |
-
์์ถ์์ 1
|
| 1470 |
-
๋๋๋ฌ์ง๋ค 1
|
| 1471 |
-
๋ฐ๋์ฒด๊ณ 1
|
| 1472 |
-
์ํ๊ฐ์ 1
|
| 1473 |
-
์ด๋ฆผ์ด์ด 1
|
| 1474 |
-
์ข์ฐํ๋ค 1
|
| 1475 |
-
๋ฉ๋ถํ๋ค 1
|
| 1476 |
-
์ ์งํด์๋ค 1
|
| 1477 |
-
๊ณต์ค์์ 1
|
| 1478 |
-
๋ํ๊ฑฐ๋ฆฌ๊ณ 1
|
| 1479 |
-
ํ์ ํ๋ค 1
|
| 1480 |
-
๊ณต๊ฐ์ผ๋ก 1
|
| 1481 |
-
์ฌํ์ํ๋ค 1
|
| 1482 |
-
ํ๋๋ฏธ์ ๊ด 1
|
| 1483 |
-
์๊ฐ์ด์ 1
|
| 1484 |
-
๋ถ๋ฅด์ฃผ์ 1
|
| 1485 |
-
์ฌ๋ผํธ๋ฅด 1
|
| 1486 |
-
์๋ฆฌ์์ 1
|
| 1487 |
-
์์ด์จ์ด์จ 1
|
| 1488 |
-
๋ด๋ก๋ผํ 1
|
| 1489 |
-
ํ๋๋ฏธ์ 1
|
| 1490 |
-
๊ฑฐ์ณ๊ฐ๋ค 1
|
| 1491 |
-
ํ๋์๋์ฐจ 1
|
| 1492 |
-
์ง์์ผ๋ก 1
|
| 1493 |
-
๋๊ปด์ง์ง 1
|
| 1494 |
-
์์๋๋ 1
|
| 1495 |
-
์ง๋ฌธ์ธ๋ฐ 1
|
| 1496 |
-
๋ฌด์์ํ๊ธฐ 1
|
| 1497 |
-
์๋ฆ๋ค์ 1
|
| 1498 |
-
๊ณต์กดํ๋ค 1
|
| 1499 |
-
๋ฐ๊พธ๋ฉด์ 1
|
| 1500 |
-
๋จ์ด๋จ๋ฆฐ๋ค 1
|
| 1501 |
-
๋ฐ์ ์์๋ 1
|
| 1502 |
-
์ฃผ๋ชฉํ๋ค 1
|
| 1503 |
-
์ฌ์ฉํ์ง 1
|
| 1504 |
-
์ฌ๊ฐ๋ํ๊ณ 1
|
| 1505 |
-
ํ์ฅ์ผ๋ก 1
|
| 1506 |
-
ํ๋ฐ๊ฟ์์ผฐ๋ค 1
|
| 1507 |
-
์ฌ๊ฐ๋ํด 1
|
| 1508 |
-
์ฅ์์๋ค 1
|
| 1509 |
-
์๊ฐํ๋ค 1
|
| 1510 |
-
๋์ด์๋ค 1
|
| 1511 |
-
์กฐ๊ฐ์ผ๋ก 1
|
| 1512 |
-
๋งค๋ฌ๋ฆฌ๊ฒ 1
|
| 1513 |
-
์ถ๊ฐ๋๋ฉด์ 1
|
| 1514 |
-
๋ค์ด๊ฐ๊ธฐ 1
|
| 1515 |
-
ํกํฌ๋ผ๊ณ 1
|
| 1516 |
-
์์ฒ์์ 1
|
| 1517 |
-
๋ฌด๊ธฐ๋ ฅํจ 1
|
| 1518 |
-
์๋ฆ๋ต๋ค 1
|
| 1519 |
-
๊ฐ๋ํ๋ค 1
|
| 1520 |
-
์์ง์ธ๋ค 1
|
| 1521 |
-
์ญ๊ฒฝ์ด๋ 1
|
| 1522 |
-
์๋ฆ๋ต๋ค๊ณ 1
|
| 1523 |
-
๋ค์ด์๋ฉฐ 1
|
| 1524 |
-
๊ณต๊ฐ์ด๋ค 1
|
| 1525 |
-
ํ๋ ์ดํฐ 1
|
| 1526 |
-
๋ถ๋๋ฌ์ 1
|
| 1527 |
-
๊ฐ๊ฐ์ ์ผ๋ก 1
|
| 1528 |
-
๊ฒฝํํ๊ฒ 1
|
| 1529 |
-
์๋ฐฉํ๊ธฐ 1
|
| 1530 |
-
์์งํด์ผ 1
|
| 1531 |
-
๊ตญํ ๊ตํต๋ถ 1
|
| 1532 |
-
ํ์์ฌ์ฌ๋ถ์์กฐ์ ์์ํ 1
|
| 1533 |
-
์ก๋ฐํ๋ค 1
|
| 1534 |
-
์ฆ๊ฐํ๋ฉด์ 1
|
| 1535 |
-
์ฒ๋ฆฌํด์ผ 1
|
| 1536 |
-
์ค์ํด์ผ 1
|
| 1537 |
-
์ ๋ฌธ๊ฐ๋ก 1
|
| 1538 |
-
ํ์ง์ ๊ฒ๋จ 1
|
| 1539 |
-
์ ๊ฒํด์ผ 1
|
| 1540 |
-
์๋
๋ถํฐ 1
|
| 1541 |
-
์
์ฃผ์์ ์ 1
|
| 1542 |
-
๋ํํ์ง 1
|
| 1543 |
-
๊ธ์งํ๋ค 1
|
| 1544 |
-
์ฌ์ ๋ฐฉ๋ฌธ 1
|
| 1545 |
-
๋ช
์ํ๊ธฐ๋ก 1
|
| 1546 |
-
๋ดํฉํ๋ค 1
|
| 1547 |
-
์ ๊ธฐ๋๋ค 1
|
| 1548 |
-
์งํํ๋ฉด 1
|
| 1549 |
-
์
์ฃผ์์ ์๋ค 1
|
| 1550 |
-
์ค์ํ๋๋ฐ 1
|
| 1551 |
-
๋ณ๊ฒฝํ๋ค 1
|
| 1552 |
-
์ฌ์ ๋ฐฉ๋ฌธ์ผ 1
|
| 1553 |
-
์ฃผ๊ฐ๋ณ๋ก 1
|
| 1554 |
-
๊ท ๋ฑํ๊ฒ 1
|
| 1555 |
-
์ ์ฐ์์คํ
1
|
| 1556 |
-
์ผ๋ฐฉ์ ์ผ๋ก 1
|
| 1557 |
-
๊ฐ์ ํด์ผ 1
|
| 1558 |
-
์ง์์ฒด๋ง๋ค 1
|
| 1559 |
-
์ฒ์ฐจ๋ง๋ณ์ด์๋ค 1
|
| 1560 |
-
๊ฐํํด์ผ 1
|
| 1561 |
-
๋ฐฉ๋ฌธํ๊ณ 1
|
| 1562 |
-
์ฐธ์ํ๊ธฐ 1
|
| 1563 |
-
์ถ๊ตญํ๋ค 1
|
| 1564 |
-
๋ฐฉ๋ฌธ๊ตญ์ธ 1
|
| 1565 |
-
์ฐธ๋ฐฐํ๊ณ 1
|
| 1566 |
-
๋งบ์ด์์ผ๋ฉฐ 1
|
| 1567 |
-
์ต์ด์ด์ 1
|
| 1568 |
-
์์ ๋ํ๋ฏผ๊ตญ 1
|
| 1569 |
-
์๋ฐฉ์์ 1
|
| 1570 |
-
์ฉ์ธํ์ง 1
|
| 1571 |
-
๊ฒจ๋ฅํ๊ธฐ 1
|
| 1572 |
-
๋ชจ๋ํฐ๋ง 1
|
| 1573 |
-
ํ์ฑ๋์ด 1
|
| 1574 |
-
์์์ผ๋ก 1
|
| 1575 |
-
ํ๋ฌ๊ฐ๊ณ 1
|
| 1576 |
-
์์๋์ง 1
|
| 1577 |
-
์ฌ๋ก ์กฐ์ฌ์ 1
|
| 1578 |
-
๋ํ๋๋ฉด์ 1
|
| 1579 |
-
์ ๊ฐ์์ 1
|
| 1580 |
-
์์ธกํ๊ธฐ 1
|
| 1581 |
-
๊ฒฐ์ ์ผ๋ก 1
|
| 1582 |
-
ํตํฉ์ง๋ณด๋น 1
|
| 1583 |
-
ํ์ ์ผ๋ก 1
|
| 1584 |
-
์์ฑ์ ๋น 1
|
| 1585 |
-
ํ๋ณดํ๋ค 1
|
| 1586 |
-
๋ค๊ฑฐํฐ๋ธ 1
|
| 1587 |
-
์ผ๊ถ์์ 1
|
| 1588 |
-
์ง๋ณด๋น์ 1
|
| 1589 |
-
๋จ๋์ผ๋ณด 1
|
| 1590 |
-
์ง์งํ๋ค 1
|
| 1591 |
-
์กฐ์ฌ์์ 1
|
| 1592 |
-
์ผ์ฃผ์ผ์ฌ 1
|
| 1593 |
-
๋ค๊ฑฐํฐ๋ธ์ 1
|
| 1594 |
-
๋ฒ์ด๋ฉด์ 1
|
| 1595 |
-
๋ํ์ ์ 1
|
| 1596 |
-
์ง๋ณด๋น์ผ๋ก 1
|
| 1597 |
-
ํ๋ํธ๊ตญ๋จ 1
|
| 1598 |
-
์ดํ์์ฅ 1
|
| 1599 |
-
๊ณต๊ฒฉํ๊ณ 1
|
| 1600 |
-
์งํ์์๋ฒ 1
|
| 1601 |
-
๋ถ๊ฐ๋์ง 1
|
| 1602 |
-
๋ฐ์ด๋ด๊ณ 1
|
| 1603 |
-
๋น์ ๋๋ 1
|
| 1604 |
-
์๊ทนํ๋๋ฐ 1
|
| 1605 |
-
์กฐ์ง์์ 1
|
| 1606 |
-
๋น์ ๋ฌดํจํ 1
|
| 1607 |
-
ํ์ ๋์ 1
|
| 1608 |
-
์๋ด์์ 1
|
| 1609 |
-
๋ค์ด์ฃผ๊ณ 1
|
| 1610 |
-
๋ถ์ถํด์ฃผ 1
|
| 1611 |
-
์๊ด๊ตฐ๋๋ฏผํ 1
|
| 1612 |
-
์์ ๋ถํฐ 1
|
| 1613 |
-
์ง์ง๋ง๋ค 1
|
| 1614 |
-
๋
ผ๋ฐญ๊น์ง 1
|
| 1615 |
-
์๊ตฌํ๋ฉฐ 1
|
| 1616 |
-
ํฌํฐ๋ฆฌ์ฆ 1
|
| 1617 |
-
๊ฒฝ์์ผ๋ก 1
|
| 1618 |
-
์๊ด์ฌ๋์ง์๊ธ 1
|
| 1619 |
-
๊ณต์ฝ์ผ๋ก 1
|
| 1620 |
-
ํ๋ฐ์ด๋ก 1
|
| 1621 |
-
์๊ด๊ตฐ๋
ธ์ธ๋ณต์ง๊ด 1
|
| 1622 |
-
์ป์ด๋๊ณ 1
|
| 1623 |
-
๋ด๋ ค๊ฐ๊ณ 1
|
| 1624 |
-
์์๋ํ 1
|
| 1625 |
-
์ต์ฐ์๋ค 1
|
| 1626 |
-
์ฃผ์ฃผ์ดํ 1
|
| 1627 |
-
๋ํ์ด์ฌ 1
|
| 1628 |
-
์ ์ํ๋ค 1
|
| 1629 |
-
์ ํ์ด๊ด๋ก 1
|
| 1630 |
-
์นด๋ค๊ธฐ๋ฉ๋ก ๋ 1
|
| 1631 |
-
์๋น์๋ 1
|
| 1632 |
-
์ด๋ฒ ์ด์ฝ๋ฆฌ์ 1
|
| 1633 |
-
๋น๊ธ์ต๊ถ 1
|
| 1634 |
-
ํ์ฌ์์ 1
|
| 1635 |
-
ํด์ธ์ฃผ์ 1
|
| 1636 |
-
์ถ์งํ๋ฉฐ 1
|
| 1637 |
-
ํ๋ซํผ์ผ๋ก์ 1
|
| 1638 |
-
๋ํ์ด์ฌ๋ก 1
|
| 1639 |
-
๋ฌผ๋ฌ๋ฌ๋ค 1
|
| 1640 |
-
๊ธฐ๊ฐ์ผ๋ก 1
|
| 1641 |
-
ํ์ ํ๋ฉด 1
|
| 1642 |
-
๊ฐ์ํํ ์๋ก 1
|
| 1643 |
-
์๊ฐ๋์ 1
|
| 1644 |
-
๋น๊ตฌ๋ฆ๋ 1
|
| 1645 |
-
์์ฆ๊ธฐ๋ 1
|
| 1646 |
-
ํ ํด๋ด๋ฏ 1
|
| 1647 |
-
์์ถํ๋ค 1
|
| 1648 |
-
์ฅ๋ง๊ธฐ๊ฐ 1
|
| 1649 |
-
๋ง์์ง๋ฉฐ 1
|
| 1650 |
-
ํธ์ฑํ๊ณ 1
|
| 1651 |
-
์ด์ํ๊ณ 1
|
| 1652 |
-
๋ณต๊ธฐ์กฐ๋ก 1
|
| 1653 |
-
์ด์๋๋ค 1
|
| 1654 |
-
์ด์ด์ง๊ฒ ๋ค 1
|
| 1655 |
-
์์ํ๊ณ 1
|
| 1656 |
-
๋ณด์ด๊ฒ ๋ค 1
|
| 1657 |
-
์ด์ด์ง๊ฒ ๋ค๊ณ 1
|
| 1658 |
-
๊ฒฝ๋จ์ง์ญ 1
|
| 1659 |
-
๋ธ๋ก์ปค๋ก 1
|
| 1660 |
-
์ ๋ ์ด๊ณ 1
|
| 1661 |
-
๋น๋์์์ฅ 1
|
| 1662 |
-
์๊ธฐํ๋ฉด์ 1
|
| 1663 |
-
์ ์น์๊ธ๋ฒ 1
|
| 1664 |
-
์ฐฝ์์ง๊ฒ 1
|
| 1665 |
-
๊ตฌ์ํ๋ฉด 1
|
| 1666 |
-
ํ์ผํ๊ณ 1
|
| 1667 |
-
๊ฐ๋น๋๊ฒ ๋๋๊ณ 1
|
| 1668 |
-
๊ฒ์ฌ์๊ฒ 1
|
| 1669 |
-
์ํ๋ฉด์ 1
|
| 1670 |
-
๊ณคํน์ค๋ฌ์ํ 1
|
| 1671 |
-
์ ์น๋ธ๋ก์ปค 1
|
| 1672 |
-
๊ตญ๋ฏผ๊ป์ 1
|
| 1673 |
-
ํ์ฌํ๊ฒ 1
|
| 1674 |
-
์๊ฐํ์ค 1
|
| 1675 |
-
์์ฌ๊ธฐ๊ด 1
|
| 1676 |
-
์์ ํ๊ณ 1
|
| 1677 |
-
์ ์ํ๊ฒ 1
|
| 1678 |
-
์์ฌํด์ผ 1
|
| 1679 |
-
์ํ์ผ๋ก 1
|
| 1680 |
-
์กฐ์ธ์ผ๋ก 1
|
| 1681 |
-
ํ
๋ ๊ทธ๋จ 1
|
| 1682 |
-
๋ํ๋๊ณ 1
|
| 1683 |
-
๋ถ์ ์ ํ 1
|
| 1684 |
-
๋ถ์ ํํ 1
|
| 1685 |
-
์ค๋ช
ํ๋๋ฐ 1
|
| 1686 |
-
๋น์ฌ์๋ค 1
|
| 1687 |
-
๋ฐ๋ฐํ๋ค 1
|
| 1688 |
-
๋ง๋์๊ณ 1
|
| 1689 |
-
ํ์ด์ค๋ถ 1
|
| 1690 |
-
๋น
ํ
ํฌ๋ค 1
|
| 1691 |
-
ํ์
ํด์ผ 1
|
| 1692 |
-
์ ๋ฝ์ฐํฉ 1
|
| 1693 |
-
๋ํญํ๊ธฐ 1
|
| 1694 |
-
๊ณต๊ฐํ๋๋ก 1
|
| 1695 |
-
ํ๊ณ์ฐ๋ 1
|
| 1696 |
-
์ข
๋ฃ๋๋ฉด 1
|
| 1697 |
-
๊ณต๊ฐํด์ผ 1
|
| 1698 |
-
๋น๊ตญ๋ผ๋ฆฌ 1
|
| 1699 |
-
๊ณต์ ํ๋ 1
|
| 1700 |
-
๊ตญ์ ํ์ฝ์ธ 1
|
| 1701 |
-
์ดํํ๊ธฐ 1
|
| 1702 |
-
๊ตญ๊ฐ๋ณ๋ณด๊ณ ์ 1
|
| 1703 |
-
๋์
ํ๊ณ 1
|
| 1704 |
-
์ ๋ณด๊ตํ 1
|
| 1705 |
-
๊ตํํ๊ณ 1
|
| 1706 |
-
๋น
ํ
ํฌ๋ก 1
|
| 1707 |
-
๊ฐ๊ณตํด์ 1
|
| 1708 |
-
๊ตญ๊ฐ์์ 1
|
| 1709 |
-
์ ๋ฝ์ฒ๋ผ 1
|
| 1710 |
-
์ถ์ง๋๊ณ 1
|
| 1711 |
-
๊ณต๊ฐํ๊ธฐ 1
|
| 1712 |
-
์ ํ๋ผ์ผ 1
|
| 1713 |
-
์๋ฒฝ๊น์ง 1
|
| 1714 |
-
์์ธ๊ฒฝ์ฐฐ์ฒญ 1
|
| 1715 |
-
ํ์ฅ์์ 1
|
| 1716 |
-
์์์ผ๋ฉฐ 1
|
| 1717 |
-
๊ฑฐ๋ถํ์ง 1
|
| 1718 |
-
๋ถ์ถํ๋ ค 1
|
| 1719 |
-
ํฌ์ฐฉ๋๊ธฐ 1
|
| 1720 |
-
๋ค์ฌ๋ค๋ณด๊ณ 1
|
| 1721 |
-
๋ถ๊ฐ๋ฅํ 1
|
| 1722 |
-
์ด์ ๊น์ง 1
|
| 1723 |
-
์ฐจ์ ์์ 1
|
| 1724 |
-
๋ํญ์ด์ 1
|
| 1725 |
-
์ ์ํ๊ณ 1
|
| 1726 |
-
์ฃผ๋ฌธํ๋ค 1
|
| 1727 |
-
๊พธ๋ฒ
๊พธ๋ฒ
1
|
| 1728 |
-
์ฒ ๊ฑฐ๋๋ค 1
|
| 1729 |
-
๊ฐํตํ๋ค 1
|
| 1730 |
-
๊ต์ฐจ๋ก๋ก 1
|
| 1731 |
-
๋๋ก๊ตํต๊ณต๋จ 1
|
| 1732 |
-
๋ถ๊ฐํผํ 1
|
| 1733 |
-
์ฒ ๊ฑฐํ๊ณ 1
|
| 1734 |
-
์กฐ์ฑํ๋ค 1
|
| 1735 |
-
์ฐ๊ฒฐํ๋ค 1
|
| 1736 |
-
์ฌ์๋๋ 1
|
| 1737 |
-
๋์ฌ์ผ๋ก 1
|
| 1738 |
-
์ถํด๊ทผํ 1
|
| 1739 |
-
๋ฐ๋์ฒด๋ฟ 1
|
| 1740 |
-
๋ฐ์ด๋ด๊ธฐ 1
|
| 1741 |
-
์ง์ง๋ถ์งํ 1
|
| 1742 |
-
์ ๋์ฃผ์ 1
|
| 1743 |
-
์ค์ ์ผ๋ก 1
|
| 1744 |
-
๊ฐ์ํ๋๋ฐ 1
|
| 1745 |
-
์์
์ด์ต์ผ๋ก 1
|
| 1746 |
-
์ ์ธํ๋ฉด 1
|
| 1747 |
-
์ ์์๋ค 1
|
| 1748 |
-
์ํฅ์ด๋ค 1
|
| 1749 |
-
์์กฐ์๋ 1
|
| 1750 |
-
์ฒด๊ฒฐํ๋ค 1
|
| 1751 |
-
๋ฐ๋ด๋ฉด์ 1
|
| 1752 |
-
ํฌํธํด๋ฆฌ์ค 1
|
| 1753 |
-
๋ค๋ณํ๋ก 1
|
| 1754 |
-
ํธ๊ดํ๋ฆ 1
|
| 1755 |
-
์๋ํ๊ณ 1
|
| 1756 |
-
๊ตฌ์กฐ์กฐ์ 1
|
| 1757 |
-
์ต๋์น์์ง 1
|
| 1758 |
-
ํด์ด์์ 1
|
| 1759 |
-
์ํฅ์ผ๋ก 1
|
| 1760 |
-
๋ถ์๋๋ค 1
|
| 1761 |
-
๊ธธ์ด์ง๊ณ 1
|
| 1762 |
-
๋น์๊ฒฝ์ 1
|
| 1763 |
-
๋์
ํ์ง 1
|
| 1764 |
-
๋ฏธ๋ํ ๊ตฌ 1
|
| 1765 |
-
ํํธํ์ฐ์ค์ด์ 1
|
| 1766 |
-
์ผ๋ณธ์์ 1
|
| 1767 |
-
๋ ์ง๋์ค๋ผ 1
|
| 1768 |
-
๋ค์ด์ฐ๋๋ฐ 1
|
| 1769 |
-
ํ๋ฃก์ ์ 1
|
| 1770 |
-
๋์์ธํ๋ค 1
|
| 1771 |
-
์ค์นด๋์ผ๋ก 1
|
| 1772 |
-
๊ฐ๊ตฌ์
์ฒด 1
|
| 1773 |
-
๊พธ๋ช๋ค๊ณ 1
|
| 1774 |
-
์ท์ฅ๊น์ง 1
|
| 1775 |
-
๋์์ด๋๋ค 1
|
| 1776 |
-
๊ณ ์ฌํด์ 1
|
| 1777 |
-
์๊ฐํ๊ณ 1
|
| 1778 |
-
๊ฐค๋ฌ๋ฆฌ์คํธ 1
|
| 1779 |
-
์ปจ์คํดํธ 1
|
| 1780 |
-
๊ฑด์ถ๊ฐ๋ 1
|
| 1781 |
-
์ด์ด์ง๋ค 1
|
| 1782 |
-
๊ฐค๋ฌ๋ฆฌ์์ 1
|
| 1783 |
-
๊ฑด์ค์
์ฒด 1
|
| 1784 |
-
ํฌ์ํ์ฌ๋ค 1
|
| 1785 |
-
ํ์๊น์ง 1
|
| 1786 |
-
๋ฒ์ด์ง๊ณ 1
|
| 1787 |
-
์กฐ์ฐ์ด์๋ค 1
|
| 1788 |
-
๋จผ์ ์๊ณ 1
|
| 1789 |
-
์ด๋๋ถํฐ 1
|
| 1790 |
-
์์๋๋ค 1
|
| 1791 |
-
๊ฐ๊ฒฐํ๋ฉด์ 1
|
| 1792 |
-
์ด๋ถํฐ๋ค 1
|
| 1793 |
-
๋์ค๏ฟฝ๏ฟฝ๏ฟฝ์ผ๋ก 1
|
| 1794 |
-
๋์์ด๋๋ก 1
|
| 1795 |
-
์คํธ์ํ 1
|
| 1796 |
-
๊ด๋ฅ์ ์ผ๋ก 1
|
| 1797 |
-
์ํธ๋ ๋ฒ ๋ฅดํธ 1
|
| 1798 |
-
์ ๊ธฐ์ ์ธ 1
|
| 1799 |
-
๊ฐ๊ด๋ฐ๊ณ 1
|
| 1800 |
-
์ดํผํ์ฐ์ค 1
|
| 1801 |
-
๋ฅดํ์ด๋ 1
|
| 1802 |
-
๋์ธ์ํ๋จ 1
|
| 1803 |
-
์ค๋ด๊ฐ๊ตฌ 1
|
| 1804 |
-
๋ฐฐ์นํ๋๋ 1
|
| 1805 |
-
๋ฌ๋ผ์ง๋ค 1
|
| 1806 |
-
์กฐ์ฌ๊ธฐ๊ด 1
|
| 1807 |
-
ํ ๋ฆฌํฉ์ค 1
|
| 1808 |
-
๊ฑธ๋ ธ๋๋ฐ 1
|
| 1809 |
-
๊ตฌ์กฐ๋งํผ 1
|
| 1810 |
-
๋ด์์ด๋ 1
|
| 1811 |
-
๋ ์ง๋์ค์ผ์๋ก 1
|
| 1812 |
-
์ง์ญ๋ฏผ๋ณด๋จ 1
|
| 1813 |
-
์ธ๊ตญ์ธ๋ค 1
|
| 1814 |
-
๊ท๋ํ๋ค 1
|
| 1815 |
-
์์๋ค๋ถํ์ค 1
|
| 1816 |
-
์ค๋์ด๋ 1
|
| 1817 |
-
์ ๋ฝ์์ 1
|
| 1818 |
-
์ธ๊ตญ์ธ๋ค์ด๊ณ 1
|
| 1819 |
-
๊ณ ๊ฐ์ด๋ผ 1
|
| 1820 |
-
ํํ๋ ์ด์ค 1
|
| 1821 |
-
๋ง์กฑ์ํฌ 1
|
| 1822 |
-
์ฐจ๋ณ์ ์ธ 1
|
| 1823 |
-
ํ์ํ๊ณ 1
|
| 1824 |
-
๋์์ธํ 1
|
| 1825 |
-
์ค๋ช
์ด๋ค 1
|
| 1826 |
-
ํผ๋์ฒ๋ก 1
|
| 1827 |
-
๋ ์ง๋์ค๋ผ๊ณ 1
|
| 1828 |
-
ํ์ด์๋ 1
|
| 1829 |
-
์คํผ์คํ
1
|
| 1830 |
-
์๊ธ๋์ผ๋ก 1
|
| 1831 |
-
๊ฐ๋ผ์์ง 1
|
| 1832 |
-
์ฌ์ ํ๊ธฐ 1
|
| 1833 |
-
์ ์คํ๊ฒ 1
|
| 1834 |
-
๋ฐ์ ธ๋ณด๊ณ 1
|
| 1835 |
-
์ ์ถ๋๋ค 1
|
| 1836 |
-
์ฐฉ์ํ๋ค๊ณ 1
|
| 1837 |
-
์ธํฐ๋ทฐํ๋ฉด์ 1
|
| 1838 |
-
๋น์๋์ฑ
์์์ฅ 1
|
| 1839 |
-
์ค์ํ๋ค๋ฉด์ 1
|
| 1840 |
-
์ฑ
์๋น์ 1
|
| 1841 |
-
๋ฏธ๋ํ๊ตญ์ฐ๊ตฌ์ 1
|
| 1842 |
-
๋น๊ตํ๊ณ 1
|
| 1843 |
-
๊ฐ์๋๊ฒฐ 1
|
| 1844 |
-
์ค์ํ์ผ๋ฉฐ 1
|
| 1845 |
-
์ฌ๋ฌด์ด์ฅ 1
|
| 1846 |
-
์ต๊ณ ์์ 1
|
| 1847 |
-
ํ์์์ 1
|
| 1848 |
-
๋ง๋ค์ด์ 1
|
| 1849 |
-
์ ๋ฒํ๊ฒ 1
|
| 1850 |
-
ํ๋ฌ๊ฐ๋์ง 1
|
| 1851 |
-
๋์ด๊ฐ๋์ง 1
|
| 1852 |
-
์กฐ์ฌํ๊ฒ ๋ค 1
|
| 1853 |
-
์์ฌ๋ฒํธ 1
|
| 1854 |
-
์บ ํ์์ 1
|
| 1855 |
-
๊ด๋ จํด์ 1
|
| 1856 |
-
๋๋ ทํด์ผ 1
|
| 1857 |
-
์๊ฐ๊ฐ๋ 1
|
| 1858 |
-
๊ฒฐ๋ก ์
๋๋ค 1
|
| 1859 |
-
์๋ฐ์ด๋ฒ 1
|
| 1860 |
-
๋ฏธ์
์์ 1
|
| 1861 |
-
์น๋ฆฌํ๋ฉฐ 1
|
| 1862 |
-
๋์์ต๋๋ค 1
|
| 1863 |
-
ํด์ฐ๋ฌผ๋ก 1
|
| 1864 |
-
์น์ธ์ดํ๋ฉฐ 1
|
| 1865 |
-
์
ฐํ์๊ฒ 1
|
| 1866 |
-
๋ฌผ์๋๋ 1
|
| 1867 |
-
๋ฐฉ์ก์์ 1
|
| 1868 |
-
์ต์ํ๋ฉด์ 1
|
| 1869 |
-
๋ง๋ค์ด์ผ๊ฒ ๋ค 1
|
| 1870 |
-
์ฃผ์ ํ์ง 1
|
| 1871 |
-
๋๋ ทํ๊ธฐ 1
|
| 1872 |
-
๋ฆฌ๋์๊ฒ 1
|
| 1873 |
-
๋ค์ํฉ๋๋ค 1
|
| 1874 |
-
๋ค์์ต๋๋ค 1
|
| 1875 |
-
์๋ฆ๋ค์ ๋ค๋ฉด 1
|
| 1876 |
-
์ค์ํ์ง 1
|
| 1877 |
-
์ข์ต๋๋ค 1
|
| 1878 |
-
์ผ๋ํด๋ 1
|
| 1879 |
-
์ํ๊น์ง 1
|
| 1880 |
-
์๋๊น์ 1
|
| 1881 |
-
๋๋ก๊ตํต๋ฒ์ 1
|
| 1882 |
-
ํน์ ๋ฒ์ฃ๊ฐ์ค์ฒ๋ฒ๋ฒ์ 1
|
| 1883 |
-
ํ์๊น์ง 1
|
| 1884 |
-
์ฒํ๋๋ก 1
|
| 1885 |
-
์ฉ์ฐ๊ฒฝ์ฐฐ์ 1
|
| 1886 |
-
์ด์ ํ๋ค 1
|
| 1887 |
-
ํ๋จ๋๋ฉด 1
|
| 1888 |
-
๊ฐ๋ฅํ๋ค๊ณ 1
|
| 1889 |
-
์๋น์์ 1
|
| 1890 |
-
์ง์๋๊ธฐ 1
|
| 1891 |
-
์ ์ฉ๋๋ฉด 1
|
| 1892 |
-
๋ถ์ด์ง๊ณ 1
|
| 1893 |
-
๋นํ๊ฑฐ๋ฆฌ๋ฉฐ 1
|
| 1894 |
-
์ฃผํํ๋ค 1
|
| 1895 |
-
์คํ ๋ฐ์ด 1
|
| 1896 |
-
๋ฐ์ํ๋ค 1
|
| 1897 |
-
์ํ์น์ฌ์ 1
|
| 1898 |
-
๊ธฐ์ํ๊ณ 1
|
| 1899 |
-
์ ๊ณ ํ๋ค 1
|
| 1900 |
-
์ ์กฐ์ฌ์ธ 1
|
| 1901 |
-
๋ฉ๋ฅด์ธ๋ฐ์ค๋ฒค์ธ 1
|
| 1902 |
-
์์
์ฌ์ธ 1
|
| 1903 |
-
๋ฉ๋ฅด์ธ๋ฐ์ค๋ฒค์ธ ์ฝ๋ฆฌ์ 1
|
| 1904 |
-
ํ์ฑ์๋์ฐจ 1
|
| 1905 |
-
๋ฒค์ธ ํ์ด๋ธ์
์๋น์ค์ฝ๋ฆฌ์ 1
|
| 1906 |
-
์์ธ์ค์์ง๋ฒ 1
|
| 1907 |
-
์ ์ถํ๋ค๊ณ 1
|
| 1908 |
-
๊ณต์ ๊ฑฐ๋์์ํ 1
|
| 1909 |
-
ํฌํจ๋์ง 1
|
| 1910 |
-
์์ ์๋ค 1
|
| 1911 |
-
์ฃผ์ฅํ๊ณ 1
|
| 1912 |
-
ํ์ฌ๋์ง 1
|
| 1913 |
-
ํฌ๋ฆฌ์คํ ํ 1
|
| 1914 |
-
์คํ์ง์คํค 1
|
| 1915 |
-
์ฅ์ฐฉ๋๋ค 1
|
| 1916 |
-
์์๋ค๋ฉด 1
|
| 1917 |
-
๋ถ๋ดํด์ผ 1
|
| 1918 |
-
์ํด๋ณด๋ค 1
|
| 1919 |
-
๋ฐฐ์ํ๊ฒ 1
|
| 1920 |
-
์์ธ๋๋ค 1
|
| 1921 |
-
๋จ์ ์ผ๋ก 1
|
| 1922 |
-
์ ์ํ์ ์ฝ๋ฆฌ์ 1
|
| 1923 |
-
ํ๋์ฌ๋ฃ 1
|
| 1924 |
-
ํ๋ฆฌํ์ค๋ฐ๋์ฒด 1
|
| 1925 |
-
ํ๋ผํฐ์ค 1
|
| 1926 |
-
์ผ์ฑ์ฆ๊ถ 1
|
| 1927 |
-
๋ฐ๋์ฒด์ฃผ 1
|
| 1928 |
-
์ด์ด๊ฐ๊ณ 1
|
| 1929 |
-
ํต์์ฒด์ก๊ด 1
|
| 1930 |
-
๋ง๋ถ์๋ค 1
|
| 1931 |
-
ํ์์์ 1
|
| 1932 |
-
์คํ์ดํฌ 1
|
| 1933 |
-
๋ฐ๊นฅ์ผ๋ก 1
|
| 1934 |
-
๋ฒค์น์์ 1
|
| 1935 |
-
๋ฐ์ด์ฌ๋๋ 1
|
| 1936 |
-
๋๊ฐ๋ค๊ณ 1
|
| 1937 |
-
์ด์ฉํ๊ณ 1
|
| 1938 |
-
์ธ์ ํ๊ณ 1
|
| 1939 |
-
์ ์์๊ฒ 1
|
| 1940 |
-
์ฃผ์ด์ง๋ค 1
|
| 1941 |
-
๋ถํ์ํ 1
|
| 1942 |
-
๊ฒฝ๊ธฐ์์ 1
|
| 1943 |
-
์ฌ์๋ถ์์ 1
|
| 1944 |
-
์กฐ๋ณ๋ฆฌ๊ทธ 1
|
| 1945 |
-
ํํผ์ ์ถ์ํ 1
|
| 1946 |
-
์ฃผ์ธ๊ณต์ด์๋ค 1
|
| 1947 |
-
๋ค์ด์
์ค 1
|
| 1948 |
-
๋์
ํ๋ค 1
|
| 1949 |
-
์ง๊ธํ๋ค 1
|
| 1950 |
-
๋ณด์์ผ๋ก 1
|
| 1951 |
-
ํ์ดํ๋ ์ด 1
|
| 1952 |
-
์ ๋ํ์ 1
|
| 1953 |
-
ํ์ดํ๋ ์ด์ 1
|
| 1954 |
-
๋ฐ์ํ๋ค 1
|
| 1955 |
-
๋ฐ์ํ๊ธฐ๋ก 1
|
| 1956 |
-
๊ธ์ ์ ์ผ๋ก 1
|
| 1957 |
-
์ ์ธ์ผ๋ก 1
|
| 1958 |
-
์์ฉํ์ง 1
|
| 1959 |
-
ํ๋ณดํ์ฅ 1
|
| 1960 |
-
์ ์ฐฉํ๊ณ 1
|
| 1961 |
-
ํ๋ฐํฌ๋ก 1
|
| 1962 |
-
์์ฅ์ฐ๋ค 1
|
| 1963 |
-
๊ธฐํ์์ 1
|
| 1964 |
-
๋ด์ผํ๋ผ 1
|
| 1965 |
-
๊ฐ๋ผ์์๋ 1
|
| 1966 |
-
๋ฐ์์ณค๋ค 1
|
| 1967 |
-
ํ๋ฐ์ด์๋ค 1
|
| 1968 |
-
์์ด๋๋ฆฌ์ธ 1
|
| 1969 |
-
๋ถ๋ฌ์ก์ง 1
|
| 1970 |
-
ํ
์ค์ค์นด 1
|
| 1971 |
-
๋ฏธ์ณค์ผ๋ 1
|
| 1972 |
-
๋ถํํฌ์ 1
|
| 1973 |
-
์ง์ด์ผํฌ 1
|
| 1974 |
-
๋๋ ค๊ฐ๋ค 1
|
| 1975 |
-
๋ฌด์ํ๋ก 1
|
| 1976 |
-
์นจ๋ฌตํ๋ค 1
|
| 1977 |
-
๊ธฐ๋ถํ๊ธฐ๋ก 1
|
| 1978 |
-
์ฝ์ ํ๋ค๊ณ 1
|
| 1979 |
-
์ถ์์ผ๋ก 1
|
| 1980 |
-
์กธ์
ํ๊ณ 1
|
| 1981 |
-
๋ฏธ๊ตญ์ผ๋ก 1
|
| 1982 |
-
์์ฅ์์ผฐ๋ค 1
|
| 1983 |
-
ํ๋ฏธ์ํธ๋ฐฉ์์กฐ์ฝ 1
|
| 1984 |
-
๋๊ตญ์ผ๋ก 1
|
| 1985 |
-
๋ง๋ จํ๊ธฐ 1
|
| 1986 |
-
์ฐธ์ฌํ๊ณ 1
|
| 1987 |
-
์กฐ์ฑ๋๋ค๊ณ 1
|
| 1988 |
-
์ถ์ํ๋ค๊ณ 1
|
| 1989 |
-
์ค์ธ์๋์ 1
|
| 1990 |
-
์ค์ํ๋ค 1
|
| 1991 |
-
์ผ์ฌ์์ผ๋ก 1
|
| 1992 |
-
์ถ์๋๋ค 1
|
| 1993 |
-
ํฌ๋ํํค 1
|
| 1994 |
-
์งํํ๋ค๊ณ 1
|
| 1995 |
-
๊ธฐ๋ถ๊ทธ๋ผ์ด๋ 1
|
| 1996 |
-
๋ฐฐํ๊ทธ๋ผ์ด๋ 1
|
| 1997 |
-
์ฆ๊ธฐ๋ฉด์ 1
|
| 1998 |
-
๊ณต์ ๊ฑฐ๋์์ํ๋ก 1
|
| 1999 |
-
์์ธ๊ณ ๋ฒ 1
|
| 2000 |
-
๊ณต์ ๊ฑฐ๋๋ฒ 1
|
| 2001 |
-
๋ถ๊ณผํ๋ค 1
|
| 2002 |
-
์กฐ์์ผ๋ก 1
|
| 2003 |
-
๋ถ๋นํ๊ฒ 1
|
| 2004 |
-
์์กํ๊ณ 1
|
| 2005 |
-
ํ๋ณตํ๊ธฐ 1
|
| 2006 |
-
๊ธฐ๊ฐํ๋ค 1
|
| 2007 |
-
์ฑ๊ฒฉ์ด๊ธฐ 1
|
| 2008 |
-
์ ๋นํ์ง 1
|
| 2009 |
-
์ต์ข
์ ์ธ 1
|
| 2010 |
-
์ฌํ์์ 1
|
| 2011 |
-
๋ด๋ ค์ง๋ค 1
|
| 2012 |
-
์ ๋ ฅํ๋ค 1
|
| 2013 |
-
๊ธฐ์
๊ณต๊ฐ 1
|
| 2014 |
-
๋ง๋ฌด๋ฆฌ๋ 1
|
| 2015 |
-
์ธ๋์ฆ๊ถ๊ฑฐ๋์์ํ 1
|
| 2016 |
-
์์๋๋ก 1
|
| 2017 |
-
์์ฅํ๋ฉด 1
|
| 2018 |
-
ํ๋งคํ๋ค 1
|
| 2019 |
-
์ฅ๋ถ๊ฐ์ก 1
|
| 2020 |
-
์ญ์ ํ๋ฉด์ 1
|
| 2021 |
-
์ต๊ณ ์ธ๋ฏผํ 1
|
| 2022 |
-
์์ ์ฐ์ค 1
|
| 2023 |
-
์ง์ํ์๋ค 1
|
| 2024 |
-
ํ๊ธฐํ๊ณ 1
|
| 2025 |
-
๊ฐ์ฑ๊ณต๋จ 1
|
| 2026 |
-
๋จ๋ถ๊ธฐ๋ณธํฉ์์ 1
|
| 2027 |
-
์ ์ ์ ์ผ๋ก 1
|
| 2028 |
-
๊น์๋ด๋ ธ๋๋ฐ 1
|
| 2029 |
-
์ ๊ธฐ๋๋ค 1
|
| 2030 |
-
ํด์๊ตญ๊ฒฝ์ 1
|
| 2031 |
-
์์ญํํ๋ ค 1
|
| 2032 |
-
๊ด์ธกํ๋ค 1
|
| 2033 |
-
๊ตฌ์ฒด์ ์ผ๋ก 1
|
| 2034 |
-
๋ช
์ํ๊ธฐ๋ณด๋ค 1
|
| 2035 |
-
๋ชจํธํ๊ฒ 1
|
| 2036 |
-
์ข
๋ฃ๋๋ค 1
|
| 2037 |
-
๋
ธ๋์ ๋ฌธ 1
|
| 2038 |
-
ํ๊ตญ์ํ 1
|
| 2039 |
-
๋ถํ์ค์ฑ 1
|
| 2040 |
-
์ง์ ํ์ 1
|
| 2041 |
-
๋ฏธ์ณค๋ค๊ณ 1
|
| 2042 |
-
ํ๋๋๊ณ 1
|
| 2043 |
-
์ค์ฅ๊ธฐ์ฑ๊ถ 1
|
| 2044 |
-
๊ณ์๋๋ฉด์ 1
|
| 2045 |
-
์์ ์
๋๋ค 1
|
| 2046 |
-
์์ ์ถ๋ก 1
|
| 2047 |
-
์ ๊ณ ๋ฐ์๋ค 1
|
| 2048 |
-
์ธ์ฒ์ง๋ฒ 1
|
| 2049 |
-
์ ๋ณดํต์ ๋ง 1
|
| 2050 |
-
์ด์ฉ์ด์ง 1
|
| 2051 |
-
์ ๋ณด๋ณดํธ 1
|
| 2052 |
-
์ ๊ณ ํ๋ค๊ณ 1
|
| 2053 |
-
์ฌํ๋ด์ฌ 1
|
| 2054 |
-
๋ช
๋ นํ๋ค 1
|
| 2055 |
-
๋จ์์น๊ตฌ 1
|
| 2056 |
-
์๋ ค์ฃผ๊ณ 1
|
| 2057 |
-
๋๊ฒจ์ก๋ค 1
|
| 2058 |
-
์ ์์ผ๋ก 1
|
| 2059 |
-
๋ชจ์งํ๋ฉด 1
|
| 2060 |
-
๊ด๋ฆฌํ๋ค 1
|
| 2061 |
-
์ ํ๋ฆฌ์ผ์ด์
1
|
| 2062 |
-
๊ฐ์ธ์ ๋ณด 1
|
| 2063 |
-
์
๋ ฅํ๋ฉด 1
|
| 2064 |
-
๋ค๋ ๋์ง 1
|
| 2065 |
-
์น๊ตฌ์๊ฒ 1
|
| 2066 |
-
๊ณ ์ํ๊ธฐ 1
|
| 2067 |
-
๋ด์ธํ๋ค 1
|
| 2068 |
-
์ฐ๋ฐํ๋ฉฐ 1
|
| 2069 |
-
์๋ฉธํ๋ค 1
|
| 2070 |
-
์ค์ํ๋ฉด์ 1
|
| 2071 |
-
์๋น์์ 1
|
| 2072 |
-
๋๋ณด์๋ค 1
|
| 2073 |
-
ํธ์๋น๋ก 1
|
| 2074 |
-
๊ฐ์์ก๋ค 1
|
| 2075 |
-
๋ผ์ํ ๋ค 1
|
| 2076 |
-
์ด์ด๊ฐ์ง 1
|
| 2077 |
-
ํ๋ฐํ๊ฒ 1
|
| 2078 |
-
๊ณต๊ฒฉํ๋ค 1
|
| 2079 |
-
๋๋ธ์คํธ๋ก 1
|
| 2080 |
-
์ฑ๊ณตํ๋ฉด์ 1
|
| 2081 |
-
์คํจํ๋ฉด์ 1
|
| 2082 |
-
์ํฉ์ด๋ฉด 1
|
| 2083 |
-
์ญ์ ํ๋ค 1
|
| 2084 |
-
ํฌ์๋ฒํธ 1
|
| 2085 |
-
๋๋ ธ๋๋ฐ 1
|
| 2086 |
-
์๋๋ฅด๋ 1
|
| 2087 |
-
๋น ๋จ๋ ธ๋ค 1
|
| 2088 |
-
๋ฐ์ผ๋ฉด์ 1
|
| 2089 |
-
๊ธฐ์ธ์ด์ง 1
|
| 2090 |
-
๋ง๋ฌด๋ฆฌํฌ์ 1
|
| 2091 |
-
๋ด์ผ์ํ 1
|
| 2092 |
-
๋ชฐ๋ ธ์ผ๋ 1
|
| 2093 |
-
๋ฐ์ด์ค๋ฅด๋ฉด์ 1
|
| 2094 |
-
๋๋ฑํ๋ค 1
|
| 2095 |
-
์ฑ๊ณตํ๊ณ 1
|
| 2096 |
-
ํฌ์ํ๋ผ์ด๋ก 1
|
| 2097 |
-
์๋น์์ 1
|
| 2098 |
-
๋ถ์งํ๋ค 1
|
| 2099 |
-
๊ตฌ์ํฌ์๋ก 1
|
| 2100 |
-
์ธ์ํ๋ค 1
|
| 2101 |
-
๋ถ๋ดํ์ง 1
|
| 2102 |
-
๋นํ์๊ฐ 1
|
| 2103 |
-
์ธ์๋๋ค 1
|
| 2104 |
-
์ค๋ฅด๋ฉด์ 1
|
| 2105 |
-
๋ํํญ๊ณต 1
|
| 2106 |
-
์ ์ฃผํญ๊ณต 1
|
| 2107 |
-
๋
ธ์ ๋ณ๋ก 1
|
| 2108 |
-
์ธ์ํ๋ค 1
|
| 2109 |
-
ํ๋ฆฌ์ฌ๋ฆผํฝ 1
|
| 2110 |
-
๊ธ๋ฉ๋ฌ๋ฆฌ์คํธ 1
|
| 2111 |
-
๋ณต๊ทํ๋ค 1
|
| 2112 |
-
์์์ง์ 1
|
| 2113 |
-
๋ฐ์์์ 1
|
| 2114 |
-
์ ๊ตญ์ฒด์ก๋ํ 1
|
| 2115 |
-
์์ ์์ 1
|
| 2116 |
-
๋ถ์ฐ๊ด์ญ์ 1
|
| 2117 |
-
์ธ์ฒ๊ด์ญ์ 1
|
| 2118 |
-
๋ชจ์ต์ด์๋ค 1
|
| 2119 |
-
๋ง์๋ค๊ณ 1
|
| 2120 |
-
์ง์ ์ํจ 1
|
| 2121 |
-
์ฌ๋ํด์ค 1
|
| 2122 |
-
๊ฐ์ฌํ๋ค 1
|
| 2123 |
-
๋ถ์ฐธํ๊ณ 1
|
| 2124 |
-
์ฐํธํ๋ฉฐ 1
|
| 2125 |
-
์ ๋ํ๋ฉฐ 1
|
| 2126 |
-
๋ง๋ฌด๋ฆฌํ๋ค 1
|
| 2127 |
-
๊ด์ค์๊ฒ 1
|
| 2128 |
-
ํ๋ตํ๋ค 1
|
| 2129 |
-
๊ฐ์ฌํ๊ณ 1
|
| 2130 |
-
๊ทธ๋ค์๊ฒ 1
|
| 2131 |
-
์กฐ๊ธ์ด๋ผ 1
|
| 2132 |
-
ํํํ๊ณ 1
|
| 2133 |
-
๊ธฐ๋ค๋ ธ๋ 1
|
| 2134 |
-
๋ง์กฑ์ค๋ฝ๋ค 1
|
| 2135 |
-
๋ณํํ๋ฉฐ 1
|
| 2136 |
-
์ข์์ก๋ค 1
|
| 2137 |
-
๊ตญ์ ๋ํ 1
|
| 2138 |
-
๋ณด์ฌ๋๋ฆฌ๊ฒ ๋ค 1
|
| 2139 |
-
์ด๋ค์ผ๋ 1
|
| 2140 |
-
๋นํํ๋ฉด์ 1
|
| 2141 |
-
์ด๋ค์ง๊ณ 1
|
| 2142 |
-
์ด๋ค์ง๋์ง 1
|
| 2143 |
-
์์์๊ณ 1
|
| 2144 |
-
๋ฐํํ๊ณ 1
|
| 2145 |
-
์ถ์ ํ์ง 1
|
| 2146 |
-
๋ณธ๋ชจ์ต์ผ๋ก 1
|
| 2147 |
-
์์๊น๋ผ๊ณ 1
|
| 2148 |
-
์๊ฐํ๊ณ 1
|
| 2149 |
-
๋ง๋ถ์๋ค 1
|
| 2150 |
-
๋จธ๋ฌผ๋ ๋ 1
|
| 2151 |
-
๋ฆ๊ฒ๋๋ง 1
|
| 2152 |
-
์์ฌ์ ๋๋ฐ 1
|
| 2153 |
-
์ง์ญ์ ์ธ 1
|
| 2154 |
-
๋ชปํ๋ค๋ฉด 1
|
| 2155 |
-
์ธ์ํ๊ฒ 1
|
| 2156 |
-
์์๊น์ง 1
|
| 2157 |
-
ํ๊ตญ์ผ๋ก 1
|
| 2158 |
-
์ ๋ฆฌ๋ฉด์ 1
|
| 2159 |
-
์ฃผ๋ชฉํ์ง 1
|
| 2160 |
-
์์ค์ธ๋ฐ 1
|
| 2161 |
-
์ฌ์ธํ๊ณ 1
|
| 2162 |
-
๊ฒช์ด์๋ 1
|
| 2163 |
-
์ ์น์ ์ธ 1
|
| 2164 |
-
ํ์ํํด์จ 1
|
| 2165 |
-
๊ทผ์์ ์ธ 1
|
| 2166 |
-
์๊ฐ๋ก์ 1
|
| 2167 |
-
์ต๋๋ ค์๋ 1
|
| 2168 |
-
๋ฒ์ญ๊ฐ๋ค 1
|
| 2169 |
-
์ฐจ์์์ 1
|
| 2170 |
-
์ ์ํ๋ค 1
|
| 2171 |
-
๋ณด๋ด๋๋ 1
|
| 2172 |
-
์ด์ผ๊ธฐํด์์ง 1
|
| 2173 |
-
ํ๋ชซํ๋ค 1
|
| 2174 |
-
์ถ์ ๋ผ์จ 1
|
| 2175 |
-
์์ฑํ๊ณ 1
|
| 2176 |
-
๊ตญ์ ๋ฌด๋ 1
|
| 2177 |
-
์ธ๊ณ๋ฌธํ 1
|
| 2178 |
-
๋์ด๊ฐ๊ณ 1
|
| 2179 |
-
์ฌํ๊ฐ๋๋ฉฐ 1
|
| 2180 |
-
์ญ์ฌ๊น์ง 1
|
| 2181 |
-
์ฌ์ธต์ ์ผ๋ก 1
|
| 2182 |
-
๋ค์ฌ๋ค๋ณด๋ฉฐ 1
|
| 2183 |
-
๋๋ผ์ธ์ง 1
|
| 2184 |
-
์ธํฐ๋ท์ ๋ฌธ์ํ๋ณด๋ค 1
|
| 2185 |
-
์ทจ๊ธํ๊ณ 1
|
| 2186 |
-
์ ์
์ฃผ๋ถ 1
|
| 2187 |
-
๋ถ๋ฅ๋๋ค 1
|
| 2188 |
-
ํฌ์ฉํ์ง 1
|
| 2189 |
-
๋ชปํ๋ค๋ฉด 1
|
| 2190 |
-
๊ฑฐ๋ฌ๋ค์ด๊ณ 1
|
| 2191 |
-
ํฌ์ฉํ์ 1
|
| 2192 |
-
๋ด์ธ์ฐ์ง 1
|
| 2193 |
-
์ธํฐ๋ท์ํ์ 1
|
| 2194 |
-
๋ ๋๊ฒจ๋ฒ๋ฆฐ๋ค 1
|
| 2195 |
-
์ธํฐ๋ท์ ๋ฌธ์ํ 1
|
| 2196 |
-
์์ฐ์ผ๋ก 1
|
| 2197 |
-
์ธํฐ๋ท์ํ๋ณด๋ค 1
|
| 2198 |
-
๊ฐ๊ณ๋์ถ 1
|
| 2199 |
-
์ธํฐ๋ท์ํ์์ 1
|
| 2200 |
-
์ธํฐ๋ท์ํ์ผ๋ก 1
|
| 2201 |
-
๋ฌ๋ ค๊ฐ๊ณ 1
|
| 2202 |
-
ํํํด์ง 1
|
| 2203 |
-
์ค์ ์ ์ฉ์๋ค 1
|
| 2204 |
-
์ค์ ์ ์ฉ๋์ถ 1
|
| 2205 |
-
์ทจ์ฝ๊ณ์ธต 1
|
| 2206 |
-
๋ฐ๋ ค๋๊ณ 1
|
| 2207 |
-
๋ฐฉ์ฆ์ด๋ค 1
|
| 2208 |
-
๋์ด์๊ธฐ 1
|
| 2209 |
-
๊ธ์ต๋น๊ตญ 1
|
| 2210 |
-
๊ด๋ฆฌํ๊ณ 1
|
| 2211 |
-
์ธํฐ๋ท์ํ๋ง 1
|
| 2212 |
-
์ด์์ด์ต 1
|
| 2213 |
-
๋น๊ธฐ์์ด์ต 1
|
| 2214 |
-
๊ณ ๋ฏผํด์ผ 1
|
| 2215 |
-
๋ง๋ฌด๋ฆฌํ 1
|
| 2216 |
-
์ฐ์
ํต์์์๋ถ 1
|
| 2217 |
-
๊ฐ๋ฐ๊ถ์ธ 1
|
| 2218 |
-
๋ถ์ฌํ๊ณ 1
|
| 2219 |
-
ํน๋ณ์๋น 1
|
| 2220 |
-
ํด์ ๊ด๋ฌผ์์ 1
|
| 2221 |
-
์
๋ฒ์๊ณ ํ๋ค๊ณ 1
|
| 2222 |
-
๊ฐ์ ํ๊ธฐ๋ก 1
|
| 2223 |
-
์์ฐํ๋ฉด 1
|
| 2224 |
-
์กฐ๊ด๋ฃ๋ก 1
|
| 2225 |
-
๊ฑฐ๋๋๋ก 1
|
| 2226 |
-
์ ๋์์ 1
|
| 2227 |
-
๋์ด๋ ์๋ก 1
|
| 2228 |
-
๊ธ์ฆํ๋ฉด 1
|
| 2229 |
-
์ ์ค๋๋ค 1
|
| 2230 |
-
๊ณต์์ ์ผ๋ก 1
|
| 2231 |
-
๊ด๊ตฌ์์ 1
|
| 2232 |
-
๋ฐ๊ฒฌํ๋ฉด 1
|
| 2233 |
-
๊ณ์ฝ์์ 1
|
| 2234 |
-
๋์ด์๋ฉด 1
|
| 2235 |
-
๊ด๋ฆฌ์๋ก 1
|
| 2236 |
-
๋ณต๊ทํ๋ค 1
|
| 2237 |
-
๊ด์ฅํ๊ณ 1
|
| 2238 |
-
์ ๋ต์ ์ธ 1
|
| 2239 |
-
์๊ธ๋๋ 1
|
| 2240 |
-
๊ฐ๋
์ผ๋ก 1
|
| 2241 |
-
๋ฌผ๋ฌ๋์ 1
|
| 2242 |
-
๊ด์ฅํ๋ฉฐ 1
|
| 2243 |
-
๋ฐ์ด์๋ฅธ 1
|
| 2244 |
-
์์ ํ๊ณ 1
|
| 2245 |
-
๋ฐ๋๊ฒ ์ง 1
|
| 2246 |
-
์ ๋ง์ฃผ๋ค 1
|
| 2247 |
-
๋ฐ์ ์ํค๊ณ 1
|
| 2248 |
-
๋ฐ๋ฐํ๊ณ 1
|
| 2249 |
-
์์๋ถ์ด 1
|
| 2250 |
-
ํฌ๋ค์๊ฒ 1
|
| 2251 |
-
๋ถ๋ฐ์ค๋ฆฌ๊ฐ์์ 1
|
| 2252 |
-
๋ง์ด๋ผ๊ณ 1
|
| 2253 |
-
๊ฐ์กฐํ๋ฉฐ 1
|
| 2254 |
-
๋ชธ๋ด์๋ 1
|
| 2255 |
-
๋๋ฅดํธ๋ฌธํธ 1
|
| 2256 |
-
๋น์๋ฅ๊ฑฐ๋ฆฌ๊ณ 1
|
| 2257 |
-
๋์ด๋ด๋ ค์ผ 1
|
| 2258 |
-
๊ฐํ๊ตฐ์ 1
|
| 2259 |
-
์ฌ๊ฐํ๋ค๋ฉด 1
|
| 2260 |
-
๋์ค์๋ผ 1
|
| 2261 |
-
๋์ด๋ด๋ฆฌ 1
|
| 2262 |
-
๋ฏผ์ฃผ์ฃผ์๊ณ 1
|
| 2263 |
-
๋ฐ์ธ์ด์์ง 1
|
| 2264 |
-
๋ง๋ค๊ฒ ๋ค๊ณ 1
|
| 2265 |
-
๊ถ์ข์์ 1
|
| 2266 |
-
๊ตฌ์ฑํ๊ณ 1
|
| 2267 |
-
์์ํ๊ด 1
|
| 2268 |
-
์ ๋ฉด์ผ๋ก 1
|
| 2269 |
-
๋ฐํ๋ฒ์ 1
|
| 2270 |
-
๋ฐ๋ฐํ๋ค 1
|
| 2271 |
-
์๋์๋ค๊ณ 1
|
| 2272 |
-
ํผ๊ฐ๊ธฐ๊ด 1
|
| 2273 |
-
์๊ณ ํ๊ณ 1
|
| 2274 |
-
ํค์ฆ์นดํ 1
|
| 2275 |
-
์๊ฐ๋์ 1
|
| 2276 |
-
์ฐพ์๋ณด๋ 1
|
| 2277 |
-
์์ธํ๋ค 1
|
| 2278 |
-
์ด๋ฅด์ ๋ค 1
|
| 2279 |
-
๊ฒฝ๋ก๋น์ 1
|
| 2280 |
-
์ฅ๊ธฐ์์์ธ์ ๋ฑ๊ธ 1
|
| 2281 |
-
์ด๋ฆฐ์ด์ง 1
|
| 2282 |
-
๊ฐ์ํ๋ค 1
|
| 2283 |
-
์์ธ์๊ต์ก์ฒญ 1
|
| 2284 |
-
์ค๋ฒํ์ด 1
|
| 2285 |
-
์ค๋ฒํ์ด์ผ๋ก 1
|
| 2286 |
-
์
ํ๋ฒ์ค๋ก 1
|
| 2287 |
-
๋ฐ๋ ค์ค๊ณ 1
|
| 2288 |
-
๋ฐ๋ ค๋ค์ค๋ค 1
|
| 2289 |
-
๋น์ทํ๋ค 1
|
| 2290 |
-
์ ์น์์ฅ 1
|
| 2291 |
-
๋
ธ์น์์ผ๋ก 1
|
| 2292 |
-
ํธ์ด๋ผ๊ณ 1
|
| 2293 |
-
์๋ํ๊ณ 1
|
| 2294 |
-
ํ์ต์ง๋ก 1
|
| 2295 |
-
๋ฐฐ๋ฌํ๊ณ 1
|
| 2296 |
-
๋จ์ง์์ 1
|
| 2297 |
-
๊ธฐ๋ถ์ฑ๋ฉ 1
|
| 2298 |
-
์ ์ํ์ 1
|
| 2299 |
-
์์ ์ฃผ๋ค 1
|
| 2300 |
-
์ ์ด๋ค์ด 1
|
| 2301 |
-
๋ถ๋ช
ํ๋ค 1
|
| 2302 |
-
์ ์ง๊ตญ์์ 1
|
| 2303 |
-
์์ค์ด๊ธฐ 1
|
| 2304 |
-
๋๋ ค๊ฐ์ผ 1
|
| 2305 |
-
์ต๊ทผ๋ถํฐ 1
|
| 2306 |
-
๋ด๋ ค๊ฐ๋ฉด์ 1
|
| 2307 |
-
์ฌํด์ง๋ฉด 1
|
| 2308 |
-
๋ฉ๋ผํ ๋ 1
|
| 2309 |
-
๋น๋ ค๋ณด๊ณ 1
|
| 2310 |
-
์ฌ์ฉํด๋ดค๋ค 1
|
| 2311 |
-
์๋ฃ๊ธฐ๊ธฐ 1
|
| 2312 |
-
์ผ์ฑ๋ฉ๋์จ 1
|
| 2313 |
-
์ฐ๊ตฌ์์ฅ 1
|
| 2314 |
-
์ ํ์ด๋ค 1
|
| 2315 |
-
์ฌ๋ฆฌํผ์ 1
|
| 2316 |
-
๊ฑฐ์ฌ๋ฆฌ์ง 1
|
| 2317 |
-
์ด๊ณณ์ผ๋ก 1
|
| 2318 |
-
์ ๋ฌํ๋ค 1
|
| 2319 |
-
์คํธ๋ ์ค 1
|
| 2320 |
-
๋น์ฝ๋ฌผ์ 1
|
| 2321 |
-
์น๋ฃ๋ฒ์ด๋ผ๊ณ 1
|
| 2322 |
-
์์ธ๋ํ๊ต 1
|
| 2323 |
-
๋ถ๋น๋ณ์ 1
|
| 2324 |
-
์ฌ์ฉํ๋๋ 1
|
| 2325 |
-
ํธ์ ๋๋ค๊ณ 1
|
| 2326 |
-
๋ตํ๋ค๊ณ 1
|
| 2327 |
-
์ค์นํ๊ณ 1
|
| 2328 |
-
๋ธ๋ฃจํฌ์ค๋ก 1
|
| 2329 |
-
๊ธฐ๋ณธ์ด๋ค 1
|
| 2330 |
-
์ค๋งํธ๊ธฐ๊ธฐ 1
|
| 2331 |
-
๋ถ์ํด์ฃผ๊ณ 1
|
| 2332 |
-
์ ์ํ๊ธฐ 1
|
| 2333 |
-
์ด๋ ค์ ์ผ๋ 1
|
| 2334 |
-
์ํฐํ๋ 1
|
| 2335 |
-
์ง๋ชฉ๋๋ 1
|
| 2336 |
-
ํ๋์์ค์ฝ 1
|
| 2337 |
-
ํฌ์
ํ์ 1
|
| 2338 |
-
์ง์ ์ผ๋ก 1
|
| 2339 |
-
๋๊ฒจ๋ฒ๋ ธ๋ค 1
|
| 2340 |
-
์ฌ๋ผ๊ฐ์ผ 1
|
| 2341 |
-
๊ฐ๊ฒฉํ๋ค 1
|
| 2342 |
-
๋ฐ์ด๋ค์๋ค 1
|
| 2343 |
-
๋ณผํฐ๋ชจ์ด 1
|
| 2344 |
-
ํ์ด์๋ค 1
|
| 2345 |
-
๋งํ๊น์ง 1
|
| 2346 |
-
๊ธฐ์ฌํ์ํ 1
|
| 2347 |
-
๋ชจ์์๋ค 1
|
| 2348 |
-
๊ทน์ ์ผ๋ก 1
|
| 2349 |
-
ํ์ ํ๋๋ 1
|
| 2350 |
-
์์ ๊ฒฝ๊ธฐ๋ก 1
|
| 2351 |
-
๊ฐ๋ ค์ง๋ค 1
|
| 2352 |
-
๋ฌ์๋ฌ๋ค 1
|
| 2353 |
-
ํ๋ฐ์ผ๋ก 1
|
| 2354 |
-
์ ๋ฆฐํ๋ค 1
|
| 2355 |
-
์๊ณ ๋๋ค 1
|
| 2356 |
-
์ธ๋๋ฌด๋ค๋ฆฌ 1
|
| 2357 |
-
์น๋ถ์์ 1
|
| 2358 |
-
๋ฑํํ๋ค 1
|
| 2359 |
-
๊ฐ๊น์ค๋ก 1
|
| 2360 |
-
๊ฐ๋ผ์์ 1
|
| 2361 |
-
๊ณ ๋ฏผ์ด๋ค 1
|
| 2362 |
-
์ ์ฝ๊ฐ๋ฐ์
์ฒด์ธ 1
|
| 2363 |
-
์ํ
์ค์ ์ธ 1
|
| 2364 |
-
๊ฑฐ๋๋๋ 1
|
| 2365 |
-
๋ฉด์ญํญ์์ 1
|
| 2366 |
-
์ ์ฉ๋๋ค 1
|
| 2367 |
-
์์ฉํ๋ค 1
|
| 2368 |
-
๊ฒฌ์ ํ๊ธฐ 1
|
| 2369 |
-
ํต๊ณผ๋๋ฉด 1
|
| 2370 |
-
๋ฉ๋ฆฌ์ธ ์ฆ๊ถ 1
|
| 2371 |
-
์๋ชจ๋ ํผ์ํฝ 1
|
| 2372 |
-
ํํ์์ด๋ก์คํ์ด์ค 1
|
| 2373 |
-
์ ํ์ง์ฃผ 1
|
| 2374 |
-
์ผ์ฑ์๋ช
1
|
| 2375 |
-
๋์ฅ์ฃผ์ธ 1
|
| 2376 |
-
์๋งค๋ํ๋ฉด์ 1
|
| 2377 |
-
์๋งค๋๋ก 1
|
| 2378 |
-
ํ๋ฝํ๋ฉด์ 1
|
| 2379 |
-
๊ตญ์ ํ์ 1
|
| 2380 |
-
์ด์์ด์ 1
|
| 2381 |
-
ํ์์์ 1
|
| 2382 |
-
๊ธฐํ๋ณํ๋ก 1
|
| 2383 |
-
์
๋งํ๋๊ณ 1
|
| 2384 |
-
ํฌ์ ๋๋ฌผ 1
|
| 2385 |
-
์ข
๋ฅ๋ฟ์ด๊ณ 1
|
| 2386 |
-
๋ถํ๋ ค์ก๋ค 1
|
| 2387 |
-
์๋ดํด์ 1
|
| 2388 |
-
์๋ฅดํจํฐ๋ 1
|
| 2389 |
-
๋ถํฌํ์ฃ 1
|
| 2390 |
-
์ค๋จ๋ฏธ๋ก 1
|
| 2391 |
-
๋ฐ๊ฒฌํ๊ณ 1
|
| 2392 |
-
๋ ์ฌ๋ ค์ 1
|
| 2393 |
-
ํ๋ญ์ด๋ฉฐ 1
|
| 2394 |
-
๋ด๋ ค์์ 1
|
| 2395 |
-
๋ชจ์ต์ด์ 1
|
| 2396 |
-
์ผํ์ฑ์ด๊ธด 1
|
| 2397 |
-
๋ค๋ฅด๋ต๋๋ค 1
|
| 2398 |
-
์ ๊ณต๋นํ 1
|
| 2399 |
-
์ ํํ๊ฑฐ๋ 1
|
| 2400 |
-
์ฌ๋ผ๊ฐ์ 1
|
| 2401 |
-
๋ฐ์ฅ๋ณด๋ค 1
|
| 2402 |
-
๋ฅ์ํ๊ฒ 1
|
| 2403 |
-
ํ๋ฌ๋์ค 1
|
| 2404 |
-
ํ๋ฐ๋ฅ์ผ๋ก 1
|
| 2405 |
-
์ค๋ชฉํ๊ฒ 1
|
| 2406 |
-
๋ค์ด๋ง์๊ธฐ 1
|
| 2407 |
-
๊ตฌ์กฐ์์ 1
|
| 2408 |
-
๋ฌด๊ฒ๊น์ง 1
|
| 2409 |
-
์๋ต๋๋ค 1
|
| 2410 |
-
๋นต๋นตํด์ง 1
|
| 2411 |
-
๋๋ธ๋ฌ์ ธ 1
|
| 2412 |
-
๋ฐ๊ฒฌ๋์ฃ 1
|
| 2413 |
-
๋ง๋ค์ด๋ด 1
|
| 2414 |
-
์๊ธฐ์๊ฒ 1
|
| 2415 |
-
์ผ์ด๋ฌ๋์ง 1
|
| 2416 |
-
์๋ต๋๋ค 1
|
| 2417 |
-
๋ฌผ๋ ธ๋ค๊ณ 1
|
| 2418 |
-
์ด๋ฆฌ์ ๋ฆฌ 1
|
| 2419 |
-
๋ค๋๋ฉด์ 1
|
| 2420 |
-
๋ง๋ค์ด์ 1
|
| 2421 |
-
๊ตฌ์ฑ์๋ค๋ผ๋ฆฌ 1
|
| 2422 |
-
๊ฒ์์ฃผ๊ธฐ 1
|
| 2423 |
-
์๊ฑฐ๋ ์ 1
|
| 2424 |
-
๋จน์ธ๋ต๋๋ค 1
|
| 2425 |
-
์ํ ์์ 1
|
| 2426 |
-
๊ฐ์๋ฐํ 1
|
| 2427 |
-
๋ฐฐ์ถํ๊ณ 1
|
| 2428 |
-
๋ฐฐ์ถํ๋ค 1
|
| 2429 |
-
๋ฑ์ฅํ์ง 1
|
| 2430 |
-
๊ตญ์ ์ฌํ์์ 1
|
| 2431 |
-
์ค๋ก ์ด์๋ค 1
|
| 2432 |
-
์์ํ๋ฉด์ 1
|
| 2433 |
-
์ฑ์ฅํ๊ณ 1
|
| 2434 |
-
๊ธ๋ก๋ฒํ๊ฒ 1
|
| 2435 |
-
๊ณต๋์์ํ 1
|
| 2436 |
-
๋ฌธํ์์ปค๋
1
|
| 2437 |
-
์ ์ง๊ตญ์ธ 1
|
| 2438 |
-
ํตํ์์ 1
|
| 2439 |
-
ํ์คํ๋ค 1
|
| 2440 |
-
ํ์ธํธ์น 1
|
| 2441 |
-
ํ๋ฏธ์ฃผ์์ 1
|
| 2442 |
-
ํจ๊ณผ์ ์ผ๋ก 1
|
| 2443 |
-
์ดํ๋ฆฌ์ 1
|
| 2444 |
-
๋ง๋ผํ๋ฅดํ
1
|
| 2445 |
-
ํ๊ตญ๋ฌธํ 1
|
| 2446 |
-
์ ๊ทผํ๊ณ 1
|
| 2447 |
-
์๋ํ๊ณ 1
|
| 2448 |
-
๊ฐ๊ณผ๋ผ์ 1
|
| 2449 |
-
์ง์ถํ๋ค 1
|
| 2450 |
-
์ด์๋ฐ๋ผ 1
|
| 2451 |
-
๋์ด๋๊ฒ 1
|
| 2452 |
-
๋งบ์๋ค๊ณ 1
|
| 2453 |
-
๋ฒ ์ด์ปค๋ฆฌ 1
|
| 2454 |
-
๊ฑฐ์ ์ผ๋ก 1
|
| 2455 |
-
์ฆ๊ฐํ๊ณ 1
|
| 2456 |
-
๊ณ์ฝ์ผ๋ก 1
|
| 2457 |
-
๋๋จ์์์์์ 1
|
| 2458 |
-
์ง์ถํ๊ฒ 1
|
| 2459 |
-
๋ฐ์ธ์ผ๋ก 1
|
| 2460 |
-
์ผ์ผํจ๋ค 1
|
| 2461 |
-
์ง์ค์ํค๊ธฐ 1
|
| 2462 |
-
์ก๋์์ 1
|
| 2463 |
-
์์ฅ์ด๋ค 1
|
| 2464 |
-
ํ์ฐ์ด๋ค 1
|
| 2465 |
-
์ค๋ฒ๊ฒ์ดํธ์ํ 1
|
| 2466 |
-
์๊ธฐ์์ 1
|
| 2467 |
-
์ธ์๋๋ฉฐ 1
|
| 2468 |
-
ํฌ๋ ๋ํธ์ค์์ค 1
|
| 2469 |
-
์ํํ๊ธฐ 1
|
| 2470 |
-
์๊ฐํ๋ค 1
|
| 2471 |
-
๊ณ ๋ฏผํ์ 1
|
| 2472 |
-
ํ๋ฝํ๋ค 1
|
| 2473 |
-
์ง๋ ์๋ก 1
|
| 2474 |
-
์ฌ๋ผ๊ฐ๋ค 1
|
| 2475 |
-
๋ฉํธ๊ฐํ 1
|
| 2476 |
-
๋ฒ์น์ด๋ 1
|
| 2477 |
-
๋ฉํธ์นผํ 1
|
| 2478 |
-
์ง์ ์ ์ผ๋ก 1
|
| 2479 |
-
๊ธฐํ๊ธ์์ ์ผ๋ก 1
|
| 2480 |
-
๋ฒ์น์ด๋ค 1
|
| 2481 |
-
๊ฐ์ํ๋ ์๋ก 1
|
| 2482 |
-
ํํ๋ณด๋ค 1
|
| 2483 |
-
์ํธํํ 1
|
| 2484 |
-
ํธ๋ผํ๋ 1
|
| 2485 |
-
์คํ ํผ๋๋ 1
|
| 2486 |
-
๋
ธ์ค์จ์คํด 1
|
| 2487 |
-
์์ ์ ์ธ 1
|
| 2488 |
-
์คํํด์ง๋ค๊ณ 1
|
| 2489 |
-
์์ํ๋ฉด์ 1
|
| 2490 |
-
์ผ๋ฐ์ ์ธ 1
|
| 2491 |
-
๋ด๊ธ์ผ๋ก 1
|
| 2492 |
-
์๋ฒ์ง์ธ 1
|
| 2493 |
-
์๊ธ์ผ๋ก 1
|
| 2494 |
-
ํฌ์ํ๊ธฐ 1
|
| 2495 |
-
ํค์๋๊ฐ 1
|
| 2496 |
-
๋ถ๊ดด์๋ค 1
|
| 2497 |
-
์ด์ ๋ณด๋ค 1
|
| 2498 |
-
๋๋ ค์ํ 1
|
| 2499 |
-
๋ํ๋๋ค 1
|
| 2500 |
-
๋ถ์กฑํ๋ค 1
|
| 2501 |
-
๋ ์ด์ค๋ 1
|
| 2502 |
-
๋ฒ์ด๋๋ ค๋ฉด 1
|
| 2503 |
-
์ฌํ์์ 1
|
| 2504 |
-
๊ธฐ๋ค๋ฆฌ๋ 1
|
| 2505 |
-
ํ์ด์จ์ด 1
|
| 2506 |
-
์์ฑํ๋ค 1
|
| 2507 |
-
๋ธ๋ฃจํค๋ฐ 1
|
| 2508 |
-
์ก์๋ด๋ฉฐ 1
|
| 2509 |
-
ํ์ฉํ์ผ๋ 1
|
| 2510 |
-
ํ๊ตญ์ฌ์์คํ 1
|
| 2511 |
-
๋ฌธํฑ๊น์ง 1
|
| 2512 |
-
๋ชจ๋ฅด๊ฒ ๋ค 1
|
| 2513 |
-
์ฐ์นํ๊ณ ์ 1
|
| 2514 |
-
์ํ๋๋ฐ 1
|
| 2515 |
-
์ถ์๋๋ฐ 1
|
| 2516 |
-
์ง๋๊ฐ๊ธธ 1
|
| 2517 |
-
์ฅํ์์ด๋ฉด์ 1
|
| 2518 |
-
์์ด์ธ์ท 1
|
| 2519 |
-
๋นผ์ด๋๋ค 1
|
| 2520 |
-
์ข์ํ์ง 1
|
| 2521 |
-
์ด๋ ค์์ 1
|
| 2522 |
-
์กฐ๊ฑด์ด๋ 1
|
| 2523 |
-
์์๋ค๊ณ 1
|
| 2524 |
-
์ด๋ฐ๋ถํฐ 1
|
| 2525 |
-
ํ๋ จํด์ค๋ฉด์ 1
|
| 2526 |
-
๋
ธ๋ ฅํ๋ค 1
|
| 2527 |
-
ํ๋ค๋ฆฌ์ง 1
|
| 2528 |
-
๋จ๋จํ๊ฒ 1
|
| 2529 |
-
์ฌ๋ผ์ฐ๋ค 1
|
| 2530 |
-
์ ์ฃผ์ผ๋ค์ 1
|
| 2531 |
-
๋ง์คํฐ์ค 1
|
| 2532 |
-
๋ฒ๋ฟ์ด์ง 1
|
| 2533 |
-
์๊ธ์์ 1
|
| 2534 |
-
๋ชจ์ต์ด๋ค 1
|
| 2535 |
-
์๋ฆฌํ๋ค 1
|
| 2536 |
-
๋ฐ๋ผ์๋ค 1
|
| 2537 |
-
ํฉ์ํ๊ณ 1
|
| 2538 |
-
์ผ๊ตฌ๋ณด๋ค 1
|
| 2539 |
-
์๊ฐํ๋๋ฐ 1
|
| 2540 |
-
๋ค์ด๋ง์ผ๋ฉด์ 1
|
| 2541 |
-
ํต๊ณผํ๊ณ 1
|
| 2542 |
-
์ฅ์์์ 1
|
| 2543 |
-
์ฟ ์๋ฐ์ค 1
|
| 2544 |
-
์ ๋ฐํฌ์๋ก 1
|
| 2545 |
-
์ฌ๋ผ์ด๋ 1
|
| 2546 |
-
์ ๊ณ ํ๋ค 1
|
| 2547 |
-
๋ถ์ํ๋ค 1
|
| 2548 |
-
์๋ํ๋ค 1
|
| 2549 |
-
๊ธฐ๋ํ๋ 1
|
| 2550 |
-
์ ๊ท๋ฆฌ๊ทธ์์ 1
|
| 2551 |
-
์ด๋ ๊น์ง 1
|
| 2552 |
-
๋ถ์งํ๋ค 1
|
| 2553 |
-
์์์นด์ดํธ 1
|
| 2554 |
-
๋ฌผ๋ฌ๋๊ธฐ 1
|
| 2555 |
-
๋ชจ๋์๊ฒ 1
|
| 2556 |
-
์ ์คํ๋ค 1
|
| 2557 |
-
๋์ด์๋ค 1
|
| 2558 |
-
๋๊ฒฐ์์ 1
|
| 2559 |
-
ํํธํ๋ค 1
|
| 2560 |
-
์ฅ๊ฑฐ๋ฆฌํฌ 1
|
| 2561 |
-
๋ฑํํ๋ค 1
|
| 2562 |
-
์ฟ ์๋ฐ์ค๋ณด๋ค 1
|
| 2563 |
-
์ด๋ฐ๊น์ง 1
|
| 2564 |
-
ํ๋ผ์ด๋ก 1
|
| 2565 |
-
์ถ๊ฒฉํ๋ค 1
|
| 2566 |
-
๊ณต๋ตํ์ง 1
|
| 2567 |
-
๋์ ํ๊ฒ 1
|
| 2568 |
-
์ฌ๋ฌด์ ํ 1
|
| 2569 |
-
์ ๊ทน์ ์ด์ง 1
|
| 2570 |
-
๊ธฐ์
๋ถ์์ฐ๊ตฌ์ 1
|
| 2571 |
-
๋ฆฌ๋์ค์ธ๋ฑ์ค 1
|
| 2572 |
-
์ถ์งํ๋ฉด์ 1
|
| 2573 |
-
๋
๋ คํ๊ณ 1
|
| 2574 |
-
ํ์ ๋๋ค 1
|
| 2575 |
-
๊ธฐ์
๋ณ๋ก 1
|
| 2576 |
-
๋ฐฐ๋นํ๋ค 1
|
| 2577 |
-
์ฅ๊ตฐ๋ฉ๊ตฐ 1
|
| 2578 |
-
์ผ์ผ์ผฐ๋ 1
|
| 2579 |
-
์ํค์คํ๋์ 1
|
| 2580 |
-
์์ ๊ฒฝ๊ธฐ 1
|
| 2581 |
-
๋๊ฐ์๋ค 1
|
| 2582 |
-
์ ๋ํ์๋ก 1
|
| 2583 |
-
์ด๋ฐ๋๋ฅด 1
|
| 2584 |
-
ํฐ๋จ๋ ธ๊ณ 1
|
| 2585 |
-
์ฑ๊ณตํ์ 1
|
| 2586 |
-
์ฒ๋ฆฌํ๋ฉฐ 1
|
| 2587 |
-
ํ์์์ 1
|
| 2588 |
-
์ข
ํก๋ฌด์งํ๋ค 1
|
| 2589 |
-
์นด์ฐํ๋งํ๋ 1
|
| 2590 |
-
์์ด์ธ์ธ 1
|
| 2591 |
-
์์ ์ผ๋ก 1
|
| 2592 |
-
๋ณผํฐ๋ชจ์ด๋ก 1
|
| 2593 |
-
๋ด์์ผ๋ก 1
|
| 2594 |
-
์ ์ํ๋ฉฐ 1
|
| 2595 |
-
์๋ง๋์ 1
|
| 2596 |
-
์นจ๋ฌต์์ผฐ๋ค 1
|
| 2597 |
-
๊ธฐ๋กํ๋ฉด์ 1
|
| 2598 |
-
๋ง๋ฌด๋ฆฌ๋ก 1
|
| 2599 |
-
์ด์ด์ง๋ฉด 1
|
| 2600 |
-
๋ด๋์ผ๋ฉด์ 1
|
| 2601 |
-
์ธ๊ณ์ ์ผ๋ก 1
|
| 2602 |
-
๋ฌธ๋ฒ๊น์ง 1
|
| 2603 |
-
๋ด๋ฒ๋ ค๋ 1
|
| 2604 |
-
๊ท์ ์ด๋ผ 1
|
| 2605 |
-
๊ณต์๋ฐฉ์ก 1
|
| 2606 |
-
๋
์ผ์ด๋ก 1
|
| 2607 |
-
ํ๊ธฐํ๋ค 1
|
| 2608 |
-
๊ณ ์ ๋ช
์ฌ 1
|
| 2609 |
-
ํ์ฉํ๊ฒ ๋ค 1
|
| 2610 |
-
๊ณต์์ ์ธ 1
|
| 2611 |
-
์์ํ์ธ 1
|
| 2612 |
-
๊ณต์ฉ์ธ์ด๋ก 1
|
| 2613 |
-
๋
์ผ์ธ๋ค 1
|
| 2614 |
-
์ต์ํ๊ฒ 1
|
| 2615 |
-
๋ด๋ ธ๋ค๊ณ 1
|
| 2616 |
-
์ ํต์ด์์ง 1
|
| 2617 |
-
๊ฐํ์์ 1
|
| 2618 |
-
๋นํํ๊ณ 1
|
| 2619 |
-
๋ด๋ฒ๋ ค๋๊ณ 1
|
| 2620 |
-
ํ๋ก ์์ 1
|
| 2621 |
-
๋จธ๋ฆฌ์นด๋ฝ 1
|
| 2622 |
-
์ฌ๋์ด๋ผ๋ฉด 1
|
| 2623 |
-
ํ๋ํํธ๋ฅดํธ 1
|
| 2624 |
-
์ผ๊ฐ์ง์ธ 1
|
| 2625 |
-
์ค๊ฐ์ค๊ฐ 1
|
| 2626 |
-
์ด์ํ๊ฒ 1
|
| 2627 |
-
๋ผ์๋ฃ์ผ๋ฉฐ 1
|
| 2628 |
-
๋ถ๊ฐํผํ๋ค๊ณ 1
|
| 2629 |
-
๋ฒ ๋ฅผ๋ฆฐ์์ ๋ํ 1
|
| 2630 |
-
์ธ์ดํ์ 1
|
| 2631 |
-
์คํ
ํ๋
ธ๋น์น 1
|
| 2632 |
-
ํ๋์ค์ด 1
|
| 2633 |
-
๋ถ์ํ๋ค 1
|
| 2634 |
-
๋จ์ด์ก๋ 1
|
| 2635 |
-
๋ค๋ ค์คฌ๋ค 1
|
| 2636 |
-
๋ฑ์ฅํ๊ณ 1
|
| 2637 |
-
์บ๋ด์ด๋ผ 1
|
| 2638 |
-
์ถ๊ฐํ๋ค 1
|
| 2639 |
-
์
ฐ์ต์คํผ์ด 1
|
| 2640 |
-
๋ค์์ผ๋ก 1
|
| 2641 |
-
ํ์์ด์๋ค 1
|
| 2642 |
-
๋ฑ์ฅ์ํจ๋ค 1
|
| 2643 |
-
์ฝํ์์ 1
|
| 2644 |
-
ํํ๋๋ค 1
|
| 2645 |
-
๋ฒ ์คํธ์
๋ฌ 1
|
| 2646 |
-
์ ๋๋ฉ์ด์
์ผ๋ก 1
|
| 2647 |
-
๋ง๋ค์ด์ง๋ฉด์ 1
|
| 2648 |
-
์ ๋ช
ํด์ก์ง 1
|
| 2649 |
-
๋ฉธ์ข
ํ๊ธฐ 1
|
| 2650 |
-
๋ชจ๋ฆฌ์
์ค์ 1
|
| 2651 |
-
์์ํ๋ค 1
|
| 2652 |
-
๋ชจ๋ฆฌ์
์ค 1
|
| 2653 |
-
๋์ฐฉํ๋ฉด์ 1
|
| 2654 |
-
์ง๊ตฌ์์ 1
|
| 2655 |
-
์ฌ๋ผ์ก๋ค 1
|
| 2656 |
-
๋ฉธ์ข
์ํค๊ณ 1
|
| 2657 |
-
๋ค๋ฑ๊ฑฐ๋ฆฌ๋ฉด์ 1
|
| 2658 |
-
๋ชปํ์ผ๋ฉฐ 1
|
| 2659 |
-
๋ฉ์ฒญํ๊ณ 1
|
| 2660 |
-
๊ธฐ๋ก์ผ๋ก 1
|
| 2661 |
-
ํฌ๋ฅดํฌ๊ฐ์ด๋ก 1
|
| 2662 |
-
์๋ฏธ์์ 1
|
| 2663 |
-
์ ๋ํ๋ค 1
|
| 2664 |
-
๊ตญ์ ํ์ ์ง 1
|
| 2665 |
-
์์์ผ๋ฉฐ 1
|
| 2666 |
-
์ดํด๋ณด์ 1
|
| 2667 |
-
๋ฒ์ฑํ๋ฉด์ 1
|
| 2668 |
-
๋ฉ์ฒญํด์ 1
|
| 2669 |
-
์๋์ฒ๋ผ 1
|
| 2670 |
-
์๋ง์ด์๋ค 1
|
| 2671 |
-
์ฐํํฌ๋ฃจ์ฆ 1
|
| 2672 |
-
๊ต์์ด์ 1
|
| 2673 |
-
์๋ช
๊ณตํ 1
|
| 2674 |
-
๋ฐ์ด์ค์ฌ์ด์ธ์ค 1
|
| 2675 |
-
์ต๊ณ ๊ณผํ์ฑ
์์ 1
|
| 2676 |
-
์ค๊ธฐ์ธํฌ 1
|
| 2677 |
-
์ฝํํ๊ฒ 1
|
| 2678 |
-
ํ๋ณธ์์ 1
|
| 2679 |
-
์ป์๋ค๊ณ 1
|
| 2680 |
-
์๊ฐ๋ฆฐ๋ค 1
|
| 2681 |
-
๊ณตํต์กฐ์ 1
|
| 2682 |
-
์งํํ๊ธฐ 1
|
| 2683 |
-
๋๋์ธ์ง 1
|
| 2684 |
-
์์ ํ๋ค 1
|
| 2685 |
-
ํ์ฆ๋ฉ์ด๋์ 1
|
| 2686 |
-
ํฐ์ฝ๋ฟ์ 1
|
| 2687 |
-
์ํ๊ฐ๋
1
|
| 2688 |
-
ํฌ์ํ๋ค 1
|
| 2689 |
-
์ ๋ช
์ธ๋ค 1
|
| 2690 |
-
๊ตฌ์ฑ์์ผ๋ก 1
|
| 2691 |
-
๊ตด์ข
์ ์ธ 1
|
| 2692 |
-
์ธ์ฐ์์ฅ 1
|
| 2693 |
-
๋ถ๊ตฌํ๊ณ 1
|
| 2694 |
-
๊ผฝ์ผ๋ผ๋ฉด 1
|
| 2695 |
-
๋ฌธ์ ์ด์ง 1
|
| 2696 |
-
์์์ ์ธ 1
|
| 2697 |
-
๊ฑฐ์ค๋ฅด๊ณ 1
|
| 2698 |
-
์์๋๊ณ 1
|
| 2699 |
-
์ฌ๊ฐํด์ก๋ค 1
|
| 2700 |
-
์ง์ง์๋ค 1
|
| 2701 |
-
๊ทน๋จ์ ์ธ 1
|
| 2702 |
-
๋ด๋ก๋จ๋ถ๋ก 1
|
| 2703 |
-
์ฝํฌ๋ฆฌํธ 1
|
| 2704 |
-
์ํ์ธ๋ค 1
|
| 2705 |
-
๋์์๊ฒ 1
|
| 2706 |
-
์ต๋์น์ธ 1
|
| 2707 |
-
๊ฑท์ด๋ถ์ด๊ณ 1
|
| 2708 |
-
๊ธ์ต์์ํ 1
|
| 2709 |
-
์ ์ฑ
์ค์ฅ 1
|
| 2710 |
-
๊ฒฝ์ ์์ 1
|
| 2711 |
-
ํ์ด์ฅ๊ณ 1
|
| 2712 |
-
๋ถ๋ฌ์ค๊ธด 1
|
| 2713 |
-
๊ฐ๊ณ๋ถ์ฑ 1
|
| 2714 |
-
ํ๊ธฐ์ํฌ 1
|
| 2715 |
-
๊ธ์ต๊ฐ๋
์์ฅ์ผ 1
|
| 2716 |
-
์์ฐ์ฑ์น 1
|
| 2717 |
-
์ฅ์ฐจ๊ด๋ค 1
|
| 2718 |
-
๋ฐ์ด๋ค์ด 1
|
| 2719 |
-
๋์ง์ง๊ฒ 1
|
| 2720 |
-
๋
ธ๋ ฅํ๊ณ 1
|
| 2721 |
-
์๋น์ฃผ๋ค 1
|
| 2722 |
-
์ ์ํ๊ณ 1
|
| 2723 |
-
๋ง๋ จํ๋ ค๊ณ 1
|
| 2724 |
-
ํด๋ฌ์คํฐ 1
|
| 2725 |
-
์ด๋ํ ์ง 1
|
| 2726 |
-
์ก๋ฐฐ์ ๋ง 1
|
| 2727 |
-
ํด๊ฒฐ๋๊ณ 1
|
| 2728 |
-
์ถ์งํ๋ค 1
|
| 2729 |
-
๋ง์ถ์๊ฑฐ๋ 1
|
| 2730 |
-
์๋ํ๋ค๊ฐ 1
|
| 2731 |
-
์ฃผ๋์ ์ผ๋ก 1
|
| 2732 |
-
๋น์ฐํ๋ค 1
|
| 2733 |
-
์ฌ๋์ด๋ผ 1
|
| 2734 |
-
๋๋๋ฌ์ง 1
|
| 2735 |
-
๋ค์๋ค๊ณ 1
|
| 2736 |
-
์ ๊ถ์์ 1
|
| 2737 |
-
๋ถ๋ฅ๋๊ธธ 1
|
| 2738 |
-
ํ์์ด๋ผ 1
|
| 2739 |
-
๋งํด์ค๋ค 1
|
| 2740 |
-
์์ธต๋ถ์์ 1
|
| 2741 |
-
์ฌ๋ผ์ง๋ฉด 1
|
| 2742 |
-
๋ง๋ฌดํ๋ค 1
|
| 2743 |
-
์ฑ
์์ง๊ณ 1
|
| 2744 |
-
์ฅ๋ฒฝ์ผ๋ก 1
|
| 2745 |
-
์๋ฉ๋ดํคํธ 1
|
| 2746 |
-
๋ฐ๋น๋ก๋์ 1
|
| 2747 |
-
๋ค๋ถ์นด๋๋ค์๋ฅด 1
|
| 2748 |
-
ํ ๋ชฉ๊ณต์ฌ๋ผ 1
|
| 2749 |
-
์ธ์ ๊น์ง๋ 1
|
| 2750 |
-
๋ง์์ฃผ์ง 1
|
| 2751 |
-
๋ฐฉ์ง์ฉ์ผ๋ก 1
|
| 2752 |
-
ํ
๋ฌ๋จ์ฒด 1
|
| 2753 |
-
์ผ์๊น์ง 1
|
| 2754 |
-
์๋ฅด๋จ๊ฐ 1
|
| 2755 |
-
ํ๋๋ฆฌ์๋์ค 1
|
| 2756 |
-
๋ณ์ง๋๋ค 1
|
| 2757 |
-
๊ตฌ์ถํ๋ค 1
|
| 2758 |
-
์๋
์ผ๋ก 1
|
| 2759 |
-
๋์ผ๋ ค๋ค 1
|
| 2760 |
-
์ํ๊ฐ๋์คํ 1
|
| 2761 |
-
๋ด์ํ๊ณ 1
|
| 2762 |
-
๋ชจ์์ด๋ค 1
|
| 2763 |
-
์ฅ๋ฒฝ์ด๋ค 1
|
| 2764 |
-
์ฝํธ๋ฆฌํธ 1
|
| 2765 |
-
๊ฐ์ฅ์ผ๋ก 1
|
| 2766 |
-
๋ก๋ง์ธ๋ค 1
|
| 2767 |
-
๊ฐ๋ฅํ์ง 1
|
| 2768 |
-
๊ด์ฃผ๊ด์ญ์ 1
|
| 2769 |
-
์กธ์
ํ๋ 1
|
| 2770 |
-
์์ธ์ผ๋ก 1
|
| 2771 |
-
๋ฑ๋จํ๋ค 1
|
| 2772 |
-
์ด๋ฌํด์ธ 1
|
| 2773 |
-
๋จํธ์์ค 1
|
| 2774 |
-
์์ธ์ ๋ฌธ 1
|
| 2775 |
-
์ ์ถ๋ฌธ์ 1
|
| 2776 |
-
์์ค๊ฐ๋ก 1
|
| 2777 |
-
๋ฐ๋ทํ๋ค 1
|
| 2778 |
-
์๊ฐ์ด๊ฑฐ๋ 1
|
| 2779 |
-
ํ๋ก ๊ฐ์ธ 1
|
| 2780 |
-
์ด๋ฆ์ด๋ผ 1
|
| 2781 |
-
ํ๋๋ฆผ์จ 1
|
| 2782 |
-
์์ค๊ฐ์ด๊ณ 1
|
| 2783 |
-
๊ฒฝํฌ์ฌ์ด๋ฒ๋ 1
|
| 2784 |
-
๋ฏธ๋์ด๋ฌธ์์ฐฝ์ 1
|
| 2785 |
-
๋ฌธํํ๋ก ๊ฐ๋ค 1
|
| 2786 |
-
๋ฌธ์์ฐฝ์๊ณผ 1
|
| 2787 |
-
๋๋ฆฌ๋ฌธํ์ 1
|
| 2788 |
-
ํฉ์์๋ฌธํ์ 1
|
| 2789 |
-
๋ฐ์๋๋ฐ 1
|
| 2790 |
-
ํ์น์์จ 1
|
| 2791 |
-
์ข์ํ๋ค 1
|
| 2792 |
-
๊ณ ๋ฑํ๊ต 1
|
| 2793 |
-
์ฐ๊ฒ ๋ค๋ฉฐ 1
|
| 2794 |
-
์ ํํ๋๋ 1
|
| 2795 |
-
์๋ฌ๋ฆฌ๋ค 1
|
| 2796 |
-
๊ฑฐ๋ถํ๊ฒ 1
|
| 2797 |
-
๋์ด๊ฐ๋ค๊ณ 1
|
| 2798 |
-
ํธ๋ผ์ฐ๋ง๋ก 1
|
| 2799 |
-
์์ ์์ 1
|
| 2800 |
-
์ฐ์์์ค 1
|
| 2801 |
-
๋ฒ ์คํธ์
๋ฌ๋ค 1
|
| 2802 |
-
์์ํ๋ค 1
|
| 2803 |
-
๋์ค์์ค๊ฐ์ด๊ธฐ๋ณด๋ค 1
|
| 2804 |
-
์ ์์์ค๊ฐ๋ค 1
|
| 2805 |
-
์ฒ์๋ถํฐ 1
|
| 2806 |
-
๋ค๋ฃธ์ผ๋ก์จ 1
|
| 2807 |
-
์ฃผ๋ชฉ๋ฐ์ง 1
|
| 2808 |
-
ํ๋์ด๋ฉด์ 1
|
| 2809 |
-
์๊ฐ๋ถํฐ 1
|
| 2810 |
-
์ถ์ ํด์จ 1
|
| 2811 |
-
์ผ์ด๋๊ธฐ 1
|
| 2812 |
-
์ด์ฌํ๋ 1
|
| 2813 |
-
์๋ฐ์ ์ผ๋ก 1
|
| 2814 |
-
๋ค์ด์๋ค 1
|
| 2815 |
-
์ฃผ์ ์์ 1
|
| 2816 |
-
์ํ์ผ๋ก 1
|
| 2817 |
-
๋ฌด๊ณ ํ๊ฒ 1
|
| 2818 |
-
๊ฐ์ฐ์์ 1
|
| 2819 |
-
๋ค์ฌ๋ค๋ณด๋ฉด 1
|
| 2820 |
-
๋งน์ธ์ด์ 1
|
| 2821 |
-
๊ถ๊ทน์ ์ธ 1
|
| 2822 |
-
์์ ์ผ๋ก 1
|
| 2823 |
-
ํ์ด๋๋ค 1
|
| 2824 |
-
๋ฌธํ์์ธ 1
|
| 2825 |
-
์๋ฒ ๋ฅดํ 1
|
| 2826 |
-
์ฒ์์ด์๋ค 1
|
| 2827 |
-
์๋ฃ์ง๋ค 1
|
| 2828 |
-
์์๋๋ฐ 1
|
| 2829 |
-
๋ณด๊ฑด๋ณต์ง์์ํ 1
|
| 2830 |
-
์ง๊ณ์์ํ 1
|
| 2831 |
-
์ํ์ด๋ฉฐ 1
|
| 2832 |
-
์ธ์ฌ์์ํ 1
|
| 2833 |
-
๊ตญ๋ฏผ๊ถ์ต์์ํ 1
|
| 2834 |
-
์กฐ์น์ด๋ค 1
|
| 2835 |
-
ํนํ์ด๋ฉฐ 1
|
| 2836 |
-
๊ฒ์ด๋ฏ๋ก 1
|
| 2837 |
-
๋์์ด๋ผ๊ณ 1
|
| 2838 |
-
์ฒ๋ฆฌํ๋ค 1
|
| 2839 |
-
์์ธ๋๋ณ์ํ 1
|
| 2840 |
-
์์ ํ๋ฉด 1
|
| 2841 |
-
๋ฌธ์ํ์ 1
|
| 2842 |
-
์์ ๋ฐ๊ธฐ 1
|
| 2843 |
-
์ํ์ ์ผ๋ก 1
|
| 2844 |
-
์ถ๋ํ๋๋ก 1
|
| 2845 |
-
๋ช
ํํ๊ฒ 1
|
| 2846 |
-
์ฌ์ ๋นํด์ผ 1
|
| 2847 |
-
์ฌ์ฅ๋ง๋น๋ก 1
|
| 2848 |
-
๋ณต๊ทํ๊ณ 1
|
| 2849 |
-
์ ๋ฐ์์ 1
|
| 2850 |
-
์ง๋ช
๋๋ค 1
|
| 2851 |
-
๋ด๋น์ณค๊ธฐ 1
|
| 2852 |
-
ํ์ฌ๊น์ง 1
|
| 2853 |
-
์๋จธ๋ฆฌ๊ทธ 1
|
| 2854 |
-
์ ๋๋ผ๋ฉด 1
|
| 2855 |
-
๋ฏธ๋ค์ํ 1
|
| 2856 |
-
ํ๋ฒ์ธ๋ธ์ค 1
|
| 2857 |
-
๋ง๋๊ฒฐ์์ 1
|
| 2858 |
-
์ญ์ฌ์์ 1
|
| 2859 |
-
๊ธฐ๋ก๋๋ค 1
|
| 2860 |
-
์ธ๋ฉดํ๋ค 1
|
| 2861 |
-
์ ๋ฌธ๋งค์ฒด 1
|
| 2862 |
-
๋๋ํํธ 1
|
| 2863 |
-
๋์ด์์ง 1
|
| 2864 |
-
๋ ์ด์ปค์ค์์ 1
|
| 2865 |
-
ํนํํ๋ค 1
|
| 2866 |
-
์ ๋ฐฐ์ด์ 1
|
| 2867 |
-
๋ธ๋ก๋๋ผ๋ฉด 1
|
| 2868 |
-
ํด๋ฌ๋ผ๊ณ 1
|
| 2869 |
-
ํ๊ตญ๋ฌธํ์ฌ๋ณด์กด๊ณผํํ 1
|
| 2870 |
-
์ดํ๋งค์ 1
|
| 2871 |
-
์ธ๋ฌธํ์ ์ 1
|
| 2872 |
-
๊ฐ์๊ต์ 1
|
| 2873 |
-
์์ ๊ต์ํ๋ถ 1
|
| 2874 |
-
๋ฐ๋์ฒด์์ฐ์ค๋น 1
|
| 2875 |
-
์จ์ดํผ๋ง์คํฐ 1
|
| 2876 |
-
๋ํ์ด๊ธฐ 1
|
| 2877 |
-
์ํํธ์จ์ด 1
|
| 2878 |
-
๋น๊ตํ๋ค 1
|
| 2879 |
-
๊ฒน์ณ๋ณด๋ฉด 1
|
| 2880 |
-
์ฆ๋ ค๋ฐ๊ณ 1
|
| 2881 |
-
๊ตฌ์ ์์ 1
|
| 2882 |
-
์กฐํํด์ 1
|
| 2883 |
-
ํ์์ด๋ผ 1
|
| 2884 |
-
ํํ์ธ์ 1
|
| 2885 |
-
ํ์ชฝ์ผ๋ก 1
|
| 2886 |
-
์ธ์๋๋ค 1
|
| 2887 |
-
ํ์์์ผ๋ก 1
|
| 2888 |
-
๋ฏธ๋ฃจ์ด๋ดค 1
|
| 2889 |
-
์ง๋ฌ๋๊ฝ 1
|
| 2890 |
-
ํ๊ณ์์ 1
|
| 2891 |
-
์ด๊ฐ๋ณธ์ผ๋ก 1
|
| 2892 |
-
์ฒจ์ํ๊ฒ 1
|
| 2893 |
-
๋ฌธํ์์ง 1
|
| 2894 |
-
๋ฐ๊ฒฌํ๋ค๋ฉฐ 1
|
| 2895 |
-
๊ฐ์ต๋๋ค 1
|
| 2896 |
-
๋์ฐธํด์ฃผ๊ธธ 1
|
| 2897 |
-
๋ชฌํธ๋ฆฌ์ฌ 1
|
| 2898 |
-
์ธ๊ถ๋จ์ฒด 1
|
| 2899 |
-
๊ฐ์ธ์ฌ์ ์ผ๋ก 1
|
| 2900 |
-
ํ์์ฐ๊ฒฐ๋ก 1
|
| 2901 |
-
๊ฐ์ ๋ถ์ก 1
|
| 2902 |
-
์ฆ์ธํ๋ค 1
|
| 2903 |
-
๊ตญ์ ํ๋ํ 1
|
| 2904 |
-
์์์ผ๋ก 1
|
| 2905 |
-
์ด์ผ๊ธฐํ๋ค 1
|
| 2906 |
-
๋๋ผ์ํ๋ค๊ณ 1
|
| 2907 |
-
์ค๊ตญ์์ 1
|
| 2908 |
-
๋ถ์ก์ํจ 1
|
| 2909 |
-
์ฌ๊ณผํ๊ณ 1
|
| 2910 |
-
๋ฐํ๋ค๊ณ 1
|
| 2911 |
-
์ต์ํ์ง 1
|
| 2912 |
-
๊ฐ๋ฅด์ณ์ฃผ๊ณ 1
|
| 2913 |
-
๊น์ผํ์จ 1
|
| 2914 |
-
ํ๋ถํ๋ค 1
|
| 2915 |
-
์ ์๋ณธ๋ถ 1
|
| 2916 |
-
์์ ๋ณด์ฅ์ด์ฌํ 1
|
| 2917 |
-
๋นํํ๊ธฐ 1
|
| 2918 |
-
์ง์ํ๋๋ก 1
|
| 2919 |
-
ํ๋ณด์ด์ค 1
|
| 2920 |
-
๊ทํํ๊ณ 1
|
| 2921 |
-
๊ณต๋์ฑ๋ช
1
|
| 2922 |
-
์์์์ 1
|
| 2923 |
-
์คํ๋๋ค๋ฉด 1
|
| 2924 |
-
์๊ตฌ์ ์ผ๋ก 1
|
| 2925 |
-
์ ์ธํ๋ค 1
|
| 2926 |
-
์กฐ์ ์ธ๋ฏผ๊ตฐ 1
|
| 2927 |
-
๋์ด๋ฒ๋ฆฌ๊ณ 1
|
| 2928 |
-
๋ฐฉ์ด์ถ์ฑ๋ฌผ๋ก 1
|
| 2929 |
-
์์ํํ 1
|
| 2930 |
-
์ ํํต์ง๋ฌธ 1
|
| 2931 |
-
๊ตฐ์ฌํ๋ จ 1
|
| 2932 |
-
ํต์ ๋ต์์ฐ 1
|
| 2933 |
-
์ด๊ฑฐํ๋ฉฐ 1
|
| 2934 |
-
์ด์ด์ค๊ณ 1
|
| 2935 |
-
๋ฌผ๋ฆฌ์ ์ผ๋ก 1
|
| 2936 |
-
๋ณด์ฌ์ฃผ๊ธฐ 1
|
| 2937 |
-
ํฉ๋์ฐธ๋ชจ๋ณธ๋ถ 1
|
| 2938 |
-
ํ์๋ณ๊ฒฝ 1
|
| 2939 |
-
์ข์ํ์ง 1
|
| 2940 |
-
์ฌ๋ผ์ง๊ณ 1
|
| 2941 |
-
๊ณต๊ณ ํด์ง๋ฉด์ 1
|
| 2942 |
-
๊ตฌ์ถํ๊ธฐ 1
|
| 2943 |
-
ํ์ฑํ๋ 1
|
| 2944 |
-
๋์ํ๊ธฐ 1
|
| 2945 |
-
์ง์คํ๋ค 1
|
| 2946 |
-
๋ฐํํ๋ฉฐ 1
|
| 2947 |
-
์ ์ํ๋ค 1
|
| 2948 |
-
์ ์ฅํ๊ณ 1
|
| 2949 |
-
์ด๋์ํค 1
|
| 2950 |
-
์ ์ฒด์์ 1
|
| 2951 |
-
ํ๋งคํ๊ณ 1
|
| 2952 |
-
ํ์ฅํ๊ธฐ๋ก 1
|
| 2953 |
-
์ฃผ๋ ฅ์ธ๋ฐ 1
|
| 2954 |
-
๋ฏธ๋๋์ผ 1
|
| 2955 |
-
์ค์ ๊ฐํ 1
|
| 2956 |
-
์๋น์๊ฒ 1
|
| 2957 |
-
์์ค๊น์ง 1
|
| 2958 |
-
์ํํ๊ฒ ๋ค 1
|
| 2959 |
-
ํต์ ํ์ 1
|
| 2960 |
-
๋ฐฐ๋ฌ์
๊ณ 1
|
| 2961 |
-
์ ๋ ฅํ๊ฒ 1
|
| 2962 |
-
์์ธ์
์ฒด์ผ์๋ก 1
|
| 2963 |
-
๋ฎ์ถฐ์ค๋ค 1
|
| 2964 |
-
๊ตฌ์ฒด์ ์ธ 1
|
| 2965 |
-
ํ์ ๋์ง 1
|
| 2966 |
-
์์์ผ๋ 1
|
| 2967 |
-
์์๋ฃ์จ๊น์ง 1
|
| 2968 |
-
๊ณ ๋ คํ๊ณ 1
|
| 2969 |
-
๋ฌธ์ ์์ํ 1
|
| 2970 |
-
์ถ๋ฒํ๋ค 1
|
| 2971 |
-
์ขํ์ง์ง 1
|
| 2972 |
-
๊ท์จํด์ผ 1
|
| 2973 |
-
์๊ทน์ ์ธ 1
|
| 2974 |
-
๊ฐ๋ ฅํ๊ฒ 1
|
| 2975 |
-
์ ์ฌํด์ผ 1
|
| 2976 |
-
์ทจ์ง์๋ค 1
|
| 2977 |
-
์ํ์ ๋ 1
|
| 2978 |
-
์ง์ญ์์ 1
|
| 2979 |
-
์ ์ฉํ๊ณ 1
|
| 2980 |
-
๋์
ํ๋ค 1
|
| 2981 |
-
์ ํธ์๋ค 1
|
| 2982 |
-
์ ๋ฐฉ์์ 1
|
| 2983 |
-
๋
๊ณผ์ ์ 1
|
| 2984 |
-
์๋น์ผ๋ก 1
|
| 2985 |
-
์ด๋ฌํด์ 1
|
| 2986 |
-
์ ๋ฅ ์ ๋ก 1
|
| 2987 |
-
์ ํํ๋ค 1
|
| 2988 |
-
๋ฐ๋ฐํ๋ค 1
|
| 2989 |
-
์ฐ๋ฒ์ด์ธ 1
|
| 2990 |
-
์ ์คํธ์ 1
|
| 2991 |
-
์นํจ์ด๋ 1
|
| 2992 |
-
ํ์ ๋๋ 1
|
| 2993 |
-
๋ค์ํด์ก๋ค 1
|
| 2994 |
-
๋์ด์ก๋ค 1
|
| 2995 |
-
๊ณผ์ ํ๋ฉด์ 1
|
| 2996 |
-
์ฆ๊ฐ์ํจ 1
|
| 2997 |
-
์ง๋จํ๋ค 1
|
| 2998 |
-
๊ฐ๋นํ์ง 1
|
| 2999 |
-
๋ถ๋ดํด์ 1
|
| 3000 |
-
๋ฐ์ํ์ง 1
|
| 3001 |
-
๋ฌด์ฐ๋๋ค 1
|
| 3002 |
-
ํ๊ฒฌ๊ฒ์ฌ 1
|
| 3003 |
-
ํ๋ณด์์ถ์ฒ์ 1
|
| 3004 |
-
๊ตฌ์ฑ์์ 1
|
| 3005 |
-
์์คํน๊ฒ๋ฒ์ 1
|
| 3006 |
-
๊ตฌ์ฑ๋๋ค 1
|
| 3007 |
-
์ถ์ฒํ๊ฒ 1
|
| 3008 |
-
์ผ๋ถํ ๊ฑด 1
|
| 3009 |
-
๋์ด์น๋ชจํฐ์ค 1
|
| 3010 |
-
์ฃผ๊ฐ์กฐ์ 1
|
| 3011 |
-
๋น ์ก๋๋ฐ 1
|
| 3012 |
-
๋ณํํ๊ฒ ๋ค 1
|
| 3013 |
-
ํด์๋๋ค 1
|
| 3014 |
-
ํํ์ธ๋ฐ 1
|
| 3015 |
-
๊ฒฐํฉ๋ ์ง 1
|
| 3016 |
-
๊ฒฐ์ ๋๋ค 1
|
| 3017 |
-
๋ถ์ํ๋ฉด 1
|
| 3018 |
-
๊ฒฐํฉํ๋ฉด 1
|
| 3019 |
-
๊ฐ๋ฐ์ด๋ 1
|
| 3020 |
-
์ฌ์ฉ๋๋ค 1
|
| 3021 |
-
์์์ฐ๊ตฌ์ 1
|
| 3022 |
-
๋ถ์ํ๋ค 1
|
| 3023 |
-
๋ถ๊ณผํ๋ค 1
|
| 3024 |
-
์์ธกํ๋ค 1
|
| 3025 |
-
๋ฐ์ด๋น๋ 1
|
| 3026 |
-
์์ฑํด๋ 1
|
| 3027 |
-
๊ฐ๋ฐํ๊ณ 1
|
| 3028 |
-
๋๋
ธ์์ฌ 1
|
| 3029 |
-
์๋ช
๊ณผํ๋ถ 1
|
| 3030 |
-
๋ก์ ํํด๋ 1
|
| 3031 |
-
์ฃผ๋ํ๋ฉฐ 1
|
| 3032 |
-
์ฐ๊ตฌ๋จ๊ณ 1
|
| 3033 |
-
ํด๊ฒฐํ๊ฒ ๋ค 1
|
| 3034 |
-
ํํ๋ฌผ์ง 1
|
| 3035 |
-
๋จน์ด์น์ฐ 1
|
| 3036 |
-
๋ฐ์ํ์ง 1
|
| 3037 |
-
๋ฉํ๊ฐ์ค 1
|
| 3038 |
-
ํํ๋น๋ฃ 1
|
| 3039 |
-
๋์ํ๋ถ 1
|
| 3040 |
-
๊ณํํ๊ณ 1
|
| 3041 |
-
๊ธฐ์ฌํ๊ณ 1
|
| 3042 |
-
๋ฌด์ ์ผ๋ก 1
|
| 3043 |
-
๋ก๋ด์ฒญ์๊ธฐ์ธ 1
|
| 3044 |
-
๋นจ์๋ค์ธ 1
|
| 3045 |
-
๊ฑธ๋ฌ๋ด๊ณ 1
|
| 3046 |
-
๋ฉด์ ์ด๋ 1
|
| 3047 |
-
์ ์ฉํ๋ค 1
|
| 3048 |
-
๊ณ ์ฉ๋
ธ๋๋ถ 1
|
| 3049 |
-
์์
๋ฒค์ฒ๊ฒฝ์ฐ๋ํ 1
|
| 3050 |
-
์ต์ฐ์์ 1
|
| 3051 |
-
ํด์์์ฐ๋ถ 1
|
| 3052 |
-
์ธ์ ๋ฐ์๋ค 1
|
| 3053 |
-
๊ฒฝ์ฐฐ์ธ์ฌ๊ฐ๋ฐ์ 1
|
| 3054 |
-
์ํฌ๋ฅด๊ฒ 1
|
| 3055 |
-
๋ค๋ ค์๋ค 1
|
| 3056 |
-
์ฝ์ผ๋ฉด์ 1
|
| 3057 |
-
๊ณ ์ณ์คฌ๋ค 1
|
| 3058 |
-
์ํํ๊ณ 1
|
| 3059 |
-
ํ์ฑํํ๋ฉด์ 1
|
| 3060 |
-
๋ง๋ค์ด๋ฌ๋ผ๊ณ 1
|
| 3061 |
-
์์ฒญํ๊ณ 1
|
| 3062 |
-
์ด์ํ๋ค 1
|
| 3063 |
-
์ํ์ด๋ค 1
|
| 3064 |
-
ํฌ์
ํ๊ณ 1
|
| 3065 |
-
๋ฒ์ฃ์๋ค 1
|
| 3066 |
-
์ผ์ด๋๋ค 1
|
| 3067 |
-
๋ฒ ํธ๋จ์ผ๋ก 1
|
| 3068 |
-
๋์ฃผํ๋ค 1
|
| 3069 |
-
์กํ๋๋ค 1
|
| 3070 |
-
๋ํผํ๋ค๊ณ 1
|
| 3071 |
-
์๊ฐ์๋ค 1
|
| 3072 |
-
ํ๊ตญ์ธ๋ค 1
|
| 3073 |
-
๋ค๋ญ๊ฒฝ์ฐฐ์ 1
|
| 3074 |
-
๊ณต์์ด๋ผ๊ณ 1
|
| 3075 |
-
๋ฌด์์ํ 1
|
| 3076 |
-
ํ๊ตญ์ด๋ก 1
|
| 3077 |
-
์น๋ฐ๊ฐ์ด๋ผ 1
|
| 3078 |
-
์ ์ฒญํ๊ฒ 1
|
| 3079 |
-
์ข์ํ๊ณ 1
|
| 3080 |
-
๋ฐฉํ์๋
๋จ 1
|
| 3081 |
-
๋ด๋น์์ธ 1
|
| 3082 |
-
๋จ๊ณ์์ 1
|
| 3083 |
-
์ต๋๋ ฅ๋ค 1
|
| 3084 |
-
ํ์ง์์ 1
|
| 3085 |
-
๊ทน๋จ์ ์ผ๋ก 1
|
| 3086 |
-
๊ธฐ์ํ์ 1
|
| 3087 |
-
๋น๊ฒจ์ก๋ค 1
|
| 3088 |
-
๋ฆ์ด์ก๋ค 1
|
| 3089 |
-
๊ธฐ์ํ์ ์ผ๋ก 1
|
| 3090 |
-
์ฌ๋ฆ์ด๊ณ 1
|
| 3091 |
-
๋นผ์๊ธฐ๊ณ 1
|
| 3092 |
-
๊ธธ์ด์ง๋ฉด์ 1
|
| 3093 |
-
์ฌ๋ผ๊ฐ๊ณ 1
|
| 3094 |
-
๊ธธ์ด์ง๊ธฐ 1
|
| 3095 |
-
๋
ํด์ง๊ธฐ 1
|
| 3096 |
-
์์น์ผ๋ก 1
|
| 3097 |
-
์ค์ ๋ถํฐ 1
|
| 3098 |
-
์น์์๋ 1
|
| 3099 |
-
์์ธ์ผ๋ก 1
|
| 3100 |
-
๋ฑ์ฅ์ผ๋ก 1
|
| 3101 |
-
์๊น์ด๊ธฐ 1
|
| 3102 |
-
์ญํ ๊น์ง 1
|
| 3103 |
-
๋งก์๊ฐ๋ฉฐ 1
|
| 3104 |
-
์งํํ๋ค 1
|
| 3105 |
-
๋ถ์์์ฅ 1
|
| 3106 |
-
์ฐ์ถํ๊ณ 1
|
| 3107 |
-
ํ๊ตญ์์ ์ข
ํฉํ๊ต 1
|
| 3108 |
-
๊ฐ๋ง์์ธ 1
|
| 3109 |
-
์ฌ๋ฏธ์๊ฒ 1
|
| 3110 |
-
์๋
๊น์ง 1
|
| 3111 |
-
๋ด์ฐ์ธํ 1
|
| 3112 |
-
์งํ๋๋ 1
|
| 3113 |
-
ํ๋ก๊ทธ๋๋จธ 1
|
| 3114 |
-
๋ถ๊ตญ์ ๋ฐ 1
|
| 3115 |
-
๊ณต์์ด๋ค 1
|
| 3116 |
-
๋นํ์ค์ 1
|
| 3117 |
-
์์ํด์ 1
|
| 3118 |
-
์ง๋์น๋ค 1
|
| 3119 |
-
์ํ์ ์ธ 1
|
| 3120 |
-
์ธ๋ฉดํ๋๋ 1
|
| 3121 |
-
์๋ณธ์ด๋ผ์ 1
|
| 3122 |
-
์์์ก์๊น 1
|
| 3123 |
-
์ฑ์ฌ๋๋ค 1
|
| 3124 |
-
์ฅ์ธ๊ด๊ณ ๋ฌผ๋ฒ 1
|
| 3125 |
-
๊ตฌ์ฒญ์์ 1
|
| 3126 |
-
๋ฐ์์ค๋ฉด 1
|
| 3127 |
-
์ํ์ ๋ผ 1
|
| 3128 |
-
๊ฐ๋ฅํ๋ 1
|
| 3129 |
-
๊ณ์ฝ์ด๊ธฐ 1
|
| 3130 |
-
๋ด๋
๋๋ก 1
|
| 3131 |
-
์ ๊ตญ์ฒด์ 1
|
| 3132 |
-
์ฎ๊ธฐ๊ธฐ๋ก 1
|
| 3133 |
-
์ฌ๋ฏธ์์ผ๋ฉด 1
|
| 3134 |
-
๊ด๊ฐ์๊ฒ 1
|
| 3135 |
-
์ธ๋ฉด๋นํ๋ค 1
|
| 3136 |
-
๋ ๋๊ฐ๊ฒ 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
assets/resources/{vocabulary_v2.csv โ vocabulary.csv}
RENAMED
|
@@ -144,74 +144,4 @@
|
|
| 144 |
12ยท12 ๊ณ์,12ยท3 ๊ณ์,
|
| 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 |
-
๋ทํ์ด,๋คํ์ด,
|
| 173 |
-
์ค๋ซ๋ง,์ค๋๋ง,
|
| 174 |
-
์ค๋๋์,์ค๋ซ๋์,
|
| 175 |
-
์ฌ์ง,์ ์ง,
|
| 176 |
-
์ ๋ง,์ฌ๋ง,
|
| 177 |
-
์ ์ผ,์ฌ์ผ,
|
| 178 |
-
์ ๊ฑธ,์ฌ๊ฑธ,
|
| 179 |
-
๊ธ์,๊ธ์ธ,
|
| 180 |
-
์ด์์,์ด์ด์,
|
| 181 |
-
ํฌ์ํ,ํฌํํ,
|
| 182 |
-
๊ณฐ๊ณฐํ,๊ณฐ๊ณฐ์ด,
|
| 183 |
-
์ผ์ผํ,์ผ์ผ์ด,
|
| 184 |
-
ํํํ,ํํ์ด,
|
| 185 |
-
๊นจ๋ํ,๊นจ๋์ด,
|
| 186 |
-
๋ช์ผ,๋ฉฐ์น ,
|
| 187 |
-
์ญํ,์ญํ ,
|
| 188 |
-
ํ ๋ ค๊ณ ,ํ๋ ค๊ณ ,
|
| 189 |
-
๊ท๋ธ,๊ท๋,
|
| 190 |
-
๊ท๋ฐ,๊ท๋,
|
| 191 |
-
๋ฐ์์ฑ,๋ฐ์๊ตญ,
|
| 192 |
-
๋๊ผฝ,๋๊ณฑ,
|
| 193 |
-
๊ณฑ๋ฐฐ๊ธฐ,๊ณฑ๋นผ๊ธฐ,
|
| 194 |
-
๋ฎํ,๋ฎ์ด,
|
| 195 |
-
๋ฎํ,๋ฎ์ฌ,
|
| 196 |
-
ํต์ฑ๋ก,ํต์งธ๋ก,
|
| 197 |
-
ํ๋งํธ๋ฉด,ํ๋งํฐ๋ฉด,
|
| 198 |
-
์ด๋ฌผ์ฉก,์ด๋ฌผ์ฉ,
|
| 199 |
-
์
ํผ,์ํผ,
|
| 200 |
-
์ผ์ฌ๋ถ๋,์ผ์ฌ๋ถ๋,
|
| 201 |
-
์ ๋์ ๋ช
,์ ์ฒด์ ๋ช
,
|
| 202 |
-
ํํ๋จ์ ,ํํ๋จ์ ,
|
| 203 |
-
๋๊ฑฐ๋๋ฝ,๋๊ณ ๋๋ฝ,
|
| 204 |
-
์ฑ๋๋ฌ์ฌ,์ฑ๋๋ชจ์ฌ,
|
| 205 |
-
๋ค์น๋ฅ๊ฑฐ๋ฆฌ,๋ค์น๋ค๊บผ๋ฆฌ,
|
| 206 |
-
์์ฑ๋ง์ถค,์์ฑ๋ง์ถค,
|
| 207 |
-
๋๋ฌผ๋ฆผ,๋๋ฌผ๋ฆผ,
|
| 208 |
-
์ผ๊ฐํ,์ผ๊ฐ,
|
| 209 |
-
์ฒผ๋ฆฐ์ง,์ฑ๋ฆฐ์ง,
|
| 210 |
-
๋ฆฌ๋์ฝ,๋ฆฌ๋์ญ,
|
| 211 |
-
๋ฉค๋ฒ์ฝ,๋ฉค๋ฒ์ญ,
|
| 212 |
-
ํํธ๋์ฝ,ํํธ๋์ญ,
|
| 213 |
-
์ปจํ
์ธ ,์ฝํ
์ธ ,
|
| 214 |
-
๋ฉ์ธ์ง,๋ฉ์์ง,
|
| 215 |
-
์ดํ๋ฆฌ์ผ์ด์
,์ ํ๋ฆฌ์ผ์ด์
,
|
| 216 |
-
์ด์ฝ๋ ,์ด์ฝ๋ฆฟ,
|
| 217 |
-
ํํ๋ ,ํธํ๋ฆฟ,
|
|
|
|
| 144 |
12ยท12 ๊ณ์,12ยท3 ๊ณ์,
|
| 145 |
์ง์ฒ์จ,์ง์ฒ๋ฅ ,
|
| 146 |
๊ฐฑ์ ๋ฅ ,๊ฐฑ์ ์จ,
|
| 147 |
+
์ธ๋๋ฐํ ๋ฅด,์ธ๋๋ฐํ๋ฅด,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
runner.py
CHANGED
|
@@ -1,20 +1,18 @@
|
|
| 1 |
-
"""
|
| 2 |
-
|
| 3 |
-
๊ต์ด ๋ก์ง์ ์ฌ๊ตฌํํ์ง ์๊ณ solar-eval ์์ง์ ๊ทธ๋๋ก import ํ๋ค
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
1. `./assets/` โ HF Space ๋ ์ด์์ (build_space.py ๊ฐ ์กฐ๋ฆฝ)
|
| 17 |
-
2. `../03-evaluation/` โ ๋ ํฌ ๋ ์ด์์ (๋ก์ปฌ ๊ฐ๋ฐ)
|
| 18 |
์์๋ก ์ฐพ๋๋ค.
|
| 19 |
"""
|
| 20 |
|
|
@@ -32,70 +30,63 @@ import yaml
|
|
| 32 |
|
| 33 |
@dataclass(frozen=True)
|
| 34 |
class Config:
|
| 35 |
-
"""๋ฐ๋ชจ๊ฐ
|
| 36 |
|
| 37 |
label: str
|
| 38 |
pipeline: str
|
| 39 |
prompt: str
|
| 40 |
model: str
|
| 41 |
note: str
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
note=(
|
| 54 |
-
"FT ๊ต์ด โ ํ์ ํ step1(5KB) โ ๋ฌธ๋งฅ ๊ต์ ์ดํ/๋ฌธ๋ฒ 2์คํ
โ ๊ฒฐ์ ๋ก ๊ท์น 6์ธต. "
|
| 55 |
-
"์
๋ ฅ 3.3k ํ ํฐ."
|
| 56 |
-
),
|
| 57 |
-
base="pipeline_dev_v40",
|
| 58 |
),
|
|
|
|
| 59 |
)
|
| 60 |
|
| 61 |
-
#:
|
| 62 |
-
|
| 63 |
-
DEFAULT_LABEL = CONFIGS[0].label
|
| 64 |
|
| 65 |
-
#
|
| 66 |
-
StepCallback = Callable[[str, int, int, str
|
| 67 |
|
| 68 |
|
| 69 |
-
def find_config_dir(
|
| 70 |
-
"""
|
| 71 |
here = Path(__file__).resolve().parent
|
| 72 |
for cand in (here / "assets", here.parent / "03-evaluation"):
|
| 73 |
-
if (cand / "pipelines" / f"{pipeline}.yaml").is_file():
|
| 74 |
return cand
|
| 75 |
raise FileNotFoundError(
|
| 76 |
-
f"config ์์ฐ์ ์ฐพ์ ์ ์์ต๋๋ค
|
| 77 |
-
f"pipelines/{pipeline}.yaml ์ด ์์ด์ผ ํฉ๋๋ค (build_space.py ์ฐธ์กฐ)."
|
| 78 |
)
|
| 79 |
|
| 80 |
|
| 81 |
-
def build_pipeline(
|
| 82 |
-
"""
|
| 83 |
-
import solar_eval.pipelines.steps # noqa: F401
|
| 84 |
from solar_eval.core.dataset_loader import DatasetLoader
|
| 85 |
from solar_eval.core.pipeline_compose import compose_pipeline
|
| 86 |
from solar_eval.models.prompt_version import load_step_prompts
|
| 87 |
from solar_eval.pipelines.registry import create_pipeline
|
| 88 |
|
| 89 |
-
|
| 90 |
-
config_dir = find_config_dir(cfg.pipeline)
|
| 91 |
pipelines_dir = config_dir / "pipelines"
|
| 92 |
|
| 93 |
def load_base(name: str) -> dict[str, Any]:
|
| 94 |
return yaml.safe_load((pipelines_dir / f"{name}.yaml").read_text(encoding="utf-8"))
|
| 95 |
|
| 96 |
-
composed = compose_pipeline(load_base(
|
| 97 |
-
prompts = load_step_prompts(config_dir / "prompts" /
|
| 98 |
-
|
| 99 |
return create_pipeline(
|
| 100 |
"multi_step",
|
| 101 |
input_fields=["original"],
|
|
@@ -111,47 +102,44 @@ def step_names(pipeline: Any) -> list[str]:
|
|
| 111 |
return [step.name for step in pipeline.steps]
|
| 112 |
|
| 113 |
|
| 114 |
-
async def _run_async(
|
| 115 |
-
pipeline: Any, text: str, cfg: Config, on_step: StepCallback | None
|
| 116 |
-
) -> dict[str, Any]:
|
| 117 |
from solar_eval.models.sample import EvalSample
|
|
|
|
| 118 |
from solar_eval.providers.upstage import UpstageProvider
|
| 119 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
sample = EvalSample(input={"original": text})
|
|
|
|
| 121 |
start = time.monotonic()
|
| 122 |
-
# ํ๊ฐ run ๊ณผ ๊ฐ์ ์กฐ๊ฑด: temp 0.0, reasoning off
|
| 123 |
result = await pipeline.run(
|
| 124 |
sample,
|
| 125 |
prompts="",
|
| 126 |
-
provider=
|
| 127 |
-
model=
|
| 128 |
temperature=0.0,
|
| 129 |
max_tokens=8000,
|
| 130 |
reasoning_effort=None,
|
| 131 |
-
on_step=
|
| 132 |
)
|
| 133 |
-
elapsed = time.monotonic() - start
|
| 134 |
return {
|
| 135 |
"output": result.output,
|
| 136 |
-
"step_outputs": result.artifacts.get("step_outputs", {}),
|
| 137 |
"usage": result.artifacts.get("usage", {}),
|
| 138 |
-
"elapsed_s":
|
| 139 |
-
"pipeline_key":
|
| 140 |
-
"prompt_key":
|
| 141 |
-
"model":
|
| 142 |
-
"config_label": cfg.label,
|
| 143 |
}
|
| 144 |
|
| 145 |
|
| 146 |
-
def run_proofread(
|
| 147 |
-
|
| 148 |
-
text: str,
|
| 149 |
-
label: str = DEFAULT_LABEL,
|
| 150 |
-
on_step: StepCallback | None = None,
|
| 151 |
-
) -> dict[str, Any]:
|
| 152 |
-
"""ํ
์คํธ 1๊ฑด ๊ต์ด. {output, step_outputs, usage, elapsed_s, pipeline_key, prompt_key, model, config_label}.
|
| 153 |
|
| 154 |
-
`
|
| 155 |
-
๊ตฌ์ฑ๋ณ๋ก ์บ์ํ๋ค. `on_step` ์ ์คํ
์์/์ข
๋ฃ์ chunk ์งํ๋ง๋ค ๋ถ๋ฆฐ๋ค(UI ์งํ ํ์).
|
| 156 |
"""
|
| 157 |
-
return asyncio.run(_run_async(pipeline, text,
|
|
|
|
| 1 |
+
"""๊ณ ๊ฐ ๋ฐ๋ชจ(HF Space) ๊ต์ด ๊ตฌ์ฑ์ ํ
์คํธ 1๊ฑด์ ์คํํ๋ ํ๋ก๊ทธ๋๋งคํฑ ๋ฌ๋.
|
| 2 |
+
|
| 3 |
+
๊ต์ด ๋ก์ง์ ์ฌ๊ตฌํํ์ง ์๊ณ solar-eval ์์ง์ ๊ทธ๋๋ก import ํ๋ค - ๋ฐ๋ชจ๊ฐ ๋ณด์ฌ์ฃผ๋ ๋์์ด ํ๊ฐ run ๊ณผ
|
| 4 |
+
๋ฐ์ดํธ ๋จ์๋ก ๊ฐ์์ผ ํ๊ธฐ ๋๋ฌธ์ด๋ค(์ฌ๋ณธ ๋ก์ง์ ๋ฐ๋์ ๊ฐ๋ผ์ง๋ค). ๋ก์ปฌ์์๋ uv ์ํฌ์คํ์ด์ค์ solar_eval ์,
|
| 5 |
+
HF Space ์์๋ `build_space.py` ๊ฐ ๋ฒค๋๋งํ ์ฌ๋ณธ์ ์ด๋ค(sys.path ์ ์คํฌ๋ฆฝํธ ์์ด ๋จผ์ ๋ผ Space ์์๋ ๋ฒค๋
|
| 6 |
+
์ฌ๋ณธ์ด ์ด๊ธด๋ค).
|
| 7 |
+
|
| 8 |
+
๊ตฌ์ฑ์ (pipeline, prompt, model) ํ ๋ฌถ์์ด๋ค. 2026-10-29 ๋ถํฐ FT ๊ต์ด + Opus ๊ธฐ์ฌ ๊ฒํ
|
| 9 |
+
(`pipeline_exp_261021_ft_hybrid`, ๊ทผ๊ฑฐ `03-evaluation/insights/261021_ft_hybrid_paragraph_article.md`)
|
| 10 |
+
ํ๋๋ง ๋
ธ์ถํ๋ค. ๊ธฐ์ฌ ๊ฒํ ์คํ
์ `timely:` ๋ชจ๋ธ์ด๋ผ Timely ๋ผ์ฐํฐ๋ฅผ ๊ฑฐ์น๋ค - `UPSTAGE_API_KEY` ์
|
| 11 |
+
`TIMELY_API_KEY` ๊ฐ ๋ ๋ค ์์ด์ผ ํ๋ค.
|
| 12 |
+
|
| 13 |
+
config ์์ฐ(ํ์ดํ๋ผ์ธ yamlยทํ๋กฌํํธยท์นํ ์ฌ์ )์
|
| 14 |
+
1. `./assets/` - HF Space ๋ ์ด์์ (build_space.py ๊ฐ ์กฐ๋ฆฝ)
|
| 15 |
+
2. `../03-evaluation/` - ๋ ํฌ ๋ ์ด์์ (๋ก์ปฌ ๊ฐ๋ฐ)
|
|
|
|
|
|
|
| 16 |
์์๋ก ์ฐพ๋๋ค.
|
| 17 |
"""
|
| 18 |
|
|
|
|
| 30 |
|
| 31 |
@dataclass(frozen=True)
|
| 32 |
class Config:
|
| 33 |
+
"""๋ฐ๋ชจ๊ฐ ๋
ธ์ถํ๋ ํ ๊ตฌ์ฑ. `assets` ๋ Space ๋ฒ๋ค์ ๋ค์ด๊ฐ config ํ์ผ(03-evaluation ๊ธฐ์ค ์๋ ๊ฒฝ๋ก)."""
|
| 34 |
|
| 35 |
label: str
|
| 36 |
pipeline: str
|
| 37 |
prompt: str
|
| 38 |
model: str
|
| 39 |
note: str
|
| 40 |
+
assets: tuple[str, ...]
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
CONFIG = Config(
|
| 44 |
+
label="FT ๊ต์ด + Opus ๊ธฐ์ฌ ๊ฒํ ",
|
| 45 |
+
pipeline="pipeline_exp_261021_ft_hybrid",
|
| 46 |
+
prompt="prompt_exp_261021_ft_hybrid",
|
| 47 |
+
model="solar-pro4", # ๋ชจ๋ธ์ ์ง์ ํ์ง ์์ ์คํ
์ฉ ๊ธฐ๋ณธ๊ฐ - ์ด ๊ตฌ์ฑ์ LLM ์คํ
์ FTยทOpus ๋ฅผ ์ง์ ์ง์ ํ๋ค
|
| 48 |
+
note=(
|
| 49 |
+
"FT ๊ต์ด ๋ชจ๋ธ์ด ๋ฌธ๋จ ๋ฌถ์๋ง๋ค 1์ฐจ ๊ต์ด โ Claude Opus ๊ฐ ๊ธฐ์ฌ ์ ์ฒด๋ฅผ ํ ๋ฒ ๋ณด๊ณ FT ์ค๊ต์ ์ ๋๋๋ฆฌ๊ณ "
|
| 50 |
+
"์ฌ์คยท์ซ์ยท๋ฌธ๋งฅ ์ค๋ฅ๋ฅผ ๊ณ ์นจ โ ๋ฐ์ดํยท์จ/๋ฅ ยทํ์ฒ๋ฆฌ ๊ท์น."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
),
|
| 52 |
+
assets=("resources/vocabulary.csv",),
|
| 53 |
)
|
| 54 |
|
| 55 |
+
#: ํ์ํ ํค. ๊ธฐ์ฌ ๊ฒํ (Opus)๊ฐ Timely ๋ฅผ ๊ฑฐ์น๋ค.
|
| 56 |
+
REQUIRED_KEYS = ("UPSTAGE_API_KEY", "TIMELY_API_KEY")
|
|
|
|
| 57 |
|
| 58 |
+
# (event, index, total, step_name) - ์คํ
์ด ๋๋ ๋๋ง๋ค "done" ์ผ๋ก ๋ถ๋ฆฐ๋ค
|
| 59 |
+
StepCallback = Callable[[str, int, int, str], None]
|
| 60 |
|
| 61 |
|
| 62 |
+
def find_config_dir() -> Path:
|
| 63 |
+
"""ํ์ดํ๋ผ์ธ yaml ์ ์ค์ ๋ก ๊ฐ์ง ์์ฐ ๋ฃจํธ. Space ๋ฒ๋ค๏ฟฝ๏ฟฝ๏ฟฝ ๋ ํฌ๋ณด๋ค ๋จผ์ ๋ค."""
|
| 64 |
here = Path(__file__).resolve().parent
|
| 65 |
for cand in (here / "assets", here.parent / "03-evaluation"):
|
| 66 |
+
if (cand / "pipelines" / f"{CONFIG.pipeline}.yaml").is_file():
|
| 67 |
return cand
|
| 68 |
raise FileNotFoundError(
|
| 69 |
+
f"config ์์ฐ์ ์ฐพ์ ์ ์์ต๋๋ค - {here}/assets ๋๋ ../03-evaluation ์ "
|
| 70 |
+
f"pipelines/{CONFIG.pipeline}.yaml ์ด ์์ด์ผ ํฉ๋๋ค (build_space.py ์ฐธ์กฐ)."
|
| 71 |
)
|
| 72 |
|
| 73 |
|
| 74 |
+
def build_pipeline() -> Any:
|
| 75 |
+
"""ํ์ดํ๋ผ์ธ ์ธ์คํด์ค. ์ฑ์์ 1ํ ๋ง๋ค์ด ์ฌ์ฌ์ฉํ๋ค."""
|
| 76 |
+
import solar_eval.pipelines.steps # noqa: F401 - STEP_REGISTRY ๋ฑ๋ก
|
| 77 |
from solar_eval.core.dataset_loader import DatasetLoader
|
| 78 |
from solar_eval.core.pipeline_compose import compose_pipeline
|
| 79 |
from solar_eval.models.prompt_version import load_step_prompts
|
| 80 |
from solar_eval.pipelines.registry import create_pipeline
|
| 81 |
|
| 82 |
+
config_dir = find_config_dir()
|
|
|
|
| 83 |
pipelines_dir = config_dir / "pipelines"
|
| 84 |
|
| 85 |
def load_base(name: str) -> dict[str, Any]:
|
| 86 |
return yaml.safe_load((pipelines_dir / f"{name}.yaml").read_text(encoding="utf-8"))
|
| 87 |
|
| 88 |
+
composed = compose_pipeline(load_base(CONFIG.pipeline), load_base=load_base)
|
| 89 |
+
prompts = load_step_prompts(config_dir / "prompts" / CONFIG.prompt)
|
|
|
|
| 90 |
return create_pipeline(
|
| 91 |
"multi_step",
|
| 92 |
input_fields=["original"],
|
|
|
|
| 102 |
return [step.name for step in pipeline.steps]
|
| 103 |
|
| 104 |
|
| 105 |
+
async def _run_async(pipeline: Any, text: str, on_step: StepCallback | None) -> dict[str, Any]:
|
|
|
|
|
|
|
| 106 |
from solar_eval.models.sample import EvalSample
|
| 107 |
+
from solar_eval.providers import INFERENCE_ROUTES, RoutedProvider
|
| 108 |
from solar_eval.providers.upstage import UpstageProvider
|
| 109 |
|
| 110 |
+
names = step_names(pipeline)
|
| 111 |
+
|
| 112 |
+
def hook(name: str, _sample: Any, _ctx: Any) -> None:
|
| 113 |
+
if on_step is not None:
|
| 114 |
+
on_step("done", names.index(name) + 1, len(names), name)
|
| 115 |
+
|
| 116 |
sample = EvalSample(input={"original": text})
|
| 117 |
+
provider = RoutedProvider(UpstageProvider(), {k: c() for k, c in INFERENCE_ROUTES.items()})
|
| 118 |
start = time.monotonic()
|
| 119 |
+
# ํ๊ฐ run ๊ณผ ๊ฐ์ ์กฐ๊ฑด: temp 0.0, reasoning off
|
| 120 |
result = await pipeline.run(
|
| 121 |
sample,
|
| 122 |
prompts="",
|
| 123 |
+
provider=provider,
|
| 124 |
+
model=CONFIG.model,
|
| 125 |
temperature=0.0,
|
| 126 |
max_tokens=8000,
|
| 127 |
reasoning_effort=None,
|
| 128 |
+
on_step=hook,
|
| 129 |
)
|
|
|
|
| 130 |
return {
|
| 131 |
"output": result.output,
|
|
|
|
| 132 |
"usage": result.artifacts.get("usage", {}),
|
| 133 |
+
"elapsed_s": time.monotonic() - start,
|
| 134 |
+
"pipeline_key": CONFIG.pipeline,
|
| 135 |
+
"prompt_key": CONFIG.prompt,
|
| 136 |
+
"model": CONFIG.model,
|
|
|
|
| 137 |
}
|
| 138 |
|
| 139 |
|
| 140 |
+
def run_proofread(pipeline: Any, text: str, on_step: StepCallback | None = None) -> dict[str, Any]:
|
| 141 |
+
"""ํ
์คํธ 1๊ฑด ๊ต์ด. {output, usage, elapsed_s, pipeline_key, prompt_key, model}.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 142 |
|
| 143 |
+
`on_step` ์ ์คํ
์ด ๋๋ ๋๋ง๋ค ("done", ๋ช ๋ฒ์งธ, ์ ์ฒด, ์คํ
์ด๋ฆ) ์ผ๋ก ๋ถ๋ฆฐ๋ค(UI ์งํ ํ์).
|
|
|
|
| 144 |
"""
|
| 145 |
+
return asyncio.run(_run_async(pipeline, text, on_step))
|
solar_eval/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (394 Bytes). View file
|
|
|
solar_eval/__pycache__/_version.cpython-312.pyc
ADDED
|
Binary file (233 Bytes). View file
|
|
|
solar_eval/__pycache__/config.cpython-312.pyc
ADDED
|
Binary file (1.27 kB). View file
|
|
|
solar_eval/cli/commands/compare.py
CHANGED
|
@@ -1,42 +1,105 @@
|
|
| 1 |
-
"""Compare runs side by side.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
import json
|
|
|
|
|
|
|
| 4 |
|
| 5 |
import click
|
| 6 |
from tabulate import tabulate
|
| 7 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
"--runs",
|
| 14 |
-
"run_ids",
|
| 15 |
-
default=None,
|
| 16 |
-
help="Comma-separated run IDs to compare",
|
| 17 |
)
|
| 18 |
-
@click.option("--last", "-n", default=None, type=int, help="Compare last N runs for a task")
|
| 19 |
-
@click.pass_context
|
| 20 |
-
def compare_command(ctx, project, task, run_ids, last):
|
| 21 |
-
"""Compare evaluation scores across runs.
|
| 22 |
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
|
| 31 |
-
artifacts_dir = cfg.artifacts_dir(project)
|
| 32 |
-
if not artifacts_dir.exists():
|
| 33 |
-
click.secho("No artifacts found.", fg="yellow")
|
| 34 |
-
return
|
| 35 |
|
| 36 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
if run_ids:
|
| 38 |
-
# Explicit run IDs
|
| 39 |
-
run_dirs = []
|
| 40 |
for rid in run_ids.split(","):
|
| 41 |
rid = rid.strip()
|
| 42 |
d = artifacts_dir / rid
|
|
@@ -45,33 +108,25 @@ def compare_command(ctx, project, task, run_ids, last):
|
|
| 45 |
else:
|
| 46 |
click.secho(f" Run not found: {rid}, skipping", fg="yellow")
|
| 47 |
else:
|
| 48 |
-
# Scan all runs, optionally filtered by task
|
| 49 |
-
run_dirs = []
|
| 50 |
for d in sorted(artifacts_dir.iterdir()):
|
| 51 |
if not d.is_dir() or not (d / "run.json").exists():
|
| 52 |
continue
|
| 53 |
-
if task:
|
| 54 |
-
|
| 55 |
-
if run_meta.get("task") != task:
|
| 56 |
-
continue
|
| 57 |
run_dirs.append(d)
|
| 58 |
-
|
| 59 |
if last and last < len(run_dirs):
|
| 60 |
run_dirs = run_dirs[-last:]
|
|
|
|
| 61 |
|
| 62 |
-
if not run_dirs:
|
| 63 |
-
click.secho("No runs found to compare.", fg="yellow")
|
| 64 |
-
return
|
| 65 |
|
| 66 |
-
|
|
|
|
| 67 |
runs_info = []
|
| 68 |
-
all_categories = set()
|
| 69 |
for d in run_dirs:
|
| 70 |
-
run_meta = json.loads((d / "run.json").read_text())
|
| 71 |
eval_file = d / "evaluation.json"
|
| 72 |
if not eval_file.exists():
|
| 73 |
continue
|
| 74 |
-
|
| 75 |
runs_info.append(
|
| 76 |
{
|
| 77 |
"run_id": d.name,
|
|
@@ -79,18 +134,19 @@ def compare_command(ctx, project, task, run_ids, last):
|
|
| 79 |
"model": run_meta.get("model", "-"),
|
| 80 |
"prompt": run_meta.get("prompt", run_meta.get("prompt_version", "-")),
|
| 81 |
"pipeline": run_meta.get("pipeline", "-"),
|
| 82 |
-
"eval_data":
|
|
|
|
| 83 |
}
|
| 84 |
)
|
| 85 |
-
|
| 86 |
|
| 87 |
-
if not runs_info:
|
| 88 |
-
click.secho("No evaluation data available for comparison.", fg="yellow")
|
| 89 |
-
return
|
| 90 |
|
| 91 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 92 |
headers = ["Category"] + [f"{r['prompt']}/{r['model']}" for r in runs_info]
|
| 93 |
-
|
| 94 |
rows = []
|
| 95 |
for cat in sorted(all_categories):
|
| 96 |
row = [cat]
|
|
@@ -98,26 +154,31 @@ def compare_command(ctx, project, task, run_ids, last):
|
|
| 98 |
score = r["eval_data"].get("scores", {}).get(cat)
|
| 99 |
row.append(f"{score:.4f}" if score is not None else "-")
|
| 100 |
rows.append(row)
|
| 101 |
-
|
| 102 |
-
# Separator and overall row
|
| 103 |
rows.append(["โ" * 20] + ["โ" * 8] * len(runs_info))
|
| 104 |
overall_row = ["Overall"]
|
| 105 |
for r in runs_info:
|
| 106 |
overall = r["eval_data"].get("overall_score")
|
| 107 |
overall_row.append(f"{overall:.4f}" if overall is not None else "-")
|
| 108 |
rows.append(overall_row)
|
|
|
|
| 109 |
|
| 110 |
-
task_label = task or "all tasks"
|
| 111 |
-
click.secho(f"\nComparison: {project}/{task_label}", fg="blue", bold=True)
|
| 112 |
|
| 113 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
for r in runs_info:
|
| 115 |
-
|
| 116 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 117 |
|
| 118 |
-
click.echo(tabulate(rows, headers=headers, tablefmt="simple"))
|
| 119 |
|
| 120 |
-
|
| 121 |
best_run = max(runs_info, key=lambda r: r["eval_data"].get("overall_score", 0))
|
| 122 |
best_score = best_run["eval_data"].get("overall_score", 0)
|
| 123 |
click.secho(
|
|
@@ -125,3 +186,45 @@ def compare_command(ctx, project, task, run_ids, last):
|
|
| 125 |
fg="green",
|
| 126 |
bold=True,
|
| 127 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Compare runs side by side.
|
| 2 |
+
|
| 3 |
+
๋น๊ต ๋์ run ๋ค์ ํ ์ ํ(`run.json` `dataset.path`ยท`selection`ยท`rows_selected`)์ด ์๋ก ๋ค๋ฅด๋ฉด ๊ฐ run ์
|
| 4 |
+
์ ํ์ ํค๋์ ์ฃ๊ณ "๋ค๋ฅธ ํ ์งํฉ์ ์ ์๋ค" ๊ฒฝ๊ณ ๋ฅผ ๋ธ๋ค(๊ณํ 2026-09-24-phase1a D19) โ `--where` ์ฌ๋ผ์ด์ค
|
| 5 |
+
run ๊ณผ ์ ์ฒด์
run ์ ์ ์๊ฐ ๊ฐ์ ํ์์ ์กฐ์ฉํ ๋๋ํ ์์ง ์๊ฒ. quick-run ์ด ์งํ์ ๋ถ๋ฅด๋ compare ๋
|
| 6 |
+
๊ฐ์ ๊ฒฝ๊ณ ๋ฅผ ๋ฐ๋๋ค. ์ ํ์ด ๋ชจ๋ ๊ฐ์ผ๋ฉด ์ถ๋ ฅ์ ์์ ๊ทธ๋๋ก๋ค.
|
| 7 |
+
|
| 8 |
+
๋ฐ์ดํฐ์
ํ์ผ ๋ฐ์ดํธ(`dataset.sha256`)๋ง ๋ค๋ฅธ ๊ฒฝ์ฐ๋ **์ ํ ์ฐจ์ด๊ฐ ์๋๋ค** โ ํ ์ถ์ฒ ํค๋ฅผ ๋ํ ์ฌ๋น๋์ฒ๋ผ
|
| 9 |
+
ํ ์งํฉ์ ๊ฐ๊ณ ๋ฐ์ดํธ๋ง ๋ฐ๋ ์ ์๋ค. ๊ทธ๋๋ ์ ๋ต์ด ๋ฐ๋์์ ์ ์์ผ๋ฏ๋ก ๋ฐ๋ก ๊ฒฝ๊ณ ํ๊ณ , ๋ผ๋ฒจ์ sha ์์๋ฆฌ๋ฅผ
|
| 10 |
+
์ค์ด ์ฌ๋์ด ์ด์ ๋ฅผ ๋ณผ ์ ์๊ฒ ํ๋ค.
|
| 11 |
+
"""
|
| 12 |
|
| 13 |
import json
|
| 14 |
+
from pathlib import Path
|
| 15 |
+
from typing import Any
|
| 16 |
|
| 17 |
import click
|
| 18 |
from tabulate import tabulate
|
| 19 |
|
| 20 |
+
#: ๋น๊ต ๋์ ํ ์งํฉ์ด ๋ค๋ฅผ ๋์ ๊ฒฝ๊ณ .
|
| 21 |
+
DIFFERENT_ROWS_WARNING = (
|
| 22 |
+
" โ ๋น๊ต ๋์ run ๋ค์ ํ ์ ํ์ด ๋ค๋ฆ
๋๋ค โ ๋ค๋ฅธ ํ ์งํฉ์ ์ ์๋ผ ์ง์ ๋น๊ตํ ์ ์์ต๋๋ค."
|
| 23 |
+
)
|
| 24 |
|
| 25 |
+
#: ํ ์ ํ์ ๊ฐ์๋ฐ ๋ฐ์ดํฐ์
ํ์ผ ๋ฐ์ดํธ๊ฐ ๋ค๋ฅผ ๋์ ๊ฒฝ๊ณ .
|
| 26 |
+
DIFFERENT_BYTES_WARNING = (
|
| 27 |
+
" โ ํ ์ ํ์ ๊ฐ์ง๋ง ๋ฐ์ดํฐ์
ํ์ผ(sha256)์ด ๋ค๋ฆ
๋๋ค โ ํ ๋ด์ฉ(์ ๋ต ๋ฑ)์ด ๋ฐ๋์์ ์ ์์ผ๋ "
|
| 28 |
+
"์ ์๋ฅผ ๋น๊ตํ๊ธฐ ์ ์ ๋ ํ์ผ์ ์ฐจ์ด๋ฅผ ํ์ธํ์ธ์."
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
|
| 31 |
+
#: ๋ผ๋ฒจ์ ์ฃ๋ sha256 ์์๋ฆฌ ์ โ legacy `prompt_sha` ํ์์ ๋ง์ถ๋ค.
|
| 32 |
+
SHA_DISPLAY_CHARS = 12
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def _dataset(run_meta: dict[str, Any]) -> dict[str, Any] | None:
|
| 36 |
+
dataset = run_meta.get("dataset")
|
| 37 |
+
return dataset if isinstance(dataset, dict) else None
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def _selection_signature(run_meta: dict[str, Any]) -> str | None:
|
| 41 |
+
"""ํ ์งํฉ์ ๋น๊ต ํค โ `dataset` ๋ธ๋ก์ด ์๋ ์ run ์ None(๋ชจ๋ฆ). ํ์ผ ๋ฐ์ดํธ(sha)๋ ๋ฃ์ง ์๋๋ค."""
|
| 42 |
+
dataset = _dataset(run_meta)
|
| 43 |
+
if dataset is None:
|
| 44 |
+
return None
|
| 45 |
+
key = {
|
| 46 |
+
"path": dataset.get("path"),
|
| 47 |
+
"selection": dataset.get("selection"),
|
| 48 |
+
"rows_selected": dataset.get("rows_selected"),
|
| 49 |
+
}
|
| 50 |
+
return json.dumps(key, sort_keys=True, ensure_ascii=False)
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def _is_filtered(run_meta: dict[str, Any]) -> bool:
|
| 54 |
+
selection = (_dataset(run_meta) or {}).get("selection") or {}
|
| 55 |
+
return selection.get("where") is not None or selection.get("limit") is not None
|
| 56 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
|
| 58 |
+
def _selection_label(run_meta: dict[str, Any], show_sha: bool = False) -> str:
|
| 59 |
+
"""์ฌ๋์ฉ ์ ํ ํ๊ธฐ: `where tags=a ยท 12/159 rows` ยท `all 159/159 rows` ยท `selection unknown`.
|
| 60 |
+
|
| 61 |
+
`show_sha` ๋ฉด ` ยท sha <12>` ๋ฅผ ๋ง๋ถ์ธ๋ค(ํ์ผ ๋ฐ์ดํธ๊ฐ ๋ค๋ฅธ run ๋ค์ ๋น๊ตํ ๋).
|
| 62 |
+
"""
|
| 63 |
+
dataset = _dataset(run_meta)
|
| 64 |
+
if dataset is None:
|
| 65 |
+
return "selection unknown (no dataset block)"
|
| 66 |
+
selection = dataset.get("selection") or {}
|
| 67 |
+
parts = []
|
| 68 |
+
where = selection.get("where")
|
| 69 |
+
if where:
|
| 70 |
+
parts.append("where " + " ".join(f"{k}={','.join(v)}" for k, v in sorted(where.items())))
|
| 71 |
+
if selection.get("limit") is not None:
|
| 72 |
+
parts.append(f"limit {selection['limit']}")
|
| 73 |
+
label = " ".join(parts) or "all"
|
| 74 |
+
label = f"{label} ยท {dataset.get('rows_selected')}/{dataset.get('rows_total')} rows"
|
| 75 |
+
sha = dataset.get("sha256")
|
| 76 |
+
if show_sha and isinstance(sha, str):
|
| 77 |
+
label = f"{label} ยท sha {sha[:SHA_DISPLAY_CHARS]}"
|
| 78 |
+
return label
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def selections_differ(run_metas: list[dict[str, Any]]) -> bool:
|
| 82 |
+
"""๋น๊ต ๋์์ ํ ์งํฉ์ด ๋ค๋ฅธ๊ฐ โ ์๋ ค์ง ํค๊ฐ ๋ ์ด์์ด๊ฑฐ๋, ๋ชจ๋ฅด๋ run ๊ณผ ํํฐ run ์ด ์์์ ๋."""
|
| 83 |
+
signatures = [_selection_signature(m) for m in run_metas]
|
| 84 |
+
known = {s for s in signatures if s is not None}
|
| 85 |
+
if len(known) > 1:
|
| 86 |
+
return True
|
| 87 |
+
unknown = any(s is None for s in signatures)
|
| 88 |
+
return unknown and any(_is_filtered(m) for m in run_metas)
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def dataset_bytes_differ(run_metas: list[dict[str, Any]]) -> bool:
|
| 92 |
+
"""๊ธฐ๋ก๋ ๋ฐ์ดํฐ์
sha256 ์ด ๋ ์ด์์ธ๊ฐ โ ํ ์ ํ๊ณผ ๋ณ๊ฐ๋ก ํ์ผ ๋ด์ฉ์ด ๋ฐ๋์๋ค๋ ์ ํธ."""
|
| 93 |
+
shas = {(_dataset(m) or {}).get("sha256") for m in run_metas}
|
| 94 |
+
return len(shas - {None}) > 1
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
def _collect_run_dirs(
|
| 98 |
+
artifacts_dir: Path, task: str | None, run_ids: str | None, last: int | None
|
| 99 |
+
) -> list[Path]:
|
| 100 |
+
"""๋น๊ตํ run ๋๋ ํ ๋ฆฌ โ `--runs` ๊ฐ ์์ผ๋ฉด ๊ทธ ๋ชฉ๋ก, ์์ผ๋ฉด (ํ์คํฌ๋ก ๊ฑฐ๋ฅธ) ์ ์ฒด."""
|
| 101 |
+
run_dirs: list[Path] = []
|
| 102 |
if run_ids:
|
|
|
|
|
|
|
| 103 |
for rid in run_ids.split(","):
|
| 104 |
rid = rid.strip()
|
| 105 |
d = artifacts_dir / rid
|
|
|
|
| 108 |
else:
|
| 109 |
click.secho(f" Run not found: {rid}, skipping", fg="yellow")
|
| 110 |
else:
|
|
|
|
|
|
|
| 111 |
for d in sorted(artifacts_dir.iterdir()):
|
| 112 |
if not d.is_dir() or not (d / "run.json").exists():
|
| 113 |
continue
|
| 114 |
+
if task and json.loads((d / "run.json").read_text()).get("task") != task:
|
| 115 |
+
continue
|
|
|
|
|
|
|
| 116 |
run_dirs.append(d)
|
|
|
|
| 117 |
if last and last < len(run_dirs):
|
| 118 |
run_dirs = run_dirs[-last:]
|
| 119 |
+
return run_dirs
|
| 120 |
|
|
|
|
|
|
|
|
|
|
| 121 |
|
| 122 |
+
def _load_runs_info(run_dirs: list[Path]) -> list[dict[str, Any]]:
|
| 123 |
+
"""ํ๊ฐ๊ฐ ์๋ run ๋ง (๋ฉํ + evaluation.json) ์ผ๋ก ์ฝ๋๋ค."""
|
| 124 |
runs_info = []
|
|
|
|
| 125 |
for d in run_dirs:
|
|
|
|
| 126 |
eval_file = d / "evaluation.json"
|
| 127 |
if not eval_file.exists():
|
| 128 |
continue
|
| 129 |
+
run_meta = json.loads((d / "run.json").read_text())
|
| 130 |
runs_info.append(
|
| 131 |
{
|
| 132 |
"run_id": d.name,
|
|
|
|
| 134 |
"model": run_meta.get("model", "-"),
|
| 135 |
"prompt": run_meta.get("prompt", run_meta.get("prompt_version", "-")),
|
| 136 |
"pipeline": run_meta.get("pipeline", "-"),
|
| 137 |
+
"eval_data": json.loads(eval_file.read_text()),
|
| 138 |
+
"run_meta": run_meta,
|
| 139 |
}
|
| 140 |
)
|
| 141 |
+
return runs_info
|
| 142 |
|
|
|
|
|
|
|
|
|
|
| 143 |
|
| 144 |
+
def _score_table(runs_info: list[dict[str, Any]]) -> str:
|
| 145 |
+
"""์นดํ
๊ณ ๋ฆฌ๋ณ ์ ์ + Overall ํ."""
|
| 146 |
+
all_categories: set[str] = set()
|
| 147 |
+
for r in runs_info:
|
| 148 |
+
all_categories.update(r["eval_data"].get("scores", {}).keys())
|
| 149 |
headers = ["Category"] + [f"{r['prompt']}/{r['model']}" for r in runs_info]
|
|
|
|
| 150 |
rows = []
|
| 151 |
for cat in sorted(all_categories):
|
| 152 |
row = [cat]
|
|
|
|
| 154 |
score = r["eval_data"].get("scores", {}).get(cat)
|
| 155 |
row.append(f"{score:.4f}" if score is not None else "-")
|
| 156 |
rows.append(row)
|
|
|
|
|
|
|
| 157 |
rows.append(["โ" * 20] + ["โ" * 8] * len(runs_info))
|
| 158 |
overall_row = ["Overall"]
|
| 159 |
for r in runs_info:
|
| 160 |
overall = r["eval_data"].get("overall_score")
|
| 161 |
overall_row.append(f"{overall:.4f}" if overall is not None else "-")
|
| 162 |
rows.append(overall_row)
|
| 163 |
+
return tabulate(rows, headers=headers, tablefmt="simple")
|
| 164 |
|
|
|
|
|
|
|
| 165 |
|
| 166 |
+
def _echo_run_details(runs_info: list[dict[str, Any]]) -> None:
|
| 167 |
+
"""run ๋ณ ํ ์ค โ ํ ์ ํ์ด๋ ๋ฐ์ดํฐ์
๋ฐ์ดํธ๊ฐ ๋ค๋ฅด๋ฉด ๊ฐ run ์ ์ ํ์ ํจ๊ป ์ฃ๋๋ค(D19)."""
|
| 168 |
+
metas = [r["run_meta"] for r in runs_info]
|
| 169 |
+
rows_differ = selections_differ(metas)
|
| 170 |
+
bytes_differ = dataset_bytes_differ(metas)
|
| 171 |
for r in runs_info:
|
| 172 |
+
label = _selection_label(r["run_meta"], show_sha=bytes_differ)
|
| 173 |
+
suffix = f" [{label}]" if rows_differ or bytes_differ else ""
|
| 174 |
+
click.echo(f" {r['prompt']} ({r['model']}, {r['pipeline']}){suffix}")
|
| 175 |
+
if rows_differ:
|
| 176 |
+
click.secho(DIFFERENT_ROWS_WARNING, fg="yellow", bold=True)
|
| 177 |
+
elif bytes_differ:
|
| 178 |
+
click.secho(DIFFERENT_BYTES_WARNING, fg="yellow", bold=True)
|
| 179 |
|
|
|
|
| 180 |
|
| 181 |
+
def _echo_best(runs_info: list[dict[str, Any]]) -> None:
|
| 182 |
best_run = max(runs_info, key=lambda r: r["eval_data"].get("overall_score", 0))
|
| 183 |
best_score = best_run["eval_data"].get("overall_score", 0)
|
| 184 |
click.secho(
|
|
|
|
| 186 |
fg="green",
|
| 187 |
bold=True,
|
| 188 |
)
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
@click.command("compare")
|
| 192 |
+
@click.argument("project")
|
| 193 |
+
@click.option("--task", "-t", default=None, help="Filter by task name")
|
| 194 |
+
@click.option(
|
| 195 |
+
"--runs",
|
| 196 |
+
"run_ids",
|
| 197 |
+
default=None,
|
| 198 |
+
help="Comma-separated run IDs to compare",
|
| 199 |
+
)
|
| 200 |
+
@click.option("--last", "-n", default=None, type=int, help="Compare last N runs for a task")
|
| 201 |
+
@click.pass_context
|
| 202 |
+
def compare_command(ctx, project, task, run_ids, last):
|
| 203 |
+
"""Compare evaluation scores across runs.
|
| 204 |
+
|
| 205 |
+
Examples:
|
| 206 |
+
solar-eval compare my-project --task ci-v1-all --last 5
|
| 207 |
+
solar-eval compare my-project --runs "run1,run2"
|
| 208 |
+
"""
|
| 209 |
+
cfg = ctx.obj["config"]
|
| 210 |
+
if cfg.is_remote:
|
| 211 |
+
raise click.ClickException("Compare is local-mode only")
|
| 212 |
+
|
| 213 |
+
artifacts_dir = cfg.artifacts_dir(project)
|
| 214 |
+
if not artifacts_dir.exists():
|
| 215 |
+
click.secho("No artifacts found.", fg="yellow")
|
| 216 |
+
return
|
| 217 |
+
run_dirs = _collect_run_dirs(artifacts_dir, task, run_ids, last)
|
| 218 |
+
if not run_dirs:
|
| 219 |
+
click.secho("No runs found to compare.", fg="yellow")
|
| 220 |
+
return
|
| 221 |
+
runs_info = _load_runs_info(run_dirs)
|
| 222 |
+
if not runs_info:
|
| 223 |
+
click.secho("No evaluation data available for comparison.", fg="yellow")
|
| 224 |
+
return
|
| 225 |
+
|
| 226 |
+
click.secho(f"\nComparison: {project}/{task or 'all tasks'}", fg="blue", bold=True)
|
| 227 |
+
_echo_run_details(runs_info)
|
| 228 |
+
click.echo()
|
| 229 |
+
click.echo(_score_table(runs_info))
|
| 230 |
+
_echo_best(runs_info)
|
solar_eval/cli/commands/failure_keys.py
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""์ถ๋ก ์คํจ ์ํ์ ์ง๋ฌธ โ run.json `failed_sample_keys` (LP5 D-LP5-4 ยท ๊ณํ ยง2.4).
|
| 2 |
+
|
| 3 |
+
๋ฌ๋๋ ์คํจ ์์น(`failed_sample_indices`)์ ์์ธ ํ์
(`failed_sample_errors`)๋ง run.json ์ ์ด๋ค โ ๋ฌ๋ ๊ฒฝ๋ก๋
|
| 4 |
+
`poc_bundle` ์ import ํ์ง ์๋๋ค(D18). ์ด ๋ชจ๋์ CLI ์ธต์ด๋ผ `poc_bundle.keys` ๋ก ์ง๋ฌธ์ ๋ฐ ์ ์๋ค:
|
| 5 |
+
`execute_plan` ์ด ๋ฌ๋๊ฐ **์ ์ ๋ฐํํ ๋ค** ์คํจ ์์น๋ง๋ค ์ค๋น๋ ๋ฐ์ดํฐ์
์ ์๋ณธ ํ์ ๊ฒฐ๊ณผ ํ๊ณผ ๊ฐ์
|
| 6 |
+
`extract_input` ยท `extract_golden` ์ ์ ์ฉํด `{sample_idx, input_key, golden_key, error_type}` ๋ฅผ ๋ํ๋ค.
|
| 7 |
+
|
| 8 |
+
ํ
์คํธ๋ ์ฃ์ง ์๋๋ค โ hex ์ง๋ฌธ๊ณผ ํ์
์ด๋ฆ๋ฟ์ด๋ค. ๊ณ์ฐํ ์ ์์ผ๋ฉด(์์น๊ฐ ์ ํ ๋ฐ ยท ์
๋ ฅ์ด ๋น ๊ฐ์ฒด ยท
|
| 9 |
+
JCS ๊ฐ ๊ฑฐ๋ถํ๋ ๊ฐ) **์๋ฌด๊ฒ๋ ์ฐ์ง ์๋๋ค**(= ๋ชจ๋ฆ). wrap ์ ๊ทธ๋ `failures.jsonl` ์ ์๋ตํ๋ค.
|
| 10 |
+
๋จ ์ฌ๊ฐ ์ ์๋๊ฐ ๋จ๊ธด ๋ชฉ๋ก์ด run ๋ฌธ์์ ์์ผ๋ฉด `[]` ๋ก ๋ฎ๋๋ค(`record_failure_keys` โ ๋ก์ ์ง๋ฌธ์ ๋จ๊ธฐ์ง ์๋๋ค).
|
| 11 |
+
๊ฒฝ๊ณ ํ ์ค์๋ ์ด์ ๋ง ์ ๊ณ ๊ฐ์ ์ ์ง ์๋๋ค.
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
+
from __future__ import annotations
|
| 15 |
+
|
| 16 |
+
import asyncio
|
| 17 |
+
import inspect
|
| 18 |
+
import json
|
| 19 |
+
from collections.abc import Mapping
|
| 20 |
+
from typing import Any
|
| 21 |
+
|
| 22 |
+
import click
|
| 23 |
+
|
| 24 |
+
from poc_bundle.keys import golden_key, input_key
|
| 25 |
+
from poc_bundle.v11 import FAILED_SAMPLE_KEYS_KEY
|
| 26 |
+
from solar_eval.core.run_records import FAILED_SAMPLE_ERRORS_KEY, extract_golden, extract_input
|
| 27 |
+
|
| 28 |
+
#: `failed_sample_errors` ์ ๊ทธ ์์น๊ฐ ์์ ๋์ ํ์
์ด๋ฆ(wrap ์ `exception:unknown` ์ผ๋ก ์ ๋๋ค).
|
| 29 |
+
UNKNOWN_ERROR_TYPE = "unknown"
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
class _KeysUnknown(Exception):
|
| 33 |
+
"""์ง๋ฌธ์ ๋ฐ ์ ์๋ค โ ์ด์ ํ ๋ง๋๋ง ์ฃ๋๋ค(๊ฐ์ ์ฃ์ง ์๋๋ค)."""
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def failure_keys(
|
| 37 |
+
run_doc: Mapping[str, Any] | None, prepared: Any, task_config: Mapping[str, Any]
|
| 38 |
+
) -> list[dict[str, Any]] | None:
|
| 39 |
+
"""run ๋ฌธ์์ ์คํจ ์์น โ `failed_sample_keys` ์์๋ค(sample_idx ์). ๋ชจ๋ฅด๋ฉด None.
|
| 40 |
+
|
| 41 |
+
`prepared` ๋ `PreparedDataset`(๋๋ `selected` ๊ฐ `(์๋ณธ ์์น, ํ)` ๋ชฉ๋ก์ธ ๊ฐ์ฒด)์ด๋ค.
|
| 42 |
+
์คํจ๊ฐ ์์ผ๋ฉด `[]` ์ด๋ค.
|
| 43 |
+
"""
|
| 44 |
+
try:
|
| 45 |
+
indices = _failed_indices(run_doc)
|
| 46 |
+
rows = dict(prepared.selected)
|
| 47 |
+
types = _error_types(run_doc.get(FAILED_SAMPLE_ERRORS_KEY))
|
| 48 |
+
return [_entry(idx, rows, task_config, types) for idx in sorted(indices)]
|
| 49 |
+
except _KeysUnknown as unknown:
|
| 50 |
+
click.secho(f" failure keys not recorded ({unknown})", fg="yellow", err=True)
|
| 51 |
+
return None
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def record_failure_keys(
|
| 55 |
+
store: Any, run_id: str, prepared: Any, task_config: Mapping[str, Any]
|
| 56 |
+
) -> None:
|
| 57 |
+
"""run ๋ฌธ์๋ฅผ ์ฝ์ด ์คํจ ์ง๋ฌธ์ ๊ณ์ฐํ๊ณ `update_run` ์ผ๋ก ๋ํ๋ค.
|
| 58 |
+
|
| 59 |
+
run ๋ฌธ์์ ์์ ์๋(์ฌ๊ฐ ์ )๊ฐ ๋จ๊ธด ๋น์ง ์์ ๋ชฉ๋ก์ด ์์ผ๋ฉด **๋ ๋ฎ๋๋ค** โ ์ ์ฅ์ `update_run` ์ ๋ณํฉ์ด๋ผ
|
| 60 |
+
๋ฎ์ง ์์ผ๋ฉด ๋ก์ ๋ชฉ๋ก์ด ๋จ๋๋ค. ์ ๋ชฉ๋ก์ด ์์ผ๋ฉด ๊ทธ๊ฒ์ผ๋ก, ์คํจ๊ฐ ์์ผ๋ฉด(`[]`, ์ฌ๊ฐ๊ฐ ์คํจ๋ฅผ ๋ชจ๋ ํ๋ณต) `[]` ๋ก
|
| 61 |
+
(wrap ์ด 0ํ์ ์ด๋ค), ๋ชจ๋ฅด๋ฉด(None) ์ญ์ `[]` ๋ก ๋ฎ๋๋ค โ `[]` ๋ ๋น์ง ์์ `failed_sample_indices` ์ ์งํฉ์ด ๋ฌ๋ผ
|
| 62 |
+
wrap ์ด ์๋ต(๋ชจ๋ฆ)ํ๋ฏ๋ก, ๋ก์ ์ง๋ฌธ์ด ์ฐ์ฐํ ๊ฐ์ ์์น ์งํฉ๊ณผ ๋ง์ ํ๋ฆฐ ์์ผ๋ก ์ฐ์ด๋ ๊ธธ์ด ์๋ค.
|
| 63 |
+
๋ก์ ๋ชฉ๋ก์ด ์์ผ๋ฉด: ๋ชจ๋ฅด๋ฉด ์ฐ์ง ์๊ณ , ์คํจ๊ฐ ์์ด๋ ์ฐ์ง ์๋๋ค โ ์คํจ ์์์ `failed_sample_indices = []` ๊ฐ
|
| 64 |
+
์ด๋ฏธ ๋งํ๋ค(์ฑ๊ณต run ๋ง๋ค run.json ์ ํ ๋ฒ ๋ ์ฐ์ง ์๋๋ค). run ๋ฌธ์๋ฅผ ์ฝ์ ์ ์๋ ์ ์ฅ์(`load_run_meta`
|
| 65 |
+
์์)๋ ๋ชจ๋ฆ์ด๋ค.
|
| 66 |
+
"""
|
| 67 |
+
loader = getattr(store, "load_run_meta", None)
|
| 68 |
+
run_doc = loader() if callable(loader) else None
|
| 69 |
+
keys = failure_keys(run_doc, prepared, task_config)
|
| 70 |
+
if _has_stale_keys(run_doc):
|
| 71 |
+
keys = keys or []
|
| 72 |
+
elif not keys:
|
| 73 |
+
return
|
| 74 |
+
pending = store.update_run(run_id, {FAILED_SAMPLE_KEYS_KEY: keys})
|
| 75 |
+
if inspect.isawaitable(pending):
|
| 76 |
+
asyncio.run(pending)
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def _has_stale_keys(run_doc: Mapping[str, Any] | None) -> bool:
|
| 80 |
+
"""run ๋ฌธ์์ `[]` ๊ฐ ์๋ `failed_sample_keys` ๊ฐ ์ด๋ฏธ ์๋ค(์์ ์๋์ ๋ชฉ๋ก โ ๋ชจ์์ ๋ฐ์ง์ง ์๋๋ค)."""
|
| 81 |
+
return isinstance(run_doc, Mapping) and run_doc.get(FAILED_SAMPLE_KEYS_KEY, []) != []
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def _failed_indices(run_doc: Mapping[str, Any] | None) -> list[int]:
|
| 85 |
+
if not isinstance(run_doc, Mapping):
|
| 86 |
+
raise _KeysUnknown("run document unreadable")
|
| 87 |
+
indices = run_doc.get("failed_sample_indices")
|
| 88 |
+
if not isinstance(indices, list):
|
| 89 |
+
raise _KeysUnknown("no failed_sample_indices list")
|
| 90 |
+
if not all(isinstance(i, int) and not isinstance(i, bool) and i >= 0 for i in indices):
|
| 91 |
+
raise _KeysUnknown("failed_sample_indices is not a list of positions")
|
| 92 |
+
if len(set(indices)) != len(indices):
|
| 93 |
+
raise _KeysUnknown("failed_sample_indices has duplicates")
|
| 94 |
+
return indices
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
def _error_types(errors: Any) -> dict[int, str]:
|
| 98 |
+
"""`failed_sample_errors` โ `{sample_idx: ํ์
์ด๋ฆ}`. ๋ชจ์์ด ํ๋ฆฐ ์์๋ ๊ฑด๋๋ด๋ค(โ unknown)."""
|
| 99 |
+
if not isinstance(errors, list):
|
| 100 |
+
return {}
|
| 101 |
+
types: dict[int, str] = {}
|
| 102 |
+
for element in errors:
|
| 103 |
+
if not isinstance(element, Mapping):
|
| 104 |
+
continue
|
| 105 |
+
idx, name = element.get("sample_idx"), element.get("error_type")
|
| 106 |
+
if isinstance(idx, int) and not isinstance(idx, bool) and isinstance(name, str) and name:
|
| 107 |
+
types[idx] = name
|
| 108 |
+
return types
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def _entry(
|
| 112 |
+
idx: int, rows: Mapping[int, Any], task_config: Mapping[str, Any], types: Mapping[int, str]
|
| 113 |
+
) -> dict[str, Any]:
|
| 114 |
+
sample = rows.get(idx)
|
| 115 |
+
if not isinstance(sample, Mapping):
|
| 116 |
+
raise _KeysUnknown("failed position outside the selection")
|
| 117 |
+
input_data = _as_stored(extract_input(sample, task_config.get("input_fields", [])))
|
| 118 |
+
if not input_data:
|
| 119 |
+
raise _KeysUnknown("empty input")
|
| 120 |
+
golden = _as_stored(extract_golden(sample, task_config))
|
| 121 |
+
try:
|
| 122 |
+
# items ๊ท์น๊ณผ ๊ฐ๋ค โ ์์ง run ์ ์
๋ ฅ ์ ์ฒด์ฑ์ ์์ง์ด ๊ณ ๋ฅธ input ๊ฐ์ฒด ์ ์ฒด(fields=None).
|
| 123 |
+
ik, gk = input_key(input_data, None), golden_key(golden)
|
| 124 |
+
except (TypeError, ValueError) as exc: # CanonicalizationError ๋ ValueError ๋ค
|
| 125 |
+
raise _KeysUnknown(f"input not fingerprintable: {type(exc).__name__}") from None
|
| 126 |
+
return {
|
| 127 |
+
"sample_idx": idx,
|
| 128 |
+
"input_key": ik,
|
| 129 |
+
"golden_key": gk,
|
| 130 |
+
"error_type": types.get(idx, UNKNOWN_ERROR_TYPE),
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
def _as_stored(value: Any) -> Any:
|
| 135 |
+
"""๊ฒฐ๊ณผ ์ ์ฅ์(`JsonlStore.insert_run_result`)๊ฐ ์ฐ๋ ์ง๋ ฌํ๋ฅผ ํ ๋ฒ ๊ฑฐ์น ๊ฐ.
|
| 136 |
+
|
| 137 |
+
items ๋ results.jsonl ์ ๋ค์ ์ฝ์ด ์ง๋ฌธ์ ๋จ๋ฏ๋ก, ๊ฐ์ ์๋ณธ ํ์ด๋ฉด ๊ฐ์ ํค๊ฐ ๋์ค๊ฒ ๊ฐ์
|
| 138 |
+
`json.dumps` ์๋ณต์ ๊ฑฐ์น๋ค(ํํ โ ๋ชฉ๋ก ยท ๋ฌธ์์ด์ด ์๋ ํค โ ๋ฌธ์์ด). ์ง๋ ฌํํ ์ ์์ผ๋ฉด ๋ชจ๋ฆ.
|
| 139 |
+
"""
|
| 140 |
+
try:
|
| 141 |
+
return json.loads(json.dumps(value, ensure_ascii=False))
|
| 142 |
+
except (TypeError, ValueError) as exc:
|
| 143 |
+
raise _KeysUnknown(f"input not serializable: {type(exc).__name__}") from None
|
solar_eval/cli/commands/run_eval.py
ADDED
|
@@ -0,0 +1,340 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""`solar-eval runs eval` โ ์ ์ถ๋ก ์์ด ๋์คํฌ์ ๊ฒฐ๊ณผ๋ง ๋ค์ ์ฑ์ ํ๋ค (๊ณํ 2026-09-24-phase1a D11).
|
| 2 |
+
|
| 3 |
+
`runs.py` ์์ ๋ผ์ด ๋๋ค(E1). ์ฎ๊ธฐ๋ฉด์ ๋ฐ๋ ๊ฒ ๋ ๊ฐ์ง:
|
| 4 |
+
|
| 5 |
+
- evaluation.json `eval_model` ์ ์์ ์ `"local"` ์ด ์๋๋ผ ํ๊ฐ๊ธฐ๊ฐ ๋ณด๊ณ ํ๋ judge ๋ชจ๋ธ์ด๋ค
|
| 6 |
+
(judge ๊ฐ ์๋ lcs_diffยทrule_based ๋ null). ๋ฌ๋์ ๊ฐ์ ๊ท์น์ด๋ค.
|
| 7 |
+
- run.json ์ `snapshot` ์ด ์์ผ๋ฉด `snapshot.config.evaluator_config` ๋ฅผ ์ ํ๊ฐ๊ธฐ๋ก ๋ค์ ์ฐ๊ณ
|
| 8 |
+
`config_fingerprint` ๋ฅผ ๋ค์ ๊ณ์ฐํ๋ฉฐ `provenance.re_evaluated_at` ์ ๋จ๊ธด๋ค โ ๋ฒ๋ค๋ก ๊ฐ์ ๋
|
| 9 |
+
ํ๊ฐ ํ์ผ๊ณผ ์ค๋
์ท์ด ์ด๊ธ๋์ง ์๊ฒ(SPEC ยง4 "ํ๊ฐ ์ ์ฒด์ฑ ์ฐ๊ธฐ"์ ์ต์ ๋์, Q14).
|
| 10 |
+
|
| 11 |
+
**๋ ๊ฒฝ๋ก(๊ณํ 2026-09-27 L6 ยง2.12 B4 ยท ๋ง์คํฐ D-M2-6).** hub ๋์ lcs_diff ๊ณ์ด์ ์๋ฃ๋ run ์ **run ๋๋ ํ ๋ฆฌ๋ฅผ ๊ฑด๋๋ฆฌ์ง
|
| 12 |
+
์๋๋ค** โ ์ฑํ๋ run ๋ฒ๋ค์ ๊ฒฐ๊ณผ๋ก ์ฑ์ ํด ํ๊ฐ ๋๋ ํ ๋ฆฌ์ ๋จ๊ธฐ๊ณ `purpose=rescore_after_fix` ์ฑ์ ๋ฒ๋ค๋ก ๋ฐํํ ๋ค
|
| 13 |
+
(opt-in) hub ์ ์ฃ๋๋ค(`solar_eval.scoring_publish`). ๊ทธ ๋ฐ(hub ๋์ ์๋ยท๋น-lcsยท๋ฏธ์๋ฃ)์ ์์ ์ค๋ ๋์(in-place) +
|
| 14 |
+
์๋ด ํ ์ค์ด๋ค. hub ๋์ run ์ ์ ๋์์ผ๋ก ๋๋ฆฌ๋ ํ์ถ๊ตฌ ํ๋๊ทธ๋ L10 ์ปท์ค๋ฒ์์ ์ง์ ๋ค(๊ณํ 2026-09-28 L10 ยง2.6 ยท D-L10-7 โ ์ฃผ๋ฉด click ์ฌ์ฉ
|
| 15 |
+
์ค๋ฅ, ์ข
๋ฃ 2). ์ข
๋ฃ ์ฝ๋: ์ฑ์ ์ ๋ง์ณค์ผ๋ฉด 0(๋ฐํยทhub ์คํจ์ ๋ฌด๊ด), `--strict-ingest`/
|
| 16 |
+
`SOLAR_EVAL_STRICT_INGEST=1` ์ด๊ณ ์คํจ ๋ค๋ฆฌ๊ฐ ์์ผ๋ฉด 3, ์ ๊ฒฝ๋ก์์ ๋ฒ๋ค์ด ์๊ฑฐ๋ ์ฑ์ ์ ๋ชป ํ์ผ๋ฉด 1.
|
| 17 |
+
|
| 18 |
+
**judge ๊ฒฝ๋ก(๊ณํ 2026-09-28 L12 ยง2.5 ยท D-L12-6ยท9ยท13).** ํ์คํฌ ํ๊ฐ๊ธฐ๊ฐ `rubric_judge` ์ธ hub ๋์ ์๋ฃ run ์ ๊ธฐ์ค๋ณ judge ์ฑ์
|
| 19 |
+
๋ฒ๋ค์ ๋ฐํํ๋ค(`scoring_publish.judge`). `--purpose {primary,variance_check}` ์ `--retry-of <evaluation_run_id>` ๋ ์ด ๊ฒฝ๋ก์์๋ง
|
| 20 |
+
๋ฐ๋๋ค(ํจ๊ป ์ฐ๊ฑฐ๋ ๋ค๋ฅธ ๊ฒฝ๋ก์์ ์ฐ๋ฉด ์ฌ์ฉ ์ค๋ฅ, ์ข
๋ฃ 2). judge ์ค์ ์ ๊ฒฝ๋ก ํ์ ์ ์ ํ์ธํ๋ค.
|
| 21 |
+
judge ๊ฐ์ง์ ์ข
๋ฃ ์ฝ๋: ๋ฐํ ์ฌ์ ๊ฑฐ๋ถ 1 ยท retry ๋์ ์์ 0 ยท ์ฑ์ ๋ค ์คํจ๋ ์ ๊ท์น ๊ทธ๋๋ก.
|
| 22 |
+
"""
|
| 23 |
+
|
| 24 |
+
from __future__ import annotations
|
| 25 |
+
|
| 26 |
+
import asyncio
|
| 27 |
+
from collections.abc import Mapping
|
| 28 |
+
from datetime import datetime, timezone
|
| 29 |
+
from pathlib import Path
|
| 30 |
+
from typing import Any
|
| 31 |
+
|
| 32 |
+
import click
|
| 33 |
+
|
| 34 |
+
from solar_eval.cli.commands import run_meta as run_meta_module
|
| 35 |
+
from solar_eval.cli.commands.run_meta import PREP_ERRORS, find_task, load_run
|
| 36 |
+
from solar_eval.cli.formatters import format_scores
|
| 37 |
+
from solar_eval.core.run_records import (
|
| 38 |
+
build_eval_detail_docs,
|
| 39 |
+
build_eval_sample_from_result,
|
| 40 |
+
build_evaluation_doc,
|
| 41 |
+
)
|
| 42 |
+
from solar_eval.core.snapshot import self_check, with_evaluator
|
| 43 |
+
from solar_eval.hub_hook import exit_code_for
|
| 44 |
+
from solar_eval.hub_hook.optin import strict_requested
|
| 45 |
+
from solar_eval.hub_hook.types import LEG_PUBLISH, PUBLISH_NO_BUNDLE_PREFIX, STATUS_SKIPPED
|
| 46 |
+
from solar_eval.scoring_publish import FAMILY_JUDGE, EvalJob, Route, decide_route, publish_run_eval
|
| 47 |
+
from solar_eval.scoring_publish import judge as judge_publish
|
| 48 |
+
from solar_eval.scoring_publish.eval_dir import REPORT_SAMPLES
|
| 49 |
+
from solar_eval.scoring_publish.publish import PublishOutcome, Scored
|
| 50 |
+
|
| 51 |
+
#: `--no-ingest`ยท`--strict-ingest` ๋์๋ง(๊ณํ L6 ยง2.2 `runs eval` ํ).
|
| 52 |
+
NO_INGEST_HELP = "์ ๊ฒฝ๋ก์์ hub ๋ค๋ฆฌ(์ ์ฌ)๋ง ๊ฑด๋๋ด๋ค โ ํ๊ฐ ๋๋ ํ ๋ฆฌยท์ฑ์ ๋ฒ๋ค ๋ฐํ์ ํ๋ค"
|
| 53 |
+
STRICT_INGEST_HELP = (
|
| 54 |
+
"์ ๊ฒฝ๋ก์ ๋ฒ๋คยท๋ฐํยทhub ๋ค๋ฆฌ๊ฐ ํ๋๋ผ๋ ์คํจํ๋ฉด ์ข
๋ฃ ์ฝ๋ 3 (๊ธฐ๋ณธ 0). "
|
| 55 |
+
"SOLAR_EVAL_STRICT_INGEST=1 ๊ณผ ๊ฐ๋ค"
|
| 56 |
+
)
|
| 57 |
+
#: judge ๊ฒฝ๋ก ์ต์
๋์๋ง(๊ณํ 2026-09-28 L12 D-L12-9).
|
| 58 |
+
PURPOSE_HELP = (
|
| 59 |
+
"rubric judge ์ฑ์ ๋ฒ๋ค์ purpose. ๊ธฐ๋ณธ primary โ ๊ฐ์ ์ค์ ์ primary ๊ฐ ์ด๋ฏธ ์์ผ๋ฉด ๊ฑฐ๋ถํ๋ค. "
|
| 60 |
+
"๊ฐ์ ์ค์ ์ ๋ค์ ์ฑ์ ํด ํ์ ๋ถ์ฐ์ ์ฌ๋ ค๋ฉด variance_check"
|
| 61 |
+
)
|
| 62 |
+
RETRY_OF_HELP = "์ด ํ๊ฐ ์คํ(evaluation_run_id, ULID)์ error ํ์ ๋ง ๋ค์ ๋ถ๋ฅธ๋ค โ purpose=retry"
|
| 63 |
+
#: judge ๊ฒฝ๋ก์์๋ง ๋ฐ๋ ์ต์
โ ํจ๊ป ์ฐ๊ฑฐ๋ ๋ค๋ฅธ ๊ฒฝ๋ก์์ ์ฐ๋ฉด ์ฌ์ฉ ์ค๋ฅ(์ข
๋ฃ 2).
|
| 64 |
+
USAGE_PURPOSE_WITH_RETRY = "--purpose ์ --retry-of ๋ ํจ๊ป ์ธ ์ ์๋ค"
|
| 65 |
+
USAGE_JUDGE_ONLY = "--purpose/--retry-of ๋ rubric judge ์ฑ์ ๋ฒ๋ค ๊ฒฝ๋ก์์๋ง ์ด๋ค"
|
| 66 |
+
#: ์ด run ์ด judge ๊ฒฝ๋ก๊ฐ ์๋ ์ด์ (๊ฒฝ๋ก ์๋ด)๊ฐ ์์ผ๋ฉด ์ฌ์ฉ ์ค๋ฅ ๋ค์ ๋ถ์ธ๋ค.
|
| 67 |
+
USAGE_NOT_JUDGE_REASON = " โ ์ด run ์ judge ๊ฒฝ๋ก๊ฐ ์๋๋ค: {notice}"
|
| 68 |
+
#: judge ๊ฐ์ง์์ ์ฑ์ ํ์ง ๋ชปํ์ ๋ โ ์ข
๋ฃ 1. ํ๋๊ทธ ๊ถ์ ๋ ๊ทธ ํ๋๊ทธ๊ฐ ํธ๋ ์คํจ์๋ง ๋ถ์ธ๋ค(์๋ ๋).
|
| 69 |
+
JUDGE_NOT_SCORED = "์ฑ์ ํ์ง ์์๋ค โ ์ ์์ฝ์ ์คํจ ๋ค๋ฆฌ์ ๊ทธ ์๋ด๋ฅผ ๋ณธ๋ค"
|
| 70 |
+
#: ๊ฐ์ ์ค์ ์ primary ๊ฐ ์ด๋ฏธ ์๋ค(`publish_judge:primary_exists`) โ purposeยทretry ํ๋๊ทธ๊ฐ ํธ๋ ์ ์ผํ ์คํจ.
|
| 71 |
+
JUDGE_NOT_SCORED_PRIMARY_EXISTS = (
|
| 72 |
+
f"{JUDGE_NOT_SCORED}. ๊ฐ์ ์ค์ ์ ๋ค์ ์ฑ์ ํ๋ ค๋ฉด --purpose variance_check, "
|
| 73 |
+
"error ๋ง ๋ค์ ๋ถ๏ฟฝ๏ฟฝ๋ ค๋ฉด --retry-of <evaluation_run_id>"
|
| 74 |
+
)
|
| 75 |
+
#: ์ฑํ๋ run ๋ฒ๋ค์ด ์๋ค(`publish_no_bundle:*`) โ ํ๋๊ทธ๋ก๋ ํ๋ฆฌ์ง ์๋๋ค.
|
| 76 |
+
JUDGE_NOT_SCORED_NO_BUNDLE = f"{JUDGE_NOT_SCORED}. ์ฑํ๋ run ๋ฒ๋ค์ด ์๋ค โ ์์ฝ์ ๋ฐฑํ ๋ช
๋ น์ผ๋ก ๋ถ๋ฅ๋ฅผ ํ์ธํ ๋ค ๋ค์ ๋๋ฆฐ๋ค"
|
| 77 |
+
#: retry ๋์์ด ์์ ๋(์ข
๋ฃ 0).
|
| 78 |
+
JUDGE_RETRY_NOTHING = "๋ค์ ๋ถ๋ฅผ error ํ์ ์ด ์๋ค โ ์ ํ๊ฐ ์คํ์ error ๊ฐ ์ด๋ฏธ ๋ชจ๋ ํ๋ ธ๋ค"
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def _ulid_option(ctx: click.Context, param: click.Parameter, value: str | None) -> str | None:
|
| 82 |
+
"""`--retry-of` ๋ ULID ๋ชจ์๋ง ๋ฐ๋๋ค โ ํ๋ฆฌ๋ฉด ์ฌ์ฉ ์ค๋ฅ(์ข
๋ฃ 2)."""
|
| 83 |
+
from poc_bundle.mirror import ULID_RE
|
| 84 |
+
|
| 85 |
+
if value is None or ULID_RE.fullmatch(value) is not None:
|
| 86 |
+
return value
|
| 87 |
+
raise click.BadParameter("evaluation_run_id ๋ ULID(26์ Crockford base32)์ฌ์ผ ํ๋ค")
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
async def _score_all(evaluator, judge_provider, results) -> tuple[list[dict], list[dict]]:
|
| 91 |
+
"""์ํ๋ณ ์ฑ์ . ์ฑ์ ์คํจ๋ ์ ์๊ฐ ์๋๋ผ ์คํจ๋ก ๋จ๊ธด๋ค โ aggregate() ์๋ ์ฑ๊ณต๋ถ๋ง ๋๊ธด๋ค."""
|
| 92 |
+
eval_results: list[dict[str, Any]] = []
|
| 93 |
+
eval_failures: list[dict[str, Any]] = []
|
| 94 |
+
for result in results:
|
| 95 |
+
# ๋ฆฌ์คํธ ์์น๊ฐ ์๋๋ผ ์๋ณธ sample_idx ๊ฐ ์ ๋ณธ์ด๋ค(F6).
|
| 96 |
+
sample_idx = result["sample_idx"]
|
| 97 |
+
try:
|
| 98 |
+
eval_sample = build_eval_sample_from_result(result, reference=result.get("golden"))
|
| 99 |
+
evaluator.validate_required_fields(eval_sample)
|
| 100 |
+
eval_result = await evaluator.evaluate(sample=eval_sample, provider=judge_provider)
|
| 101 |
+
eval_results.append({**eval_result, "sample_idx": sample_idx})
|
| 102 |
+
except Exception as e:
|
| 103 |
+
click.secho(f" Sample {sample_idx} eval failed: {e}", fg="yellow")
|
| 104 |
+
eval_failures.append({"sample_idx": sample_idx, "error": str(e)})
|
| 105 |
+
return eval_results, eval_failures
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
def _updated_snapshot(
|
| 109 |
+
run_meta: dict[str, Any], evaluator_config: dict[str, Any] | None, judge_provider: Any
|
| 110 |
+
) -> dict[str, Any] | None:
|
| 111 |
+
"""์ค๋
์ท์ด ์์ผ๋ฉด ์ ํ๊ฐ๊ธฐ๋ก ๋ค์ ์ด ์ค๋
์ท, ์์ผ๋ฉด None(์ run ์ ์ค๋
์ท์ ์๋ก ์ฐ์ง ์๋๋ค).
|
| 112 |
+
|
| 113 |
+
์ฑ์ **์ ์** ๋ถ๋ฅธ๋ค โ ๊ธฐ๋ก๋ ์ค๋
์ท์ด ๊นจ์ก๊ฑฐ๋ ์ ํ๊ฐ๊ธฐ๋ก ์ง๋ฌธ์ ๋ค์ ๋ฐ ์ ์์ผ๋ฉด ์ ๋ฃ judge
|
| 114 |
+
ํธ์ถ์ ํ๊ธฐ ์ ์ ๋ฉ์ถ๋ค. `judge_provider` ๋ ์ด๋ฒ ์ฌ์ฑ์ ์ด ์ค์ ๋ก ์ฐ๋ ๊ฒ์ด๋ค โ judge ๊ฐ ์๋ run ์
|
| 115 |
+
llm_judge ๋ก ๋ค์ ์ฑ์ ํ๋ฉด ๊ทธ ํ๋ก๋ฐ์ด๋ ๋ชจ๋์ด `runtime_impl_hash` ์ ๋ค์ด๊ฐ์ผ ํ๋ค(D7).
|
| 116 |
+
`re_evaluated_at` ์ ์ฑ์ ์ด ๋๋ ๋ค `_stamp` ๊ฐ ๋ฎ์ด์ด๋ค.
|
| 117 |
+
"""
|
| 118 |
+
snapshot = run_meta.get("snapshot")
|
| 119 |
+
if snapshot is None:
|
| 120 |
+
return None
|
| 121 |
+
started = datetime.now(timezone.utc).isoformat()
|
| 122 |
+
try:
|
| 123 |
+
# ๊ธฐ๋ก๋ ์ค๋
์ท๋ถํฐ ๊ฒ์ฌํ๋ค โ `config` ๊ฐ ์๋ ์ค๋
์ท์ด ๋งจ KeyError ๋ก ์ฃฝ์ง ์๊ฒ.
|
| 124 |
+
self_check(snapshot)
|
| 125 |
+
updated = with_evaluator(
|
| 126 |
+
snapshot, evaluator_config, re_evaluated_at=started, judge_provider=judge_provider
|
| 127 |
+
)
|
| 128 |
+
self_check(updated)
|
| 129 |
+
except PREP_ERRORS as e:
|
| 130 |
+
raise click.ClickException(f"cannot update the run snapshot's evaluator: {e}") from None
|
| 131 |
+
return updated
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
def _stamp(snapshot: dict[str, Any], at: str) -> dict[str, Any]:
|
| 135 |
+
"""`provenance.re_evaluated_at` ์ ์ฑ์ ์ด ๋๋ ์๊ฐ์ผ๋ก โ provenance ๋ ์ง๋ฌธ ๋ฐ์ด๋ค."""
|
| 136 |
+
return {**snapshot, "provenance": {**snapshot["provenance"], "re_evaluated_at": at}}
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
def _make_evaluator(evaluator_config: dict[str, Any] | None):
|
| 140 |
+
"""๋ฌ๋ยท์ค๋
์ท๊ณผ ๊ฐ์ ๊ท์น โ ํค๊ฐ ์์ ๋๋ง ๊ธฐ๋ณธ ํ๊ฐ๊ธฐ. ์๋ชป๋ ์ค์ ์ ์ฑ์ ์ ์ ๋ฉ์ถ๋ค."""
|
| 141 |
+
from solar_eval.evaluators.registry import DEFAULT_EVALUATOR_CONFIG, create_evaluator
|
| 142 |
+
|
| 143 |
+
raw = DEFAULT_EVALUATOR_CONFIG if evaluator_config is None else evaluator_config
|
| 144 |
+
try:
|
| 145 |
+
return create_evaluator(raw)
|
| 146 |
+
except ValueError as e:
|
| 147 |
+
raise click.ClickException(f"invalid evaluator config: {e}") from None
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
async def _evaluate_run(store, run_id, run_meta, task_config, results) -> dict[str, Any]:
|
| 151 |
+
"""์ฑ์ โ evaluation.jsonยทeval_detailsยทrun.json(์ํยท์ค๋
์ท) ์ ์ฅ. ์ง๊ณ๋ฅผ ๋๋ ค์ค๋ค."""
|
| 152 |
+
evaluator_config = task_config.get("evaluator")
|
| 153 |
+
evaluator = _make_evaluator(evaluator_config)
|
| 154 |
+
# judge provider ๋ฅผ ์ ๋๊ธฐ๋ฉด llm_judge ๊ณ์ด์ ์ ์ํ์์ ์คํจํ๋ค -- ๊ทธ๋ฆฌ๊ณ
|
| 155 |
+
# ๊ทธ ์คํจ๊ฐ ์์ ์ 0์ placeholder ๋ก ๋ฎ์ฌ ์กฐ์ฉํ completed ๋ก ๋๋ฌ๋ค(F8).
|
| 156 |
+
_, judge_provider = run_meta_module.make_providers(evaluator_config)
|
| 157 |
+
snapshot = _updated_snapshot(run_meta, evaluator_config, judge_provider)
|
| 158 |
+
eval_results, eval_failures = await _score_all(evaluator, judge_provider, results)
|
| 159 |
+
now = datetime.now(timezone.utc)
|
| 160 |
+
if snapshot is not None:
|
| 161 |
+
snapshot = _stamp(snapshot, now.isoformat())
|
| 162 |
+
|
| 163 |
+
aggregated = evaluator.aggregate(eval_results)
|
| 164 |
+
# eval_model = ํ๊ฐ๊ธฐ๊ฐ ๋ณด๊ณ ํ๋ judge ๋ชจ๋ธ(์์ผ๏ฟฝ๏ฟฝ None) โ ์์ ์ "local" ์ ๊ฑฐ์ง์ด์๋ค(D11).
|
| 165 |
+
doc = build_evaluation_doc(
|
| 166 |
+
run_id, aggregated, eval_results, eval_failures, evaluator.judge_model
|
| 167 |
+
)
|
| 168 |
+
eval_id = await store.create_evaluation(doc)
|
| 169 |
+
await store.insert_eval_details(build_eval_detail_docs(eval_id, eval_results, eval_failures))
|
| 170 |
+
update: dict[str, Any] = {"status": "completed", "completed_at": now}
|
| 171 |
+
if snapshot is not None:
|
| 172 |
+
update["snapshot"] = snapshot
|
| 173 |
+
await store.update_run(run_id, update)
|
| 174 |
+
return {**aggregated, "overall_score": doc["overall_score"], "failed": len(eval_failures)}
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
def _report(aggregated: Mapping[str, Any], run_dir: Path) -> None:
|
| 178 |
+
click.echo()
|
| 179 |
+
click.secho("Evaluation completed!", fg="green", bold=True)
|
| 180 |
+
overall = aggregated.get("overall_score")
|
| 181 |
+
if overall is None:
|
| 182 |
+
# ์ฑ์ ์ด ํ ๊ฑด๋ ์ฑ๊ณตํ์ง ๋ชปํ๋ค -- ์ซ์๋ฅผ ์ฐ์ผ๋ฉด "0์ "์ผ๋ก ์ฝํ๋ค.
|
| 183 |
+
click.secho(" Overall Score: ์ธก์ ์์ (์ ์ํ ์ฑ์ ์คํจ)", fg="red", bold=True)
|
| 184 |
+
else:
|
| 185 |
+
click.echo(f" Overall Score: {overall:.4f}")
|
| 186 |
+
samples = aggregated.get(REPORT_SAMPLES)
|
| 187 |
+
if samples is not None:
|
| 188 |
+
click.echo(f" Samples: {samples} (์ฑํ๋ run ๋ฒ๋ค ๊ธฐ์ค)")
|
| 189 |
+
failed = aggregated.get("failed", 0)
|
| 190 |
+
if failed:
|
| 191 |
+
click.secho(f" ์ฑ์ ์คํจ: {failed}๊ฑด (score: null ๋ก ๊ธฐ๋ก๋จ)", fg="yellow")
|
| 192 |
+
scores = aggregated.get("scores", {})
|
| 193 |
+
if scores:
|
| 194 |
+
click.echo(f" Scores: {format_scores(scores)}")
|
| 195 |
+
click.echo(f"\nResults saved to: {run_dir}/")
|
| 196 |
+
|
| 197 |
+
|
| 198 |
+
@click.command("eval")
|
| 199 |
+
@click.argument("project")
|
| 200 |
+
@click.option("--run-id", "-r", required=True, help="Run ID to evaluate")
|
| 201 |
+
@click.option("--no-ingest", is_flag=True, help=NO_INGEST_HELP)
|
| 202 |
+
@click.option("--strict-ingest", is_flag=True, help=STRICT_INGEST_HELP)
|
| 203 |
+
@click.option(
|
| 204 |
+
"--purpose", type=click.Choice(judge_publish.CLI_PURPOSES), default=None, help=PURPOSE_HELP
|
| 205 |
+
)
|
| 206 |
+
@click.option(
|
| 207 |
+
"--retry-of",
|
| 208 |
+
"retry_of",
|
| 209 |
+
default=None,
|
| 210 |
+
metavar="EVALUATION_RUN_ID",
|
| 211 |
+
callback=_ulid_option,
|
| 212 |
+
help=RETRY_OF_HELP,
|
| 213 |
+
)
|
| 214 |
+
@click.pass_context
|
| 215 |
+
def eval_run(
|
| 216 |
+
ctx: click.Context,
|
| 217 |
+
project: str,
|
| 218 |
+
run_id: str,
|
| 219 |
+
no_ingest: bool,
|
| 220 |
+
strict_ingest: bool,
|
| 221 |
+
purpose: str | None,
|
| 222 |
+
retry_of: str | None,
|
| 223 |
+
) -> None:
|
| 224 |
+
"""Run evaluation only on existing inference results (no new inference)."""
|
| 225 |
+
cfg = ctx.obj["config"]
|
| 226 |
+
if cfg.is_remote:
|
| 227 |
+
raise click.ClickException("Eval is local-mode only")
|
| 228 |
+
if purpose is not None and retry_of is not None:
|
| 229 |
+
raise click.UsageError(USAGE_PURPOSE_WITH_RETRY)
|
| 230 |
+
|
| 231 |
+
run_dir, store, run_meta = load_run(cfg, project, run_id)
|
| 232 |
+
results = store.load_existing_results()
|
| 233 |
+
if not results:
|
| 234 |
+
raise click.ClickException(f"No inference results found in {run_dir}/results.jsonl")
|
| 235 |
+
task = run_meta["task"]
|
| 236 |
+
click.secho(f"Evaluating run: {run_id}", fg="blue", bold=True)
|
| 237 |
+
|
| 238 |
+
_, task_config = find_task(cfg, project, task)
|
| 239 |
+
run_meta_module.preflight_judge_config(task_config)
|
| 240 |
+
route = decide_route(cfg, project, run_dir, run_meta, task_config)
|
| 241 |
+
if route.family != FAMILY_JUDGE and (purpose is not None or retry_of is not None):
|
| 242 |
+
raise click.UsageError(_judge_only_message(route))
|
| 243 |
+
if route.target is None:
|
| 244 |
+
click.secho(f" Task: {task}, Samples: {len(results)}", fg="blue")
|
| 245 |
+
_echo_route(route)
|
| 246 |
+
# ์ด์ ํ๊ฐ๋ฅผ ๋ฏธ๋ฆฌ ์ง์ฐ์ง ์๋๋ค(F3) -- ์ฌํ๊ฐ๊ฐ ์คํจํด๋ ์ ์ ์๋ ๋จ๋๋ค.
|
| 247 |
+
aggregated = asyncio.run(_evaluate_run(store, run_id, run_meta, task_config, results))
|
| 248 |
+
_report(aggregated, run_dir)
|
| 249 |
+
return
|
| 250 |
+
# ์ ๊ฒฝ๋ก์ ์ฑ์ ์
๋ ฅ์ ์ฑํ๋ run ๋ฒ๋ค ์ฌ๋ณธ์ด๋ค โ ์๋ณธ ํ ์(์ค๋ณต ํฌํจ)๋ ์ฐ์ง ์๊ณ ์ฑ์ ๋ค ๋ณด๊ณ ์ ์ฃ๋๋ค(๋ฆฌ๋ทฐ ์ง์ 4).
|
| 251 |
+
click.secho(f" Task: {task}, Samples: ์ฑํ๋ run ๋ฒ๋ค ๊ธฐ์ค(์๋ ๋ณด๊ณ )", fg="blue")
|
| 252 |
+
strict = strict_requested(strict_ingest)
|
| 253 |
+
judge_options = (purpose, retry_of)
|
| 254 |
+
scored, code = _publish(route, project, run_dir, task_config, no_ingest, strict, judge_options)
|
| 255 |
+
if scored is not None:
|
| 256 |
+
_report(scored.aggregated, scored.path)
|
| 257 |
+
if code:
|
| 258 |
+
ctx.exit(code)
|
| 259 |
+
|
| 260 |
+
|
| 261 |
+
def _judge_only_message(route: Route) -> str:
|
| 262 |
+
"""judge ์ ์ฉ ํ๋๊ทธ๋ฅผ ๋ค๋ฅธ ๊ฒฝ๋ก์์ ์ด ์ฌ์ฉ ์ค๋ฅ โ ๊ฒฝ๋ก ์๋ด(๋ฏธ์๋ฃ statusยท๋์ ์๋ ๋ฑ)๊ฐ ์์ผ๋ฉด ์ด์ ๋ก ๋ถ์ธ๋ค."""
|
| 263 |
+
if route.notice is None:
|
| 264 |
+
return USAGE_JUDGE_ONLY
|
| 265 |
+
return USAGE_JUDGE_ONLY + USAGE_NOT_JUDGE_REASON.format(notice=route.notice)
|
| 266 |
+
|
| 267 |
+
|
| 268 |
+
def _echo_route(route: Route) -> None:
|
| 269 |
+
"""์ ๊ฒฝ๋ก ์๋ด ํ ์ค(standalone ์ ์๋ค)."""
|
| 270 |
+
if route.notice is not None:
|
| 271 |
+
click.secho(f" โ {route.notice}", fg="blue")
|
| 272 |
+
|
| 273 |
+
|
| 274 |
+
def _publish(
|
| 275 |
+
route: Route,
|
| 276 |
+
project: str,
|
| 277 |
+
run_dir: Path,
|
| 278 |
+
task_config: dict[str, Any],
|
| 279 |
+
no_ingest: bool,
|
| 280 |
+
strict: bool,
|
| 281 |
+
judge_options: tuple[str | None, str | None] = (None, None),
|
| 282 |
+
) -> tuple[Scored | None, int]:
|
| 283 |
+
"""์ ๊ฒฝ๋ก(`scoring_publish.publish_run_eval`) โ (์ฑ์ , ์ข
๋ฃ ์ฝ๋). ์ฑ์ ์ ๋ชป ํ์ผ๋ฉด ClickException(์ข
๋ฃ 1).
|
| 284 |
+
|
| 285 |
+
`judge_options` = (`--purpose`, `--retry-of`) โ judge ๊ฒฝ๋ก๋ง ์ด๋ค. judge ๊ฐ์ง๋ `_judge_outcome` ์ด ์ข
๋ฃ ์ฝ๋๋ฅผ ์ ํ๋ค
|
| 286 |
+
(retry ๋์ ์์์ด๋ฉด ์ฑ์ ์์ด ์ข
๋ฃ 0 โ ๊ทธ๋ ์ฑ์ ์ None).
|
| 287 |
+
"""
|
| 288 |
+
evaluator_config = task_config.get("evaluator")
|
| 289 |
+
evaluator = _make_evaluator(evaluator_config) # ์๋ชป๋ ์ค์ ์ ์ ๊ธยท๋ฐฑํ ์ ์ ๋ฉ์ถ๋ค
|
| 290 |
+
_, judge_provider = run_meta_module.make_providers(evaluator_config)
|
| 291 |
+
purpose, retry_of = judge_options
|
| 292 |
+
job = EvalJob(
|
| 293 |
+
project=project,
|
| 294 |
+
run_dir_name=run_dir.name,
|
| 295 |
+
evaluator=evaluator,
|
| 296 |
+
evaluator_config=evaluator_config,
|
| 297 |
+
judge_provider=judge_provider,
|
| 298 |
+
score_all=_score_all,
|
| 299 |
+
no_ingest=no_ingest,
|
| 300 |
+
strict=strict,
|
| 301 |
+
family=route.family,
|
| 302 |
+
purpose=purpose,
|
| 303 |
+
retry_of=retry_of,
|
| 304 |
+
)
|
| 305 |
+
outcome = publish_run_eval(route.target, job)
|
| 306 |
+
if route.family == FAMILY_JUDGE:
|
| 307 |
+
return _judge_outcome(outcome, strict)
|
| 308 |
+
if outcome.scored is None:
|
| 309 |
+
raise click.ClickException(
|
| 310 |
+
"์ฑ์ ํ์ง ์์๋ค โ ์ ์์ฝ์ ์คํจ ๋ค๋ฆฌ์ ๋์ด๋ฆฌ๊ธฐ๋ฅผ ๋ณธ๋ค(run ๋๋ ํ ๋ฆฌ๋ ๊ทธ๋๋ก๋ค)"
|
| 311 |
+
)
|
| 312 |
+
return outcome.scored, exit_code_for(outcome.result, strict)
|
| 313 |
+
|
| 314 |
+
|
| 315 |
+
def _judge_outcome(outcome: PublishOutcome, strict: bool) -> tuple[Scored | None, int]:
|
| 316 |
+
"""judge ๊ฐ์ง์ ์ข
๋ฃ ์ฝ๋(ยง2.5 ยท ๊ฒํ F8). ์ฑ์ ํ์ผ๋ฉด L6b ๊ท์น(0, strict ์ด๊ณ ์คํจ ๋ค๋ฆฌ๋ฉด 3).
|
| 317 |
+
|
| 318 |
+
์ฑ์ ํ์ง ๋ชปํ์ผ๋ฉด: retry ๋์ ์์ โ ์๋ด ํ ์ค ๋ค (None, 0), ๊ทธ ๋ฐ(๋ฐํ ์ฌ์ ๊ฑฐ๋ถยท๋ฒ๋ค ์์ยท์ ๊ธ ๋ฑ) โ judge ์ ์ฉ ๋ฌธ๊ตฌ๋ก
|
| 319 |
+
์ข
๋ฃ 1. purposeยทretry ํ๋๊ทธ๋ ๊ทธ๊ฒ์ด ํธ๋ ์คํจ(`primary_exists`)์๋ง ๊ถํ๋ค โ ๋ฒ๋ค ์์ยท์ ๊ธ ์คํจ์๋ ์์ฝ์ ์๋ด๋ฅผ ๊ฐ๋ฆฌํจ๋ค.
|
| 320 |
+
"""
|
| 321 |
+
if outcome.scored is not None:
|
| 322 |
+
return outcome.scored, exit_code_for(outcome.result, strict)
|
| 323 |
+
leg = outcome.result.legs.get(LEG_PUBLISH)
|
| 324 |
+
code = leg.code if leg is not None else None
|
| 325 |
+
if leg is not None and (leg.status, code) == (
|
| 326 |
+
STATUS_SKIPPED,
|
| 327 |
+
judge_publish.RETRY_NOTHING_TO_DO,
|
| 328 |
+
):
|
| 329 |
+
click.secho(f" {JUDGE_RETRY_NOTHING}", fg="blue")
|
| 330 |
+
return None, 0
|
| 331 |
+
raise click.ClickException(_judge_not_scored_message(code))
|
| 332 |
+
|
| 333 |
+
|
| 334 |
+
def _judge_not_scored_message(code: str | None) -> str:
|
| 335 |
+
"""๋ฐํ ๋ค๋ฆฌ ์ฝ๋ โ ์ข
๋ฃ 1 ๋ฌธ๊ตฌ. ์ฝ๋ ๋ฌธ์์ด์ `scoring_publish.judge`ยท`hub_hook.types` ์ ์์๋ง ์ด๋ค."""
|
| 336 |
+
if code == f"{judge_publish.PUBLISH_JUDGE_PREFIX}{judge_publish.JUDGE_PRIMARY_EXISTS}":
|
| 337 |
+
return JUDGE_NOT_SCORED_PRIMARY_EXISTS
|
| 338 |
+
if isinstance(code, str) and code.startswith(PUBLISH_NO_BUNDLE_PREFIX):
|
| 339 |
+
return JUDGE_NOT_SCORED_NO_BUNDLE
|
| 340 |
+
return JUDGE_NOT_SCORED
|
solar_eval/cli/commands/run_meta.py
ADDED
|
@@ -0,0 +1,538 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""run ์์ยทresume ์ด ๊ณต์ ํ๋ ์ค๋น ๋จ๊ณ์ run.json ๋ฉํ ์กฐ๋ฆฝ (๊ณํ 2026-09-24-phase1a D3ยทD5ยทD12ยทD17ยทD19).
|
| 2 |
+
|
| 3 |
+
`runs start` ๋ run ๋๋ ํ ๋ฆฌ๋ฅผ ๋ง๋ค๊ธฐ **์ ์** ์ด ๋ชจ๋๋ก ๋ชจ๋ ๊ฒ์ ์ ํ๋ค:
|
| 4 |
+
ํ์คํฌยทํ๋กฌํํธยทํ์ดํ๋ผ์ธ ํด์ โ ํ ์ ํ(`where`ยท`limit`) โ ๋ฐ์ดํฐ์
ํ ๋ฒ ์ฝ๊ธฐ โ ํ์ดํ๋ผ์ธ ์์ฑ โ
|
| 5 |
+
์ค๋
์ท(run.json `dataset`ยท`snapshot` ๋ธ๋ก, ์๊ธฐ ๊ฒ์ฆ๊น์ง). ๊ทธ๋์ 0ํ ์ ํยท๋ผ๋ฒจ ๋๋ฝยท์๋ ์ฌ์ ยท๊นจ์ง lock
|
| 6 |
+
๊ฐ์ ์ค๋ฅ๊ฐ ๋น run ๋๋ ํ ๋ฆฌ๋ฅผ ๋จ๊ธฐ์ง ์๊ณ , run.json ์ด ์ ์ sha256ยท์ค๋
์ท๊ณผ ๋ฌ๋๊ฐ ์ค์ ๋ก ๋๋
|
| 7 |
+
๋ฐ์ดํฐยทํ์ดํ๋ผ์ธ์ด **๊ฐ์ ๊ฐ์ฒด**์์ ๋์จ๋ค.
|
| 8 |
+
|
| 9 |
+
resume ์ ๊ฐ์ ์กฐ๋ฆฝ ํจ์(`build_snapshot`)๋ก ์ง๊ธ ๋์คํฌ ๊ธฐ์ค ์ค๋
์ท์ ๋ค์ ๋ ์ค์ ๋๋ฆฌํํธ๋ฅผ ๋ง๋๋ค(D17).
|
| 10 |
+
|
| 11 |
+
`poc_bundle` import ๋ ์ด ๋ชจ๋(๊ณผ `run_resume`ยท`run_eval`)๊ณผ `core/snapshot/**` ์์๋ง ํ๋ค(D18).
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
+
from __future__ import annotations
|
| 15 |
+
|
| 16 |
+
import subprocess
|
| 17 |
+
from collections.abc import Callable, Mapping, Sequence
|
| 18 |
+
from dataclasses import dataclass
|
| 19 |
+
from pathlib import Path
|
| 20 |
+
from typing import Any
|
| 21 |
+
|
| 22 |
+
import click
|
| 23 |
+
from poc_bundle.data.errors import DataError
|
| 24 |
+
|
| 25 |
+
from solar_eval.core.dataset_loader import DatasetLoader, LoadedJsonl
|
| 26 |
+
from solar_eval.core.dataset_prep import (
|
| 27 |
+
PreparedDataset,
|
| 28 |
+
build_selection,
|
| 29 |
+
prepare_dataset,
|
| 30 |
+
reject_defaults_where,
|
| 31 |
+
)
|
| 32 |
+
from solar_eval.core.pipeline_factory import build_task_pipeline
|
| 33 |
+
from solar_eval.core.row_filter import RowSelection, parse_cli_where
|
| 34 |
+
from solar_eval.core.snapshot import EngineSnapshotInputs, build_engine_snapshot, self_check
|
| 35 |
+
from solar_eval.core.snapshot import environment
|
| 36 |
+
from solar_eval.core.snapshot.dataset_block import describe_dataset
|
| 37 |
+
from solar_eval.evaluators.judge_config import JudgeConfigError, validate_judge_config
|
| 38 |
+
|
| 39 |
+
#: run ์ ์์ํ๊ธฐ ์ ์ ์ฌ๋์ด ์ฝ์ ๋ฉ์์ง๋ก ๋ฐ๊ฟ ๋ฉ์ถ๋ ์ค๋ฅ โ ํ ์ ํยทmeta_fieldsยทdefaults.where
|
| 40 |
+
#: (ValueError ๊ณ์ด), ์๋ ๋ฐ์ดํฐ์
ยท์ง์ ์์ฐ(FileNotFoundError ๊ณ์ด), ๊นจ์ง datasets.lock(DataError).
|
| 41 |
+
PREP_ERRORS: tuple[type[Exception], ...] = (ValueError, FileNotFoundError, DataError)
|
| 42 |
+
|
| 43 |
+
#: ์ง๋ฌธ์ ์ฌ๋์ด ๋์ผ๋ก ๋์กฐํ ๋ ๋ณด์ฌ ์ฃผ๋ ์์๋ฆฌ ์ โ legacy `prompt_sha` ํ์๊ณผ ๋ง์ถ๋ค.
|
| 44 |
+
FINGERPRINT_DISPLAY_CHARS = 12
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def resolve_prompt(prompts_dir: Path, prompt_name: str) -> Path:
|
| 48 |
+
"""Resolve prompt from prompts/ directory (directory or YAML file).
|
| 49 |
+
|
| 50 |
+
Resolution order:
|
| 51 |
+
1. prompts/{name}/ (directory TXT format)
|
| 52 |
+
2. prompts/{name}.yaml (flat YAML layout)
|
| 53 |
+
3. prompts/_library/{name}.yaml (legacy layout)
|
| 54 |
+
"""
|
| 55 |
+
dir_path = prompts_dir / prompt_name
|
| 56 |
+
if dir_path.is_dir():
|
| 57 |
+
return dir_path
|
| 58 |
+
yaml_path = prompts_dir / f"{prompt_name}.yaml"
|
| 59 |
+
if yaml_path.exists():
|
| 60 |
+
return yaml_path
|
| 61 |
+
legacy_path = prompts_dir / "_library" / f"{prompt_name}.yaml"
|
| 62 |
+
if legacy_path.exists():
|
| 63 |
+
return legacy_path
|
| 64 |
+
raise click.ClickException(
|
| 65 |
+
f"Prompt '{prompt_name}' not found. Searched:\n"
|
| 66 |
+
f" {dir_path}/ (directory)\n"
|
| 67 |
+
f" {yaml_path} (YAML)\n"
|
| 68 |
+
f" {legacy_path} (legacy)"
|
| 69 |
+
)
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def resolve_pipeline_file(project_dir: Path, pipeline_name: str) -> Path:
|
| 73 |
+
"""Resolve pipeline YAML from pipelines/ directory."""
|
| 74 |
+
path = project_dir / "pipelines" / f"{pipeline_name}.yaml"
|
| 75 |
+
if not path.exists():
|
| 76 |
+
raise click.ClickException(f"Pipeline '{pipeline_name}' not found: {path}")
|
| 77 |
+
return path
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def compose_pipeline_config(project_dir: Path, pipeline_name: str, overrides: dict) -> dict:
|
| 81 |
+
"""ํ์ดํ๋ผ์ธ์ `extends`/override ๊น์ง ํด์ํด ๋๋ ค์ค๋ค.
|
| 82 |
+
|
| 83 |
+
์กฐ๋ฆฝ ์คํจ(์๋ base, ์๋ ์คํ
์ ๊ฐ๋ฆฌํค๋ override ๋ฑ)๋ run ์ ์์ํ๊ธฐ ์ ์
|
| 84 |
+
์ฃฝ์ธ๋ค โ ํจ๊ณผ ์๋ override ๋ก ๋์๊ฐ run ์ ๊ฒฐ๋ก ์ ์กฐ์ฉํ ์ค์ผ์ํจ๋ค.
|
| 85 |
+
"""
|
| 86 |
+
from solar_eval.core.pipeline_compose import PipelineCompositionError, compose_pipeline
|
| 87 |
+
from solar_eval.core.project_loader import load_pipeline_file
|
| 88 |
+
|
| 89 |
+
pipelines_dir = resolve_pipeline_file(project_dir, pipeline_name).parent
|
| 90 |
+
try:
|
| 91 |
+
composed = load_pipeline_file(pipelines_dir, pipeline_name)
|
| 92 |
+
if overrides:
|
| 93 |
+
composed = compose_pipeline(composed, overrides=overrides)
|
| 94 |
+
return composed
|
| 95 |
+
except (PipelineCompositionError, FileNotFoundError) as e:
|
| 96 |
+
raise click.ClickException(str(e)) from None
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def config_source(cfg, project: str) -> str:
|
| 100 |
+
"""run ์ config ์ถ์ฒ โ ๋ ํฌ ๊ด๋ฆฌ ํ๋ก์ ํธ๋ฉด ๋ ํฌ ์๋ ๊ฒฝ๋ก, ์๋๋ฉด ๋ฐ์ดํฐ ํ ์ ๋ ๊ฒฝ๋ก."""
|
| 101 |
+
config_dir = cfg.config_dir(project)
|
| 102 |
+
if cfg.is_repo_managed(project) and cfg.repo_root is not None:
|
| 103 |
+
return str(config_dir.relative_to(cfg.repo_root))
|
| 104 |
+
return str(config_dir)
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def git_commit(repo_root: Path | None) -> str | None:
|
| 108 |
+
"""์ฌํ์ฉ ๋์ฅ: ๋ ํฌ HEAD ์ปค๋ฐ. ์์
ํธ๋ฆฌ๊ฐ ๋๋ฌ์ฐ๋ฉด '-dirty' ๋ฅผ ๋ถ์ธ๋ค (๋ ํฌ ์ ์ฒด โ D8 ๋ถ๋ณ)."""
|
| 109 |
+
if repo_root is None:
|
| 110 |
+
return None
|
| 111 |
+
try:
|
| 112 |
+
head = subprocess.run(
|
| 113 |
+
["git", "-C", str(repo_root), "rev-parse", "--short", "HEAD"],
|
| 114 |
+
capture_output=True,
|
| 115 |
+
text=True,
|
| 116 |
+
check=True,
|
| 117 |
+
).stdout.strip()
|
| 118 |
+
status = subprocess.run(
|
| 119 |
+
["git", "-C", str(repo_root), "status", "--porcelain"],
|
| 120 |
+
capture_output=True,
|
| 121 |
+
text=True,
|
| 122 |
+
check=True,
|
| 123 |
+
).stdout.strip()
|
| 124 |
+
return f"{head}-dirty" if status else head
|
| 125 |
+
except (OSError, subprocess.CalledProcessError):
|
| 126 |
+
return None
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
def find_task(cfg, project: str, task: str) -> tuple[dict[str, Any], dict[str, Any]]:
|
| 130 |
+
"""๋ก๋๋ ํ๋ก์ ํธ ์ค์ ๊ณผ ํ์คํฌ ์ค์ . ์์ผ๋ฉด ClickException."""
|
| 131 |
+
from solar_eval.core.project_loader import load_all_project_configs
|
| 132 |
+
|
| 133 |
+
configs = load_all_project_configs(cfg.projects_dir, cfg.config_dirs)
|
| 134 |
+
project_config = next((c for c in configs if c["name"] == project), None)
|
| 135 |
+
if not project_config:
|
| 136 |
+
raise click.ClickException(f"Project not found in {cfg.projects_dir}: {project}")
|
| 137 |
+
task_config = next((t for t in project_config.get("tasks", []) if t["name"] == task), None)
|
| 138 |
+
if not task_config:
|
| 139 |
+
raise click.ClickException(f"Task '{task}' not found in project '{project}'")
|
| 140 |
+
return project_config, task_config
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
def load_run(cfg, project: str, run_id: str) -> tuple[Path, Any, dict[str, Any]]:
|
| 144 |
+
"""(run ๋๋ ํ ๋ฆฌ, ์คํ ์ด, run.json). ์์ผ๋ฉด ClickException."""
|
| 145 |
+
from solar_eval.stores import JsonlStore
|
| 146 |
+
|
| 147 |
+
run_dir = cfg.artifacts_dir(project) / run_id
|
| 148 |
+
if not run_dir.exists():
|
| 149 |
+
raise click.ClickException(f"Run directory not found: {run_dir}")
|
| 150 |
+
store = JsonlStore(run_dir)
|
| 151 |
+
run_meta = store.load_run_meta()
|
| 152 |
+
if not run_meta:
|
| 153 |
+
raise click.ClickException(f"No run.json found in {run_dir}")
|
| 154 |
+
if not run_meta.get("task"):
|
| 155 |
+
raise click.ClickException("Cannot determine task from run.json")
|
| 156 |
+
return run_dir, store, run_meta
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
def load_prompt(
|
| 160 |
+
prompt_path: Path,
|
| 161 |
+
model: str,
|
| 162 |
+
temperature: float,
|
| 163 |
+
max_tokens: int,
|
| 164 |
+
reasoning_effort: str | None,
|
| 165 |
+
) -> tuple[dict[str, Any], str]:
|
| 166 |
+
"""ํ๋กฌํํธ ํ์ผ์ ๋ฌ๋๊ฐ ๋ฐ๋ dict ๋ก โ (prompt, ํฌ๋งท `multi_step`|`single_step`)."""
|
| 167 |
+
from solar_eval.models.prompt_version import (
|
| 168 |
+
detect_prompt_format,
|
| 169 |
+
load_prompt_messages,
|
| 170 |
+
load_step_prompts,
|
| 171 |
+
)
|
| 172 |
+
|
| 173 |
+
fmt = detect_prompt_format(prompt_path)
|
| 174 |
+
if fmt == "multi_step":
|
| 175 |
+
prompt: dict[str, Any] = {"step_prompts": load_step_prompts(prompt_path)}
|
| 176 |
+
else:
|
| 177 |
+
prompt = {"messages": load_prompt_messages(prompt_path)}
|
| 178 |
+
prompt.update({"model": model, "temperature": temperature, "max_tokens": max_tokens})
|
| 179 |
+
if reasoning_effort:
|
| 180 |
+
prompt["reasoning_effort"] = reasoning_effort
|
| 181 |
+
return prompt, fmt
|
| 182 |
+
|
| 183 |
+
|
| 184 |
+
def make_providers(evaluator_config: Mapping[str, Any] | None = None) -> tuple[Any, Any]:
|
| 185 |
+
"""(์ถ๋ก ํ๋ก๋ฐ์ด๋, judge ํ๋ก๋ฐ์ด๋). ํ
์คํธ๋ ์ด ํจ์๋ฅผ ๋ฐ๊ฟ ๋ผ์ ๋คํธ์ํฌ ์์ด ๋๋ค.
|
| 186 |
+
|
| 187 |
+
`evaluator_config` ๋ ํ์คํฌ์ ํ๊ฐ๊ธฐ ์ค์ (ํค๊ฐ ์์ผ๋ฉด None) โ judge ์ธ์คํด์ค๋ฅผ ์ ์ธ(`judge_provider`)์ผ๋ก ๊ณ ๋ฅด๋
|
| 188 |
+
์๋ฆฌ๋ค(๊ณํ 2026-09-28 L12 ยง2.8 ยท D-L12-10). ์ ์ธ์ด ์๊ฑฐ๋ ํ์ ์๋ ํ๊ฐ๊ธฐ๋ ์ค๋์ฒ๋ผ `OpenAIProvider()` โ
|
| 189 |
+
run.json ์ค๋
์ท `provenance.providers.judge` ๋ฐ์ดํธ๊ฐ ๋ฐ๋์ง ์๋๋ค. ์ถ๋ก ํ๋ก๋ฐ์ด๋๋ ๋ `UpstageProvider()`.
|
| 190 |
+
|
| 191 |
+
Raises:
|
| 192 |
+
JudgeConfigError: ๋ชจ๋ฅด๋ providerยทํฉ์ฑ ํ๊ฐ๊ธฐ ์์ provider ์ถฉ๋.
|
| 193 |
+
"""
|
| 194 |
+
from solar_eval.evaluators.judge_config import (
|
| 195 |
+
PROVIDER_OPENAI,
|
| 196 |
+
PROVIDER_UPSTAGE,
|
| 197 |
+
judge_provider_name,
|
| 198 |
+
)
|
| 199 |
+
from solar_eval.providers import UpstageProvider
|
| 200 |
+
from solar_eval.providers.openai_provider import OpenAIProvider
|
| 201 |
+
|
| 202 |
+
judge_classes = {PROVIDER_UPSTAGE: UpstageProvider, PROVIDER_OPENAI: OpenAIProvider}
|
| 203 |
+
name = judge_provider_name(evaluator_config) or PROVIDER_OPENAI
|
| 204 |
+
return UpstageProvider(), judge_classes[name]()
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
def route_inference_provider(provider: Any, step_models: Sequence[str]) -> Any:
|
| 208 |
+
"""์คํ
๋ชจ๋ธ์ ๋ผ์ฐํธ ์ ๋์ด(`timely:<๋ชจ๋ธ>`)๊ฐ ํ๋๋ผ๋ ์์ผ๋ฉด ์ถ๋ก ์ ๊ณต์๋ฅผ `RoutedProvider` ๋ก ๊ฐ์ผ๋ค.
|
| 209 |
+
|
| 210 |
+
์์ผ๋ฉด ๊ทธ๋๋ก ๋๋ ค์ค๋ค โ ๋ผ์ฐํธ ์๋ run ์ provenance(`providers.inference` ๋ชจ๋)๊ฐ ์์ ๋ฐ์ดํธ ๊ทธ๋๋ก์ฌ์ผ ํ๋ค.
|
| 211 |
+
์ธ๋ถ ๋ผ์ฐํธ๋ฅผ ์จ๋ ๋๋๊ฐ๋ ์์ง์ด ์๋๋ผ case ์ ๋ฐ์ดํฐ ์ ์ฑ
(๊ณ์ฝ)์ด ์ ํ๋ค โ 2026-10 judge egress ์ ์ฑ
ํ์ง(#353)์ ๊ฐ์ ์๋ฆฌ.
|
| 212 |
+
"""
|
| 213 |
+
from solar_eval.providers import INFERENCE_ROUTES, RoutedProvider
|
| 214 |
+
from solar_eval.providers.routed import route_prefixes
|
| 215 |
+
|
| 216 |
+
if not route_prefixes(list(step_models), INFERENCE_ROUTES):
|
| 217 |
+
return provider
|
| 218 |
+
return RoutedProvider(provider, {k: cls() for k, cls in INFERENCE_ROUTES.items()})
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
@dataclass(frozen=True)
|
| 222 |
+
class RunPlan:
|
| 223 |
+
"""run ํ๋๏ฟฝ๏ฟฝ๏ฟฝ ๋ฌด์์ผ๋ก ๋๋๊ฐ โ run ๋๋ ํ ๋ฆฌ๋ฅผ ๋ง๋ค๊ธฐ ์ ์ ์ ๋ถ ์ ํด์ง๋ค."""
|
| 224 |
+
|
| 225 |
+
project: str
|
| 226 |
+
task: str
|
| 227 |
+
project_config: dict[str, Any]
|
| 228 |
+
task_config: dict[str, Any]
|
| 229 |
+
prompt_name: str
|
| 230 |
+
prompt_path: Path
|
| 231 |
+
prompt_format: str
|
| 232 |
+
prompt: dict[str, Any]
|
| 233 |
+
model: str
|
| 234 |
+
temperature: float
|
| 235 |
+
max_tokens: int
|
| 236 |
+
reasoning_effort: str | None
|
| 237 |
+
pipeline_name: str
|
| 238 |
+
pipeline_config: dict[str, Any]
|
| 239 |
+
pipeline_overrides: dict[str, Any] | None
|
| 240 |
+
max_workers: int
|
| 241 |
+
dataset_loader: DatasetLoader
|
| 242 |
+
prepared: PreparedDataset
|
| 243 |
+
pipeline: Any
|
| 244 |
+
provider: Any
|
| 245 |
+
judge_provider: Any
|
| 246 |
+
|
| 247 |
+
@property
|
| 248 |
+
def selection(self) -> RowSelection | None:
|
| 249 |
+
return self.prepared.selection
|
| 250 |
+
|
| 251 |
+
|
| 252 |
+
def resolve_pipeline(
|
| 253 |
+
cfg, project: str, task_config: dict[str, Any], pipeline_name: str | None, overrides: dict
|
| 254 |
+
) -> tuple[dict[str, Any], str, dict[str, Any]]:
|
| 255 |
+
"""(ํ์ดํ๋ผ์ธ์ ์น์ task_config, ์คํจ ํ์ดํ๋ผ์ธ ์ด๋ฆ, ํด์๋ ํ์ดํ๋ผ์ธ dict).
|
| 256 |
+
|
| 257 |
+
`-P` ๊ฐ ์์ผ๋ฉด config ์ ๋ณธ์ pipelines/ ์์ `extends`ยท`--set` ๊น์ง ํด์ํ๊ณ , ์์ผ๋ฉด
|
| 258 |
+
project.yaml ๊ธฐ๋ณธ๊ฐ(project_loader ๊ฐ ์ด๋ฏธ ํด์)์ ์ด๋ค. config ๋ ๋ ํฌ ๊ด๋ฆฌ ํ๋ก์ ํธ๋ฉด git
|
| 259 |
+
์ชฝ์ด ์ ๋ณธ์ด๋ค. `extends` ์์๊ณผ `--set` override ๋ ์ฌ๊ธฐ์ ์ ๋ถ ํด์๋ผ, ์คํ ์์ง์๋ ์์ฑ๋
|
| 260 |
+
steps ๋ชฉ๋ก๋ง ๋์ด๊ฐ๋ค.
|
| 261 |
+
"""
|
| 262 |
+
if pipeline_name:
|
| 263 |
+
pipeline_config = compose_pipeline_config(cfg.config_dir(project), pipeline_name, overrides)
|
| 264 |
+
return {**task_config, "pipeline_config": pipeline_config}, pipeline_name, pipeline_config
|
| 265 |
+
if overrides:
|
| 266 |
+
raise click.ClickException("--set requires an explicit --pipeline/-P")
|
| 267 |
+
pc = task_config.get("pipeline_config", {})
|
| 268 |
+
effective = pc.get("name", "default") if isinstance(pc, dict) else str(pc)
|
| 269 |
+
return task_config, effective, pc if isinstance(pc, dict) else {}
|
| 270 |
+
|
| 271 |
+
|
| 272 |
+
class _CheckedReader:
|
| 273 |
+
"""`prepare_dataset` ์ ๋๊ธฐ๋ ์์ ๋๋ฆฌ์ โ ํ์ผ์ ์ฝ์ **์งํ**, ํ์ ๊ณ ๋ฅด๊ธฐ **์ ์** `on_read` ๋ฅผ ๋ถ๋ฅธ๋ค.
|
| 274 |
+
|
| 275 |
+
resume ์ ๋ฐ์ดํฐ์
sha ๊ฐ๋(D17 ์์์ ์ฒซ์งธ)๊ฐ ์ ํยท๋ผ๋ฒจ ๊ฒ์ฌ๋ณด๋ค ๋จผ์ ๊ฑธ๋ฆฌ๊ฒ ํ๋ ค๋ ๊ฒ์ด๋ค โ
|
| 276 |
+
๋ฐ์ดํฐ์
์ด ๋ฐ๋์ด ์ ํ์ด 0ํ์ด ๋๋ฉด ์์ธ์ "where ๊ฐ 0ํ" ์ด ์๋๋ผ "๋ฐ์ดํฐ์
์ด ๋ฐ๋์๋ค" ๋ค.
|
| 277 |
+
๋ฐ์ดํธ๋ ์ฌ์ ํ ํ ๋ฒ๋ง ์ฝ๋๋ค(D16).
|
| 278 |
+
"""
|
| 279 |
+
|
| 280 |
+
def __init__(self, loader: DatasetLoader, on_read: Callable[[LoadedJsonl], None]) -> None:
|
| 281 |
+
self._loader = loader
|
| 282 |
+
self._on_read = on_read
|
| 283 |
+
|
| 284 |
+
def read_jsonl(self, repo: str, path: str, source: str) -> LoadedJsonl:
|
| 285 |
+
loaded = self._loader.read_jsonl(repo, path, source=source)
|
| 286 |
+
self._on_read(loaded)
|
| 287 |
+
return loaded
|
| 288 |
+
|
| 289 |
+
|
| 290 |
+
def _step_models(pipeline: Any) -> list[str]:
|
| 291 |
+
"""ํ์ดํ๋ผ์ธ ์คํ
๋ค์ด ๋ช
์ํ ๋ชจ๋ธ ๋ฌธ์์ด โ ๋ผ์ฐํธ ์ ๋์ด ๊ฒ์ฌ์ฉ(์๋ ์คํ
์ run ์ `-m` ๋ชจ๋ธ = Upstage)."""
|
| 292 |
+
return [
|
| 293 |
+
str(step.config["model"])
|
| 294 |
+
for step in getattr(pipeline, "steps", [])
|
| 295 |
+
if isinstance(getattr(step, "config", None), dict) and step.config.get("model")
|
| 296 |
+
]
|
| 297 |
+
|
| 298 |
+
|
| 299 |
+
def assemble_plan(
|
| 300 |
+
cfg,
|
| 301 |
+
selection: RowSelection | None,
|
| 302 |
+
*,
|
| 303 |
+
on_read: Callable[[LoadedJsonl], None] | None = None,
|
| 304 |
+
**fields: Any,
|
| 305 |
+
) -> RunPlan:
|
| 306 |
+
"""๋ฐ์ดํฐ์
์ ํ ๋ฒ ์ฝ์ด ๊ณ ๋ฅด๊ณ , ๋ฌ๋๊ฐ ๋ ํ์ดํ๋ผ์ธ ์ธ์คํด์ค๋ฅผ ๋ง๋ ๋ค(D6ยทD16).
|
| 307 |
+
|
| 308 |
+
`fields` ๋ `RunPlan` ์ ๋๋จธ์ง ํ๋(projectยทtask_configยทprompt ๋ฑ)๋ค. `on_read` ๋ ๋ฐ์ดํฐ์
์
|
| 309 |
+
์ฝ์ ์งํยท์ ํ ์ ์ ๋ถ๋ฆฐ๋ค(resume ์ sha ๊ฐ๋ โ ๊ฑฐ๊ธฐ์ ๋ ClickException ์ ๊ทธ๋๋ก ์ฌ๋ผ๊ฐ๋ค).
|
| 310 |
+
|
| 311 |
+
judge ์ค์ ์ค๋ฅ๋ ๋ฐ์ดํฐ์
์ฝ๊ธฐยทํ์ดํ๋ผ์ธยทprovider ์์ฑ๋ณด๋ค ๋จผ์ ํ์ธํ๋ค.
|
| 312 |
+
`runs start`ยท`quick-run`ยท`runs batch`(ํ๋กฌํํธ๋ง๋ค)ยท`runs resume` ์ด ๋ชจ๋ ์ด ์๋ฆฌ๋ฅผ ์ง๋๋ค.
|
| 313 |
+
|
| 314 |
+
Raises:
|
| 315 |
+
click.ClickException: judge ์ค์ ยท์ ํยทmeta_fieldsยท๋ฐ์ดํฐ์
ยทํ์ดํ๋ผ์ธ ์์ฑ ์ค๋ฅ(run ์์ ์ ).
|
| 316 |
+
"""
|
| 317 |
+
preflight_judge_config(fields["task_config"])
|
| 318 |
+
loader = DatasetLoader(base_dir=cfg.projects_dir)
|
| 319 |
+
reader: Any = loader if on_read is None else _CheckedReader(loader, on_read)
|
| 320 |
+
try:
|
| 321 |
+
prepared = prepare_dataset(
|
| 322 |
+
reader, fields["project_config"], fields["task_config"], selection
|
| 323 |
+
)
|
| 324 |
+
pipeline = build_task_pipeline(
|
| 325 |
+
fields["project_config"], fields["task_config"], fields["prompt"], loader
|
| 326 |
+
)
|
| 327 |
+
except PREP_ERRORS as e:
|
| 328 |
+
raise click.ClickException(str(e)) from None
|
| 329 |
+
provider, judge_provider = make_providers(fields["task_config"].get("evaluator"))
|
| 330 |
+
provider = route_inference_provider(provider, _step_models(pipeline))
|
| 331 |
+
return RunPlan(
|
| 332 |
+
dataset_loader=loader,
|
| 333 |
+
prepared=prepared,
|
| 334 |
+
pipeline=pipeline,
|
| 335 |
+
provider=provider,
|
| 336 |
+
judge_provider=judge_provider,
|
| 337 |
+
**fields,
|
| 338 |
+
)
|
| 339 |
+
|
| 340 |
+
|
| 341 |
+
def preflight_judge_config(task_config: Mapping[str, Any]) -> None:
|
| 342 |
+
"""Judge ์ค์ ์ค๋ฅ๋ฅผ CLI ์ค๋ฅ๋ก ๋ฐ๊พธ๋ provider ๋ ๋ง๋ค์ง ์๋๋ค."""
|
| 343 |
+
try:
|
| 344 |
+
validate_judge_config(task_config)
|
| 345 |
+
except JudgeConfigError as exc:
|
| 346 |
+
raise click.ClickException(str(exc)) from None
|
| 347 |
+
|
| 348 |
+
|
| 349 |
+
def start_options(
|
| 350 |
+
project_config: dict[str, Any],
|
| 351 |
+
task_config: dict[str, Any],
|
| 352 |
+
set_options: tuple[str, ...],
|
| 353 |
+
where: tuple[str, ...],
|
| 354 |
+
limit: int | None,
|
| 355 |
+
) -> tuple[dict[str, Any], RowSelection]:
|
| 356 |
+
"""(`--set` override, ํ ์ ํ) โ ํ๋กฌํํธ์ ๋ฌด๊ดํ ์์ ๊ฒ์ฌ. `runs batch` ๋ ๋ฃจํ ์ ์ ํ ๋ฒ ๋ถ๋ฅธ๋ค.
|
| 357 |
+
|
| 358 |
+
Raises:
|
| 359 |
+
click.ClickException: `defaults.where`ยทwhere ๋ฌธ๋ฒยท`--set` ๋ฌธ๋ฒ ์ค๋ฅ.
|
| 360 |
+
"""
|
| 361 |
+
from solar_eval.core.pipeline_compose import PipelineCompositionError, parse_set_option
|
| 362 |
+
|
| 363 |
+
try:
|
| 364 |
+
reject_defaults_where(project_config)
|
| 365 |
+
overrides = parse_set_option(set_options)
|
| 366 |
+
selection = build_selection(task_config, parse_cli_where(where), limit)
|
| 367 |
+
except (PipelineCompositionError, *PREP_ERRORS) as e:
|
| 368 |
+
raise click.ClickException(str(e)) from None
|
| 369 |
+
return overrides, selection
|
| 370 |
+
|
| 371 |
+
|
| 372 |
+
def plan_run(
|
| 373 |
+
cfg,
|
| 374 |
+
*,
|
| 375 |
+
project: str,
|
| 376 |
+
task: str,
|
| 377 |
+
prompt_name: str,
|
| 378 |
+
model: str,
|
| 379 |
+
pipeline_name: str | None,
|
| 380 |
+
reasoning_effort: str | None,
|
| 381 |
+
temperature: float,
|
| 382 |
+
max_tokens: int,
|
| 383 |
+
max_workers: int,
|
| 384 |
+
limit: int | None,
|
| 385 |
+
set_options: tuple[str, ...] = (),
|
| 386 |
+
where: tuple[str, ...] = (),
|
| 387 |
+
) -> RunPlan:
|
| 388 |
+
"""`runs start` ์ ์ค๋น ๋จ๊ณ ์ ๋ถ โ `reject_defaults_where` โ ์ ํ โ `prepare_dataset` โ
|
| 389 |
+
`build_task_pipeline` (๊ณํ ยง9 G2 ์์). ๋ชจ๋ ์ค๋ฅ๋ run ๋๋ ํ ๋ฆฌ๋ฅผ ๋ง๋ค๊ธฐ ์ ์ ๋๋ค.
|
| 390 |
+
"""
|
| 391 |
+
project_config, task_config = find_task(cfg, project, task)
|
| 392 |
+
overrides, selection = start_options(project_config, task_config, set_options, where, limit)
|
| 393 |
+
prompt_path = resolve_prompt(cfg.prompts_dir(project), prompt_name)
|
| 394 |
+
prompt, fmt = load_prompt(prompt_path, model, temperature, max_tokens, reasoning_effort)
|
| 395 |
+
task_config, effective, pipeline_config = resolve_pipeline(
|
| 396 |
+
cfg, project, task_config, pipeline_name, overrides
|
| 397 |
+
)
|
| 398 |
+
return assemble_plan(
|
| 399 |
+
cfg,
|
| 400 |
+
selection,
|
| 401 |
+
project=project,
|
| 402 |
+
task=task,
|
| 403 |
+
project_config=project_config,
|
| 404 |
+
task_config=task_config,
|
| 405 |
+
prompt_name=prompt_name,
|
| 406 |
+
prompt_path=prompt_path,
|
| 407 |
+
prompt_format=fmt,
|
| 408 |
+
prompt=prompt,
|
| 409 |
+
model=model,
|
| 410 |
+
temperature=temperature,
|
| 411 |
+
max_tokens=max_tokens,
|
| 412 |
+
reasoning_effort=reasoning_effort,
|
| 413 |
+
pipeline_name=effective,
|
| 414 |
+
pipeline_config=pipeline_config,
|
| 415 |
+
pipeline_overrides=overrides or None,
|
| 416 |
+
max_workers=max_workers,
|
| 417 |
+
)
|
| 418 |
+
|
| 419 |
+
|
| 420 |
+
def dataset_block(cfg, plan: RunPlan) -> dict[str, Any]:
|
| 421 |
+
"""run.json `dataset` ๋ธ๋ก โ ์ฝ์ ๋ฐ์ดํธ์ sha256ยทํ ์ยท์ ๊ทํ๋ ์ ํยทlock ๋์กฐ(S6)."""
|
| 422 |
+
prepared = plan.prepared
|
| 423 |
+
selection = plan.selection or RowSelection()
|
| 424 |
+
try:
|
| 425 |
+
return describe_dataset(
|
| 426 |
+
sha256=prepared.sha256,
|
| 427 |
+
rows_total=prepared.rows_total,
|
| 428 |
+
rows_selected=prepared.rows_selected,
|
| 429 |
+
selection=selection.to_json(),
|
| 430 |
+
meta_fields=prepared.meta_fields,
|
| 431 |
+
task_config=plan.task_config,
|
| 432 |
+
loader_cfg=plan.project_config.get("dataset") or {},
|
| 433 |
+
config_dir=cfg.config_dir(plan.project),
|
| 434 |
+
project_name=plan.project,
|
| 435 |
+
repo_root=environment.engine_repo(),
|
| 436 |
+
)
|
| 437 |
+
except PREP_ERRORS as e:
|
| 438 |
+
raise click.ClickException(f"dataset lock check failed: {e}") from None
|
| 439 |
+
|
| 440 |
+
|
| 441 |
+
def build_snapshot(
|
| 442 |
+
cfg,
|
| 443 |
+
plan: RunPlan,
|
| 444 |
+
dataset: dict[str, Any] | None,
|
| 445 |
+
*,
|
| 446 |
+
source: str | None,
|
| 447 |
+
commit: str | None,
|
| 448 |
+
) -> dict[str, Any]:
|
| 449 |
+
"""run.json `snapshot` ๋ธ๋ก(engine-snapshot/v1). start ์ resume ์ด ๊ฐ์ ํจ์๋ฅผ ์ด๋ค(D17).
|
| 450 |
+
|
| 451 |
+
Raises:
|
| 452 |
+
click.ClickException: ์ ์ธํ ์์์ด ์๊ฑฐ๋ ์ค๋
์ท์ด ์๊ธฐ ๊ฒ์ฆ(D5)์ ํต๊ณผํ์ง ๋ชปํ ๋.
|
| 453 |
+
"""
|
| 454 |
+
inputs = EngineSnapshotInputs(
|
| 455 |
+
model=plan.model,
|
| 456 |
+
temperature=plan.temperature,
|
| 457 |
+
max_tokens=plan.max_tokens,
|
| 458 |
+
reasoning_effort=plan.reasoning_effort,
|
| 459 |
+
pipeline_type=plan.task_config.get("pipeline", "single_step"),
|
| 460 |
+
input_fields=plan.task_config.get("input_fields", []),
|
| 461 |
+
resolved_pipeline=plan.pipeline_config,
|
| 462 |
+
prompt_path=plan.prompt_path,
|
| 463 |
+
pipeline=plan.pipeline,
|
| 464 |
+
evaluator_config=plan.task_config.get("evaluator"),
|
| 465 |
+
inference_provider=plan.provider,
|
| 466 |
+
judge_provider=plan.judge_provider,
|
| 467 |
+
prompt_name=plan.prompt_name,
|
| 468 |
+
pipeline_name=plan.pipeline_name,
|
| 469 |
+
config_source=source,
|
| 470 |
+
git_commit=commit,
|
| 471 |
+
pipeline_overrides=plan.pipeline_overrides,
|
| 472 |
+
max_workers=plan.max_workers,
|
| 473 |
+
config_dir=cfg.config_dir(plan.project),
|
| 474 |
+
repo_managed=cfg.is_repo_managed(plan.project),
|
| 475 |
+
dataset=dataset,
|
| 476 |
+
)
|
| 477 |
+
try:
|
| 478 |
+
snapshot = build_engine_snapshot(inputs)
|
| 479 |
+
self_check(snapshot)
|
| 480 |
+
except PREP_ERRORS as e:
|
| 481 |
+
raise click.ClickException(f"cannot snapshot this run's config: {e}") from None
|
| 482 |
+
return snapshot
|
| 483 |
+
|
| 484 |
+
|
| 485 |
+
def auto_note(note: str | None, plan: RunPlan) -> str | None:
|
| 486 |
+
"""`--where` run ์ note ์๋ ๊ธฐ์
(D19) โ Experiment Hub ๋ชฉ๋ก์์ ์ ์ฒด์
๋ฐ๋ณต๊ณผ ์ฌ๋์ด ๊ตฌ๋ถํ๊ฒ.
|
| 487 |
+
|
| 488 |
+
note ๊ฐ ๋น๋ฉด `where tags=yul-ryul ยท 12/159 rows`, ์ฌ์ฉ์๊ฐ ์ฃผ๋ฉด ๊ทธ ๋ค์ ` [where โฆ]`.
|
| 489 |
+
where ๊ฐ ์๋ run ์ note ๋ฅผ ๊ทธ๋๋ก ๋๋ค(๋ค๋ฅธ run ์ run.json ๋ฐ์ดํธ ๋ถ๋ณ).
|
| 490 |
+
"""
|
| 491 |
+
selection = plan.selection
|
| 492 |
+
if selection is None or selection.where is None:
|
| 493 |
+
return note
|
| 494 |
+
prepared = plan.prepared
|
| 495 |
+
label = (
|
| 496 |
+
f"where {selection.where.describe()} ยท {prepared.rows_selected}/{prepared.rows_total} rows"
|
| 497 |
+
)
|
| 498 |
+
return f"{note} [{label}]" if note else label
|
| 499 |
+
|
| 500 |
+
|
| 501 |
+
def build_run_meta(cfg, plan: RunPlan, note: str | None) -> dict[str, Any]:
|
| 502 |
+
"""run.json ์ด๊ธฐ ๋ด์ฉ โ legacy ํค(๊ฐ ๊ท์น ๋ถ๋ณ, D12) ๋ค์ `dataset`ยท`snapshot`(๊ณํ ยง3.2 ์์)."""
|
| 503 |
+
from solar_eval.core.fingerprint import pipeline_fingerprint, prompt_fingerprint
|
| 504 |
+
|
| 505 |
+
source = config_source(cfg, plan.project)
|
| 506 |
+
commit = git_commit(cfg.repo_root) if cfg.is_repo_managed(plan.project) else None
|
| 507 |
+
dataset = dataset_block(cfg, plan)
|
| 508 |
+
return {
|
| 509 |
+
"task": plan.task,
|
| 510 |
+
"model": plan.model,
|
| 511 |
+
"prompt": plan.prompt_name,
|
| 512 |
+
"pipeline": plan.pipeline_name,
|
| 513 |
+
"temperature": plan.temperature,
|
| 514 |
+
"max_tokens": plan.max_tokens,
|
| 515 |
+
"reasoning_effort": plan.reasoning_effort,
|
| 516 |
+
# ์ ๋๋ ธ๋์ง โ ์์ ์๋ ๋ณ๋ ๋ฉ๋
ธํธ(experiments/*.yaml)๊ฐ ๋ด์์ง๋ง run ๊ณผ ๋ถ๋ฆฌ๋ผ ์์ด
|
| 517 |
+
# ์ฐธ์กฐ๊ฐ ๋์ด์ง๊ณ ์ฉ์๋ค. run ๊ณผ ๊ฐ์ ํ์ผ์ ๋๋ฉด ๋์ด์ง ์๊ฐ ์๋ค. where run ์ ์๋ ๊ธฐ์
(D19).
|
| 518 |
+
"note": auto_note(note, plan),
|
| 519 |
+
# provenance: ์ด run ์ config(ํ๋กฌํํธยทํ์ดํ๋ผ์ธ)๊ฐ ์ด๋์ ์๋์ง (W&B/DVC ์ ๋์ฅ)
|
| 520 |
+
"config_source": source,
|
| 521 |
+
"git_commit": commit,
|
| 522 |
+
# ๋ด์ฉ ์ง๋ฌธ โ ์ด๋ฆ์ด ์๋๋ผ ๋ด์ฉ์ผ๋ก ์ค์ ์ ์๋ณํ๋ค. ์ด๋ฆ๋ง ๋ค๋ฅธ ์ฌ๋ณธ,
|
| 523 |
+
# `extends` ๋ก ์กฐ๋ฆฝํ ๊ฒ, `--set` ์ผ๋ก ์ฆ์์์ ๋ง๋ ๊ฒ์ด ๋ชจ๋ ๊ฐ์ ๊ฐ์ด๋ฉด
|
| 524 |
+
# ๊ฐ์ ์ง๋ฌธ์ ๊ฐ๋๋ค (core/fingerprint.py). legacy ๊ฐ ๊ท์น ๊ทธ๋๋ก(D12) โ ์ ์ง๋ฌธ์ snapshot ์.
|
| 525 |
+
"prompt_sha": prompt_fingerprint(plan.prompt_path),
|
| 526 |
+
"pipeline_sha": pipeline_fingerprint(plan.pipeline_config),
|
| 527 |
+
# ํ์ผ ์์ด ๋๋ฆฐ ์คํ์ด๋ฉด ๋ฌด์์ ๋ฐ๊ฟจ๋์ง๊ฐ ์ฌ๊ธฐ ๋จ๋๋ค.
|
| 528 |
+
"pipeline_overrides": plan.pipeline_overrides,
|
| 529 |
+
"dataset": dataset,
|
| 530 |
+
"snapshot": build_snapshot(cfg, plan, dataset, source=source, commit=commit),
|
| 531 |
+
}
|
| 532 |
+
|
| 533 |
+
|
| 534 |
+
def short_fingerprint(value: str | None) -> str:
|
| 535 |
+
"""64-hex ์ง๋ฌธ์ `sha256:<12>` ๋ก โ legacy `prompt_sha` ์ ๊ฐ์ ๋ชจ์์ผ๋ก ๋ ๋์กฐ."""
|
| 536 |
+
if not value:
|
| 537 |
+
return "-"
|
| 538 |
+
return f"sha256:{value[:FINGERPRINT_DISPLAY_CHARS]}"
|
solar_eval/cli/commands/run_progress.py
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""run ์คํ + ์งํ ํ์ (Rich progress bar) โ `runs start`ยท`runs resume` ์ด ๊ณต์ ํ๋ค.
|
| 2 |
+
|
| 3 |
+
`runs.py` ๊ฐ 800์ค ์ํ์ ๋์ด ๋ผ์ด ๋๋ค(๊ณํ 2026-09-24-phase1a E1). ๋์์ ์ฎ๊ธฐ๊ธฐ ์ ๊ณผ ๊ฐ๊ณ ,
|
| 4 |
+
๋ฌ๋์ ์ ํยท์ค๋น๋ ๋ฐ์ดํฐ์
ยทํ์ดํ๋ผ์ธ ์ธ์คํด์ค๋ฅผ ๋๊ธฐ๋ ์ธ์๋ง ๋ํ๋ค(D3ยทD6).
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
from __future__ import annotations
|
| 8 |
+
|
| 9 |
+
import asyncio
|
| 10 |
+
import logging
|
| 11 |
+
from typing import Any
|
| 12 |
+
|
| 13 |
+
import click
|
| 14 |
+
from rich.progress import BarColumn, Progress, SpinnerColumn, TextColumn, TimeElapsedColumn
|
| 15 |
+
|
| 16 |
+
from solar_eval.cli.formatters import format_scores
|
| 17 |
+
|
| 18 |
+
#: ์งํ ํ์ ์๋ก ๋์ด์ฌ๋ ค ์ฐ๋ ๋ก๊ฑฐ๋ค.
|
| 19 |
+
_LOG_NAMES = (
|
| 20 |
+
"solar_eval.pipelines.pipeline",
|
| 21 |
+
"solar_eval.pipelines.steps.llm",
|
| 22 |
+
"solar_eval.core.runner",
|
| 23 |
+
"solar_eval.providers.upstage",
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class _ProgressView:
|
| 28 |
+
"""Rich ์งํ ํ์(๊ฐ๋ฅํ ๋)์ ํ๋ฌธ ์ถ๋ ฅ ์ฌ์ด๋ฅผ ๊ฐ๋ฅด๋ ์์ ์ํ ๋ฌถ์."""
|
| 29 |
+
|
| 30 |
+
def __init__(self) -> None:
|
| 31 |
+
from rich.console import Console
|
| 32 |
+
from rich.live import Live
|
| 33 |
+
|
| 34 |
+
self.bar = Progress(
|
| 35 |
+
SpinnerColumn(),
|
| 36 |
+
TextColumn("[progress.description]{task.description}"),
|
| 37 |
+
BarColumn(),
|
| 38 |
+
TextColumn("{task.completed}/{task.total}"),
|
| 39 |
+
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
| 40 |
+
TimeElapsedColumn(),
|
| 41 |
+
)
|
| 42 |
+
self.live: Live | None = None
|
| 43 |
+
self.use_rich = True
|
| 44 |
+
self.inference_task_id: Any = None
|
| 45 |
+
self.eval_task_id: Any = None
|
| 46 |
+
self._handler: logging.Handler | None = None
|
| 47 |
+
self._closed = False
|
| 48 |
+
try:
|
| 49 |
+
self.live = Live(self.bar, console=Console(stderr=True), refresh_per_second=8)
|
| 50 |
+
self.live.start()
|
| 51 |
+
except Exception as e: # ํฐ๋ฏธ๋์ด Rich ๋ฅผ ๋ชป ๋ฐ์ผ๋ฉด ํ๋ฌธ์ผ๋ก โ ์์ธ์ ํ ์ค ๋จ๊ธด๋ค.
|
| 52 |
+
click.secho(f" (์งํ ํ์ ์์ด ํ๋ฌธ์ผ๋ก ์ถ๋ ฅํฉ๋๋ค: {e})", fg="yellow")
|
| 53 |
+
self.use_rich = False
|
| 54 |
+
if self.use_rich:
|
| 55 |
+
self._redirect_logs()
|
| 56 |
+
|
| 57 |
+
def _redirect_logs(self) -> None:
|
| 58 |
+
"""ํ์ดํ๋ผ์ธยท๋ฌ๋ ๋ก๊ทธ๋ฅผ ์งํ ํ์ ์๋ก ์ฌ๋ฆฐ๋ค."""
|
| 59 |
+
from rich.text import Text
|
| 60 |
+
|
| 61 |
+
live = self.live
|
| 62 |
+
|
| 63 |
+
class _LiveLogHandler(logging.Handler):
|
| 64 |
+
def emit(self, record):
|
| 65 |
+
if live:
|
| 66 |
+
live.console.print(Text(record.getMessage(), style="dim"))
|
| 67 |
+
|
| 68 |
+
self._handler = _LiveLogHandler()
|
| 69 |
+
for name in _LOG_NAMES:
|
| 70 |
+
lg = logging.getLogger(name)
|
| 71 |
+
lg.addHandler(self._handler)
|
| 72 |
+
lg.propagate = False
|
| 73 |
+
logging.getLogger("httpx").setLevel(logging.WARNING)
|
| 74 |
+
|
| 75 |
+
def cleanup(self) -> None:
|
| 76 |
+
"""์งํ ํ์๋ฅผ ๋ซ๊ณ ๋ก๊ฑฐ๋ฅผ ๋๋๋ฆฐ๋ค. ์ฌ๋ฌ ๋ฒ ๋ถ๋ฌ๋ ๋๋ค(๋ ์ด๋ฒคํธ + finally)."""
|
| 77 |
+
if self.use_rich and self.live is not None and not self._closed:
|
| 78 |
+
self._closed = True
|
| 79 |
+
self.live.stop()
|
| 80 |
+
for name in _LOG_NAMES:
|
| 81 |
+
logging.getLogger(name).removeHandler(self._handler)
|
| 82 |
+
logging.getLogger(name).propagate = True
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def _on_inference(view: _ProgressView, etype: str, event: dict[str, Any]) -> None:
|
| 86 |
+
if etype == "dataset_loaded":
|
| 87 |
+
if view.use_rich:
|
| 88 |
+
view.inference_task_id = view.bar.add_task("Inference", total=event["total"])
|
| 89 |
+
else:
|
| 90 |
+
click.secho(f" Dataset loaded: {event['total']} samples", fg="blue")
|
| 91 |
+
elif etype == "inference_progress":
|
| 92 |
+
if view.use_rich and view.inference_task_id is not None:
|
| 93 |
+
view.bar.update(view.inference_task_id, completed=event["completed"])
|
| 94 |
+
elif not view.use_rich:
|
| 95 |
+
c, t = event["completed"], event["total"]
|
| 96 |
+
if c % 10 == 0 or c == t:
|
| 97 |
+
click.echo(f" Inference: {c}/{t}")
|
| 98 |
+
elif etype == "inference_complete":
|
| 99 |
+
if view.use_rich and view.inference_task_id is not None:
|
| 100 |
+
view.bar.update(view.inference_task_id, completed=event["total"])
|
| 101 |
+
else:
|
| 102 |
+
click.secho(f" Inference complete: {event['total']} samples", fg="green")
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
def _on_evaluation(view: _ProgressView, etype: str, event: dict[str, Any]) -> None:
|
| 106 |
+
if etype == "evaluation_start":
|
| 107 |
+
if view.use_rich:
|
| 108 |
+
view.eval_task_id = view.bar.add_task("Evaluation", total=event.get("total", 0) or 1)
|
| 109 |
+
else:
|
| 110 |
+
click.secho(" Starting evaluation...", fg="blue")
|
| 111 |
+
elif etype == "evaluation_progress" and view.use_rich and view.eval_task_id is not None:
|
| 112 |
+
view.bar.update(view.eval_task_id, completed=event["completed"])
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
def _on_finish(view: _ProgressView, etype: str, event: dict[str, Any]) -> None:
|
| 116 |
+
if etype == "done":
|
| 117 |
+
if view.use_rich and view.eval_task_id is not None:
|
| 118 |
+
view.bar.update(view.eval_task_id, completed=view.bar.tasks[view.eval_task_id].total)
|
| 119 |
+
view.cleanup()
|
| 120 |
+
click.echo()
|
| 121 |
+
click.secho("Run completed!", fg="green", bold=True)
|
| 122 |
+
overall = event.get("overall_score")
|
| 123 |
+
if overall is not None:
|
| 124 |
+
click.echo(f" Overall Score: {overall:.4f}")
|
| 125 |
+
scores = event.get("scores", {})
|
| 126 |
+
if scores:
|
| 127 |
+
click.echo(f" Scores: {format_scores(scores)}")
|
| 128 |
+
elif etype == "error":
|
| 129 |
+
view.cleanup()
|
| 130 |
+
click.echo()
|
| 131 |
+
click.secho(f" Run failed: {event.get('error')}", fg="red", bold=True)
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
def _run_with_progress(
|
| 135 |
+
runner,
|
| 136 |
+
run_id,
|
| 137 |
+
project_config,
|
| 138 |
+
task_config,
|
| 139 |
+
prompt,
|
| 140 |
+
max_workers,
|
| 141 |
+
limit,
|
| 142 |
+
completed_results: list[dict[str, Any]] | None = None,
|
| 143 |
+
selection=None,
|
| 144 |
+
prepared=None,
|
| 145 |
+
pipeline=None,
|
| 146 |
+
):
|
| 147 |
+
"""Execute a run with Rich progress bar display.
|
| 148 |
+
|
| 149 |
+
`completed_results` ๋ resume ์ด ์ด๋ฏธ ๋๋ ์ํ์ ๋ค์ ๋๋ฆฌ์ง ์๊ฒ ํ๋ ๋ค๋ฆฌ๋ค --
|
| 150 |
+
์ฌ๊ธฐ์ ์ ๋๊ธฐ๋ฉด runner ๊ฐ ํ๋ผ๋ฏธํฐ๋ฅผ ๊ฐ๊ณ ์์ด๋ ์์ฉ์ด ์๋ค(F2). `selection`ยท`prepared`ยท
|
| 151 |
+
`pipeline` ์ CLI ๊ฐ run ์์ ์ ์ ์ ํ ํ ์ ํยท๋ฐ์ดํฐ์
ยทํ์ดํ๋ผ์ธ ์ธ์คํด์ค๋ค(D3ยทD6).
|
| 152 |
+
|
| 153 |
+
`execute_run` ์ ์น๋ช
์ ์คํจ์์ raise ํ๋ฏ๋ก ์ด ํจ์๋ raise ํ๋ค --
|
| 154 |
+
์ผ๋ถ๋ฌ ๊ฐ์ธ์ง ์๋๋ค. "error" ์ด๋ฒคํธ๊ฐ ์ด๋ฏธ ์ฌ๋์ด ์ฝ์ ๋ฉ์์ง๋ฅผ ์ฐ์๊ณ ,
|
| 155 |
+
์ฌ๊ธฐ์ ๋ ๊ฐ์ธ๋ฉด ๊ฐ์ ๋ด์ฉ์ด ๋ ๋ฒ ๋์จ๋ค.
|
| 156 |
+
"""
|
| 157 |
+
view = _ProgressView()
|
| 158 |
+
|
| 159 |
+
async def on_progress(event):
|
| 160 |
+
etype = event.get("type")
|
| 161 |
+
_on_inference(view, etype, event)
|
| 162 |
+
_on_evaluation(view, etype, event)
|
| 163 |
+
_on_finish(view, etype, event)
|
| 164 |
+
|
| 165 |
+
try:
|
| 166 |
+
asyncio.run(
|
| 167 |
+
runner.execute_run(
|
| 168 |
+
run_id=run_id,
|
| 169 |
+
project_config=project_config,
|
| 170 |
+
task_config=task_config,
|
| 171 |
+
prompt=prompt,
|
| 172 |
+
on_progress=on_progress,
|
| 173 |
+
max_workers=max_workers,
|
| 174 |
+
limit=limit,
|
| 175 |
+
completed_results=completed_results,
|
| 176 |
+
selection=selection,
|
| 177 |
+
prepared=prepared,
|
| 178 |
+
pipeline=pipeline,
|
| 179 |
+
)
|
| 180 |
+
)
|
| 181 |
+
finally:
|
| 182 |
+
# ๋ ์ด๋ฒคํธ ์์ด ๋๋๋(๋ฌ๋๊ฐ ์ด๋ฒคํธ ์ ์ ์ฃฝ์) ํฐ๋ฏธ๋ยท๋ก๊ฑฐ๋ฅผ ์๋๋๋ก ๋๋ฆฐ๋ค.
|
| 183 |
+
view.cleanup()
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
def execute_plan(
|
| 187 |
+
plan, run_id: str, store, completed_results: list[dict[str, Any]] | None = None
|
| 188 |
+
) -> None:
|
| 189 |
+
"""์ค๋น๋ `RunPlan` ์ ๋ฌ๋๋ก ๋๋ฆฐ๋ค โ `runs start`ยท`runs resume` ๊ณต์ฉ.
|
| 190 |
+
|
| 191 |
+
๋ฌ๋๋ plan ์ด ์ฅ **๊ฐ์ ์ธ์คํด์ค**(์ค๋น๋ ๋ฐ์ดํฐ์
ยทํ์ดํ๋ผ์ธยทํ๋ก๋ฐ์ด๋)๋ก ๋๋ค(D6ยทD16).
|
| 192 |
+
`limit` ์ `prepared.selection` ์์ ์์ผ๋ฏ๋ก ๋ฐ๋ก ๋๊ธฐ์ง ์๋๋ค.
|
| 193 |
+
"""
|
| 194 |
+
from solar_eval.core.runner import BatchRunner
|
| 195 |
+
|
| 196 |
+
runner = BatchRunner(
|
| 197 |
+
store=store,
|
| 198 |
+
inference_provider=plan.provider,
|
| 199 |
+
judge_provider=plan.judge_provider,
|
| 200 |
+
dataset_loader=plan.dataset_loader,
|
| 201 |
+
)
|
| 202 |
+
_run_with_progress(
|
| 203 |
+
runner,
|
| 204 |
+
run_id,
|
| 205 |
+
plan.project_config,
|
| 206 |
+
plan.task_config,
|
| 207 |
+
plan.prompt,
|
| 208 |
+
plan.max_workers,
|
| 209 |
+
limit=None,
|
| 210 |
+
completed_results=completed_results,
|
| 211 |
+
prepared=plan.prepared,
|
| 212 |
+
pipeline=plan.pipeline,
|
| 213 |
+
)
|
| 214 |
+
# ๋ฌ๋๊ฐ raise ํ๋ฉด(FAILED) ์ฌ๊ธฐ ์ค์ง ์๋๋ค. ํ
์ ํธ์ถ์๊ฐ ์ด ํจ์ **๋ค์** ๋ถ๋ฅธ๋ค โ ์์ ๊ทธ๋๋ก.
|
| 215 |
+
_record_failure_keys(plan, run_id, store)
|
| 216 |
+
|
| 217 |
+
|
| 218 |
+
def _record_failure_keys(plan, run_id: str, store) -> None:
|
| 219 |
+
"""์คํจ ์ํ ์ง๋ฌธ์ run.json ์ ๋ํ๋ค(LP5 D-LP5-4). ๋ชป ํด๋ run ์ ๊ทธ๋๋ก ๋๋๋ค.
|
| 220 |
+
|
| 221 |
+
์คํจ ์ง๋ฌธ์ ์์ด๋ ๋๋ ์ ๋ณด๋ค โ ์์ผ๋ฉด wrap ์ด `failures.jsonl` ์ ์๋ต(๋ชจ๋ฆ)ํ๋ค. ๊ทธ๋์ ๋์ฐ๋ฏธ์
|
| 222 |
+
์์ธ๋ ๊ฒฝ๊ณ ํ ์ค(์์ธ **ํ์
๋ง** โ ๋ฉ์์ง๋ ์
๋ ฅ ์กฐ๊ฐ์ ๋ด์ ์ ์๋ค)๋ก ๋๋ฌ๋ด๊ณ run ์ ๊ณ์ ๋๋ธ๋ค.
|
| 223 |
+
`poc_bundle` ์ ์ฌ๊ธฐ์ ์ฒ์ import ํ๋ค โ CLI ๋ฅผ ๋์ฐ๋ ๊ฒ๋ง์ผ๋ก ๋ฒ๋ค ๊ณ์ธต์ ์ฃ์ง ์๋๋ค.
|
| 224 |
+
"""
|
| 225 |
+
from solar_eval.cli.commands import failure_keys
|
| 226 |
+
|
| 227 |
+
try:
|
| 228 |
+
failure_keys.record_failure_keys(store, run_id, plan.prepared, plan.task_config)
|
| 229 |
+
except Exception as exc: # ์คํจ ์ง๋ฌธ์ ์ ํ ์ ๋ณด โ run ์ ์คํจ๋ก ๋ง๋ค์ง ์๋๋ค(๊ณํ ยง2.4)
|
| 230 |
+
click.secho(f" failure keys not recorded ({type(exc).__name__})", fg="yellow")
|
solar_eval/cli/commands/run_resume.py
ADDED
|
@@ -0,0 +1,298 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""`solar-eval runs resume` โ ๋๊ธด run ์ ์ด์ด ๋๋ฆฐ๋ค (๊ณํ 2026-09-24-phase1a D3ยทD17).
|
| 2 |
+
|
| 3 |
+
`runs.py` ์์ ๋ผ์ด ๋๋ค(E1). resume ์ **run.json ์ด ๊ธฐ๋กํ ๊ฒ**์ผ๋ก ์ด์ด ๊ฐ๋ค โ ์ง๊ธ์ project.yamlยทCLI ์์
|
| 4 |
+
์ ํ์ ๋ค์ ๋ง๋ค์ง ์๋๋ค. ๋ฌ๋ ํธ์ถ **์ ์** ๋ค ๊ฐ๋๋ฅผ ์ฐจ๋ก๋ก ๊ฑด๋ค(์คํจํด๋ results ๊ฐ ๋์ง ์๋๋ค):
|
| 5 |
+
|
| 6 |
+
1. ๋ฐ์ดํฐ์
sha ๊ฐ๋ โ ์ฝ์ ํ์ผ sha256 ์ด run.json `dataset.sha256` ๊ณผ ๋ค๋ฅด๋ฉด ๋ฉ์ถ๋ค. ํ์ผ์ ์ฝ์
|
| 7 |
+
์งํยทํ์ ๊ณ ๋ฅด๊ธฐ ์ ์ ๊ฑด๋ค โ ๋ฐ๋ ๋ฐ์ดํฐ์
์ด ์ ํยท๋ผ๋ฒจ ์ค๋ฅ๋ก ์๋ชป ๋ณด๊ณ ๋์ง ์๊ฒ.
|
| 8 |
+
2. ์ค์ ๋๋ฆฌํํธ ๊ฐ๋ โ ์์ ๋์ ๊ฐ์ ์กฐ๋ฆฝ ํจ์๋ก ์ง๊ธ ๋์คํฌ ๊ธฐ์ค ์ค๋
์ท์ ๋ค์ ๋
|
| 9 |
+
`config_fingerprint` ๊ฐ ๋ค๋ฅด๋ฉด ๋ฉ์ถ๋ค. ์ฐํ ํ๋๊ทธ๋ ์๋ค โ ๋ ์ค์ ์ด ์์ธ results ๊ฐ ํ ์ค๋
์ท ์๋
|
| 10 |
+
๋ฌถ์ด๋ ๊ฒ์ด ์ด ๊ฐ๋๊ฐ ๋ง๋ ๊ฑฐ์ง ๊ทธ ์์ฒด๋ค(๊ฒํ F2ยทM4).
|
| 11 |
+
3. ์ ํ ๋ฐ idx ๊ฐ๋ โ ์ด๋ฏธ ๋๋ `sample_idx` ๊ฐ ์ ํ ๋ฐ์ด๋ฉด ๋ฉ์ถ๋ค.
|
| 12 |
+
4. ์ ๋ต ํค ๊ฐ๋ โ ๋๋ ํ์ ์ ์ฅ๋ `golden`ยท`input` ์ด ์ง๊ธ ํ์คํฌ ์ค์ (`golden_field`ยท`golden_fields`ยท
|
| 13 |
+
`input_fields`)์ผ๋ก ๋ฝ์ ๊ฐ๊ณผ ๋ค๋ฅด๋ฉด ๋ฉ์ถ๋ค. `golden_field` ๋ ์ง๋ฌธ์๋ sha ์๋ ๋ค์ด๊ฐ์ง ์๊ธฐ ๋๋ฌธ์ด๋ค.
|
| 14 |
+
|
| 15 |
+
config ๊ฐ ๊ฐ์ผ๋ฉด `snapshot.provenance.resumes` ์ ์ด๋ฒ ์์ง ์ํ๋ฅผ ๋ง๋ถ์ด๊ณ (dirty OR, ํธ๋ฆฌ ํด์๊ฐ ๋ฐ๋๋ฉด
|
| 16 |
+
`resumed_with_engine_change`) ์ด์ด ๊ฐ๋ค. `dataset`ยท`snapshot` ๋ธ๋ก์ด ์๋ ์ run ์ ์ค๋์ฒ๋ผ ์ ์ฒด ํ์ผ๋ก ์ด์ด
|
| 17 |
+
๊ฐ๊ณ ๊ฒฝ๊ณ ๋ง ๋จ๊ธด๋ค โ ์์ ์์ ์ค์ ์ ๋ชจ๋ฅด๋ฏ๋ก ์ค๋
์ท์ ์๋ก ์ฐ์ง ์๋๋ค.
|
| 18 |
+
|
| 19 |
+
๋๋๋ฉด `runs start` ์ ๊ฐ์ run ์ข
๋ฃ ํ
(`hub_hook.run_hook`, command=`resume`)์ ๋ถ๋ฅธ๋ค(๊ณํ 2026-09-27 L6a ยง2.2) โ
|
| 20 |
+
`--no-ingest` ๋ ํ
์ ์ฒด๋ฅผ ๊ฑด๋๋ฐ๊ณ , `--strict-ingest`(๋๋ `SOLAR_EVAL_STRICT_INGEST=1`)๋ฉด ์คํจ ๋ค๋ฆฌ๊ฐ ์์ ๋ ์ข
๋ฃ 3.
|
| 21 |
+
์ค๋ชจํฌ ํ์ ์ run.json ์ด ๊ธฐ๋กํ ์ ํ(`dataset.selection.limit`)์ผ๋ก ํ๋ค(D-L6-10).
|
| 22 |
+
"""
|
| 23 |
+
|
| 24 |
+
from __future__ import annotations
|
| 25 |
+
|
| 26 |
+
import asyncio
|
| 27 |
+
from datetime import datetime, timezone
|
| 28 |
+
from pathlib import Path
|
| 29 |
+
from typing import Any
|
| 30 |
+
|
| 31 |
+
import click
|
| 32 |
+
|
| 33 |
+
from solar_eval.cli.commands.run_meta import (
|
| 34 |
+
PREP_ERRORS,
|
| 35 |
+
RunPlan,
|
| 36 |
+
assemble_plan,
|
| 37 |
+
build_snapshot,
|
| 38 |
+
compose_pipeline_config,
|
| 39 |
+
config_source,
|
| 40 |
+
find_task,
|
| 41 |
+
git_commit,
|
| 42 |
+
load_prompt,
|
| 43 |
+
load_run,
|
| 44 |
+
resolve_prompt,
|
| 45 |
+
)
|
| 46 |
+
from solar_eval.cli.commands.run_progress import execute_plan
|
| 47 |
+
from solar_eval.core.dataset_prep import check_resume_indices
|
| 48 |
+
from solar_eval.core.row_filter import RowSelection, format_indices
|
| 49 |
+
from solar_eval.core.run_records import extract_golden, extract_input
|
| 50 |
+
from solar_eval.core.snapshot import config_diff_keys
|
| 51 |
+
from solar_eval.core.snapshot.reproducibility import merge_resume
|
| 52 |
+
from solar_eval.hub_hook import HookOptions, exit_code_for, run_hook
|
| 53 |
+
from solar_eval.hub_hook.optin import strict_requested
|
| 54 |
+
from solar_eval.hub_hook.target import is_smoke_run
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
def _recorded_selection(run_meta: dict[str, Any]) -> RowSelection | None:
|
| 58 |
+
"""run.json ์ด ๊ธฐ๋กํ ์ ํ. `dataset` ๋ธ๋ก์ด ์๋ ์ run ์ None(์ ์ฒด โ ์ค๋ ๋์)."""
|
| 59 |
+
dataset = run_meta.get("dataset")
|
| 60 |
+
if dataset is None:
|
| 61 |
+
return None
|
| 62 |
+
try:
|
| 63 |
+
return RowSelection.from_json(dataset.get("selection") or {"where": None, "limit": None})
|
| 64 |
+
except PREP_ERRORS as e:
|
| 65 |
+
raise click.ClickException(f"run.json dataset.selection is invalid: {e}") from None
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def _resume_task_config(run_meta: dict[str, Any], task_config: dict[str, Any]) -> dict[str, Any]:
|
| 69 |
+
"""run.json ์ด ๊ธฐ๋กํ `meta_fields` ๋ก ๋ง์ถ๋ค โ ํ run ์ results ํ ๋ชจ์์ด ์์ด์ง ์๊ฒ.
|
| 70 |
+
|
| 71 |
+
`dataset` ๋ธ๋ก์ด ์๋ ์ run ์ `meta_fields` ๊ฐ ์๋ ์์ run ์ด๋ผ meta ๋ฅผ ์ฃ์ง ์๋๋ค.
|
| 72 |
+
"""
|
| 73 |
+
dataset = run_meta.get("dataset")
|
| 74 |
+
recorded = dataset.get("meta_fields") if dataset is not None else None
|
| 75 |
+
base = {k: v for k, v in task_config.items() if k != "meta_fields"}
|
| 76 |
+
return {**base, "meta_fields": list(recorded)} if recorded else base
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def _resume_prompt(cfg, project: str, run_meta: dict[str, Any]) -> dict[str, Any]:
|
| 80 |
+
"""run.json ์ ํ๋กฌํํธ ์ด๋ฆยท๋ชจ๋ธยทํ๋ผ๋ฏธํฐ๋ก ๋ฌ๋ prompt ๋ฅผ ๋ค์ ๋ง๋ ๋ค(ํ์ฌ ํ์ผ ๊ธฐ์ค)."""
|
| 81 |
+
prompt_name = run_meta.get("prompt")
|
| 82 |
+
if not prompt_name:
|
| 83 |
+
raise click.ClickException(
|
| 84 |
+
"Cannot determine prompt from run.json. Only runs created with the new CLI can be resumed."
|
| 85 |
+
)
|
| 86 |
+
model = run_meta.get("model", "solar-pro2")
|
| 87 |
+
temperature = run_meta.get("temperature", 0.0)
|
| 88 |
+
max_tokens = run_meta.get("max_tokens", 4000)
|
| 89 |
+
effort = run_meta.get("reasoning_effort")
|
| 90 |
+
prompt_path = resolve_prompt(cfg.prompts_dir(project), prompt_name)
|
| 91 |
+
prompt, fmt = load_prompt(prompt_path, model, temperature, max_tokens, effort)
|
| 92 |
+
return {
|
| 93 |
+
"prompt_name": prompt_name,
|
| 94 |
+
"prompt_path": prompt_path,
|
| 95 |
+
"prompt_format": fmt,
|
| 96 |
+
"prompt": prompt,
|
| 97 |
+
"model": model,
|
| 98 |
+
"temperature": temperature,
|
| 99 |
+
"max_tokens": max_tokens,
|
| 100 |
+
"reasoning_effort": effort,
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def _resume_pipeline(
|
| 105 |
+
cfg, project: str, run_meta: dict[str, Any], task_config: dict[str, Any]
|
| 106 |
+
) -> dict[str, Any]:
|
| 107 |
+
"""run.json ์ ํ์ดํ๋ผ์ธ ์ด๋ฆ(+`--set` overrides)์ผ๋ก ํ์ดํ๋ผ์ธ์ ๋ค์ ํด์ํ๋ค."""
|
| 108 |
+
overrides = run_meta.get("pipeline_overrides") or {}
|
| 109 |
+
pipeline_name = run_meta.get("pipeline") or "default"
|
| 110 |
+
if pipeline_name != "default":
|
| 111 |
+
# ์๋ run ์ด `--set` ์ผ๋ก ๋ฐ๊ฟ ๋๋ฆฐ ๊ฒ์ด๋ฉด ๊ทธ override ๊น์ง ๋ณต์ํด์ผ ๊ฐ์ ์ค์ ์ด๋ค.
|
| 112 |
+
pipeline_config = compose_pipeline_config(cfg.config_dir(project), pipeline_name, overrides)
|
| 113 |
+
task_config = {**task_config, "pipeline_config": pipeline_config}
|
| 114 |
+
else:
|
| 115 |
+
pc = task_config.get("pipeline_config")
|
| 116 |
+
pipeline_config = pc if isinstance(pc, dict) else {}
|
| 117 |
+
return {
|
| 118 |
+
"task_config": task_config,
|
| 119 |
+
"pipeline_name": pipeline_name,
|
| 120 |
+
"pipeline_config": pipeline_config,
|
| 121 |
+
"pipeline_overrides": overrides or None,
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
def _plan_resume(
|
| 126 |
+
cfg, project: str, run_meta: dict[str, Any], max_workers: int
|
| 127 |
+
) -> tuple[RunPlan, RowSelection | None]:
|
| 128 |
+
"""run.json ์ ๋ชจ๋ธยทํ๋กฌํํธยทํ์ดํ๋ผ์ธ(+overrides)ยท์ ํ์ผ๋ก ์คํ ๊ณํ์ ๋ค์ ๋ง๋ ๋ค.
|
| 129 |
+
|
| 130 |
+
๋ฐ์ดํฐ์
sha ๊ฐ๋๋ ํ์ผ์ ์ฝ์ ์งํยทํ์ ๊ณ ๋ฅด๊ธฐ ์ ์ ๊ฑด๋ค(`on_read`) โ ๋ฐ์ดํฐ์
์ด ๋ฐ๋์ด
|
| 131 |
+
์ ํ์ด 0ํ์ด ๋๊ฑฐ๋ ๋ผ๋ฒจ์ด ๋น ์ก์ ๋ "where ๊ฐ ํ๋ ธ๋ค" ๊ฐ ์๋๋ผ ์์ธ์ ์๋ฆฐ๋ค(D17 ์์).
|
| 132 |
+
"""
|
| 133 |
+
project_config, task_config = find_task(cfg, project, run_meta["task"])
|
| 134 |
+
task_config = _resume_task_config(run_meta, task_config)
|
| 135 |
+
selection = _recorded_selection(run_meta)
|
| 136 |
+
plan = assemble_plan(
|
| 137 |
+
cfg,
|
| 138 |
+
selection,
|
| 139 |
+
on_read=lambda loaded: _guard_dataset_sha(loaded.sha256, run_meta),
|
| 140 |
+
project=project,
|
| 141 |
+
task=run_meta["task"],
|
| 142 |
+
project_config=project_config,
|
| 143 |
+
max_workers=max_workers,
|
| 144 |
+
**_resume_prompt(cfg, project, run_meta),
|
| 145 |
+
**_resume_pipeline(cfg, project, run_meta, task_config),
|
| 146 |
+
)
|
| 147 |
+
return plan, selection
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
def _guard_dataset_sha(current: str, run_meta: dict[str, Any]) -> None:
|
| 151 |
+
recorded = (run_meta.get("dataset") or {}).get("sha256")
|
| 152 |
+
if recorded is not None and recorded != current:
|
| 153 |
+
raise click.ClickException(
|
| 154 |
+
"the dataset changed since the run started "
|
| 155 |
+
f"(sha256 {recorded[:12]}โฆ โ {current[:12]}โฆ) โ start a new run"
|
| 156 |
+
)
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
def _guard_config_drift(cfg, plan: RunPlan, run_meta: dict[str, Any]) -> dict[str, Any] | None:
|
| 160 |
+
"""์ง๊ธ ๋์คํฌ ๊ธฐ์ค ์ค๋
์ท์ ๋ค์ ๋ ์ง๋ฌธ์ ๋์กฐํ๋ค. ๊ฐ์ผ๋ฉด resume ์ ๋ฐ์ํ ์ ์ค๋
์ท(D17)."""
|
| 161 |
+
snapshot = run_meta.get("snapshot")
|
| 162 |
+
if snapshot is None:
|
| 163 |
+
click.secho(
|
| 164 |
+
" โ run.json ์ snapshot ์ด ์๋ ์ run ์
๋๋ค โ ์ค์ ๋๋ฆฌํํธ๋ฅผ ๊ฒ์ฌํ์ง ๋ชปํ๊ณ ์ด์ด ๊ฐ๋๋ค.",
|
| 165 |
+
fg="yellow",
|
| 166 |
+
)
|
| 167 |
+
return None
|
| 168 |
+
current = build_snapshot(
|
| 169 |
+
cfg,
|
| 170 |
+
plan,
|
| 171 |
+
run_meta.get("dataset"),
|
| 172 |
+
source=config_source(cfg, plan.project),
|
| 173 |
+
commit=git_commit(cfg.repo_root) if cfg.is_repo_managed(plan.project) else None,
|
| 174 |
+
)
|
| 175 |
+
if current["config_fingerprint"] != snapshot.get("config_fingerprint"):
|
| 176 |
+
changed = config_diff_keys(snapshot.get("config") or {}, current["config"])
|
| 177 |
+
raise click.ClickException(
|
| 178 |
+
f"the run's config changed since it started: {', '.join(changed) or '?'} "
|
| 179 |
+
"โ start a new run (mixing two configs in one run is not allowed)"
|
| 180 |
+
)
|
| 181 |
+
at = datetime.now(timezone.utc).isoformat()
|
| 182 |
+
return merge_resume(snapshot, current["provenance"]["engine"], at)
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
def _guard_selection(plan: RunPlan, completed_indices: set[int]) -> None:
|
| 186 |
+
try:
|
| 187 |
+
check_resume_indices(plan.prepared, completed_indices)
|
| 188 |
+
except PREP_ERRORS as e:
|
| 189 |
+
raise click.ClickException(str(e)) from None
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
def _drifted_rows(plan: RunPlan, completed_results: list[dict[str, Any]]) -> list[int]:
|
| 193 |
+
"""์ด๋ฏธ ๋๋ ํ ์ค ์ ์ฅ๋ `input`ยท`golden` ์ด ์ง๊ธ ํ์คํฌ ์ค์ ์ผ๋ก ๋ฝ์ ๊ฐ๊ณผ ๋ค๋ฅธ sample_idx."""
|
| 194 |
+
records = plan.prepared.loaded.records
|
| 195 |
+
input_fields = plan.task_config.get("input_fields", [])
|
| 196 |
+
drifted = set()
|
| 197 |
+
for result in completed_results:
|
| 198 |
+
row = records[result["sample_idx"]]
|
| 199 |
+
if "golden" in result and result["golden"] != extract_golden(row, plan.task_config):
|
| 200 |
+
drifted.add(result["sample_idx"])
|
| 201 |
+
if "input" in result and result["input"] != extract_input(row, input_fields):
|
| 202 |
+
drifted.add(result["sample_idx"])
|
| 203 |
+
return sorted(drifted)
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
def _guard_answer_key(plan: RunPlan, completed_results: list[dict[str, Any]]) -> None:
|
| 207 |
+
"""์ ๋ต ํค(`golden_field`ยท`golden_fields`)ยท์
๋ ฅ ํ๋๊ฐ run ์์ ๋ค ๋ฐ๋์์ผ๋ฉด ๋ฉ์ถ๋ค(D17 ๋ณด๊ฐ).
|
| 208 |
+
|
| 209 |
+
`golden_field` ๋ ์ค๋
์ท config ์๋ run.json `dataset` ๋ธ๋ก์๋ ์์ด ์ง๋ฌธยทsha ๊ฐ๋๋ฅผ ์ง๋๊ฐ๋ค โ
|
| 210 |
+
๊ทธ๋๋ก ์ด์ด ๊ฐ๋ฉด ํ run ์ ๋ ์ ๋ต ํค๊ฐ ์์ด๊ณ , ๋๋ ํ๊น์ง ์ ์ ๋ต์ผ๋ก ์ฑ์ ๋ผ results ์ `golden`
|
| 211 |
+
๊ณผ ์ ์๊ฐ ์ด๊ธ๋๋ค. ๊ทธ๋์ ์ ์ฅ๋ ๊ฐ๊ณผ ์ง๊ธ ๋ฝ์ ๊ฐ์ ํ๋ง๋ค ์ง์ ๋์กฐํ๋ค(์ run ๋ ๊ฐ์ ๊ท์น).
|
| 212 |
+
์ ํ ๋ฐ idx ๊ฐ๋ ๋ค์ ๋ถ๋ฅธ๋ค โ ๋๋ idx ๊ฐ ๋ชจ๋ ํ์ผ ์์ ์๋ค.
|
| 213 |
+
"""
|
| 214 |
+
drifted = _drifted_rows(plan, completed_results)
|
| 215 |
+
if drifted:
|
| 216 |
+
raise click.ClickException(
|
| 217 |
+
"the task's answer key or input fields changed since the run started "
|
| 218 |
+
f"(stored golden/input differs for {format_indices(drifted)}) โ start a new run"
|
| 219 |
+
)
|
| 220 |
+
|
| 221 |
+
|
| 222 |
+
@click.command("resume")
|
| 223 |
+
@click.argument("project")
|
| 224 |
+
@click.option("--run-id", "-r", required=True, help="Run ID to resume")
|
| 225 |
+
@click.option("--max-workers", "-w", default=5, type=int, help="Max parallel workers")
|
| 226 |
+
@click.option(
|
| 227 |
+
"--no-ingest",
|
| 228 |
+
is_flag=True,
|
| 229 |
+
help="run ์ด ๋๋ ๋ค hub ํ
(๋ฒ๋คยทhub ์ ์ฌ)์ ํต์งธ๋ก ๊ฑด๋๋ด๋ค โ ๋์ด๋ฆฌ๋ ๋ช
๋ น์ ํ ์ค๋ก ์๋ ค ์ค๋ค",
|
| 230 |
+
)
|
| 231 |
+
@click.option(
|
| 232 |
+
"--strict-ingest",
|
| 233 |
+
is_flag=True,
|
| 234 |
+
help="hub ํ
๋ค๋ฆฌ๊ฐ ํ๋๋ผ๋ ์คํจํ๋ฉด ์ข
๋ฃ ์ฝ๋ 3 (๊ธฐ๋ณธ 0). SOLAR_EVAL_STRICT_INGEST=1 ๊ณผ ๊ฐ๋ค",
|
| 235 |
+
)
|
| 236 |
+
@click.pass_context
|
| 237 |
+
def resume_run(
|
| 238 |
+
ctx: click.Context,
|
| 239 |
+
project: str,
|
| 240 |
+
run_id: str,
|
| 241 |
+
max_workers: int,
|
| 242 |
+
no_ingest: bool,
|
| 243 |
+
strict_ingest: bool,
|
| 244 |
+
) -> None:
|
| 245 |
+
"""Resume an interrupted evaluation run."""
|
| 246 |
+
cfg = ctx.obj["config"]
|
| 247 |
+
if cfg.is_remote:
|
| 248 |
+
raise click.ClickException("Resume is local-mode only")
|
| 249 |
+
run_dir, store, run_meta = load_run(cfg, project, run_id)
|
| 250 |
+
if run_meta.get("status", "unknown") == "completed":
|
| 251 |
+
raise click.ClickException(f"Run {run_id} is already completed. Nothing to resume.")
|
| 252 |
+
|
| 253 |
+
existing_results = store.load_existing_results()
|
| 254 |
+
completed = {r["sample_idx"] for r in existing_results}
|
| 255 |
+
click.secho(f"Resuming run: {run_id}", fg="blue", bold=True)
|
| 256 |
+
click.secho(
|
| 257 |
+
f" Task: {run_meta['task']}, Already completed: {len(completed)} samples", fg="blue"
|
| 258 |
+
)
|
| 259 |
+
|
| 260 |
+
plan, selection = _plan_resume(cfg, project, run_meta, max_workers)
|
| 261 |
+
if selection is None:
|
| 262 |
+
click.secho(
|
| 263 |
+
" โ run.json ์ dataset ๋ธ๋ก์ด ์๋ ์ run โ ์ ์ฒด ํ์ผ๋ก ์ด์ด ๊ฐ๋๋ค.", fg="yellow"
|
| 264 |
+
)
|
| 265 |
+
# ์์: ๋ฐ์ดํฐ์
sha(์ฝ๋ ์ฆ์, _plan_resume ์) โ ์ค์ ๋๋ฆฌํํธ โ ์ ํ ๋ฐ idx โ ์ ๋ต ํค (D17).
|
| 266 |
+
# ๋ชจ๋ ๋ฌ๋ ํธ์ถ ์ ์ด๋ผ ์คํจํด๋ results ๊ฐ ๋์ง ์๋๋ค.
|
| 267 |
+
merged = _guard_config_drift(cfg, plan, run_meta)
|
| 268 |
+
_guard_selection(plan, completed)
|
| 269 |
+
_guard_answer_key(plan, existing_results)
|
| 270 |
+
if merged is not None:
|
| 271 |
+
asyncio.run(store.update_run(run_id, {"snapshot": merged}))
|
| 272 |
+
click.secho(f" Model: {plan.model}", fg="blue")
|
| 273 |
+
|
| 274 |
+
# ์ด์ ํ๊ฐ๋ฅผ ๋ฏธ๋ฆฌ ์ง์ฐ์ง ์๋๋ค(F3) -- create_evaluation() ์ด ์์์ ๊ต์ฒด๋ผ ์์ ํ๋ค.
|
| 275 |
+
# ์ด๋ฏธ ๋๋ ์ํ์ ๊ฑด๋๋ด๋ค -- completed_results ๋ฅผ ์ ๋๊ธฐ๋ฉด resume ์ด ์ ์ํ์ ๋ค์ ์ ๋ฃ
|
| 276 |
+
# ํธ์ถํ๊ณ results.jsonl ์ ๊ฐ์ sample_idx ๋ฅผ ์ค๋ณต append ํ๋ค(F2).
|
| 277 |
+
execute_plan(plan, run_id, store, completed_results=existing_results)
|
| 278 |
+
click.echo(f"\nResults saved to: {run_dir}/")
|
| 279 |
+
_finish_with_hook(ctx, project, run_dir, run_meta, no_ingest, strict_ingest)
|
| 280 |
+
|
| 281 |
+
|
| 282 |
+
def _finish_with_hook(
|
| 283 |
+
ctx: click.Context,
|
| 284 |
+
project: str,
|
| 285 |
+
run_dir: Path,
|
| 286 |
+
run_meta: dict[str, Any],
|
| 287 |
+
no_ingest: bool,
|
| 288 |
+
strict_ingest: bool,
|
| 289 |
+
) -> None:
|
| 290 |
+
"""run ์ข
๋ฃ ํ
(command=`resume`). ์ค๋ชจํฌ๋ run.json ์ด ๊ธฐ๋กํ ์ ํ์ผ๋ก ๋ณธ๋ค(D-L6-10)."""
|
| 291 |
+
strict = strict_requested(strict_ingest)
|
| 292 |
+
options = HookOptions(
|
| 293 |
+
command="resume", no_ingest=no_ingest, strict=strict, smoke=is_smoke_run(run_meta)
|
| 294 |
+
)
|
| 295 |
+
# ํ
์คํจ๋ ๊ธฐ๋ณธ ์ข
๋ฃ 0 โ strict ์์๋ง 3(D-L6-11 ยท ๋ง์คํฐ D-M2-3).
|
| 296 |
+
code = exit_code_for(run_hook(ctx.obj["config"], project, run_dir, options), strict)
|
| 297 |
+
if code:
|
| 298 |
+
ctx.exit(code)
|
solar_eval/cli/commands/runs.py
CHANGED
|
@@ -1,23 +1,41 @@
|
|
| 1 |
-
"""Run management commands โ local execution.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
-
import asyncio
|
| 4 |
import json
|
| 5 |
-
import subprocess
|
| 6 |
import time
|
|
|
|
| 7 |
from datetime import datetime
|
| 8 |
from pathlib import Path
|
| 9 |
from typing import Any
|
| 10 |
|
| 11 |
import click
|
| 12 |
-
from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn, TimeElapsedColumn
|
| 13 |
|
| 14 |
from solar_eval.cli.commands.projects import _resolve_project_id
|
| 15 |
-
from solar_eval.cli.
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
|
| 23 |
@click.group("runs")
|
|
@@ -25,6 +43,31 @@ def runs_group() -> None:
|
|
| 25 |
"""Manage evaluation runs."""
|
| 26 |
|
| 27 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
@runs_group.command("list")
|
| 29 |
@click.argument("project")
|
| 30 |
@click.option("--task", "-t", default=None, help="Filter by task name")
|
|
@@ -102,7 +145,7 @@ def list_runs(ctx: click.Context, project: str, task: str | None) -> None:
|
|
| 102 |
@click.option(
|
| 103 |
"--note",
|
| 104 |
default=None,
|
| 105 |
-
help="์ด run ์ ์ ๋๋ฆฌ๋์ง ํ ์ค (run.json ์ ๊ธฐ๋ก๋๊ณ
|
| 106 |
)
|
| 107 |
@click.option(
|
| 108 |
"--set",
|
|
@@ -114,28 +157,11 @@ def list_runs(ctx: click.Context, project: str, task: str | None) -> None:
|
|
| 114 |
"์: --set steps.basic_correction.reasoning_effort=high. ์ฌ๋ฌ ๋ฒ ์ธ ์ ์๋ค."
|
| 115 |
),
|
| 116 |
)
|
| 117 |
-
@click.option(
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
help="run ์ด ๋๋ ๋ค evalhub ์๋ ์ ์ฌ๋ฅผ ๊ฑด๋๋ด๋ค (์ฌ๋ฌ run ์ ๋๋ฆฌ๊ณ ํ ๋ฒ์ ์ ์ฌํ ๋)",
|
| 121 |
-
)
|
| 122 |
@click.pass_context
|
| 123 |
-
def start_run(
|
| 124 |
-
ctx: click.Context,
|
| 125 |
-
project: str,
|
| 126 |
-
task: str,
|
| 127 |
-
prompt_name: str,
|
| 128 |
-
model: str,
|
| 129 |
-
pipeline_name: str | None,
|
| 130 |
-
reasoning_effort: str | None,
|
| 131 |
-
temperature: float,
|
| 132 |
-
max_tokens: int,
|
| 133 |
-
max_workers: int,
|
| 134 |
-
limit: int | None,
|
| 135 |
-
note: str | None,
|
| 136 |
-
set_options: tuple[str, ...],
|
| 137 |
-
no_ingest: bool,
|
| 138 |
-
) -> None:
|
| 139 |
"""Start an evaluation run.
|
| 140 |
|
| 141 |
Example:
|
|
@@ -147,80 +173,17 @@ def start_run(
|
|
| 147 |
-p prompt_dev_v23 -m solar-pro3 -P pipeline_dev_v24 \\
|
| 148 |
--set steps.basic_correction.repeat_temperatures='[0.0, 0.5]' \\
|
| 149 |
--note "SC ์จ๋ ํญ ์ค์"
|
| 150 |
-
"""
|
| 151 |
-
cfg = ctx.obj["config"]
|
| 152 |
-
|
| 153 |
-
_start_local(
|
| 154 |
-
cfg,
|
| 155 |
-
project=project,
|
| 156 |
-
task=task,
|
| 157 |
-
prompt_name=prompt_name,
|
| 158 |
-
model=model,
|
| 159 |
-
pipeline_name=pipeline_name,
|
| 160 |
-
reasoning_effort=reasoning_effort,
|
| 161 |
-
temperature=temperature,
|
| 162 |
-
max_tokens=max_tokens,
|
| 163 |
-
max_workers=max_workers,
|
| 164 |
-
limit=limit,
|
| 165 |
-
note=note,
|
| 166 |
-
set_options=set_options,
|
| 167 |
-
no_ingest=no_ingest,
|
| 168 |
-
)
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
def _resolve_prompt(prompts_dir: Path, prompt_name: str) -> Path:
|
| 172 |
-
"""Resolve prompt from prompts/ directory (directory or YAML file).
|
| 173 |
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
2. prompts/{name}.yaml (flat YAML layout)
|
| 177 |
-
3. prompts/_library/{name}.yaml (legacy layout)
|
| 178 |
"""
|
| 179 |
-
#
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
return yaml_path
|
| 187 |
-
# Legacy _library layout
|
| 188 |
-
legacy_path = prompts_dir / "_library" / f"{prompt_name}.yaml"
|
| 189 |
-
if legacy_path.exists():
|
| 190 |
-
return legacy_path
|
| 191 |
-
raise click.ClickException(
|
| 192 |
-
f"Prompt '{prompt_name}' not found. Searched:\n"
|
| 193 |
-
f" {dir_path}/ (directory)\n"
|
| 194 |
-
f" {yaml_path} (YAML)\n"
|
| 195 |
-
f" {legacy_path} (legacy)"
|
| 196 |
-
)
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
def _resolve_pipeline_file(project_dir: Path, pipeline_name: str) -> Path:
|
| 200 |
-
"""Resolve pipeline YAML from pipelines/ directory."""
|
| 201 |
-
path = project_dir / "pipelines" / f"{pipeline_name}.yaml"
|
| 202 |
-
if not path.exists():
|
| 203 |
-
raise click.ClickException(f"Pipeline '{pipeline_name}' not found: {path}")
|
| 204 |
-
return path
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
def _compose_pipeline_config(project_dir: Path, pipeline_name: str, overrides: dict) -> dict:
|
| 208 |
-
"""ํ์ดํ๋ผ์ธ์ `extends`/override ๊น์ง ํด์ํด ๋๋ ค์ค๋ค.
|
| 209 |
-
|
| 210 |
-
์กฐ๋ฆฝ ์คํจ(์๋ base, ์๋ ์คํ
์ ๊ฐ๋ฆฌํค๋ override ๋ฑ)๋ run ์ ์์ํ๊ธฐ ์ ์
|
| 211 |
-
์ฃฝ์ธ๋ค โ ํจ๊ณผ ์๋ override ๋ก ๋์๊ฐ run ์ ๊ฒฐ๋ก ์ ์กฐ์ฉํ ์ค์ผ์ํจ๋ค.
|
| 212 |
-
"""
|
| 213 |
-
from solar_eval.core.pipeline_compose import PipelineCompositionError, compose_pipeline
|
| 214 |
-
from solar_eval.core.project_loader import load_pipeline_file
|
| 215 |
-
|
| 216 |
-
pipelines_dir = _resolve_pipeline_file(project_dir, pipeline_name).parent
|
| 217 |
-
try:
|
| 218 |
-
composed = load_pipeline_file(pipelines_dir, pipeline_name)
|
| 219 |
-
if overrides:
|
| 220 |
-
composed = compose_pipeline(composed, overrides=overrides)
|
| 221 |
-
return composed
|
| 222 |
-
except (PipelineCompositionError, FileNotFoundError) as e:
|
| 223 |
-
raise click.ClickException(str(e)) from None
|
| 224 |
|
| 225 |
|
| 226 |
def _build_run_name(task: str, pipeline_name: str, prompt_name: str) -> str:
|
|
@@ -241,7 +204,7 @@ def _make_run_dir(
|
|
| 241 |
๊ฐ์ ์ด์ ๊ฐ์ (task, pipeline, prompt) ์กฐํฉ์ผ๋ก ๋ run ์ด ์์๋๋ฉด
|
| 242 |
`_build_run_name` ์ด ๊ฐ์ ์ด๋ฆ์ ๋ธ๋ค. `mkdir(exist_ok=False)` ๋ฅผ ์ฐ๋ฉด ๋ ํ๋ก์ธ์ค๊ฐ
|
| 243 |
๋์์ ๊ฐ์ ์ด๋ฆ์ผ๋ก mkdir ํด๋ ์ ํํ ํ๋๋ง ์ฑ๊ณตํ๊ณ , ์ง ์ชฝ์ ์ ํ์์คํฌํ๋ก
|
| 244 |
-
์ฌ์๋ํ๋ค โ ํ์ผ ํฌ๋งท ์์ฒด๋ ์ ๋ฐ๊พธ๋ฏ๋ก
|
| 245 |
"""
|
| 246 |
for _ in range(_RUN_DIR_MAX_ATTEMPTS):
|
| 247 |
run_name = _build_run_name(task, pipeline_name, prompt_name)
|
|
@@ -254,254 +217,104 @@ def _make_run_dir(
|
|
| 254 |
raise click.ClickException(f"Could not allocate a unique run directory under {artifacts_dir}")
|
| 255 |
|
| 256 |
|
| 257 |
-
def _config_source(cfg, project: str) -> str:
|
| 258 |
-
"""run ์ config ์ถ์ฒ โ ๋ ํฌ ๊ด๋ฆฌ ํ๋ก์ ํธ๋ฉด ๋ ํฌ ์๋ ๊ฒฝ๋ก, ์๋๋ฉด ๋ฐ์ดํฐ ํ ์ ๋ ๊ฒฝ๋ก."""
|
| 259 |
-
config_dir = cfg.config_dir(project)
|
| 260 |
-
if cfg.is_repo_managed(project) and cfg.repo_root is not None:
|
| 261 |
-
return str(config_dir.relative_to(cfg.repo_root))
|
| 262 |
-
return str(config_dir)
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
def _git_commit(repo_root: Path | None) -> str | None:
|
| 266 |
-
"""์ฌํ์ฉ ๋์ฅ: ๋ ํฌ HEAD ์ปค๋ฐ. ์์
ํธ๋ฆฌ๊ฐ ๋๋ฌ์ฐ๋ฉด '-dirty' ๋ฅผ ๋ถ์ธ๋ค."""
|
| 267 |
-
if repo_root is None:
|
| 268 |
-
return None
|
| 269 |
-
try:
|
| 270 |
-
head = subprocess.run(
|
| 271 |
-
["git", "-C", str(repo_root), "rev-parse", "--short", "HEAD"],
|
| 272 |
-
capture_output=True,
|
| 273 |
-
text=True,
|
| 274 |
-
check=True,
|
| 275 |
-
).stdout.strip()
|
| 276 |
-
status = subprocess.run(
|
| 277 |
-
["git", "-C", str(repo_root), "status", "--porcelain"],
|
| 278 |
-
capture_output=True,
|
| 279 |
-
text=True,
|
| 280 |
-
check=True,
|
| 281 |
-
).stdout.strip()
|
| 282 |
-
return f"{head}-dirty" if status else head
|
| 283 |
-
except (OSError, subprocess.CalledProcessError):
|
| 284 |
-
return None
|
| 285 |
-
|
| 286 |
-
|
| 287 |
def _start_local(
|
| 288 |
cfg,
|
| 289 |
-
|
| 290 |
-
task: str,
|
| 291 |
-
prompt_name: str,
|
| 292 |
-
model: str,
|
| 293 |
-
pipeline_name: str | None,
|
| 294 |
-
reasoning_effort: str | None,
|
| 295 |
-
temperature: float,
|
| 296 |
-
max_tokens: int,
|
| 297 |
-
max_workers: int,
|
| 298 |
-
limit: int | None,
|
| 299 |
note: str | None = None,
|
| 300 |
-
set_options: tuple[str, ...] = (),
|
| 301 |
no_ingest: bool = False,
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
)
|
| 314 |
-
|
| 315 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 316 |
from solar_eval.stores import JsonlStore
|
| 317 |
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
raise click.ClickException(f"Project not found in {cfg.projects_dir}: {project}")
|
| 323 |
-
|
| 324 |
-
task_config = next((t for t in project_config.get("tasks", []) if t["name"] == task), None)
|
| 325 |
-
if not task_config:
|
| 326 |
-
raise click.ClickException(f"Task '{task}' not found in project '{project}'")
|
| 327 |
-
|
| 328 |
-
# Resolve prompt file
|
| 329 |
-
prompts_dir = cfg.prompts_dir(project)
|
| 330 |
-
prompt_path = _resolve_prompt(prompts_dir, prompt_name)
|
| 331 |
-
|
| 332 |
-
fmt = detect_prompt_format(prompt_path)
|
| 333 |
-
if fmt == "multi_step":
|
| 334 |
-
step_prompts = load_step_prompts(prompt_path)
|
| 335 |
-
prompt = {
|
| 336 |
-
"step_prompts": step_prompts,
|
| 337 |
-
"model": model,
|
| 338 |
-
"temperature": temperature,
|
| 339 |
-
"max_tokens": max_tokens,
|
| 340 |
-
}
|
| 341 |
-
else:
|
| 342 |
-
messages = load_prompt_messages(prompt_path)
|
| 343 |
-
prompt = {
|
| 344 |
-
"messages": messages,
|
| 345 |
-
"model": model,
|
| 346 |
-
"temperature": temperature,
|
| 347 |
-
"max_tokens": max_tokens,
|
| 348 |
-
}
|
| 349 |
-
|
| 350 |
-
if reasoning_effort:
|
| 351 |
-
prompt["reasoning_effort"] = reasoning_effort
|
| 352 |
-
|
| 353 |
-
# Resolve pipeline โ config ๋ ๋ ํฌ ๊ด๋ฆฌ ํ๋ก์ ํธ๋ฉด git ์ชฝ์ด ์ ๋ณธ.
|
| 354 |
-
# `extends` ์์๊ณผ `--set` override ๋ ์ฌ๊ธฐ์ ์ ๋ถ ํด์๋ผ, ์คํ ์์ง์๋ ์์ฑ๋
|
| 355 |
-
# steps ๋ชฉ๋ก๋ง ๋์ด๊ฐ๋ค.
|
| 356 |
-
try:
|
| 357 |
-
cli_overrides = parse_set_option(set_options)
|
| 358 |
-
except PipelineCompositionError as e:
|
| 359 |
-
raise click.ClickException(str(e)) from None
|
| 360 |
-
|
| 361 |
-
effective_pipeline = pipeline_name
|
| 362 |
-
if pipeline_name:
|
| 363 |
-
pipeline_config = _compose_pipeline_config(
|
| 364 |
-
cfg.config_dir(project), pipeline_name, cli_overrides
|
| 365 |
-
)
|
| 366 |
-
task_config = {**task_config, "pipeline_config": pipeline_config}
|
| 367 |
-
else:
|
| 368 |
-
# Use default from project.yaml (already resolved by project_loader)
|
| 369 |
-
pc = task_config.get("pipeline_config", {})
|
| 370 |
-
effective_pipeline = pc.get("name", "default") if isinstance(pc, dict) else str(pc)
|
| 371 |
-
if cli_overrides:
|
| 372 |
-
raise click.ClickException("--set requires an explicit --pipeline/-P")
|
| 373 |
-
pipeline_config = pc if isinstance(pc, dict) else {}
|
| 374 |
-
|
| 375 |
-
# Build run name and directory -- ์ด๋ฆ ์ถฉ๋์ ์ฌ๊ธฐ์ ์์์ ์ผ๋ก ๊ฑธ๋ฌ์ง๋ค(F10).
|
| 376 |
run_name, run_dir = _make_run_dir(
|
| 377 |
-
cfg.artifacts_dir(project), task,
|
| 378 |
-
)
|
| 379 |
-
store = JsonlStore(run_dir)
|
| 380 |
-
|
| 381 |
-
# Save run metadata
|
| 382 |
-
run_meta = {
|
| 383 |
-
"task": task,
|
| 384 |
-
"model": model,
|
| 385 |
-
"prompt": prompt_name,
|
| 386 |
-
"pipeline": effective_pipeline,
|
| 387 |
-
"temperature": temperature,
|
| 388 |
-
"max_tokens": max_tokens,
|
| 389 |
-
"reasoning_effort": reasoning_effort,
|
| 390 |
-
# ์ ๋๋ ธ๋์ง โ ์์ ์๋ ๋ณ๋ ๋ฉ๋
ธํธ(experiments/*.yaml)๊ฐ ๋ด์์ง๋ง run ๊ณผ ๋ถ๋ฆฌ๋ผ ์์ด
|
| 391 |
-
# ์ฐธ์กฐ๊ฐ ๋์ด์ง๊ณ ์ฉ์๋ค. run ๊ณผ ๊ฐ์ ํ์ผ์ ๋๋ฉด ๋์ด์ง ์๊ฐ ์๋ค.
|
| 392 |
-
"note": note,
|
| 393 |
-
# provenance: ์ด run ์ config(ํ๋กฌํํธยทํ์ดํ๋ผ์ธ)๊ฐ ์ด๋์ ์๋์ง (W&B/DVC ์ ๋์ฅ)
|
| 394 |
-
"config_source": _config_source(cfg, project),
|
| 395 |
-
"git_commit": _git_commit(cfg.repo_root) if cfg.is_repo_managed(project) else None,
|
| 396 |
-
# ๋ด์ฉ ์ง๋ฌธ โ ์ด๋ฆ์ด ์๋๋ผ ๋ด์ฉ์ผ๋ก ์ค์ ์ ์๋ณํ๋ค. ์ด๋ฆ๋ง ๋ค๋ฅธ ์ฌ๋ณธ,
|
| 397 |
-
# `extends` ๋ก ์กฐ๋ฆฝํ ๊ฒ, `--set` ์ผ๋ก ์ฆ์์์ ๋ง๋ ๊ฒ์ด ๋ชจ๋ ๊ฐ์ ๊ฐ์ด๋ฉด
|
| 398 |
-
# ๊ฐ์ ์ง๋ฌธ์ ๊ฐ๋๋ค (core/fingerprint.py).
|
| 399 |
-
"prompt_sha": prompt_fingerprint(prompt_path),
|
| 400 |
-
"pipeline_sha": pipeline_fingerprint(pipeline_config),
|
| 401 |
-
# ํ์ผ ์์ด ๋๋ฆฐ ์คํ์ด๋ฉด ๋ฌด์์ ๋ฐ๊ฟจ๋์ง๊ฐ ์ฌ๊ธฐ ๋จ๋๋ค.
|
| 402 |
-
"pipeline_overrides": cli_overrides or None,
|
| 403 |
-
}
|
| 404 |
-
# run_dir ์ _make_run_dir ์ด ์ด๋ฏธ ์์์ ์ผ๋ก ๋ง๋ค์๋ค -- ์ฌ๊ธฐ์ ๋ค์ mkdir ํ์ง ์๋๋ค.
|
| 405 |
-
(run_dir / "run.json").write_text(json.dumps(run_meta, ensure_ascii=False, indent=2))
|
| 406 |
-
|
| 407 |
-
# Log run info
|
| 408 |
-
click.secho(f"Prompt: {prompt_name} [{fmt}]", fg="blue")
|
| 409 |
-
click.secho(f"Model: {model}", fg="blue")
|
| 410 |
-
click.secho(f"Pipeline: {effective_pipeline}", fg="blue")
|
| 411 |
-
for path, value in cli_overrides.items():
|
| 412 |
-
click.secho(f" override: {path} = {value!r}", fg="magenta")
|
| 413 |
-
click.secho(
|
| 414 |
-
f"Fingerprint: prompt {run_meta['prompt_sha']} / pipeline {run_meta['pipeline_sha']}",
|
| 415 |
-
fg="blue",
|
| 416 |
)
|
| 417 |
-
|
| 418 |
-
|
| 419 |
-
|
| 420 |
-
click.secho(f"Limit: {limit} samples", fg="yellow")
|
| 421 |
-
click.secho(f"Starting local run: {project}/{run_name}", fg="blue")
|
| 422 |
-
click.secho(f"Output: {run_dir}", fg="blue")
|
| 423 |
-
|
| 424 |
-
# Create runner
|
| 425 |
-
provider = UpstageProvider()
|
| 426 |
-
judge_provider = OpenAIProvider()
|
| 427 |
-
dataset_loader = DatasetLoader(base_dir=cfg.projects_dir)
|
| 428 |
-
runner = BatchRunner(
|
| 429 |
-
store=store,
|
| 430 |
-
inference_provider=provider,
|
| 431 |
-
judge_provider=judge_provider,
|
| 432 |
-
dataset_loader=dataset_loader,
|
| 433 |
-
)
|
| 434 |
-
|
| 435 |
-
# Run with progress display
|
| 436 |
-
_run_with_progress(runner, run_name, project_config, task_config, prompt, max_workers, limit)
|
| 437 |
|
| 438 |
_warn_if_incomplete(run_dir)
|
|
|
|
|
|
|
| 439 |
click.echo(f"\nResults saved to: {run_dir}/")
|
| 440 |
-
if
|
| 441 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 442 |
|
| 443 |
|
| 444 |
-
def
|
| 445 |
-
"""
|
|
|
|
|
|
|
| 446 |
|
| 447 |
-
|
| 448 |
-
|
| 449 |
-
"๋ฌด์์ด ์ ๋๊ณ ์ด๋ป๊ฒ ๋์ด๋ฆฌ๋์ง"๋ง ์๋ฆฐ๋ค. ์ ์ฌ๋ฅผ ๋ชป ํ๋ค๋ ์ฌ์ค ์์ฒด๋ฅผ
|
| 450 |
-
์กฐ์ฉํ ๋๊ธฐ์ง๋ ์๋๋ค โ ๊ทธ๋ฌ๋ฉด evalhub ํ๋ฉด์ด ๋ก์ ์ฑ๋ก ๋จ๋๋ค.
|
| 451 |
|
| 452 |
-
๋ ํฌ ๋ฐ standalone ์ค์น์๋ evalhub ์์ฒด๊ฐ ์์ผ๋ฏ๋ก ์กฐ์ฉํ ๊ฑด๋๋ด๋ค.
|
| 453 |
-
"""
|
| 454 |
-
if cfg.repo_root is None:
|
| 455 |
-
return
|
| 456 |
|
| 457 |
-
|
| 458 |
-
|
| 459 |
-
|
| 460 |
-
["pnpm", "--filter", "@poc/eval-store", "ingest", "--only", project],
|
| 461 |
-
cwd=str(cfg.repo_root),
|
| 462 |
-
capture_output=True,
|
| 463 |
-
text=True,
|
| 464 |
-
timeout=INGEST_TIMEOUT_SECONDS,
|
| 465 |
-
)
|
| 466 |
-
except FileNotFoundError:
|
| 467 |
-
_warn_ingest_skipped(project, run_dir, "pnpm ์ ์ฐพ์ ์ ์์ต๋๋ค")
|
| 468 |
-
return
|
| 469 |
-
except subprocess.TimeoutExpired:
|
| 470 |
-
_warn_ingest_skipped(project, run_dir, f"{INGEST_TIMEOUT_SECONDS}์ด ์์ ๋๋์ง ์์์ต๋๋ค")
|
| 471 |
-
return
|
| 472 |
-
except OSError as e:
|
| 473 |
-
_warn_ingest_skipped(project, run_dir, str(e))
|
| 474 |
-
return
|
| 475 |
-
|
| 476 |
-
if result.returncode == 0:
|
| 477 |
-
click.secho(f" evalhub ์ ์ฌ ์๋ฃ โ {project}", fg="green")
|
| 478 |
-
return
|
| 479 |
-
_warn_ingest_skipped(project, run_dir, _ingest_failure_reason(result))
|
| 480 |
-
|
| 481 |
-
|
| 482 |
-
def _ingest_failure_reason(result: subprocess.CompletedProcess[str]) -> str:
|
| 483 |
-
"""ingest ์คํจ ์ถ๋ ฅ์์ ์ฌ๋์ด ์ฝ์ ํ ์ค์ ๋ฝ๋๋ค.
|
| 484 |
|
| 485 |
-
|
| 486 |
-
|
| 487 |
-
"""
|
| 488 |
-
|
| 489 |
-
|
| 490 |
-
|
| 491 |
-
|
| 492 |
-
|
| 493 |
-
|
| 494 |
-
|
| 495 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 496 |
|
| 497 |
|
| 498 |
-
def
|
| 499 |
-
"""
|
| 500 |
-
|
| 501 |
-
|
| 502 |
-
|
| 503 |
-
|
| 504 |
-
|
|
|
|
|
|
|
|
|
|
| 505 |
|
| 506 |
|
| 507 |
def _warn_if_incomplete(run_dir: Path) -> None:
|
|
@@ -535,141 +348,6 @@ def _warn_if_incomplete(run_dir: Path) -> None:
|
|
| 535 |
)
|
| 536 |
|
| 537 |
|
| 538 |
-
def _run_with_progress(
|
| 539 |
-
runner,
|
| 540 |
-
run_id,
|
| 541 |
-
project_config,
|
| 542 |
-
task_config,
|
| 543 |
-
prompt,
|
| 544 |
-
max_workers,
|
| 545 |
-
limit,
|
| 546 |
-
completed_results: list[dict[str, Any]] | None = None,
|
| 547 |
-
):
|
| 548 |
-
"""Execute a run with Rich progress bar display.
|
| 549 |
-
|
| 550 |
-
`completed_results` ๋ resume ์ด ์ด๋ฏธ ๋๋ ์ํ์ ๋ค์ ๋๋ฆฌ์ง ์๊ฒ ํ๋ ๋ค๋ฆฌ๋ค --
|
| 551 |
-
์ฌ๊ธฐ์ ์ ๋๊ธฐ๋ฉด runner ๊ฐ ํ๋ผ๋ฏธํฐ๋ฅผ ๊ฐ๊ณ ์์ด๋ ์์ฉ์ด ์๋ค(F2).
|
| 552 |
-
|
| 553 |
-
`execute_run` ์ ์น๋ช
์ ์คํจ์์ raise ํ๋ฏ๋ก ์ด ํจ์๋ raise ํ๋ค --
|
| 554 |
-
์ผ๋ถ๋ฌ ๊ฐ์ธ์ง ์๋๋ค. ์์ชฝ `on_progress` ์ "error" ์ด๋ฒคํธ๊ฐ ์ด๋ฏธ ์ฌ๋์ด ์ฝ์
|
| 555 |
-
๋ฉ์์ง๋ฅผ ์ฐ์๊ณ , ์ฌ๊ธฐ์ ๋ ๊ฐ์ธ๋ฉด ๊ฐ์ ๋ด์ฉ์ด ๋ ๋ฒ ๋์จ๋ค.
|
| 556 |
-
"""
|
| 557 |
-
import logging as _logging
|
| 558 |
-
|
| 559 |
-
from rich.console import Console
|
| 560 |
-
from rich.live import Live
|
| 561 |
-
from rich.text import Text
|
| 562 |
-
|
| 563 |
-
console = Console(stderr=True)
|
| 564 |
-
use_rich = True
|
| 565 |
-
live_ctx: Live | None = None
|
| 566 |
-
|
| 567 |
-
progress_bar = Progress(
|
| 568 |
-
SpinnerColumn(),
|
| 569 |
-
TextColumn("[progress.description]{task.description}"),
|
| 570 |
-
BarColumn(),
|
| 571 |
-
TextColumn("{task.completed}/{task.total}"),
|
| 572 |
-
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
| 573 |
-
TimeElapsedColumn(),
|
| 574 |
-
)
|
| 575 |
-
|
| 576 |
-
try:
|
| 577 |
-
live_ctx = Live(progress_bar, console=console, refresh_per_second=8)
|
| 578 |
-
live_ctx.start()
|
| 579 |
-
except Exception:
|
| 580 |
-
use_rich = False
|
| 581 |
-
|
| 582 |
-
# Redirect pipeline/runner logs above the progress bar
|
| 583 |
-
class _LiveLogHandler(_logging.Handler):
|
| 584 |
-
def emit(self, record):
|
| 585 |
-
if live_ctx:
|
| 586 |
-
live_ctx.console.print(Text(record.getMessage(), style="dim"))
|
| 587 |
-
|
| 588 |
-
_log_names = (
|
| 589 |
-
"solar_eval.pipelines.pipeline",
|
| 590 |
-
"solar_eval.pipelines.steps.llm",
|
| 591 |
-
"solar_eval.core.runner",
|
| 592 |
-
"solar_eval.providers.upstage",
|
| 593 |
-
)
|
| 594 |
-
if use_rich:
|
| 595 |
-
_live_handler = _LiveLogHandler()
|
| 596 |
-
for name in _log_names:
|
| 597 |
-
lg = _logging.getLogger(name)
|
| 598 |
-
lg.addHandler(_live_handler)
|
| 599 |
-
lg.propagate = False
|
| 600 |
-
_logging.getLogger("httpx").setLevel(_logging.WARNING)
|
| 601 |
-
|
| 602 |
-
def _cleanup_live():
|
| 603 |
-
if use_rich:
|
| 604 |
-
live_ctx.stop()
|
| 605 |
-
for name in _log_names:
|
| 606 |
-
_logging.getLogger(name).removeHandler(_live_handler)
|
| 607 |
-
_logging.getLogger(name).propagate = True
|
| 608 |
-
|
| 609 |
-
inference_task_id = None
|
| 610 |
-
eval_task_id = None
|
| 611 |
-
|
| 612 |
-
async def on_progress(event):
|
| 613 |
-
nonlocal inference_task_id, eval_task_id
|
| 614 |
-
etype = event.get("type")
|
| 615 |
-
if etype == "dataset_loaded":
|
| 616 |
-
total = event["total"]
|
| 617 |
-
if use_rich:
|
| 618 |
-
inference_task_id = progress_bar.add_task("Inference", total=total)
|
| 619 |
-
else:
|
| 620 |
-
click.secho(f" Dataset loaded: {total} samples", fg="blue")
|
| 621 |
-
elif etype == "inference_progress":
|
| 622 |
-
if use_rich and inference_task_id is not None:
|
| 623 |
-
progress_bar.update(inference_task_id, completed=event["completed"])
|
| 624 |
-
elif not use_rich:
|
| 625 |
-
c, t = event["completed"], event["total"]
|
| 626 |
-
if c % 10 == 0 or c == t:
|
| 627 |
-
click.echo(f" Inference: {c}/{t}")
|
| 628 |
-
elif etype == "inference_complete":
|
| 629 |
-
if use_rich and inference_task_id is not None:
|
| 630 |
-
progress_bar.update(inference_task_id, completed=event["total"])
|
| 631 |
-
else:
|
| 632 |
-
click.secho(f" Inference complete: {event['total']} samples", fg="green")
|
| 633 |
-
elif etype == "evaluation_start":
|
| 634 |
-
total = event.get("total", 0)
|
| 635 |
-
if use_rich:
|
| 636 |
-
eval_task_id = progress_bar.add_task("Evaluation", total=total or 1)
|
| 637 |
-
else:
|
| 638 |
-
click.secho(" Starting evaluation...", fg="blue")
|
| 639 |
-
elif etype == "evaluation_progress":
|
| 640 |
-
if use_rich and eval_task_id is not None:
|
| 641 |
-
progress_bar.update(eval_task_id, completed=event["completed"])
|
| 642 |
-
elif etype == "done":
|
| 643 |
-
if use_rich and eval_task_id is not None:
|
| 644 |
-
progress_bar.update(eval_task_id, completed=progress_bar.tasks[eval_task_id].total)
|
| 645 |
-
_cleanup_live()
|
| 646 |
-
click.echo()
|
| 647 |
-
click.secho("Run completed!", fg="green", bold=True)
|
| 648 |
-
overall = event.get("overall_score")
|
| 649 |
-
if overall is not None:
|
| 650 |
-
click.echo(f" Overall Score: {overall:.4f}")
|
| 651 |
-
scores = event.get("scores", {})
|
| 652 |
-
if scores:
|
| 653 |
-
click.echo(f" Scores: {format_scores(scores)}")
|
| 654 |
-
elif etype == "error":
|
| 655 |
-
_cleanup_live()
|
| 656 |
-
click.echo()
|
| 657 |
-
click.secho(f" Run failed: {event.get('error')}", fg="red", bold=True)
|
| 658 |
-
|
| 659 |
-
asyncio.run(
|
| 660 |
-
runner.execute_run(
|
| 661 |
-
run_id=run_id,
|
| 662 |
-
project_config=project_config,
|
| 663 |
-
task_config=task_config,
|
| 664 |
-
prompt=prompt,
|
| 665 |
-
on_progress=on_progress,
|
| 666 |
-
max_workers=max_workers,
|
| 667 |
-
limit=limit,
|
| 668 |
-
completed_results=completed_results,
|
| 669 |
-
)
|
| 670 |
-
)
|
| 671 |
-
|
| 672 |
-
|
| 673 |
@runs_group.command("batch")
|
| 674 |
@click.argument("project")
|
| 675 |
@click.option("--task", "-t", required=True, help="Task name")
|
|
@@ -684,331 +362,101 @@ def _run_with_progress(
|
|
| 684 |
)
|
| 685 |
@click.option("--reasoning-effort", "-re", default=None, help="Reasoning effort")
|
| 686 |
@click.option("--max-workers", "-w", default=5, type=int, help="Max parallel workers")
|
|
|
|
|
|
|
|
|
|
| 687 |
@click.pass_context
|
| 688 |
-
def batch_runs(ctx, project, task, prompts,
|
| 689 |
-
"""Run evaluation for multiple prompts sequentially.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 690 |
cfg = ctx.obj["config"]
|
| 691 |
if cfg.is_remote:
|
| 692 |
raise click.ClickException("Batch is local-mode only")
|
| 693 |
|
| 694 |
prompt_list = [p.strip() for p in prompts.split(",")]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 695 |
click.secho(f"Batch run: {project}/{task} โ prompts: {prompt_list}", fg="blue", bold=True)
|
| 696 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 697 |
for pname in prompt_list:
|
| 698 |
click.secho(f"\n{'=' * 50}", fg="blue")
|
| 699 |
click.secho(f"Running prompt: {pname}...", fg="blue", bold=True)
|
| 700 |
click.secho(f"{'=' * 50}", fg="blue")
|
| 701 |
try:
|
| 702 |
-
|
| 703 |
-
|
| 704 |
-
|
| 705 |
-
|
| 706 |
-
|
| 707 |
-
|
| 708 |
-
|
| 709 |
-
|
| 710 |
-
|
| 711 |
-
|
| 712 |
-
|
| 713 |
-
|
| 714 |
)
|
| 715 |
except Exception as e:
|
|
|
|
| 716 |
click.secho(f" {pname} failed: {e}", fg="red")
|
| 717 |
-
|
| 718 |
-
|
| 719 |
-
click.secho("Batch complete!", fg="green", bold=True)
|
| 720 |
|
| 721 |
|
| 722 |
-
|
| 723 |
-
|
| 724 |
-
|
| 725 |
-
|
| 726 |
-
@click.pass_context
|
| 727 |
-
def resume_run(ctx: click.Context, project: str, run_id: str, max_workers: int) -> None:
|
| 728 |
-
"""Resume an interrupted evaluation run."""
|
| 729 |
-
cfg = ctx.obj["config"]
|
| 730 |
-
if cfg.is_remote:
|
| 731 |
-
raise click.ClickException("Resume is local-mode only")
|
| 732 |
-
|
| 733 |
-
from solar_eval.core.dataset_loader import DatasetLoader
|
| 734 |
-
from solar_eval.core.project_loader import load_all_project_configs
|
| 735 |
-
from solar_eval.core.runner import BatchRunner
|
| 736 |
-
from solar_eval.providers import UpstageProvider
|
| 737 |
-
from solar_eval.providers.openai_provider import OpenAIProvider
|
| 738 |
-
from solar_eval.stores import JsonlStore
|
| 739 |
-
|
| 740 |
-
# Find the run directory
|
| 741 |
-
run_dir = cfg.artifacts_dir(project) / run_id
|
| 742 |
-
if not run_dir.exists():
|
| 743 |
-
raise click.ClickException(f"Run directory not found: {run_dir}")
|
| 744 |
-
|
| 745 |
-
store = JsonlStore(run_dir)
|
| 746 |
-
run_meta = store.load_run_meta()
|
| 747 |
-
|
| 748 |
-
if not run_meta:
|
| 749 |
-
raise click.ClickException(f"No run.json found in {run_dir}")
|
| 750 |
-
|
| 751 |
-
status = run_meta.get("status", "unknown")
|
| 752 |
-
if status == "completed":
|
| 753 |
-
raise click.ClickException(f"Run {run_id} is already completed. Nothing to resume.")
|
| 754 |
-
|
| 755 |
-
task = run_meta.get("task")
|
| 756 |
-
if not task:
|
| 757 |
-
raise click.ClickException("Cannot determine task from run.json")
|
| 758 |
-
|
| 759 |
-
# Load existing results
|
| 760 |
-
existing_results = store.load_existing_results()
|
| 761 |
-
completed_indices = {r["sample_idx"] for r in existing_results}
|
| 762 |
-
click.secho(f"Resuming run: {run_id}", fg="blue", bold=True)
|
| 763 |
-
click.secho(f" Task: {task}, Already completed: {len(completed_indices)} samples", fg="blue")
|
| 764 |
-
|
| 765 |
-
# Load project/task config
|
| 766 |
-
configs = load_all_project_configs(cfg.projects_dir, cfg.config_dirs)
|
| 767 |
-
project_config = next((c for c in configs if c["name"] == project), None)
|
| 768 |
-
if not project_config:
|
| 769 |
-
raise click.ClickException(f"Project not found: {project}")
|
| 770 |
-
|
| 771 |
-
task_config = next((t for t in project_config.get("tasks", []) if t["name"] == task), None)
|
| 772 |
-
if not task_config:
|
| 773 |
-
raise click.ClickException(f"Task '{task}' not found in project '{project}'")
|
| 774 |
-
|
| 775 |
-
# Resolve prompt from run.json
|
| 776 |
-
prompt_name = run_meta.get("prompt")
|
| 777 |
-
pipeline_name = run_meta.get("pipeline")
|
| 778 |
-
model = run_meta.get("model", "solar-pro2")
|
| 779 |
-
|
| 780 |
-
if not prompt_name:
|
| 781 |
-
raise click.ClickException(
|
| 782 |
-
"Cannot determine prompt from run.json. Only runs created with the new CLI can be resumed."
|
| 783 |
-
)
|
| 784 |
|
| 785 |
-
|
| 786 |
-
|
| 787 |
-
|
| 788 |
-
|
| 789 |
-
|
| 790 |
-
|
| 791 |
-
|
| 792 |
-
|
| 793 |
-
|
| 794 |
-
|
| 795 |
-
|
| 796 |
-
|
| 797 |
-
"temperature": run_meta.get("temperature", 0.0),
|
| 798 |
-
"max_tokens": run_meta.get("max_tokens", 4000),
|
| 799 |
-
}
|
| 800 |
-
else:
|
| 801 |
-
prompt = {
|
| 802 |
-
"messages": load_prompt_messages(prompt_path),
|
| 803 |
-
"model": model,
|
| 804 |
-
"temperature": run_meta.get("temperature", 0.0),
|
| 805 |
-
"max_tokens": run_meta.get("max_tokens", 4000),
|
| 806 |
-
}
|
| 807 |
-
if run_meta.get("reasoning_effort"):
|
| 808 |
-
prompt["reasoning_effort"] = run_meta["reasoning_effort"]
|
| 809 |
-
|
| 810 |
-
# Override pipeline if specified โ ์๋ run ์ด `--set` ์ผ๋ก ๋ฐ๊ฟ ๋๋ฆฐ ๊ฒ์ด๋ฉด
|
| 811 |
-
# ๊ทธ override ๊น์ง ๋ณต์ํด์ผ ๊ฐ์ ์ค์ ์ผ๋ก ์ด์ด์ง๋ค.
|
| 812 |
-
if pipeline_name and pipeline_name != "default":
|
| 813 |
-
task_config = {
|
| 814 |
-
**task_config,
|
| 815 |
-
"pipeline_config": _compose_pipeline_config(
|
| 816 |
-
cfg.config_dir(project),
|
| 817 |
-
pipeline_name,
|
| 818 |
-
run_meta.get("pipeline_overrides") or {},
|
| 819 |
-
),
|
| 820 |
-
}
|
| 821 |
-
|
| 822 |
-
click.secho(f" Model: {model}", fg="blue")
|
| 823 |
-
|
| 824 |
-
# ์ด์ ํ๊ฐ๋ฅผ ๋ฏธ๋ฆฌ ์ง์ฐ์ง ์๋๋ค(F3) -- ์ฌํ๊ฐ๊ฐ ๋์ค์ ์ฃฝ์ผ๋ฉด ๋ณต๊ตฌํ ์ ์๊ฐ
|
| 825 |
-
# ์์ด์ง๋ค. create_evaluation() ์ด ์์์ ๊ต์ฒด๋ผ ์ง์ฐ์ง ์์๋ ์์ ํ๋ค.
|
| 826 |
-
|
| 827 |
-
# Setup providers and runner
|
| 828 |
-
provider = UpstageProvider()
|
| 829 |
-
judge_provider = OpenAIProvider()
|
| 830 |
-
dataset_loader = DatasetLoader(base_dir=cfg.projects_dir)
|
| 831 |
-
runner = BatchRunner(
|
| 832 |
-
store=store,
|
| 833 |
-
inference_provider=provider,
|
| 834 |
-
judge_provider=judge_provider,
|
| 835 |
-
dataset_loader=dataset_loader,
|
| 836 |
-
)
|
| 837 |
-
|
| 838 |
-
_run_with_progress(
|
| 839 |
-
runner,
|
| 840 |
-
run_id,
|
| 841 |
-
project_config,
|
| 842 |
-
task_config,
|
| 843 |
-
prompt,
|
| 844 |
-
max_workers,
|
| 845 |
-
limit=None,
|
| 846 |
-
# ์ด๋ฏธ ๋๋ ์ํ์ ๊ฑด๋๋ด๋ค -- ์ ๋๊ธฐ๋ฉด resume ์ด ์ ์ํ์ ๋ค์ ์ ๋ฃ ํธ์ถํ๊ณ
|
| 847 |
-
# results.jsonl ์ ๊ฐ์ sample_idx ๋ฅผ ์ค๋ณต append ํ๋ค(F2).
|
| 848 |
-
completed_results=existing_results,
|
| 849 |
-
)
|
| 850 |
-
|
| 851 |
-
click.echo(f"\nResults saved to: {run_dir}/")
|
| 852 |
-
|
| 853 |
-
|
| 854 |
-
@runs_group.command("eval")
|
| 855 |
-
@click.argument("project")
|
| 856 |
-
@click.option(
|
| 857 |
-
"--run-id",
|
| 858 |
-
"-r",
|
| 859 |
-
required=True,
|
| 860 |
-
help="Run ID to evaluate",
|
| 861 |
-
)
|
| 862 |
-
@click.pass_context
|
| 863 |
-
def eval_run(ctx: click.Context, project: str, run_id: str) -> None:
|
| 864 |
-
"""Run evaluation only on existing inference results (no new inference)."""
|
| 865 |
-
cfg = ctx.obj["config"]
|
| 866 |
-
if cfg.is_remote:
|
| 867 |
-
raise click.ClickException("Eval is local-mode only")
|
| 868 |
-
|
| 869 |
-
from solar_eval.core.project_loader import load_all_project_configs
|
| 870 |
-
from solar_eval.core.runner import build_eval_sample_from_result
|
| 871 |
-
from solar_eval.evaluators.registry import create_evaluator
|
| 872 |
-
from solar_eval.stores import JsonlStore
|
| 873 |
-
|
| 874 |
-
# Find run directory
|
| 875 |
-
run_dir = cfg.artifacts_dir(project) / run_id
|
| 876 |
-
if not run_dir.exists():
|
| 877 |
-
raise click.ClickException(f"Run directory not found: {run_dir}")
|
| 878 |
-
|
| 879 |
-
store = JsonlStore(run_dir)
|
| 880 |
-
run_meta = store.load_run_meta()
|
| 881 |
-
if not run_meta:
|
| 882 |
-
raise click.ClickException(f"No run.json found in {run_dir}")
|
| 883 |
-
|
| 884 |
-
task = run_meta.get("task")
|
| 885 |
-
if not task:
|
| 886 |
-
raise click.ClickException("Cannot determine task from run.json")
|
| 887 |
-
|
| 888 |
-
# Load results
|
| 889 |
-
results = store.load_existing_results()
|
| 890 |
-
if not results:
|
| 891 |
-
raise click.ClickException(f"No inference results found in {run_dir}/results.jsonl")
|
| 892 |
-
|
| 893 |
-
click.secho(f"Evaluating run: {run_id}", fg="blue", bold=True)
|
| 894 |
-
click.secho(f" Task: {task}, Samples: {len(results)}", fg="blue")
|
| 895 |
-
|
| 896 |
-
# Load task config for evaluator settings
|
| 897 |
-
configs = load_all_project_configs(cfg.projects_dir, cfg.config_dirs)
|
| 898 |
-
project_config = next((c for c in configs if c["name"] == project), None)
|
| 899 |
-
if not project_config:
|
| 900 |
-
raise click.ClickException(f"Project not found: {project}")
|
| 901 |
-
|
| 902 |
-
task_config = next((t for t in project_config.get("tasks", []) if t["name"] == task), None)
|
| 903 |
-
if not task_config:
|
| 904 |
-
raise click.ClickException(f"Task '{task}' not found in project '{project}'")
|
| 905 |
-
|
| 906 |
-
# ์ด์ ํ๊ฐ๋ฅผ ๋ฏธ๋ฆฌ ์ง์ฐ์ง ์๋๋ค(F3) -- ์๋ ์ฌํ๊ฐ๊ฐ ์คํจํด๋ ์ ์ ์๋ ๋จ๋๋ค.
|
| 907 |
-
|
| 908 |
-
# Run evaluation
|
| 909 |
-
evaluator = create_evaluator(task_config.get("evaluator", {"type": "llm_judge"}))
|
| 910 |
-
# judge provider ๋ฅผ ์ ๋๊ธฐ๋ฉด llm_judge ๊ณ์ด์ ์ ์ํ์์ ์คํจํ๋ค -- ๊ทธ๋ฆฌ๊ณ
|
| 911 |
-
# ๊ทธ ์คํจ๊ฐ ์์ ์ 0์ placeholder ๋ก ๋ฎ์ฌ ์กฐ์ฉํ completed ๋ก ๋๋ฌ๋ค(F8).
|
| 912 |
-
from solar_eval.providers.openai_provider import OpenAIProvider
|
| 913 |
-
|
| 914 |
-
judge_provider = OpenAIProvider()
|
| 915 |
-
|
| 916 |
-
from datetime import timezone
|
| 917 |
-
|
| 918 |
-
async def run_eval():
|
| 919 |
-
# ์ฑ์ ์คํจ๋ ์ ์๊ฐ ์๋๋ผ ์คํจ๋ก ๋จ๊ธด๋ค -- aggregate() ์๋ ์ฑ๊ณต๋ถ๋ง ๋๊ธด๋ค.
|
| 920 |
-
eval_results: list[dict[str, Any]] = []
|
| 921 |
-
eval_failures: list[dict[str, Any]] = []
|
| 922 |
-
|
| 923 |
-
for result in results:
|
| 924 |
-
# ๋ฆฌ์คํธ ์์น๊ฐ ์๋๋ผ ์๋ณธ sample_idx ๊ฐ ์ ๋ณธ์ด๋ค(F6).
|
| 925 |
-
sample_idx = result["sample_idx"]
|
| 926 |
-
try:
|
| 927 |
-
eval_sample = build_eval_sample_from_result(result, reference=result.get("golden"))
|
| 928 |
-
evaluator.validate_required_fields(eval_sample)
|
| 929 |
-
eval_result = await evaluator.evaluate(sample=eval_sample, provider=judge_provider)
|
| 930 |
-
eval_results.append({**eval_result, "sample_idx": sample_idx})
|
| 931 |
-
except Exception as e:
|
| 932 |
-
click.secho(f" Sample {sample_idx} eval failed: {e}", fg="yellow")
|
| 933 |
-
eval_failures.append({"sample_idx": sample_idx, "error": str(e)})
|
| 934 |
-
|
| 935 |
-
# Aggregate and save
|
| 936 |
-
aggregated = evaluator.aggregate(eval_results)
|
| 937 |
-
# ์ฑ๊ณตํ ์ฑ์ ์ด ํ๋๋ ์์ผ๋ฉด ์ ์ ์๋ฆฌ๋ฅผ ๋น์ด๋ค -- 0.0 ์ "0์ "์ด๋ผ๋ ์ธก์ ๊ฐ์ด๋ผ
|
| 938 |
-
# "์ธก์ ์ด ์์๋ค"์ ๊ตฌ๋ถ๋์ง ์๋๋ค(runner.py ์ ๊ฐ์ ๊ท์น).
|
| 939 |
-
overall_score = aggregated.get("overall_score", 0.0) if eval_results else None
|
| 940 |
-
eval_id = await store.create_evaluation(
|
| 941 |
-
{
|
| 942 |
-
"run_id": run_id,
|
| 943 |
-
"scores": aggregated.get("scores", {}),
|
| 944 |
-
"overall_score": overall_score,
|
| 945 |
-
"eval_model": "local",
|
| 946 |
-
"eval_success_count": len(eval_results),
|
| 947 |
-
"eval_failed_count": len(eval_failures),
|
| 948 |
-
"failed_sample_indices": [f["sample_idx"] for f in eval_failures],
|
| 949 |
-
}
|
| 950 |
-
)
|
| 951 |
-
|
| 952 |
-
# Save per-sample eval details -- ์ฑ๊ณต/์คํจ ๋ ๋ค ํ ํ์ฉ ๋จ๊ธด๋ค.
|
| 953 |
-
eval_detail_docs = []
|
| 954 |
-
for er in eval_results:
|
| 955 |
-
eval_detail_docs.append(
|
| 956 |
-
{
|
| 957 |
-
"evaluation_id": eval_id,
|
| 958 |
-
"sample_idx": er["sample_idx"],
|
| 959 |
-
"category_scores": er.get("category_scores", {}),
|
| 960 |
-
"error_counts": {
|
| 961 |
-
k: v.get("error_count", 0)
|
| 962 |
-
for k, v in er.get("details", {}).items()
|
| 963 |
-
if isinstance(v, dict)
|
| 964 |
-
},
|
| 965 |
-
"severity": er.get("severity", ""),
|
| 966 |
-
"score": er.get("score", 0.0),
|
| 967 |
-
}
|
| 968 |
-
)
|
| 969 |
-
for f in eval_failures:
|
| 970 |
-
eval_detail_docs.append(
|
| 971 |
-
{
|
| 972 |
-
"evaluation_id": eval_id,
|
| 973 |
-
"sample_idx": f["sample_idx"],
|
| 974 |
-
"category_scores": {},
|
| 975 |
-
"error_counts": {},
|
| 976 |
-
"severity": None,
|
| 977 |
-
# 0.0 ์ด ์๋๋ผ None -- ์ฑ์ ์คํจ๋ฅผ ์ต์ ์ ์์ ๊ตฌ๋ถํ๋ค.
|
| 978 |
-
"score": None,
|
| 979 |
-
"error": f["error"],
|
| 980 |
-
}
|
| 981 |
-
)
|
| 982 |
-
await store.insert_eval_details(eval_detail_docs)
|
| 983 |
-
|
| 984 |
-
# Update run status
|
| 985 |
-
await store.update_run(
|
| 986 |
-
run_id,
|
| 987 |
-
{
|
| 988 |
-
"status": "completed",
|
| 989 |
-
"completed_at": datetime.now(timezone.utc),
|
| 990 |
-
},
|
| 991 |
-
)
|
| 992 |
-
|
| 993 |
-
return {**aggregated, "overall_score": overall_score, "failed": len(eval_failures)}
|
| 994 |
-
|
| 995 |
-
aggregated = asyncio.run(run_eval())
|
| 996 |
-
|
| 997 |
-
click.echo()
|
| 998 |
-
click.secho("Evaluation completed!", fg="green", bold=True)
|
| 999 |
-
overall = aggregated.get("overall_score")
|
| 1000 |
-
if overall is None:
|
| 1001 |
-
# ์ฑ์ ์ด ํ ๊ฑด๋ ์ฑ๊ณตํ์ง ๋ชปํ๋ค -- ์ซ์๋ฅผ ์ฐ์ผ๋ฉด "0์ "์ผ๋ก ์ฝํ๋ค.
|
| 1002 |
-
click.secho(" Overall Score: ์ธก์ ์์ (์ ์ํ ์ฑ์ ์คํจ)", fg="red", bold=True)
|
| 1003 |
-
else:
|
| 1004 |
-
click.echo(f" Overall Score: {overall:.4f}")
|
| 1005 |
-
failed = aggregated.get("failed", 0)
|
| 1006 |
-
if failed:
|
| 1007 |
-
click.secho(f" ์ฑ์ ์คํจ: {failed}๊ฑด (score: null ๋ก ๊ธฐ๋ก๋จ)", fg="yellow")
|
| 1008 |
-
scores = aggregated.get("scores", {})
|
| 1009 |
-
if scores:
|
| 1010 |
-
click.echo(f" Scores: {format_scores(scores)}")
|
| 1011 |
-
click.echo(f"\nResults saved to: {run_dir}/")
|
| 1012 |
|
| 1013 |
|
| 1014 |
@runs_group.command("results")
|
|
@@ -1048,3 +496,8 @@ def show_results(ctx: click.Context, run_ref: str, limit: int | None) -> None:
|
|
| 1048 |
results = results[:limit]
|
| 1049 |
click.secho(format_results_table(results), fg="blue")
|
| 1050 |
click.echo(f"\nShowing {len(results)} result(s)")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Run management commands โ local execution.
|
| 2 |
+
|
| 3 |
+
`resume`ยท`eval` ๋ช
๋ น๊ณผ ์งํ ํ์ยทrun ์ค๋น(run.json ๋ฉํ) ๋ 800์ค ์ํ ๋๋ฌธ์ ์ ๋ชจ๋๋ก ๋ผ์ด ๋๋ค
|
| 4 |
+
(๊ณํ 2026-09-24-phase1a E1): `run_resume`ยท`run_eval`ยท`run_progress`ยท`run_meta`. ์ด ๋ชจ๋์ `list`ยท`start`ยท
|
| 5 |
+
`batch`ยท`results` ์ run ๋๋ ํ ๋ฆฌ ํ ๋น์ ๋งก๋๋ค. run ์ด ๋๋ ๋ค์ ์ ์ฌ๋ `solar_eval.hub_hook` ํ
์ด ํ๋ค
|
| 6 |
+
(๊ณํ 2026-09-27 L6a โ ๋ฒ๋ค ๋ค๋ฆฌ โ hub ๋ค๋ฆฌ, ๋ ๊ฑฐ์ ์ ์ฌ๋ ๋ถ๋ฅด์ง ์๋๋ค: ๋ง์คํฐ D-M2-4 (b)).
|
| 7 |
+
"""
|
| 8 |
|
|
|
|
| 9 |
import json
|
|
|
|
| 10 |
import time
|
| 11 |
+
from dataclasses import replace
|
| 12 |
from datetime import datetime
|
| 13 |
from pathlib import Path
|
| 14 |
from typing import Any
|
| 15 |
|
| 16 |
import click
|
|
|
|
| 17 |
|
| 18 |
from solar_eval.cli.commands.projects import _resolve_project_id
|
| 19 |
+
from solar_eval.cli.commands.run_eval import eval_run
|
| 20 |
+
from solar_eval.cli.commands.run_meta import find_task, preflight_judge_config, start_options
|
| 21 |
+
from solar_eval.cli.commands.run_meta import resolve_prompt as _resolve_prompt # noqa: F401 -- prompts.py ๊ฐ ์ด๋ค
|
| 22 |
+
from solar_eval.cli.commands.run_progress import execute_plan
|
| 23 |
+
from solar_eval.cli.commands.run_resume import resume_run
|
| 24 |
+
from solar_eval.cli.formatters import format_results_table, format_runs_table
|
| 25 |
+
from solar_eval.hub_hook import HookOptions, HookResult, exit_code_for, finish_batch, run_hook
|
| 26 |
+
from solar_eval.hub_hook.console import echo_batch_summary
|
| 27 |
+
from solar_eval.hub_hook.optin import strict_requested
|
| 28 |
+
from solar_eval.hub_hook.target import is_smoke_run
|
| 29 |
+
from solar_eval.hub_hook.types import (
|
| 30 |
+
BATCH_PENDING,
|
| 31 |
+
COMMAND_BATCH,
|
| 32 |
+
LEG_BUNDLE,
|
| 33 |
+
LEG_HUB,
|
| 34 |
+
NO_BUNDLE,
|
| 35 |
+
HookMode,
|
| 36 |
+
LegResult,
|
| 37 |
+
bundle_ready,
|
| 38 |
+
)
|
| 39 |
|
| 40 |
|
| 41 |
@click.group("runs")
|
|
|
|
| 43 |
"""Manage evaluation runs."""
|
| 44 |
|
| 45 |
|
| 46 |
+
#: `--where` ์ต์
๋์๋ง โ startยทbatch ๊ฐ ๊ฐ์ ๋ฌธ๋ฒ์ ์ด๋ค(๊ณํ D1).
|
| 47 |
+
WHERE_HELP = (
|
| 48 |
+
"ํ ํํฐ key=v1,v2 (๋ฐ๋ณต ๊ฐ๋ฅ, AND). tags ๋ ๋ชจ๋ ๊ฐ์ง ํ, category ๋ ๋ชฉ๋ก ์ค ํ๋. "
|
| 49 |
+
"์: --where tags=yul-ryul --where category=a,b"
|
| 50 |
+
)
|
| 51 |
+
|
| 52 |
+
#: `--no-ingest`ยท`--strict-ingest` ๋์๋ง โ startยทresume ์ด ๊ฐ์ ๋ป์ผ๋ก ์ด๋ค(๊ณํ L6 ยง2.2).
|
| 53 |
+
NO_INGEST_HELP = (
|
| 54 |
+
"run ์ด ๋๋ ๋ค hub ํ
(๋ฒ๋คยทhub ์ ์ฌ)์ ํต์งธ๋ก ๊ฑด๋๋ด๋ค โ ๋์ด๋ฆฌ๋ ๋ช
๋ น์ ํ ์ค๋ก ์๋ ค ์ค๋ค"
|
| 55 |
+
)
|
| 56 |
+
STRICT_INGEST_HELP = (
|
| 57 |
+
"hub ํ
๋ค๋ฆฌ๊ฐ ํ๋๋ผ๋ ์คํจํ๋ฉด ์ข
๋ฃ ์ฝ๋ 3 (๊ธฐ๋ณธ์ 0 โ run ๊ฒฐ๊ณผ๋ ๋์คํฌ์ ์๋ค). "
|
| 58 |
+
"SOLAR_EVAL_STRICT_INGEST=1 ๊ณผ ๊ฐ๋ค"
|
| 59 |
+
)
|
| 60 |
+
|
| 61 |
+
#: Experiment Hub ๋ฌถ์ ์ง๋ฌธ์ด ์ ํ์ ๋ณด์ง ์๋๋ค๋ ๊ฒฝ๊ณ (๊ณํ D19 ยท ยง11-2).
|
| 62 |
+
FILTERED_RUN_EXPERIMENT_HUB_WARNING = (
|
| 63 |
+
" โ Experiment Hub ๋ ์์ง ํํฐ run ์ ๊ฐ์ ํ์คํฌ ์ ์ฒด์
์ '๊ฐ์ ์ค์ ๋ฐ๋ณต' ๊ณผ ๊ตฌ๋ถํ์ง ๋ชปํฉ๋๋ค "
|
| 64 |
+
"(ยง11-2) โ ์ด run ์ ์ ์๋ ์ CLI ์ถ๋ ฅ๊ณผ note ๋ก ์ฝ์ผ์ธ์."
|
| 65 |
+
)
|
| 66 |
+
|
| 67 |
+
#: rubric judge ํ์คํฌ์ `runs start` ๋ค ์๋ด(๊ณํ 2026-09-28 L12 D-L12-6) โ in-run ์ ๋ ๊ฑฐ์ ์ฑ์ ๋ง, ์ฑ์ ๋ฒ๋ค์ `runs eval` ์ด ๋ธ๋ค.
|
| 68 |
+
JUDGE_BUNDLE_HINT = "judge ์ฑ์ ๋ฒ๋ค์ `solar-eval runs eval {project} -r {run}` ์ด ๋ฐํํ๋ค"
|
| 69 |
+
|
| 70 |
+
|
| 71 |
@runs_group.command("list")
|
| 72 |
@click.argument("project")
|
| 73 |
@click.option("--task", "-t", default=None, help="Filter by task name")
|
|
|
|
| 145 |
@click.option(
|
| 146 |
"--note",
|
| 147 |
default=None,
|
| 148 |
+
help="์ด run ์ ์ ๋๋ฆฌ๋์ง ํ ์ค (run.json ์ ๊ธฐ๋ก๋๊ณ Experiment Hub ๊ฐ ๊ทธ๋๋ก ์ฝ๋๋ค)",
|
| 149 |
)
|
| 150 |
@click.option(
|
| 151 |
"--set",
|
|
|
|
| 157 |
"์: --set steps.basic_correction.reasoning_effort=high. ์ฌ๋ฌ ๋ฒ ์ธ ์ ์๋ค."
|
| 158 |
),
|
| 159 |
)
|
| 160 |
+
@click.option("--no-ingest", is_flag=True, help=NO_INGEST_HELP)
|
| 161 |
+
@click.option("--strict-ingest", is_flag=True, help=STRICT_INGEST_HELP)
|
| 162 |
+
@click.option("--where", "where", multiple=True, metavar="KEY=V1,V2", help=WHERE_HELP)
|
|
|
|
|
|
|
| 163 |
@click.pass_context
|
| 164 |
+
def start_run(ctx: click.Context, project: str, **options: Any) -> None:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 165 |
"""Start an evaluation run.
|
| 166 |
|
| 167 |
Example:
|
|
|
|
| 173 |
-p prompt_dev_v23 -m solar-pro3 -P pipeline_dev_v24 \\
|
| 174 |
--set steps.basic_correction.repeat_temperatures='[0.0, 0.5]' \\
|
| 175 |
--note "SC ์จ๋ ํญ ์ค์"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 176 |
|
| 177 |
+
solar-eval runs start chosun-proofreading -t ci-v2-all \\
|
| 178 |
+
-p prompt_prod_251231 -m solar-pro3 --where tags=yul-ryul
|
|
|
|
|
|
|
| 179 |
"""
|
| 180 |
+
# click ์ต์
์ด๋ฆ์ด `_start_local` ํค์๋ ์ธ์ ์ด๋ฆ๊ณผ ๊ฐ๋ค โ ๊ทธ๋๋ก ๋๊ธด๋ค.
|
| 181 |
+
strict = strict_requested(options.pop("strict_ingest"))
|
| 182 |
+
result = _start_local(ctx.obj["config"], project=project, strict_ingest=strict, **options)
|
| 183 |
+
# ํ
์คํจ๋ ๊ธฐ๋ณธ ์ข
๋ฃ 0 โ strict ์์๋ง 3(D-L6-11 ยท ๋ง์คํฐ D-M2-3).
|
| 184 |
+
code = exit_code_for(result, strict)
|
| 185 |
+
if code:
|
| 186 |
+
ctx.exit(code)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 187 |
|
| 188 |
|
| 189 |
def _build_run_name(task: str, pipeline_name: str, prompt_name: str) -> str:
|
|
|
|
| 204 |
๊ฐ์ ์ด์ ๊ฐ์ (task, pipeline, prompt) ์กฐํฉ์ผ๋ก ๋ run ์ด ์์๋๋ฉด
|
| 205 |
`_build_run_name` ์ด ๊ฐ์ ์ด๋ฆ์ ๋ธ๋ค. `mkdir(exist_ok=False)` ๋ฅผ ์ฐ๋ฉด ๋ ํ๋ก์ธ์ค๊ฐ
|
| 206 |
๋์์ ๊ฐ์ ์ด๋ฆ์ผ๋ก mkdir ํด๋ ์ ํํ ํ๋๋ง ์ฑ๊ณตํ๊ณ , ์ง ์ชฝ์ ์ ํ์์คํฌํ๋ก
|
| 207 |
+
์ฌ์๋ํ๋ค โ ํ์ผ ํฌ๋งท ์์ฒด๋ ์ ๋ฐ๊พธ๋ฏ๋ก Experiment Hub ์ run_id ํ์ฑ๊ณผ ํธํ๋๋ค.
|
| 208 |
"""
|
| 209 |
for _ in range(_RUN_DIR_MAX_ATTEMPTS):
|
| 210 |
run_name = _build_run_name(task, pipeline_name, prompt_name)
|
|
|
|
| 217 |
raise click.ClickException(f"Could not allocate a unique run directory under {artifacts_dir}")
|
| 218 |
|
| 219 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 220 |
def _start_local(
|
| 221 |
cfg,
|
| 222 |
+
*,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 223 |
note: str | None = None,
|
|
|
|
| 224 |
no_ingest: bool = False,
|
| 225 |
+
strict_ingest: bool = False,
|
| 226 |
+
hook_options: HookOptions | None = None,
|
| 227 |
+
**plan_options: Any,
|
| 228 |
+
) -> HookResult | None:
|
| 229 |
+
"""run ํ๋๋ฅผ ๋ก์ปฌ์์ ๋๋ฆฐ๋ค (๊ณํ ยง9 G2 ์์).
|
| 230 |
+
|
| 231 |
+
`plan_options` ๋ `run_meta.plan_run` ์ ํค์๋ ์ธ์ ๊ทธ๋๋ก๋ค โ `project`ยท`task`ยท`prompt_name`ยท
|
| 232 |
+
`model`ยท`pipeline_name`ยท`reasoning_effort`ยท`temperature`ยท`max_tokens`ยท`max_workers`ยท`limit`
|
| 233 |
+
(ํ์), `set_options`ยท`where`(์ ํ). ๋ชจ๋ฅด๋ ํค๋ `plan_run` ์ด TypeError ๋ก ๊ฑฐ๋ถํ๋ค.
|
| 234 |
+
|
| 235 |
+
`reject_defaults_where` โ ์ ํ(`--where`ยท`limit`) โ `prepare_dataset` โ `build_task_pipeline`
|
| 236 |
+
โ run.json ๋ฉํ(`dataset`ยท`snapshot` + ์๊ธฐ ๊ฒ์ฆ) โ `_make_run_dir` โ ๋ฌ๋์ ์ค๋น๋ ๋ฐ์ดํฐ์
ยท
|
| 237 |
+
์ ํยทํ์ดํ๋ผ์ธ **๊ฐ์ ์ธ์คํด์ค**๋ฅผ ๋๊ธด๋ค. run.json ๋ฉํ๋ ๋๋ ํ ๋ฆฌ๋ฅผ ๋ง๋ค๊ธฐ ์ ์ ์กฐ๋ฆฝํ๋ค โ
|
| 238 |
+
์๋ ์ฌ์ ยท๊นจ์ง lock ๊ฐ์ ์ค๋ฅ๊ฐ ๋น run ๋๋ ํ ๋ฆฌ๋ฅผ ๋จ๊ธฐ์ง ์๊ฒ.
|
| 239 |
+
|
| 240 |
+
๋๋๋ฉด run ์ข
๋ฃ ํ
(`hub_hook.run_hook`)์ ๋ถ๋ฅด๊ณ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋๋ ค์ค๋ค(hub ๋์์ด ์๋๋ฉด None ์ผ ์ ์๋ค).
|
| 241 |
+
ํ
์ ์คํจํด๋ ์์ธ๋ฅผ ๋ด์ง ์๋๋ค โ ์ข
๋ฃ ์ฝ๋๋ ํธ์ถ์๊ฐ `exit_code_for` ๋ก ์ ํ๋ค.
|
| 242 |
+
`hook_options` ๋ฅผ ์ฃผ๋ฉด(batch โ `HookMode.BATCH_ITEM`) `no_ingest`ยท`strict_ingest` ๋์ ๊ทธ๊ฒ์ ์ด๋ค(์ค๋ชจํฌ ํ์ ๋ง ๋ง์์ด๋ค).
|
| 243 |
+
"""
|
| 244 |
+
from solar_eval.cli.commands import run_meta
|
| 245 |
from solar_eval.stores import JsonlStore
|
| 246 |
|
| 247 |
+
plan = run_meta.plan_run(cfg, **plan_options)
|
| 248 |
+
project = plan.project
|
| 249 |
+
meta = run_meta.build_run_meta(cfg, plan, note)
|
| 250 |
+
# ์ด๋ฆ ์ถฉ๋์ ์ฌ๊ธฐ์ ์์์ ์ผ๋ก ๊ฑธ๋ฌ์ง๋ค(F10). run.json ์ ๊ทธ ์งํ ํ ๋ฒ์ ์ด๋ค.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 251 |
run_name, run_dir = _make_run_dir(
|
| 252 |
+
cfg.artifacts_dir(project), plan.task, plan.pipeline_name, plan.prompt_name
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 253 |
)
|
| 254 |
+
(run_dir / "run.json").write_text(json.dumps(meta, ensure_ascii=False, indent=2))
|
| 255 |
+
_echo_run_info(plan, meta, f"{project}/{run_name}", run_dir)
|
| 256 |
+
execute_plan(plan, run_name, JsonlStore(run_dir))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 257 |
|
| 258 |
_warn_if_incomplete(run_dir)
|
| 259 |
+
if plan.selection is not None and plan.selection.where is not None:
|
| 260 |
+
click.secho(FILTERED_RUN_EXPERIMENT_HUB_WARNING, fg="yellow")
|
| 261 |
click.echo(f"\nResults saved to: {run_dir}/")
|
| 262 |
+
if hook_options is None:
|
| 263 |
+
hook_options = HookOptions(command="start", no_ingest=no_ingest, strict=strict_ingest)
|
| 264 |
+
options = replace(hook_options, smoke=is_smoke_run(plan))
|
| 265 |
+
result = run_hook(cfg, project, run_dir, options)
|
| 266 |
+
_echo_judge_bundle_hint(plan, project, run_name)
|
| 267 |
+
return result
|
| 268 |
|
| 269 |
|
| 270 |
+
def _echo_judge_bundle_hint(plan, project: str, run_name: str) -> None:
|
| 271 |
+
"""rubric judge ํ์คํฌ๋ฉด ํ
๋ค ์๋ด ํ ์ค โ ํ
(eval-wrap)์ lcs ์ ์ฉ์ด๋ผ judge ์ฑ์ ๋ฒ๋ค์ ๋ด์ง ์๋๋ค(D-L12-6)."""
|
| 272 |
+
from solar_eval.evaluators.judge_config import RUBRIC_JUDGE_TYPE
|
| 273 |
+
from solar_eval.scoring_publish.publish import evaluator_type
|
| 274 |
|
| 275 |
+
if evaluator_type(plan.task_config.get("evaluator")) == RUBRIC_JUDGE_TYPE:
|
| 276 |
+
click.secho(f" {JUDGE_BUNDLE_HINT.format(project=project, run=run_name)}", fg="blue")
|
|
|
|
|
|
|
| 277 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 278 |
|
| 279 |
+
def _echo_run_info(plan, meta: dict, run_ref: str, run_dir: Path) -> None:
|
| 280 |
+
"""run ์์ ์์ฝ โ ์ค์ ยท์ง๋ฌธ(config sha256:<12>)ยท์ ํยท์ฌํ ๊ฐ๋ฅ์ฑ."""
|
| 281 |
+
from solar_eval.cli.commands.run_meta import short_fingerprint
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 282 |
|
| 283 |
+
snapshot = meta["snapshot"]
|
| 284 |
+
dataset = meta["dataset"]
|
| 285 |
+
click.secho(f"Prompt: {plan.prompt_name} [{plan.prompt_format}]", fg="blue")
|
| 286 |
+
click.secho(f"Model: {plan.model}", fg="blue")
|
| 287 |
+
click.secho(f"Pipeline: {plan.pipeline_name}", fg="blue")
|
| 288 |
+
for path, value in (plan.pipeline_overrides or {}).items():
|
| 289 |
+
click.secho(f" override: {path} = {value!r}", fg="magenta")
|
| 290 |
+
click.secho(
|
| 291 |
+
f"Fingerprint: config {short_fingerprint(snapshot['config_fingerprint'])}"
|
| 292 |
+
f" / prompt {meta['prompt_sha']} / pipeline {meta['pipeline_sha']}",
|
| 293 |
+
fg="blue",
|
| 294 |
+
)
|
| 295 |
+
if plan.reasoning_effort:
|
| 296 |
+
click.secho(f"Reasoning effort: {plan.reasoning_effort}", fg="blue")
|
| 297 |
+
selection = plan.selection
|
| 298 |
+
if selection is not None and selection.where is not None:
|
| 299 |
+
click.secho(f"Where: {selection.where.describe()}", fg="yellow")
|
| 300 |
+
if selection is not None and selection.limit:
|
| 301 |
+
click.secho(f"Limit: {selection.limit} samples", fg="yellow")
|
| 302 |
+
click.secho(f"Rows: {dataset['rows_selected']}/{dataset['rows_total']}", fg="blue")
|
| 303 |
+
_echo_reproducibility(snapshot)
|
| 304 |
+
click.secho(f"Starting local run: {run_ref}", fg="blue")
|
| 305 |
+
click.secho(f"Output: {run_dir}", fg="blue")
|
| 306 |
|
| 307 |
|
| 308 |
+
def _echo_reproducibility(snapshot: dict) -> None:
|
| 309 |
+
"""์ฌํ ๊ฐ๋ฅ์ฑ ํ ์ค โ ๋ง๋ ์ด์ ๋ฅผ ๊ทธ๋๋ก ๋ณด์ฌ ์ค๋ค(๊ณํ R5)."""
|
| 310 |
+
repro = snapshot.get("reproducibility") or {}
|
| 311 |
+
verdict = repro.get("rerunnable")
|
| 312 |
+
label = {True: "true", False: "false", None: "unknown"}[verdict]
|
| 313 |
+
blockers = repro.get("rerunnable_blockers") or []
|
| 314 |
+
suffix = f" โ {', '.join(blockers)}" if blockers else ""
|
| 315 |
+
click.secho(f"Rerunnable: {label}{suffix}", fg="green" if verdict else "yellow")
|
| 316 |
+
if snapshot.get("missing"):
|
| 317 |
+
click.secho(f" snapshot missing: {', '.join(snapshot['missing'])}", fg="yellow")
|
| 318 |
|
| 319 |
|
| 320 |
def _warn_if_incomplete(run_dir: Path) -> None:
|
|
|
|
| 348 |
)
|
| 349 |
|
| 350 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 351 |
@runs_group.command("batch")
|
| 352 |
@click.argument("project")
|
| 353 |
@click.option("--task", "-t", required=True, help="Task name")
|
|
|
|
| 362 |
)
|
| 363 |
@click.option("--reasoning-effort", "-re", default=None, help="Reasoning effort")
|
| 364 |
@click.option("--max-workers", "-w", default=5, type=int, help="Max parallel workers")
|
| 365 |
+
@click.option("--where", "where", multiple=True, metavar="KEY=V1,V2", help=WHERE_HELP)
|
| 366 |
+
@click.option("--no-ingest", is_flag=True, help=NO_INGEST_HELP)
|
| 367 |
+
@click.option("--strict-ingest", is_flag=True, help=STRICT_INGEST_HELP)
|
| 368 |
@click.pass_context
|
| 369 |
+
def batch_runs(ctx, project, task, prompts, no_ingest, strict_ingest, **run_options):
|
| 370 |
+
"""Run evaluation for multiple prompts sequentially.
|
| 371 |
+
|
| 372 |
+
run ๋ง๋ค ๋ฒ๋ค ๋ค๋ฆฌ๋ง ๋๊ณ (`HookMode.BATCH_ITEM`), ๋์ hub ๋ค๋ฆฌ๋ฅผ ํ ๋ฒ ๋ถ๋ฅธ๋ค(๊ณํ L6 D-L6-12).
|
| 373 |
+
์ข
๋ฃ ์ฝ๋: ํ๋กฌํํธ ์คํจ๊ฐ ์์ผ๋ฉด 1(๋จผ์ ), ์๋๊ณ strict ์ด๋ฉฐ ํ
์ด ์คํจํ run ์ด ์์ผ๋ฉด 3, ๊ทธ ๋ฐ 0(D-L6-11).
|
| 374 |
+
"""
|
| 375 |
cfg = ctx.obj["config"]
|
| 376 |
if cfg.is_remote:
|
| 377 |
raise click.ClickException("Batch is local-mode only")
|
| 378 |
|
| 379 |
prompt_list = [p.strip() for p in prompts.split(",")]
|
| 380 |
+
# ํ๋กฌํํธ์ ๋ฌด๊ดํ ๊ฒ์ฌ(defaults.whereยท--where ๋ฌธ๋ฒยทํ์คํฌ where ํฉ์น๊ธฐ)๋ ๋ฃจํ ์ ์ ํ ๋ฒ โ
|
| 381 |
+
# ํ๋ ธ์ผ๋ฉด ํ๋กฌํํธ๋ง๋ค ๊ฐ์ ์ค๋ฅ๋ฅผ ๋ํ์ดํ์ง ์๊ณ ๋ฐ๋ก ๋ฉ์ถ๋ค(D3 "์คํจ๋ ํฌ๊ฒ").
|
| 382 |
+
project_config, task_config = find_task(cfg, project, task)
|
| 383 |
+
start_options(project_config, task_config, (), run_options["where"], None)
|
| 384 |
+
preflight_judge_config(task_config)
|
| 385 |
click.secho(f"Batch run: {project}/{task} โ prompts: {prompt_list}", fg="blue", bold=True)
|
| 386 |
|
| 387 |
+
options = HookOptions(
|
| 388 |
+
command=COMMAND_BATCH,
|
| 389 |
+
mode=HookMode.BATCH_ITEM,
|
| 390 |
+
no_ingest=no_ingest,
|
| 391 |
+
strict=strict_requested(strict_ingest),
|
| 392 |
+
)
|
| 393 |
+
results, failed = _run_batch_prompts(cfg, project, task, prompt_list, options, run_options)
|
| 394 |
+
results = _with_batch_hub_leg(results, finish_batch(cfg, project, results, options))
|
| 395 |
+
click.secho(f"\n{'=' * 50}", fg="blue")
|
| 396 |
+
# standalone(๋ ํฌ ๋ฐ โ ํ
์ด None ๋ง ๋๋ ค์ค๋ค)์ ํ
์ฝ์์ด ์๋ค(ยง2.3 "์ค๋๊ณผ ๊ฐ๋ค", ๋ฆฌ๋ทฐ ์ง์ 3).
|
| 397 |
+
if cfg.repo_root is not None or any(r is not None for r in results):
|
| 398 |
+
echo_batch_summary(results)
|
| 399 |
+
if failed:
|
| 400 |
+
raise click.ClickException(
|
| 401 |
+
f"{len(failed)} of {len(prompt_list)} prompts failed: {', '.join(failed)}"
|
| 402 |
+
)
|
| 403 |
+
code = max((exit_code_for(r, options.strict) for r in results), default=0)
|
| 404 |
+
if code:
|
| 405 |
+
ctx.exit(code)
|
| 406 |
+
click.secho("Batch complete!", fg="green", bold=True)
|
| 407 |
+
|
| 408 |
+
|
| 409 |
+
def _run_batch_prompts(
|
| 410 |
+
cfg, project: str, task: str, prompt_list: list[str], options: HookOptions, run_options: dict
|
| 411 |
+
) -> tuple[list[HookResult | None], list[str]]:
|
| 412 |
+
"""ํ๋กฌํํธ๋ง๋ค run ํ๋ + ๋ฒ๋ค ๋ค๋ฆฌ. ํ
๊ฒฐ๊ณผ์ ์คํจํ ํ๋กฌํํธ ์ด๋ฆ์ ๋๋ ค์ค๋ค.
|
| 413 |
+
|
| 414 |
+
ํ
์ ์์ธ๋ฅผ ๋ด์ง ์๋๋ค(`run_hook` ์ด ๋ด์ํ๋ค, D-L6-2) โ ์ฌ๊ธฐ์ ์กํ๋ ์์ธ๋ run ์์ฒด์ ์คํจ๋ฟ์ด๋ค.
|
| 415 |
+
"""
|
| 416 |
+
results: list[HookResult | None] = []
|
| 417 |
+
failed: list[str] = []
|
| 418 |
for pname in prompt_list:
|
| 419 |
click.secho(f"\n{'=' * 50}", fg="blue")
|
| 420 |
click.secho(f"Running prompt: {pname}...", fg="blue", bold=True)
|
| 421 |
click.secho(f"{'=' * 50}", fg="blue")
|
| 422 |
try:
|
| 423 |
+
results.append(
|
| 424 |
+
_start_local(
|
| 425 |
+
cfg,
|
| 426 |
+
project=project,
|
| 427 |
+
task=task,
|
| 428 |
+
prompt_name=pname,
|
| 429 |
+
temperature=0.0,
|
| 430 |
+
max_tokens=4000,
|
| 431 |
+
limit=None,
|
| 432 |
+
hook_options=options,
|
| 433 |
+
**run_options,
|
| 434 |
+
)
|
| 435 |
)
|
| 436 |
except Exception as e:
|
| 437 |
+
# ํ ํ๋กฌํํธ์ ์คํจ๊ฐ ๋๋จธ์ง๋ฅผ ๋ง์ง ์๊ฒ ์ด์ด ๊ฐ๋, ๋์์ exit code ๋ก ์๋ฆฐ๋ค.
|
| 438 |
click.secho(f" {pname} failed: {e}", fg="red")
|
| 439 |
+
failed.append(pname)
|
| 440 |
+
return results, failed
|
|
|
|
| 441 |
|
| 442 |
|
| 443 |
+
def _with_batch_hub_leg(
|
| 444 |
+
results: list[HookResult | None], hub: LegResult | None
|
| 445 |
+
) -> list[HookResult | None]:
|
| 446 |
+
"""batch ๋ hub ๋ค๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ run ๋ณ ๊ฒฐ๊ณผ์ ์น๋๋ค(์์ฝยท์ข
๋ฃ ์ฝ๋์ฉ โ ์ํ ํ์ผ์ `finish_batch` ๊ฐ ๊ณ ์น๋ค).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 447 |
|
| 448 |
+
๋ฒ๋ค์ด ์๊ธด run ์ ๋ ๋ค๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ, ๋ฒ๋ค์ด ์๋ run ์ `skipped:no_bundle` ์ ๋ฐ๋๋ค(`hook._legs_in_lock` ๊ณผ ๊ฐ์ ๊ท์น).
|
| 449 |
+
"""
|
| 450 |
+
merged: list[HookResult | None] = []
|
| 451 |
+
for result in results:
|
| 452 |
+
pending = result.legs.get(LEG_HUB) if result is not None else None
|
| 453 |
+
if result is None or pending is None or pending.code != BATCH_PENDING:
|
| 454 |
+
merged.append(result)
|
| 455 |
+
continue
|
| 456 |
+
leg = hub if hub is not None and bundle_ready(result.legs.get(LEG_BUNDLE)) else None
|
| 457 |
+
leg = leg or LegResult.skipped(NO_BUNDLE)
|
| 458 |
+
merged.append(replace(result, legs={**result.legs, LEG_HUB: leg}))
|
| 459 |
+
return merged
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 460 |
|
| 461 |
|
| 462 |
@runs_group.command("results")
|
|
|
|
| 496 |
results = results[:limit]
|
| 497 |
click.secho(format_results_table(results), fg="blue")
|
| 498 |
click.echo(f"\nShowing {len(results)} result(s)")
|
| 499 |
+
|
| 500 |
+
|
| 501 |
+
# ๋ผ์ด ๋ธ ๋ช
๋ น ๋ฑ๋ก โ `solar-eval runs resume|eval` ํ๋ฉด์ ๊ทธ๋๋ก๋ค.
|
| 502 |
+
runs_group.add_command(resume_run)
|
| 503 |
+
runs_group.add_command(eval_run)
|
solar_eval/cli/commands/score.py
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""`solar-eval score` โ ์คํ๋ผ์ธ ์ฌ์ฑ์ ๋ช
๋ น ๊ทธ๋ฃน (๊ณํ 2026-09-26 L3b ยง2.1).
|
| 2 |
+
|
| 3 |
+
`score proofread-diff` ๋ run ๋ฒ๋ค์ `results.jsonl` ์ ๋ ๊ฑฐ์ `lcs_diff` ์ ๊ฐ์ ํจ์๋ก ๋ค์ ์ฑ์ ํด ์ฑ์ ๋ฒ๋ค
|
| 4 |
+
(`poc-scoring/v1`, `purpose=rescore_after_fix`)๋ก ๋ธ๋ค. ๋ ๋ชจ๋:
|
| 5 |
+
|
| 6 |
+
- `<run_bundle_dir>โฆ` ๋ชจ๋: ๋ฒ๋ค์ด `โฆ/eval/bundles/<id>` ์ ์์ผ๋ฉด ๊ธฐ๋ณธ ์ถ๋ ฅ์ `โฆ/eval/evaluations`, ์๋๋ฉด `--out` ํ์.
|
| 7 |
+
`--mirror` ๋ฉด ๊ฐ ๋ฒ๋ค ๋งค๋ํ์คํธ `case` ์ ๋ฏธ๋ฌ `evaluations` ์ ๋๋๋ค.
|
| 8 |
+
- `--case a/c --all` ๋ชจ๋: ๋ฏธ๋ฌ์ ๊ทธ case run ๋ฒ๋ค ์ ๋ถ(`poc_bundle.adopt` ๋ก ์ฑํ). `--mirror` ๋๋ `--out` ์ด ํ์ํ๋ค.
|
| 9 |
+
`--run <run_id>`(๋ฐ๋ณต)๋ฅผ ์ฃผ๋ฉด ๊ทธ run ๋ค๋ง(๊ณํ 2026-09-27 L6 ยง2.11 โ ์ฑํ ๊ท์น์ ๊ฐ๊ณ , ์์ญ์ ์๋ run ์ ์ค๋ฅ
|
| 10 |
+
`run_not_in_mirror`). `--run` ์ ์ด ๋ชจ๋์์๋ง ๋ฐ๊ณ , ๋น ๊ฐยท๊ณต๋ฐฑ๋งยท์ ์ด ๋ฌธ์ยท512์ ์ด๊ณผ๋ ์ฌ์ฉ ์ค๋ฅ๋ค(ES
|
| 11 |
+
`hub ingest --expect-run` ๊ณผ ๊ฐ์ ๋ฒ์ โ ์๋ run ์ด stderr ์ ๊ทธ๋๋ก ์ฐํ๋ฏ๋ก ์ค ์์กฐ๋ฅผ ๋ง๋๋ค).
|
| 12 |
+
- `--out DIR` ๊ณผ `--mirror` ๋ ํจ๊ป ์ฐ์ง ์๋๋ค. `--mirror-root` ๋ ๋ฏธ๋ฌ **๋ฒํท์ ๋ถ๋ชจ**๋ค(ยง2.1 "๋ฏธ๋ฌ ๋ฃจํธ ์ธต").
|
| 13 |
+
๋๋ ํ ๋ฆฌ ๋ชจ๋์์๋ `--mirror` ์ ํจ๊ป๋ง ๋ฐ๋๋ค. ์๋ run ๋ฒ๋ค ๊ฒฝ๋ก๋ ์ฌ์ฉ ์ค๋ฅ(์ข
๋ฃ 2)๋ค.
|
| 14 |
+
|
| 15 |
+
stdout ์ run ๋ง๋ค ํ ์ค `written <path>` ยท `exists <path>` ยท `skip <run_id> <code>` ์ด๊ณ , case ๋ชจ๋๋ ๋ง์ง๋ง ์ค์ JSON ์์ฝ
|
| 16 |
+
`{written, exists, skipped:{code:n}, errors, n_original_missing}` ์ ๋ธ๋ค. ์ค๋ฅ๋ stderr ์ `error <run_id> <code>` ํ ์ค์ฉ
|
| 17 |
+
(case ์ ์ฒด ์ค๋ฅ๋ run_id ์๋ฆฌ์ `-`, `case_area_missing`ยท`case_area_empty` ๋ ๋ค์ ํด์ํ ์์ญ ๊ฒฝ๋ก๋ฅผ ๋ถ์ธ๋ค).
|
| 18 |
+
์ด๋ ์ชฝ์๋ ์
๋ ฅยท์ถ๋ ฅยท์ ๋ต ์๋ฌธ์ ์ฃ์ง ์๋๋ค.
|
| 19 |
+
|
| 20 |
+
์ข
๋ฃ ์ฝ๋: 0 = ์ ๋ถ written/exists/๊ท์น skip ยท 1 = ์ค๋ฅ 1๊ฑด ์ด์ ยท 2 = ์ฌ์ฉ ์ค๋ฅ.
|
| 21 |
+
|
| 22 |
+
import ๊ฒฝ๊ณ(D1): ์ด ๋ชจ๋์ click ๋ง ๋ชจ๋ ์ต์์์์ import ํ๋ค. `solar_eval.rescore`(๊ทธ๋ฆฌ๊ณ ๊ทธ๊ฒ์ด ๋์ด์ค๋
|
| 23 |
+
`poc_bundle`)๋ **๋ช
๋ น ํจ์ ์์์** import ํ๋ค โ `solar-eval` CLI ๋ฅผ ๋์ฐ๋ ๊ฒ๋ง์ผ๋ก ์ฌ์ฑ์ ๊ธฐยท๋ฒ๋ค writer ๊ฐ
|
| 24 |
+
๋ก๋ฉ๋์ง ์๊ฒ.
|
| 25 |
+
"""
|
| 26 |
+
|
| 27 |
+
from __future__ import annotations
|
| 28 |
+
|
| 29 |
+
import json
|
| 30 |
+
import sys
|
| 31 |
+
from pathlib import Path
|
| 32 |
+
from typing import Any
|
| 33 |
+
|
| 34 |
+
import click
|
| 35 |
+
|
| 36 |
+
_DIR = click.Path(file_okay=False, path_type=Path)
|
| 37 |
+
# ์
๋ ฅ run ๋ฒ๋ค์ ์์ด์ผ ํ๋ค โ ๊ฒฝ๋ก ์คํ๊ฐ ๊ท์น skip(`latest_bundle_invalid`)ยท์ข
๋ฃ 0 ์ผ๋ก ๋๋์ง ์๊ฒ click ์ด ์ข
๋ฃ 2 ๋ก ๋ง๋๋ค.
|
| 38 |
+
_EXISTING_DIR = click.Path(exists=True, file_okay=False, path_type=Path)
|
| 39 |
+
EXIT_ERRORS = 1
|
| 40 |
+
EXIT_USAGE = 2
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
@click.group("score")
|
| 44 |
+
def score_group() -> None:
|
| 45 |
+
"""Offline rescoring of run bundles (scoring bundles, poc-scoring/v1)."""
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
@score_group.command("proofread-diff")
|
| 49 |
+
@click.argument("run_bundle_dirs", nargs=-1, type=_EXISTING_DIR)
|
| 50 |
+
@click.option("--case", "case_key", default=None, help="accounts/<a>/<c> as a/c (with --all).")
|
| 51 |
+
@click.option("--all", "all_runs", is_flag=True, default=False, help="Every adopted run bundle.")
|
| 52 |
+
@click.option("--out", "out_dir", type=_DIR, default=None, help="Parent dir for scoring bundles.")
|
| 53 |
+
@click.option("--mirror", is_flag=True, default=False, help="Write into the local mirror.")
|
| 54 |
+
@click.option(
|
| 55 |
+
"--mirror-root",
|
| 56 |
+
type=_DIR,
|
| 57 |
+
default=None,
|
| 58 |
+
help="Parent of the mirror bucket (default: data home blob-mirror).",
|
| 59 |
+
)
|
| 60 |
+
@click.option(
|
| 61 |
+
"--run",
|
| 62 |
+
"run_ids",
|
| 63 |
+
multiple=True,
|
| 64 |
+
help="Only this run_id (repeatable; with --case --all).",
|
| 65 |
+
)
|
| 66 |
+
def proofread_diff(
|
| 67 |
+
run_bundle_dirs: tuple[Path, ...],
|
| 68 |
+
case_key: str | None,
|
| 69 |
+
all_runs: bool,
|
| 70 |
+
out_dir: Path | None,
|
| 71 |
+
mirror: bool,
|
| 72 |
+
mirror_root: Path | None,
|
| 73 |
+
run_ids: tuple[str, ...],
|
| 74 |
+
) -> None:
|
| 75 |
+
"""Rescore run bundles with proofread_diff@2 (same scoring as legacy lcs_diff)."""
|
| 76 |
+
_check_usage(run_bundle_dirs, case_key, all_runs, out_dir, mirror, mirror_root, run_ids)
|
| 77 |
+
if case_key is not None:
|
| 78 |
+
code = _run_case(case_key, out_dir, mirror_root, frozenset(run_ids) or None)
|
| 79 |
+
else:
|
| 80 |
+
code = _run_dirs(run_bundle_dirs, out_dir, mirror_root if mirror else None, mirror)
|
| 81 |
+
sys.exit(code)
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def _usage(message: str) -> None:
|
| 85 |
+
click.echo(f"usage error: {message}", err=True)
|
| 86 |
+
sys.exit(EXIT_USAGE)
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def _check_usage(
|
| 90 |
+
dirs: tuple[Path, ...],
|
| 91 |
+
case_key: str | None,
|
| 92 |
+
all_runs: bool,
|
| 93 |
+
out_dir: Path | None,
|
| 94 |
+
mirror: bool,
|
| 95 |
+
mirror_root: Path | None,
|
| 96 |
+
run_ids: tuple[str, ...] = (),
|
| 97 |
+
) -> None:
|
| 98 |
+
"""๋ ๋ชจ๋์ ์ธ์ ์กฐํฉ โ ์ด๊ธ๋๋ฉด ์ข
๋ฃ 2."""
|
| 99 |
+
if out_dir is not None and mirror:
|
| 100 |
+
_usage("--out and --mirror are mutually exclusive")
|
| 101 |
+
if case_key is not None or all_runs:
|
| 102 |
+
if dirs:
|
| 103 |
+
_usage("give run bundle directories or --case/--all, not both")
|
| 104 |
+
if case_key is None or not all_runs:
|
| 105 |
+
_usage("--case a/c needs --all (and --all needs --case)")
|
| 106 |
+
if out_dir is None and not mirror:
|
| 107 |
+
_usage("--case --all needs --mirror or --out DIR")
|
| 108 |
+
elif not dirs:
|
| 109 |
+
_usage("give run bundle directories, or --case a/c --all")
|
| 110 |
+
elif mirror_root is not None and not mirror:
|
| 111 |
+
# ๋๋ ํ ๋ฆฌ ๋ชจ๋์์ --mirror-root ๋ --mirror ์ ์ถ๋ ฅ ์๋ฆฌ์ผ ๋ฟ์ด๋ค โ ์กฐ์ฉํ ๋ฒ๋ฆฌ์ง ์๋๋ค
|
| 112 |
+
_usage("--mirror-root needs --mirror when rescoring run bundle directories")
|
| 113 |
+
if run_ids:
|
| 114 |
+
_check_run_filter(case_key, run_ids)
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
def _check_run_filter(case_key: str | None, run_ids: tuple[str, ...]) -> None:
|
| 118 |
+
"""`--run` ์ case ๋ชจ๋์ run ํํฐ๋ค(L6 ยง2.11) โ ๋๋ ํ ๋ฆฌ ๋ชจ๋์์ ์กฐ์ฉํ ๋ฒ๋ฆฌ์ง ์๊ณ , ๊ฐ์ ๊ฒฝ๊ณ์์ ๊ฑฐ๋ฅธ๋ค.
|
| 119 |
+
|
| 120 |
+
case ๋ชจ๋์ `--case`/`--all` ์ง์ `_check_usage` ๊ฐ ๋จผ์ ๋ง์ผ๋ฏ๋ก ์ฌ๊ธฐ์ `case_key is None` ์ด๋ฉด ๋๋ ํ ๋ฆฌ ๋ชจ๋๋ค.
|
| 121 |
+
๊ฐ ๊ท์น์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ฒฝ๊ณ(`rescore_case`)์ ๊ฐ์ `is_valid_run_id` ๋ค. ๊ฑฐ๋ถ ๋ฌธ๊ตฌ์ ๊ฐ ์๋ฌธ์ ์ฃ์ง ์๋๋ค.
|
| 122 |
+
"""
|
| 123 |
+
from solar_eval.rescore.case import MAX_RUN_ID_LENGTH, is_valid_run_id
|
| 124 |
+
|
| 125 |
+
if case_key is None:
|
| 126 |
+
_usage("--run needs --case a/c --all (not run bundle directories)")
|
| 127 |
+
if not all(is_valid_run_id(r) for r in run_ids):
|
| 128 |
+
_usage(
|
| 129 |
+
"--run needs a non-blank run_id without control characters,"
|
| 130 |
+
f" at most {MAX_RUN_ID_LENGTH} characters"
|
| 131 |
+
)
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
def _run_case(
|
| 135 |
+
case_key: str,
|
| 136 |
+
out_dir: Path | None,
|
| 137 |
+
mirror_root: Path | None,
|
| 138 |
+
run_ids: frozenset[str] | None = None,
|
| 139 |
+
) -> int:
|
| 140 |
+
from solar_eval.rescore.case import rescore_case
|
| 141 |
+
from solar_eval.rescore.paths import RescorePathError, parse_case_key
|
| 142 |
+
|
| 143 |
+
try:
|
| 144 |
+
account, case = parse_case_key(case_key)
|
| 145 |
+
except RescorePathError as exc:
|
| 146 |
+
_usage(str(exc))
|
| 147 |
+
summary = rescore_case(account, case, mirror_root=mirror_root, out_dir=out_dir, run_ids=run_ids)
|
| 148 |
+
for outcome in summary.outcomes:
|
| 149 |
+
_echo_outcome(outcome)
|
| 150 |
+
for failure in summary.failures:
|
| 151 |
+
suffix = f" {failure.detail}" if failure.detail else ""
|
| 152 |
+
click.echo(f"error {failure.run_id or '-'} {failure.code}{suffix}", err=True)
|
| 153 |
+
report: dict[str, Any] = summary.to_json()
|
| 154 |
+
click.echo(json.dumps(report, ensure_ascii=False, sort_keys=False))
|
| 155 |
+
return EXIT_ERRORS if report["errors"] else 0
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
def _run_dirs(
|
| 159 |
+
dirs: tuple[Path, ...], out_dir: Path | None, mirror_root: Path | None, mirror: bool
|
| 160 |
+
) -> int:
|
| 161 |
+
from solar_eval.rescore.evaluator_doc import current_evaluator_document
|
| 162 |
+
from solar_eval.rescore.from_bundle import RescoreError, rescore_run_bundle
|
| 163 |
+
from solar_eval.rescore.paths import default_evaluations_dir, resolve_mirror_root
|
| 164 |
+
|
| 165 |
+
targets = [(d, out_dir or (None if mirror else default_evaluations_dir(d))) for d in dirs]
|
| 166 |
+
missing = [d for d, target in targets if target is None and not mirror]
|
| 167 |
+
if missing:
|
| 168 |
+
_usage(f"{missing[0]} is not under โฆ/eval/bundles/ โ give --out DIR")
|
| 169 |
+
root = resolve_mirror_root(mirror_root) if mirror else None
|
| 170 |
+
evaluator = current_evaluator_document()
|
| 171 |
+
errors = 0
|
| 172 |
+
for bundle_dir, target in targets:
|
| 173 |
+
try:
|
| 174 |
+
outcome = rescore_run_bundle(bundle_dir, target, mirror_root=root, evaluator=evaluator)
|
| 175 |
+
except RescoreError as exc:
|
| 176 |
+
click.echo(f"error {exc.run_id or bundle_dir.name} {exc.code}", err=True)
|
| 177 |
+
errors += 1
|
| 178 |
+
continue
|
| 179 |
+
_echo_outcome(outcome, fallback=bundle_dir.name)
|
| 180 |
+
return EXIT_ERRORS if errors else 0
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
def _echo_outcome(outcome: Any, fallback: str = "-") -> None:
|
| 184 |
+
if outcome.status == "skip":
|
| 185 |
+
click.echo(f"skip {outcome.run_id or fallback} {outcome.code}")
|
| 186 |
+
else:
|
| 187 |
+
click.echo(f"{outcome.status} {outcome.path}")
|
solar_eval/cli/commands/sync.py
CHANGED
|
@@ -17,7 +17,7 @@ logger = logging.getLogger(__name__)
|
|
| 17 |
@click.option(
|
| 18 |
"--mongo-uri",
|
| 19 |
envvar="MONGODB_URI",
|
| 20 |
-
default="mongodb://localhost:
|
| 21 |
help="MongoDB connection URI",
|
| 22 |
)
|
| 23 |
@click.option("--db", "database", default="solar_eval", help="MongoDB database name")
|
|
@@ -122,9 +122,10 @@ def _sync_single_run(store, project: str, run_dir: Path, cfg) -> dict[str, int]:
|
|
| 122 |
"input": result.get("input", {}),
|
| 123 |
"output": result.get("output", ""),
|
| 124 |
"golden": result.get("golden", ""),
|
| 125 |
-
|
| 126 |
-
"
|
| 127 |
-
"
|
|
|
|
| 128 |
}
|
| 129 |
)
|
| 130 |
sample_count = store.bulk_insert_samples(samples)
|
|
|
|
| 17 |
@click.option(
|
| 18 |
"--mongo-uri",
|
| 19 |
envvar="MONGODB_URI",
|
| 20 |
+
default="mongodb://localhost:57017",
|
| 21 |
help="MongoDB connection URI",
|
| 22 |
)
|
| 23 |
@click.option("--db", "database", default="solar_eval", help="MongoDB database name")
|
|
|
|
| 122 |
"input": result.get("input", {}),
|
| 123 |
"output": result.get("output", ""),
|
| 124 |
"golden": result.get("golden", ""),
|
| 125 |
+
# ์ฌ์ฉ๋ยท์ง์ฐ์ด ์๋ ํ์ None ๊ทธ๋๋ก -- 0 ์ ์ง์ด๋ด์ง ์๋๋ค(L6b B1).
|
| 126 |
+
"input_tokens": result.get("input_tokens"),
|
| 127 |
+
"output_tokens": result.get("output_tokens"),
|
| 128 |
+
"inference_time_ms": result.get("inference_time_ms"),
|
| 129 |
}
|
| 130 |
)
|
| 131 |
sample_count = store.bulk_insert_samples(samples)
|
solar_eval/cli/config.py
CHANGED
|
@@ -1,28 +1,155 @@
|
|
| 1 |
-
"""CLI configuration from YAML file and environment variables.
|
| 2 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
import logging
|
| 4 |
import os
|
|
|
|
|
|
|
|
|
|
| 5 |
from pathlib import Path
|
| 6 |
from typing import Any
|
| 7 |
|
| 8 |
import yaml
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
logger = logging.getLogger(__name__)
|
| 13 |
|
| 14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
def default_projects_dir() -> Path:
|
| 16 |
"""CBS ๊ณต์ฉ ๋ฐ์ดํฐ ํ ์๋์ ํ๋ก์ ํธ ๋ฃจํธ (huggingface ์บ์์ ๊ฐ์ ๋ฐฉ์).
|
| 17 |
|
| 18 |
- ๊ธฐ๋ณธ๊ฐ: ``~/.cache/cbs-data/solar-eval-projects``
|
| 19 |
- ``CBS_DATA_HOME`` ์ด ์ค์ ๋๋ฉด ๋ฐ์ดํฐ ํ ์์ฒด๋ฅผ ์ฎ๊ธด๋ค: ``$CBS_DATA_HOME/solar-eval-projects``
|
| 20 |
- ``XDG_CACHE_HOME`` ์ด ์ค์ ๋๋ฉด ์บ์ ๋ฃจํธ๋ก ์กด์คํ๋ค: ``$XDG_CACHE_HOME/cbs-data/solar-eval-projects``
|
|
|
|
|
|
|
| 21 |
"""
|
| 22 |
data_home = os.environ.get("CBS_DATA_HOME")
|
| 23 |
if data_home:
|
| 24 |
return Path(data_home).expanduser() / "solar-eval-projects"
|
| 25 |
-
cache_root = os.environ.get("XDG_CACHE_HOME"
|
| 26 |
return Path(cache_root).expanduser() / "cbs-data" / "solar-eval-projects"
|
| 27 |
|
| 28 |
|
|
@@ -66,7 +193,7 @@ def discover_config_dirs(repo_root: Path | None) -> dict[str, Path]:
|
|
| 66 |
|
| 67 |
def _load_yaml_config(config_path: Path | None = None) -> dict[str, Any]:
|
| 68 |
"""YAML ์ค์ ํ์ผ ๋ก๋. ํ์ผ ์์ผ๋ฉด ๋น dict ๋ฐํ."""
|
| 69 |
-
path =
|
| 70 |
if not path.is_file():
|
| 71 |
return {}
|
| 72 |
try:
|
|
@@ -74,7 +201,8 @@ def _load_yaml_config(config_path: Path | None = None) -> dict[str, Any]:
|
|
| 74 |
data = yaml.safe_load(f)
|
| 75 |
return data if isinstance(data, dict) else {}
|
| 76 |
except Exception as e:
|
| 77 |
-
|
|
|
|
| 78 |
return {}
|
| 79 |
|
| 80 |
|
|
@@ -83,13 +211,16 @@ class CLIConfig:
|
|
| 83 |
|
| 84 |
Priority (low โ high):
|
| 85 |
1. Hard-coded defaults (projects_dir: ~/.cache/cbs-data/solar-eval-projects)
|
| 86 |
-
2. YAML config file (~/.upstage-cloud-dev/solar-eval-config.yaml)
|
| 87 |
-
3. Environment variables (SOLAR_EVAL_URL, CBS_DATA_HOME, SOLAR_EVAL_PROJECTS, etc.)
|
| 88 |
4. CLI options (--remote, etc.)
|
| 89 |
"""
|
| 90 |
|
| 91 |
def __init__(self, config_path: Path | None = None) -> None:
|
| 92 |
-
|
|
|
|
|
|
|
|
|
|
| 93 |
|
| 94 |
yaml_remote = yaml_cfg.get("remote_url") or yaml_cfg.get("remote")
|
| 95 |
self.remote_url: str | None = os.environ.get("SOLAR_EVAL_URL", yaml_remote)
|
|
@@ -126,6 +257,10 @@ class CLIConfig:
|
|
| 126 |
if openai_cfg.get("api_key") and not os.environ.get("OPENAI_API_KEY"):
|
| 127 |
os.environ["OPENAI_API_KEY"] = openai_cfg["api_key"]
|
| 128 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
@property
|
| 130 |
def is_remote(self) -> bool:
|
| 131 |
return self.remote_url is not None
|
|
|
|
| 1 |
+
"""CLI configuration from YAML file and environment variables.
|
| 2 |
|
| 3 |
+
**`hub:` ์ (๊ณํ 2026-09-28 L10 ยง2.6 ยท D-L10-6).** ์ฌ์ฉ์ ์ค์ ํ์ผ์ `hub.database_url`ยท`hub.confirm` ์ run ์ข
๋ฃ ํ
์ hub
|
| 4 |
+
๋ค๋ฆฌ opt-in ์ด๋ค. ์ฌ๊ธฐ์๋ ๋ชจ์๋ง ์ฝ์ด `CLIConfig.hub`(`HubConfig`)์ ๋๊ณ , ํ๊ฒฝ ๋ณ์์์ ์ฐ์ ์์ยท์์ด์ ํธ ์ธ์
ํ์ ์
|
| 5 |
+
`solar_eval.hub_hook.optin.resolve_hub_optin` ํ ๊ณณ์ด ํ๋ค. ๋ชจ์ ์ค๋ฅ(๋งคํ์ด ์๋ยทURL ๋ชจ์์ด ์๋ยท๋ชจ๋ฅด๋ ํคยท์ค์ ํ์ผ์ด
|
| 6 |
+
์์ ์ ์ ์ฉ ๊ถํ์ด ์๋)๋ `problems` ์ **ํค ์ด๋ฆ๋ง** ์ฃ๋๋ค โ ๊ฐ์ ๋ก๊ทธยท์์ธยท`repr` ์ด๋์๋ ์๋ค.
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
import functools
|
| 10 |
import logging
|
| 11 |
import os
|
| 12 |
+
import stat
|
| 13 |
+
from collections.abc import Mapping
|
| 14 |
+
from dataclasses import dataclass, field
|
| 15 |
from pathlib import Path
|
| 16 |
from typing import Any
|
| 17 |
|
| 18 |
import yaml
|
| 19 |
|
| 20 |
+
#: 2026-10-02 ์ ์์น โ ์ ๊ฒฝ๋ก์ ํ์ผ์ด ์์ ๋๋ง ๊ฒฝ๊ณ ์ ํจ๊ป ์ฝ๋๋ค(`make dev-docker-up` ์ด ์ ๊ฒฝ๋ก๋ก ๋ณต์ฌํ๋ค).
|
| 21 |
+
LEGACY_CONFIG_PATH = Path.home() / ".upstage-cloud-dev" / "solar-eval-config.yaml"
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def _config_home() -> Path:
|
| 25 |
+
"""๋ ํฌ ์ฌ์ฉ์ ์ค์ ํ โ `CBS_ARSENAL_CONFIG_HOME` > `$XDG_CONFIG_HOME/cbs-arsenal` > `~/.config/cbs-arsenal`.
|
| 26 |
+
|
| 27 |
+
Go `internal/config.ConfigHome` ๊ณผ ๊ฐ์ ๊ท์น์ด๋ค(monorepo-conventions.md "์ฌ์ฉ์ ์ค์ ํ").
|
| 28 |
+
"""
|
| 29 |
+
override = os.environ.get("CBS_ARSENAL_CONFIG_HOME")
|
| 30 |
+
if override:
|
| 31 |
+
return Path(override)
|
| 32 |
+
xdg = os.environ.get("XDG_CONFIG_HOME")
|
| 33 |
+
return Path(xdg) / "cbs-arsenal" if xdg else Path.home() / ".config" / "cbs-arsenal"
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
DEFAULT_CONFIG_PATH = _config_home() / "solar-eval.yaml"
|
| 37 |
+
|
| 38 |
+
#: `hub:` ์ ์ด ๋ฐ๋ ํค.
|
| 39 |
+
HUB_CONFIG_KEYS = ("database_url", "confirm")
|
| 40 |
+
#: `hub.database_url` ์ด ๋ฐ๋ URL ๋ชจ์(hub CLI `connect.ts` ์ ๊ฐ์ ๋ ์คํด).
|
| 41 |
+
HUB_URL_SCHEMES = ("postgres://", "postgresql://")
|
| 42 |
+
#: ๊ทธ๋ฃนยท๋ค๋ฅธ ์ฌ์ฉ์ ๊ถํ ๋นํธ โ ์๊ฒฉ์ฆ๋ช
URL ์ ๋ด์ ์ค์ ํ์ผ์ ์์ ์๋ง ์ฝ๊ณ ์จ์ผ ํ๋ค(ssh ํค์ ๊ฐ์ ๊ท์น).
|
| 43 |
+
LOOSE_MODE_BITS = stat.S_IRWXG | stat.S_IRWXO
|
| 44 |
+
#: ๋ฌธ์ ๋ชฉ๋ก์ ์ฃ๋ ํค ์ด๋ฆ ๊ธธ์ด ์ํ(๊ฐ์ด ์๋๋ผ ์ด๋ฆ์ด์ง๋ง ๊ธธ๊ฒ ์ฃ์ง ์๋๋ค).
|
| 45 |
+
_KEY_NAME_MAX = 40
|
| 46 |
|
| 47 |
logger = logging.getLogger(__name__)
|
| 48 |
|
| 49 |
|
| 50 |
+
@dataclass(frozen=True)
|
| 51 |
+
class HubConfig:
|
| 52 |
+
"""์ค์ ํ์ผ `hub:` ์ โ ๊ฐ์ `repr` ์ ์ฃ์ง ์๋๋ค. `problems` ๊ฐ ์์ผ๋ฉด ์ค์ ์ถ์ฒ opt-in ์ ๊บผ์ง๋ค(optin ์ด ํ์ )."""
|
| 53 |
+
|
| 54 |
+
database_url: str | None = field(default=None, repr=False)
|
| 55 |
+
confirm: str | None = field(default=None, repr=False)
|
| 56 |
+
problems: tuple[str, ...] = ()
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def config_file_path(config_path: Path | None = None) -> Path:
|
| 60 |
+
"""์ฝ์ ์ค์ ํ์ผ โ ๋ช
์ ๊ฒฝ๋ก > `SOLAR_EVAL_CONFIG` > ๊ธฐ๋ณธ ๊ฒฝ๋ก(`<์ค์ ํ>/solar-eval.yaml`) > ์ ๊ฒฝ๋ก(์์ ๋๋ง, ๊ฒฝ๊ณ ).
|
| 61 |
+
|
| 62 |
+
๊ธฐ๋ณธ ๊ฒฝ๋ก์ ํ์ผ์ด ์๊ณ ์ `~/.upstage-cloud-dev/solar-eval-config.yaml` ์ด ์์ผ๋ฉด ์ ๊ฒ์ ์ฝ๋๋ค โ ์ฎ๊ธฐ๊ธฐ ์ ์ ์ค์ ์ด
|
| 63 |
+
์กฐ์ฉํ ์ฌ๋ผ์ง์ง ์๊ฒ. ๋ ๋ค ์์ผ๋ฉด ๊ธฐ๋ณธ ๊ฒฝ๋ก(์๋ ํ์ผ = ๋น ์ค์ )๋ค.
|
| 64 |
+
"""
|
| 65 |
+
if config_path:
|
| 66 |
+
return config_path
|
| 67 |
+
env_path = os.environ.get("SOLAR_EVAL_CONFIG")
|
| 68 |
+
if env_path is not None:
|
| 69 |
+
return Path(env_path)
|
| 70 |
+
if DEFAULT_CONFIG_PATH.exists() or not LEGACY_CONFIG_PATH.exists():
|
| 71 |
+
return DEFAULT_CONFIG_PATH
|
| 72 |
+
_warn_legacy_once(LEGACY_CONFIG_PATH, DEFAULT_CONFIG_PATH)
|
| 73 |
+
return LEGACY_CONFIG_PATH
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
@functools.cache
|
| 77 |
+
def _warn_legacy_once(legacy: Path, primary: Path) -> None:
|
| 78 |
+
"""์ ๊ฒฝ๋ก ๊ฒฝ๊ณ ๋ ๊ฒฝ๋ก ์๋ง๋ค ํ๋ก์ธ์ค๋น ํ ๋ฒ โ `CLIConfig` ๊ฐ ์ฌ๋ฌ ๋ฒ ๋ง๋ค์ด์ ธ๋ ๋ฐ๋ณตํ์ง ์๋๋ค."""
|
| 79 |
+
logger.warning(
|
| 80 |
+
"์ ์ค์ ๊ฒฝ๋ก %s ๋ฅผ ์ฝ๋๋ค โ %s ๋ก ์ฎ๊ธด๋ค(make dev-docker-up ์ด ๋ณต์ฌํ๋ค)", legacy, primary
|
| 81 |
+
)
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def parse_hub(yaml_cfg: Mapping[str, Any], path: Path | None = None) -> HubConfig | None:
|
| 85 |
+
"""`hub:` ์ โ `HubConfig`(์๊ฑฐ๋ ๋น์์ผ๋ฉด None). ๋ชจ์ ์ค๋ฅ๋ ๊ฐ ์์ด `problems` ๋ก(ยง2.6 "์ค์ ๋ชจ์ ์ค๋ฅ")."""
|
| 86 |
+
raw = yaml_cfg.get("hub")
|
| 87 |
+
if raw is None:
|
| 88 |
+
return None
|
| 89 |
+
if not isinstance(raw, Mapping):
|
| 90 |
+
return HubConfig(problems=("hub: ๋งคํ์ด ์๋๋ค",))
|
| 91 |
+
problems = [f"hub.{_key_name(key)}: ๋ชจ๋ฅด๋ ํค" for key in raw if key not in HUB_CONFIG_KEYS]
|
| 92 |
+
url = _hub_url(raw.get("database_url"), problems)
|
| 93 |
+
confirm = _hub_confirm(raw.get("confirm"), problems)
|
| 94 |
+
if url is not None and path is not None:
|
| 95 |
+
problems += _permission_problems(path)
|
| 96 |
+
return HubConfig(url, confirm, tuple(problems))
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def _key_name(key: Any) -> str:
|
| 100 |
+
return str(key)[:_KEY_NAME_MAX] if isinstance(key, str) else f"<{type(key).__name__}>"
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def _hub_url(value: Any, problems: list[str]) -> str | None:
|
| 104 |
+
"""URL ๋ฌธ์์ด(์๋ค ๊ณต๋ฐฑ ์ ๊ฑฐ). ๋ชจ์์ด ํ๋ ค๋ ๋ฌธ์์ด์ด๋ฉด ๋๋ ค์ค๋ค โ ๋ ์ค์ ์ ๊ฐ๋ ๊ฐ๋ฆผยท๊ฑฐ๋ถ ๋์์ด๋ค."""
|
| 105 |
+
if value is None:
|
| 106 |
+
return None
|
| 107 |
+
if not isinstance(value, str):
|
| 108 |
+
problems.append("hub.database_url: ๋ฌธ์์ด์ด ์๋๋ค")
|
| 109 |
+
return None
|
| 110 |
+
url = value.strip()
|
| 111 |
+
if not url:
|
| 112 |
+
return None
|
| 113 |
+
if not url.startswith(HUB_URL_SCHEMES):
|
| 114 |
+
problems.append("hub.database_url: postgres:// ๋๋ postgresql:// URL ์ด ์๋๋ค")
|
| 115 |
+
return url
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
def _hub_confirm(value: Any, problems: list[str]) -> str | None:
|
| 119 |
+
if value is None:
|
| 120 |
+
return None
|
| 121 |
+
if not isinstance(value, str):
|
| 122 |
+
problems.append("hub.confirm: ๋ฌธ์์ด์ด ์๋๋ค")
|
| 123 |
+
return None
|
| 124 |
+
return value.strip() or None
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
def _permission_problems(path: Path) -> list[str]:
|
| 128 |
+
"""์๊ฒฉ์ฆ๋ช
URL ์ ๋ด์ ์ค์ ํ์ผ์ด ๊ทธ๋ฃนยท๋ค๋ฅธ ์ฌ์ฉ์์๊ฒ ์ด๋ ค ์์ผ๋ฉด ๋ฌธ์ ํ ์ค(๊ฐยท๊ฒฝ๋ก ์์)."""
|
| 129 |
+
if os.name == "nt": # POSIX ๊ถํ ๋นํธ๊ฐ ๋ป์ ๊ฐ์ง ์๋๋ค
|
| 130 |
+
return []
|
| 131 |
+
try:
|
| 132 |
+
mode = path.stat().st_mode
|
| 133 |
+
except OSError as exc:
|
| 134 |
+
return [f"์ค์ ํ์ผ ๊ถํ์ ํ์ธํ ์ ์๋ค({type(exc).__name__})"]
|
| 135 |
+
if mode & LOOSE_MODE_BITS:
|
| 136 |
+
return ["์ค์ ํ์ผ์ด ์์ ์ ์ ์ฉ์ด ์๋๋ค โ chmod 600 <์ค์ ํ์ผ>"]
|
| 137 |
+
return []
|
| 138 |
+
|
| 139 |
+
|
| 140 |
def default_projects_dir() -> Path:
|
| 141 |
"""CBS ๊ณต์ฉ ๋ฐ์ดํฐ ํ ์๋์ ํ๋ก์ ํธ ๋ฃจํธ (huggingface ์บ์์ ๊ฐ์ ๋ฐฉ์).
|
| 142 |
|
| 143 |
- ๊ธฐ๋ณธ๊ฐ: ``~/.cache/cbs-data/solar-eval-projects``
|
| 144 |
- ``CBS_DATA_HOME`` ์ด ์ค์ ๋๋ฉด ๋ฐ์ดํฐ ํ ์์ฒด๋ฅผ ์ฎ๊ธด๋ค: ``$CBS_DATA_HOME/solar-eval-projects``
|
| 145 |
- ``XDG_CACHE_HOME`` ์ด ์ค์ ๋๋ฉด ์บ์ ๋ฃจํธ๋ก ์กด์คํ๋ค: ``$XDG_CACHE_HOME/cbs-data/solar-eval-projects``
|
| 146 |
+
- ๋น ``XDG_CACHE_HOME`` ์ XDG ๊ท์ฝ๋๋ก ์๋ ๊ฒ์ผ๋ก ๋ณธ๋ค โ ``poc_bundle.home.data_home`` ๊ณผ ๊ฐ์ ๊ท์น์ด๋ค
|
| 147 |
+
(๊ณํ 2026-09-27 L6 Q-L6-17 โ ์ ์๋ ์๋ ๊ฒฝ๋ก ``cbs-data/โฆ`` ๋ก ํ๋ ค ํธ์ถ ์์น๋ง๋ค ๋ฌ๋๋ค).
|
| 148 |
"""
|
| 149 |
data_home = os.environ.get("CBS_DATA_HOME")
|
| 150 |
if data_home:
|
| 151 |
return Path(data_home).expanduser() / "solar-eval-projects"
|
| 152 |
+
cache_root = os.environ.get("XDG_CACHE_HOME") or "~/.cache"
|
| 153 |
return Path(cache_root).expanduser() / "cbs-data" / "solar-eval-projects"
|
| 154 |
|
| 155 |
|
|
|
|
| 193 |
|
| 194 |
def _load_yaml_config(config_path: Path | None = None) -> dict[str, Any]:
|
| 195 |
"""YAML ์ค์ ํ์ผ ๋ก๋. ํ์ผ ์์ผ๋ฉด ๋น dict ๋ฐํ."""
|
| 196 |
+
path = config_file_path(config_path)
|
| 197 |
if not path.is_file():
|
| 198 |
return {}
|
| 199 |
try:
|
|
|
|
| 201 |
data = yaml.safe_load(f)
|
| 202 |
return data if isinstance(data, dict) else {}
|
| 203 |
except Exception as e:
|
| 204 |
+
# ์์ธ ์๋ฌธ์ ์ฃ์ง ์๋๋ค โ YAML ์ค๋ฅ ๋ฌธ๊ตฌ๊ฐ ์ค ์กฐ๊ฐ(์๊ฒฉ์ฆ๋ช
URL ํฌํจ)์ ๋ด์ ์ ์๋ค(L10 ยง2.6 "๋น๋ฐ").
|
| 205 |
+
logger.warning("Failed to load config from %s: %s", path, type(e).__name__)
|
| 206 |
return {}
|
| 207 |
|
| 208 |
|
|
|
|
| 211 |
|
| 212 |
Priority (low โ high):
|
| 213 |
1. Hard-coded defaults (projects_dir: ~/.cache/cbs-data/solar-eval-projects)
|
| 214 |
+
2. YAML config file (~/.config/cbs-arsenal/solar-eval.yaml, ์ ~/.upstage-cloud-dev/solar-eval-config.yaml) โ `hub:` ์ ํฌํจ(๋ชจ๋ ์ค๋ช
)
|
| 215 |
+
3. Environment variables (SOLAR_EVAL_URL, CBS_DATA_HOME, SOLAR_EVAL_PROJECTS, HUB_DATABASE_URL, etc.)
|
| 216 |
4. CLI options (--remote, etc.)
|
| 217 |
"""
|
| 218 |
|
| 219 |
def __init__(self, config_path: Path | None = None) -> None:
|
| 220 |
+
path = config_file_path(config_path)
|
| 221 |
+
yaml_cfg = _load_yaml_config(path)
|
| 222 |
+
#: run ์ข
๋ฃ ํ
hub ๋ค๋ฆฌ opt-in(์ค์ ์ถ์ฒ) โ ํด์์ `hub_hook.optin.resolve_hub_optin`.
|
| 223 |
+
self.hub: HubConfig | None = parse_hub(yaml_cfg, path)
|
| 224 |
|
| 225 |
yaml_remote = yaml_cfg.get("remote_url") or yaml_cfg.get("remote")
|
| 226 |
self.remote_url: str | None = os.environ.get("SOLAR_EVAL_URL", yaml_remote)
|
|
|
|
| 257 |
if openai_cfg.get("api_key") and not os.environ.get("OPENAI_API_KEY"):
|
| 258 |
os.environ["OPENAI_API_KEY"] = openai_cfg["api_key"]
|
| 259 |
|
| 260 |
+
timely_cfg = yaml_cfg.get("timely", {})
|
| 261 |
+
if timely_cfg.get("api_key") and not os.environ.get("TIMELY_API_KEY"):
|
| 262 |
+
os.environ["TIMELY_API_KEY"] = timely_cfg["api_key"]
|
| 263 |
+
|
| 264 |
@property
|
| 265 |
def is_remote(self) -> bool:
|
| 266 |
return self.remote_url is not None
|
solar_eval/cli/formatters.py
CHANGED
|
@@ -29,36 +29,69 @@ def format_overall_score(score: float | None) -> str:
|
|
| 29 |
|
| 30 |
|
| 31 |
def format_projects_table(projects: list[dict[str, Any]]) -> str:
|
| 32 |
-
rows = [
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
|
| 40 |
|
| 41 |
def format_prompts_table(prompts: list[dict[str, Any]]) -> str:
|
| 42 |
-
rows = [
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
|
| 52 |
|
| 53 |
def format_runs_table(runs: list[dict[str, Any]]) -> str:
|
| 54 |
-
rows = [
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
|
| 63 |
|
| 64 |
def format_results_table(results: list[dict[str, Any]]) -> str:
|
|
@@ -66,15 +99,19 @@ def format_results_table(results: list[dict[str, Any]]) -> str:
|
|
| 66 |
for r in results:
|
| 67 |
inp = str(r.get("input", ""))[:40]
|
| 68 |
out = str(r.get("output", ""))[:40]
|
| 69 |
-
rows.append(
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
|
| 79 |
|
| 80 |
def format_evaluation_detail(evaluation: dict[str, Any]) -> str:
|
|
@@ -91,14 +128,21 @@ def format_evaluation_detail(evaluation: dict[str, Any]) -> str:
|
|
| 91 |
|
| 92 |
|
| 93 |
def format_dashboard_table(dashboard: dict[str, Any]) -> str:
|
| 94 |
-
rows = [
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
|
| 30 |
|
| 31 |
def format_projects_table(projects: list[dict[str, Any]]) -> str:
|
| 32 |
+
rows = [
|
| 33 |
+
[
|
| 34 |
+
p.get("name", ""),
|
| 35 |
+
p.get("display_name", ""),
|
| 36 |
+
p.get("description", "")[:50],
|
| 37 |
+
p.get("task_count", 0),
|
| 38 |
+
]
|
| 39 |
+
for p in projects
|
| 40 |
+
]
|
| 41 |
+
return tabulate(
|
| 42 |
+
rows, headers=["Name", "Display Name", "Description", "Tasks"], tablefmt="simple"
|
| 43 |
+
)
|
| 44 |
|
| 45 |
|
| 46 |
def format_prompts_table(prompts: list[dict[str, Any]]) -> str:
|
| 47 |
+
rows = [
|
| 48 |
+
[
|
| 49 |
+
p.get("id", "")[:12],
|
| 50 |
+
p.get("task", ""),
|
| 51 |
+
f"v{p.get('version', '?')}",
|
| 52 |
+
p.get("model", ""),
|
| 53 |
+
p.get("temperature", ""),
|
| 54 |
+
(p.get("system_prompt", "") or "")[:40] + "...",
|
| 55 |
+
]
|
| 56 |
+
for p in prompts
|
| 57 |
+
]
|
| 58 |
+
return tabulate(
|
| 59 |
+
rows,
|
| 60 |
+
headers=["ID", "Task", "Version", "Model", "Temp", "Prompt Preview"],
|
| 61 |
+
tablefmt="simple",
|
| 62 |
+
)
|
| 63 |
|
| 64 |
|
| 65 |
def format_runs_table(runs: list[dict[str, Any]]) -> str:
|
| 66 |
+
rows = [
|
| 67 |
+
[
|
| 68 |
+
r.get("id", "")[:12],
|
| 69 |
+
r.get("task", ""),
|
| 70 |
+
format_status(r.get("status", "")),
|
| 71 |
+
f"{r.get('completed_samples', 0)}/{r.get('total_samples', 0)}",
|
| 72 |
+
r.get("created_at", "")[:19],
|
| 73 |
+
]
|
| 74 |
+
for r in runs
|
| 75 |
+
]
|
| 76 |
+
return tabulate(
|
| 77 |
+
rows, headers=["ID", "Task", "Status", "Progress", "Created"], tablefmt="simple"
|
| 78 |
+
)
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
# ์ฌ์ฉ๋ยท์ง์ฐ์ด ์ธก์ ๋์ง ์์ ์นธ(`None`) -- 0 ๊ณผ ๊ตฌ๋ถ๋๊ฒ ์ฐ๋๋ค(L6b B1 ยท D-M2-24).
|
| 82 |
+
MISSING_CELL = "โ"
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def _usage_cell(value: Any) -> Any:
|
| 86 |
+
"""ํ ํฐ ์นธ -- `None` ์ด๋ฉด `โ`, ๊ทธ ๋ฐ์ ๊ทธ๋๋ก(ํค๊ฐ ์์ ์์ ๋์ ๋น ์นธ ํฌํจ)."""
|
| 87 |
+
return MISSING_CELL if value is None else value
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
def format_latency(value: Any) -> str:
|
| 91 |
+
"""์ง์ฐ ์นธ -- ์ซ์์ผ ๋๋ง `โฆms`, ๊ทธ ๋ฐ(`None`ยทํค ์์ยท๋ฌธ์์ดยท๋ถ๋ฆฌ์ธ)์ `โ`."""
|
| 92 |
+
if isinstance(value, bool) or not isinstance(value, (int, float)):
|
| 93 |
+
return MISSING_CELL
|
| 94 |
+
return f"{value:.0f}ms"
|
| 95 |
|
| 96 |
|
| 97 |
def format_results_table(results: list[dict[str, Any]]) -> str:
|
|
|
|
| 99 |
for r in results:
|
| 100 |
inp = str(r.get("input", ""))[:40]
|
| 101 |
out = str(r.get("output", ""))[:40]
|
| 102 |
+
rows.append(
|
| 103 |
+
[
|
| 104 |
+
r.get("sample_idx", ""),
|
| 105 |
+
inp + ("..." if len(inp) >= 40 else ""),
|
| 106 |
+
out + ("..." if len(out) >= 40 else ""),
|
| 107 |
+
_usage_cell(r.get("input_tokens", "")),
|
| 108 |
+
_usage_cell(r.get("output_tokens", "")),
|
| 109 |
+
format_latency(r.get("inference_time_ms")),
|
| 110 |
+
]
|
| 111 |
+
)
|
| 112 |
+
return tabulate(
|
| 113 |
+
rows, headers=["#", "Input", "Output", "In Tok", "Out Tok", "Time"], tablefmt="simple"
|
| 114 |
+
)
|
| 115 |
|
| 116 |
|
| 117 |
def format_evaluation_detail(evaluation: dict[str, Any]) -> str:
|
|
|
|
| 128 |
|
| 129 |
|
| 130 |
def format_dashboard_table(dashboard: dict[str, Any]) -> str:
|
| 131 |
+
rows = [
|
| 132 |
+
[
|
| 133 |
+
r.get("run_id", "")[:12],
|
| 134 |
+
r.get("task", ""),
|
| 135 |
+
f"v{r.get('prompt_version', '?')}",
|
| 136 |
+
r.get("model", ""),
|
| 137 |
+
format_overall_score(r.get("overall_score")),
|
| 138 |
+
format_scores(r.get("scores")),
|
| 139 |
+
r.get("total_samples", ""),
|
| 140 |
+
r.get("date", "")[:10],
|
| 141 |
+
]
|
| 142 |
+
for r in dashboard.get("runs", [])
|
| 143 |
+
]
|
| 144 |
+
return tabulate(
|
| 145 |
+
rows,
|
| 146 |
+
headers=["Run ID", "Task", "Version", "Model", "Score", "Metrics", "Samples", "Date"],
|
| 147 |
+
tablefmt="simple",
|
| 148 |
+
)
|
solar_eval/cli/main.py
CHANGED
|
@@ -59,6 +59,7 @@ from solar_eval.cli.commands.runs import runs_group
|
|
| 59 |
from solar_eval.cli.commands.eval import eval_group
|
| 60 |
from solar_eval.cli.commands.dashboard import dashboard_group
|
| 61 |
from solar_eval.cli.commands.datasets import datasets_group
|
|
|
|
| 62 |
|
| 63 |
from solar_eval.cli.commands.compare import compare_command
|
| 64 |
from solar_eval.cli.commands.quickrun import quick_run
|
|
@@ -73,6 +74,8 @@ solar_eval_cli.add_command(datasets_group)
|
|
| 73 |
solar_eval_cli.add_command(compare_command)
|
| 74 |
solar_eval_cli.add_command(quick_run)
|
| 75 |
solar_eval_cli.add_command(sync_to_mongodb)
|
|
|
|
|
|
|
| 76 |
|
| 77 |
|
| 78 |
def entrypoint() -> None:
|
|
|
|
| 59 |
from solar_eval.cli.commands.eval import eval_group
|
| 60 |
from solar_eval.cli.commands.dashboard import dashboard_group
|
| 61 |
from solar_eval.cli.commands.datasets import datasets_group
|
| 62 |
+
from solar_eval.cli.commands.score import score_group
|
| 63 |
|
| 64 |
from solar_eval.cli.commands.compare import compare_command
|
| 65 |
from solar_eval.cli.commands.quickrun import quick_run
|
|
|
|
| 74 |
solar_eval_cli.add_command(compare_command)
|
| 75 |
solar_eval_cli.add_command(quick_run)
|
| 76 |
solar_eval_cli.add_command(sync_to_mongodb)
|
| 77 |
+
# ์คํ๋ผ์ธ ์ฌ์ฑ์ (`score proofread-diff`) โ ๊ณํ 2026-09-26 L3b ยง2.1.
|
| 78 |
+
solar_eval_cli.add_command(score_group)
|
| 79 |
|
| 80 |
|
| 81 |
def entrypoint() -> None:
|
solar_eval/config.py
CHANGED
|
@@ -7,6 +7,9 @@ class ProviderConfig(BaseSettings):
|
|
| 7 |
upstage_api_key: str = ""
|
| 8 |
upstage_api_base: str = "https://api.upstage.ai/v1"
|
| 9 |
openai_api_key: str = ""
|
|
|
|
|
|
|
|
|
|
| 10 |
hf_token: str = ""
|
| 11 |
default_model: str = "solar-pro2"
|
| 12 |
|
|
|
|
| 7 |
upstage_api_key: str = ""
|
| 8 |
upstage_api_base: str = "https://api.upstage.ai/v1"
|
| 9 |
openai_api_key: str = ""
|
| 10 |
+
# Timely Router (OpenAI ํธํ ๋ฉํฐ๋ชจ๋ธ ๋ผ์ฐํฐ) โ ์คํ
`model: timely:<๋ชจ๋ธ>` ์ด ์ด๋ค
|
| 11 |
+
timely_api_key: str = ""
|
| 12 |
+
timely_api_base: str = "https://api.timelyrouter.ai/v1"
|
| 13 |
hf_token: str = ""
|
| 14 |
default_model: str = "solar-pro2"
|
| 15 |
|
solar_eval/core/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (713 Bytes). View file
|
|
|
solar_eval/core/__pycache__/dataset_loader.cpython-312.pyc
ADDED
|
Binary file (7.55 kB). View file
|
|
|
solar_eval/core/__pycache__/pipeline_compose.cpython-312.pyc
ADDED
|
Binary file (11 kB). View file
|
|
|
solar_eval/core/dataset_loader.py
CHANGED
|
@@ -1,6 +1,9 @@
|
|
| 1 |
import csv
|
|
|
|
|
|
|
| 2 |
import json
|
| 3 |
import logging
|
|
|
|
| 4 |
from pathlib import Path
|
| 5 |
|
| 6 |
from huggingface_hub import hf_hub_download, list_repo_files
|
|
@@ -10,6 +13,34 @@ from solar_eval.config import get_provider_config
|
|
| 10 |
logger = logging.getLogger(__name__)
|
| 11 |
|
| 12 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
class DatasetLoader:
|
| 14 |
"""Loads datasets from HuggingFace repos or local filesystem."""
|
| 15 |
|
|
@@ -37,24 +68,34 @@ class DatasetLoader:
|
|
| 37 |
return self.base_dir / repo / path
|
| 38 |
return repo_path / path
|
| 39 |
|
| 40 |
-
def
|
| 41 |
self, repo: str, path: str, source: str = "huggingface", force: bool = False
|
| 42 |
-
) ->
|
| 43 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
if source == "local":
|
| 45 |
local_path = self._resolve_local_path(repo, path)
|
| 46 |
if not local_path.exists():
|
| 47 |
raise FileNotFoundError(f"Local dataset not found: {local_path}")
|
| 48 |
else:
|
| 49 |
local_path = self.download_file(repo, path, force=force)
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
for line in f:
|
| 53 |
-
line = line.strip()
|
| 54 |
-
if line:
|
| 55 |
-
records.append(json.loads(line))
|
| 56 |
logger.info(f"Loaded {len(records)} records from {local_path}")
|
| 57 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
|
| 59 |
def load_csv(
|
| 60 |
self, repo: str, path: str, source: str = "huggingface", force: bool = False
|
|
|
|
| 1 |
import csv
|
| 2 |
+
import hashlib
|
| 3 |
+
import io
|
| 4 |
import json
|
| 5 |
import logging
|
| 6 |
+
from dataclasses import dataclass
|
| 7 |
from pathlib import Path
|
| 8 |
|
| 9 |
from huggingface_hub import hf_hub_download, list_repo_files
|
|
|
|
| 13 |
logger = logging.getLogger(__name__)
|
| 14 |
|
| 15 |
|
| 16 |
+
@dataclass(frozen=True)
|
| 17 |
+
class LoadedJsonl:
|
| 18 |
+
"""ํ ๋ฒ ์ฝ์ JSONL -- ํ์ฑํ ๋ ์ฝ๋์ **๊ฐ์ ๋ฐ์ดํธ**์ sha256 (๊ณํ D16).
|
| 19 |
+
|
| 20 |
+
`sha256` ์ 64์ ์๋ฌธ์ hex ๋ค(`sha256:` ์ ๋์ด ์์). ํ์ผ sha ์ ํ์ฑ์ด ์๋ก
|
| 21 |
+
๋ค๋ฅธ read ์์ ๋์ค๋ฉด ๊ทธ ์ฌ์ด์ ํ์ผ์ด ๋ฐ๋์์ ๋ run.json ์ด ๊ฑฐ์ง์ ์ ๋๋ค.
|
| 22 |
+
"""
|
| 23 |
+
|
| 24 |
+
records: list[dict]
|
| 25 |
+
sha256: str
|
| 26 |
+
local_path: Path
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def parse_jsonl_bytes(data: bytes) -> list[dict]:
|
| 30 |
+
"""JSONL ๋ฐ์ดํธ๋ฅผ ๋ ์ฝ๋ ๋ชฉ๋ก์ผ๋ก. ์ค๋ `open()` ๋ฐ๋ณต๊ณผ **๊ฐ์ ์ค ๊ท์น**์ด๋ค.
|
| 31 |
+
|
| 32 |
+
universal newlines(`\n`ยท`\r\n`ยท๋จ๋
`\r` ๋ชจ๋ ์ค ๋)๋ก ์ค์ ๋๋๊ณ , `strip()` ๋ค
|
| 33 |
+
๋น ์ค์ ๊ฑด๋๋ด๋ค -- `sample_idx`(๋ ์ฝ๋ ์์น)๊ฐ ์ run ๊ณผ ์ด๊ธ๋์ง ์์์ผ ํ๋ค.
|
| 34 |
+
"""
|
| 35 |
+
records = []
|
| 36 |
+
with io.TextIOWrapper(io.BytesIO(data), encoding="utf-8") as f:
|
| 37 |
+
for line in f:
|
| 38 |
+
line = line.strip()
|
| 39 |
+
if line:
|
| 40 |
+
records.append(json.loads(line))
|
| 41 |
+
return records
|
| 42 |
+
|
| 43 |
+
|
| 44 |
class DatasetLoader:
|
| 45 |
"""Loads datasets from HuggingFace repos or local filesystem."""
|
| 46 |
|
|
|
|
| 68 |
return self.base_dir / repo / path
|
| 69 |
return repo_path / path
|
| 70 |
|
| 71 |
+
def read_jsonl(
|
| 72 |
self, repo: str, path: str, source: str = "huggingface", force: bool = False
|
| 73 |
+
) -> LoadedJsonl:
|
| 74 |
+
"""JSONL ์ **๋ฐ์ดํธ๋ก ํ ๋ฒ** ์ฝ์ด sha256 ๊ณผ ๋ ์ฝ๋๋ฅผ ํจ๊ป ๋๋ ค์ค๋ค (D16).
|
| 75 |
+
|
| 76 |
+
sha ๋ `hashlib` ๋ก ๊ณ์ฐํ๋ค -- ์ด ๋ชจ๋์ ๋ฒค๋ ์ฌ๋ณธ(HF Space)์ ์ค๋ฆฌ๋ฏ๋ก
|
| 77 |
+
poc-bundle ํจํค์ง๋ฅผ ๊ฐ์ ธ์ค์ง ์๋๋ค(D18).
|
| 78 |
+
"""
|
| 79 |
if source == "local":
|
| 80 |
local_path = self._resolve_local_path(repo, path)
|
| 81 |
if not local_path.exists():
|
| 82 |
raise FileNotFoundError(f"Local dataset not found: {local_path}")
|
| 83 |
else:
|
| 84 |
local_path = self.download_file(repo, path, force=force)
|
| 85 |
+
data = Path(local_path).read_bytes()
|
| 86 |
+
records = parse_jsonl_bytes(data)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
logger.info(f"Loaded {len(records)} records from {local_path}")
|
| 88 |
+
return LoadedJsonl(
|
| 89 |
+
records=records,
|
| 90 |
+
sha256=hashlib.sha256(data).hexdigest(),
|
| 91 |
+
local_path=Path(local_path),
|
| 92 |
+
)
|
| 93 |
+
|
| 94 |
+
def load_jsonl(
|
| 95 |
+
self, repo: str, path: str, source: str = "huggingface", force: bool = False
|
| 96 |
+
) -> list[dict]:
|
| 97 |
+
"""Load and parse a JSONL file from HuggingFace or local filesystem."""
|
| 98 |
+
return self.read_jsonl(repo, path, source=source, force=force).records
|
| 99 |
|
| 100 |
def load_csv(
|
| 101 |
self, repo: str, path: str, source: str = "huggingface", force: bool = False
|
solar_eval/core/dataset_prep.py
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""run ์์ ์ ๋ฐ์ดํฐ์
์ค๋น -- ํ ๋ฒ ์ฝ๊ณ , ๊ณ ๋ฅด๊ณ , ํ ๋ฉํ ๊ท์น์ ๊ฒ์ฆํ๋ค (๊ณํ D3ยทD13ยทD16).
|
| 2 |
+
|
| 3 |
+
CLI(`runs start/batch`)๋ run ๋๋ ํ ๋ฆฌ๋ฅผ ๋ง๋ค๊ธฐ **์ ์** `prepare_dataset` ์ ๋ถ๋ฌ
|
| 4 |
+
0ํ ์ ํยท๋ผ๋ฒจ ๋๋ฝยท`meta_fields` ์ค๋ฅ๋ฅผ ๋จผ์ ํฐ๋จ๋ฆฌ๊ณ , ๊ฐ์ `PreparedDataset` ์
|
| 5 |
+
๋ฌ๋(`execute_run(prepared=...)`)์ run.json ๊ธฐ๋ก(sha256ยทํ ์ยท์ ํ)์ ํจ๊ป ๋๊ธด๋ค.
|
| 6 |
+
๊ทธ๋์ run.json ์ sha256 ๊ณผ ์ค์ ๋ก ๋ ํ์ด **๊ฐ์ ๋ฐ์ดํธ**์์ ๋์จ๋ค.
|
| 7 |
+
|
| 8 |
+
๋ฌ๋๋ `prepared` ๊ฐ ์์ผ๋ฉด(์๋น์คยท์ ํธ์ถ์) ๊ฐ์ ํจ์๋ก ์ค์ค๋ก ์ค๋นํ๋ค.
|
| 9 |
+
|
| 10 |
+
์ด ๋ชจ๋์ poc-bundle ํจํค์ง์ packaging ์ ๊ฐ์ ธ์ค์ง ์๋๋ค(D18).
|
| 11 |
+
"""
|
| 12 |
+
|
| 13 |
+
from __future__ import annotations
|
| 14 |
+
|
| 15 |
+
import copy
|
| 16 |
+
from dataclasses import dataclass
|
| 17 |
+
from typing import Any, Mapping
|
| 18 |
+
|
| 19 |
+
from solar_eval.core.dataset_loader import DatasetLoader, LoadedJsonl
|
| 20 |
+
from solar_eval.core.row_filter import (
|
| 21 |
+
RowSelection,
|
| 22 |
+
SelectionError,
|
| 23 |
+
Where,
|
| 24 |
+
combine,
|
| 25 |
+
format_indices,
|
| 26 |
+
parse_where,
|
| 27 |
+
select,
|
| 28 |
+
)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class DatasetPrepError(ValueError):
|
| 32 |
+
"""ํ์คํฌ์ ํ ๋ฉํยทwhere ์ ์ธ์ด ๊ท์น์ ๋ง์ง ์์ ๋ -- run ์ ์์ํ์ง ์๋๋ค."""
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
@dataclass(frozen=True)
|
| 36 |
+
class PreparedDataset:
|
| 37 |
+
"""run ์ด ๋ ํ ์งํฉ. `selected` ๋ `(์๋ณธ ํ์ผ์ 0-based ์์น, ํ)` ํํ๋ค์ด๋ค."""
|
| 38 |
+
|
| 39 |
+
loaded: LoadedJsonl
|
| 40 |
+
selected: tuple[tuple[int, dict[str, Any]], ...]
|
| 41 |
+
rows_total: int
|
| 42 |
+
meta_fields: tuple[str, ...] | None
|
| 43 |
+
selection: RowSelection | None = None
|
| 44 |
+
|
| 45 |
+
@property
|
| 46 |
+
def rows_selected(self) -> int:
|
| 47 |
+
return len(self.selected)
|
| 48 |
+
|
| 49 |
+
@property
|
| 50 |
+
def sha256(self) -> str:
|
| 51 |
+
return self.loaded.sha256
|
| 52 |
+
|
| 53 |
+
@property
|
| 54 |
+
def selected_indices(self) -> frozenset[int]:
|
| 55 |
+
return frozenset(idx for idx, _ in self.selected)
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
def reject_defaults_where(project_config: Mapping[str, Any]) -> None:
|
| 59 |
+
"""`defaults.where` ๋ฅผ ๊ฑฐ๋ถํ๋ค (D1).
|
| 60 |
+
|
| 61 |
+
`_resolve_defaults` ๋ ํค ๋จ์ ์์ ๋ณํฉ์ด๋ผ `defaults.where` ๊ฐ ๋ผ๋ฒจ ์๋ ํ์คํฌ์๋
|
| 62 |
+
ํผ์ ธ ๊ทธ run ์ด ์ ๋ถ ์ฃฝ๊ณ , ํ์คํฌ์ where ๊ฐ ์์ผ๋ฉด ํต์งธ๋ก ๋ฌด์๋๋ค("๋ณํฉ"์ด ์๋๋ผ
|
| 63 |
+
"๋์ฒด"). ๋ก๋๋ dict ์ `defaults` ํค๊ฐ ๋ณํฉ ๋ค์๋ ๋จ์ ์์ผ๋ฏ๋ก ๊ทธ๊ฒ์ ๋ณธ๋ค.
|
| 64 |
+
๋ก๋ ์์ ์ด ์๋๋ผ run ์์ ์ ๊ฒ์ฌํ๋ค -- ํ ํ๋ก์ ํธ์ ์๋ชป๋ ์ ์ธ์ด ๋ค๋ฅธ
|
| 65 |
+
ํ๋ก์ ํธ ๋ก๋๋ฅผ ๋ง์ง ์๊ฒ.
|
| 66 |
+
"""
|
| 67 |
+
defaults = project_config.get("defaults") or {}
|
| 68 |
+
if isinstance(defaults, Mapping) and "where" in defaults:
|
| 69 |
+
name = project_config.get("name", "?")
|
| 70 |
+
raise DatasetPrepError(
|
| 71 |
+
f"project {name!r}: defaults.where is not allowed -- "
|
| 72 |
+
"declare where on a task or pass --where on the CLI"
|
| 73 |
+
)
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
def _data_keys(task_config: Mapping[str, Any]) -> set[str]:
|
| 77 |
+
"""์
๋ ฅยท์ ๋ต์ผ๋ก ์ด๋ฏธ ์ฐ๋ ๋ฐ์ดํฐ ํ ํค -- meta ๋ก ์ค๋ณต ๋ณต์ ํ์ง ์๋๋ค."""
|
| 78 |
+
keys = set(task_config.get("input_fields") or [])
|
| 79 |
+
golden_field = task_config.get("golden_field")
|
| 80 |
+
if golden_field:
|
| 81 |
+
keys.add(golden_field)
|
| 82 |
+
golden_fields = task_config.get("golden_fields") or {}
|
| 83 |
+
if isinstance(golden_fields, Mapping):
|
| 84 |
+
keys.update(v for v in golden_fields.values() if isinstance(v, str))
|
| 85 |
+
return keys
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
def validate_meta_fields(task_config: Mapping[str, Any]) -> tuple[str, ...] | None:
|
| 89 |
+
"""ํ์คํฌ `meta_fields` ๊ฒ์ฆ (D13) -- ๋น์ด ์์ง ์์ ๋ฌธ์์ด ๋ฆฌ์คํธ, ์ค๋ณตยท์
๋ ฅ/์ ๋ต ๊ฒน์นจ ๋ถ๊ฐ.
|
| 90 |
+
|
| 91 |
+
์ ์ธ์ด ์์ผ๋ฉด `None`(results ํ์ `meta` ํค๋ฅผ ์ฐ์ง ์๋๋ค -- ๋ค๋ฅธ ํ๋ก์ ํธ์ ๋ฐ์ดํธ ๋ถ๋ณ).
|
| 92 |
+
"""
|
| 93 |
+
raw = task_config.get("meta_fields")
|
| 94 |
+
if raw is None:
|
| 95 |
+
return None
|
| 96 |
+
task = task_config.get("name", "?")
|
| 97 |
+
if not isinstance(raw, list) or not raw:
|
| 98 |
+
raise DatasetPrepError(f"task {task!r}: meta_fields must be a non-empty list of strings")
|
| 99 |
+
if not all(isinstance(f, str) and f.strip() for f in raw):
|
| 100 |
+
raise DatasetPrepError(f"task {task!r}: meta_fields values must be non-empty strings")
|
| 101 |
+
duplicates = sorted({f for f in raw if raw.count(f) > 1})
|
| 102 |
+
if duplicates:
|
| 103 |
+
raise DatasetPrepError(f"task {task!r}: meta_fields has duplicates: {duplicates}")
|
| 104 |
+
overlap = sorted(set(raw) & _data_keys(task_config))
|
| 105 |
+
if overlap:
|
| 106 |
+
raise DatasetPrepError(f"task {task!r}: meta_fields overlap input/golden fields: {overlap}")
|
| 107 |
+
return tuple(raw)
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
def row_meta(row: Mapping[str, Any], meta_fields: tuple[str, ...] | None) -> dict[str, Any] | None:
|
| 111 |
+
"""๋ฐ์ดํฐ ํ์์ `meta_fields` ํค๋ง ๊ณจ๋ผ ๋ณต์ฌํ๋ค. ํ์ ์๋ ํค๋ **์๋ต**ํ๋ค.
|
| 112 |
+
|
| 113 |
+
๊ฐ์ ๊น์ ๋ณต์ฌ๋ณธ์ด๋ค -- ๊ฒฐ๊ณผ ํ์ด ๋ฐ์ดํฐ์
ํ ๊ฐ์ฒด๋ฅผ ๊ณต์ ํ์ง ์๊ฒ.
|
| 114 |
+
"""
|
| 115 |
+
if meta_fields is None:
|
| 116 |
+
return None
|
| 117 |
+
return {key: copy.deepcopy(row[key]) for key in meta_fields if key in row}
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
def build_selection(
|
| 121 |
+
task_config: Mapping[str, Any], cli_where: Where | None = None, limit: int | None = None
|
| 122 |
+
) -> RowSelection:
|
| 123 |
+
"""ํ์คํฌ `where` ์ CLI where(`Where` ๋๋ `None`)๋ฅผ ํฉ์ณ run ์ ์ ํ์ ๋ง๋ ๋ค.
|
| 124 |
+
|
| 125 |
+
์ ํ์ CLI ๊ฐ **๋ช
์์ ์ผ๋ก** ๋ง๋ค์ด ๋ฌ๋์ ๋๊ธด๋ค -- ๋ฌ๋๋ `task_config["where"]` ๋ฅผ
|
| 126 |
+
์ค์ค๋ก ์ฝ์ง ์๋๋ค(D3).
|
| 127 |
+
"""
|
| 128 |
+
task_name = task_config.get("name", "?")
|
| 129 |
+
task_where = parse_where(task_config.get("where"), origin=f"task {task_name!r}")
|
| 130 |
+
return RowSelection(where=combine(task_where, cli_where), limit=limit or None)
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
def prepare_dataset(
|
| 134 |
+
loader: DatasetLoader,
|
| 135 |
+
project_config: Mapping[str, Any],
|
| 136 |
+
task_config: Mapping[str, Any],
|
| 137 |
+
selection: RowSelection | None,
|
| 138 |
+
) -> PreparedDataset:
|
| 139 |
+
"""๋ฐ์ดํฐ์
์ ํ ๋ฒ ์ฝ๊ณ ์ ํ์ ์ ์ฉํ๋ค. 0ํยท๋ผ๋ฒจ ๋๋ฝยทmeta ์ค๋ฅ๋ ์ฌ๊ธฐ์ ํฐ์ง๋ค."""
|
| 140 |
+
meta_fields = validate_meta_fields(task_config)
|
| 141 |
+
dataset_config = project_config.get("dataset") or {}
|
| 142 |
+
loaded = loader.read_jsonl(
|
| 143 |
+
dataset_config.get("repo", ""),
|
| 144 |
+
task_config.get("dataset_path", ""),
|
| 145 |
+
source=dataset_config.get("source", "huggingface"),
|
| 146 |
+
)
|
| 147 |
+
selected = select(loaded.records, selection)
|
| 148 |
+
return PreparedDataset(
|
| 149 |
+
loaded=loaded,
|
| 150 |
+
selected=tuple(selected),
|
| 151 |
+
rows_total=len(loaded.records),
|
| 152 |
+
meta_fields=meta_fields,
|
| 153 |
+
selection=selection,
|
| 154 |
+
)
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
def check_resume_indices(prepared: PreparedDataset, completed_indices: set[int]) -> None:
|
| 158 |
+
"""์ด๋ฏธ ๋๋ `sample_idx` ๊ฐ ์ ํ ๋ฐ์ด๋ฉด ์คํจํ๋ค (D3 resume ๊ฐ๋).
|
| 159 |
+
|
| 160 |
+
์ ํ ๋ฐ ๊ฒฐ๊ณผ๊ฐ ์์ธ results ๋ ์ด run ์ ์ ํ์ผ๋ก ์ค๋ช
๋์ง ์๋๋ค -- ์ ํ์ด๋
|
| 161 |
+
๋ฐ์ดํฐ์
์ด run ์์ ๋ค ๋ฐ๋์๋ค๋ ๋ป์ด๋ค.
|
| 162 |
+
"""
|
| 163 |
+
outside = sorted(completed_indices - prepared.selected_indices)
|
| 164 |
+
if outside:
|
| 165 |
+
raise SelectionError(
|
| 166 |
+
"completed results are outside this run's selection "
|
| 167 |
+
f"({format_indices(outside)}) "
|
| 168 |
+
"-- the selection or dataset changed since the run started; start a new run"
|
| 169 |
+
)
|
solar_eval/core/fingerprint.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
| 3 |
์ ํ์ํ๊ฐ: ์ด ์์ง์ ์์ฐ์ ์ค๋ ์ด๋ฆ์ผ๋ก๋ง ์๋ณ๋ผ ์๊ณ (`prompt_dev_v23`
|
| 4 |
โ `pipeline_dev_v24` ์ฒ๋ผ ์ง์ ๋ฒํธ๊ฐ ์ด๊ธ๋๋ ์ผ์ด ์ฆ์๋ค), ๊ทธ๋์
|
| 5 |
"์ด๋ฆ์ด ๋ค๋ฅธ ๋ run ์ด ์ฌ์ค ๊ฐ์ ์ค์ " ์ธ์ง๋ฅผ ์ฌ๋์ด ํด์๋ฅผ ๋ ์ ๋์กฐํด์ผ ํ๋ค
|
| 6 |
-
(`insights/
|
| 7 |
run ์ ์ง๋ฌธ์ ์ฐ์ด ๋๋ฉด ๊ทธ ๋์กฐ๊ฐ ์กฐํ ํ ๋ฒ์ผ๋ก ๋๋๋ค.
|
| 8 |
|
| 9 |
์ง๋ฌธ์ **ํด์์ด ๋๋ ์ค์ **์ ๋์์ผ๋ก ๊ณ์ฐํ๋ค. `extends`/override ๋ก ์กฐ๋ฆฝํ
|
|
@@ -16,7 +16,7 @@ run ์ ์ง๋ฌธ์ ์ฐ์ด ๋๋ฉด ๊ทธ ๋์กฐ๊ฐ ์กฐํ ํ ๋ฒ์ผ๋ก ๋๋๋ค.
|
|
| 16 |
|
| 17 |
import hashlib
|
| 18 |
import json
|
| 19 |
-
from collections.abc import Mapping, Sequence
|
| 20 |
from pathlib import Path
|
| 21 |
from typing import Any
|
| 22 |
|
|
@@ -84,16 +84,28 @@ def prompt_fingerprint(path: Path) -> str | None:
|
|
| 84 |
return None
|
| 85 |
|
| 86 |
|
| 87 |
-
def
|
| 88 |
-
"""ํ๋กฌํํธ ๋๋ ํ ๋ฆฌ
|
| 89 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
for file in sorted(root.rglob("*")):
|
| 91 |
if not file.is_file() or file.suffix.lower() not in PROMPT_ASSET_SUFFIXES:
|
| 92 |
continue
|
| 93 |
if any(part.startswith(".") for part in file.relative_to(root).parts):
|
| 94 |
continue
|
| 95 |
-
|
| 96 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
|
| 98 |
|
| 99 |
def _file_digest(path: Path) -> str:
|
|
|
|
| 3 |
์ ํ์ํ๊ฐ: ์ด ์์ง์ ์์ฐ์ ์ค๋ ์ด๋ฆ์ผ๋ก๋ง ์๋ณ๋ผ ์๊ณ (`prompt_dev_v23`
|
| 4 |
โ `pipeline_dev_v24` ์ฒ๋ผ ์ง์ ๋ฒํธ๊ฐ ์ด๊ธ๋๋ ์ผ์ด ์ฆ์๋ค), ๊ทธ๋์
|
| 5 |
"์ด๋ฆ์ด ๋ค๋ฅธ ๋ run ์ด ์ฌ์ค ๊ฐ์ ์ค์ " ์ธ์ง๋ฅผ ์ฌ๋์ด ํด์๋ฅผ ๋ ์ ๋์กฐํด์ผ ํ๋ค
|
| 6 |
+
(`insights/260803_etd_regression_prod_vs_v24.md` ์ '์ด๋ฆ ๋งคํ' ์ ์ด ๊ทธ ํ์ ์ด๋ค).
|
| 7 |
run ์ ์ง๋ฌธ์ ์ฐ์ด ๋๋ฉด ๊ทธ ๋์กฐ๊ฐ ์กฐํ ํ ๋ฒ์ผ๋ก ๋๋๋ค.
|
| 8 |
|
| 9 |
์ง๋ฌธ์ **ํด์์ด ๋๋ ์ค์ **์ ๋์์ผ๋ก ๊ณ์ฐํ๋ค. `extends`/override ๋ก ์กฐ๋ฆฝํ
|
|
|
|
| 16 |
|
| 17 |
import hashlib
|
| 18 |
import json
|
| 19 |
+
from collections.abc import Iterator, Mapping, Sequence
|
| 20 |
from pathlib import Path
|
| 21 |
from typing import Any
|
| 22 |
|
|
|
|
| 84 |
return None
|
| 85 |
|
| 86 |
|
| 87 |
+
def iter_prompt_asset_files(root: Path) -> Iterator[Path]:
|
| 88 |
+
"""ํ๋กฌํํธ ๋๋ ํ ๋ฆฌ์์ ์ง๋ฌธ ๋์ ํ์ผ์ ๊ฒฝ๋ก ์ ๋ ฌ ์์๋ก ๋ธ๋ค.
|
| 89 |
+
|
| 90 |
+
๊ท์น: `PROMPT_ASSET_SUFFIXES` ํ์ฅ์(๋์๋ฌธ์ ๋ฌด์)์ ์ผ๋ฐ ํ์ผ, `root` ๊ธฐ์ค ๊ฒฝ๋ก์
|
| 91 |
+
์ด๋ ๋ถ๋ถ์ด๋ `.` ์ผ๋ก ์์ํ๋ฉด(์จ๊น) ์ ์ธ. legacy `prompt_sha` ์ ์ค๋
์ท์
|
| 92 |
+
`prompt_assets`(`core/snapshot/resources.py`)๊ฐ **๊ฐ์ ํ์ผ ์งํฉ**์ ๋ณด๋๋ก ์ด ํจ์
|
| 93 |
+
ํ๋๋ฅผ ๊ณต์ ํ๋ค(๊ณํ 2026-09-24-phase1a D12).
|
| 94 |
+
"""
|
| 95 |
for file in sorted(root.rglob("*")):
|
| 96 |
if not file.is_file() or file.suffix.lower() not in PROMPT_ASSET_SUFFIXES:
|
| 97 |
continue
|
| 98 |
if any(part.startswith(".") for part in file.relative_to(root).parts):
|
| 99 |
continue
|
| 100 |
+
yield file
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def _collect_prompt_assets(root: Path) -> dict[str, str]:
|
| 104 |
+
"""ํ๋กฌํํธ ๋๋ ํ ๋ฆฌ์ `{์๋๊ฒฝ๋ก: ํ์ผ์ง๋ฌธ}` ๋งต. ์จ๊น ํ์ผ์ ์ ์ธํ๋ค."""
|
| 105 |
+
return {
|
| 106 |
+
file.relative_to(root).as_posix(): _file_digest(file)
|
| 107 |
+
for file in iter_prompt_asset_files(root)
|
| 108 |
+
}
|
| 109 |
|
| 110 |
|
| 111 |
def _file_digest(path: Path) -> str:
|
solar_eval/core/pipeline_factory.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""ํ์คํฌ โ ํ์ดํ๋ผ์ธ ์์ฑ ํ ๊ณณ (๊ณํ D6ยท๊ฒํ F11).
|
| 2 |
+
|
| 3 |
+
CLI ์ ๋ฌ๋๊ฐ ํ์ดํ๋ผ์ธ์ ๋ฐ๋ก ๋ง๋ค๋ฉด `project_dir`ยท`config_dir` ํด์์ด ๊ฐ๋ฆด ์ ์๋ค.
|
| 4 |
+
๊ทธ๋์ ์์ฑ ๊ท์น์ ์ฌ๊ธฐ ํ๋๋ก ๋๊ณ , CLI ๋ ํ ๋ฒ ๋ง๋ ์ธ์คํด์ค๋ฅผ ์ค๋
์ท
|
| 5 |
+
(`step_impl_hash`ยท`resources`)๊ณผ `execute_run(pipeline=...)` ์ **๊ฐ์ด** ๋๊ธด๋ค.
|
| 6 |
+
๋ฌ๋๋ `pipeline` ์ธ์๊ฐ ์์ผ๋ฉด(์๋น์คยท์ ํธ์ถ์) ์ด ํจ์๋ก ๋ง๋ ๋ค.
|
| 7 |
+
|
| 8 |
+
์ด ๋ชจ๋์ poc-bundle ํจํค์ง์ packaging ์ ๊ฐ์ ธ์ค์ง ์๋๋ค(D18).
|
| 9 |
+
"""
|
| 10 |
+
|
| 11 |
+
from __future__ import annotations
|
| 12 |
+
|
| 13 |
+
from pathlib import Path
|
| 14 |
+
from typing import Any, Mapping
|
| 15 |
+
|
| 16 |
+
from solar_eval.core.dataset_loader import DatasetLoader
|
| 17 |
+
from solar_eval.pipelines.base import BasePipeline
|
| 18 |
+
from solar_eval.pipelines.registry import create_pipeline
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def task_project_dir(
|
| 22 |
+
project_config: Mapping[str, Any], dataset_loader: DatasetLoader
|
| 23 |
+
) -> Path | None:
|
| 24 |
+
"""๋ฐ์ดํฐ ๋ฃจํธ ๊ธฐ์ค ํ๋ก์ ํธ ๋๋ ํ ๋ฆฌ -- v24 `tool_calling_judge` ๊ฐ `pmi_lookup` ์๋๊ฒฝ๋ก๋ฅผ ํ ๋ ์ด๋ค.
|
| 25 |
+
|
| 26 |
+
`dataset_loader.base_dir` ์ด projects ๋ฃจํธ์ด๋ฏ๋ก project ์ด๋ฆ์ ๋ถ์ด๋ฉด project_dir ์ด
|
| 27 |
+
๋๋ค -- CLI(`_start_local`)๊ฐ ์ด๋ฏธ ์ฐ๋ ๊ฒ๊ณผ ๊ฐ์ ๊ด๋ก(ยง5-F).
|
| 28 |
+
"""
|
| 29 |
+
project_name = project_config.get("name")
|
| 30 |
+
if dataset_loader.base_dir and project_name:
|
| 31 |
+
return dataset_loader.base_dir / project_name
|
| 32 |
+
return None
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def build_task_pipeline(
|
| 36 |
+
project_config: Mapping[str, Any],
|
| 37 |
+
task_config: Mapping[str, Any],
|
| 38 |
+
prompt: Mapping[str, Any],
|
| 39 |
+
dataset_loader: DatasetLoader,
|
| 40 |
+
) -> BasePipeline:
|
| 41 |
+
"""ํ์คํฌ ์ค์ ์ผ๋ก ์คํํ ํ์ดํ๋ผ์ธ์ ๋ง๋ ๋ค. ๋ชจ๋ฅด๋ ์คํ
์ด๋ฆ์ ์ฌ๊ธฐ์ ์ฃฝ๋๋ค(run ์ ).
|
| 42 |
+
|
| 43 |
+
`config_dir` ์ ๋ ํฌ ๊ด๋ฆฌ ํ๋ก์ ํธ๋ฉด project_loader ๊ฐ ์ฑ์ ๋ config ์ ๋ณธ ๊ฒฝ๋ก
|
| 44 |
+
(03-evaluation) -- ์นํ ์ฌ์ ๊ฐ์ ์ง์ ์์ฐ์ด ์ฌ๊ธฐ์ ์จ๋ค.
|
| 45 |
+
"""
|
| 46 |
+
return create_pipeline(
|
| 47 |
+
pipeline_type=task_config.get("pipeline", "single_step"),
|
| 48 |
+
input_fields=task_config.get("input_fields", []),
|
| 49 |
+
pipeline_config=task_config.get("pipeline_config"),
|
| 50 |
+
prompts=prompt.get("step_prompts", {}),
|
| 51 |
+
dataset_loader=dataset_loader,
|
| 52 |
+
project_dir=task_project_dir(project_config, dataset_loader),
|
| 53 |
+
config_dir=project_config.get("config_dir"),
|
| 54 |
+
)
|
solar_eval/core/row_filter.py
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""ํ๊ทธ ํ ํํฐ -- `where: {tags, category}` ๋ฌธ๋ฒ๊ณผ ํ ์ ํ (๊ณํ D1~D3).
|
| 2 |
+
|
| 3 |
+
๋ฌธ๋ฒ์ ๋ ํค๋ก ๋ซํ ์๋ค:
|
| 4 |
+
|
| 5 |
+
- `tags`: **AND** -- ํ์ `tags` ๊ฐ ๋์ดํ ํ๊ทธ๋ฅผ ๋ชจ๋ ๊ฐ์ ธ์ผ ํ๋ค(๋ค์ค๊ฐ ๋ผ๋ฒจ).
|
| 6 |
+
- `category`: **OR** -- ํ์ `category`(ํ๋ง๋ค ํ๋)๊ฐ ๋ชฉ๋ก ์ค ํ๋์ฌ์ผ ํ๋ค.
|
| 7 |
+
- ๋ ๋ค ์์ผ๋ฉด AND.
|
| 8 |
+
|
| 9 |
+
์ ํ ์์๋ `where โ limit(์์์ N๊ฐ)` ์ด๊ณ , ์ ํ๋ ํ์ **์๋ณธ ํ์ผ์ 0-based ๋ ์ฝ๋
|
| 10 |
+
์์น**(`sample_idx`)๋ฅผ ๊ทธ๋๋ก ๋ค๊ณ ๊ฐ๋ค -- Experiment Hub ์กฐ์ธยทresumeยทeval_details ๊ฐ ๋ชจ๋ ์ด
|
| 11 |
+
๋ฒํธ๋ฅผ ์ ๋ณธ์ผ๋ก ์ฐ๊ธฐ ๋๋ฌธ์ด๋ค(D2).
|
| 12 |
+
|
| 13 |
+
์ค๋ฅ ๋ฉ์์ง๋ `sample_idx` ์ ํค ์ด๋ฆยทํ์
์ด๋ฆ๋ง ์ฃ๊ณ **ํ ๋ณธ๋ฌธ์ ์ฃ์ง ์๋๋ค**
|
| 14 |
+
(๊ณ ๊ฐ ํ
์คํธ๊ฐ ๋ก๊ทธยท์์ธ๋ก ์์ง ์๊ฒ).
|
| 15 |
+
|
| 16 |
+
์ด ๋ชจ๋์ poc-bundle ํจํค์ง์ packaging ์ ๊ฐ์ ธ์ค์ง ์๋๋ค(D18 -- HF Space ๋ฒค๋ ์ฌ๋ณธ).
|
| 17 |
+
"""
|
| 18 |
+
|
| 19 |
+
from __future__ import annotations
|
| 20 |
+
|
| 21 |
+
from dataclasses import dataclass
|
| 22 |
+
from typing import Any, Iterable, Mapping, Sequence
|
| 23 |
+
|
| 24 |
+
WHERE_KEYS: tuple[str, ...] = ("tags", "category")
|
| 25 |
+
# ์ค๋ฅ ๋ฉ์์ง์ ์ฃ๋ ๋ฌธ์ ํ ๋ฒํธ์ ์ต๋ ๊ฐ์ -- ์ ๋ถ ๋์ดํ๋ฉด ๋ฉ์์ง๊ฐ ๋ฐ์ดํฐ์
ํฌ๊ธฐ๋งํผ ๊ธธ์ด์ง๋ค.
|
| 26 |
+
_MAX_REPORTED_IDX = 5
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class SelectionError(ValueError):
|
| 30 |
+
"""where ๋ฌธ๋ฒ ์ค๋ฅ, ๋ผ๋ฒจ์ด ๋ ๋ถ์ ๋ฐ์ดํฐ์
, 0ํ ์ ํ ๋ฑ -- run ์ ์์ํ์ง ์๋๋ค."""
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
@dataclass(frozen=True)
|
| 34 |
+
class Where:
|
| 35 |
+
"""์ ๊ทํ๋ ํ ํํฐ. ๊ฐ์ ์ ๋ ฌยท์ค๋ณต ์ ๊ฑฐ๋ ํํ์ด๊ณ , ์ง์ ์ ํ ํค๋ `None` ์ด๋ค."""
|
| 36 |
+
|
| 37 |
+
tags: tuple[str, ...] | None = None
|
| 38 |
+
category: tuple[str, ...] | None = None
|
| 39 |
+
|
| 40 |
+
def is_empty(self) -> bool:
|
| 41 |
+
return self.tags is None and self.category is None
|
| 42 |
+
|
| 43 |
+
def to_json(self) -> dict[str, list[str]]:
|
| 44 |
+
"""์ ๊ทํ JSON -- ํค ์ ๋ ฌ(D3: category โ tags), ๊ฐ ์ ๋ ฌ, ์ง์ ์ ํ ํค๋ ์๋ต."""
|
| 45 |
+
out: dict[str, list[str]] = {}
|
| 46 |
+
for key in sorted(WHERE_KEYS):
|
| 47 |
+
values = getattr(self, key)
|
| 48 |
+
if values is not None:
|
| 49 |
+
out[key] = list(values)
|
| 50 |
+
return out
|
| 51 |
+
|
| 52 |
+
def describe(self) -> str:
|
| 53 |
+
"""์ฌ๋์ฉ ํ ์ค ํ๊ธฐ: `category=c tags=a,b` -- ํค ์์๋ `to_json` ๊ณผ ๊ฐ๋ค(์ ๋ ฌ)."""
|
| 54 |
+
return " ".join(f"{k}={','.join(v)}" for k, v in self.to_json().items())
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
@dataclass(frozen=True)
|
| 58 |
+
class RowSelection:
|
| 59 |
+
"""run ์ด ๋ ํ ์งํฉ์ ์ ์: `where`(์์ผ๋ฉด ์ ์ฒด) ๋ค ์์์ `limit` ๊ฐ."""
|
| 60 |
+
|
| 61 |
+
where: Where | None = None
|
| 62 |
+
limit: int | None = None
|
| 63 |
+
|
| 64 |
+
def __post_init__(self) -> None:
|
| 65 |
+
if self.where is not None and self.where.is_empty():
|
| 66 |
+
# ๋น Where ์ None ์ด ๊ฐ์ ๋ป์ธ๋ฐ ์๋ก ๋ค๋ฅด๊ฒ ๋น๊ต๋๋ฉด resume ๊ฐ๋๊ฐ ํ๋ค๋ฆฐ๋ค.
|
| 67 |
+
object.__setattr__(self, "where", None)
|
| 68 |
+
limit = self.limit
|
| 69 |
+
if limit is not None and (isinstance(limit, bool) or not isinstance(limit, int)):
|
| 70 |
+
raise SelectionError(f"limit must be a positive integer, got {type(limit).__name__}")
|
| 71 |
+
if limit is not None and limit < 1:
|
| 72 |
+
raise SelectionError(f"limit must be a positive integer, got {limit}")
|
| 73 |
+
|
| 74 |
+
def to_json(self) -> dict[str, Any]:
|
| 75 |
+
"""run.json `dataset.selection` ์ ๊ธฐ๋กํ๋ ์ ๊ทํ ํํ."""
|
| 76 |
+
return {
|
| 77 |
+
"where": self.where.to_json() if self.where is not None else None,
|
| 78 |
+
"limit": self.limit,
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
@classmethod
|
| 82 |
+
def from_json(cls, obj: Any) -> RowSelection:
|
| 83 |
+
"""`to_json()` ์ ์ญ. resume ์ด run.json ์ ๊ธฐ๋ก๋ ์ ํ์ ๋์ด๋ฆด ๋ ์ด๋ค."""
|
| 84 |
+
if not isinstance(obj, Mapping):
|
| 85 |
+
raise SelectionError(f"selection must be an object, got {type(obj).__name__}")
|
| 86 |
+
unknown = sorted(set(obj) - {"where", "limit"})
|
| 87 |
+
if unknown:
|
| 88 |
+
raise SelectionError(f"selection has unknown keys: {unknown}")
|
| 89 |
+
where = parse_where(obj.get("where"), origin="recorded selection")
|
| 90 |
+
return cls(where=where, limit=obj.get("limit"))
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
def _parse_values(key: str, raw: Any, origin: str) -> tuple[str, ...]:
|
| 94 |
+
"""where ๊ฐ ํ๋ ๊ฒ์ฆ: ๋น์ด ์์ง ์์ ๋ฌธ์์ด์ ๋น์ด ์์ง ์์ ๋ฆฌ์คํธ, ์ค๋ณต ๋ถ๊ฐ.
|
| 95 |
+
|
| 96 |
+
๊ฐ์ ์๋ค ๊ณต๋ฐฑ์ ๋ผ์ด ์ ์ฅํ๋ค -- CLI(`parse_cli_where`)๋ ๋ผ๋ฏ๋ก YAML ๊ณผ CLI ๊ฐ ๊ฐ์
|
| 97 |
+
๊ฐ์ผ๋ก ์ ๊ทํ๋๋ค. ์ค๋ณต ๊ฒ์ฌ๋ ๋ ๊ฐ์ผ๋ก ํ๋ค.
|
| 98 |
+
"""
|
| 99 |
+
if not isinstance(raw, list) or not raw:
|
| 100 |
+
raise SelectionError(f"{origin}: where.{key} must be a non-empty list of strings")
|
| 101 |
+
for value in raw:
|
| 102 |
+
if not isinstance(value, str) or not value.strip():
|
| 103 |
+
raise SelectionError(f"{origin}: where.{key} values must be non-empty strings")
|
| 104 |
+
values = [value.strip() for value in raw]
|
| 105 |
+
duplicates = sorted({v for v in values if values.count(v) > 1})
|
| 106 |
+
if duplicates:
|
| 107 |
+
raise SelectionError(f"{origin}: where.{key} has duplicate values: {duplicates}")
|
| 108 |
+
return tuple(sorted(values))
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def parse_where(obj: Any, origin: str) -> Where | None:
|
| 112 |
+
"""`project.yaml` ๏ฟฝ๏ฟฝ์คํฌ์ `where` ๊ฐ์ฒด(๋๋ ๊ธฐ๋ก๋ ์ ํ)๋ฅผ ๊ฒ์ฆํด `Where` ๋ก ๋ง๋ ๋ค.
|
| 113 |
+
|
| 114 |
+
`None` ์ "ํํฐ ์์". ๋ชจ๋ฅด๋ ํคยท๋น ๋ฆฌ์คํธยท์ค๋ณต์ ์ค๋ฅ๋ค(๋ฌธ๋ฒ์ด ๋ซํ ์์ด์ผ ์คํ๊ฐ
|
| 115 |
+
์กฐ์ฉํ 0ํ์ด ๋์ง ์๋๋ค).
|
| 116 |
+
"""
|
| 117 |
+
if obj is None:
|
| 118 |
+
return None
|
| 119 |
+
if not isinstance(obj, Mapping):
|
| 120 |
+
raise SelectionError(f"{origin}: where must be an object, got {type(obj).__name__}")
|
| 121 |
+
unknown = sorted(str(k) for k in set(obj) - set(WHERE_KEYS))
|
| 122 |
+
if unknown:
|
| 123 |
+
raise SelectionError(
|
| 124 |
+
f"{origin}: where has unknown keys {unknown} (allowed: tags, category)"
|
| 125 |
+
)
|
| 126 |
+
if not obj:
|
| 127 |
+
raise SelectionError(f"{origin}: where is empty -- omit it instead")
|
| 128 |
+
parsed = {key: _parse_values(key, obj[key], origin) for key in WHERE_KEYS if key in obj}
|
| 129 |
+
return Where(**parsed)
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
def parse_cli_where(values: Iterable[str]) -> Where | None:
|
| 133 |
+
"""CLI `--where key=v1,v2` (๋ฐ๋ณต ๊ฐ๋ฅ)๋ฅผ ํ๋์ `Where` ๋ก ์ ๋๋ค.
|
| 134 |
+
|
| 135 |
+
๋ฐ๋ณต์ AND ๋ค: `tags` ๋ ํฉ์งํฉ(๋ชจ๋ ๊ฐ์ ธ์ผ ํจ), `category` ๋ ๊ต์งํฉ -- `combine` ๊ณผ
|
| 136 |
+
๊ฐ์ ๊ท์น์ด๋ค.
|
| 137 |
+
"""
|
| 138 |
+
result: Where | None = None
|
| 139 |
+
for raw in values:
|
| 140 |
+
key, sep, rest = raw.partition("=")
|
| 141 |
+
key = key.strip()
|
| 142 |
+
if not sep:
|
| 143 |
+
raise SelectionError(f"--where {raw!r}: expected key=value1,value2")
|
| 144 |
+
if key not in WHERE_KEYS:
|
| 145 |
+
raise SelectionError(f"--where {raw!r}: unknown key {key!r} (allowed: tags, category)")
|
| 146 |
+
items = [part.strip() for part in rest.split(",")]
|
| 147 |
+
if not rest.strip() or any(not part for part in items):
|
| 148 |
+
raise SelectionError(f"--where {raw!r}: empty value")
|
| 149 |
+
one = parse_where({key: items}, origin=f"--where {raw!r}")
|
| 150 |
+
result = combine(result, one)
|
| 151 |
+
return result
|
| 152 |
+
|
| 153 |
+
|
| 154 |
+
def combine(task: Where | None, cli: Where | None) -> Where | None:
|
| 155 |
+
"""ํ์คํฌ where ์ CLI where ๋ฅผ AND ๋ก ํฉ์น๋ค.
|
| 156 |
+
|
| 157 |
+
`tags` ๋ ํฉ์งํฉ(๋ชจ๋ ๊ฐ์ ธ์ผ ํจ), `category` ๋ ๊ต์งํฉ(๋ ๋ค ์์ ๋). ๊ต์งํฉ์ด ๋น๋ฉด
|
| 158 |
+
์ด๋ค ํ๋ ๊ณ ๋ฅผ ์ ์์ผ๋ฏ๋ก run ์ ์์ํ๊ธฐ ์ ์ ์คํจํ๋ค.
|
| 159 |
+
"""
|
| 160 |
+
if task is None or task.is_empty():
|
| 161 |
+
return cli if cli is not None and not cli.is_empty() else None
|
| 162 |
+
if cli is None or cli.is_empty():
|
| 163 |
+
return task
|
| 164 |
+
tags = _merge_optional(task.tags, cli.tags, lambda a, b: a | b)
|
| 165 |
+
category = _merge_optional(task.category, cli.category, lambda a, b: a & b)
|
| 166 |
+
if category is not None and not category:
|
| 167 |
+
raise SelectionError(
|
| 168 |
+
f"where category has no overlap: {list(task.category or ())} "
|
| 169 |
+
f"vs {list(cli.category or ())}"
|
| 170 |
+
)
|
| 171 |
+
return Where(tags=tags, category=category)
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
def _merge_optional(a, b, op) -> tuple[str, ...] | None:
|
| 175 |
+
if a is None:
|
| 176 |
+
return b
|
| 177 |
+
if b is None:
|
| 178 |
+
return a
|
| 179 |
+
return tuple(sorted(op(set(a), set(b))))
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
def format_indices(indices: Sequence[int]) -> str:
|
| 183 |
+
"""์ค๋ฅ ๋ฉ์์ง์ฉ `sample_idx 1, 4, ... (+N more)` -- ํ ๋ณธ๋ฌธ ์์ด ๋ฒํธ๋ง."""
|
| 184 |
+
shown = ", ".join(str(i) for i in indices[:_MAX_REPORTED_IDX])
|
| 185 |
+
more = (
|
| 186 |
+
f" (+{len(indices) - _MAX_REPORTED_IDX} more)" if len(indices) > _MAX_REPORTED_IDX else ""
|
| 187 |
+
)
|
| 188 |
+
return f"sample_idx {shown}{more}"
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
def _check_labels(records: Sequence[Any], where: Where) -> None:
|
| 192 |
+
"""where ๊ฐ ๋ณด๋ ํค๊ฐ ๋ชจ๋ ํ์ ์ฌ๋ฐ๋ฅธ ํ์
์ผ๋ก ์๋์ง -- ๋ ๋ถ์ ๋ผ๋ฒจ์ ์กฐ์ฉํ ์๋ฅด์ง ์๋๋ค."""
|
| 193 |
+
problems: dict[str, list[int]] = {}
|
| 194 |
+
for idx, row in enumerate(records):
|
| 195 |
+
if not isinstance(row, Mapping):
|
| 196 |
+
problems.setdefault("row is not an object", []).append(idx)
|
| 197 |
+
continue
|
| 198 |
+
if where.tags is not None:
|
| 199 |
+
_check_tags(row, idx, problems)
|
| 200 |
+
if where.category is not None:
|
| 201 |
+
_check_category(row, idx, problems)
|
| 202 |
+
if problems:
|
| 203 |
+
details = "; ".join(
|
| 204 |
+
f"{reason} at {format_indices(idx)}" for reason, idx in problems.items()
|
| 205 |
+
)
|
| 206 |
+
raise SelectionError(
|
| 207 |
+
f"dataset rows cannot be filtered by where {where.describe()}: {details}"
|
| 208 |
+
)
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
def _check_tags(row: Mapping[str, Any], idx: int, problems: dict[str, list[int]]) -> None:
|
| 212 |
+
if "tags" not in row:
|
| 213 |
+
problems.setdefault("missing 'tags'", []).append(idx)
|
| 214 |
+
elif not isinstance(row["tags"], list):
|
| 215 |
+
problems.setdefault("'tags' is not a list", []).append(idx)
|
| 216 |
+
elif not all(isinstance(t, str) for t in row["tags"]):
|
| 217 |
+
problems.setdefault("'tags' has a non-string element", []).append(idx)
|
| 218 |
+
|
| 219 |
+
|
| 220 |
+
def _check_category(row: Mapping[str, Any], idx: int, problems: dict[str, list[int]]) -> None:
|
| 221 |
+
if "category" not in row:
|
| 222 |
+
problems.setdefault("missing 'category'", []).append(idx)
|
| 223 |
+
elif row["category"] is not None and not isinstance(row["category"], str):
|
| 224 |
+
# `category: null`(apply --lenient ๊ฐ ๋จ๊ธฐ๋ ํ)์ ํ์
์ค๋ฅ๊ฐ ์๋๋ผ ๋ถ์ผ์น๋ค.
|
| 225 |
+
problems.setdefault("'category' is not a string", []).append(idx)
|
| 226 |
+
|
| 227 |
+
|
| 228 |
+
def _absent_values(records: Sequence[Mapping[str, Any]], where: Where) -> dict[str, list[str]]:
|
| 229 |
+
"""where ๊ฐ ์ค ๋ฐ์ดํฐ์
์ **์ด๋ ํ์๋** ์๋ ๊ฐ -- ์คํยท๋์๋ฌธ์ ์ฐจ์ด๋ฅผ ์ก๋๋ค.
|
| 230 |
+
|
| 231 |
+
ํํฐ ๊ฒฐ๊ณผ๊ฐ ์๋๋ผ ํ์ผ ์ ์ฒด์ ๋์กฐํ๋ค: `tags=a, category=x,y` ์์ `y` ๊ฐ ํ์ผ์ ์์ง๋ง
|
| 232 |
+
`a` ํ๊ทธ ํ์๋ง ์๋ ๊ฒ์ ์ ๋นํ ๋น ์กฐํฉ์ด์ง ์คํ๊ฐ ์๋๋ค. ๋ผ๋ฒจ ๊ฒ์ฌ(`_check_labels`)๋ฅผ
|
| 233 |
+
ํต๊ณผํ ๋ค์ ๋ถ๋ฅด๋ฏ๋ก ํ์
์ ์ด๋ฏธ ๋ง๋ค.
|
| 234 |
+
"""
|
| 235 |
+
absent: dict[str, list[str]] = {}
|
| 236 |
+
if where.category is not None:
|
| 237 |
+
present = {row["category"] for row in records}
|
| 238 |
+
missing = [v for v in where.category if v not in present]
|
| 239 |
+
if missing:
|
| 240 |
+
absent["category"] = missing
|
| 241 |
+
if where.tags is not None:
|
| 242 |
+
present = {tag for row in records for tag in row["tags"]}
|
| 243 |
+
missing = [v for v in where.tags if v not in present]
|
| 244 |
+
if missing:
|
| 245 |
+
absent["tags"] = missing
|
| 246 |
+
return absent
|
| 247 |
+
|
| 248 |
+
|
| 249 |
+
def _describe_absent(absent: dict[str, list[str]]) -> str:
|
| 250 |
+
return "; ".join(f"{key} {values}" for key, values in absent.items())
|
| 251 |
+
|
| 252 |
+
|
| 253 |
+
def _matches(row: Mapping[str, Any], where: Where) -> bool:
|
| 254 |
+
if where.tags is not None and not set(where.tags).issubset(row["tags"]):
|
| 255 |
+
return False
|
| 256 |
+
return where.category is None or row["category"] in where.category
|
| 257 |
+
|
| 258 |
+
|
| 259 |
+
def select(
|
| 260 |
+
records: Sequence[dict[str, Any]], selection: RowSelection | None
|
| 261 |
+
) -> list[tuple[int, dict[str, Any]]]:
|
| 262 |
+
"""์ ํ์ ์ ์ฉํด `(์๋ณธ idx, ํ)` ๋ชฉ๋ก์ ๋๋ ค์ค๋ค. ์์๋ ์๋ณธ ์์.
|
| 263 |
+
|
| 264 |
+
`selection` ์ด `None` ์ด๋ฉด ์ ์ฒด. where ๊ฐ ์๋๋ฐ 0ํ์ด๋ฉด `SelectionError`
|
| 265 |
+
(์กฐ์ฉํ ๋น run ์ ๋ง๋ค์ง ์๋๋ค). where ์์ด ๋น ํ์ผ์ธ ๊ฒฝ์ฐ๋ ์ค๋ ๋์ ๊ทธ๋๋ก ๋๋ค.
|
| 266 |
+
where ๊ฐ ์ค ํ์ผ์ ์ด๋ ํ์๋ ์๋ ๊ฐ์ด ์์ด๋ `SelectionError` ๋ค -- `category` ๋
|
| 267 |
+
OR ๋ผ ์คํ ํ๋๊ฐ 0ํ์ด ์๋๋ผ "์กฐ์ฉํ ์ค์ด๋ ์ ํ"์ด ๋๊ธฐ ๋๋ฌธ์ด๋ค(D1 ์ด ํํ์
|
| 268 |
+
๋ฌธ์์ด์ ๋ฒ๋ฆฐ ๊ฒ๊ณผ ๊ฐ์ ์ด์ ).
|
| 269 |
+
"""
|
| 270 |
+
indexed = list(enumerate(records))
|
| 271 |
+
if selection is None:
|
| 272 |
+
return indexed
|
| 273 |
+
where = selection.where
|
| 274 |
+
if where is not None:
|
| 275 |
+
_check_labels(records, where)
|
| 276 |
+
absent = _absent_values(records, where)
|
| 277 |
+
indexed = [(idx, row) for idx, row in indexed if _matches(row, where)]
|
| 278 |
+
if not indexed:
|
| 279 |
+
hint = f" (not in any row: {_describe_absent(absent)})" if absent else ""
|
| 280 |
+
raise SelectionError(f"where {where.describe()} matched 0 of {len(records)} rows{hint}")
|
| 281 |
+
if absent:
|
| 282 |
+
raise SelectionError(
|
| 283 |
+
f"where value(s) not in any of {len(records)} rows: {_describe_absent(absent)} "
|
| 284 |
+
"-- check spelling/case, or drop them"
|
| 285 |
+
)
|
| 286 |
+
if selection.limit is not None:
|
| 287 |
+
indexed = indexed[: selection.limit]
|
| 288 |
+
return indexed
|
solar_eval/core/run_records.py
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""run ์ฐ์ถ๋ฌผ ๋ ์ฝ๋ ์กฐ๋ฆฝ -- results ํ ยท eval_details ํ ยท evaluation ๋ฌธ์.
|
| 2 |
+
|
| 3 |
+
๋ฌ๋(`execute_run`)์ `runs eval`(์ฌ์ฑ์ ) CLI ๊ฐ ๊ฐ์ ๋ชจ์์ ์ฐ๋๋ก ํ ๊ณณ์ ๋๋ค
|
| 4 |
+
(์์ ์ ๋ ๊ณณ์ ๋ณต๋ถ๋ผ ์์๋ค). ์ ์ฅ ํค ์ด๋ฆ์ eval-store ์ `resultRowSchema`ยท
|
| 5 |
+
`evaluationJsonSchema` ์ ํ์ ํธํ์ด์ด์ผ ํ๋ฏ๋ก ๋ฐ๊พธ์ง ์๋๋ค.
|
| 6 |
+
|
| 7 |
+
`evaluation` ๋ฌธ์์ `eval_model` ์ **์ธ์**๋ค -- ์ด๋ค judge ๋ชจ๋ธ๋ก ์ฑ์ ํ๋์ง๋
|
| 8 |
+
ํธ์ถ์(ํ๊ฐ๊ธฐ)๊ฐ ์๋ค. ์ด ๋ชจ๋์ด ๊ฐ์ ์ง์ด๋ด์ง ์๋๋ค.
|
| 9 |
+
|
| 10 |
+
์ด ๋ชจ๋์ poc-bundle ํจํค์ง์ packaging ์ ๊ฐ์ ธ์ค์ง ์๋๋ค(D18).
|
| 11 |
+
"""
|
| 12 |
+
|
| 13 |
+
from __future__ import annotations
|
| 14 |
+
|
| 15 |
+
from typing import Any, Mapping, Sequence
|
| 16 |
+
|
| 17 |
+
from solar_eval.models.sample import EvalSample
|
| 18 |
+
|
| 19 |
+
#: run.json ์์ ์ถ๋ก ์คํจ ์ํ์ ์์ธ ํ์
๋ชฉ๋ก์ด ์ฌ๋ ํค(LP5 D-LP5-4). ์ด๋ฆ์ ์ ๋ณธ์ ์ฌ๊ธฐ ํ ๊ณณ์ด๋ค --
|
| 20 |
+
#: CLI ์ ์คํจ ์ง๋ฌธ ๋์ฐ๋ฏธ(`cli/commands/failure_keys.py`)๋ ์ด ์์๋ก ์ฝ๋๋ค.
|
| 21 |
+
FAILED_SAMPLE_ERRORS_KEY = "failed_sample_errors"
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def build_failure_errors(errors: Sequence[tuple[int, BaseException]]) -> list[dict[str, Any]]:
|
| 25 |
+
"""์ถ๋ก ์คํจ `(์๋ณธ sample_idx, ์์ธ)` ๋ชฉ๋ก โ run.json `failed_sample_errors` ์์๋ค.
|
| 26 |
+
|
| 27 |
+
์์๋ `{"sample_idx", "error_type"}` ๋ ํค์ด๊ณ `error_type` ์ ์์ธ **ํ์
์ด๋ฆ**๋ฟ์ด๋ค --
|
| 28 |
+
๋ฉ์์ง๋ ์
๋ ฅ ์กฐ๊ฐ์ ๋ด์ ์ ์์ด ์ฃ์ง ์๋๋ค. `sample_idx` ์ค๋ฆ์ฐจ์, ์คํจ๊ฐ ์์ผ๋ฉด `[]`.
|
| 29 |
+
"""
|
| 30 |
+
ordered = sorted(errors, key=lambda pair: pair[0])
|
| 31 |
+
return [{"sample_idx": idx, "error_type": type(exc).__name__} for idx, exc in ordered]
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def extract_input(sample: Mapping[str, Any], input_fields: Sequence[str]) -> dict[str, Any]:
|
| 35 |
+
"""๋ฐ์ดํฐ ํ์์ `input_fields` ๋ง ๊ณจ๋ผ ์
๋ ฅ dict ๋ฅผ ๋ง๋ ๋ค(์๋ ํค๋ ๋น ๋ฌธ์์ด)."""
|
| 36 |
+
return {field: sample.get(field, "") for field in input_fields}
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def extract_golden(sample: Mapping[str, Any], task_config: Mapping[str, Any]) -> Any:
|
| 40 |
+
"""๋ฐ์ดํฐ ํ์์ ์ ๋ต(golden)์ ๊บผ๋ธ๋ค -- `golden_field` ์ฐ์ , ๋ค์ `golden_fields`."""
|
| 41 |
+
golden_field = task_config.get("golden_field")
|
| 42 |
+
golden_fields = task_config.get("golden_fields")
|
| 43 |
+
if golden_field:
|
| 44 |
+
return sample.get(golden_field, "")
|
| 45 |
+
if golden_fields:
|
| 46 |
+
return {k: sample.get(v, "") for k, v in golden_fields.items()}
|
| 47 |
+
return ""
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def build_eval_sample_from_result(result: dict[str, Any], reference: Any) -> EvalSample:
|
| 51 |
+
"""์ ์ฅ๋ ๊ฒฐ๊ณผ dict(๋ ๊ฑฐ์ 8+trace ํค)์์ ์ฑ์ ์ฉ `EvalSample` ์ ๋์ด๋ฆฐ๋ค.
|
| 52 |
+
|
| 53 |
+
`insert_run_result` ๊ฐ ์ฐ๋ ํค ์ด๋ฆ(`golden`/`trace`)์ `EvalSample` ํ๋ ์ด๋ฆ
|
| 54 |
+
(`reference`/`artifacts`)๊ณผ ๋ค๋ฅด๋ค -- eval-store ํ์ ํธํ์ ์ํด ์ ์ฅ ์คํค๋ง๋
|
| 55 |
+
๊ทธ๋๋ก ๋๊ณ (๋ง์ด๊ทธ๋ ์ด์
๊ณํ ยง9-1) ์ฑ์ ์ง์ ์๋ง ์ฌ๊ธฐ์ ๋๋๋ฆฐ๋ค. `execute_run`
|
| 56 |
+
๋ด๋ถ ๋ฃจํ์ `runs eval` CLI(์ฌ์ฑ์ , ์ ์ถ๋ก ์์ด ๋์คํฌ์ `results.jsonl` ์
|
| 57 |
+
๊ทธ๋๋ก ์) ์์ชฝ์์ ๊ณต์ ํ๋ค.
|
| 58 |
+
|
| 59 |
+
Args:
|
| 60 |
+
result: `insert_run_result` ์ ๋๊ธด ๊ฒ๊ณผ ๊ฐ์ ํํ(๋๋ `JsonlStore.
|
| 61 |
+
load_existing_results()` ๋ก ๋์คํฌ์์ ๋ค์ ์ฝ์ ๊ฒ). `input`/`output`/
|
| 62 |
+
`trace` ํค๋ฅผ ์ฝ๋๋ค.
|
| 63 |
+
reference: ์ ๋ต(golden) ๊ฐ. ํธ์ถ์๊ฐ ์ง์ ๋๊ธด๋ค -- ๋ ํธ์ถ์์ golden
|
| 64 |
+
์ถ์ถ ๊ฒฝ๋ก๊ฐ ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ด๋ค(์๋ ์ฐธ๊ณ ). ์ด ํจ์๋ ์ด๋ ์ชฝ์ธ์ง ๋ชจ๋ฅธ ์ฑ
|
| 65 |
+
๊ฐ๋ง ๋ฐ์ ๊ทธ๋๋ก `sample.reference` ์ ์ฑ์ด๋ค.
|
| 66 |
+
|
| 67 |
+
- `execute_run` ๋ด๋ถ ๋ฃจํ: `_golden_raw`(๋์คํฌ์ ์ ๋จ๋ ์คํ ์ค
|
| 68 |
+
์์ ํค, resume ์์๋ ๋งค ์ํ๋ง๋ค ์๋ก ๊ณ์ฐ๋จ)๋ฅผ ๋๊ธด๋ค.
|
| 69 |
+
- `runs eval` CLI: ๋์คํฌ์ ์ ์ฅ๋ `golden` ํค๋ฅผ ๊ทธ๋๋ก ๋๊ธด๋ค
|
| 70 |
+
(`_golden_raw` ๋ ์ ์ด์ ์ ์ฅ๋์ง ์์ CLI ์ชฝ์ ์๋ค).
|
| 71 |
+
|
| 72 |
+
Returns:
|
| 73 |
+
`input`/`output`/`reference`/`artifacts` ๊ฐ ์ฑ์์ง `EvalSample`.
|
| 74 |
+
`contexts` ๋ ์์ง ์๋ฌด๋ ์ ์จ์ ๊ธฐ๋ณธ๊ฐ(`None`) ๊ทธ๋๋ก๋ค.
|
| 75 |
+
"""
|
| 76 |
+
trace = result.get("trace") or {}
|
| 77 |
+
return EvalSample(
|
| 78 |
+
input=result.get("input"),
|
| 79 |
+
output=result.get("output"),
|
| 80 |
+
reference=reference,
|
| 81 |
+
artifacts=trace,
|
| 82 |
+
)
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def _usage_mapping(usage: Any) -> Mapping[str, Any]:
|
| 86 |
+
"""artifacts ์ `usage` ๋ฅผ ์ฝ์ ์ ์๋ dict ๋ก -- dict ๊ฐ ์๋๋ฉด(์์ยท`None` ํฌํจ) ๋น dict.
|
| 87 |
+
|
| 88 |
+
๋น dict ์์ ๊บผ๋ธ ํ ํฐ ์นธ์ `None` ์ด ๋๋ค(์ธก์ ์์). ์์ ์๋ `usage: None` ์ธ ์ํ์ด
|
| 89 |
+
`None.get` ์ผ๋ก ํ ์กฐ๋ฆฝ ์์ฒด๋ฅผ ์ฃฝ์๋ค.
|
| 90 |
+
"""
|
| 91 |
+
return usage if isinstance(usage, Mapping) else {}
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def build_result_row(
|
| 95 |
+
run_id: str,
|
| 96 |
+
sample_idx: int,
|
| 97 |
+
input_data: dict[str, Any],
|
| 98 |
+
golden: Any,
|
| 99 |
+
eval_sample: EvalSample,
|
| 100 |
+
meta: dict[str, Any] | None = None,
|
| 101 |
+
) -> dict[str, Any]:
|
| 102 |
+
"""results.jsonl ํ ํ. ์ค๋์ 8ํค + `trace`, ๊ทธ๋ฆฌ๊ณ `meta_fields` ๊ฐ ์์ ๋๋ง `meta`.
|
| 103 |
+
|
| 104 |
+
`sample_idx` ๋ **์๋ณธ ํ์ผ์ 0-based ๋ ์ฝ๋ ์์น**๋ค(ํํฐยทlimit ๋ค์๋, D2).
|
| 105 |
+
|
| 106 |
+
trace ๋ artifacts ์ ์ฒด๋ฅผ ๊ทธ๋๋ก ๋ฃ๋๋ค -- step_outputs ๋ฟ ์๋๋ผ v24 ๊ฐ ์ฑ์ฐ๋
|
| 107 |
+
judge_decisions/judge_tool_calls/self_consistency_runs/corrections ๋ ์ฌ๊ธฐ ์ ๋ฃ์ผ๋ฉด
|
| 108 |
+
์ ์ฅ ์ง์ ์ ํต์งธ๋ก ๋ฒ๋ ค์ง๋ค (์ค์ judge LLM ํธ์ถยทself-consistency ๋ฐ๋ณต ํธ์ถ ๋น์ฉ์ด
|
| 109 |
+
๋๊ฐ ์ฐ์ถ๋ฌผ์ด๋ค). ํน์ ํค๋ง ํ๋์ฝ๋ฉํด ์ฎ๊ธฐ๋ฉด ๋ค์์ ํ์ดํ๋ผ์ธ์ด ์ artifacts ํค๋ฅผ
|
| 110 |
+
์ถ๊ฐํ ๋๋ง๋ค ์ฌ๊ธฐ๋ฅผ ๋ ๊ณ ์ณ์ผ ํ๋ฏ๋ก ํต์งธ๋ก ๋๊ธด๋ค -- resultRowSchema.trace ๋
|
| 111 |
+
.passthrough() ๋ผ ์ฌ๋ถ ํค๋ฅผ ๊ทธ๋๋ก ๋ฐ๋๋ค (source-schemas.ts:223-228).
|
| 112 |
+
|
| 113 |
+
`meta` ๋ input ์ด ์๋๋ค -- ๋ฐ์ดํฐ ํ์ ํ๊ทธยท์ถ์ฒ ๋ณต์ฌ๋ณธ์ด๊ณ (D13), `meta_fields`
|
| 114 |
+
์ ์ธ์ด ์์ผ๋ฉด ํค ์์ฒด๋ฅผ ์ฐ์ง ์์ ๋ค๋ฅธ ํ๋ก์ ํธ์ results ๋ฐ์ดํธ๊ฐ ๊ทธ๋๋ก๋ค.
|
| 115 |
+
|
| 116 |
+
์ฌ์ฉ๋ยท์ง์ฐ(`input_tokens`ยท`output_tokens`ยท`inference_time_ms`)์ artifacts ์ ์์ผ๋ฉด
|
| 117 |
+
`None` ์ด๋ค(L6b B1 ยท D-M2-24) -- `0` ์ ์ง์ด ๋ฃ์ผ๋ฉด "์ธก์ ์์" ์ด "0 ์ ์ผ๋ค" ๋
|
| 118 |
+
์ธก์ ๊ฐ๊ณผ ๊ตฌ๋ถ๋์ง ์๋๋ค. ๊ฐ์ด ์์ผ๋ฉด ๊ทธ๋๋ก ์ฎ๊ธด๋ค(`0` ๋ ๊ทธ๋๋ก) -- ์ฌ์ฉ๋์ด ์๋
|
| 119 |
+
ํ์ ๋ฐ์ดํธ๋ ์์ ๊ณผ ๊ฐ๋ค.
|
| 120 |
+
"""
|
| 121 |
+
usage = _usage_mapping(eval_sample.artifacts.get("usage"))
|
| 122 |
+
row: dict[str, Any] = {
|
| 123 |
+
"run_id": run_id,
|
| 124 |
+
"sample_idx": sample_idx,
|
| 125 |
+
"input": input_data,
|
| 126 |
+
"output": eval_sample.output,
|
| 127 |
+
"golden": golden,
|
| 128 |
+
"input_tokens": usage.get("prompt_tokens"),
|
| 129 |
+
"output_tokens": usage.get("completion_tokens"),
|
| 130 |
+
"inference_time_ms": eval_sample.artifacts.get("inference_time_ms"),
|
| 131 |
+
"trace": {
|
| 132 |
+
"step_outputs": {},
|
| 133 |
+
**eval_sample.artifacts,
|
| 134 |
+
},
|
| 135 |
+
}
|
| 136 |
+
if meta is not None:
|
| 137 |
+
row["meta"] = meta
|
| 138 |
+
return row
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
def build_evaluation_doc(
|
| 142 |
+
run_id: str,
|
| 143 |
+
aggregated: Mapping[str, Any],
|
| 144 |
+
eval_results: Sequence[Mapping[str, Any]],
|
| 145 |
+
eval_failures: Sequence[Mapping[str, Any]],
|
| 146 |
+
eval_model: str | None,
|
| 147 |
+
) -> dict[str, Any]:
|
| 148 |
+
"""evaluation.json ๋ฌธ์.
|
| 149 |
+
|
| 150 |
+
์ฑ์ ์ ์ฑ๊ณตํ ์ํ์ด ํ๋๋ ์์ผ๋ฉด ์ ์ ์๋ฆฌ๋ฅผ ๋น์ด๋ค -- aggregate() ๋ ๋น ์
๋ ฅ์
|
| 151 |
+
0.0 ์ ๋๋ ค์ฃผ๋๋ฐ, ๊ทธ๊ฑด "0์ ์ ๋ฐ์๋ค"๋ ์ธก์ ๊ฐ์ด๋ผ "์ธก์ ์์ฒด๊ฐ ์์๋ค"์ ๊ตฌ๋ถ๋์ง
|
| 152 |
+
์๋๋ค. judge ๊ฐ ํต์งธ๋ก ์ฃฝ์ run ์ด Experiment Hub ์ฐจํธ์์ ํ์ง ๊ธ๋ฝ์ผ๋ก ๋ณด์ด๋ฉด ์ ๋๋ค.
|
| 153 |
+
"""
|
| 154 |
+
overall_score = aggregated.get("overall_score", 0.0) if eval_results else None
|
| 155 |
+
return {
|
| 156 |
+
"run_id": run_id,
|
| 157 |
+
"scores": aggregated.get("scores", {}),
|
| 158 |
+
"overall_score": overall_score,
|
| 159 |
+
"eval_model": eval_model,
|
| 160 |
+
"eval_success_count": len(eval_results),
|
| 161 |
+
"eval_failed_count": len(eval_failures),
|
| 162 |
+
"failed_sample_indices": [f["sample_idx"] for f in eval_failures],
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
def build_eval_detail_docs(
|
| 167 |
+
evaluation_id: Any,
|
| 168 |
+
eval_results: Sequence[Mapping[str, Any]],
|
| 169 |
+
eval_failures: Sequence[Mapping[str, Any]],
|
| 170 |
+
) -> list[dict[str, Any]]:
|
| 171 |
+
"""eval_details.jsonl ํ๋ค -- ์ฑ๊ณต/์คํจ ๋ ๋ค ํ ํ์ฉ ๋จ๊ธด๋ค.
|
| 172 |
+
|
| 173 |
+
results.jsonl ๊ณผ eval_details.jsonl ์ด ํญ์ ๊ฐ์ sample_idx ์งํฉ์ ๊ฐ๋ฆฌํค๊ฒ ํด์
|
| 174 |
+
๋ถ๋ถ์ ์ผ๋ก๋ง ์ฑ์ ๋ run ์์ "์ด ์ํ์ ์ ์ ๋ณด์ด์ง"๋ฅผ ์์ค๋ค.
|
| 175 |
+
"""
|
| 176 |
+
docs: list[dict[str, Any]] = []
|
| 177 |
+
for er in eval_results:
|
| 178 |
+
docs.append(
|
| 179 |
+
{
|
| 180 |
+
"evaluation_id": evaluation_id,
|
| 181 |
+
"sample_idx": er["sample_idx"],
|
| 182 |
+
"category_scores": er.get("category_scores", {}),
|
| 183 |
+
"error_counts": {
|
| 184 |
+
k: v.get("error_count", 0)
|
| 185 |
+
for k, v in er.get("details", {}).items()
|
| 186 |
+
if isinstance(v, dict)
|
| 187 |
+
},
|
| 188 |
+
"severity": er.get("severity", ""),
|
| 189 |
+
"score": er.get("score", 0.0),
|
| 190 |
+
}
|
| 191 |
+
)
|
| 192 |
+
for f in eval_failures:
|
| 193 |
+
docs.append(
|
| 194 |
+
{
|
| 195 |
+
"evaluation_id": evaluation_id,
|
| 196 |
+
"sample_idx": f["sample_idx"],
|
| 197 |
+
"category_scores": {},
|
| 198 |
+
"error_counts": {},
|
| 199 |
+
"severity": None,
|
| 200 |
+
# 0.0 ์ด ์๋๋ผ None -- ์ฑ์ ์คํจ๋ฅผ ์ต์ ์ ์์ ๊ตฌ๋ถํ๋ค (F1).
|
| 201 |
+
"score": None,
|
| 202 |
+
"error": f["error"],
|
| 203 |
+
}
|
| 204 |
+
)
|
| 205 |
+
return docs
|
solar_eval/core/runner.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
| 1 |
import asyncio
|
| 2 |
import logging
|
| 3 |
import sys
|
|
|
|
| 4 |
from datetime import datetime, timezone
|
| 5 |
from typing import Any, Callable
|
| 6 |
|
|
@@ -8,51 +9,106 @@ from typing import Any, Callable
|
|
| 8 |
logging.basicConfig(stream=sys.stderr, level=logging.INFO, format="%(message)s")
|
| 9 |
|
| 10 |
from solar_eval.core.dataset_loader import DatasetLoader
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
from solar_eval.evaluators.registry import create_evaluator
|
| 12 |
from solar_eval.models.enums import RunStatus
|
| 13 |
from solar_eval.models.sample import EvalSample
|
| 14 |
-
from solar_eval.pipelines.
|
| 15 |
from solar_eval.providers.base import BaseProvider
|
| 16 |
from solar_eval.stores.base import ResultStore
|
| 17 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
logger = logging.getLogger(__name__)
|
| 19 |
|
| 20 |
OnProgress = Callable[[dict[str, Any]], Any]
|
| 21 |
|
|
|
|
|
|
|
| 22 |
|
| 23 |
-
def build_eval_sample_from_result(result: dict[str, Any], reference: Any) -> EvalSample:
|
| 24 |
-
"""์ ์ฅ๋ ๊ฒฐ๊ณผ dict(๋ ๊ฑฐ์ 8+trace ํค)์์ ์ฑ์ ์ฉ `EvalSample` ์ ๋์ด๋ฆฐ๋ค.
|
| 25 |
-
|
| 26 |
-
`insert_run_result` ๊ฐ ์ฐ๋ ํค ์ด๋ฆ(`golden`/`trace`)์ `EvalSample` ํ๋ ์ด๋ฆ
|
| 27 |
-
(`reference`/`artifacts`)๊ณผ ๋ค๋ฅด๋ค -- eval-store ํ์ ํธํ์ ์ํด ์ ์ฅ ์คํค๋ง๋
|
| 28 |
-
๊ทธ๋๋ก ๋๊ณ (๋ง์ด๊ทธ๋ ์ด์
๊ณํ ยง9-1) ์ฑ์ ์ง์ ์๋ง ์ฌ๊ธฐ์ ๋๋๋ฆฐ๋ค. `execute_run`
|
| 29 |
-
๋ด๋ถ ๋ฃจํ์ `runs eval` CLI(์ฌ์ฑ์ , ์ ์ถ๋ก ์์ด ๋์คํฌ์ `results.jsonl` ์
|
| 30 |
-
๊ทธ๋๋ก ์) ์์ชฝ์์ ๊ณต์ ํ๋ค.
|
| 31 |
-
|
| 32 |
-
Args:
|
| 33 |
-
result: `insert_run_result` ์ ๋๊ธด ๊ฒ๊ณผ ๊ฐ์ ํํ(๋๋ `JsonlStore.
|
| 34 |
-
load_existing_results()` ๋ก ๋์คํฌ์์ ๋ค์ ์ฝ์ ๊ฒ). `input`/`output`/
|
| 35 |
-
`trace` ํค๋ฅผ ์ฝ๋๋ค.
|
| 36 |
-
reference: ์ ๋ต(golden) ๊ฐ. ํธ์ถ์๊ฐ ์ง์ ๋๊ธด๋ค -- ๋ ํธ์ถ์์ golden
|
| 37 |
-
์ถ์ถ ๊ฒฝ๋ก๊ฐ ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ด๋ค(์๋ ์ฐธ๊ณ ). ์ด ํจ์๋ ์ด๋ ์ชฝ์ธ์ง ๋ชจ๋ฅธ ์ฑ
|
| 38 |
-
๊ฐ๋ง ๋ฐ์ ๊ทธ๋๋ก `sample.reference` ์ ์ฑ์ด๋ค.
|
| 39 |
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
|
|
|
|
|
|
|
|
|
| 44 |
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
|
|
|
| 48 |
"""
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
|
| 57 |
|
| 58 |
class BatchRunner:
|
|
@@ -80,27 +136,31 @@ class BatchRunner:
|
|
| 80 |
max_workers: int = 5,
|
| 81 |
limit: int | None = None,
|
| 82 |
completed_results: list[dict[str, Any]] | None = None,
|
|
|
|
|
|
|
|
|
|
| 83 |
) -> None:
|
| 84 |
"""Execute a full inference + evaluation run.
|
| 85 |
|
| 86 |
Args:
|
| 87 |
completed_results: Previously completed results for resume.
|
| 88 |
Samples with matching sample_idx will be skipped.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
|
| 90 |
์ํ ๋จ์ ์คํจ(์ถ๋ก /ํ๊ฐ)๋ ์ผํค๊ณ ์ํ(PARTIAL/eval_failed_count)๋ก ๊ธฐ๋กํ์ง๋ง,
|
| 91 |
-
run ์ ํต์งธ๋ก ๋ชป ๋๊ฒ ๋ง๋๋ ์์ธ(๋ฐ์ดํฐ์
๋ก๋ ์คํจ,
|
| 92 |
-
์คํจ, ์คํ ์ด ์ฐ๊ธฐ ์คํจ ๋ฑ)๋ ์ํ๋ฅผ FAILED ๋ก ๋จ๊ธด ๋ค
|
| 93 |
-
"๋ฌด์จ ์ผ์ด ์์ด๋ ์์ธ ์์ด ๋ฐํ"์ด ์๋๋ค. ํธ์ถ์๋ ์ด
|
| 94 |
-
์๋ค๊ณ ๊ฐ์ ํด์ผ ํ๋ค.
|
| 95 |
"""
|
| 96 |
try:
|
| 97 |
-
# Build set of already-completed sample indices for resume
|
| 98 |
-
completed_by_idx: dict[int, dict[str, Any]] = {}
|
| 99 |
-
if completed_results:
|
| 100 |
-
for r in completed_results:
|
| 101 |
-
completed_by_idx[r["sample_idx"]] = r
|
| 102 |
-
|
| 103 |
-
# Update status to running
|
| 104 |
await self.store.update_run(
|
| 105 |
run_id,
|
| 106 |
{
|
|
@@ -108,275 +168,28 @@ class BatchRunner:
|
|
| 108 |
"started_at": datetime.now(timezone.utc),
|
| 109 |
},
|
| 110 |
)
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
repo_name = dataset_config.get("repo", "")
|
| 116 |
-
dataset_path = task_config.get("dataset_path", "")
|
| 117 |
-
data = self.dataset_loader.load_jsonl(repo_name, dataset_path, source=source)
|
| 118 |
-
|
| 119 |
-
if limit and limit < len(data):
|
| 120 |
-
data = data[:limit]
|
| 121 |
-
|
| 122 |
-
remaining = len(data) - len(completed_by_idx)
|
| 123 |
-
await self.store.update_run(run_id, {"total_samples": len(data)})
|
| 124 |
-
if on_progress:
|
| 125 |
-
await on_progress({"type": "dataset_loaded", "total": len(data)})
|
| 126 |
-
if completed_by_idx:
|
| 127 |
-
logger.info(f"Resuming: {len(completed_by_idx)} done, {remaining} remaining")
|
| 128 |
-
|
| 129 |
-
# Create pipeline
|
| 130 |
-
# project_dir: v24 ์ tool_calling_judge ๊ฐ pmi_lookup ์๋๊ฒฝ๋ก๋ฅผ ํ ๋๋ง
|
| 131 |
-
# ์ด๋ค (ยง5-F). dataset_loader.base_dir ์ด projects ๋ฃจํธ์ด๋ฏ๋ก project ์ด๋ฆ์
|
| 132 |
-
# ๋ถ์ด๋ฉด project_dir ์ด ๋๋ค -- CLI(`_start_local`)๊ฐ ์ด๋ฏธ ์ฐ๋ ๊ฒ๊ณผ ๊ฐ์ ๊ด๋ก.
|
| 133 |
-
project_name = project_config.get("name")
|
| 134 |
-
project_dir = (
|
| 135 |
-
self.dataset_loader.base_dir / project_name
|
| 136 |
-
if self.dataset_loader.base_dir and project_name
|
| 137 |
-
else None
|
| 138 |
-
)
|
| 139 |
-
# config_dir: ๋ ํฌ ๊ด๋ฆฌ ํ๋ก์ ํธ๋ฉด project_loader ๊ฐ ์ฑ์ ๋ config ์ ๋ณธ
|
| 140 |
-
# ๊ฒฝ๋ก(03-evaluation). ์นํ ์ฌ์ ๊ฐ์ ์ง์ ์์ฐ์ด ์ฌ๊ธฐ์ ์จ๋ค.
|
| 141 |
-
pipeline = create_pipeline(
|
| 142 |
-
pipeline_type=task_config.get("pipeline", "single_step"),
|
| 143 |
-
input_fields=task_config.get("input_fields", []),
|
| 144 |
-
pipeline_config=task_config.get("pipeline_config"),
|
| 145 |
-
prompts=prompt.get("step_prompts", {}),
|
| 146 |
-
dataset_loader=self.dataset_loader,
|
| 147 |
-
project_dir=project_dir,
|
| 148 |
-
config_dir=project_config.get("config_dir"),
|
| 149 |
-
)
|
| 150 |
-
|
| 151 |
-
# Run inference with concurrency control
|
| 152 |
-
semaphore = asyncio.Semaphore(max_workers)
|
| 153 |
-
completed = len(completed_by_idx)
|
| 154 |
-
|
| 155 |
-
async def process_sample(idx: int, sample: dict) -> dict[str, Any]:
|
| 156 |
-
nonlocal completed
|
| 157 |
-
|
| 158 |
-
# Skip already-completed samples (resume)
|
| 159 |
-
if idx in completed_by_idx:
|
| 160 |
-
existing = completed_by_idx[idx]
|
| 161 |
-
golden_field = task_config.get("golden_field")
|
| 162 |
-
golden = sample.get(golden_field, "") if golden_field else ""
|
| 163 |
-
return {**existing, "_golden_raw": golden}
|
| 164 |
-
|
| 165 |
-
async with semaphore:
|
| 166 |
-
input_data = {
|
| 167 |
-
field: sample.get(field, "")
|
| 168 |
-
for field in task_config.get("input_fields", [])
|
| 169 |
-
}
|
| 170 |
-
|
| 171 |
-
# Get golden reference
|
| 172 |
-
golden_field = task_config.get("golden_field")
|
| 173 |
-
golden_fields = task_config.get("golden_fields")
|
| 174 |
-
if golden_field:
|
| 175 |
-
golden = sample.get(golden_field, "")
|
| 176 |
-
elif golden_fields:
|
| 177 |
-
golden = {k: sample.get(v, "") for k, v in golden_fields.items()}
|
| 178 |
-
else:
|
| 179 |
-
golden = ""
|
| 180 |
-
|
| 181 |
-
eval_sample = EvalSample(input=input_data, reference=golden)
|
| 182 |
-
|
| 183 |
-
try:
|
| 184 |
-
eval_sample = await pipeline.run(
|
| 185 |
-
sample=eval_sample,
|
| 186 |
-
prompts=prompt.get("system_prompt", ""),
|
| 187 |
-
provider=self.inference_provider,
|
| 188 |
-
model=prompt.get("model", "solar-pro2"),
|
| 189 |
-
temperature=prompt.get("temperature", 0.0),
|
| 190 |
-
max_tokens=prompt.get("max_tokens", 8000),
|
| 191 |
-
reasoning_effort=prompt.get("reasoning_effort"),
|
| 192 |
-
messages=prompt.get("messages"),
|
| 193 |
-
)
|
| 194 |
-
except Exception as e:
|
| 195 |
-
ts = datetime.now().strftime("%H:%M:%S")
|
| 196 |
-
logger.warning(f"[{ts}] Sample {idx} failed: {type(e).__name__}: {e}")
|
| 197 |
-
raise
|
| 198 |
-
|
| 199 |
-
# ์ ์ฅ ํํ๋ ์ด์ ๊ณผ ๊ฐ์ 8ํค dict (eval-store ์ resultRowSchema ๊ฐ
|
| 200 |
-
# ์ฝ๋ ์ด๋ฆ๋ค๊ณผ ํ์ ํธํ) + trace ๋ฅผ ์ถ๊ฐํ๋ค. trace ๋ artifacts
|
| 201 |
-
# ์ ์ฒด๋ฅผ ๊ทธ๋๋ก ๋ฃ๋๋ค -- step_outputs ๋ฟ ์๋๋ผ v24 ๊ฐ ์ฑ์ฐ๋
|
| 202 |
-
# judge_decisions/judge_tool_calls/self_consistency_runs/corrections
|
| 203 |
-
# ๋ ์ฌ๊ธฐ ์ ๋ฃ์ผ๋ฉด ์ ์ฅ ์ง์ ์ ํต์งธ๋ก ๋ฒ๋ ค์ง๋ค (์ค์ judge LLM ํธ์ถยท
|
| 204 |
-
# self-consistency ๋ฐ๋ณต ํธ์ถ ๋น์ฉ์ด ๋๊ฐ ์ฐ์ถ๋ฌผ์ด๋ค). ํน์ ํค๋ง
|
| 205 |
-
# ํ๋์ฝ๋ฉํด ์ฎ๊ธฐ๋ฉด ๋ค์์ ํ์ดํ๋ผ์ธ์ด ์ artifacts ํค๋ฅผ ์ถ๊ฐํ
|
| 206 |
-
# ๋๋ง๋ค ์ฌ๊ธฐ๋ฅผ ๋ ๊ณ ์ณ์ผ ํ๋ฏ๋ก ํต์งธ๋ก ๋๊ธด๋ค --
|
| 207 |
-
# resultRowSchema.trace ๋ .passthrough() ๋ผ ์ฌ๋ถ ํค๋ฅผ ๊ทธ๋๋ก ๋ฐ๋๋ค
|
| 208 |
-
# (source-schemas.ts:223-228).
|
| 209 |
-
run_result = {
|
| 210 |
-
"run_id": run_id,
|
| 211 |
-
"sample_idx": idx,
|
| 212 |
-
"input": input_data,
|
| 213 |
-
"output": eval_sample.output,
|
| 214 |
-
"golden": golden,
|
| 215 |
-
"input_tokens": eval_sample.artifacts.get("usage", {}).get(
|
| 216 |
-
"prompt_tokens", 0
|
| 217 |
-
),
|
| 218 |
-
"output_tokens": eval_sample.artifacts.get("usage", {}).get(
|
| 219 |
-
"completion_tokens", 0
|
| 220 |
-
),
|
| 221 |
-
"inference_time_ms": eval_sample.artifacts.get("inference_time_ms", 0),
|
| 222 |
-
"trace": {
|
| 223 |
-
"step_outputs": {},
|
| 224 |
-
**eval_sample.artifacts,
|
| 225 |
-
},
|
| 226 |
-
}
|
| 227 |
-
await self.store.insert_run_result(run_result)
|
| 228 |
-
|
| 229 |
-
completed += 1
|
| 230 |
-
ts = datetime.now().strftime("%H:%M:%S")
|
| 231 |
-
logger.info(f"[{ts}] Sample {idx} completed ({completed}/{len(data)})")
|
| 232 |
-
await self.store.update_run(run_id, {"completed_samples": completed})
|
| 233 |
-
if on_progress:
|
| 234 |
-
await on_progress(
|
| 235 |
-
{
|
| 236 |
-
"type": "inference_progress",
|
| 237 |
-
"completed": completed,
|
| 238 |
-
"total": len(data),
|
| 239 |
-
"sample_idx": idx,
|
| 240 |
-
}
|
| 241 |
-
)
|
| 242 |
-
|
| 243 |
-
return {**run_result, "_golden_raw": golden}
|
| 244 |
-
|
| 245 |
-
tasks = [process_sample(i, sample) for i, sample in enumerate(data)]
|
| 246 |
-
results = await asyncio.gather(*tasks, return_exceptions=True)
|
| 247 |
-
|
| 248 |
-
# Filter out exceptions
|
| 249 |
-
valid_results = [r for r in results if isinstance(r, dict)]
|
| 250 |
-
errors = [(i, r) for i, r in enumerate(results) if isinstance(r, Exception)]
|
| 251 |
-
if errors:
|
| 252 |
-
logger.warning(f"Run {run_id}: {len(errors)}/{len(results)} samples failed")
|
| 253 |
-
for sample_idx, err in errors:
|
| 254 |
-
logger.warning(f" Sample {sample_idx} error: {type(err).__name__}: {err}")
|
| 255 |
-
|
| 256 |
-
if on_progress:
|
| 257 |
-
await on_progress({"type": "inference_complete", "total": len(valid_results)})
|
| 258 |
-
|
| 259 |
-
# Run evaluation
|
| 260 |
-
await self.store.update_run(run_id, {"status": RunStatus.EVALUATING})
|
| 261 |
-
if on_progress:
|
| 262 |
-
await on_progress({"type": "evaluation_start", "total": len(valid_results)})
|
| 263 |
-
|
| 264 |
-
evaluator = create_evaluator(task_config.get("evaluator", {"type": "llm_judge"}))
|
| 265 |
-
# eval_results: ์ฑ๊ณตํ evaluate() ๋ฐํ๊ฐ + "sample_idx"(์ ๋ณธ, ยง0.5).
|
| 266 |
-
# eval_failures: ์ฑ์ ์์ฒด๊ฐ ์ ๋ ์ํ -- aggregate() ์๋ ์ ๋ ์ ๋๊ธด๋ค
|
| 267 |
-
# (lcs_diff.aggregate() ์ฒ๋ผ r["details"]["tp"] ๋ฅผ ์ง์ ์ฝ๋ ๊ตฌํ์ด ์คํจ
|
| 268 |
-
# ํญ๋ชฉ์ ๋ง๋๋ฉด KeyError ๋ก ์ฃฝ๋๋ค, ยง0.1).
|
| 269 |
-
eval_results: list[dict[str, Any]] = []
|
| 270 |
-
eval_failures: list[dict[str, Any]] = []
|
| 271 |
-
|
| 272 |
-
for i, result in enumerate(valid_results):
|
| 273 |
-
# F6: enumerate ์์น๊ฐ ์๋๋ผ result["sample_idx"] ๊ฐ ์ ๋ณธ์ด๋ค --
|
| 274 |
-
# ์ค๊ฐ ์ํ์ด ์ถ๋ก ์์ ์คํจํ๋ฉด valid_results ์ ๋ฆฌ์คํธ ์์น์ ์๋ณธ
|
| 275 |
-
# sample_idx ๊ฐ ์ด๊ธ๋๋ค.
|
| 276 |
-
sample_idx = result["sample_idx"]
|
| 277 |
-
try:
|
| 278 |
-
eval_sample = build_eval_sample_from_result(
|
| 279 |
-
result, reference=result["_golden_raw"]
|
| 280 |
-
)
|
| 281 |
-
evaluator.validate_required_fields(eval_sample)
|
| 282 |
-
eval_result = await evaluator.evaluate(
|
| 283 |
-
sample=eval_sample,
|
| 284 |
-
provider=self.judge_provider,
|
| 285 |
-
)
|
| 286 |
-
eval_results.append({**eval_result, "sample_idx": sample_idx})
|
| 287 |
-
|
| 288 |
-
if on_progress and i % 10 == 0:
|
| 289 |
-
await on_progress(
|
| 290 |
-
{
|
| 291 |
-
"type": "evaluation_progress",
|
| 292 |
-
"completed": i + 1,
|
| 293 |
-
"total": len(valid_results),
|
| 294 |
-
}
|
| 295 |
-
)
|
| 296 |
-
except Exception as e:
|
| 297 |
-
logger.warning(f"Evaluation failed for sample {sample_idx}: {e}")
|
| 298 |
-
eval_failures.append({"sample_idx": sample_idx, "error": str(e)})
|
| 299 |
-
|
| 300 |
-
# Aggregate and save evaluation -- eval_results ์๋ ์คํจ ํญ๋ชฉ์ด ์ ์์ฌ
|
| 301 |
-
# ์์ผ๋ฏ๋ก ๊ธฐ์กด aggregate() ๊ตฌํ์ด ๊ทธ๋๋ก ๋์ํ๋ค.
|
| 302 |
-
aggregated = evaluator.aggregate(eval_results)
|
| 303 |
-
# ์ฑ์ ์ ์ฑ๊ณตํ ์ํ์ด ํ๋๋ ์์ผ๋ฉด ์ ์ ์๋ฆฌ๋ฅผ ๋น์ด๋ค -- aggregate() ๋ ๋น
|
| 304 |
-
# ์
๋ ฅ์ 0.0 ์ ๋๋ ค์ฃผ๋๋ฐ, ๊ทธ๊ฑด "0์ ์ ๋ฐ์๋ค"๋ ์ธก์ ๊ฐ์ด๋ผ "์ธก์ ์์ฒด๊ฐ
|
| 305 |
-
# ์์๋ค"์ ๊ตฌ๋ถ๋์ง ์๋๋ค. judge ๊ฐ ํต์งธ๋ก ์ฃฝ์ run ์ด evalhub ์ฐจํธ์์
|
| 306 |
-
# ํ์ง ๊ธ๋ฝ์ผ๋ก ๋ณด์ด๋ฉด F1 ์ ๋ฐ๋ง ๊ณ ์น ์
์ด๋ค.
|
| 307 |
-
overall_score = aggregated.get("overall_score", 0.0) if eval_results else None
|
| 308 |
-
eval_id = await self.store.create_evaluation(
|
| 309 |
-
{
|
| 310 |
-
"run_id": run_id,
|
| 311 |
-
"scores": aggregated.get("scores", {}),
|
| 312 |
-
"overall_score": overall_score,
|
| 313 |
-
"eval_model": "gpt-4o",
|
| 314 |
-
"eval_success_count": len(eval_results),
|
| 315 |
-
"eval_failed_count": len(eval_failures),
|
| 316 |
-
"failed_sample_indices": [f["sample_idx"] for f in eval_failures],
|
| 317 |
-
}
|
| 318 |
-
)
|
| 319 |
-
|
| 320 |
-
# Save per-sample eval details -- ์ฑ๊ณต/์คํจ ๋ ๋ค ํ ํ์ฉ ๋จ๊ธด๋ค.
|
| 321 |
-
# results.jsonl ๊ณผ eval_details.jsonl ์ด ํญ์ ๊ฐ์ sample_idx ์งํฉ์
|
| 322 |
-
# ๊ฐ๋ฆฌํค๊ฒ ํด์ ๋ถ๋ถ์ ์ผ๋ก๋ง ์ฑ์ ๋ run ์์ "์ด ์ํ์ ์ ์ ๋ณด์ด์ง"๋ฅผ
|
| 323 |
-
# ์์ค๋ค.
|
| 324 |
-
eval_detail_docs = []
|
| 325 |
-
for er in eval_results:
|
| 326 |
-
eval_detail_docs.append(
|
| 327 |
-
{
|
| 328 |
-
"evaluation_id": eval_id,
|
| 329 |
-
"sample_idx": er["sample_idx"],
|
| 330 |
-
"category_scores": er.get("category_scores", {}),
|
| 331 |
-
"error_counts": {
|
| 332 |
-
k: v.get("error_count", 0)
|
| 333 |
-
for k, v in er.get("details", {}).items()
|
| 334 |
-
if isinstance(v, dict)
|
| 335 |
-
},
|
| 336 |
-
"severity": er.get("severity", ""),
|
| 337 |
-
"score": er.get("score", 0.0),
|
| 338 |
-
}
|
| 339 |
)
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
"sample_idx": f["sample_idx"],
|
| 345 |
-
"category_scores": {},
|
| 346 |
-
"error_counts": {},
|
| 347 |
-
"severity": None,
|
| 348 |
-
# 0.0 ์ด ์๋๋ผ None -- ์ฑ์ ์คํจ๋ฅผ ์ต์ ์ ์์ ๊ตฌ๋ถํ๋ค (F1).
|
| 349 |
-
"score": None,
|
| 350 |
-
"error": f["error"],
|
| 351 |
-
}
|
| 352 |
)
|
| 353 |
-
|
| 354 |
-
|
| 355 |
-
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
{
|
| 364 |
-
"status": final_status,
|
| 365 |
-
"completed_at": datetime.now(timezone.utc),
|
| 366 |
-
"failed_samples": len(errors),
|
| 367 |
-
"failed_sample_indices": [i for i, _ in errors],
|
| 368 |
-
},
|
| 369 |
)
|
| 370 |
-
|
| 371 |
-
if on_progress:
|
| 372 |
-
await on_progress(
|
| 373 |
-
{
|
| 374 |
-
"type": "done",
|
| 375 |
-
"overall_score": aggregated.get("overall_score", 0.0),
|
| 376 |
-
"scores": aggregated.get("scores", {}),
|
| 377 |
-
}
|
| 378 |
-
)
|
| 379 |
-
|
| 380 |
except Exception as e:
|
| 381 |
logger.exception(f"Run {run_id} failed")
|
| 382 |
await self.store.update_run(
|
|
@@ -389,6 +202,185 @@ class BatchRunner:
|
|
| 389 |
if on_progress:
|
| 390 |
await on_progress({"type": "error", "error": str(e)})
|
| 391 |
# F4: ์ํ๋ฅผ FAILED ๋ก ๋จ๊ธฐ๊ณ ์งํ ์ํฉ๊น์ง ์๋ฆฐ ๋ค **๊ทธ๋๋ก ์ฌ์ ํํ๋ค**.
|
| 392 |
-
# ์ฌ๊ธฐ์ ์ผํค๋ฉด ํธ์ถ์(CLI)๋ "Results saved to ..." ๋ฅผ ์ฐ๊ณ
|
| 393 |
# ์๋ํ ๋ค์ exit 0 ์ ๋ธ๋ค -- ๋ฐ์ดํฐ์
์กฐ์ฐจ ๋ชป ์ฝ์ run ์ด ์ฑ๊ณต์ผ๋ก ๋ณด์ธ๋ค.
|
| 394 |
raise
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import asyncio
|
| 2 |
import logging
|
| 3 |
import sys
|
| 4 |
+
from dataclasses import dataclass
|
| 5 |
from datetime import datetime, timezone
|
| 6 |
from typing import Any, Callable
|
| 7 |
|
|
|
|
| 9 |
logging.basicConfig(stream=sys.stderr, level=logging.INFO, format="%(message)s")
|
| 10 |
|
| 11 |
from solar_eval.core.dataset_loader import DatasetLoader
|
| 12 |
+
from solar_eval.core.dataset_prep import (
|
| 13 |
+
PreparedDataset,
|
| 14 |
+
check_resume_indices,
|
| 15 |
+
prepare_dataset,
|
| 16 |
+
row_meta,
|
| 17 |
+
)
|
| 18 |
+
from solar_eval.core.pipeline_factory import build_task_pipeline
|
| 19 |
+
from solar_eval.core.row_filter import RowSelection
|
| 20 |
+
from solar_eval.core.run_records import (
|
| 21 |
+
FAILED_SAMPLE_ERRORS_KEY,
|
| 22 |
+
build_eval_detail_docs,
|
| 23 |
+
build_eval_sample_from_result,
|
| 24 |
+
build_evaluation_doc,
|
| 25 |
+
build_failure_errors,
|
| 26 |
+
build_result_row,
|
| 27 |
+
extract_golden,
|
| 28 |
+
extract_input,
|
| 29 |
+
)
|
| 30 |
from solar_eval.evaluators.registry import create_evaluator
|
| 31 |
from solar_eval.models.enums import RunStatus
|
| 32 |
from solar_eval.models.sample import EvalSample
|
| 33 |
+
from solar_eval.pipelines.base import BasePipeline
|
| 34 |
from solar_eval.providers.base import BaseProvider
|
| 35 |
from solar_eval.stores.base import ResultStore
|
| 36 |
|
| 37 |
+
# `build_eval_sample_from_result` ๋ run_records ๋ก ์ฎ๊ฒผ์ง๋ง `runs eval` ๋ฑ ๊ธฐ์กด ํธ์ถ์๊ฐ
|
| 38 |
+
# ์ด ๋ชจ๋์์ import ํ๋ฏ๋ก ๊ทธ๋๋ก ๋
ธ์ถํ๋ค.
|
| 39 |
+
__all__ = ["BatchRunner", "build_eval_sample_from_result", "resolve_selection"]
|
| 40 |
+
|
| 41 |
logger = logging.getLogger(__name__)
|
| 42 |
|
| 43 |
OnProgress = Callable[[dict[str, Any]], Any]
|
| 44 |
|
| 45 |
+
# ํ์คํฌ์ evaluator ์ ์ธ์ด ์์ ๋ ์ฐ๋ ๊ธฐ๋ณธ ํ๊ฐ๊ธฐ.
|
| 46 |
+
DEFAULT_EVALUATOR_CONFIG: dict[str, Any] = {"type": "llm_judge"}
|
| 47 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
|
| 49 |
+
def resolve_selection(
|
| 50 |
+
task_config: dict[str, Any],
|
| 51 |
+
selection: RowSelection | None,
|
| 52 |
+
prepared: PreparedDataset | None,
|
| 53 |
+
limit: int | None,
|
| 54 |
+
) -> RowSelection | None:
|
| 55 |
+
"""๋ฌ๋๊ฐ ๋ฐ๋ฅผ ์ ํ์ ์ ํ๋ค -- ๋ฌ๋๋ ๋๊ฒจ๋ฐ์ ์ ํ๋ง ๋ฐ๋ฅธ๋ค(๊ณํ D3).
|
| 56 |
|
| 57 |
+
- `prepared` ๊ฐ ์์ผ๋ฉด ๊ทธ ์ ํ์ด ์ ๋ณธ์ด๋ค. `selection`ยท`limit` ๊ฐ ํจ๊ป ์ค๋ฉด ๊ฐ์์ผ ํ๋ค.
|
| 58 |
+
- ์ `limit` ์ธ์๋ง ์ค๋ฉด `RowSelection(limit=...)` ๋ก ๋ฐ๊พผ๋ค(0ยทNone = ์ ์ฒด, ์ค๋ ๋์).
|
| 59 |
+
- ๋ฌ๋๋ `task_config["where"]` ๋ฅผ ์ค์ค๋ก ์ฝ์ง ์๋๋ค. ํธ์ถ์๊ฐ ์ ํ์ ๋๊ธฐ์ง ์์๋๋ฐ
|
| 60 |
+
ํ์คํฌ์ where ๊ฐ ์์ผ๋ฉด ๊ฒฝ๊ณ ๋ง ๋จ๊ธฐ๊ณ ์ ์ฒด ํ์ ๋๋ค(์๋น์ค ๊ฒฝ๋ก -- ์กฐ์ฉํ ํํฐ run ๊ธ์ง).
|
| 61 |
"""
|
| 62 |
+
legacy_limit = limit or None
|
| 63 |
+
if prepared is not None:
|
| 64 |
+
if selection is not None and selection != prepared.selection:
|
| 65 |
+
raise ValueError("execute_run: selection differs from prepared.selection")
|
| 66 |
+
selection = prepared.selection
|
| 67 |
+
prepared_limit = selection.limit if selection is not None else None
|
| 68 |
+
if legacy_limit is not None and legacy_limit != prepared_limit:
|
| 69 |
+
raise ValueError("execute_run: limit differs from prepared.selection.limit")
|
| 70 |
+
elif selection is not None:
|
| 71 |
+
if legacy_limit is not None and legacy_limit != selection.limit:
|
| 72 |
+
raise ValueError("execute_run: limit differs from selection.limit")
|
| 73 |
+
elif legacy_limit is not None:
|
| 74 |
+
selection = RowSelection(limit=legacy_limit)
|
| 75 |
+
|
| 76 |
+
# CLI ๋ ํ์คํฌ where ๋ฅผ ์ ํ์ ํฉ์ณ ๋๊ธฐ๋ฏ๋ก, ํ์คํฌ์ where ๊ฐ ์๋๋ฐ ์ ์ฉํ ์ ํ์
|
| 77 |
+
# where ๊ฐ ์๋ค๋ฉด ํธ์ถ์๊ฐ where ๋ฅผ ๋ชจ๋ฅด๋ ๊ฒฝ๋ก(์๋น์คยท์ ํธ์ถ์)๋ค.
|
| 78 |
+
if task_config.get("where") is not None and (selection is None or selection.where is None):
|
| 79 |
+
logger.warning(
|
| 80 |
+
"Task %r declares where, but where is not applied on this path -- running all rows",
|
| 81 |
+
task_config.get("name"),
|
| 82 |
+
)
|
| 83 |
+
return selection
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
def _index_completed(
|
| 87 |
+
completed_results: list[dict[str, Any]] | None, prepared: PreparedDataset
|
| 88 |
+
) -> dict[int, dict[str, Any]]:
|
| 89 |
+
"""resume ์ฉ `{sample_idx: ๊ฒฐ๊ณผ}`. ์ ํ ๋ฐ idx ๊ฐ ์์ฌ ์์ผ๋ฉด ์คํจํ๋ค(D3)."""
|
| 90 |
+
completed_by_idx = {r["sample_idx"]: r for r in completed_results or []}
|
| 91 |
+
check_resume_indices(prepared, set(completed_by_idx))
|
| 92 |
+
return completed_by_idx
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
@dataclass
|
| 96 |
+
class _RunContext:
|
| 97 |
+
"""ํ run ์ ์คํ ์ํ -- ์ํ ์ฒ๋ฆฌ ํจ์๋ค์ด ๊ณต์ ํ๋ค."""
|
| 98 |
+
|
| 99 |
+
run_id: str
|
| 100 |
+
task_config: dict[str, Any]
|
| 101 |
+
prompt: dict[str, Any]
|
| 102 |
+
pipeline: BasePipeline
|
| 103 |
+
prepared: PreparedDataset
|
| 104 |
+
completed_by_idx: dict[int, dict[str, Any]]
|
| 105 |
+
on_progress: OnProgress | None
|
| 106 |
+
semaphore: asyncio.Semaphore
|
| 107 |
+
completed: int = 0
|
| 108 |
+
|
| 109 |
+
@property
|
| 110 |
+
def total(self) -> int:
|
| 111 |
+
return self.prepared.rows_selected
|
| 112 |
|
| 113 |
|
| 114 |
class BatchRunner:
|
|
|
|
| 136 |
max_workers: int = 5,
|
| 137 |
limit: int | None = None,
|
| 138 |
completed_results: list[dict[str, Any]] | None = None,
|
| 139 |
+
selection: RowSelection | None = None,
|
| 140 |
+
prepared: PreparedDataset | None = None,
|
| 141 |
+
pipeline: BasePipeline | None = None,
|
| 142 |
) -> None:
|
| 143 |
"""Execute a full inference + evaluation run.
|
| 144 |
|
| 145 |
Args:
|
| 146 |
completed_results: Previously completed results for resume.
|
| 147 |
Samples with matching sample_idx will be skipped.
|
| 148 |
+
selection: ๋ ํ์ ์ ํ(`where`ยท`limit`). `None` = ์ ์ฒด. ๋ฌ๋๋ ํ์คํฌ์
|
| 149 |
+
`where` ๋ฅผ ์ค์ค๋ก ์ฝ์ง ์๋๋ค -- ํธ์ถ์(CLI)๊ฐ ๋ง๋ค์ด ๋๊ธด๋ค(D3).
|
| 150 |
+
prepared: ํธ์ถ์๊ฐ ๋ฏธ๋ฆฌ ์ฝ๊ณ ๊ณ ๋ฅธ ๋ฐ์ดํฐ์
. ์ฃผ๋ฉด ๋ฌ๋๊ฐ ๋ค์ ์ฝ์ง ์๋๋ค --
|
| 151 |
+
run.json ์ sha256 ๊ณผ ์ค์ ๋ก ๋ ํ์ด ๊ฐ์ ๋ฐ์ดํธ์์ ๋์จ๋ค(D16).
|
| 152 |
+
pipeline: ํธ์ถ์๊ฐ ๋ง๋ ํ์ดํ๋ผ์ธ. ์ค๋
์ท์ ๋ฌ ๊ฒ๊ณผ **๊ฐ์ ์ธ์คํด์ค**๋ฅผ
|
| 153 |
+
๋๋ฆฌ๊ธฐ ์ํด ๋ฐ๋๋ค(D6). ์์ผ๋ฉด `build_task_pipeline` ์ผ๋ก ๋ง๋ ๋ค.
|
| 154 |
+
|
| 155 |
+
`sample_idx` ๋ ํํฐยทlimit ๋ค์๋ ์๋ณธ ํ์ผ์ 0-based ๋ ์ฝ๋ ์์น๋ค(D2).
|
| 156 |
|
| 157 |
์ํ ๋จ์ ์คํจ(์ถ๋ก /ํ๊ฐ)๋ ์ผํค๊ณ ์ํ(PARTIAL/eval_failed_count)๋ก ๊ธฐ๋กํ์ง๋ง,
|
| 158 |
+
run ์ ํต์งธ๋ก ๋ชป ๋๊ฒ ๋ง๋๋ ์์ธ(๋ฐ์ดํฐ์
๋ก๋ ์คํจ, 0ํ ์ ํ, ์ ํ ๋ฐ resume,
|
| 159 |
+
ํ์ดํ๋ผ์ธ/ํ๊ฐ๊ธฐ ์์ฑ ์คํจ, ์คํ ์ด ์ฐ๊ธฐ ์คํจ ๋ฑ)๋ ์ํ๋ฅผ FAILED ๋ก ๋จ๊ธด ๋ค
|
| 160 |
+
๊ทธ๋๋ก ์ฌ์ ํํ๋ค -- "๋ฌด์จ ์ผ์ด ์์ด๋ ์์ธ ์์ด ๋ฐํ"์ด ์๋๋ค. ํธ์ถ์๋ ์ด
|
| 161 |
+
ํจ์๊ฐ raise ํ ์ ์๋ค๊ณ ๊ฐ์ ํด์ผ ํ๋ค.
|
| 162 |
"""
|
| 163 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 164 |
await self.store.update_run(
|
| 165 |
run_id,
|
| 166 |
{
|
|
|
|
| 168 |
"started_at": datetime.now(timezone.utc),
|
| 169 |
},
|
| 170 |
)
|
| 171 |
+
effective = resolve_selection(task_config, selection, prepared, limit)
|
| 172 |
+
if prepared is None:
|
| 173 |
+
prepared = prepare_dataset(
|
| 174 |
+
self.dataset_loader, project_config, task_config, effective
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 175 |
)
|
| 176 |
+
completed_by_idx = _index_completed(completed_results, prepared)
|
| 177 |
+
if pipeline is None:
|
| 178 |
+
pipeline = build_task_pipeline(
|
| 179 |
+
project_config, task_config, prompt, self.dataset_loader
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 180 |
)
|
| 181 |
+
ctx = _RunContext(
|
| 182 |
+
run_id=run_id,
|
| 183 |
+
task_config=task_config,
|
| 184 |
+
prompt=prompt,
|
| 185 |
+
pipeline=pipeline,
|
| 186 |
+
prepared=prepared,
|
| 187 |
+
completed_by_idx=completed_by_idx,
|
| 188 |
+
on_progress=on_progress,
|
| 189 |
+
completed=len(completed_by_idx),
|
| 190 |
+
semaphore=asyncio.Semaphore(max_workers),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 191 |
)
|
| 192 |
+
await self._run_all(ctx)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 193 |
except Exception as e:
|
| 194 |
logger.exception(f"Run {run_id} failed")
|
| 195 |
await self.store.update_run(
|
|
|
|
| 202 |
if on_progress:
|
| 203 |
await on_progress({"type": "error", "error": str(e)})
|
| 204 |
# F4: ์ํ๋ฅผ FAILED ๋ก ๋จ๊ธฐ๊ณ ์งํ ์ํฉ๊น์ง ์๋ฆฐ ๋ค **๊ทธ๋๋ก ์ฌ์ ํํ๋ค**.
|
| 205 |
+
# ์ฌ๊ธฐ์ ์ผํค๋ฉด ํธ์ถ์(CLI)๋ "Results saved to ..." ๋ฅผ ์ฐ๊ณ Experiment Hub ์ ์ฌ๊น์ง
|
| 206 |
# ์๋ํ ๋ค์ exit 0 ์ ๋ธ๋ค -- ๋ฐ์ดํฐ์
์กฐ์ฐจ ๋ชป ์ฝ์ run ์ด ์ฑ๊ณต์ผ๋ก ๋ณด์ธ๋ค.
|
| 207 |
raise
|
| 208 |
+
|
| 209 |
+
async def _run_all(self, ctx: _RunContext) -> None:
|
| 210 |
+
"""์ถ๋ก โ ์ฑ์ โ ์ํ ๋ง๊ฐ. ์น๋ช
์ ์์ธ๋ ํธ์ถ์(`execute_run`)๊ฐ FAILED ๋ก ๋จ๊ธด๋ค."""
|
| 211 |
+
await self.store.update_run(ctx.run_id, {"total_samples": ctx.total})
|
| 212 |
+
if ctx.on_progress:
|
| 213 |
+
await ctx.on_progress({"type": "dataset_loaded", "total": ctx.total})
|
| 214 |
+
if ctx.completed_by_idx:
|
| 215 |
+
remaining = ctx.total - len(ctx.completed_by_idx)
|
| 216 |
+
logger.info(f"Resuming: {len(ctx.completed_by_idx)} done, {remaining} remaining")
|
| 217 |
+
|
| 218 |
+
valid_results, errors = await self._infer_all(ctx)
|
| 219 |
+
aggregated = await self._evaluate_all(ctx, valid_results)
|
| 220 |
+
|
| 221 |
+
# Mark run as completed/partial -- ์ถ๋ก ์ด ์ผ๋ถ ์ํ์์ ์คํจํ์ผ๋ฉด PARTIAL, ํ๊ฐ
|
| 222 |
+
# ์คํจ๋ ์ด ์ํ์ ์ํฅ์ ์ฃผ์ง ์๋๋ค(ํ๊ฐ ์ฑ๊ณต/์คํจ๋ eval_success_count/
|
| 223 |
+
# eval_failed_count ๋ก๋ง ํํํ๋ค, ยง0.4). errors ์ ์ธ๋ฑ์ค๋ `_infer_all` ์ด
|
| 224 |
+
# gather ์์น๋ฅผ ์๋ณธ sample_idx ๋ก ๋งคํํด ๋ ๊ฐ์ด๋ค(D2).
|
| 225 |
+
final_status = RunStatus.PARTIAL if errors else RunStatus.COMPLETED
|
| 226 |
+
await self.store.update_run(
|
| 227 |
+
ctx.run_id,
|
| 228 |
+
{
|
| 229 |
+
"status": final_status,
|
| 230 |
+
"completed_at": datetime.now(timezone.utc),
|
| 231 |
+
"failed_samples": len(errors),
|
| 232 |
+
"failed_sample_indices": [i for i, _ in errors],
|
| 233 |
+
# ๊ฐ์ ํธ์ถ์์ ์ด๋ค -- ๋ ๋ชฉ๋ก์ ์์น ์งํฉ์ด ๋ ๊ฐ๋ค. ํ์
์ด๋ฆ๋ง(LP5 D-LP5-4).
|
| 234 |
+
FAILED_SAMPLE_ERRORS_KEY: build_failure_errors(errors),
|
| 235 |
+
},
|
| 236 |
+
)
|
| 237 |
+
if ctx.on_progress:
|
| 238 |
+
await ctx.on_progress(
|
| 239 |
+
{
|
| 240 |
+
"type": "done",
|
| 241 |
+
"overall_score": aggregated.get("overall_score", 0.0),
|
| 242 |
+
"scores": aggregated.get("scores", {}),
|
| 243 |
+
}
|
| 244 |
+
)
|
| 245 |
+
|
| 246 |
+
async def _infer_all(
|
| 247 |
+
self, ctx: _RunContext
|
| 248 |
+
) -> tuple[list[dict[str, Any]], list[tuple[int, BaseException]]]:
|
| 249 |
+
"""์ ํ๋ ํ์ ๋์์ ์ถ๋ก ํ๋ค. ์คํจ๋ `(์๋ณธ sample_idx, ์์ธ)` ๋ก ๋๋ ค์ค๋ค."""
|
| 250 |
+
selected = ctx.prepared.selected
|
| 251 |
+
tasks = [self._infer_one(ctx, idx, sample) for idx, sample in selected]
|
| 252 |
+
results = await asyncio.gather(*tasks, return_exceptions=True)
|
| 253 |
+
|
| 254 |
+
valid_results = [r for r in results if isinstance(r, dict)]
|
| 255 |
+
# gather ๋ tasks ์์๋ฅผ ๋ณด์กดํ๋ฏ๋ก ๊ฒฐ๊ณผ ์์น = selected ์์น๋ค. ์์น๋ฅผ ๊ทธ๋๋ก
|
| 256 |
+
# ์ฐ๋ฉด ํํฐยทlimit run ์์ ์๋ฑํ ํ์ ์คํจ๋ก ์ ๋๋ค -- ์๋ณธ idx ๋ก ๋งคํํ๋ค(D2).
|
| 257 |
+
errors = [
|
| 258 |
+
(selected[pos][0], r) for pos, r in enumerate(results) if isinstance(r, BaseException)
|
| 259 |
+
]
|
| 260 |
+
if errors:
|
| 261 |
+
logger.warning(f"Run {ctx.run_id}: {len(errors)}/{len(results)} samples failed")
|
| 262 |
+
for sample_idx, err in errors:
|
| 263 |
+
logger.warning(f" Sample {sample_idx} error: {type(err).__name__}: {err}")
|
| 264 |
+
|
| 265 |
+
if ctx.on_progress:
|
| 266 |
+
await ctx.on_progress({"type": "inference_complete", "total": len(valid_results)})
|
| 267 |
+
return valid_results, errors
|
| 268 |
+
|
| 269 |
+
async def _infer_one(self, ctx: _RunContext, idx: int, sample: dict) -> dict[str, Any]:
|
| 270 |
+
"""์ํ ํ๋ ์ถ๋ก ยท์ ์ฅ. `idx` ๋ ์๋ณธ ํ์ผ ์์น๋ค."""
|
| 271 |
+
golden = extract_golden(sample, ctx.task_config)
|
| 272 |
+
# Skip already-completed samples (resume)
|
| 273 |
+
if idx in ctx.completed_by_idx:
|
| 274 |
+
return {**ctx.completed_by_idx[idx], "_golden_raw": golden}
|
| 275 |
+
|
| 276 |
+
async with ctx.semaphore:
|
| 277 |
+
input_data = extract_input(sample, ctx.task_config.get("input_fields", []))
|
| 278 |
+
eval_sample = EvalSample(input=input_data, reference=golden)
|
| 279 |
+
prompt = ctx.prompt
|
| 280 |
+
try:
|
| 281 |
+
eval_sample = await ctx.pipeline.run(
|
| 282 |
+
sample=eval_sample,
|
| 283 |
+
prompts=prompt.get("system_prompt", ""),
|
| 284 |
+
provider=self.inference_provider,
|
| 285 |
+
model=prompt.get("model", "solar-pro2"),
|
| 286 |
+
temperature=prompt.get("temperature", 0.0),
|
| 287 |
+
max_tokens=prompt.get("max_tokens", 8000),
|
| 288 |
+
reasoning_effort=prompt.get("reasoning_effort"),
|
| 289 |
+
messages=prompt.get("messages"),
|
| 290 |
+
)
|
| 291 |
+
except Exception as e:
|
| 292 |
+
ts = datetime.now().strftime("%H:%M:%S")
|
| 293 |
+
logger.warning(f"[{ts}] Sample {idx} failed: {type(e).__name__}: {e}")
|
| 294 |
+
raise
|
| 295 |
+
|
| 296 |
+
run_result = build_result_row(
|
| 297 |
+
ctx.run_id,
|
| 298 |
+
idx,
|
| 299 |
+
input_data,
|
| 300 |
+
golden,
|
| 301 |
+
eval_sample,
|
| 302 |
+
meta=row_meta(sample, ctx.prepared.meta_fields),
|
| 303 |
+
)
|
| 304 |
+
await self.store.insert_run_result(run_result)
|
| 305 |
+
await self._report_inference(ctx, idx)
|
| 306 |
+
return {**run_result, "_golden_raw": golden}
|
| 307 |
+
|
| 308 |
+
async def _report_inference(self, ctx: _RunContext, idx: int) -> None:
|
| 309 |
+
ctx.completed += 1
|
| 310 |
+
ts = datetime.now().strftime("%H:%M:%S")
|
| 311 |
+
logger.info(f"[{ts}] Sample {idx} completed ({ctx.completed}/{ctx.total})")
|
| 312 |
+
await self.store.update_run(ctx.run_id, {"completed_samples": ctx.completed})
|
| 313 |
+
if ctx.on_progress:
|
| 314 |
+
await ctx.on_progress(
|
| 315 |
+
{
|
| 316 |
+
"type": "inference_progress",
|
| 317 |
+
"completed": ctx.completed,
|
| 318 |
+
"total": ctx.total,
|
| 319 |
+
"sample_idx": idx,
|
| 320 |
+
}
|
| 321 |
+
)
|
| 322 |
+
|
| 323 |
+
async def _evaluate_all(
|
| 324 |
+
self, ctx: _RunContext, valid_results: list[dict[str, Any]]
|
| 325 |
+
) -> dict[str, Any]:
|
| 326 |
+
"""์ฑ์ ํ๊ณ evaluationยทeval_details ๋ฅผ ์ ์ฅํ๋ค. ์ง๊ณ dict ๋ฅผ ๋๋ ค์ค๋ค."""
|
| 327 |
+
await self.store.update_run(ctx.run_id, {"status": RunStatus.EVALUATING})
|
| 328 |
+
if ctx.on_progress:
|
| 329 |
+
await ctx.on_progress({"type": "evaluation_start", "total": len(valid_results)})
|
| 330 |
+
|
| 331 |
+
evaluator = create_evaluator(ctx.task_config.get("evaluator", DEFAULT_EVALUATOR_CONFIG))
|
| 332 |
+
eval_results, eval_failures = await self._score(ctx, evaluator, valid_results)
|
| 333 |
+
|
| 334 |
+
# eval_results ์๋ ์คํจ ํญ๋ชฉ์ด ์ ์์ฌ ์์ผ๋ฏ๋ก ๊ธฐ์กด aggregate() ๊ตฌํ์ด ๊ทธ๋๋ก ๋์ํ๋ค.
|
| 335 |
+
aggregated = evaluator.aggregate(eval_results)
|
| 336 |
+
eval_id = await self.store.create_evaluation(
|
| 337 |
+
build_evaluation_doc(
|
| 338 |
+
# eval_model = ํ๊ฐ๊ธฐ๊ฐ ๋ณด๊ณ ํ๋ judge ๋ชจ๋ธ(์์ผ๋ฉด None) -- ์์ ์ ํ๋์ฝ๋ฉ
|
| 339 |
+
# "gpt-4o" ๋ lcs_diff run ์๋ judge ๊ฐ ์๋ ๊ฒ์ฒ๋ผ ์ ์๋ค(๊ณํ D11).
|
| 340 |
+
ctx.run_id,
|
| 341 |
+
aggregated,
|
| 342 |
+
eval_results,
|
| 343 |
+
eval_failures,
|
| 344 |
+
evaluator.judge_model,
|
| 345 |
+
)
|
| 346 |
+
)
|
| 347 |
+
await self.store.insert_eval_details(
|
| 348 |
+
build_eval_detail_docs(eval_id, eval_results, eval_failures)
|
| 349 |
+
)
|
| 350 |
+
return aggregated
|
| 351 |
+
|
| 352 |
+
async def _score(
|
| 353 |
+
self, ctx: _RunContext, evaluator: Any, valid_results: list[dict[str, Any]]
|
| 354 |
+
) -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
|
| 355 |
+
"""์ํ๋ณ ์ฑ์ .
|
| 356 |
+
|
| 357 |
+
eval_results: ์ฑ๊ณตํ evaluate() ๋ฐํ๊ฐ + "sample_idx"(์ ๋ณธ, ยง0.5).
|
| 358 |
+
eval_failures: ์ฑ์ ์์ฒด๊ฐ ์ ๋ ์ํ -- aggregate() ์๋ ์ ๋ ์ ๋๊ธด๋ค
|
| 359 |
+
(lcs_diff.aggregate() ์ฒ๋ผ r["details"]["tp"] ๋ฅผ ์ง์ ์ฝ๋ ๊ตฌํ์ด ์คํจ ํญ๋ชฉ์
|
| 360 |
+
๋ง๋๋ฉด KeyError ๋ก ์ฃฝ๋๋ค, ยง0.1).
|
| 361 |
+
"""
|
| 362 |
+
eval_results: list[dict[str, Any]] = []
|
| 363 |
+
eval_failures: list[dict[str, Any]] = []
|
| 364 |
+
for i, result in enumerate(valid_results):
|
| 365 |
+
# F6: enumerate ์์น๊ฐ ์๋๋ผ result["sample_idx"] ๊ฐ ์ ๋ณธ์ด๋ค.
|
| 366 |
+
sample_idx = result["sample_idx"]
|
| 367 |
+
try:
|
| 368 |
+
eval_sample = build_eval_sample_from_result(result, reference=result["_golden_raw"])
|
| 369 |
+
evaluator.validate_required_fields(eval_sample)
|
| 370 |
+
eval_result = await evaluator.evaluate(
|
| 371 |
+
sample=eval_sample,
|
| 372 |
+
provider=self.judge_provider,
|
| 373 |
+
)
|
| 374 |
+
eval_results.append({**eval_result, "sample_idx": sample_idx})
|
| 375 |
+
if ctx.on_progress and i % 10 == 0:
|
| 376 |
+
await ctx.on_progress(
|
| 377 |
+
{
|
| 378 |
+
"type": "evaluation_progress",
|
| 379 |
+
"completed": i + 1,
|
| 380 |
+
"total": len(valid_results),
|
| 381 |
+
}
|
| 382 |
+
)
|
| 383 |
+
except Exception as e:
|
| 384 |
+
logger.warning(f"Evaluation failed for sample {sample_idx}: {e}")
|
| 385 |
+
eval_failures.append({"sample_idx": sample_idx, "error": str(e)})
|
| 386 |
+
return eval_results, eval_failures
|
solar_eval/core/snapshot/__init__.py
ADDED
|
@@ -0,0 +1,400 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""run.json `snapshot` ๋ธ๋ก(engine-snapshot/v1) โ "๋ฌด์์ผ๋ก ๋๋ ธ๋๊ฐ"๋ฅผ ์์ ํ ์ ๋๋ค.
|
| 2 |
+
|
| 3 |
+
๊ณํ 2026-09-24-phase1a D5~D12. ๋ชจ์์ ์ ๋ณธ์ poc-bundle(SPEC ยง3.4 ์์ ์ฑ ๊ท๋ฒ ํค 8๊ฐ)์ด๊ณ , ์ง๋ฌธ์
|
| 4 |
+
`poc_bundle.snapshot.config_fingerprint`(JCS) ๋ฅผ ๊ทธ๋๋ก ์ด๋ค โ ๊ท์น์ ๋ ๋ฒ ์ง์ง ์๋๋ค.
|
| 5 |
+
|
| 6 |
+
- `config` = ์ง๋ฌธ ๋์(๊ธฐ๋ฅ). `provenance` = ์ง๋ฌธ ๋ฐ(์ด๋ฆยท์์นยท์ปค๋ฐยท์คํ ์ต์
).
|
| 7 |
+
- `build_engine_snapshot(inputs)` ๋ run ์์ **์ ์** ๋ถ๋ฅธ๋ค. ์ ์ธํ๋๋ฐ ์๋ ์์์ ์ฌ๊ธฐ์ ์ฃฝ๋๋ค.
|
| 8 |
+
- `self_check(snapshot)` = ๊ท๋ฒ ํค ๋๋ฝ ์์ + ์ง๋ฌธ ์ฌ๊ณ์ฐ ์ผ์น (์์ง ์๊ธฐ ๊ฒ์ฆ, D5).
|
| 9 |
+
|
| 10 |
+
`poc_bundle`ยท`packaging` import ๋ ์ด ํจํค์ง(์ cli ์ run ๋ฉํ ๋ชจ๋) ์์์๋ง ํ๋ค โ HF Space ๋ฒค๋
|
| 11 |
+
์ฌ๋ณธ์ด import ํ๋ ๋ชจ๋์ ๋ ์์ด ๋์์ผ ํ๋ค(D18).
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
+
from __future__ import annotations
|
| 15 |
+
|
| 16 |
+
import copy
|
| 17 |
+
import subprocess
|
| 18 |
+
from collections.abc import Mapping, Sequence
|
| 19 |
+
from dataclasses import dataclass, field
|
| 20 |
+
from pathlib import Path
|
| 21 |
+
from typing import Any
|
| 22 |
+
|
| 23 |
+
from poc_bundle.canonical import json_fingerprint
|
| 24 |
+
from poc_bundle.snapshot import completeness_gaps, config_fingerprint
|
| 25 |
+
|
| 26 |
+
from solar_eval.core.snapshot import environment, impl_hash, reproducibility, resources
|
| 27 |
+
from solar_eval.core.snapshot.impl_hash import ImplRoots
|
| 28 |
+
from solar_eval.evaluators.registry import DEFAULT_EVALUATOR_CONFIG, create_evaluator
|
| 29 |
+
|
| 30 |
+
ENGINE_SNAPSHOT_SCHEMA = "engine-snapshot/v1"
|
| 31 |
+
|
| 32 |
+
#: `snapshot.config` ํค ์์ (๊ณํ ยง3.2). ์ง๋ฌธ์ JCS ๋ผ ์์์ ๋ฌด๊ดํ์ง๋ง ์ฌ๋์ด ์ฝ๋ ์์๋ฅผ ๊ณ ์ ํ๋ค.
|
| 33 |
+
CONFIG_KEYS = (
|
| 34 |
+
"model",
|
| 35 |
+
"temperature",
|
| 36 |
+
"max_tokens",
|
| 37 |
+
"reasoning_effort",
|
| 38 |
+
"pipeline_type",
|
| 39 |
+
"input_fields",
|
| 40 |
+
"resolved_pipeline",
|
| 41 |
+
"prompt_assets",
|
| 42 |
+
"resources",
|
| 43 |
+
"evaluator_config",
|
| 44 |
+
"step_impl_hash",
|
| 45 |
+
"runtime_impl_hash",
|
| 46 |
+
"dependency_lock",
|
| 47 |
+
"runner_version",
|
| 48 |
+
)
|
| 49 |
+
|
| 50 |
+
#: ๋ฐํ์ยทํ๊ฐ๊ธฐ ํํฌ ๋ชฉ๋ก์ `provenance.impl_modules` ์ ๋จ๊ธธ ๋์ ํค.
|
| 51 |
+
RUNTIME_MODULES_KEY = "@runtime"
|
| 52 |
+
EVALUATOR_MODULES_KEY = "@evaluator"
|
| 53 |
+
|
| 54 |
+
#: ํ๋ก๋ฐ์ด๋ ๋ชจ๋ ์ด๋ฆ์ ๋จ๊ธฐ๋ provenance ํค โ ์ฌ์ฑ์ ์ด ๋ฐํ์ ํด์๋ฅผ ๋ค์ ๋ฐ ์ฌ๋ฃ.
|
| 55 |
+
PROVIDERS_KEY = "providers"
|
| 56 |
+
|
| 57 |
+
#: ์์ง ๋ ํฌ๋ฅผ ์๋์ผ๋ก ์ฐพ์ผ๋ผ๋ ํ์(`EngineSnapshotInputs.repo` ๊ธฐ๋ณธ๊ฐ).
|
| 58 |
+
AUTO = "auto"
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
class SnapshotError(ValueError):
|
| 62 |
+
"""์ค๋
์ท์ด ์ค์ค๋ก์ ๊ท์น์ ์ด๊ฒผ๋ค โ ๊ท๋ฒ ํค ๋๋ฝยท์ง๋ฌธ ๋ถ์ผ์นยท๋ชจ๋ฅด๋ schema."""
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
@dataclass(frozen=True)
|
| 66 |
+
class EngineSnapshotInputs:
|
| 67 |
+
"""์ค๋
์ท ์ฌ๋ฃ. ํ์ผยทgit ์ฝ๊ธฐ๋ `roots`ยท`engine`ยท`repo`ยท`git_run` ์ผ๋ก ๋ฐ๊ฟ ๋ผ์ธ ์ ์๋ค.
|
| 68 |
+
|
| 69 |
+
Attributes:
|
| 70 |
+
pipeline: ๋ฌ๋๊ฐ **์ค์ ๋ก ๋๋ฆด** `Pipeline` ์ธ์คํด์ค(D6) โ `step_impl_hash`ยท`resources` ์ฌ๋ฃ.
|
| 71 |
+
resolved_pipeline: `extends`ยท`--set` ๊น์ง ํด์๋ ํ์ดํ๋ผ์ธ dict(์์ผ๋ฉด None/{}).
|
| 72 |
+
prompt_path: ํ๋กฌํํธ ๋๋ ํ ๋ฆฌ ๋๋ ๋จ์ผ YAML ํ์ผ.
|
| 73 |
+
evaluator_config: ํ์คํฌ์ evaluator ์ค์ . None ์ "ํค ์์"์ผ๋ก ์ฝ์ด ๋ฌ๋ ๊ธฐ๋ณธ๊ฐ
|
| 74 |
+
(`llm_judge`)์ ์ด๋ค. ํค๊ฐ ์๋๋ฐ ๋น์์ผ๋ฉด(`{}`) ์ฌ๊ธฐ์ ValueError โ ๋ฌ๋๊ฐ ์ฃฝ์ ์ค์ ์ด๋ค.
|
| 75 |
+
dataset: `dataset_block.describe_dataset` ๊ฒฐ๊ณผ(์ฌํ ํ์ ์ฌ๋ฃ). None = ๋ชจ๋ฆ.
|
| 76 |
+
engine: ๋ฏธ๋ฆฌ ๊ณ์ฐํ `environment.engine_state` ๊ฒฐ๊ณผ. None ์ด๋ฉด ์ฌ๊ธฐ์ ๊ณ์ฐํ๋ค.
|
| 77 |
+
repo: ์์ง ๋ ํฌ(uv.lockยทengine_state ์ฉ). `AUTO` ๋ฉด `environment.engine_repo()`.
|
| 78 |
+
"""
|
| 79 |
+
|
| 80 |
+
model: str
|
| 81 |
+
temperature: float
|
| 82 |
+
max_tokens: int
|
| 83 |
+
reasoning_effort: str | None
|
| 84 |
+
pipeline_type: str
|
| 85 |
+
input_fields: Sequence[str]
|
| 86 |
+
resolved_pipeline: Mapping[str, Any] | None
|
| 87 |
+
prompt_path: Path
|
| 88 |
+
pipeline: Any
|
| 89 |
+
evaluator_config: Mapping[str, Any] | None
|
| 90 |
+
inference_provider: Any
|
| 91 |
+
judge_provider: Any = None
|
| 92 |
+
prompt_name: str | None = None
|
| 93 |
+
pipeline_name: str | None = None
|
| 94 |
+
config_source: str | None = None
|
| 95 |
+
git_commit: str | None = None
|
| 96 |
+
pipeline_overrides: Mapping[str, Any] | None = None
|
| 97 |
+
max_workers: int | None = None
|
| 98 |
+
config_dir: Path | None = None
|
| 99 |
+
repo_managed: bool = False
|
| 100 |
+
dataset: Mapping[str, Any] | None = None
|
| 101 |
+
roots: ImplRoots | None = None
|
| 102 |
+
engine: Mapping[str, Any] | None = None
|
| 103 |
+
repo: Path | str | None = AUTO
|
| 104 |
+
git_run: Any = field(default=subprocess.run)
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def evaluator_block(
|
| 108 |
+
evaluator_config: Mapping[str, Any] | None, roots: ImplRoots | None = None
|
| 109 |
+
) -> tuple[dict[str, Any], list[str]]:
|
| 110 |
+
"""`config.evaluator_config = {config, judge_model, impl_hash}` ์ ํํฌ ๋ผ๋ฒจ.
|
| 111 |
+
|
| 112 |
+
๊ธฐ๋ณธ๊ฐ์ `evaluator_config is None`(ํ์คํฌ์ ํค๊ฐ ์์)์ผ ๋๋ง ์ด๋ค โ ๋ฌ๋์
|
| 113 |
+
`task_config.get("evaluator", DEFAULT)` ์ ๊ฐ์ ๊ท์น. `{}`ยท`type` ์๋ ์ค์ ์ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ๋ฎ์ง ์๊ณ
|
| 114 |
+
`create_evaluator` ๊ฐ ValueError ๋ก ๊ฑฐ๋ถํ๋ค(์ถ๋ก ์ ์ ์ฃฝ๋๋ค, D5).
|
| 115 |
+
|
| 116 |
+
Raises:
|
| 117 |
+
ValueError: ํ๊ฐ๊ธฐ ์ค์ ์ด ๊ฐ์ฒด๊ฐ ์๋๊ฑฐ๋ `type` ์ด ์๊ฑฐ๏ฟฝ๏ฟฝ๏ฟฝ ๋ชจ๋ฅด๋ ๊ฐ์ผ ๋.
|
| 118 |
+
"""
|
| 119 |
+
raw = DEFAULT_EVALUATOR_CONFIG if evaluator_config is None else evaluator_config
|
| 120 |
+
evaluator = create_evaluator(copy.deepcopy(raw))
|
| 121 |
+
effective = copy.deepcopy(dict(raw))
|
| 122 |
+
digest, modules = impl_hash.evaluator_impl_hash(evaluator, roots)
|
| 123 |
+
block = {"config": effective, "judge_model": evaluator.judge_model, "impl_hash": digest}
|
| 124 |
+
return block, modules
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
def _resolve_repo(inputs: EngineSnapshotInputs) -> Path | None:
|
| 128 |
+
if inputs.repo == AUTO:
|
| 129 |
+
return environment.engine_repo()
|
| 130 |
+
return Path(inputs.repo) if inputs.repo is not None else None
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
def _engine(inputs: EngineSnapshotInputs, repo: Path | None, roots: ImplRoots) -> dict[str, Any]:
|
| 134 |
+
"""`provenance.engine` โ ์ฃผ์
๊ฐ์ด ์์ผ๋ฉด ๊ทธ๊ฒ, ๋ ํฌ๊ฐ ์์ผ๋ฉด ํธ๋ฆฌ ํด์๋ง(commitยทdirty null)."""
|
| 135 |
+
if inputs.engine is not None:
|
| 136 |
+
return copy.deepcopy(dict(inputs.engine))
|
| 137 |
+
if repo is None:
|
| 138 |
+
tree = impl_hash.engine_tree_sha256(roots)
|
| 139 |
+
return {"commit": None, "tree_sha256": tree, "dirty": None, "dirty_scope": []}
|
| 140 |
+
return environment.engine_state(
|
| 141 |
+
inputs.config_dir,
|
| 142 |
+
inputs.repo_managed,
|
| 143 |
+
repo=repo,
|
| 144 |
+
roots=roots,
|
| 145 |
+
run=inputs.git_run,
|
| 146 |
+
)
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
def _recorded_providers(inputs: EngineSnapshotInputs) -> dict[str, str | None]:
|
| 150 |
+
"""`provenance.providers = {inference, judge}` โ ์ด๋ฒ run ์ ์ฃผ์
๋ ํ๋ก๋ฐ์ด๋์ ๋ชจ๋ ์ด๋ฆ.
|
| 151 |
+
|
| 152 |
+
judge ๋ ์ฐ๋ ์ ์ฐ๋ ์ ๋๋ค โ ์ฌ์ฑ์ (`with_evaluator`)์ด judge ๋ฅผ ์๋ก ์ฐ๊ฒ ๋๋ฉด ์ด ์ด๋ฆ์ผ๋ก
|
| 153 |
+
๋ฐํ์ ํด์๋ฅผ ๋ค์ ๋ฌ๋ค.
|
| 154 |
+
"""
|
| 155 |
+
judge = inputs.judge_provider
|
| 156 |
+
return {
|
| 157 |
+
"inference": impl_hash.provider_module(inputs.inference_provider),
|
| 158 |
+
"judge": None if judge is None else impl_hash.provider_module(judge),
|
| 159 |
+
}
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
def _runtime(
|
| 163 |
+
providers: Mapping[str, str | None], judge_model: str | None, roots: ImplRoots
|
| 164 |
+
) -> tuple[str, list[str]]:
|
| 165 |
+
"""๋ฐํ์ ํด์ โ ์ถ๋ก ํ๋ก๋ฐ์ด๋ + (ํ๊ฐ๊ธฐ๊ฐ judge ๋ฅผ ์ค์ ๋ก ๋ถ๋ฅผ ๋๋ง) judge ํ๋ก๋ฐ์ด๋.
|
| 166 |
+
|
| 167 |
+
lcs_diff run ์ judge ๋ฅผ ์ ์ฐ๋ฏ๋ก ๋ฃ์ง ์๋๋ค. ๋น๋์ ์ฌ์ฑ์ ์ด ๊ฐ์ ๊ท์น์ ์ด๋ค.
|
| 168 |
+
"""
|
| 169 |
+
used = [providers["inference"]]
|
| 170 |
+
if judge_model is not None and providers.get("judge") is not None:
|
| 171 |
+
used.append(providers["judge"])
|
| 172 |
+
return impl_hash.runtime_impl_hash_for_modules([m for m in used if m is not None], roots)
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
@dataclass(frozen=True)
|
| 176 |
+
class _Materials:
|
| 177 |
+
"""`config` ์, ์ง๋ฌธ ๋ฐ์ผ๋ก ๊ฐ๋ ๋ถ์ฐ๋ฌผ(์์ ํด์ยทํํฌ ๋ผ๋ฒจยทํ๋ก๋ฐ์ด๋ ๋ชจ๋)."""
|
| 178 |
+
|
| 179 |
+
config: dict[str, Any]
|
| 180 |
+
collected: resources.CollectedResources
|
| 181 |
+
impl_modules: dict[str, list[str]]
|
| 182 |
+
providers: dict[str, str | None]
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
def _collect_config(inputs: EngineSnapshotInputs, roots: ImplRoots) -> _Materials:
|
| 186 |
+
"""์ง๋ฌธ ๋์ `config` ๋ฅผ `CONFIG_KEYS` ์์๋ก ๋ชจ์๋ค โ ํ์ผยท์์ยท๊ตฌํ ํด์๋ฅผ ์ฌ๊ธฐ์ ์ฝ๋๋ค."""
|
| 187 |
+
collected = resources.collect_resources(inputs.pipeline)
|
| 188 |
+
evaluator, evaluator_modules = evaluator_block(inputs.evaluator_config, roots)
|
| 189 |
+
step_hashes, step_modules = impl_hash.step_impl_hashes(inputs.pipeline, roots)
|
| 190 |
+
providers = _recorded_providers(inputs)
|
| 191 |
+
runtime_hash, runtime_modules = _runtime(providers, evaluator["judge_model"], roots)
|
| 192 |
+
config = {
|
| 193 |
+
"model": inputs.model,
|
| 194 |
+
"temperature": inputs.temperature,
|
| 195 |
+
"max_tokens": inputs.max_tokens,
|
| 196 |
+
"reasoning_effort": inputs.reasoning_effort,
|
| 197 |
+
"pipeline_type": inputs.pipeline_type,
|
| 198 |
+
"input_fields": list(inputs.input_fields),
|
| 199 |
+
"resolved_pipeline": copy.deepcopy(dict(inputs.resolved_pipeline or {})),
|
| 200 |
+
"prompt_assets": resources.prompt_assets(inputs.prompt_path),
|
| 201 |
+
"resources": collected.hashes,
|
| 202 |
+
"evaluator_config": evaluator,
|
| 203 |
+
"step_impl_hash": step_hashes,
|
| 204 |
+
"runtime_impl_hash": runtime_hash,
|
| 205 |
+
"dependency_lock": environment.dependency_lock(),
|
| 206 |
+
"runner_version": environment.runner_version(),
|
| 207 |
+
}
|
| 208 |
+
impl_modules = {
|
| 209 |
+
**step_modules,
|
| 210 |
+
EVALUATOR_MODULES_KEY: evaluator_modules,
|
| 211 |
+
RUNTIME_MODULES_KEY: runtime_modules,
|
| 212 |
+
}
|
| 213 |
+
config = {key: config[key] for key in CONFIG_KEYS}
|
| 214 |
+
return _Materials(config, collected, impl_modules, providers)
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
def _provenance(
|
| 218 |
+
inputs: EngineSnapshotInputs,
|
| 219 |
+
repo: Path | None,
|
| 220 |
+
engine: dict[str, Any],
|
| 221 |
+
materials: _Materials,
|
| 222 |
+
steps_use_model: bool,
|
| 223 |
+
) -> dict[str, Any]:
|
| 224 |
+
"""์ง๋ฌธ ๋ฐ ์ถ์ฒ โ ์ด๋ฆยท์์นยท์ปค๋ฐยท์์ง ์ํยท์ค์น ํํฌยท์คํ ์ต์
(๊ณํ ยง3.2 ์์)."""
|
| 225 |
+
return {
|
| 226 |
+
"prompt": inputs.prompt_name,
|
| 227 |
+
"pipeline": inputs.pipeline_name,
|
| 228 |
+
"config_source": inputs.config_source,
|
| 229 |
+
"git_commit": inputs.git_commit,
|
| 230 |
+
"pipeline_overrides": copy.deepcopy(inputs.pipeline_overrides),
|
| 231 |
+
"engine": engine,
|
| 232 |
+
"uv_lock_sha256": environment.uv_lock_sha256(repo),
|
| 233 |
+
"installed_closure": environment.installed_closure(),
|
| 234 |
+
"impl_modules": materials.impl_modules,
|
| 235 |
+
PROVIDERS_KEY: materials.providers,
|
| 236 |
+
"steps_use_model": steps_use_model,
|
| 237 |
+
"execution": {"max_workers": inputs.max_workers},
|
| 238 |
+
"resumes": [],
|
| 239 |
+
}
|
| 240 |
+
|
| 241 |
+
|
| 242 |
+
def _reproducibility(
|
| 243 |
+
inputs: EngineSnapshotInputs,
|
| 244 |
+
materials: _Materials,
|
| 245 |
+
engine: dict[str, Any],
|
| 246 |
+
steps_use_model: bool,
|
| 247 |
+
) -> dict[str, Any]:
|
| 248 |
+
verdict, blockers = reproducibility.rerunnable(
|
| 249 |
+
engine_dirty=engine.get("dirty"),
|
| 250 |
+
repo_managed=inputs.repo_managed,
|
| 251 |
+
dataset=inputs.dataset,
|
| 252 |
+
resources=materials.collected.refs,
|
| 253 |
+
)
|
| 254 |
+
config = materials.config
|
| 255 |
+
return {
|
| 256 |
+
"engine_dirty": engine.get("dirty"),
|
| 257 |
+
"rerunnable": verdict,
|
| 258 |
+
"rerunnable_blockers": blockers,
|
| 259 |
+
"deterministic": reproducibility.deterministic(
|
| 260 |
+
temperature=inputs.temperature,
|
| 261 |
+
resolved_pipeline=config["resolved_pipeline"],
|
| 262 |
+
steps_use_model=steps_use_model,
|
| 263 |
+
judge_model=config["evaluator_config"]["judge_model"],
|
| 264 |
+
),
|
| 265 |
+
}
|
| 266 |
+
|
| 267 |
+
|
| 268 |
+
def build_engine_snapshot(inputs: EngineSnapshotInputs) -> dict[str, Any]:
|
| 269 |
+
"""`{schema, config, config_fingerprint, missing, provenance, reproducibility}` ๋ฅผ ์กฐ๋ฆฝํ๋ค.
|
| 270 |
+
|
| 271 |
+
Raises:
|
| 272 |
+
KnowledgeAssetError: ์คํ
์ด ์ ์ธํ ์์์ ๋ก์ปฌ์์ ์ฐพ์ ์ ์์ ๋(run ์์ ์ ).
|
| 273 |
+
FileNotFoundError / ValueError: ํ๋กฌํํธ๊ฐ ์๊ฑฐ๋ ๋น์์ ๋, ๊ตฌํ ๋ชจ๋์ ํ ์ ์์ ๋.
|
| 274 |
+
"""
|
| 275 |
+
roots = inputs.roots or impl_hash.default_roots()
|
| 276 |
+
materials = _collect_config(inputs, roots)
|
| 277 |
+
repo = _resolve_repo(inputs)
|
| 278 |
+
engine = _engine(inputs, repo, roots)
|
| 279 |
+
steps_use_model = any(step.uses_model for step in inputs.pipeline.steps)
|
| 280 |
+
return {
|
| 281 |
+
"schema": ENGINE_SNAPSHOT_SCHEMA,
|
| 282 |
+
"config": materials.config,
|
| 283 |
+
"config_fingerprint": config_fingerprint(materials.config),
|
| 284 |
+
"missing": ["resources"] if materials.collected.unresolved else [],
|
| 285 |
+
"provenance": _provenance(inputs, repo, engine, materials, steps_use_model),
|
| 286 |
+
"reproducibility": _reproducibility(inputs, materials, engine, steps_use_model),
|
| 287 |
+
}
|
| 288 |
+
|
| 289 |
+
|
| 290 |
+
def self_check(snapshot: Mapping[str, Any]) -> None:
|
| 291 |
+
"""์์ง ์๊ธฐ ๊ฒ์ฆ(D5) โ ์ด๋ฏธ ์๋ poc-bundle ํจ์๋ง ์ด๋ค.
|
| 292 |
+
|
| 293 |
+
Raises:
|
| 294 |
+
SnapshotError: schema ๊ฐ ๋ค๋ฅด๊ฑฐ๋, ๊ท๋ฒ ํค๊ฐ ๋น ์ก๊ฑฐ๋, ์ง๋ฌธ์ด ์ฌ๊ณ์ฐ๊ณผ ๋ค๋ฅผ ๋.
|
| 295 |
+
"""
|
| 296 |
+
if snapshot.get("schema") != ENGINE_SNAPSHOT_SCHEMA:
|
| 297 |
+
raise SnapshotError(f"unknown snapshot schema {snapshot.get('schema')!r}")
|
| 298 |
+
config = snapshot.get("config")
|
| 299 |
+
if not isinstance(config, Mapping):
|
| 300 |
+
raise SnapshotError("snapshot.config must be an object")
|
| 301 |
+
gaps = completeness_gaps(config)
|
| 302 |
+
if gaps:
|
| 303 |
+
raise SnapshotError(f"snapshot.config lacks normative keys {gaps} (SPEC ยง3.4)")
|
| 304 |
+
expected = config_fingerprint(config)
|
| 305 |
+
if snapshot.get("config_fingerprint") != expected:
|
| 306 |
+
raise SnapshotError(
|
| 307 |
+
f"config_fingerprint {snapshot.get('config_fingerprint')!r} != recomputed {expected!r}"
|
| 308 |
+
)
|
| 309 |
+
|
| 310 |
+
|
| 311 |
+
def config_diff_keys(a: Mapping[str, Any], b: Mapping[str, Any]) -> list[str]:
|
| 312 |
+
"""๋ `snapshot.config` ์ ๊ฐ์ด ๋ค๋ฅธ ์ต์์ ํค(์ ๋ ฌ) โ resume ๋๋ฆฌํํธ ๋ฉ์์ง์ฉ(D17).
|
| 313 |
+
|
| 314 |
+
๊ฐ ๋น๊ต๋ JCS ์ง๋ฌธ์ผ๋ก ํ๋ค(`0.0` โก `0`).
|
| 315 |
+
"""
|
| 316 |
+
keys = set(a) | set(b)
|
| 317 |
+
return sorted(
|
| 318 |
+
k
|
| 319 |
+
for k in keys
|
| 320 |
+
if k not in a or k not in b or json_fingerprint(a[k]) != json_fingerprint(b[k])
|
| 321 |
+
)
|
| 322 |
+
|
| 323 |
+
|
| 324 |
+
def _rescore_providers(provenance: Mapping[str, Any], judge_provider: Any) -> dict[str, str | None]:
|
| 325 |
+
"""์ฌ์ฑ์ ์ ํ๋ก๋ฐ์ด๋ ๋ชจ๋ โ ์ถ๋ก ์ ๊ธฐ๋ก๋ ์ด๋ฆ, judge ๋ ๋๊ฒจ๋ฐ์ ๊ฒ(์์ผ๋ฉด ๊ธฐ๋ก๋ ์ด๋ฆ).
|
| 326 |
+
|
| 327 |
+
Raises:
|
| 328 |
+
SnapshotError: ๊ธฐ๋ก๋ ์ถ๋ก ํ๋ก๋ฐ์ด๋ ๋ชจ๋์ด ์์ด ๋ฐํ์ ํด์๋ฅผ ๋ค์ ๋ฐ ์ ์์ ๋.
|
| 329 |
+
"""
|
| 330 |
+
recorded = provenance.get(PROVIDERS_KEY)
|
| 331 |
+
if not isinstance(recorded, Mapping) or not isinstance(recorded.get("inference"), str):
|
| 332 |
+
raise SnapshotError(
|
| 333 |
+
"snapshot.provenance.providers.inference is missing โ cannot recompute "
|
| 334 |
+
"runtime_impl_hash for the new evaluator"
|
| 335 |
+
)
|
| 336 |
+
judge = recorded.get("judge")
|
| 337 |
+
if judge_provider is not None:
|
| 338 |
+
judge = impl_hash.provider_module(judge_provider)
|
| 339 |
+
return {"inference": recorded["inference"], "judge": judge}
|
| 340 |
+
|
| 341 |
+
|
| 342 |
+
def with_evaluator(
|
| 343 |
+
snapshot: Mapping[str, Any],
|
| 344 |
+
evaluator_config: Mapping[str, Any] | None,
|
| 345 |
+
re_evaluated_at: str,
|
| 346 |
+
roots: ImplRoots | None = None,
|
| 347 |
+
judge_provider: Any = None,
|
| 348 |
+
) -> dict[str, Any]:
|
| 349 |
+
"""`runs eval`(์ฌ์ฑ์ )์ฉ โ ํ๊ฐ๊ธฐ ๋ธ๋ก์ ์ ํ๊ฐ๊ธฐ๋ก ๋ฐ๊พธ๊ณ ์ง๋ฌธ์ ๋ค์ ๊ณ์ฐํ **์** ์ค๋
์ท(D11).
|
| 350 |
+
|
| 351 |
+
judge ์ ๋ฌด๊ฐ ๋ฐ๋๋ฉด judge ํ๋ก๋ฐ์ด๋ ๋ชจ๋์ด ๋ฐํ์ ํด์์ ๋ค์ด๊ฐ๊ฑฐ๋ ๋น ์ ธ์ผ ํ๋ฏ๋ก
|
| 352 |
+
`runtime_impl_hash`ยท`impl_modules["@runtime"]` ๋ ๋น๋์ ๊ฐ์ ๊ท์น(`_runtime`)์ผ๋ก ๋ค์ ๋ฌ๋ค โ
|
| 353 |
+
์ฌ์ฑ์ run ์ด ๊ฐ์ ๊ธฐ๋ฅ ์ค์ ์ ์ run ๊ณผ ๊ฐ์ ์ง๋ฌธ์ ๊ฐ๊ฒ. `judge_provider` ๋ฅผ ์ ์ฃผ๋ฉด ๋น๋ ๋
|
| 354 |
+
๊ธฐ๋กํ judge ๋ชจ๋์ ์ด๋ค. `provenance.re_evaluated_at` ์ ๋จ๊ธฐ๊ณ `deterministic` ๋ ๋ค์ ํ์ ํ๋ค.
|
| 355 |
+
|
| 356 |
+
Raises:
|
| 357 |
+
ValueError: ํ๊ฐ๊ธฐ ์ค์ ์ด ์๋ชป๋์ ๋(`evaluator_block`).
|
| 358 |
+
SnapshotError: ๊ธฐ๋ก๋ ํ๋ก๋ฐ์ด๋ ๋ชจ๋์ด ์์ด ๋ฐํ์ ํด์๋ฅผ ๋ค์ ๋ฐ ์ ์์ ๋.
|
| 359 |
+
"""
|
| 360 |
+
roots = roots or impl_hash.default_roots()
|
| 361 |
+
updated = copy.deepcopy(dict(snapshot))
|
| 362 |
+
block, modules = evaluator_block(evaluator_config, roots)
|
| 363 |
+
provenance = updated.setdefault("provenance", {})
|
| 364 |
+
providers = _rescore_providers(provenance, judge_provider)
|
| 365 |
+
runtime_hash, runtime_modules = _runtime(providers, block["judge_model"], roots)
|
| 366 |
+
config = updated["config"]
|
| 367 |
+
config["evaluator_config"] = block
|
| 368 |
+
config["runtime_impl_hash"] = runtime_hash
|
| 369 |
+
updated["config_fingerprint"] = config_fingerprint(config)
|
| 370 |
+
impl_modules = provenance.setdefault("impl_modules", {})
|
| 371 |
+
impl_modules[EVALUATOR_MODULES_KEY] = modules
|
| 372 |
+
impl_modules[RUNTIME_MODULES_KEY] = runtime_modules
|
| 373 |
+
provenance[PROVIDERS_KEY] = providers
|
| 374 |
+
provenance["re_evaluated_at"] = re_evaluated_at
|
| 375 |
+
steps_use_model = provenance.get("steps_use_model")
|
| 376 |
+
if isinstance(steps_use_model, bool):
|
| 377 |
+
updated.setdefault("reproducibility", {})["deterministic"] = reproducibility.deterministic(
|
| 378 |
+
temperature=config.get("temperature"),
|
| 379 |
+
resolved_pipeline=config.get("resolved_pipeline") or {},
|
| 380 |
+
steps_use_model=steps_use_model,
|
| 381 |
+
judge_model=block["judge_model"],
|
| 382 |
+
)
|
| 383 |
+
return updated
|
| 384 |
+
|
| 385 |
+
|
| 386 |
+
__all__ = [
|
| 387 |
+
"AUTO",
|
| 388 |
+
"CONFIG_KEYS",
|
| 389 |
+
"DEFAULT_EVALUATOR_CONFIG",
|
| 390 |
+
"ENGINE_SNAPSHOT_SCHEMA",
|
| 391 |
+
"EngineSnapshotInputs",
|
| 392 |
+
"ImplRoots",
|
| 393 |
+
"PROVIDERS_KEY",
|
| 394 |
+
"SnapshotError",
|
| 395 |
+
"build_engine_snapshot",
|
| 396 |
+
"config_diff_keys",
|
| 397 |
+
"evaluator_block",
|
| 398 |
+
"self_check",
|
| 399 |
+
"with_evaluator",
|
| 400 |
+
]
|
solar_eval/core/snapshot/dataset_block.py
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""run.json `dataset` ๋ธ๋ก(engine-dataset/v1)๊ณผ lock ๋์กฐ (๊ณํ 2026-09-24-phase1a S6 ยท D15).
|
| 2 |
+
|
| 3 |
+
lock ๋์กฐ์ ์ ์(๊ฒํ F8) โ `matched = true` ๋ ์
์ด **๋ชจ๋** ๋ง์ ๋๋ค:
|
| 4 |
+
|
| 5 |
+
1. ์ฝ์ ํ์ผ sha256 == lock ํญ๋ชฉ `sha256`
|
| 6 |
+
2. ์ด run ์ ํ์คํฌ ์ด๋ฆ โ lock ํญ๋ชฉ `dataset.tasks`
|
| 7 |
+
3. ํ์คํฌ์ `input_fields`ยท`golden_field`ยท`golden_fields` == lock ํญ๋ชฉ์ `inputFields`ยท`goldenField`ยท
|
| 8 |
+
`goldenFields` (lock `datasetVersion` ์ ์ด ์คํ์ผ๋ก ๊ณ์ฐ๋๋ค)
|
| 9 |
+
|
| 10 |
+
ํ๋๋ผ๋ ๋ค๋ฅด๋ฉด `matched: false` + `mismatch: ["sha256" | "task" | "fields", โฆ]`. ๊ทธ๋์ผ "์ ์ฒด์
run ์
|
| 11 |
+
`dataset_version` == lock `datasetVersion`" ์ด ์กฐ์ฉํ ๊นจ์ง์ง ์๋๋ค.
|
| 12 |
+
|
| 13 |
+
์ด ๋ชจ๋์ engine-filter ์ ํ์
(`PreparedDataset`)์ ๊ธฐ๋์ง ์๊ณ **์์ ๊ฐ**์ ๋ฐ๋๋ค.
|
| 14 |
+
"""
|
| 15 |
+
|
| 16 |
+
from __future__ import annotations
|
| 17 |
+
|
| 18 |
+
import copy
|
| 19 |
+
import logging
|
| 20 |
+
from collections.abc import Mapping, Sequence
|
| 21 |
+
from pathlib import Path
|
| 22 |
+
from typing import Any
|
| 23 |
+
|
| 24 |
+
from poc_bundle.data.lockfile import read_lock
|
| 25 |
+
from poc_bundle.data.lock_model import DatasetsLock, LockDatasetEntry
|
| 26 |
+
|
| 27 |
+
logger = logging.getLogger(__name__)
|
| 28 |
+
|
| 29 |
+
ENGINE_DATASET_SCHEMA = "engine-dataset/v1"
|
| 30 |
+
|
| 31 |
+
#: config_dir ๊ธฐ์ค lock ์์น (`poc-bundle data push` ๊ฐ ์ด๋ค).
|
| 32 |
+
LOCK_RELATIVE_PATH = Path("dataset") / "datasets.lock.yaml"
|
| 33 |
+
|
| 34 |
+
#: lock ๋์กฐ๊ฐ ์ ์ฉ๋๋ ๋ก๋ โ lock ์ ๋ก์ปฌ ๋ฐ์ดํฐ ํ(cbs-data ์์ pull ํ datasets ์์ญ)์ ์์ฅ์ด๋ค.
|
| 35 |
+
LOCAL_SOURCE = "local"
|
| 36 |
+
|
| 37 |
+
MISMATCH_SHA = "sha256"
|
| 38 |
+
MISMATCH_TASK = "task"
|
| 39 |
+
MISMATCH_FIELDS = "fields"
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def lock_path_for(config_dir: Path) -> Path:
|
| 43 |
+
return Path(config_dir) / LOCK_RELATIVE_PATH
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def task_field_spec(
|
| 47 |
+
task_config: Mapping[str, Any],
|
| 48 |
+
) -> tuple[list[str], str | None, dict[str, str] | None]:
|
| 49 |
+
"""(input_fields, golden_field, golden_fields) โ lock ๊ณ์ฐ(`poc_bundle.data.taskfields._golden`)๊ณผ
|
| 50 |
+
๊ฐ์ ์ ๊ทํ: ๋น ๊ฐ์ None, ๋ ๋ค ์์ผ๋ฉด golden_field ๊ฐ ์ด๊ธด๋ค(์์ง runner ๊ท์น).
|
| 51 |
+
"""
|
| 52 |
+
inputs = list(task_config.get("input_fields") or [])
|
| 53 |
+
field = task_config.get("golden_field") or None
|
| 54 |
+
fields = task_config.get("golden_fields") or None
|
| 55 |
+
if field and fields:
|
| 56 |
+
fields = None
|
| 57 |
+
return inputs, field, dict(fields) if fields else None
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def _display_path(path: Path, repo_root: Path | None) -> str:
|
| 61 |
+
if repo_root is not None:
|
| 62 |
+
try:
|
| 63 |
+
return path.resolve().relative_to(repo_root.resolve()).as_posix()
|
| 64 |
+
except ValueError:
|
| 65 |
+
pass
|
| 66 |
+
return path.resolve().as_posix()
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
def _find_entry(lock: DatasetsLock, dataset_path: str) -> LockDatasetEntry | None:
|
| 70 |
+
return next((e for e in lock.datasets if e.path == dataset_path), None)
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def compare_entry(
|
| 74 |
+
entry: LockDatasetEntry, sha256: str, task_name: str, task_config: Mapping[str, Any]
|
| 75 |
+
) -> list[str]:
|
| 76 |
+
"""lock ํญ๋ชฉ๊ณผ ์ด run ์ ๋ถ์ผ์น ์ฌ์ (์์ ๊ณ ์ : sha256 โ task โ fields). ๋น ๋ชฉ๋ก = matched."""
|
| 77 |
+
mismatch: list[str] = []
|
| 78 |
+
if entry.sha256 != sha256:
|
| 79 |
+
mismatch.append(MISMATCH_SHA)
|
| 80 |
+
info = entry.dataset
|
| 81 |
+
if info is None:
|
| 82 |
+
# dataset ๋ธ๋ก์ด ์๋ ํญ๋ชฉ(.jsonl ์ด ์๋)์ ํ์คํฌยทํ๋๋ฅผ ๋์กฐํ ์ ์๋ค.
|
| 83 |
+
return [*mismatch, MISMATCH_TASK, MISMATCH_FIELDS]
|
| 84 |
+
if task_name not in info.tasks:
|
| 85 |
+
mismatch.append(MISMATCH_TASK)
|
| 86 |
+
inputs, field, fields = task_field_spec(task_config)
|
| 87 |
+
lock_fields = dict(info.golden_fields) if info.golden_fields else None
|
| 88 |
+
if (inputs, field, fields) != (list(info.input_fields), info.golden_field, lock_fields):
|
| 89 |
+
mismatch.append(MISMATCH_FIELDS)
|
| 90 |
+
return mismatch
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
def lock_block(
|
| 94 |
+
*,
|
| 95 |
+
sha256: str,
|
| 96 |
+
dataset_path: str,
|
| 97 |
+
task_config: Mapping[str, Any],
|
| 98 |
+
config_dir: Path | None,
|
| 99 |
+
project_name: str,
|
| 100 |
+
repo_root: Path | None = None,
|
| 101 |
+
) -> dict[str, Any] | None:
|
| 102 |
+
"""`dataset.lock` โ lock ์ด ์๊ฑฐ๋ยทํญ๋ชฉ์ด ์๊ฑฐ๋ยทํ๋ก์ ํธ ์ด๋ฆ์ด ๋ค๋ฅด๋ฉด None.
|
| 103 |
+
|
| 104 |
+
Raises:
|
| 105 |
+
poc_bundle.data.errors.LockError: lock ํ์ผ์ด ๊ท์น(L01~L20)์ ์ด๊ธธ ๋ โ ๊นจ์ง ์์ฅ์ผ๋ก ๋์กฐํ
|
| 106 |
+
run ์ ๋ง๋ค์ง ์๋๋ค(`make data-lint` ๊ฐ ๊ฐ์ ์ค๋ฅ๋ฅผ ๋ธ๋ค).
|
| 107 |
+
"""
|
| 108 |
+
if config_dir is None:
|
| 109 |
+
return None
|
| 110 |
+
path = lock_path_for(config_dir)
|
| 111 |
+
if not path.is_file():
|
| 112 |
+
return None
|
| 113 |
+
lock = read_lock(path)
|
| 114 |
+
if lock.project is not None and lock.project != project_name:
|
| 115 |
+
logger.warning(
|
| 116 |
+
"datasets.lock.yaml project %r != run project %r โ lock not used for %s",
|
| 117 |
+
lock.project,
|
| 118 |
+
project_name,
|
| 119 |
+
dataset_path,
|
| 120 |
+
)
|
| 121 |
+
return None
|
| 122 |
+
entry = _find_entry(lock, dataset_path)
|
| 123 |
+
if entry is None:
|
| 124 |
+
return None
|
| 125 |
+
mismatch = compare_entry(entry, sha256, str(task_config.get("name", "")), task_config)
|
| 126 |
+
info = entry.dataset
|
| 127 |
+
return {
|
| 128 |
+
"path": _display_path(path, repo_root),
|
| 129 |
+
"matched": not mismatch,
|
| 130 |
+
"mismatch": mismatch,
|
| 131 |
+
"dataset_version": info.dataset_version if info else None,
|
| 132 |
+
"source": info.source.model_dump(mode="json") if info and info.source else None,
|
| 133 |
+
"builder": info.builder.model_dump(mode="json") if info and info.builder else None,
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
def describe_dataset(
|
| 138 |
+
*,
|
| 139 |
+
sha256: str,
|
| 140 |
+
rows_total: int,
|
| 141 |
+
rows_selected: int,
|
| 142 |
+
selection: Mapping[str, Any] | None,
|
| 143 |
+
meta_fields: Sequence[str] | None,
|
| 144 |
+
task_config: Mapping[str, Any],
|
| 145 |
+
loader_cfg: Mapping[str, Any],
|
| 146 |
+
config_dir: Path | None,
|
| 147 |
+
project_name: str,
|
| 148 |
+
repo_root: Path | None = None,
|
| 149 |
+
) -> dict[str, Any]:
|
| 150 |
+
"""run.json `dataset` ๋ธ๋ก. `selection` ์ engine-filter ๊ฐ ์ ๊ทํํ JSON(`{where, limit}`).
|
| 151 |
+
|
| 152 |
+
Raises:
|
| 153 |
+
ValueError: ํ ์๊ฐ ์์์ด๊ฑฐ๋ ์ ํ ํ์ด ์ ์ฒด๋ณด๋ค ๋ง์ ๋(ํธ์ถ์ ๋ฒ๊ทธ๋ฅผ ํฌ๊ฒ ์๋ฆฐ๋ค).
|
| 154 |
+
"""
|
| 155 |
+
if rows_total < 0 or rows_selected < 0 or rows_selected > rows_total:
|
| 156 |
+
raise ValueError(f"invalid row counts: selected={rows_selected}, total={rows_total}")
|
| 157 |
+
source = loader_cfg.get("source", "huggingface")
|
| 158 |
+
dataset_path = str(task_config.get("dataset_path", ""))
|
| 159 |
+
lock = None
|
| 160 |
+
if source == LOCAL_SOURCE:
|
| 161 |
+
lock = lock_block(
|
| 162 |
+
sha256=sha256,
|
| 163 |
+
dataset_path=dataset_path,
|
| 164 |
+
task_config=task_config,
|
| 165 |
+
config_dir=config_dir,
|
| 166 |
+
project_name=project_name,
|
| 167 |
+
repo_root=repo_root,
|
| 168 |
+
)
|
| 169 |
+
return {
|
| 170 |
+
"schema": ENGINE_DATASET_SCHEMA,
|
| 171 |
+
"loader": {"source": source, "repo": loader_cfg.get("repo", "")},
|
| 172 |
+
"path": dataset_path,
|
| 173 |
+
"sha256": sha256,
|
| 174 |
+
"rows_total": rows_total,
|
| 175 |
+
"rows_selected": rows_selected,
|
| 176 |
+
"selection": copy.deepcopy(dict(selection)) if selection is not None else None,
|
| 177 |
+
"meta_fields": list(meta_fields) if meta_fields else None,
|
| 178 |
+
"lock": lock,
|
| 179 |
+
}
|
solar_eval/core/snapshot/environment.py
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""์คํ ํ๊ฒฝ ์ฌ๋ฃ โ ์์กด ๋ฒ์ ยท์์ง ๋ฒ์ ยท์์ง ๋ ํฌ ์ํ (๊ณํ 2026-09-24-phase1a D8ยทD9).
|
| 2 |
+
|
| 3 |
+
config ์ ๋ค์ด๊ฐ๋ ๊ฒ(์ง๋ฌธ ๋์)์ ์ถ๋ ฅ ๊ฒฝ๋ก์ ์๋ **์ง์ ** ๋ฐํ์ ์์กด๊ณผ ์์ง ๋ฒ์ ยทํ์ด์ฌ ๋ถ๋ฒ์ ๋ฟ์ด๋ค.
|
| 4 |
+
์ ์ด ์ค์น ํํฌยทuv.lockยท์์ง ์ปค๋ฐยทํธ๋ฆฌ ํด์ยทdirty ๋ provenance(์ง๋ฌธ ๋ฐ)๋ก ๊ฐ๋ค โ ๋ฌด๊ดํ ์ํฌ์คํ์ด์ค
|
| 5 |
+
๋ฉค๋ฒ์ ๋ณํ๊ฐ ์ค์ ์ง๋ฌธ์ ํ๋ค์ง ์๊ฒ.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
from __future__ import annotations
|
| 9 |
+
|
| 10 |
+
import logging
|
| 11 |
+
import subprocess
|
| 12 |
+
import sys
|
| 13 |
+
from collections.abc import Callable
|
| 14 |
+
from importlib import metadata
|
| 15 |
+
from pathlib import Path
|
| 16 |
+
from typing import Any
|
| 17 |
+
|
| 18 |
+
from packaging.requirements import InvalidRequirement, Requirement
|
| 19 |
+
from packaging.utils import canonicalize_name
|
| 20 |
+
from poc_bundle.canonical import content_sha256
|
| 21 |
+
|
| 22 |
+
import solar_eval
|
| 23 |
+
from solar_eval._version import __version__
|
| 24 |
+
from solar_eval.core.snapshot.impl_hash import ImplRoots, engine_tree_sha256
|
| 25 |
+
|
| 26 |
+
logger = logging.getLogger(__name__)
|
| 27 |
+
|
| 28 |
+
#: ์์ง ๋ฐฐํฌ ์ด๋ฆ.
|
| 29 |
+
ENGINE_DISTRIBUTION = "solar-eval"
|
| 30 |
+
|
| 31 |
+
#: ๋ชจ๋ธ ์
์ถ๋ ฅ์ ๋ฟ์ง ์๋ ์ง์ ์์กด โ CLI ํ์ยท์ค์ ๋ก๋ยท๋ฒ๋ค ๋๊ตฌ. `dependency_lock` ์์ ๋บ๋ค.
|
| 32 |
+
#: ์ ์ง์ ์์กด์ ์ถ๊ฐํ๋ฉด ์ด ๋ชฉ๋ก์ด๋ ์ถ๋ ฅ ์์กด ์ค ํ๋๋ก ๋ถ๋ฅํ๊ธฐ ์ ๊น์ง ํ
์คํธ๊ฐ ๊นจ์ง๋ค(์๋).
|
| 33 |
+
NON_OUTPUT_DEPENDENCIES = frozenset(
|
| 34 |
+
{"click", "tabulate", "rich", "pydantic-settings", "poc-bundle", "packaging"}
|
| 35 |
+
)
|
| 36 |
+
|
| 37 |
+
DEPENDENCY_LOCK_SCHEME = "direct-runtime/v1"
|
| 38 |
+
|
| 39 |
+
#: config_dir ์์์ ์ถ๋ ฅ์ ์ํฅ์ ์ฃผ๋ ๊ฒฝ๋ก(dirty ๋ฒ์). scripts/ยทinsights/ยทREADME ๋ ๋บ๋ค(๊ฒํ F12ยทm3).
|
| 40 |
+
CONFIG_DIRTY_ENTRIES = (
|
| 41 |
+
"project.yaml",
|
| 42 |
+
"prompts",
|
| 43 |
+
"pipelines",
|
| 44 |
+
"resources",
|
| 45 |
+
"dataset/datasets.lock.yaml",
|
| 46 |
+
)
|
| 47 |
+
|
| 48 |
+
GitRun = Callable[..., Any]
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def _marker_env() -> dict[str, str]:
|
| 52 |
+
return {"extra": ""}
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def direct_requirements(distribution: str = ENGINE_DISTRIBUTION) -> list[str]:
|
| 56 |
+
"""๋ฐฐํฌ์ ์ง์ `Requires-Dist` ์ค ํ์ฌ ํ๊ฒฝ์์ ์ผ์ง๋ ๊ฒ(extra ์์)์ ์ ๊ทํ ์ด๋ฆ(์ ๋ ฌ)."""
|
| 57 |
+
names: set[str] = set()
|
| 58 |
+
for raw in metadata.requires(distribution) or []:
|
| 59 |
+
try:
|
| 60 |
+
req = Requirement(raw)
|
| 61 |
+
except InvalidRequirement as exc:
|
| 62 |
+
raise ValueError(f"{distribution}: unparsable requirement {raw!r}: {exc}") from exc
|
| 63 |
+
if req.marker is not None and not req.marker.evaluate(_marker_env()):
|
| 64 |
+
continue
|
| 65 |
+
names.add(canonicalize_name(req.name))
|
| 66 |
+
return sorted(names)
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
def direct_runtime_dependencies() -> dict[str, str]:
|
| 70 |
+
"""`dependency_lock.packages` โ ์ถ๋ ฅ ๊ฒฝ๋ก์ ์๋ ์ง์ ์์กด์ ์ค์น ๋ฒ์ ."""
|
| 71 |
+
return {
|
| 72 |
+
name: metadata.version(name)
|
| 73 |
+
for name in direct_requirements()
|
| 74 |
+
if name not in NON_OUTPUT_DEPENDENCIES
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
def dependency_lock() -> dict[str, Any]:
|
| 79 |
+
return {
|
| 80 |
+
"scheme": DEPENDENCY_LOCK_SCHEME,
|
| 81 |
+
"root": ENGINE_DISTRIBUTION,
|
| 82 |
+
"packages": direct_runtime_dependencies(),
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
def installed_closure(distribution: str = ENGINE_DISTRIBUTION) -> dict[str, str | None]:
|
| 87 |
+
"""์์ง์์ ๋ฟ๋ ์ค์น ํํฌ `{์ด๋ฆ: ๋ฒ์ }` โ provenance ์ ์ฉ(ํ๊ฒฝ ์ฌ๊ตฌ์ฑยท๋๋ฒ๊ทธ).
|
| 88 |
+
|
| 89 |
+
์ผ์ง ์๊ตฌ์ธ๋ฐ ์ค์น๋ผ ์์ง ์์ผ๋ฉด ๋ฒ์ None ์ผ๋ก ๋จ๊ธด๋ค(์จ๊ธฐ์ง ์๋๋ค).
|
| 90 |
+
"""
|
| 91 |
+
seen: dict[str, str | None] = {}
|
| 92 |
+
queue: list[str] = [str(canonicalize_name(distribution))]
|
| 93 |
+
while queue:
|
| 94 |
+
name = queue.pop()
|
| 95 |
+
if name in seen:
|
| 96 |
+
continue
|
| 97 |
+
try:
|
| 98 |
+
seen[name] = metadata.version(name)
|
| 99 |
+
except metadata.PackageNotFoundError:
|
| 100 |
+
seen[name] = None
|
| 101 |
+
continue
|
| 102 |
+
queue.extend(n for n in direct_requirements(name) if n not in seen)
|
| 103 |
+
return dict(sorted(seen.items()))
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def runner_version() -> dict[str, str]:
|
| 107 |
+
"""`{"package", "version", "python": "<major>.<minor>"}` โ ํจ์น ๋ฒ์ ์ ๋ฃ์ง ์๋๋ค."""
|
| 108 |
+
return {
|
| 109 |
+
"package": ENGINE_DISTRIBUTION,
|
| 110 |
+
"version": __version__,
|
| 111 |
+
"python": f"{sys.version_info.major}.{sys.version_info.minor}",
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
def engine_src_dir() -> Path:
|
| 116 |
+
"""์์ง ์์ค ๋ฃจํธ(`packages/solar-eval-python/src`) โ `solar_eval.__file__` ๊ธฐ์ค."""
|
| 117 |
+
return Path(solar_eval.__file__).resolve().parent.parent
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
def engine_repo(start: Path | None = None) -> Path | None:
|
| 121 |
+
"""์์ง ์ฝ๋๊ฐ ๋ git ๋ ํฌ โ `start`(๊ธฐ๋ณธ ์์ง ์์ค)์์ ์๋ก `.git` ์ ์ฐพ๋๋ค.
|
| 122 |
+
|
| 123 |
+
`cfg.repo_root` ๋ฅผ ์ฐ์ง ์๋๋ค: `--projects-dir` ๋ฉด None ์ด ๋๋ค(๊ฒํ F13). ์ํฌํธ๋ฆฌ์ `.git` ์
|
| 124 |
+
ํ์ผ์ด๋ผ ์กด์ฌ๋ง ๋ณธ๋ค.
|
| 125 |
+
"""
|
| 126 |
+
here = (start or engine_src_dir()).resolve()
|
| 127 |
+
for candidate in (here, *here.parents):
|
| 128 |
+
if (candidate / ".git").exists():
|
| 129 |
+
return candidate
|
| 130 |
+
return None
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
def dirty_scope(
|
| 134 |
+
repo: Path, config_dir: Path | None, repo_managed: bool, src_dir: Path | None = None
|
| 135 |
+
) -> list[str]:
|
| 136 |
+
"""dirty ํ์ ๋ฒ์(๋ ํฌ ์๋ POSIX, ์ ๋ ฌ). ๋ ํฌ ๋ฐ ๊ฒฝ๋ก๋ ๋ฃ์ง ์๋๋ค."""
|
| 137 |
+
src = (src_dir or engine_src_dir()).resolve()
|
| 138 |
+
candidates = [src, src.parent / "pyproject.toml", repo / "uv.lock"]
|
| 139 |
+
if repo_managed and config_dir is not None:
|
| 140 |
+
candidates += [Path(config_dir).resolve() / entry for entry in CONFIG_DIRTY_ENTRIES]
|
| 141 |
+
root = repo.resolve()
|
| 142 |
+
scope = set()
|
| 143 |
+
for path in candidates:
|
| 144 |
+
try:
|
| 145 |
+
scope.add(path.resolve().relative_to(root).as_posix())
|
| 146 |
+
except ValueError:
|
| 147 |
+
logger.warning("dirty scope path outside the engine repo, skipped: %s", path)
|
| 148 |
+
return sorted(scope)
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
def _git(run: GitRun, repo: Path, *args: str) -> str | None:
|
| 152 |
+
"""git ํ ๋ฒ. ์คํจ(git ์์ยท๋ ํฌ ์๋ยท๋น์ ์ ์ข
๋ฃ)๋ None โ ํธ์ถ์๊ฐ null ๋ก ๊ธฐ๋กํ๋ค."""
|
| 153 |
+
try:
|
| 154 |
+
result = run(["git", "-C", str(repo), *args], capture_output=True, text=True, check=True)
|
| 155 |
+
except (OSError, subprocess.CalledProcessError) as exc:
|
| 156 |
+
logger.warning("git %s failed in %s: %s", " ".join(args[:2]), repo, exc)
|
| 157 |
+
return None
|
| 158 |
+
return result.stdout
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
def engine_state(
|
| 162 |
+
config_dir: Path | None,
|
| 163 |
+
repo_managed: bool,
|
| 164 |
+
*,
|
| 165 |
+
repo: Path | None = None,
|
| 166 |
+
src_dir: Path | None = None,
|
| 167 |
+
roots: ImplRoots | None = None,
|
| 168 |
+
run: GitRun = subprocess.run,
|
| 169 |
+
) -> dict[str, Any]:
|
| 170 |
+
"""`provenance.engine = {commit, tree_sha256, dirty, dirty_scope}` (D8).
|
| 171 |
+
|
| 172 |
+
`repo` ๋ฅผ ์ ์ฃผ๋ฉด `engine_repo()`. ๋ ํฌ๋ฅผ ๋ชป ์ฐพ๊ฑฐ๋ git ์ด ์คํจํ๋ฉด `commit`ยท`dirty` ๋ null,
|
| 173 |
+
`tree_sha256` ์ git ์์ด๋ ๊ณ์ฐ๋๋ค.
|
| 174 |
+
"""
|
| 175 |
+
repo = repo if repo is not None else engine_repo(src_dir)
|
| 176 |
+
tree = engine_tree_sha256(roots)
|
| 177 |
+
if repo is None:
|
| 178 |
+
return {"commit": None, "tree_sha256": tree, "dirty": None, "dirty_scope": []}
|
| 179 |
+
scope = dirty_scope(repo, config_dir, repo_managed, src_dir)
|
| 180 |
+
head = _git(run, repo, "rev-parse", "HEAD")
|
| 181 |
+
# ๋น ๋ฒ์๋ก status ๋ฅผ ๋ถ๋ฅด๋ฉด ๋ ํฌ ์ ์ฒด๋ฅผ ๋ณธ๋ค โ ๋ฒ์๊ฐ ์์ผ๋ฉด ํ์ ํ์ง ์๋๋ค(null).
|
| 182 |
+
# `--literal-pathspecs`: config_dir ์ด๋ฆ์ `[`ยท`*`ยท`?` ๋ฅผ glob ์ผ๋ก ์ฝ์ง ์๊ฒ(๋ฒ์ ๋ฐ ๋ณ๊ฒฝ์ด dirty ๋ก).
|
| 183 |
+
status = (
|
| 184 |
+
_git(
|
| 185 |
+
run,
|
| 186 |
+
repo,
|
| 187 |
+
"--literal-pathspecs",
|
| 188 |
+
"status",
|
| 189 |
+
"--porcelain",
|
| 190 |
+
"--untracked-files=normal",
|
| 191 |
+
"--",
|
| 192 |
+
*scope,
|
| 193 |
+
)
|
| 194 |
+
if scope
|
| 195 |
+
else None
|
| 196 |
+
)
|
| 197 |
+
return {
|
| 198 |
+
"commit": head.strip() if head else None,
|
| 199 |
+
"tree_sha256": tree,
|
| 200 |
+
"dirty": None if status is None else bool(status.strip()),
|
| 201 |
+
"dirty_scope": scope,
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
|
| 205 |
+
def uv_lock_sha256(repo: Path | None) -> str | None:
|
| 206 |
+
"""๋ ํฌ `uv.lock` ์ sha256 โ provenance ์ ์ฉ(๋ชจ๋
ธ๋ ํฌ lock ์ ๋ฌด๊ดํ ๋ฉค๋ฒ๋ก๋ ๋ฐ๋๋ค)."""
|
| 207 |
+
if repo is None:
|
| 208 |
+
return None
|
| 209 |
+
lock = repo / "uv.lock"
|
| 210 |
+
return content_sha256(lock) if lock.is_file() else None
|
solar_eval/core/snapshot/impl_hash.py
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""๊ตฌํ ํด์ โ ๋ชจ๋์ AST import ํํฌ ร ํ์ผ ๋ฐ์ดํธ (๊ณํ 2026-09-24-phase1a D7).
|
| 2 |
+
|
| 3 |
+
์ธ ๊ฐ๋๊ฐ ๊ฐ์ ์ฌ๋ฃ๋ฅผ ์ด๋ค:
|
| 4 |
+
|
| 5 |
+
- **์คํ
**(`step_impl_hashes`): ์คํ
ํด๋์ค ๋ชจ๋์์ ์์ํ import ํํฌ.
|
| 6 |
+
- **ํ๊ฐ๊ธฐ**(`evaluator_impl_hash`): ํ๊ฐ๊ธฐ ํด๋์ค(ํฉ์ฑ์ด๋ฉด ํ์ ์ ๋ถ)์ ๋ชจ๋์์ ์์ํ ํํฌ.
|
| 7 |
+
- **๋ฐํ์**(`runtime_impl_hash`): `RUNTIME_MODULES` + ์ค์ ๋ก ์ด ํ๋ก๋ฐ์ด๋ ๋ชจ๋ โ ํํฌ๋ฅผ ๋์ง ์๊ณ
|
| 8 |
+
๋ชฉ๋ก์ ํ์ผ ๋ฐ์ดํธ๋ง ๋ณธ๋ค(๋ชฉ๋ก์ด ๊ณง ์ ์).
|
| 9 |
+
|
| 10 |
+
๋ชจ๋ โ ํ์ผ์ **์ด๋ฆ์ผ๋ก** ํผ๋ค(`ImplRoots`). ์ด๋ฏธ import ๋ ๋ชจ๋์ `__file__` ์ด๋ importlib spec ์
|
| 11 |
+
์ฐ์ง ์์ผ๋ฏ๋ก, ํ
์คํธ๊ฐ ๊ธฐ๋ณธ ๋ฃจํธ๋ฅผ tmp ์ฌ๋ณธ์ผ๋ก ๋ฐ๊ฟ ๋ผ์ฐ๋ฉด ์ฌimport ์์ด "๊ตฌํ๋ง ๋ฐ๊พผ" ์ํฉ์ด
|
| 12 |
+
๋ง๋ค์ด์ง๋ค. ํด์๋ ๊ณ์ฐํ๋ ์๊ฐ ๋์คํฌ์ ํ์ผ์ ์ฝ๋๋ค.
|
| 13 |
+
"""
|
| 14 |
+
|
| 15 |
+
from __future__ import annotations
|
| 16 |
+
|
| 17 |
+
import ast
|
| 18 |
+
import os
|
| 19 |
+
import sys
|
| 20 |
+
import sysconfig
|
| 21 |
+
from collections.abc import Iterable, Sequence
|
| 22 |
+
from dataclasses import dataclass, field
|
| 23 |
+
from pathlib import Path
|
| 24 |
+
from typing import Any
|
| 25 |
+
|
| 26 |
+
from poc_bundle.canonical import content_sha256, json_fingerprint
|
| 27 |
+
|
| 28 |
+
import solar_eval
|
| 29 |
+
|
| 30 |
+
#: ์์ง ํจํค์ง ์ด๋ฆ โ ํจํค์ง ๋ฃจํธ์์๋ ์ด ์ด๋ฆ ์๋ ๋ชจ๋๋ง ํผ๋ค(์๋ํํฐ ์ฐจ๋จ).
|
| 31 |
+
ENGINE_PACKAGE = "solar_eval"
|
| 32 |
+
|
| 33 |
+
#: ๋ฌ๋ยทํ์ดํ๋ผ์ธ ์คํ๊ธฐยทํ๋กฌํํธ ์กฐ๋ฆฝ โ ๋ชจ๋ ์คํ
์ ๋๊ฐ์ด ์ํฅ์ ์ฃผ๋ ํ๋ ์์ํฌ ๋ชจ๋.
|
| 34 |
+
#: ์ฌ๊ธฐ์ ์ด๋ฒ run ์ด ์ฐ๋ ํ๋ก๋ฐ์ด๋ ๋ชจ๋์ ๋ํด `runtime_impl_hash` ๋ฅผ ๋ฌ๋ค. ํ
์คํธ๊ฐ ์งํฉ์ ๊ณ ์ ํ๋ค.
|
| 35 |
+
#: ๋ฌ๋์์ ๋ผ์ด ๋ธ ๋์ฐ๋ฏธ(์
๋ ฅยท์ ๋ต ์ถ์ถ `run_records`, ํ ์ ํ `dataset_prep`ยท`row_filter`, ํ์ดํ๋ผ์ธ
|
| 36 |
+
#: ์์ฑ `pipeline_factory`)๋ ๋ฌ๋ ๋ก์ง์ด๋ผ ํจ๊ป ๋ณธ๋ค โ `solar_eval.core.*` ๋ ์คํ
ยทํ๊ฐ๊ธฐ ํํฌ์์ ๋น ์ง๋ฏ๋ก
|
| 37 |
+
#: ์ฌ๊ธฐ ์์ผ๋ฉด ์ด๋ค ํด์๋ ์ด ํ์ผ๋ค์ ๋ณด์ง ์๋๋ค. ํ
์คํธ๊ฐ ๋ฌ๋์ core import ๊ฐ ์ ๋ถ ์ฌ๊ธฐ ์๋์ง ์งํจ๋ค.
|
| 38 |
+
RUNTIME_MODULES: tuple[str, ...] = (
|
| 39 |
+
"solar_eval.core.runner",
|
| 40 |
+
"solar_eval.core.dataset_loader",
|
| 41 |
+
"solar_eval.core.dataset_prep",
|
| 42 |
+
"solar_eval.core.row_filter",
|
| 43 |
+
"solar_eval.core.pipeline_factory",
|
| 44 |
+
"solar_eval.core.run_records",
|
| 45 |
+
"solar_eval.pipelines.pipeline",
|
| 46 |
+
"solar_eval.pipelines.registry",
|
| 47 |
+
"solar_eval.pipelines.base",
|
| 48 |
+
"solar_eval.evaluators.base",
|
| 49 |
+
"solar_eval.evaluators.registry",
|
| 50 |
+
"solar_eval.providers.base",
|
| 51 |
+
"solar_eval.models.prompt_version",
|
| 52 |
+
"solar_eval.models.sample",
|
| 53 |
+
)
|
| 54 |
+
|
| 55 |
+
#: ์คํ
ยทํ๊ฐ๊ธฐ ํํฌ์์ ๋นผ๋ ๊ณต์ฉ ๊ธฐ๋ฐ โ ๋ฐํ์ ํด์๊ฐ ํ ๋ฒ ๋ณด๊ฑฐ๋ ์ถ๋ ฅ๊ณผ ๋ฌด๊ดํ ๊ฒ.
|
| 56 |
+
_EXCLUDED_EXACT = frozenset(
|
| 57 |
+
{*RUNTIME_MODULES, "solar_eval.pipelines.errors", "solar_eval.config", "solar_eval._version"}
|
| 58 |
+
)
|
| 59 |
+
_EXCLUDED_PREFIXES = (
|
| 60 |
+
"solar_eval.providers.",
|
| 61 |
+
"solar_eval.cli.",
|
| 62 |
+
"solar_eval.stores.",
|
| 63 |
+
"solar_eval.core.",
|
| 64 |
+
)
|
| 65 |
+
|
| 66 |
+
#: ๊ตฌํ ํด์ ๋ผ๋ฒจ ์ ๋์ด โ `pkg:<๋ฃจํธ ๊ธฐ์ค POSIX ๊ฒฝ๋ก>`.
|
| 67 |
+
LABEL_PREFIX = "pkg:"
|
| 68 |
+
#: ๋ฃจํธ์์ ํ ์ ์๋ ํ๋ก๋ฐ์ด๋ ๋ชจ๋(ํ
์คํธ mock ๋ฑ)์ ๋ผ๋ฒจ ์ ๋์ด. ๊ฐ์ null ๋ก ๋ค์ด๊ฐ๋ค.
|
| 69 |
+
UNRESOLVED_PREFIX = "unresolved:"
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
class ImplRootError(ValueError):
|
| 73 |
+
"""๊ตฌํ ํด์ ๋ฃจํธ๋ก ์ธ ์ ์๋ ๋๋ ํ ๋ฆฌ(์๋ํํฐยทํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ญ)."""
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
def _forbidden_bases() -> list[Path]:
|
| 77 |
+
"""๋ฑ๋ก ๋ฃจํธ๋ก ๊ฑฐ๋ถํ๋ ์์ญ โ site-packagesยทํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌยท์ธํฐํ๋ฆฌํฐ prefix."""
|
| 78 |
+
paths = sysconfig.get_paths()
|
| 79 |
+
bases = [paths.get(k) for k in ("purelib", "platlib", "stdlib")]
|
| 80 |
+
bases += [sys.prefix, sys.base_prefix]
|
| 81 |
+
return [Path(b).resolve() for b in bases if b]
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def _is_within(path: Path, base: Path) -> bool:
|
| 85 |
+
try:
|
| 86 |
+
path.relative_to(base)
|
| 87 |
+
except ValueError:
|
| 88 |
+
return False
|
| 89 |
+
return True
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
@dataclass(frozen=True)
|
| 93 |
+
class ResolvedModule:
|
| 94 |
+
"""์ด๋ฆ์ผ๋ก ํผ ๋ชจ๋ ํ๋. `label` ์ ์ง๋ฌธ ํค(`pkg:solar_eval/pipelines/rules.py`)."""
|
| 95 |
+
|
| 96 |
+
name: str
|
| 97 |
+
path: Path
|
| 98 |
+
label: str
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
@dataclass(frozen=True)
|
| 102 |
+
class ImplRoots:
|
| 103 |
+
"""๋ชจ๋ ์ด๋ฆ โ ํ์ผ ํด์ ๋ฃจํธ.
|
| 104 |
+
|
| 105 |
+
- `package_parent`: `solar_eval` ํจํค์ง์ ๋ถ๋ชจ ๋๋ ํ ๋ฆฌ. ์ฌ๊ธฐ์๋ `solar_eval.*` ์ด๋ฆ๋ง ํผ๋ค โ
|
| 106 |
+
์์ง์ด site-packages ์ ์ค์น๋ผ ์์ด๋ ์๋ํํฐ๊ฐ ํํฌ์ ๋ค์ด์ค์ง ์๋๋ค.
|
| 107 |
+
- `extra`: ํ
์คํธยทํ์ฅ์ด ๋ช
์ ๋ฑ๋กํ ๋๋ ํ ๋ฆฌ. ์ฌ๊ธฐ์๋ ์ด๋ค ์ด๋ฆ์ด๋ ํผ๋ค. ์๋ํํฐยทํ์ค
|
| 108 |
+
๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ญ(`sysconfig` purelib/platlib/stdlib, `sys.prefix`, `sys.base_prefix`)์ ๊ฑฐ๋ถํ๋ค
|
| 109 |
+
(uv ์ํฌ์คํ์ด์ค venv ๊ฐ ๋ ํฌ ์์ ์์ด "๋ ํฌ ์" ๊ท์น์ด๋ฉด httpx ๊ฐ ํํฌ์ ๋ค์ด์จ๋ค โ ๊ฒํ F3).
|
| 110 |
+
"""
|
| 111 |
+
|
| 112 |
+
package_parent: Path
|
| 113 |
+
extra: tuple[Path, ...] = field(default=())
|
| 114 |
+
|
| 115 |
+
def __post_init__(self) -> None:
|
| 116 |
+
forbidden = _forbidden_bases()
|
| 117 |
+
for root in self.extra:
|
| 118 |
+
resolved = Path(root).resolve()
|
| 119 |
+
if any(_is_within(resolved, base) for base in forbidden):
|
| 120 |
+
raise ImplRootError(
|
| 121 |
+
f"{root} is inside the interpreter/site-packages area โ third-party code is "
|
| 122 |
+
"covered by dependency_lock, not by implementation hashes"
|
| 123 |
+
)
|
| 124 |
+
|
| 125 |
+
@classmethod
|
| 126 |
+
def for_engine(cls, extra: Iterable[Path] = ()) -> ImplRoots:
|
| 127 |
+
"""์ง๊ธ import ๋ `solar_eval` ํจํค์ง์ ๋ถ๋ชจ + ๋ฑ๋ก ๋๋ ํ ๋ฆฌ."""
|
| 128 |
+
parent = Path(solar_eval.__file__).resolve().parent.parent
|
| 129 |
+
return cls(parent, tuple(Path(p) for p in extra))
|
| 130 |
+
|
| 131 |
+
def with_extra(self, *dirs: Path) -> ImplRoots:
|
| 132 |
+
return ImplRoots(self.package_parent, (*self.extra, *(Path(d) for d in dirs)))
|
| 133 |
+
|
| 134 |
+
def resolve(self, name: str) -> ResolvedModule | None:
|
| 135 |
+
"""๋ชจ๋ ์ด๋ฆ์ ํ์ผ๋ก ํผ๋ค: `R/<a>/<b>.py` ๋๋ `R/<a>/<b>/__init__.py`. ์์ผ๋ฉด None."""
|
| 136 |
+
parts = name.split(".")
|
| 137 |
+
if not all(p.isidentifier() for p in parts):
|
| 138 |
+
return None
|
| 139 |
+
roots: list[Path] = []
|
| 140 |
+
if parts[0] == ENGINE_PACKAGE:
|
| 141 |
+
roots.append(self.package_parent)
|
| 142 |
+
roots.extend(self.extra)
|
| 143 |
+
for root in roots:
|
| 144 |
+
base = Path(root).joinpath(*parts)
|
| 145 |
+
for candidate in (base.with_suffix(".py"), base / "__init__.py"):
|
| 146 |
+
if candidate.is_file() and _exact_case(Path(root), candidate):
|
| 147 |
+
rel = candidate.relative_to(root).as_posix()
|
| 148 |
+
return ResolvedModule(name, candidate, LABEL_PREFIX + rel)
|
| 149 |
+
return None
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
def _exact_case(root: Path, candidate: Path) -> bool:
|
| 153 |
+
"""`candidate` ์ ๊ฒฝ๋ก ์ฑ๋ถ์ด ๋๋ ํ ๋ฆฌ ๋ชฉ๋ก์ ์ด๋ฆ๊ณผ **๋์๋ฌธ์๊น์ง** ๊ฐ์๊ฐ.
|
| 154 |
+
|
| 155 |
+
macOS APFS ์ `is_file()` ์ ๋์๋ฌธ์๋ฅผ ๋ฌด์ํ๋ค โ `from pkg import Helper`(ํด๋์ค)๊ฐ
|
| 156 |
+
ํ์ ๋ชจ๋ `pkg/helper.py` ๋ก ์๋ชป ํ๋ ค, ๊ฐ์ ์ฝ๋๊ฐ Linux ์ ๋ค๋ฅธ ํํฌยท๋ผ๋ฒจ์ ๋ธ๋ค.
|
| 157 |
+
ํ์ด์ฌ import ๋ ๋์๋ฌธ์๋ฅผ ๊ตฌ๋ถํ๋ฏ๋ก ์ฌ๊ธฐ์๋ ๋ชฉ๋ก๊ณผ ์ ํํ ๋์กฐํด Linux ์ ๊ฐ๊ฒ ํ๋ค.
|
| 158 |
+
"""
|
| 159 |
+
current = root
|
| 160 |
+
for part in candidate.relative_to(root).parts:
|
| 161 |
+
try:
|
| 162 |
+
names = os.listdir(current)
|
| 163 |
+
except OSError:
|
| 164 |
+
return False
|
| 165 |
+
if part not in names:
|
| 166 |
+
return False
|
| 167 |
+
current = current / part
|
| 168 |
+
return True
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
# ๊ธฐ๋ณธ ๋ฃจํธ โ None ์ด๋ฉด `ImplRoots.for_engine()`. ํ
์คํธ๋ `set_default_roots` ๋ก ์ฌ๋ณธ์ ๋ผ์ด๋ค.
|
| 172 |
+
_DEFAULT_ROOTS: ImplRoots | None = None
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
def default_roots() -> ImplRoots:
|
| 176 |
+
"""๊ธฐ๋ณธ ํด์ ๋ฃจํธ. `set_default_roots` ๋ก ๋ฐ๊พผ ๊ฐ์ด ์์ผ๋ฉด ๊ทธ๊ฒ."""
|
| 177 |
+
return _DEFAULT_ROOTS if _DEFAULT_ROOTS is not None else ImplRoots.for_engine()
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
def set_default_roots(roots: ImplRoots | None) -> ImplRoots | None:
|
| 181 |
+
"""๊ธฐ๋ณธ ๋ฃจํธ๋ฅผ ๋ฐ๊พธ๊ณ ์ด์ ๊ฐ์ ๋๋ ค์ค๋ค(๋ณต์์ฉ). None ์ด๋ฉด ์์ง ๊ธฐ๋ณธ์ผ๋ก ๋๋๋ฆฐ๋ค."""
|
| 182 |
+
global _DEFAULT_ROOTS
|
| 183 |
+
previous = _DEFAULT_ROOTS
|
| 184 |
+
_DEFAULT_ROOTS = roots
|
| 185 |
+
return previous
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
def is_excluded(module: ResolvedModule) -> bool:
|
| 189 |
+
"""์คํ
ยทํ๊ฐ๊ธฐ ํํฌ์์ ๋นผ๋๊ฐ โ ๋ฐํ์ ๋ชฉ๋กยท๊ณต์ฉ ๊ธฐ๋ฐยท๋ชจ๋ ํจํค์ง `__init__`."""
|
| 190 |
+
if module.path.name == "__init__.py":
|
| 191 |
+
return True
|
| 192 |
+
if module.name in _EXCLUDED_EXACT:
|
| 193 |
+
return True
|
| 194 |
+
return module.name.startswith(_EXCLUDED_PREFIXES)
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
def _package_of(module: ResolvedModule) -> str:
|
| 198 |
+
if module.path.name == "__init__.py":
|
| 199 |
+
return module.name
|
| 200 |
+
return module.name.rpartition(".")[0]
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
def _absolute_from(module: ResolvedModule, node: ast.ImportFrom) -> str | None:
|
| 204 |
+
"""`from ..x import y` ์ ๊ธฐ์ค ๋ชจ๋ ์ด๋ฆ. ์ต์์ ๋ฐ์ผ๋ก ๋๊ฐ๋ฉด None."""
|
| 205 |
+
if node.level == 0:
|
| 206 |
+
return node.module
|
| 207 |
+
package = _package_of(module).split(".") if _package_of(module) else []
|
| 208 |
+
keep = len(package) - (node.level - 1)
|
| 209 |
+
if keep < 0 or (keep == 0 and not node.module):
|
| 210 |
+
return None
|
| 211 |
+
base = package[:keep]
|
| 212 |
+
if node.module:
|
| 213 |
+
base += node.module.split(".")
|
| 214 |
+
return ".".join(base) if base else None
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
def imported_names(module: ResolvedModule, roots: ImplRoots) -> list[str]:
|
| 218 |
+
"""๋ชจ๋ ์์ค์ **๋ชจ๋ ** import(ํจ์ ์ยทTYPE_CHECKING ๋ธ๋ก ํฌํจ)๊ฐ ๊ฐ๋ฆฌํค๋ ๋ชจ๋ ์ด๋ฆ.
|
| 219 |
+
|
| 220 |
+
`from P import X` ๋ `P.X` ๊ฐ ๋ชจ๋์ด๋ฉด ๊ทธ ๋ชจ๋, ์๋๋ฉด `P`.
|
| 221 |
+
"""
|
| 222 |
+
tree = ast.parse(module.path.read_bytes(), filename=str(module.path))
|
| 223 |
+
names: list[str] = []
|
| 224 |
+
for node in ast.walk(tree):
|
| 225 |
+
if isinstance(node, ast.Import):
|
| 226 |
+
names.extend(alias.name for alias in node.names)
|
| 227 |
+
elif isinstance(node, ast.ImportFrom):
|
| 228 |
+
base = _absolute_from(module, node)
|
| 229 |
+
if base is None:
|
| 230 |
+
continue
|
| 231 |
+
for alias in node.names:
|
| 232 |
+
candidate = f"{base}.{alias.name}"
|
| 233 |
+
is_module = alias.name != "*" and roots.resolve(candidate) is not None
|
| 234 |
+
names.append(candidate if is_module else base)
|
| 235 |
+
return names
|
| 236 |
+
|
| 237 |
+
|
| 238 |
+
def import_closure(
|
| 239 |
+
start: Iterable[str], roots: ImplRoots, *, exclude: bool = True
|
| 240 |
+
) -> dict[str, ResolvedModule]:
|
| 241 |
+
"""์์ ๋ชจ๋๋ค์์ ๋ฃจํธ ์์ผ๋ก ๋ฟ๋ import ํํฌ. ์์ ๋ชจ๋์ ์ ์ธ ๊ท์น๊ณผ ๋ฌด๊ดํ๊ฒ ๋ค์ด๊ฐ๋ค.
|
| 242 |
+
|
| 243 |
+
Raises:
|
| 244 |
+
ValueError: ์์ ๋ชจ๋์ ๋ฃจํธ์์ ํ ์ ์์ ๋(๊ตฌํ์ ๋ณผ ์ ์๋ ์คํ
ยทํ๊ฐ๊ธฐ).
|
| 245 |
+
"""
|
| 246 |
+
closure: dict[str, ResolvedModule] = {}
|
| 247 |
+
queue: list[ResolvedModule] = []
|
| 248 |
+
for name in start:
|
| 249 |
+
resolved = roots.resolve(name)
|
| 250 |
+
if resolved is None:
|
| 251 |
+
raise ValueError(
|
| 252 |
+
f"module {name!r} is not under the implementation roots "
|
| 253 |
+
f"({roots.package_parent}, extra={list(roots.extra)}) โ register its directory"
|
| 254 |
+
)
|
| 255 |
+
queue.append(resolved)
|
| 256 |
+
while queue:
|
| 257 |
+
module = queue.pop()
|
| 258 |
+
if module.name in closure:
|
| 259 |
+
continue
|
| 260 |
+
closure[module.name] = module
|
| 261 |
+
for name in imported_names(module, roots):
|
| 262 |
+
found = roots.resolve(name)
|
| 263 |
+
if found is None or found.name in closure:
|
| 264 |
+
continue
|
| 265 |
+
if exclude and is_excluded(found):
|
| 266 |
+
continue
|
| 267 |
+
queue.append(found)
|
| 268 |
+
return closure
|
| 269 |
+
|
| 270 |
+
|
| 271 |
+
def impl_hash(modules: Iterable[ResolvedModule]) -> str:
|
| 272 |
+
"""`json_fingerprint({label: content_sha256(ํ์ผ ๋ฐ์ดํธ)})` โ ์ฃผ์ ๋ณ๊ฒฝ๋ ํด์๋ฅผ ๋ฐ๊พผ๋ค(์๋)."""
|
| 273 |
+
return json_fingerprint({m.label: content_sha256(m.path) for m in modules})
|
| 274 |
+
|
| 275 |
+
|
| 276 |
+
def closure_hash(start: Iterable[str], roots: ImplRoots) -> tuple[str, list[str]]:
|
| 277 |
+
"""(ํํฌ ํด์, ์ ๋ ฌ๋ ๋ผ๋ฒจ ๋ชฉ๋ก)."""
|
| 278 |
+
closure = import_closure(start, roots)
|
| 279 |
+
return impl_hash(closure.values()), sorted(m.label for m in closure.values())
|
| 280 |
+
|
| 281 |
+
|
| 282 |
+
def step_impl_hashes(
|
| 283 |
+
pipeline: Any, roots: ImplRoots | None = None
|
| 284 |
+
) -> tuple[dict[str, str], dict[str, list[str]]]:
|
| 285 |
+
"""์์ฑ๋ `Pipeline` ์ ์คํ
๋ง๋ค (ํค โ ํด์, ํค โ ํํฌ ๋ผ๋ฒจ). ํค๋ ๋ ์ง์คํธ๋ฆฌ ์กฐํ ์ด๋ฆ.
|
| 286 |
+
|
| 287 |
+
single_step ์ ์๋ฌต `llm_completion` ๋ ํ์ดํ๋ผ์ธ ๊ฐ์ฒด์ ์์ผ๋ฏ๋ก ๋ค์ด๊ฐ๋ค(๊ฒํ F10).
|
| 288 |
+
"""
|
| 289 |
+
from solar_eval.pipelines.registry import step_key
|
| 290 |
+
|
| 291 |
+
roots = roots or default_roots()
|
| 292 |
+
hashes: dict[str, str] = {}
|
| 293 |
+
modules: dict[str, list[str]] = {}
|
| 294 |
+
for step in pipeline.steps:
|
| 295 |
+
key = step_key(step.config)
|
| 296 |
+
if key in hashes:
|
| 297 |
+
continue
|
| 298 |
+
hashes[key], modules[key] = closure_hash([type(step).__module__], roots)
|
| 299 |
+
return dict(sorted(hashes.items())), dict(sorted(modules.items()))
|
| 300 |
+
|
| 301 |
+
|
| 302 |
+
def _evaluator_modules(evaluator: Any) -> list[str]:
|
| 303 |
+
names = [type(evaluator).__module__]
|
| 304 |
+
for child in evaluator.sub_evaluators():
|
| 305 |
+
names.extend(_evaluator_modules(child))
|
| 306 |
+
return names
|
| 307 |
+
|
| 308 |
+
|
| 309 |
+
def evaluator_impl_hash(evaluator: Any, roots: ImplRoots | None = None) -> tuple[str, list[str]]:
|
| 310 |
+
"""ํ๊ฐ๊ธฐ(ํฉ์ฑ์ด๋ฉด ํ์ ์ ๋ถ) ๋ชจ๋์์ ์์ํ ํํฌ์ (ํด์, ๋ผ๋ฒจ)."""
|
| 311 |
+
return closure_hash(_evaluator_modules(evaluator), roots or default_roots())
|
| 312 |
+
|
| 313 |
+
|
| 314 |
+
def provider_module(provider: Any) -> str:
|
| 315 |
+
"""ํ๋ก๋ฐ์ด๋(์ธ์คํด์ค๋ ํด๋์ค)๊ฐ ์ ์๋ ๋ชจ๋ ์ด๋ฆ."""
|
| 316 |
+
return provider.__module__ if isinstance(provider, type) else type(provider).__module__
|
| 317 |
+
|
| 318 |
+
|
| 319 |
+
def runtime_impl_hash(
|
| 320 |
+
providers: Sequence[Any], roots: ImplRoots | None = None
|
| 321 |
+
) -> tuple[str, list[str]]:
|
| 322 |
+
"""`RUNTIME_MODULES` + ํ๋ก๋ฐ์ด๋ ๋ชจ๋์ ํ์ผ ๋ฐ์ดํธ ์ง๋ฌธ (ํํฌ ์์).
|
| 323 |
+
|
| 324 |
+
ํ๋ก๋ฐ์ด๋๋ ์ธ์คํด์ค๋ ํด๋์ค๋ฅผ ๋ฐ๋๋ค. ๋ฃจํธ์์ ํ ์ ์๋ ํ๋ก๋ฐ์ด๋ ๋ชจ๋(ํ
์คํธ mock ๋ฑ)์
|
| 325 |
+
`unresolved:<๋ชจ๋>` ํค์ null ๋ก ๋ค์ด๊ฐ๋ค โ ๋ชจ๋ฅด๋ ์ฝ๋๋ฅผ ์กฐ์ฉํ ๋นผ์ง ์๋๋ค.
|
| 326 |
+
|
| 327 |
+
Raises:
|
| 328 |
+
ValueError: `RUNTIME_MODULES` ์ค ํ๋๋ฅผ ๋ฃจํธ์์ ํ ์ ์์ ๋.
|
| 329 |
+
"""
|
| 330 |
+
return runtime_impl_hash_for_modules([provider_module(p) for p in providers], roots)
|
| 331 |
+
|
| 332 |
+
|
| 333 |
+
def runtime_impl_hash_for_modules(
|
| 334 |
+
provider_modules: Iterable[str], roots: ImplRoots | None = None
|
| 335 |
+
) -> tuple[str, list[str]]:
|
| 336 |
+
"""`runtime_impl_hash` ์ ๋ชจ๋ ์ด๋ฆํ โ ์ฌ์ฑ์ (`with_evaluator`)์ด ๊ธฐ๋ก๋ ๋ชจ๋ ์ด๋ฆ์ผ๋ก ๋ค์ ๋ฌ๋ค."""
|
| 337 |
+
roots = roots or default_roots()
|
| 338 |
+
entries: dict[str, str | None] = {}
|
| 339 |
+
for name in RUNTIME_MODULES:
|
| 340 |
+
resolved = roots.resolve(name)
|
| 341 |
+
if resolved is None:
|
| 342 |
+
raise ValueError(f"runtime module {name!r} not found under {roots.package_parent}")
|
| 343 |
+
entries[resolved.label] = content_sha256(resolved.path)
|
| 344 |
+
for name in sorted(set(provider_modules)):
|
| 345 |
+
resolved = roots.resolve(name)
|
| 346 |
+
if resolved is None:
|
| 347 |
+
entries[UNRESOLVED_PREFIX + name] = None
|
| 348 |
+
else:
|
| 349 |
+
entries[resolved.label] = content_sha256(resolved.path)
|
| 350 |
+
return json_fingerprint(entries), sorted(entries)
|
| 351 |
+
|
| 352 |
+
|
| 353 |
+
def engine_tree_sha256(roots: ImplRoots | None = None) -> str:
|
| 354 |
+
"""์์ง ํจํค์ง ํธ๋ฆฌ(`solar_eval/**/*.py`, `__pycache__` ์ ์ธ)์ ํ์ผ ๋ฐ์ดํธ ์ง๋ฌธ โ provenance ์ฉ.
|
| 355 |
+
|
| 356 |
+
git ์์ด๋ ๊ณ์ฐ๋๋ค. ํค๋ ํจํค์ง ๋ถ๋ชจ ๊ธฐ์ค POSIX ๊ฒฝ๋ก(`solar_eval/core/runner.py`).
|
| 357 |
+
"""
|
| 358 |
+
parent = (roots or default_roots()).package_parent
|
| 359 |
+
package = Path(parent) / ENGINE_PACKAGE
|
| 360 |
+
entries = {
|
| 361 |
+
path.relative_to(parent).as_posix(): content_sha256(path)
|
| 362 |
+
for path in sorted(package.rglob("*.py"))
|
| 363 |
+
if "__pycache__" not in path.relative_to(parent).parts
|
| 364 |
+
}
|
| 365 |
+
return json_fingerprint(entries)
|
solar_eval/core/snapshot/reproducibility.py
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""run.json `snapshot.reproducibility` โ ๋ค์ ๋๋ฆด ์ ์๋๊ฐยท๊ฒฐ์ ์ ์ธ๊ฐ (๊ณํ 2026-09-24-phase1a D10ยทD17).
|
| 2 |
+
|
| 3 |
+
๋ ํ๋ ๋ชจ๋ run.json(๊ณผ ๋ฒ๋ค `config_snapshot.json` provenance)์๋ง ๋๋ค. ๋งค๋ํ์คํธ ๋
ธ์ถ์
|
| 4 |
+
`poc-bundle/v2` ๋ก ๋ฏธ๋ฃฌ๋ค(Q8).
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
from __future__ import annotations
|
| 8 |
+
|
| 9 |
+
import copy
|
| 10 |
+
from collections.abc import Iterable, Mapping, Sequence
|
| 11 |
+
from typing import Any
|
| 12 |
+
|
| 13 |
+
from solar_eval.pipelines.pipeline import ResourceRef
|
| 14 |
+
|
| 15 |
+
BLOCKER_ENGINE_DIRTY = "engine_dirty"
|
| 16 |
+
BLOCKER_CONFIG_OUTSIDE_REPO = "config_outside_repo"
|
| 17 |
+
BLOCKER_DATASET_NOT_LOCKED = "dataset_not_locked"
|
| 18 |
+
BLOCKER_DATASET_LOCK_MISMATCH = "dataset_lock_mismatch"
|
| 19 |
+
BLOCKER_DATASET_REMOTE = "dataset_remote_source"
|
| 20 |
+
BLOCKER_RESUMED_ENGINE_CHANGE = "resumed_with_engine_change"
|
| 21 |
+
BLOCKER_RESOURCE_UNRESOLVED = "resource_unresolved:"
|
| 22 |
+
BLOCKER_RESOURCE_OUTSIDE_REPO = "resource_outside_repo:"
|
| 23 |
+
|
| 24 |
+
_LOCAL_SOURCE = "local"
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def _dataset_blockers(dataset: Mapping[str, Any] | None) -> list[str]:
|
| 28 |
+
if dataset is None:
|
| 29 |
+
return [BLOCKER_DATASET_NOT_LOCKED]
|
| 30 |
+
if dataset.get("loader", {}).get("source") != _LOCAL_SOURCE:
|
| 31 |
+
return [BLOCKER_DATASET_REMOTE]
|
| 32 |
+
lock = dataset.get("lock")
|
| 33 |
+
if lock is None:
|
| 34 |
+
return [BLOCKER_DATASET_NOT_LOCKED]
|
| 35 |
+
if not lock.get("matched"):
|
| 36 |
+
return [BLOCKER_DATASET_LOCK_MISMATCH]
|
| 37 |
+
return []
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def _resource_blockers(refs: Mapping[str, ResourceRef]) -> list[str]:
|
| 41 |
+
blockers: list[str] = []
|
| 42 |
+
for label, ref in sorted(refs.items()):
|
| 43 |
+
if ref.path is None:
|
| 44 |
+
blockers.append(BLOCKER_RESOURCE_UNRESOLVED + label)
|
| 45 |
+
elif ref.kind != "config":
|
| 46 |
+
# data: ์์(PMI ๋ฑ)์ ๋ ํฌ ๋ฐ ๋ฐ์ดํฐ ํ์ด๊ณ lock ๋์๋ ์๋๋ค โ ์ปค๋ฐ์ผ๋ก ๋ณต์ํ ์ ์๋ค.
|
| 47 |
+
blockers.append(BLOCKER_RESOURCE_OUTSIDE_REPO + label)
|
| 48 |
+
return blockers
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def _verdict(blockers: Sequence[str], engine_dirty: bool | None) -> bool | None:
|
| 52 |
+
if blockers:
|
| 53 |
+
return False
|
| 54 |
+
return None if engine_dirty is None else True
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
def rerunnable(
|
| 58 |
+
*,
|
| 59 |
+
engine_dirty: bool | None,
|
| 60 |
+
repo_managed: bool,
|
| 61 |
+
dataset: Mapping[str, Any] | None,
|
| 62 |
+
resources: Mapping[str, ResourceRef],
|
| 63 |
+
) -> tuple[bool | None, list[str]]:
|
| 64 |
+
"""(rerunnable, ๋ง๋ ์ด์ ๋ชฉ๋ก). true = ๊ธฐ๋ก๋ ์ ๋ณด๋ง์ผ๋ก ๊ฐ์ ์
๋ ฅ์ ๋ค์ ๋ง๋ค ์ ์๋ค.
|
| 65 |
+
|
| 66 |
+
๋ง๋ ์ด์ ๊ฐ ํ๋๋ผ๋ ์์ผ๋ฉด false, ์๋๋ฐ `engine_dirty` ๊ฐ null(git ๋ถ๋ช
)์ด๋ฉด null.
|
| 67 |
+
"""
|
| 68 |
+
blockers: list[str] = []
|
| 69 |
+
if engine_dirty:
|
| 70 |
+
blockers.append(BLOCKER_ENGINE_DIRTY)
|
| 71 |
+
if not repo_managed:
|
| 72 |
+
blockers.append(BLOCKER_CONFIG_OUTSIDE_REPO)
|
| 73 |
+
blockers += _dataset_blockers(dataset)
|
| 74 |
+
blockers += _resource_blockers(resources)
|
| 75 |
+
return _verdict(blockers, engine_dirty), blockers
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
def _positive(value: Any) -> bool:
|
| 79 |
+
return isinstance(value, int | float) and not isinstance(value, bool) and value > 0
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
def _step_temperatures(step: Mapping[str, Any]) -> Iterable[Any]:
|
| 83 |
+
yield step.get("temperature")
|
| 84 |
+
temps = step.get("repeat_temperatures")
|
| 85 |
+
if isinstance(temps, Sequence) and not isinstance(temps, str | bytes):
|
| 86 |
+
yield from temps
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def deterministic(
|
| 90 |
+
*,
|
| 91 |
+
temperature: Any,
|
| 92 |
+
resolved_pipeline: Mapping[str, Any],
|
| 93 |
+
steps_use_model: bool,
|
| 94 |
+
judge_model: str | None,
|
| 95 |
+
) -> bool | None:
|
| 96 |
+
"""true = ๋ชจ๋ธ ํธ์ถ์ด ์ ํ ์๋ค ยท false = ์ํ๋ง ์จ๋ > 0 ์ด ํ์ธ๋จ ยท null = ์จ๋ 0 ์ LLM ํธ์ถ.
|
| 97 |
+
|
| 98 |
+
API ๋ ์จ๋ 0 ์์๋ ๊ฒฐ์ ์ ์ด๋ผ๋ ๋ณด์ฅ์ด ์์ผ๋ฏ๋ก ๊ทธ ๊ฒฝ์ฐ๋ ๋ชจ๋ฅธ๋ค(null, Q7).
|
| 99 |
+
"""
|
| 100 |
+
if not steps_use_model and judge_model is None:
|
| 101 |
+
return True
|
| 102 |
+
temps: list[Any] = [temperature]
|
| 103 |
+
for step in resolved_pipeline.get("steps") or []:
|
| 104 |
+
if isinstance(step, Mapping):
|
| 105 |
+
temps.extend(_step_temperatures(step))
|
| 106 |
+
if any(_positive(t) for t in temps):
|
| 107 |
+
return False
|
| 108 |
+
return None
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def _or3(values: Iterable[bool | None]) -> bool | None:
|
| 112 |
+
"""ํ๋๋ผ๋ true โ true, ์๋๊ณ ํ๋๋ผ๋ null โ null, ๋ชจ๋ false โ false."""
|
| 113 |
+
items = list(values)
|
| 114 |
+
if any(v is True for v in items):
|
| 115 |
+
return True
|
| 116 |
+
if any(v is None for v in items):
|
| 117 |
+
return None
|
| 118 |
+
return False
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
def merge_resume(snapshot: Mapping[str, Any], resume_engine: Mapping[str, Any], at: str) -> dict:
|
| 122 |
+
"""resume 1ํ๋ฅผ ์ค๋
์ท์ ๋ฐ์ํ **์** ์ค๋
์ท(์
๋ ฅ์ ๋ฐ๊พธ์ง ์๋๋ค, D17).
|
| 123 |
+
|
| 124 |
+
- `provenance.resumes` ์ `{at, engine: {commit, tree_sha256, dirty}}` ๋ฅผ ๋ง๋ถ์ธ๋ค.
|
| 125 |
+
- `reproducibility.engine_dirty` = ์์ยท๋ชจ๋ resume ์ dirty OR.
|
| 126 |
+
- ์์ง ํธ๋ฆฌ ํด์๊ฐ ์์๊ณผ ๋ค๋ฅด๋ฉด `resumed_with_engine_change` ๋ฅผ ๋ง๋ ์ด์ ์ ๋ํ๋ค.
|
| 127 |
+
configยท์ง๋ฌธ์ ๊ฑด๋๋ฆฌ์ง ์๋๋ค โ ์ค์ ๋๋ฆฌํํธ๋ ํธ์ถ์๊ฐ ์ง๋ฌธ ๋น๊ต๋ก ๋จผ์ ๋ง๋๋ค.
|
| 128 |
+
"""
|
| 129 |
+
merged = copy.deepcopy(dict(snapshot))
|
| 130 |
+
provenance = merged.setdefault("provenance", {})
|
| 131 |
+
start = provenance.get("engine") or {}
|
| 132 |
+
entry = {
|
| 133 |
+
"at": at,
|
| 134 |
+
"engine": {k: resume_engine.get(k) for k in ("commit", "tree_sha256", "dirty")},
|
| 135 |
+
}
|
| 136 |
+
resumes = [*provenance.get("resumes", []), entry]
|
| 137 |
+
provenance["resumes"] = resumes
|
| 138 |
+
repro = merged.setdefault("reproducibility", {})
|
| 139 |
+
dirty = _or3([start.get("dirty"), *(r["engine"]["dirty"] for r in resumes)])
|
| 140 |
+
blockers = list(repro.get("rerunnable_blockers", []))
|
| 141 |
+
if dirty and BLOCKER_ENGINE_DIRTY not in blockers:
|
| 142 |
+
blockers.insert(0, BLOCKER_ENGINE_DIRTY)
|
| 143 |
+
changed = any(r["engine"]["tree_sha256"] != start.get("tree_sha256") for r in resumes)
|
| 144 |
+
if changed and BLOCKER_RESUMED_ENGINE_CHANGE not in blockers:
|
| 145 |
+
blockers.append(BLOCKER_RESUMED_ENGINE_CHANGE)
|
| 146 |
+
repro["engine_dirty"] = dirty
|
| 147 |
+
repro["rerunnable_blockers"] = blockers
|
| 148 |
+
repro["rerunnable"] = _verdict(blockers, dirty)
|
| 149 |
+
return merged
|
solar_eval/core/snapshot/resources.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""์ค๋
์ท์ ํ์ผ ์ฌ๋ฃ โ ์คํ
์ด ์ฝ์ ์์(`resources`)๊ณผ ํ๋กฌํํธ ํ์ผ(`prompt_assets`).
|
| 2 |
+
|
| 3 |
+
๋ ๋ค **๋ด์ฉ**๋ง ๋ณธ๋ค: ๋๋ ํ ๋ฆฌยทํ์ผ ์ด๋ฆ์ ๊ฐ์ ๋ค์ด๊ฐ์ง ์์ผ๋ฏ๋ก(ํ๋กฌํํธ๋ ๋๋ ํ ๋ฆฌ ๊ธฐ์ค ์๋๊ฒฝ๋ก๊ฐ
|
| 4 |
+
ํค) ์ด๋ฆ๋ง ๋ฐ๊พผ ์ฌ๋ณธ์ ๊ฐ์ ๊ฐ์ด๋ค (๊ณํ 2026-09-24-phase1a D6).
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
from __future__ import annotations
|
| 8 |
+
|
| 9 |
+
from dataclasses import dataclass
|
| 10 |
+
from pathlib import Path
|
| 11 |
+
from typing import Any
|
| 12 |
+
|
| 13 |
+
from poc_bundle.canonical import content_sha256
|
| 14 |
+
|
| 15 |
+
from solar_eval.core.fingerprint import iter_prompt_asset_files
|
| 16 |
+
from solar_eval.pipelines.pipeline import ResourceRef
|
| 17 |
+
|
| 18 |
+
#: ๋จ์ผ YAML ํ๋กฌํํธ ํ์ผ์ `prompt_assets` ํค โ ํ์ผ ์ด๋ฆ์ด ์ง๋ฌธ์ ๋ค์ด๊ฐ์ง ์๊ฒ.
|
| 19 |
+
SINGLE_FILE_KEY = "@file"
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
@dataclass(frozen=True)
|
| 23 |
+
class CollectedResources:
|
| 24 |
+
"""`resources` ๊ฐ๊ณผ ๊ทธ ํด์ ๊ฒฐ๊ณผ.
|
| 25 |
+
|
| 26 |
+
Attributes:
|
| 27 |
+
hashes: `{๋ผ๋ฒจ: sha256_64 | None}`(๋ผ๋ฒจ ์ ๋ ฌ). None = ํด์ ๋ชป ํจ(`hf:` ์ ์ธ ๋ฑ).
|
| 28 |
+
refs: ๋ผ๋ฒจ โ ์๋ `ResourceRef`(์ฌํ ํ์ ์ด ์ข
๋ฅ๋ฅผ ๋ณธ๋ค).
|
| 29 |
+
"""
|
| 30 |
+
|
| 31 |
+
hashes: dict[str, str | None]
|
| 32 |
+
refs: dict[str, ResourceRef]
|
| 33 |
+
|
| 34 |
+
@property
|
| 35 |
+
def unresolved(self) -> list[str]:
|
| 36 |
+
"""๊ฐ์ด None ์ธ ๋ผ๋ฒจ โ ํ๋๋ผ๋ ์์ผ๋ฉด `snapshot.missing` ์ `resources` ๊ฐ ์ค๋ฅธ๋ค."""
|
| 37 |
+
return [label for label, value in self.hashes.items() if value is None]
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def collect_resources(pipeline: Any) -> CollectedResources:
|
| 41 |
+
"""`Pipeline` ์ธ์คํด์ค์ ์คํ
๋ง๋ค `resource_refs()` ๋ฅผ ๋ชจ์ ํ์ผ sha256 ์ ๋ฌ๋ค.
|
| 42 |
+
|
| 43 |
+
๊ฐ์ ๋ผ๋ฒจ(๊ฐ์ ์ฌ์ ์ ๋ ์คํ
์ด ์ฐ๋ prod ํ์ดํ๋ผ์ธ)์ ํ ๋ฒ๋ง ์ ๋๋ค. `hf:` ์์์ ๋คํธ์ํฌ๋ฅผ
|
| 44 |
+
ํ์ง ์๊ณ null ์ด๋ค. ์ ์ธํ๋๋ฐ ๋ก์ปฌ์ ์๋ ์์์ ์คํ
์ `resource_refs()` ๊ฐ
|
| 45 |
+
`KnowledgeAssetError` ๋ก ์ฃฝ์ธ๋ค โ run ์์ ์ ์.
|
| 46 |
+
"""
|
| 47 |
+
refs: dict[str, ResourceRef] = {}
|
| 48 |
+
for step in pipeline.steps:
|
| 49 |
+
for ref in step.resource_refs():
|
| 50 |
+
refs.setdefault(ref.label, ref)
|
| 51 |
+
hashes: dict[str, str | None] = {}
|
| 52 |
+
for label in sorted(refs):
|
| 53 |
+
path = refs[label].path
|
| 54 |
+
hashes[label] = content_sha256(path) if path is not None else None
|
| 55 |
+
return CollectedResources(hashes, {label: refs[label] for label in sorted(refs)})
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
def prompt_assets(prompt_path: Path) -> dict[str, str]:
|
| 59 |
+
"""ํ๋กฌํํธ์ `{์๋๊ฒฝ๋ก: sha256_64}` โ ๋๋ ํ ๋ฆฌ๋ฉด legacy `prompt_sha` ์ ๊ฐ์ ํ์ผ ์งํฉ.
|
| 60 |
+
|
| 61 |
+
๋จ์ผ ํ์ผ(YAML ํ๋กฌํํธ)์ `{"@file": sha256_64}`.
|
| 62 |
+
|
| 63 |
+
Raises:
|
| 64 |
+
FileNotFoundError: ๊ฒฝ๋ก๊ฐ ์์ ๋.
|
| 65 |
+
ValueError: ๋๋ ํ ๋ฆฌ์ ์ง๋ฌธ ๋์ ํ์ผ์ด ํ๋๋ ์์ ๋(๋น ํ๋กฌํํธ๋ก ๋ run ์ ๋ง๋๋ค).
|
| 66 |
+
"""
|
| 67 |
+
path = Path(prompt_path)
|
| 68 |
+
if path.is_dir():
|
| 69 |
+
assets = {
|
| 70 |
+
file.relative_to(path).as_posix(): content_sha256(file)
|
| 71 |
+
for file in iter_prompt_asset_files(path)
|
| 72 |
+
}
|
| 73 |
+
if not assets:
|
| 74 |
+
raise ValueError(f"prompt directory has no prompt asset files: {path}")
|
| 75 |
+
return assets
|
| 76 |
+
if path.is_file():
|
| 77 |
+
return {SINGLE_FILE_KEY: content_sha256(path)}
|
| 78 |
+
raise FileNotFoundError(f"prompt not found: {path}")
|
solar_eval/core/task_discovery.py
CHANGED
|
@@ -15,7 +15,7 @@
|
|
| 15 |
|
| 16 |
**๋ช
์ ํ์คํฌ๊ฐ ์ด๊ธด๋ค.** ์ด๋ฏธ `tasks:` ์ ์๋ dataset_path ๋ ๋ฐ๊ฒฌ ๋์์์
|
| 17 |
๋น ์ง๋ฏ๋ก, ๊ท์น์ ์ ๋ง๋ ์ด๋ฆ(`paragraph`, `yul-ryul`)์ ์์ ๊ทธ๋๋ก ์ ์ด ๋๋ฉด
|
| 18 |
-
๋๋ค. ๊ณผ๊ฑฐ run ์ด ๊ทธ ์ด๋ฆ์ผ๋ก ๊ธฐ๋ก๋ผ ์์ด ์ด๋ฆ์ ๋ฐ๊พธ๋ฉด
|
| 19 |
๋์ด์ง๊ธฐ ๋๋ฌธ์, ์ด ์ฐ์ ์์๋ ํํ ๋์์ด ์๋๋ค.
|
| 20 |
"""
|
| 21 |
|
|
|
|
| 15 |
|
| 16 |
**๋ช
์ ํ์คํฌ๊ฐ ์ด๊ธด๋ค.** ์ด๋ฏธ `tasks:` ์ ์๋ dataset_path ๋ ๋ฐ๊ฒฌ ๋์์์
|
| 17 |
๋น ์ง๋ฏ๋ก, ๊ท์น์ ์ ๋ง๋ ์ด๋ฆ(`paragraph`, `yul-ryul`)์ ์์ ๊ทธ๋๋ก ์ ์ด ๋๋ฉด
|
| 18 |
+
๋๋ค. ๊ณผ๊ฑฐ run ์ด ๊ทธ ์ด๋ฆ์ผ๋ก ๊ธฐ๋ก๋ผ ์์ด ์ด๋ฆ์ ๋ฐ๊พธ๋ฉด Experiment Hub ์์ ๊ณ๋ณด๊ฐ
|
| 19 |
๋์ด์ง๊ธฐ ๋๋ฌธ์, ์ด ์ฐ์ ์์๋ ํํ ๋์์ด ์๋๋ค.
|
| 20 |
"""
|
| 21 |
|
solar_eval/evaluators/base.py
CHANGED
|
@@ -19,15 +19,33 @@ class BaseEvaluator(ABC):
|
|
| 19 |
|
| 20 |
required_fields: frozenset[str] = frozenset()
|
| 21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
@abstractmethod
|
| 23 |
async def evaluate(
|
| 24 |
self,
|
| 25 |
sample: EvalSample,
|
| 26 |
provider: BaseProvider | None = None,
|
| 27 |
-
judge_model: str =
|
| 28 |
) -> dict[str, Any]:
|
| 29 |
"""`sample` ํ ๊ฑด์ ์ฑ์ ํ๋ค. `{score, details}` ๋ฅผ ๋ฐํํ๋ค.
|
| 30 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
์ฑ์ ์ ์คํจํ๋ฉด(judge ํธ์ถ ์์ธ, ์๋ชป๋ ์๋ต ๋ฑ) ๊ทธ ์คํจ๋ **์์ธ๋ก ์ ํํ๋ค** --
|
| 32 |
์ ์(0.0 ์ด๋ 1.0 ์ด๋ )๋ก ์นํํด ๋ฐํํ์ง ์๋๋ค. fail-open(์ฅ์ ๋ฅผ ๋ง์ ์ผ๋ก
|
| 33 |
๋๊ฐ์ํค๋ ๊ฒ)์ ๋ง๋ค์ง ์๋ ๊ฒ์ด ์ด ๊ณ์ฝ์ ํต์ฌ์ด๋ค. ์ด ์์ธ๋ฅผ ์ก์ ์คํจ
|
|
|
|
| 19 |
|
| 20 |
required_fields: frozenset[str] = frozenset()
|
| 21 |
|
| 22 |
+
@property
|
| 23 |
+
def judge_model(self) -> str | None:
|
| 24 |
+
"""์ฑ์ ์ ์ฐ๋ judge ๋ชจ๋ธ โ ๋ชจ๋ธ์ ๋ถ๋ฅด์ง ์๋ ํ๊ฐ๊ธฐ๋ None.
|
| 25 |
+
|
| 26 |
+
evaluation.json ์ `eval_model` ๊ณผ run.json ์ค๋
์ท์ `evaluator_config.judge_model`
|
| 27 |
+
์ด ์ด ๊ฐ์ ๊ทธ๋๋ก ์ด๋ค(๊ณํ 2026-09-24-phase1a D11). ์์ ์๋ ๋ฌ๋๊ฐ ํ๊ฐ๊ธฐ์
|
| 28 |
+
๋ฌด๊ดํ๊ฒ `"gpt-4o"` ๋ฅผ ์ ์ด lcs_diff run ์๋ judge ๊ฐ ์๋ ๊ฒ์ฒ๋ผ ๋ณด์๋ค.
|
| 29 |
+
"""
|
| 30 |
+
return None
|
| 31 |
+
|
| 32 |
+
def sub_evaluators(self) -> list["BaseEvaluator"]:
|
| 33 |
+
"""ํฉ์ฑ ํ๊ฐ๊ธฐ์ ํ์ ํ๊ฐ๊ธฐ โ ์ค๋
์ท์ ๊ตฌํ ํด์๊ฐ ํ์ ๋ชจ๋๊น์ง ๋ณด๋๋ก. ๊ธฐ๋ณธ []."""
|
| 34 |
+
return []
|
| 35 |
+
|
| 36 |
@abstractmethod
|
| 37 |
async def evaluate(
|
| 38 |
self,
|
| 39 |
sample: EvalSample,
|
| 40 |
provider: BaseProvider | None = None,
|
| 41 |
+
judge_model: str | None = None,
|
| 42 |
) -> dict[str, Any]:
|
| 43 |
"""`sample` ํ ๊ฑด์ ์ฑ์ ํ๋ค. `{score, details}` ๋ฅผ ๋ฐํํ๋ค.
|
| 44 |
|
| 45 |
+
`judge_model` ์ ํธ์ถ์๊ฐ judge ๋ชจ๋ธ์ **๋ฎ์ด์ธ** ๋๋ง ์ค๋ค. None ์ด๋ฉด ํ๊ฐ๊ธฐ๊ฐ
|
| 46 |
+
์๊ธฐ ์ค์ ๊ฐ(`self.judge_model`)์ ์ด๋ค โ ๋ฌ๋ยท`runs eval` ์ ๋๊ธฐ์ง ์์ผ๋ฏ๋ก ๋ณด๊ณ ๊ฐ๊ณผ
|
| 47 |
+
์ค์ ํธ์ถ ๋ชจ๋ธ์ด ์ด๊ธ๋์ง ์๋๋ค.
|
| 48 |
+
|
| 49 |
์ฑ์ ์ ์คํจํ๋ฉด(judge ํธ์ถ ์์ธ, ์๋ชป๋ ์๋ต ๋ฑ) ๊ทธ ์คํจ๋ **์์ธ๋ก ์ ํํ๋ค** --
|
| 50 |
์ ์(0.0 ์ด๋ 1.0 ์ด๋ )๋ก ์นํํด ๋ฐํํ์ง ์๋๋ค. fail-open(์ฅ์ ๋ฅผ ๋ง์ ์ผ๋ก
|
| 51 |
๋๊ฐ์ํค๋ ๊ฒ)์ ๋ง๋ค์ง ์๋ ๊ฒ์ด ์ด ๊ณ์ฝ์ ํต์ฌ์ด๋ค. ์ด ์์ธ๋ฅผ ์ก์ ์คํจ
|