File size: 14,867 Bytes
3c856c0
 
 
 
30c3967
 
 
5588471
30c3967
 
 
 
 
 
 
 
3c856c0
2c95c7e
3c856c0
30c3967
 
 
2c95c7e
3047218
2c95c7e
3047218
30c3967
6e404e3
30c3967
3c856c0
 
30c3967
2c95c7e
 
3c856c0
30c3967
e55b0a5
30c3967
e55b0a5
30c3967
 
 
d56bc01
 
30c3967
 
d56bc01
 
 
 
 
 
 
 
 
 
30c3967
3c856c0
30c3967
 
d56bc01
30c3967
 
 
 
 
 
5e9e543
30c3967
 
 
 
 
 
 
 
 
 
 
d56bc01
 
30c3967
d56bc01
 
 
30c3967
 
 
 
 
 
 
 
 
d56bc01
 
 
 
30c3967
 
d56bc01
 
 
 
 
30c3967
 
d56bc01
 
 
 
 
30c3967
d56bc01
 
 
30c3967
 
 
 
 
 
d56bc01
30c3967
 
 
 
 
 
d56bc01
 
 
 
30c3967
d56bc01
 
 
 
 
30c3967
 
d56bc01
30c3967
 
 
d56bc01
 
 
 
 
 
 
30c3967
 
d56bc01
30c3967
 
 
 
 
 
 
 
5588471
 
 
 
 
 
30c3967
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
feaf833
30c3967
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
490d0b1
04cf55a
 
 
30c3967
 
 
 
 
 
 
 
 
 
 
 
 
 
490d0b1
 
30c3967
7357a15
30c3967
 
 
 
feaf833
30c3967
 
 
 
 
 
 
 
 
feaf833
30c3967
 
 
7357a15
30c3967
 
 
 
 
 
 
 
3c856c0
 
73b542b
 
 
d56bc01
15e1734
d56bc01
73b542b
 
3c856c0
 
30c3967
73b542b
 
 
 
 
d56bc01
 
73b542b
 
 
3c856c0
 
 
 
 
 
2c95c7e
3c856c0
 
2c95c7e
3c856c0
 
2c95c7e
3c856c0
30c3967
3c856c0
 
30c3967
 
 
 
 
 
 
 
73b542b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3c856c0
30c3967
 
 
27c9b8f
3c856c0
 
7357a15
2c95c7e
7357a15
04cf55a
3c856c0
 
5588471
 
 
3c856c0
 
 
 
04cf55a
5588471
3c856c0
 
 
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
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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
import gradio as gr
import os
import json
import pandas as pd
from huggingface_hub import HfApi, hf_hub_download
from datasets import load_dataset
import requests
import datetime

TOKEN = os.environ.get("HF_TOKEN")
OWNER = os.environ.get("OWNER")
RESULTS_COMMUNITY = f"{OWNER}/benchmark_results"
api = HfApi()

URL = os.environ.get("URL")


def load_data(source, refresh=False):
    if source == "core":
        with open("data.json", "r") as f:
            data = json.load(f)
    else:
        if refresh:
            ds = load_dataset(RESULTS_COMMUNITY, download_mode="force_redownload")
        else:
            ds = load_dataset(RESULTS_COMMUNITY)
        data = []
        for entry in ds['train']:
            data.append(entry)
    return data


