File size: 5,868 Bytes
23866a2
 
 
 
 
 
 
 
 
 
 
8b27dd6
23866a2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8b27dd6
23866a2
 
 
 
 
 
8b27dd6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23866a2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8b27dd6
 
 
23866a2
 
 
 
 
 
 
 
8b27dd6
 
23866a2
 
 
 
8b27dd6
23866a2
 
 
 
 
 
 
 
8b27dd6
 
23866a2
 
 
 
 
 
 
 
 
 
 
 
 
 
8b27dd6
 
 
 
 
 
 
 
 
 
23866a2
 
 
 
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#!/usr/bin/env python3
"""Build eval.json + products.json for the HERB viewer.

HERB ships one JSON per product, each bundling heterogeneous *artifacts*
(slack / documents / meeting_transcripts / meeting_chats / urls / prs) plus
answerable + unanswerable questions. The viewer streams the (heavy) artifact
content on demand straight from the Salesforce/HERB dataset on the HF CDN, so
this script only derives the small bundled files:

- eval.json      one entry per question (815 answerable + 699 unanswerable);
                 product, kind, question, ground_truth, citations (evidence
                 artifact ids), type, and computation/comparison label.
- products.json  one entry per product with per-type artifact counts and
                 question counts (drives the Corpus/Eval product filters).

employees.json / customers.json (id -> name maps) are copied verbatim from the
dataset's metadata/ folder and bundled for client-side id resolution.

Run from the viewer repo root:
    python scripts/build_data.py [--herb-dir DIR]

Reads:  <herb-dir>/products/*.json
Writes: eval.json, products.json
"""
import argparse
import glob
import json
import os
import re

ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
DEFAULT_HERB = "/mnt/ramdisk/blobstore/timchen0618/data/HERB"
ARTIFACT_TYPES = ["slack", "documents", "meeting_transcripts",
                  "meeting_chats", "urls", "prs"]

COMPUTATION_COMPARISON_PATTERNS = [
    (
        "maximum_or_minimum",
        re.compile(
            r"\b(?:maximum|minimum|highest|lowest|largest|smallest|"
            r"most|least|fewest|greatest)\b",
            re.IGNORECASE,
        ),
    ),
    (
        "numeric_comparison",
        re.compile(
            r"\b(?:compare|comparison|versus|vs\.?|difference between|"
            r"more than|less than|greater than|fewer than|higher than|"
            r"lower than)\b",
            re.IGNORECASE,
        ),
    ),
    (
        "arithmetic",
        re.compile(
            r"\b(?:sum|total|average|mean|percentage|percent|ratio|"
            r"how many|combined|altogether|remaining)\b",
            re.IGNORECASE,
        ),
    ),
]


def computation_comparison(question):
    """Classify questions using the benchmark split requested for analysis."""
    for reason, pattern in COMPUTATION_COMPARISON_PATTERNS:
        if pattern.search(question or ""):
            return True, reason
    return False, "none"


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--herb-dir", default=DEFAULT_HERB)
    args = ap.parse_args()

    prod_files = sorted(glob.glob(os.path.join(args.herb_dir, "products", "*.json")))
    if not prod_files:
        raise SystemExit(f"no product json under {args.herb_dir}/products/")

    eval_rows = []
    products = []
    for pf in prod_files:
        name = os.path.basename(pf)[:-5]
        d = json.load(open(pf, encoding="utf-8"))

        counts = {t: len(d.get(t, []) or []) for t in ARTIFACT_TYPES}
        aq = d.get("answerable_questions", []) or []
        uq = d.get("unanswerable_questions", []) or []
        products.append({
            "name": name,
            "counts": counts,
            "n_artifacts": sum(counts.values()),
            "n_answerable": len(aq),
            "n_unanswerable": len(uq),
        })

        for i, q in enumerate(aq):
            is_computation_comparison, category_reason = computation_comparison(
                q.get("question", "")
            )
            eval_rows.append({
                "gid": f"{name}#a{i}",
                "product": name,
                "kind": "answerable",
                "question": q.get("question", ""),
                "ground_truth": q.get("ground_truth"),
                "citations": q.get("citations", []) or [],
                "type": q.get("type", ""),
                "computation_comparison": is_computation_comparison,
                "computation_comparison_reason": category_reason,
            })
        for i, q in enumerate(uq):
            # unanswerable questions are plain strings
            qtext = q if isinstance(q, str) else (q.get("question", "") if isinstance(q, dict) else str(q))
            is_computation_comparison, category_reason = computation_comparison(qtext)
            eval_rows.append({
                "gid": f"{name}#u{i}",
                "product": name,
                "kind": "unanswerable",
                "question": qtext,
                "ground_truth": None,
                "citations": [],
                "type": "unanswerable",
                "computation_comparison": is_computation_comparison,
                "computation_comparison_reason": category_reason,
            })

    with open(os.path.join(ROOT, "eval.json"), "w", encoding="utf-8") as f:
        json.dump(eval_rows, f, ensure_ascii=False, indent=0)
    with open(os.path.join(ROOT, "products.json"), "w", encoding="utf-8") as f:
        json.dump(products, f, ensure_ascii=False, indent=2)

    tot_art = sum(p["n_artifacts"] for p in products)
    n_ans = sum(p["n_answerable"] for p in products)
    n_unans = sum(p["n_unanswerable"] for p in products)
    print(f"wrote eval.json: {len(eval_rows)} questions ({n_ans} answerable, {n_unans} unanswerable)")
    print(f"wrote products.json: {len(products)} products, {tot_art} artifacts total")
    from collections import Counter
    print("answerable types:", dict(Counter(r["type"] for r in eval_rows if r["kind"] == "answerable")))
    for kind in ("answerable", "unanswerable"):
        bracket_counts = Counter(
            row["computation_comparison"]
            for row in eval_rows
            if row["kind"] == kind
        )
        print(
            f"{kind} computation/comparison: "
            f"{bracket_counts[True]} yes, {bracket_counts[False]} no"
        )


if __name__ == "__main__":
    main()