File size: 2,752 Bytes
9c84f9d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
from typing import Any

from solar_eval.evaluators.base import BaseEvaluator
from solar_eval.evaluators.scoring import compute_proofread_metrics
from solar_eval.models.sample import EvalSample
from solar_eval.providers.base import BaseProvider


class LCSDiffEvaluator(BaseEvaluator):
    """Token-level LCS diff evaluator for proofreading.

    Computes precision and recall by comparing golden corrections vs predicted corrections.
    3-way ๋น„๊ต๋‹ค -- "์ •๋‹ต์ด ์›๋ฌธ์—์„œ ๋ฌด์—‡์„ ๊ณ ์ณค๋‚˜" ๋Œ€ "๋ชจ๋ธ์ด ์›๋ฌธ์—์„œ ๋ฌด์—‡์„ ๊ณ ์ณค๋‚˜"๋ฅผ
    ๋น„๊ตํ•˜๋Š” ๊ตฌ์กฐ๋ผ ์›๋ฌธ(`sample.input`)์ด ์—†์œผ๋ฉด ์„ฑ๋ฆฝํ•˜์ง€ ์•Š๋Š”๋‹ค
    (๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ณ„ํš ยง3).
    """

    required_fields = frozenset({"input", "output", "reference"})

    def __init__(self, metrics: list[str] | None = None) -> None:
        self.metrics = metrics or ["precision", "recall", "tp", "fp", "missing", "redundant"]

    async def evaluate(
        self,
        sample: EvalSample,
        provider: BaseProvider | None = None,
        judge_model: str = "gpt-4o",
    ) -> dict[str, Any]:
        original = sample.input.get("original", "")
        result = compute_proofread_metrics(original, str(sample.reference), sample.output)
        return {
            "score": result["f1"],  # Use F1 as primary score
            "details": result,
        }

    def aggregate(self, results: list[dict[str, Any]]) -> dict[str, Any]:
        if not results:
            return {"overall_score": 0.0, "scores": {}}

        total_tp = sum(r["details"]["tp"] for r in results)
        total_fp = sum(r["details"]["fp"] for r in results)
        total_missing = sum(r["details"]["missing"] for r in results)
        total_redundant = sum(r["details"]["redundant"] for r in results)

        no_golden = (total_tp + total_fp + total_missing) == 0
        no_pred = (total_tp + total_fp + total_redundant) == 0

        if no_golden and no_pred:
            precision, recall = 100.0, 100.0
        else:
            precision = (
                total_tp / (total_tp + total_fp + total_redundant) * 100 if not no_pred else 0.0
            )
            recall = (
                total_tp / (total_tp + total_fp + total_missing) * 100 if not no_golden else 0.0
            )
        f1 = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0.0

        return {
            "overall_score": f1,
            "scores": {
                "f1": f1,
                "precision": precision,
                "recall": recall,
                "tp": total_tp,
                "fp": total_fp,
                "missing": total_missing,
                "redundant": total_redundant,
            },
            "num_samples": len(results),
        }