def build_table(source, refresh=False):
    data = load_data(source, refresh)

    if source == "core":
        headers = ["Benchmark", "Category", "Pile-train Dirty (%)", "DCLM-baseline Dirty (%)", "CC-2025-05 Dirty (%)", "CC-2025-08 Dirty (%)"]
    else:
        headers = ["Benchmark", "Contributor", "Pile-train Dirty (%)", "DCLM-baseline Dirty (%)", "CC-2025-05 Dirty (%)", "CC-2025-08 Dirty (%)"]

    html = """
    <table id="benchmarkTable" style="border-collapse: collapse; width: 100%;">
    <thead>
    <tr>
    """
    for col in headers:
        html += f'''
        <th onclick="sortTable(this)" style="cursor: pointer; border: 1px solid #ddd; padding: 8px; text-align: right;">
            {col}
            <span class="tri-container">
                <span class="triangle-up"></span>
                <span class="triangle-down"></span>
            </span>
        </th>
        '''
    html += "</tr></thead><tbody>"

    for entry in data:
        name = entry.get("Benchmark", "")
        url = entry.get("URL", "#")
        hyperlink = f'<a href="{url}" target="_blank">{name}</a>' if url else name

        row = {
            "Benchmark": hyperlink,
            "Pile-train Dirty (%)": entry.get("Pile Dirty", -1),
            "DCLM-baseline Dirty (%)": entry.get("DCLM Dirty", -1),
            "CC-2025-05 Dirty (%)": entry.get("CC202505 Dirty", -1),
            "CC-2025-08 Dirty (%)": entry.get("CC202508 Dirty", -1)
        }

        if source == "core":
            row["Category"] = entry.get("Category", "")
        elif source == "community":
            row["Contributor"] = entry.get("Contributor", "")

        html += "<tr>"
        for col in headers:
            val = row.get(col, "")
            if isinstance(val, float) and val >= 0:
                val_display = f"{val:5.1f}"
                html += f'<td style="border: 1px solid #ddd; padding: 8px; text-align: right;">{val_display}</td>'
            elif isinstance(val, float):
                html += f'<td style="border: 1px solid #ddd; padding: 8px; text-align: right;">N/A</td>'
            else:
                html += f'<td style="border: 1px solid #ddd; padding: 8px; text-align: right;">{val}</td>'
        html += "</tr>\n"

    html += "</tbody></table>"

    html += """
    <script>
        let sortDirection = {};

        function sortTable(header) {
            const table = document.getElementById("benchmarkTable");
            const rows = Array.from(table.tBodies[0].rows);
            const columnIndex = Array.from(header.parentNode.children).indexOf(header);
            const isAscending = sortDirection[columnIndex] === 'ascending';
            sortDirection[columnIndex] = isAscending ? 'descending' : 'ascending';

            Array.from(header.parentNode.children).forEach(th => {
                const up = th.querySelector('.triangle-up');
                const down = th.querySelector('.triangle-down');
                if (up) up.classList.remove('active');
                if (down) down.classList.remove('active');
            });

            if (sortDirection[columnIndex] === 'ascending') {
                header.querySelector('.triangle-up').classList.add('active');
            } else {
                header.querySelector('.triangle-down').classList.add('active');
            }

            rows.sort((rowA, rowB) => {
                const cellA = rowA.cells[columnIndex].innerText;
                const cellB = rowB.cells[columnIndex].innerText;
                if (isNaN(cellA)) {
                    return isAscending ? cellA.localeCompare(cellB) : cellB.localeCompare(cellA);
                }
                return isAscending ? parseFloat(cellA) - parseFloat(cellB) : parseFloat(cellB) - parseFloat(cellA);
            });

            rows.forEach(row => table.tBodies[0].appendChild(row));
        }
    </script>
    """

    html += """
    <style>
        thead tr {
            background-color: #f0f0f0;
        }
        .tri-container {
            display: inline-block;
            margin-left: 4px;
            vertical-align: middle;
        }
        .triangle-up, .triangle-down {
            display: block;
            width: 0;
            height: 0;
            margin: 1px auto;
            border-left: 5px solid transparent;
            border-right: 5px solid transparent;
        }
        .triangle-up {
            border-bottom: 5px solid #999;
        }
        .triangle-down {
            border-top: 5px solid #999;
        }
        .triangle-up.active {
            border-bottom: 5px solid #000;
        }
        .triangle-down.active {
            border-top: 5px solid #000;
        }
    </style>
    """

    return html


