feat(token-tracking): accumulate token usage across session_async calls

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
wangwei
2026-07-02 15:09:30 +08:00
co-authored by Copilot
parent 6a2bbf8239
commit 8245c7f9c3
2 changed files with 119 additions and 14 deletions
+35 -14
View File
@@ -193,6 +193,7 @@ class SessionScoreJobManager:
# Lazy imports — keep web server bootable if ragas is not installed.
from rag_eval.advisor import run_advisor
from rag_eval.metrics.token_tracker import track_token_usage
from rag_eval.metrics.weights import compute_weighted_score
from rag_eval.reporting.writers import write_run_artifacts
from rag_eval.settings import EvaluationSettings
@@ -215,20 +216,21 @@ class SessionScoreJobManager:
try:
# --- Scoring (can run concurrently for the same session) ----------
if effective:
raw_scores = inline_scorer.score(
question=request.question,
answer=request.answer,
contexts=request.contexts_as_list(),
ground_truth=request.ground_truth,
metrics=effective,
judge_model=judge_model,
embedding_model=embedding_model,
settings=settings,
judge_language=judge_language,
)
else:
raw_scores = {}
with track_token_usage() as usage_tracker:
if effective:
raw_scores = inline_scorer.score(
question=request.question,
answer=request.answer,
contexts=request.contexts_as_list(),
ground_truth=request.ground_truth,
metrics=effective,
judge_model=judge_model,
embedding_model=embedding_model,
settings=settings,
judge_language=judge_language,
)
else:
raw_scores = {}
latency_ms = int((time.monotonic() - t0) * 1000)
finished_at = utc_now_iso()
@@ -250,6 +252,14 @@ class SessionScoreJobManager:
run_dir = self._output_dir / run_id
run_dir.mkdir(parents=True, exist_ok=True)
# Merge this call's token usage into the session's running total, so
# repeated calls accumulate instead of overwriting (mirrors the
# scores.csv append-only accumulation below).
existing_metadata = self._read_metadata(run_dir)
merged_token_usage = usage_tracker.merge_into(
existing_metadata.get("token_usage", {})
)
# Read all existing rows, then append the new one
existing_rows = self._read_score_rows(run_dir)
call_number = len(existing_rows) + 1
@@ -312,6 +322,7 @@ class SessionScoreJobManager:
valid_samples=valid_samples,
invalid_samples=[],
score_rows=all_rows,
token_usage=merged_token_usage,
)
write_run_artifacts(result)
@@ -376,6 +387,16 @@ class SessionScoreJobManager:
except (OSError, ValueError):
return []
def _read_metadata(self, run_dir: Path) -> dict[str, Any]:
"""Read this session's existing metadata.json, returning {} if absent/unreadable."""
metadata_path = run_dir / "metadata.json"
if not metadata_path.is_file():
return {}
try:
return json.loads(metadata_path.read_text(encoding="utf-8"))
except (OSError, ValueError):
return {}
def _read_metric_means(self, run_dir: Path) -> dict[str, float | None]:
"""Compute per-metric means from the session's scores.csv."""
scores_path = run_dir / "scores.csv"