runner: 진행 상황 출력 — 프로그램별 호출 건수와 호출 하나 끝날 때마다 [i/n]·경과·예상 남은 시간 (stderr)
고객사 실측: unit 100+ 인 프로그램은 15분 넘게 아무것도 안 찍혀 멈춘 것으로 보였다. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
c577d5cb93
commit
ce459ba8f0
+18
-3
@@ -17,6 +17,7 @@ import argparse
|
|||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
import sys
|
||||||
import time
|
import time
|
||||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
@@ -428,18 +429,29 @@ def _llm_call(llm, system: str, prompt: str) -> dict:
|
|||||||
return {"error": e}
|
return {"error": e}
|
||||||
|
|
||||||
|
|
||||||
|
def _progress(i: int, n: int, t0: float, c: dict) -> None:
|
||||||
|
"""호출 하나가 끝날 때마다 한 줄 — 긴 프로그램에서 멈춘 것처럼 보이지 않게 (stderr)."""
|
||||||
|
state = "실패" if "error" in c else ("대기" if c.get("pending") else "ok")
|
||||||
|
elapsed = time.time() - t0
|
||||||
|
eta = elapsed / i * (n - i) if i else 0
|
||||||
|
print(f" [{i}/{n}] {state} {elapsed:.0f}s 경과, 약 {eta:.0f}s 남음", file=sys.stderr, flush=True)
|
||||||
|
|
||||||
|
|
||||||
def _run_calls_parallel(llm, calls: list[dict], concurrency: int) -> None:
|
def _run_calls_parallel(llm, calls: list[dict], concurrency: int) -> None:
|
||||||
"""calls 각 원소의 'prompt' 를 호출하고 결과를 그 자리에 채운다 (in-place)."""
|
"""calls 각 원소의 'prompt' 를 호출하고 결과를 그 자리에 채운다 (in-place)."""
|
||||||
if not calls:
|
if not calls:
|
||||||
return
|
return
|
||||||
|
t0 = time.time()
|
||||||
if concurrency <= 1 or len(calls) == 1:
|
if concurrency <= 1 or len(calls) == 1:
|
||||||
for c in calls:
|
for i, c in enumerate(calls, 1):
|
||||||
c.update(_llm_call(llm, SYSTEM_PROMPT, c["prompt"]))
|
c.update(_llm_call(llm, SYSTEM_PROMPT, c["prompt"]))
|
||||||
|
_progress(i, len(calls), t0, c)
|
||||||
return
|
return
|
||||||
with ThreadPoolExecutor(max_workers=min(concurrency, len(calls))) as ex:
|
with ThreadPoolExecutor(max_workers=min(concurrency, len(calls))) as ex:
|
||||||
futures = {ex.submit(_llm_call, llm, SYSTEM_PROMPT, c["prompt"]): c for c in calls}
|
futures = {ex.submit(_llm_call, llm, SYSTEM_PROMPT, c["prompt"]): c for c in calls}
|
||||||
for fut in as_completed(futures):
|
for i, fut in enumerate(as_completed(futures), 1):
|
||||||
futures[fut].update(fut.result())
|
futures[fut].update(fut.result())
|
||||||
|
_progress(i, len(futures), t0, futures[fut])
|
||||||
|
|
||||||
|
|
||||||
def _dedupe_plan(con: sqlite3.Connection, pending: list[sqlite3.Row]) -> tuple[list[sqlite3.Row], dict]:
|
def _dedupe_plan(con: sqlite3.Connection, pending: list[sqlite3.Row]) -> tuple[list[sqlite3.Row], dict]:
|
||||||
@@ -569,7 +581,10 @@ def summarize_units(program: str | None, limit: int | None, fake: bool = False,
|
|||||||
jobs.append((u, [dict(x) for x in prompts]))
|
jobs.append((u, [dict(x) for x in prompts]))
|
||||||
|
|
||||||
# 2) LLM 호출 (병렬, DB 접근 없음) — 수정사항 10번
|
# 2) LLM 호출 (병렬, DB 접근 없음) — 수정사항 10번
|
||||||
_run_calls_parallel(llm, [c for _, calls in jobs for c in calls], conc)
|
all_calls = [c for _, calls in jobs for c in calls]
|
||||||
|
print(f"[{p['name']}] unit {len(units)}건 → LLM 호출 {len(all_calls)}건 (동시 {conc})",
|
||||||
|
file=sys.stderr, flush=True)
|
||||||
|
_run_calls_parallel(llm, all_calls, conc)
|
||||||
|
|
||||||
# 3) 검증·저장 (DB 쓰기, 메인 스레드)
|
# 3) 검증·저장 (DB 쓰기, 메인 스레드)
|
||||||
for u, calls in jobs:
|
for u, calls in jobs:
|
||||||
|
|||||||
Reference in New Issue
Block a user