def record_submission(benchmark_name, contributor, jsonl_file, hf_path, hf_split, field_name, hf_config, profile: gr.OAuthProfile):
    user_data = requests.get(f"https://huggingface.co/api/users/{profile.username}/overview")
    creation_date = json.loads(user_data.content)["createdAt"]
    if datetime.datetime.now() - datetime.datetime.strptime(creation_date, '%Y-%m-%dT%H:%M:%S.%fZ') < datetime.timedelta(days=10):
        return format_error("This account is not authorized to submit.")
    
    if not benchmark_name or not benchmark_name.strip():
        return "❌ Please provide a benchmark name."
    
    if not field_name or not field_name.strip():
        return "❌ Please provide a field name."
    
    has_jsonl = jsonl_file is not None
    has_hf = hf_path and hf_path.strip()
    
    if not has_jsonl and not has_hf:
        return "❌ Please provide either a .jsonl file or a HuggingFace dataset path."
    
    if has_jsonl:
        try:
            with open(jsonl_file.name, 'r', encoding='utf-8') as f:
                line_count = 0
                for line in f:
                    line_count += 1
                    if line_count > 5:
                        break
                    
                    try:
                        entry = json.loads(line.strip())
                        if field_name.strip() not in entry:
                            available_fields = list(entry.keys())
                            return f"❌ Field '{field_name.strip()}' not found in JSONL file. Available fields: {', '.join(available_fields)}"
                    except json.JSONDecodeError as e:
                        return f"❌ Invalid JSON format in line {line_count}: {str(e)}"
                
                if line_count == 0:
                    return "❌ The uploaded file is empty."
                    
        except Exception as e:
            return f"❌ Error reading file: {str(e)}"
    elif has_hf:
        if not hf_split or not hf_split.strip():
            return "❌ Please provide a dataset split for the HuggingFace dataset."
        
        try:
            if hf_config:
                dataset_info = load_dataset(hf_path.strip(), hf_config.strip(), split=hf_split.strip(), streaming=True, trust_remote_code=True)
            else:
                dataset_info = load_dataset(hf_path.strip(), split=hf_split.strip(), streaming=True, trust_remote_code=True)
            first_item = next(iter(dataset_info))
            if field_name.strip() not in first_item:
                available_fields = list(first_item.keys())
                return f"❌ Field '{field_name.strip()}' not found in dataset. Available fields: {', '.join(available_fields)}"
        except Exception as e:
            return f"❌ Could not access HuggingFace dataset: {str(e)}"
    
    try:
        data = {
            'name': benchmark_name.strip(),
            'contributor': contributor.strip(),
            'type': 'jsonl' if has_jsonl else 'hf',
            'split': hf_split.strip() if has_hf else '',
            'field_name': field_name.strip(),
            'hf_path': hf_path.strip() if has_hf else '',
            'hf_config': hf_config.strip() if has_hf else ''
        }
        print(json.dumps(data))
        files = {}
        if has_jsonl:
            files['file'] = (benchmark_name.strip() + '.jsonl', open(jsonl_file.name, 'rb'), 'application/json')
        
        response = requests.post(f"{URL}/", data={"payload": json.dumps(data)}, files=files, timeout=30)
        
        if files:
            files['file'][1].close()
        
        if response.status_code == 200:
            result = response.json()
            if result.get("status") == "success":
                message = result.get('message', 'Submission successful!')
                
                full_message = f"{message}"
                
                return full_message
            elif result.get("status") == "info":
                return f"❌ {result.get('message', 'Submission already exists')}"
            else:
                return f"❌ {result.get('message', 'Unknown error occurred')}"
        else:
            return f"❌ Server error: {response.status_code} - {response.text}"
            
    except Exception as e:
        return f"❌ Error submitting benchmark: {str(e)}"


with gr.Blocks() as interface:
    gr.HTML(
            '''<h1 text-align="center">πŸ“– Benchmark Contamination Monitoring System</h1>

            <p style='font-size: 16px;'>This system monitors potential contamination in benchmark datasets used for evaluating language models across various open-source corpora 🧐.</p>
            <p style='font-size: 16px;'>The system is released along with our paper <a href="https://arxiv.org/abs/2506.12229">Infini-gram mini: Exact n-gram Search at the Internet Scale with FM-Index</a>, which documents the methodology and findings in detail.</p>
            <p style='font-size: 16px;'>We welcome the community to submit new benchmarks for contamination analysis using the <b>"Add New Benchmarks"</b> tab.</p>
            '''
        )

    with gr.Tabs():
        with gr.Tab(label="Bulletin"):
            gr.Markdown("## Benchmark Contamination Bulletin")
            with gr.Accordion(label='Click to view instructions', open=False):
                gr.Markdown('''
                The **Benchmark Contamination Bulletin** presents contamination statistics for evaluation benchmarks across different data sources.

                - Benchmarks analyzed in our paper are under the **core** source. Community-submitted benchmarks appear under the **community** source.
                - The contamination rate represents the percentage of *dirty* benchmark entries.
                - The bulletin will be updated regularly to include contamination checks on newly released Common Crawl dumps.
                ''')

            source_radio = gr.Radio(
                choices=["core", "community"],
                label="Select Benchmark Source",
                value="core"
            )

            leaderboard_html = gr.HTML(build_table("core", refresh=False))

            def update_table(source):
                return build_table(source, refresh=True)

            source_radio.change(
                fn=build_table,
                inputs=source_radio,
                outputs=leaderboard_html
            )

            refresh_button = gr.Button("Refresh")
            refresh_button.click(
                fn=update_table,
                inputs=source_radio,
                outputs=leaderboard_html
            )

        with gr.Tab(label="Add New Benchmarks"):
            gr.Markdown('''
            ## Add Your Own Benchmarks for Contamination Checking

            You can use this form to submit a benchmark for contamination checking. Submissions may include either a direct upload or a reference to a publicly available dataset on Hugging Face.

            ### Submission Guidelines:
            - **Benchmark Name**: Provide a name for your benchmark.
            - **Contributor**: Enter your name or affiliation.
            - **Data Source**:
                - Upload a `.jsonl` file containing your benchmark entries, or
                - Specify a Hugging Face dataset path (`author/benchmark-name`) along with the   appropriate split (e.g., `test`, `validation`).
            - **Field Name**: Indicate the field to analyze for contamination:
                - For question-answering datasets: use the question field.
                - For language understanding tasks: use the context or passage field.

            ### What Happens Next:
            Once submitted, your benchmark will be queued for analysis. Results will be published in the **community** section of the bulletin.
            
            Processing time may vary depending on the dataset format and size. You can check the results by navigating to the **Bulletin** tab and selecting the **community** source, then clicking **Refresh**.
            ''')


            with gr.Row():
                benchmark_name_input = gr.Textbox(label="Benchmark Name")
                contributor_input = gr.Textbox(label="Contributor")

            with gr.Row():
                jsonl_input = gr.File(label="Upload .jsonl File", file_types=[".jsonl"])
                with gr.Column():
                    hf_path_input = gr.Textbox(label="HuggingFace Dataset Path", placeholder="e.g., author/benchmark-name")
                    hf_split_input = gr.Textbox(label="Dataset split (only if providing HuggingFace Dataset)", placeholder="e.g., validation, test")
                    hf_config_input = gr.Textbox(label="Dataset Config (optional)", placeholder="name of dataset config")
            field_name_input = gr.Textbox(label="Context or Question Field Name", placeholder="e.g., context, question, ...")

            with gr.Row():
                gr.LoginButton()
                submit_button = gr.Button("Submit for Contamination Check")
            result_output = gr.Textbox(label="Submission Status", interactive=False)

            submit_button.click(
                fn=record_submission,
                inputs=[benchmark_name_input, contributor_input, jsonl_input, hf_path_input, hf_split_input, field_name_input, hf_config_input],
                outputs=result_output,
            )

interface.launch()