Upload folder using huggingface_hub
Browse files- .gitignore +4 -8
- README.md +103 -10
- app.py +1 -2
- create_leaderboard.py +44 -53
- data/leaderboard.csv +20 -46
- main_results.tex +44 -0
- objective_public_CalibErr.tex +31 -0
- requirements.txt +1 -0
- results_domain_specific.tex +41 -0
- results_private_set.tex +44 -0
- results_subset_en.tex +47 -0
- results_subset_zh.tex +47 -0
- tabs/data_viewer_side_by_side_tab.py +71 -33
- tabs/data_viewer_tab.py +75 -50
- tabs/leaderboard_tab.py +349 -147
- tabs/shared_data.py +67 -8
- utils/merge_raw_data.py +82 -48
- utils/parse_icbcbench_tex.py +219 -0
- utils/rank_leaderboard.py +214 -84
.gitignore
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
|
|
| 1 |
__pycache__/
|
| 2 |
-
*.
|
| 3 |
-
*$py.class
|
| 4 |
-
.env
|
| 5 |
-
.venv
|
| 6 |
-
env/
|
| 7 |
-
venv/
|
| 8 |
-
ENV/
|
| 9 |
.DS_Store
|
| 10 |
-
*.
|
|
|
|
|
|
| 1 |
+
.venv/
|
| 2 |
__pycache__/
|
| 3 |
+
*.pyc
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
.DS_Store
|
| 5 |
+
*.egg-info/
|
| 6 |
+
.ipynb_checkpoints/
|
README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
colorTo: gray
|
| 6 |
sdk: docker
|
| 7 |
app_file: app.py
|
|
@@ -9,20 +9,113 @@ pinned: false
|
|
| 9 |
license: apache-2.0
|
| 10 |
---
|
| 11 |
|
| 12 |
-
#
|
| 13 |
|
| 14 |
-
|
| 15 |
|
| 16 |
## Overview
|
| 17 |
-
This Hugging Face Space hosts the interactive leaderboard for the DeepResearch Bench project.
|
| 18 |
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
-
### Local Development
|
| 22 |
```bash
|
| 23 |
pip install -r requirements.txt
|
|
|
|
|
|
|
| 24 |
python app.py
|
| 25 |
```
|
| 26 |
|
| 27 |
-
|
| 28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: ICBCBench Leaderboard
|
| 3 |
+
emoji: 🏦
|
| 4 |
+
colorFrom: red
|
| 5 |
colorTo: gray
|
| 6 |
sdk: docker
|
| 7 |
app_file: app.py
|
|
|
|
| 9 |
license: apache-2.0
|
| 10 |
---
|
| 11 |
|
| 12 |
+
# ICBCBench Leaderboard
|
| 13 |
|
| 14 |
+
Interactive leaderboard for **ICBCBench: An Industry Consortium Benchmark for Financial Deep Research**.
|
| 15 |
|
| 16 |
## Overview
|
|
|
|
| 17 |
|
| 18 |
+
ICBCBench adopts a dual-track evaluation paradigm:
|
| 19 |
+
|
| 20 |
+
- **Objective Track**: Closed-ended financial questions with verifiable answers, judged by GPT-5.4.
|
| 21 |
+
- **Subjective Track**: Long-form financial research reports, evaluated by Gemini-3.1-Pro-Preview using expert-aligned rubrics, citation consistency, and source quality.
|
| 22 |
+
|
| 23 |
+
The public subset contains 120 tasks across Global (English) and Chinese markets.
|
| 24 |
+
|
| 25 |
+
- 📄 Paper: <https://arxiv.org/abs/2606.17458>
|
| 26 |
+
- 🤗 HF Space: <https://huggingface.co/spaces/DeepFin-Intelligence/ICBCBench-Leaderboard>
|
| 27 |
+
- 💻 Code: <https://github.com/ICBCBench/ICBCBench>
|
| 28 |
+
- 📊 Dataset: <https://huggingface.co/datasets/ICBCBench/ICBCBench>
|
| 29 |
+
|
| 30 |
+
## Repository Layout
|
| 31 |
+
|
| 32 |
+
```
|
| 33 |
+
├── app.py # Gradio entry point
|
| 34 |
+
├── create_leaderboard.py # Main Gradio Blocks UI
|
| 35 |
+
├── tabs/
|
| 36 |
+
│ ├── leaderboard_tab.py # Leaderboard rendering
|
| 37 |
+
│ ├── data_viewer_tab.py # Single-model report viewer
|
| 38 |
+
│ ├── data_viewer_side_by_side_tab.py # Side-by-side comparison
|
| 39 |
+
│ └── shared_data.py # Lazy data loading
|
| 40 |
+
├── utils/
|
| 41 |
+
│ ├── rank_leaderboard.py # Aggregate raw results -> leaderboard.csv
|
| 42 |
+
│ └── merge_raw_data.py # Merge raw data + scores -> data_viewer.jsonl
|
| 43 |
+
└── data/
|
| 44 |
+
├── leaderboard.csv # Aggregated leaderboard (generated)
|
| 45 |
+
├── data_viewer.jsonl # Per-sample viewer data (generated)
|
| 46 |
+
├── raw_data/ # Per-model prompts + articles
|
| 47 |
+
└── raw_results/ # Per-model evaluation scores
|
| 48 |
+
```
|
| 49 |
+
|
| 50 |
+
## Data Format
|
| 51 |
+
|
| 52 |
+
### `data/leaderboard.csv`
|
| 53 |
+
|
| 54 |
+
| Column | Description |
|
| 55 |
+
|--------|-------------|
|
| 56 |
+
| `model` | Model / system name |
|
| 57 |
+
| `overall` | Weighted overall score across EN & ZH, Objective & Subjective |
|
| 58 |
+
| `objective_en` | Objective score on Global (English) tasks |
|
| 59 |
+
| `objective_zh` | Objective score on Chinese tasks |
|
| 60 |
+
| `objective_avg` | Average objective score across both language tracks |
|
| 61 |
+
| `subjective_en` | Subjective report score on Global (English) tasks |
|
| 62 |
+
| `subjective_zh` | Subjective report score on Chinese tasks |
|
| 63 |
+
| `subjective_avg` | Average subjective score across both language tracks |
|
| 64 |
+
| `citation_score` | Citation consistency score (0-100) |
|
| 65 |
+
| `source_quality` | Source authority & timeliness score (0-100) |
|
| 66 |
+
| `rmsce` | Root Mean Square Calibration Error for objective confidence (lower is better) |
|
| 67 |
+
|
| 68 |
+
### `data/raw_results/<model>/results.jsonl`
|
| 69 |
+
|
| 70 |
+
Each line should be a JSON object:
|
| 71 |
+
|
| 72 |
+
```json
|
| 73 |
+
{
|
| 74 |
+
"id": "1",
|
| 75 |
+
"language": "en",
|
| 76 |
+
"track": "objective",
|
| 77 |
+
"score": 0.85,
|
| 78 |
+
"correct": true,
|
| 79 |
+
"confidence": 0.90
|
| 80 |
+
}
|
| 81 |
+
```
|
| 82 |
+
|
| 83 |
+
or for subjective tasks:
|
| 84 |
+
|
| 85 |
+
```json
|
| 86 |
+
{
|
| 87 |
+
"id": "41",
|
| 88 |
+
"language": "zh",
|
| 89 |
+
"track": "subjective",
|
| 90 |
+
"score": 0.72,
|
| 91 |
+
"expert_score": 0.74,
|
| 92 |
+
"citation_score": 0.68,
|
| 93 |
+
"source_quality_score": 0.70
|
| 94 |
+
}
|
| 95 |
+
```
|
| 96 |
+
|
| 97 |
+
Scores can be either 0-1 ratios or 0-100 percentages.
|
| 98 |
+
|
| 99 |
+
## Local Development
|
| 100 |
|
|
|
|
| 101 |
```bash
|
| 102 |
pip install -r requirements.txt
|
| 103 |
+
python utils/rank_leaderboard.py # generate data/leaderboard.csv
|
| 104 |
+
python utils/merge_raw_data.py # generate data/data_viewer.jsonl
|
| 105 |
python app.py
|
| 106 |
```
|
| 107 |
|
| 108 |
+
> **Note**: The `data/raw_results/` directory in this repository currently contains legacy DeepResearch-Bench-format `race_result.txt` files. Before running `rank_leaderboard.py` for ICBCBench, replace these with ICBCBench `results.jsonl` files as described above. The checked-in `data/leaderboard.csv` is a sample ICBCBench leaderboard and will be overwritten if you rerun the script without updating `raw_results/`.
|
| 109 |
+
|
| 110 |
+
Then open <http://localhost:7860>.
|
| 111 |
+
|
| 112 |
+
## Citation
|
| 113 |
+
|
| 114 |
+
```bibtex
|
| 115 |
+
@article{li2026icbcbench,
|
| 116 |
+
author = {Weiya Li and Zhiwei Tang and Yizhou He and Chenghao Wang and Liang Feng and Xiao Sun and Dongrui Liu and Zichen Wen and Hu Wei and Jinghang Wang and Yi Luo and Li Guo and Linfeng Zhang},
|
| 117 |
+
title = {ICBCBench: An Industry Consortium Benchmark for Financial Deep Research},
|
| 118 |
+
journal = {arXiv preprint arXiv:2606.17458},
|
| 119 |
+
year = {2026},
|
| 120 |
+
}
|
| 121 |
+
```
|
app.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
# -*- coding: utf-8 -*-
|
| 3 |
"""
|
| 4 |
-
|
| 5 |
"""
|
| 6 |
|
| 7 |
from __future__ import annotations
|
|
@@ -13,5 +13,4 @@ if __name__ == "__main__":
|
|
| 13 |
server_name="0.0.0.0",
|
| 14 |
server_port=7860,
|
| 15 |
share=False,
|
| 16 |
-
show_api=False,
|
| 17 |
)
|
|
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
# -*- coding: utf-8 -*-
|
| 3 |
"""
|
| 4 |
+
ICBCBench HF Space entrypoint.
|
| 5 |
"""
|
| 6 |
|
| 7 |
from __future__ import annotations
|
|
|
|
| 13 |
server_name="0.0.0.0",
|
| 14 |
server_port=7860,
|
| 15 |
share=False,
|
|
|
|
| 16 |
)
|
create_leaderboard.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
# -*- coding: utf-8 -*-
|
| 3 |
"""
|
| 4 |
-
Gradio UI –
|
|
|
|
| 5 |
"""
|
| 6 |
|
| 7 |
from __future__ import annotations
|
|
@@ -10,11 +11,11 @@ from datetime import datetime
|
|
| 10 |
import pandas as pd
|
| 11 |
import gradio as gr
|
| 12 |
|
| 13 |
-
# ---- Tab
|
| 14 |
from tabs.leaderboard_tab import create_leaderboard_tab
|
| 15 |
-
from tabs.
|
| 16 |
-
from tabs.
|
| 17 |
-
|
| 18 |
|
| 19 |
def get_leaderboard_info():
|
| 20 |
leaderboard_path = Path(__file__).parent / "data" / "leaderboard.csv"
|
|
@@ -27,7 +28,8 @@ def get_leaderboard_info():
|
|
| 27 |
return model_count, last_update
|
| 28 |
except Exception:
|
| 29 |
pass
|
| 30 |
-
return
|
|
|
|
| 31 |
|
| 32 |
model_count, last_update = get_leaderboard_info()
|
| 33 |
|
|
@@ -35,28 +37,27 @@ model_count, last_update = get_leaderboard_info()
|
|
| 35 |
# UI
|
| 36 |
# ---------------------------------------------------------------------------
|
| 37 |
|
| 38 |
-
with gr.Blocks(title="
|
| 39 |
|
| 40 |
-
# =========
|
| 41 |
gr.HTML("""
|
| 42 |
<style>
|
| 43 |
.title-block{
|
| 44 |
-
|
| 45 |
-
background: linear-gradient(to right, #
|
| 46 |
-
background: -
|
| 47 |
-
background: -moz-linear-gradient(to right, #009CFF, #823AFF);
|
| 48 |
-webkit-background-clip: text;
|
| 49 |
-webkit-text-fill-color: transparent;
|
| 50 |
background-clip: text;
|
| 51 |
color: transparent;
|
| 52 |
-
|
| 53 |
text-align: center;
|
| 54 |
font-size: 2rem;
|
| 55 |
font-weight: 700;
|
| 56 |
margin: 0 0 1rem 0;
|
| 57 |
padding-bottom: 0.2rem;
|
| 58 |
-
display: inline-block;
|
| 59 |
-
width: 100%;
|
| 60 |
}
|
| 61 |
.intro-block{
|
| 62 |
text-align:center;
|
|
@@ -72,46 +73,36 @@ with gr.Blocks(title="DeepResearch Bench") as demo:
|
|
| 72 |
</style>
|
| 73 |
""")
|
| 74 |
|
| 75 |
-
# =========
|
| 76 |
gr.HTML(f"""
|
| 77 |
<div class="title-block">
|
| 78 |
-
|
| 79 |
</div>
|
| 80 |
|
| 81 |
<div class="intro-block">
|
| 82 |
-
|
| 83 |
-
<
|
| 84 |
-
<a href="https://
|
| 85 |
-
<a href="https://arxiv.org/abs/
|
| 86 |
-
<a href="
|
|
|
|
| 87 |
Total models: {model_count} | Last Update: {last_update}<br>
|
| 88 |
<small style="color: #666; font-size: 0.9em;">
|
| 89 |
-
|
| 90 |
-
Gemini-2.5 Eval tab — Race judge: gemini-2.5-pro | Fact-check: gemini-2.5-flash
|
| 91 |
</small>
|
| 92 |
</div>
|
| 93 |
""")
|
| 94 |
|
| 95 |
-
# =========
|
| 96 |
with gr.Tabs():
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
sbs_on_load, sbs_outputs = create_data_viewer_side_by_side_tab()
|
| 101 |
-
dv_on_load, dv_outputs = create_data_viewer_tab()
|
| 102 |
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
"""
|
| 106 |
-
🚧 **Prompt-to-Leaderboard** module not implemented yet.
|
| 107 |
-
Planned: inspect how individual prompts affect overall model ranking.
|
| 108 |
-
"""
|
| 109 |
-
)
|
| 110 |
-
|
| 111 |
-
demo.load(fn=dv_on_load, outputs=dv_outputs)
|
| 112 |
-
demo.load(fn=sbs_on_load, outputs=sbs_outputs)
|
| 113 |
|
| 114 |
-
# ========= Citation
|
| 115 |
gr.HTML("""
|
| 116 |
<style>
|
| 117 |
.citation-block {
|
|
@@ -158,26 +149,26 @@ Planned: inspect how individual prompts affect overall model ranking.
|
|
| 158 |
background-color: #198754;
|
| 159 |
}
|
| 160 |
</style>
|
| 161 |
-
|
| 162 |
<div class="citation-block">
|
| 163 |
<div class="citation-title">📚 Citation</div>
|
| 164 |
<div class="citation-content" id="citation-text">
|
| 165 |
<button class="copy-btn" onclick="copyCitation()">Copy</button>
|
| 166 |
-
@article{
|
| 167 |
-
author = {
|
| 168 |
-
title = {
|
| 169 |
-
journal = {arXiv preprint},
|
| 170 |
-
year = {
|
| 171 |
}</div>
|
| 172 |
</div>
|
| 173 |
-
|
| 174 |
<script>
|
| 175 |
function copyCitation() {
|
| 176 |
-
const citationText = `@article{
|
| 177 |
-
author = {
|
| 178 |
-
title = {
|
| 179 |
-
journal = {arXiv preprint},
|
| 180 |
-
year = {
|
| 181 |
}`;
|
| 182 |
navigator.clipboard.writeText(citationText).then(function() {
|
| 183 |
const btn = document.querySelector('.copy-btn');
|
|
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
# -*- coding: utf-8 -*-
|
| 3 |
"""
|
| 4 |
+
Gradio UI – ICBCBench Leaderboard
|
| 5 |
+
Dual-track (Objective / Subjective) leaderboard for financial deep research.
|
| 6 |
"""
|
| 7 |
|
| 8 |
from __future__ import annotations
|
|
|
|
| 11 |
import pandas as pd
|
| 12 |
import gradio as gr
|
| 13 |
|
| 14 |
+
# ---- Tab components ----
|
| 15 |
from tabs.leaderboard_tab import create_leaderboard_tab
|
| 16 |
+
# from tabs.data_viewer_tab import create_data_viewer_tab
|
| 17 |
+
# from tabs.data_viewer_side_by_side_tab import create_data_viewer_side_by_side_tab
|
| 18 |
+
|
| 19 |
|
| 20 |
def get_leaderboard_info():
|
| 21 |
leaderboard_path = Path(__file__).parent / "data" / "leaderboard.csv"
|
|
|
|
| 28 |
return model_count, last_update
|
| 29 |
except Exception:
|
| 30 |
pass
|
| 31 |
+
return 0, "Not yet updated"
|
| 32 |
+
|
| 33 |
|
| 34 |
model_count, last_update = get_leaderboard_info()
|
| 35 |
|
|
|
|
| 37 |
# UI
|
| 38 |
# ---------------------------------------------------------------------------
|
| 39 |
|
| 40 |
+
with gr.Blocks(title="ICBCBench Leaderboard") as demo:
|
| 41 |
|
| 42 |
+
# ========= Global CSS =========
|
| 43 |
gr.HTML("""
|
| 44 |
<style>
|
| 45 |
.title-block{
|
| 46 |
+
background: linear-gradient(to right, #C41E3A, #8B0000);
|
| 47 |
+
background: -webkit-linear-gradient(to right, #C41E3A, #8B0000);
|
| 48 |
+
background: -moz-linear-gradient(to right, #C41E3A, #8B0000);
|
|
|
|
| 49 |
-webkit-background-clip: text;
|
| 50 |
-webkit-text-fill-color: transparent;
|
| 51 |
background-clip: text;
|
| 52 |
color: transparent;
|
| 53 |
+
|
| 54 |
text-align: center;
|
| 55 |
font-size: 2rem;
|
| 56 |
font-weight: 700;
|
| 57 |
margin: 0 0 1rem 0;
|
| 58 |
padding-bottom: 0.2rem;
|
| 59 |
+
display: inline-block;
|
| 60 |
+
width: 100%;
|
| 61 |
}
|
| 62 |
.intro-block{
|
| 63 |
text-align:center;
|
|
|
|
| 73 |
</style>
|
| 74 |
""")
|
| 75 |
|
| 76 |
+
# ========= Header =========
|
| 77 |
gr.HTML(f"""
|
| 78 |
<div class="title-block">
|
| 79 |
+
ICBCBench: An Industry Consortium Benchmark for Financial Deep Research
|
| 80 |
</div>
|
| 81 |
|
| 82 |
<div class="intro-block">
|
| 83 |
+
ICBCBench evaluates Deep Research Agents on financial tasks through a dual-track paradigm:<br>
|
| 84 |
+
<strong>Objective</strong> (verifiable answers) and <strong>Subjective</strong> (expert-aligned report quality).<br>
|
| 85 |
+
<a href="https://huggingface.co/spaces/DeepFin-Intelligence/ICBCBench-Leaderboard" target="_blank">HF Space</a> |
|
| 86 |
+
<a href="https://arxiv.org/abs/2606.17458" target="_blank">Paper</a> |
|
| 87 |
+
<a href="https://github.com/ICBCBench/ICBCBench" target="_blank">Code</a> |
|
| 88 |
+
<a href="https://huggingface.co/datasets/ICBCBench/ICBCBench" target="_blank">Dataset</a> |
|
| 89 |
Total models: {model_count} | Last Update: {last_update}<br>
|
| 90 |
<small style="color: #666; font-size: 0.9em;">
|
| 91 |
+
Objective judge: GPT-5.4 | Subjective judge: Gemini-3.1-Pro-Preview
|
|
|
|
| 92 |
</small>
|
| 93 |
</div>
|
| 94 |
""")
|
| 95 |
|
| 96 |
+
# ========= Main Tabs =========
|
| 97 |
with gr.Tabs():
|
| 98 |
+
create_leaderboard_tab() # 🏆 Leaderboard
|
| 99 |
+
# sbs_on_load, sbs_outputs = create_data_viewer_side_by_side_tab() # ⚔️ Side-by-Side
|
| 100 |
+
# dv_on_load, dv_outputs = create_data_viewer_tab() # 🔍 Data Viewer
|
|
|
|
|
|
|
| 101 |
|
| 102 |
+
# demo.load(fn=dv_on_load, outputs=dv_outputs)
|
| 103 |
+
# demo.load(fn=sbs_on_load, outputs=sbs_outputs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
|
| 105 |
+
# ========= Citation =========
|
| 106 |
gr.HTML("""
|
| 107 |
<style>
|
| 108 |
.citation-block {
|
|
|
|
| 149 |
background-color: #198754;
|
| 150 |
}
|
| 151 |
</style>
|
| 152 |
+
|
| 153 |
<div class="citation-block">
|
| 154 |
<div class="citation-title">📚 Citation</div>
|
| 155 |
<div class="citation-content" id="citation-text">
|
| 156 |
<button class="copy-btn" onclick="copyCitation()">Copy</button>
|
| 157 |
+
@article{li2026icbcbench,
|
| 158 |
+
author = {Weiya Li and Zhiwei Tang and Yizhou He and Chenghao Wang and Liang Feng and Xiao Sun and Dongrui Liu and Zichen Wen and Hu Wei and Jinghang Wang and Yi Luo and Li Guo and Linfeng Zhang},
|
| 159 |
+
title = {ICBCBench: An Industry Consortium Benchmark for Financial Deep Research},
|
| 160 |
+
journal = {arXiv preprint arXiv:2606.17458},
|
| 161 |
+
year = {2026},
|
| 162 |
}</div>
|
| 163 |
</div>
|
| 164 |
+
|
| 165 |
<script>
|
| 166 |
function copyCitation() {
|
| 167 |
+
const citationText = `@article{li2026icbcbench,
|
| 168 |
+
author = {Weiya Li and Zhiwei Tang and Yizhou He and Chenghao Wang and Liang Feng and Xiao Sun and Dongrui Liu and Zichen Wen and Hu Wei and Jinghang Wang and Yi Luo and Li Guo and Linfeng Zhang},
|
| 169 |
+
title = {ICBCBench: An Industry Consortium Benchmark for Financial Deep Research},
|
| 170 |
+
journal = {arXiv preprint arXiv:2606.17458},
|
| 171 |
+
year = {2026},
|
| 172 |
}`;
|
| 173 |
navigator.clipboard.writeText(citationText).then(function() {
|
| 174 |
const btn = document.querySelector('.copy-btn');
|
data/leaderboard.csv
CHANGED
|
@@ -1,46 +1,20 @@
|
|
| 1 |
-
model,
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
deepdog,53.52,53.14,56.10,51.83,51.18,-,-
|
| 22 |
-
RecallRadar,53.19,53.91,53.53,52.18,52.38,-,-
|
| 23 |
-
MindDR-V1.5,52.54,51.54,55.30,50.45,51.26,-,-
|
| 24 |
-
tavily-research,52.44,52.84,53.59,51.92,49.21,-,-
|
| 25 |
-
thinkdepthai-deepresearch,52.43,52.02,53.88,52.04,50.12,-,-
|
| 26 |
-
salesforce-air-deep-research,50.65,50.00,51.09,50.77,50.32,-,-
|
| 27 |
-
gensee-search-gpt-5,50.60,50.06,50.76,51.31,49.72,32.94,21.06
|
| 28 |
-
gemini-2.5-pro-deepresearch,49.71,49.51,49.45,50.12,50.00,78.30,165.34
|
| 29 |
-
langchain-open-deep-research-gpt-5,49.33,49.80,47.34,51.05,48.99,34.74,22.44
|
| 30 |
-
openai-deepresearch,46.45,46.46,43.73,49.39,47.22,75.01,39.79
|
| 31 |
-
raaa-deep-research,46.13,43.77,48.34,47.21,43.78,-,-
|
| 32 |
-
dr-tulu,45.49,44.08,44.65,49.56,42.30,-,-
|
| 33 |
-
claude-research,45.00,45.34,42.79,47.58,44.66,-,-
|
| 34 |
-
kimi-researcher,44.64,44.96,41.97,47.14,45.59,-,-
|
| 35 |
-
doubao-deepresearch,44.34,44.84,40.56,47.95,44.69,52.86,52.62
|
| 36 |
-
langchain-open-deep-research,43.44,42.97,39.17,48.09,45.22,49.10,29.49
|
| 37 |
-
nvidia-aiq-research-assistant,40.52,37.98,38.39,44.59,42.63,-,-
|
| 38 |
-
tongyi-deepresearch-30B-A3B,40.46,39.46,34.44,46.22,44.27,-,-
|
| 39 |
-
perplexity-Research,40.46,39.10,35.65,46.11,43.08,82.63,31.20
|
| 40 |
-
grok-deeper-search,38.22,36.08,30.89,46.59,42.17,73.08,8.58
|
| 41 |
-
sonar-reasoning-pro,37.76,34.96,31.65,44.93,42.42,45.19,9.39
|
| 42 |
-
sonar-reasoning,37.75,34.73,32.59,44.42,42.39,52.58,13.37
|
| 43 |
-
claude-3-7-sonnet-with-search,36.63,35.95,31.29,44.05,36.07,87.32,24.51
|
| 44 |
-
sonar-pro,36.19,33.92,29.69,43.39,41.07,79.72,16.75
|
| 45 |
-
gemini-2.5-pro-preview-05-06,31.90,31.75,24.61,40.24,32.76,-,-
|
| 46 |
-
gpt-4o-search-preview,30.74,27.81,20.44,41.01,37.60,86.63,5.05
|
|
|
|
| 1 |
+
model,overall,objective_en,objective_zh,objective_avg,subjective_en,subjective_zh,subjective_avg,expert_avg,citation_score,source_quality,rmsce
|
| 2 |
+
OpenClaw(+GPT-5.5),59.09,50.00,67.50,58.75,59.60,59.25,59.42,74.28,-,-,46.23
|
| 3 |
+
DeerFlow(+GPT-5.5),58.76,52.50,60.00,56.25,64.85,57.67,61.26,76.57,-,-,47.83
|
| 4 |
+
Gemini-deep-research,58.23,50.00,52.50,51.25,64.77,65.69,65.23,71.79,56.94,12.86,50.77
|
| 5 |
+
OpenClaw(+DeepSeek-V4-Pro),54.54,37.50,57.50,47.50,65.79,57.36,61.58,76.97,-,-,56.57
|
| 6 |
+
DeerFlow(+DeepSeek-V4-Pro),51.57,27.50,55.00,41.25,65.71,58.08,61.89,77.36,-,-,64.52
|
| 7 |
+
OpenAI-o3-deep-research,51.24,37.50,32.50,35.00,71.84,63.12,67.48,72.60,74.47,15.49,61.11
|
| 8 |
+
MiroThinker,48.63,52.50,45.00,48.75,53.15,43.88,48.52,60.64,-,-,55.57
|
| 9 |
+
Kimi-deep-research,46.16,35.00,35.00,35.00,60.19,54.44,57.31,71.64,-,-,62.94
|
| 10 |
+
GPT-5.5,43.75,27.50,27.50,27.50,62.69,57.33,60.01,75.02,-,-,52.32
|
| 11 |
+
Jina-deepsearch,42.73,37.50,35.00,36.25,47.51,50.89,49.20,52.52,46.36,27.54,65.67
|
| 12 |
+
Claude-opus-4-7,42.38,25.00,20.00,22.50,63.71,60.83,62.27,77.83,-,-,39.24
|
| 13 |
+
Doubao-deep-research,40.76,37.50,20.00,28.75,52.93,52.61,52.77,65.97,-,-,71.30
|
| 14 |
+
Perplexity-deep-research,39.26,22.50,22.50,22.50,63.17,48.85,56.01,70.02,-,-,55.65
|
| 15 |
+
Kimi-k2.5,38.48,17.50,10.00,13.75,64.81,61.60,63.20,79.01,-,-,77.62
|
| 16 |
+
Gemini-3.1-pro-preview,38.20,22.50,12.50,17.50,59.53,58.25,58.89,73.62,-,-,75.60
|
| 17 |
+
DeepSeek-V4-Pro,31.17,5.00,15.00,10.00,49.09,55.59,52.34,65.42,-,-,76.12
|
| 18 |
+
Grok-3-deepsearch,30.46,10.00,5.00,7.50,56.43,50.40,53.41,66.77,-,-,84.70
|
| 19 |
+
Qwen-deep-research,29.97,2.50,17.50,10.00,51.59,48.25,49.92,62.40,-,-,83.37
|
| 20 |
+
Tongyi-deepresearch-30b-a3b,24.09,2.50,5.00,3.75,46.69,42.16,44.42,55.53,-,-,87.92
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
main_results.tex
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
\begin{table*}[t]
|
| 2 |
+
\centering
|
| 3 |
+
\small
|
| 4 |
+
\setlength{\tabcolsep}{5pt}
|
| 5 |
+
\renewcommand{\arraystretch}{1.08}
|
| 6 |
+
|
| 7 |
+
\caption{\textbf{Main results on ICBCBench.} We report performance on global (EN) and Chinese (ZH) market scenarios. Objective evaluates closed-ended tasks, while Subjective assesses open-ended financial research reports. The best and second-best scores are highlighted in \textbf{bold} and \underline{underline}, respectively. Higher is better for all metrics.}
|
| 8 |
+
\label{tab:main_results}
|
| 9 |
+
|
| 10 |
+
\begin{tabular}{lcccccc}
|
| 11 |
+
\toprule
|
| 12 |
+
\multirow{2}{*}{\textbf{System}} & \multicolumn{3}{c}{\textbf{Global (EN)}} & \multicolumn{3}{c}{\textbf{Chinese (ZH)}} \\
|
| 13 |
+
\cmidrule(lr){2-4} \cmidrule(lr){5-7}
|
| 14 |
+
& \textbf{Objective} & \textbf{Subjective} & \textbf{Overall} & \textbf{Objective} & \textbf{Subjective} & \textbf{Overall} \\
|
| 15 |
+
\midrule
|
| 16 |
+
\rowcolor{gray!20}
|
| 17 |
+
\multicolumn{7}{c}{\textbf{\textit{Closed}}} \\
|
| 18 |
+
Gemini-deep-research & 50.00 & 64.77 & \underline{57.38} & 52.50 & \textbf{65.69} & \underline{59.09} \\
|
| 19 |
+
OpenAI-o3-deep-research & 37.50 & \textbf{71.84} & 54.67 & 32.50 & \underline{63.12} & 47.81 \\
|
| 20 |
+
Kimi-deep-research & 35.00 & 60.19 & 47.59 & 35.00 & 54.44 & 44.72 \\
|
| 21 |
+
Doubao-deep-research & 37.50 & 52.93 & 45.22 & 20.00 & 52.61 & 36.30 \\
|
| 22 |
+
GPT-5.5 & 27.50 & 62.69 & 45.09 & 27.50 & 57.33 & 42.41 \\
|
| 23 |
+
Claude-opus-4-7 & 25.00 & 63.71 & 44.36 & 20.00 & 60.83 & 40.41 \\
|
| 24 |
+
Perplexity-deep-research & 22.50 & 63.17 & 42.84 & 22.50 & 48.85 & 35.67 \\
|
| 25 |
+
Gemini-3.1-pro-preview & 22.50 & 59.53 & 41.02 & 12.50 & 58.25 & 35.38 \\
|
| 26 |
+
Grok-3-deepsearch & 10.00 & 56.43 & 33.22 & 5.00 & 50.40 & 27.70 \\
|
| 27 |
+
Qwen-deep-research & 2.50 & 51.59 & 27.05 & 17.50 & 48.25 & 32.88 \\
|
| 28 |
+
\midrule
|
| 29 |
+
\rowcolor{gray!20}
|
| 30 |
+
\multicolumn{7}{c}{\textbf{\textit{Open}}} \\
|
| 31 |
+
DeerFlow(+GPT-5.5) & \textbf{52.50} & 64.85 & \textbf{58.67} & \underline{60.00} & 57.67 & 58.84 \\
|
| 32 |
+
OpenClaw(+GPT-5.5) & 50.00 & 59.60 & 54.80 & \textbf{67.50} & 59.25 & \textbf{63.38} \\
|
| 33 |
+
MiroThinker & \textbf{52.50} & 53.15 & 52.83 & 45.00 & 43.88 & 44.44 \\
|
| 34 |
+
OpenClaw(+DeepSeek-V4-Pro) & 37.50 & \underline{65.79} & 51.65 & 57.50 & 57.36 & 57.43 \\
|
| 35 |
+
DeerFlow(+DeepSeek-V4-Pro) & 27.50 & 65.71 & 46.60 & 55.00 & 58.08 & 56.54 \\
|
| 36 |
+
Jina-deepsearch & 37.50 & 47.51 & 42.50 & 35.00 & 50.89 & 42.95 \\
|
| 37 |
+
Kimi-k2.5 & 17.50 & 64.81 & 41.16 & 10.00 & 61.60 & 35.80 \\
|
| 38 |
+
DeepSeek-V4-Pro & 5.00 & 49.09 & 27.05 & 15.00 & 55.59 & 35.30 \\
|
| 39 |
+
Tongyi-deepresearch-30b-a3b & 2.50 & 46.69 & 24.59 & 5.00 & 42.16 & 23.58 \\
|
| 40 |
+
% OpenClaw(+DeepSeek-V4-Flash) & -- & -- & -- & -- & -- & -- \\
|
| 41 |
+
% DeerFlow(+DeepSeek-V4-Flash) & -- & -- & -- & -- & -- & -- \\
|
| 42 |
+
\bottomrule
|
| 43 |
+
\end{tabular}
|
| 44 |
+
\end{table*}
|
objective_public_CalibErr.tex
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
\begin{table}[t]
|
| 2 |
+
\centering
|
| 3 |
+
\small
|
| 4 |
+
\caption{Objective Evaluation Results on Public Subset (All Languages). Higher Accuracy and lower Calibration Error are better.}
|
| 5 |
+
\label{tab:objective_public_CalibErr}
|
| 6 |
+
\begin{tabular}{lcc}
|
| 7 |
+
\toprule
|
| 8 |
+
\textbf{Model} & \textbf{Accuracy (\%)} & \textbf{Calibration Error (\%)} \\
|
| 9 |
+
\midrule
|
| 10 |
+
OpenClaw(+GPT-5.5) & 58.75 & 46.23 \\
|
| 11 |
+
DeerFlow(+GPT-5.5) & 56.25 & 47.83 \\
|
| 12 |
+
Gemini-deep-research & 51.25 & 50.77 \\
|
| 13 |
+
MiroThinker & 48.75 & 55.57 \\
|
| 14 |
+
OpenClaw(+DeepSeek-V4-Pro) & 47.50 & 56.57 \\
|
| 15 |
+
DeerFlow(+DeepSeek-V4-Pro) & 41.25 & 64.52 \\
|
| 16 |
+
Jina-deepsearch & 36.25 & 65.67 \\
|
| 17 |
+
OpenAI-o3-deep-research & 35.00 & 61.11 \\
|
| 18 |
+
Kimi-deep-research & 35.00 & 62.94 \\
|
| 19 |
+
Doubao-deep-research & 28.75 & 71.30 \\
|
| 20 |
+
GPT-5.5 & 27.50 & 52.32 \\
|
| 21 |
+
Claude-opus-4-7 & 22.50 & 39.24 \\
|
| 22 |
+
Perplexity-deep-research & 22.50 & 55.65 \\
|
| 23 |
+
Gemini-3.1-pro-preview & 17.50 & 75.60 \\
|
| 24 |
+
Kimi-k2.5 & 13.75 & 77.62 \\
|
| 25 |
+
DeepSeek-V4-Pro & 10.00 & 76.12 \\
|
| 26 |
+
Qwen-deep-research & 10.00 & 83.37 \\
|
| 27 |
+
Grok-3-deepsearch & 7.50 & 84.70 \\
|
| 28 |
+
Tongyi-deepresearch-30b-a3b & 3.75 & 87.92 \\
|
| 29 |
+
\bottomrule
|
| 30 |
+
\end{tabular}
|
| 31 |
+
\end{table}
|
requirements.txt
CHANGED
|
@@ -1,4 +1,5 @@
|
|
| 1 |
gradio>=5.31.0,<6.0.0
|
|
|
|
| 2 |
pandas>=1.5
|
| 3 |
numpy
|
| 4 |
plotly
|
|
|
|
| 1 |
gradio>=5.31.0,<6.0.0
|
| 2 |
+
huggingface_hub>=0.25.0
|
| 3 |
pandas>=1.5
|
| 4 |
numpy
|
| 5 |
plotly
|
results_domain_specific.tex
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
\begin{table*}[t]
|
| 2 |
+
\centering
|
| 3 |
+
\small
|
| 4 |
+
\setlength{\tabcolsep}{6pt}
|
| 5 |
+
\renewcommand{\arraystretch}{1.1}
|
| 6 |
+
|
| 7 |
+
\caption{\textbf{Domain-specific objective performance across major financial sectors.} This table reports objective task accuracy across Banking, Capital Markets, Insurance, and Other Financial Services. The results reveal that Open-Agentic frameworks consistently outperform closed-source proprietary models across all domains, highlighting their robust factual extraction and tool-use capabilities in specialized financial contexts. The best and second-best scores are highlighted in \textbf{bold} and \underline{underline}.}
|
| 8 |
+
|
| 9 |
+
\label{tab:results_domain_specific}
|
| 10 |
+
|
| 11 |
+
\begin{tabular}{lccccc}
|
| 12 |
+
\toprule
|
| 13 |
+
\textbf{System} & \textbf{Banking} & \textbf{Capital Markets} & \textbf{Insurance} & \textbf{Others} & \textbf{Average} \\
|
| 14 |
+
\midrule
|
| 15 |
+
|
| 16 |
+
\rowcolor{gray!20}
|
| 17 |
+
\multicolumn{6}{c}{\textbf{\textit{Closed}}} \\
|
| 18 |
+
Gemini-deep-research & \underline{53.57} & \underline{66.67} & 40.00 & 25.00 & 46.31 \\
|
| 19 |
+
Kimi-deep-research & 28.57 & 50.00 & 30.00 & 25.00 & 33.39 \\
|
| 20 |
+
OpenAI-o3-deep-research & 42.86 & 33.33 & 30.00 & 25.00 & 32.80 \\
|
| 21 |
+
GPT-5.5 & 25.00 & 20.83 & 35.00 & 37.50 & 29.58 \\
|
| 22 |
+
Doubao-deep-research & 35.71 & 29.17 & 25.00 & 12.50 & 25.60 \\
|
| 23 |
+
Perplexity-deep-research & 17.86 & 25.00 & 25.00 & 25.00 & 23.21 \\
|
| 24 |
+
Claude-opus-4-7 & 28.57 & 12.50 & 30.00 & 12.50 & 20.89 \\
|
| 25 |
+
Gemini-3.1-pro-preview & 21.43 & 16.67 & 15.00 & 12.50 & 16.40 \\
|
| 26 |
+
Grok-3-deepsearch & 7.14 & 8.33 & 5.00 & 12.50 & 8.24 \\
|
| 27 |
+
Qwen-deep-research & 10.71 & 20.83 & 0.00 & 0.00 & 7.89 \\
|
| 28 |
+
\rowcolor{gray!20}
|
| 29 |
+
\multicolumn{6}{c}{\textbf{\textit{Open}}} \\
|
| 30 |
+
OpenClaw(+GPT-5.5) & \textbf{57.14} & 62.50 & \underline{50.00} & \textbf{75.00} & \textbf{61.16} \\
|
| 31 |
+
DeerFlow(+GPT-5.5) & 46.43 & \underline{66.67} & \textbf{55.00} & \underline{62.50} & \underline{57.65} \\
|
| 32 |
+
MiroThinker & \underline{53.57} & 58.33 & 35.00 & 37.50 & 46.10 \\
|
| 33 |
+
OpenClaw(+DeepSeek-V4-Pro) & 42.86 & \underline{66.67} & 35.00 & 37.50 & 45.51 \\
|
| 34 |
+
DeerFlow(+DeepSeek-V4-Pro) & 21.43 & \textbf{70.83} & 25.00 & \underline{62.50} & 44.94 \\
|
| 35 |
+
Jina-deepsearch & 39.29 & 41.67 & 30.00 & 25.00 & 33.99 \\
|
| 36 |
+
Kimi-k2.5 & 14.29 & 12.50 & 20.00 & 0.00 & 11.70 \\
|
| 37 |
+
DeepSeek-V4-Pro & 10.71 & 4.17 & 15.00 & 12.50 & 10.60 \\
|
| 38 |
+
Tongyi-deepresearch-30b-a3b & 3.57 & 4.17 & 5.00 & 0.00 & 3.18 \\
|
| 39 |
+
\bottomrule
|
| 40 |
+
\end{tabular}
|
| 41 |
+
\end{table*}
|
results_private_set.tex
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
\begin{table*}[t]
|
| 2 |
+
\centering
|
| 3 |
+
\small
|
| 4 |
+
\setlength{\tabcolsep}{5pt}
|
| 5 |
+
\renewcommand{\arraystretch}{1.08}
|
| 6 |
+
|
| 7 |
+
\caption{\textbf{Generalization performance on the private hold-out set of ICBCBench.} The private set is not publicly released and is designed to evaluate model generalization and prevent benchmark overfitting. Results are reported on both global (EN) and Chinese (ZH) scenarios across objective and subjective tasks. The best and second-best scores are highlighted in \textbf{bold} and \underline{underline}, respectively. Higher is better for all metrics.}
|
| 8 |
+
\label{tab:results_private_set}
|
| 9 |
+
|
| 10 |
+
\begin{tabular}{lcccccc}
|
| 11 |
+
\toprule
|
| 12 |
+
\multirow{2}{*}{\textbf{System}} & \multicolumn{3}{c}{\textbf{Global (EN)}} & \multicolumn{3}{c}{\textbf{Chinese (ZH)}} \\
|
| 13 |
+
\cmidrule(lr){2-4} \cmidrule(lr){5-7}
|
| 14 |
+
& \textbf{Objective} & \textbf{Subjective} & \textbf{Overall} & \textbf{Objective} & \textbf{Subjective} & \textbf{Overall} \\
|
| 15 |
+
\midrule
|
| 16 |
+
\rowcolor{gray!20}
|
| 17 |
+
\multicolumn{7}{c}{\textbf{\textit{Closed}}} \\
|
| 18 |
+
Gemini-deep-research & \underline{75.00} & 64.68 & 69.84 & 45.00 & \underline{63.19} & 54.09 \\
|
| 19 |
+
OpenAI-o3-deep-research & 55.00 & \textbf{69.05} & 62.02 & 35.00 & 61.86 & 48.43 \\
|
| 20 |
+
Kimi-deep-research & 55.00 & 59.57 & 57.28 & 40.00 & 55.24 & 47.62 \\
|
| 21 |
+
Doubao-deep-research & 40.00 & 47.17 & 43.59 & 25.00 & 49.47 & 37.23 \\
|
| 22 |
+
Perplexity-deep-research & 20.00 & 60.53 & 40.27 & 30.00 & 45.64 & 37.82 \\
|
| 23 |
+
GPT-5.5 & 20.00 & 57.00 & 38.50 & 20.00 & 53.93 & 36.97 \\
|
| 24 |
+
Claude-opus-4-7 & 5.00 & 62.57 & 33.78 & 20.00 & 58.38 & 39.19 \\
|
| 25 |
+
Gemini-3.1-pro-preview & 10.00 & 56.43 & 33.22 & 25.00 & 55.40 & 40.20 \\
|
| 26 |
+
Grok-3-deepsearch & 10.00 & 53.03 & 31.52 & 15.00 & 41.89 & 28.45 \\
|
| 27 |
+
Qwen-deep-research & 10.00 & 48.30 & 29.15 & 20.00 & 45.18 & 32.59 \\
|
| 28 |
+
\midrule
|
| 29 |
+
\rowcolor{gray!20}
|
| 30 |
+
\multicolumn{7}{c}{\textbf{\textit{Open}}} \\
|
| 31 |
+
OpenClaw(+DeepSeek-V4-Pro) & \textbf{85.00} & 64.83 & \textbf{74.91} & \underline{55.00} & 56.09 & \underline{55.55} \\
|
| 32 |
+
DeerFlow(+DeepSeek-V4-Pro) & \underline{75.00} & \underline{68.63} & \underline{71.81} & 40.00 & 56.27 & 48.14 \\
|
| 33 |
+
OpenClaw(+GPT-5.5) & 70.00 & 61.63 & 65.81 & \textbf{60.00} & 57.24 & \textbf{58.62} \\
|
| 34 |
+
DeerFlow(+GPT-5.5) & 65.00 & 59.80 & 62.40 & 45.00 & 57.67 & 51.34 \\
|
| 35 |
+
MiroThinker & 65.00 & 43.30 & 54.15 & 40.00 & 36.18 & 38.09 \\
|
| 36 |
+
Jina-deepsearch & 20.00 & 48.60 & 34.30 & 10.00 & 45.37 & 27.68 \\
|
| 37 |
+
Kimi-k2.5 & 5.00 & 62.63 & 33.81 & 20.00 & \textbf{64.16} & 42.08 \\
|
| 38 |
+
Tongyi-deepresearch-30b-a3b & 0.00 & 46.27 & 23.14 & 5.00 & 36.91 & 20.95 \\
|
| 39 |
+
DeepSeek-V4-Pro & 5.00 & 22.50 & 13.75 & 20.00 & 49.47 & 34.73 \\
|
| 40 |
+
% OpenClaw(+DeepSeek-V4-Flash) & -- & -- & -- & -- & -- & -- \\
|
| 41 |
+
% DeerFlow(+DeepSeek-V4-Flash) & -- & -- & -- & -- & -- & -- \\
|
| 42 |
+
\bottomrule
|
| 43 |
+
\end{tabular}
|
| 44 |
+
\end{table*}
|
results_subset_en.tex
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
\begin{table*}[t]
|
| 2 |
+
\centering
|
| 3 |
+
\small
|
| 4 |
+
\setlength{\tabcolsep}{5pt}
|
| 5 |
+
\renewcommand{\arraystretch}{1.1}
|
| 6 |
+
|
| 7 |
+
\caption{\textbf{Performance on English (EN) tasks in ICBCBench.} We report objective and subjective results for global market scenarios. Objective includes text-only and aggregated scores (All), while Subjective evaluates report quality via Expert Rubrics, Citation Consistency, and Source Quality. The Overall score is the arithmetic mean of the Objective and Subjective scores. The best and second-best scores are highlighted in \textbf{bold} and \underline{underline}, respectively. Higher is better for all metrics.}
|
| 8 |
+
\label{tab:results_subset_en}
|
| 9 |
+
|
| 10 |
+
\begin{tabular}{lcccccc}
|
| 11 |
+
\toprule
|
| 12 |
+
\multirow{2}{*}{\textbf{System}}
|
| 13 |
+
& \multicolumn{2}{c}{\textbf{Objective}}
|
| 14 |
+
& \multicolumn{3}{c}{\textbf{Subjective}}
|
| 15 |
+
& \multirow{2}{*}{\textbf{Overall}} \\
|
| 16 |
+
\cmidrule(lr){2-3} \cmidrule(lr){4-6}
|
| 17 |
+
& \textbf{Text-Only} & \textbf{All} & \textbf{Expert} & \textbf{Citation} & \textbf{Source} & \\
|
| 18 |
+
\midrule
|
| 19 |
+
\rowcolor{gray!30}
|
| 20 |
+
\multicolumn{7}{c}{\textbf{\textit{Closed}}} \\
|
| 21 |
+
Gemini-deep-research & \underline{57.14} & 50.00 & 72.23 & \underline{56.94} & 12.86 & \underline{57.38} \\
|
| 22 |
+
OpenAI-o3-deep-research & 37.14 & 37.50 & 78.55 & \textbf{74.47} & \underline{15.49} & 54.67 \\
|
| 23 |
+
Kimi-deep-research & 40.00 & 35.00 & 75.23 & -- & -- & 47.59 \\
|
| 24 |
+
Doubao-deep-research & 42.86 & 37.50 & 66.17 & -- & -- & 45.22 \\
|
| 25 |
+
GPT-5.5 & 20.00 & 27.50 & 78.37 & -- & -- & 45.09 \\
|
| 26 |
+
Claude-opus-4-7 & 17.14 & 25.00 & 79.63 & -- & -- & 44.36 \\
|
| 27 |
+
Perplexity-deep-research & 25.71 & 22.50 & 78.97 & -- & -- & 42.84 \\
|
| 28 |
+
Gemini-3.1-pro-preview & 20.00 & 22.50 & 74.42 & -- & -- & 41.02 \\
|
| 29 |
+
Grok-3-deepsearch & 8.57 & 10.00 & 70.53 & -- & -- & 33.22 \\
|
| 30 |
+
Qwen-deep-research & 2.86 & 2.50 & 64.48 & -- & -- & 27.05 \\
|
| 31 |
+
\midrule
|
| 32 |
+
\rowcolor{gray!30}
|
| 33 |
+
\multicolumn{7}{c}{\textbf{\textit{Open}}} \\
|
| 34 |
+
DeerFlow(+GPT-5.5) & \underline{57.14} & \textbf{52.50} & 81.07 & -- & -- & \textbf{58.67} \\
|
| 35 |
+
OpenClaw(+GPT-5.5) & 54.29 & 50.00 & 74.50 & -- & -- & 54.80 \\
|
| 36 |
+
MiroThinker & \textbf{60.00} & \textbf{52.50} & 66.43 & -- & -- & 52.83 \\
|
| 37 |
+
OpenClaw(+DeepSeek-V4-Pro) & 40.00 & 37.50 & \textbf{82.23} & -- & -- & 51.65 \\
|
| 38 |
+
DeerFlow(+DeepSeek-V4-Pro) & 31.43 & 27.50 & \underline{82.13} & -- & -- & 46.60 \\
|
| 39 |
+
Jina-deepsearch & 34.29 & 37.50 & 50.15 & 46.36 & \textbf{27.54} & 42.50 \\
|
| 40 |
+
Kimi-k2.5 & 14.29 & 17.50 & 81.02 & -- & -- & 41.16 \\
|
| 41 |
+
DeepSeek-V4-Pro & 5.71 & 5.00 & 61.37 & -- & -- & 27.05 \\
|
| 42 |
+
Tongyi-deepresearch-30b-a3b & 2.86 & 2.50 & 58.37 & -- & -- & 24.59 \\
|
| 43 |
+
% OpenClaw(+DeepSeek-V4-Flash) & -- & -- & -- & -- & -- & -- \\
|
| 44 |
+
% DeerFlow(+DeepSeek-V4-Flash) & -- & -- & -- & -- & -- & -- \\
|
| 45 |
+
\bottomrule
|
| 46 |
+
\end{tabular}
|
| 47 |
+
\end{table*}
|
results_subset_zh.tex
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
\begin{table*}[t]
|
| 2 |
+
\centering
|
| 3 |
+
\small
|
| 4 |
+
\setlength{\tabcolsep}{5pt}
|
| 5 |
+
\renewcommand{\arraystretch}{1.1}
|
| 6 |
+
|
| 7 |
+
\caption{\textbf{Performance on Chinese (ZH) tasks in ICBCBench.} We report objective and subjective results for domestic market scenarios. Objective includes text-only and aggregated scores (All), while Subjective evaluates report quality via Expert Rubrics, Citation Consistency, and Source Quality. The Overall score is the arithmetic mean of the Objective and Subjective scores. The best and second-best scores are highlighted in \textbf{bold} and \underline{underline}, respectively. Higher is better for all metrics.}
|
| 8 |
+
\label{tab:results_subset_zh}
|
| 9 |
+
|
| 10 |
+
\begin{tabular}{lcccccc}
|
| 11 |
+
\toprule
|
| 12 |
+
\multirow{2}{*}{\textbf{System}}
|
| 13 |
+
& \multicolumn{2}{c}{\textbf{Objective}}
|
| 14 |
+
& \multicolumn{3}{c}{\textbf{Subjective}}
|
| 15 |
+
& \multirow{2}{*}{\textbf{Overall}} \\
|
| 16 |
+
\cmidrule(lr){2-3} \cmidrule(lr){4-6}
|
| 17 |
+
& \textbf{Text-Only} & \textbf{All} & \textbf{Expert} & \textbf{Citation} & \textbf{Source} & \\
|
| 18 |
+
\midrule
|
| 19 |
+
\rowcolor{gray!30}
|
| 20 |
+
\multicolumn{7}{c}{\textbf{\textit{Closed}}} \\
|
| 21 |
+
Gemini-deep-research & 61.76 & 52.50 & 71.35 & \underline{73.16} & 12.97 & \underline{59.09} \\
|
| 22 |
+
OpenAI-o3-deep-research & 38.24 & 32.50 & 66.65 & \textbf{79.35} & \underline{18.65} & 47.81 \\
|
| 23 |
+
Kimi-deep-research & 41.18 & 35.00 & 68.05 & -- & -- & 44.72 \\
|
| 24 |
+
GPT-5.5 & 29.41 & 27.50 & 71.67 & -- & -- & 42.41 \\
|
| 25 |
+
Claude-opus-4-7 & 23.53 & 20.00 & \underline{76.03} & -- & -- & 40.41 \\
|
| 26 |
+
Doubao-deep-research & 23.53 & 20.00 & 65.77 & -- & -- & 36.30 \\
|
| 27 |
+
Perplexity-deep-research & 26.47 & 22.50 & 61.07 & -- & -- & 35.67 \\
|
| 28 |
+
Gemini-3.1-pro-preview & 14.71 & 12.50 & 72.82 & -- & -- & 35.38 \\
|
| 29 |
+
Qwen-deep-research & 20.59 & 17.50 & 60.32 & -- & -- & 32.88 \\
|
| 30 |
+
Grok-3-deepsearch & 5.88 & 5.00 & 63.00 & -- & -- & 27.70 \\
|
| 31 |
+
\midrule
|
| 32 |
+
\rowcolor{gray!30}
|
| 33 |
+
\multicolumn{7}{c}{\textbf{\textit{Open}}} \\
|
| 34 |
+
OpenClaw(+GPT-5.5) & \textbf{70.59} & \textbf{67.50} & 74.07 & -- & -- & \textbf{63.38} \\
|
| 35 |
+
DeerFlow(+GPT-5.5) & \underline{64.71} & \underline{60.00} & 72.08 & -- & -- & 58.84 \\
|
| 36 |
+
OpenClaw(+DeepSeek-V4-Pro) & 61.76 & 57.50 & 71.70 & -- & -- & 57.43 \\
|
| 37 |
+
DeerFlow(+DeepSeek-V4-Pro) & 61.76 & 55.00 & 72.60 & -- & -- & 56.54 \\
|
| 38 |
+
MiroThinker & 52.94 & 45.00 & 54.85 & -- & -- & 44.44 \\
|
| 39 |
+
Jina-deepsearch & 41.18 & 35.00 & 54.88 & 38.02 & \textbf{31.83} & 42.95 \\
|
| 40 |
+
Kimi-k2.5 & 11.76 & 10.00 & \textbf{77.00} & -- & -- & 35.80 \\
|
| 41 |
+
DeepSeek-V4-Pro & 17.65 & 15.00 & 69.48 & -- & -- & 35.30 \\
|
| 42 |
+
Tongyi-deepresearch-30b-a3b & 5.88 & 5.00 & 52.70 & -- & -- & 23.58 \\
|
| 43 |
+
% OpenClaw(+DeepSeek-V4-Flash) & -- & -- & -- & -- & -- & -- \\
|
| 44 |
+
% DeerFlow(+DeepSeek-V4-Flash) & -- & -- & -- & -- & -- & -- \\
|
| 45 |
+
\bottomrule
|
| 46 |
+
\end{tabular}
|
| 47 |
+
\end{table*}
|
tabs/data_viewer_side_by_side_tab.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
# -*- coding: utf-8 -*-
|
| 3 |
"""
|
| 4 |
-
Data-Viewer Side-by-Side tab
|
| 5 |
"""
|
| 6 |
|
| 7 |
import gradio as gr
|
|
@@ -10,6 +10,7 @@ import re
|
|
| 10 |
|
| 11 |
from tabs.shared_data import get_entries_for_task, get_index
|
| 12 |
|
|
|
|
| 13 |
def make_user_task_markdown(item_id, prompt):
|
| 14 |
return f"""### User Task 🎯
|
| 15 |
|
|
@@ -17,24 +18,27 @@ def make_user_task_markdown(item_id, prompt):
|
|
| 17 |
|
| 18 |
**Description:** {prompt}"""
|
| 19 |
|
|
|
|
| 20 |
def make_article_markdown(article: str) -> str:
|
| 21 |
if article and isinstance(article, str):
|
| 22 |
processed_article = re.sub(r'\n{2,}', '\n\n', article)
|
| 23 |
table_pattern = r'(\|[^\n]*\n(?:[\|\s\-:]+\n)?(?:\|[^\n]*\n)*)'
|
| 24 |
tables = []
|
|
|
|
| 25 |
def replace_table(match):
|
| 26 |
tables.append(match.group(1))
|
| 27 |
return f'__TABLE_PLACEHOLDER_{len(tables)-1}__'
|
|
|
|
| 28 |
processed_article = re.sub(table_pattern, replace_table, processed_article)
|
| 29 |
processed_article = re.sub(r'(?<!\n)\*\s*\*\*([^*]+?)\*\*:', r'\n\n* **\1**:', processed_article)
|
| 30 |
processed_article = re.sub(r'\*\s*\*\*([^*]+?)\*\*:\s*([^*]*?)\s*\*\s*\*\*', r'* **\1**: \2\n * **', processed_article)
|
| 31 |
-
processed_article = re.sub(r'(?<!\n)\[\d+[^]]*\]\*\s*\*\*', r'\n\n* **', processed_article)
|
| 32 |
lines = processed_article.split('\n')
|
| 33 |
result_lines = []
|
| 34 |
for i, line in enumerate(lines):
|
| 35 |
result_lines.append(line)
|
| 36 |
-
if (i < len(lines) - 1 and
|
| 37 |
-
line.strip() and
|
| 38 |
lines[i + 1].strip() and
|
| 39 |
not line.strip().startswith('*') and
|
| 40 |
not lines[i + 1].strip().startswith('*') and
|
|
@@ -50,21 +54,63 @@ def make_article_markdown(article: str) -> str:
|
|
| 50 |
|
| 51 |
{processed_article}"""
|
| 52 |
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
html_items_str = ""
|
| 62 |
for title, score in scores_data:
|
| 63 |
-
score_value = score if score is not None else "N/A"
|
| 64 |
html_items_str += f"""
|
| 65 |
<div style="text-align: center; padding: 10px 3px; flex-grow: 1; flex-basis: 19%; min-width: 0;">
|
| 66 |
<h4 style="margin: 0 0 5px 0; font-size: 1em; color: #4a4a4a; font-weight: 600; line-height: 1.2;">{title}</h4>
|
| 67 |
-
<p style="margin: 0; font-size: 1.1em; font-weight: bold; color: #333;">{
|
| 68 |
</div>
|
| 69 |
"""
|
| 70 |
return f"""
|
|
@@ -74,14 +120,15 @@ def make_scores_html(overall, comprehensiveness, insight, instruction, readabili
|
|
| 74 |
</div>
|
| 75 |
</div>"""
|
| 76 |
|
|
|
|
| 77 |
# ---------- 生成 Tab ----------
|
| 78 |
def create_data_viewer_side_by_side_tab():
|
| 79 |
with gr.Tab("⚔️Side-by-Side Viewer"):
|
| 80 |
gr.HTML(
|
| 81 |
"""<style>
|
| 82 |
.card{background:#fff;border:1px solid #e0e0e0;border-radius:8px;padding:22px 24px;margin:18px 0;box-shadow:0 2px 4px rgba(0,0,0,.06);}
|
| 83 |
-
.scrollable-sm{max-height:180px;overflow-y:auto;}
|
| 84 |
-
.scrollable-lg{max-height:550px;overflow-y:auto;}
|
| 85 |
.card p{color:#424242 !important;line-height:1.75;margin:0 0 14px 0;text-align:justify;}
|
| 86 |
.card ul,.card ol{margin:12px 0 12px 24px;color:#424242 !important;}
|
| 87 |
.card li{margin:4px 0;color:#424242 !important;}
|
|
@@ -112,7 +159,7 @@ def create_data_viewer_side_by_side_tab():
|
|
| 112 |
|
| 113 |
def fetch_side_by_side_data(selected_task_display, model_a_name, model_b_name):
|
| 114 |
empty_article = make_article_markdown("")
|
| 115 |
-
empty_scores = make_scores_html(
|
| 116 |
|
| 117 |
if not selected_task_display:
|
| 118 |
no_task_msg = "请选择一个任务。"
|
|
@@ -125,10 +172,7 @@ def create_data_viewer_side_by_side_tab():
|
|
| 125 |
(task.get("prompt", "") for task in index.get("tasks", []) if str(task.get("id")) == item_id_str),
|
| 126 |
"任务描述未找到。"
|
| 127 |
)
|
| 128 |
-
user_task_md_content = make_user_task_markdown(
|
| 129 |
-
item_id_str,
|
| 130 |
-
task_prompt
|
| 131 |
-
)
|
| 132 |
|
| 133 |
outputs_a = [make_article_markdown("模型A未选择或数据未找到"), empty_scores]
|
| 134 |
outputs_b = [make_article_markdown("模型B未选择或数据未找到"), empty_scores]
|
|
@@ -138,20 +182,14 @@ def create_data_viewer_side_by_side_tab():
|
|
| 138 |
if model_a_name:
|
| 139 |
entry_a = entries.get(model_a_name)
|
| 140 |
if entry_a:
|
| 141 |
-
outputs_a[0] = make_article_markdown(entry_a
|
| 142 |
-
outputs_a[1] = make_scores_html(
|
| 143 |
-
entry_a["overall_score"], entry_a["comprehensiveness_score"],
|
| 144 |
-
entry_a["insight_score"], entry_a["instruction_following_score"],
|
| 145 |
-
entry_a["readability_score"])
|
| 146 |
|
| 147 |
if model_b_name:
|
| 148 |
entry_b = entries.get(model_b_name)
|
| 149 |
if entry_b:
|
| 150 |
-
outputs_b[0] = make_article_markdown(entry_b
|
| 151 |
-
outputs_b[1] = make_scores_html(
|
| 152 |
-
entry_b["overall_score"], entry_b["comprehensiveness_score"],
|
| 153 |
-
entry_b["insight_score"], entry_b["instruction_following_score"],
|
| 154 |
-
entry_b["readability_score"])
|
| 155 |
|
| 156 |
return user_task_md_content, outputs_a[0], outputs_a[1], outputs_b[0], outputs_b[1]
|
| 157 |
|
|
@@ -164,7 +202,7 @@ def create_data_viewer_side_by_side_tab():
|
|
| 164 |
def on_load():
|
| 165 |
index = get_index()
|
| 166 |
empty_article = make_article_markdown("")
|
| 167 |
-
empty_scores = make_scores_html(
|
| 168 |
all_models = index.get("models", [])
|
| 169 |
tasks = index.get("tasks", [])
|
| 170 |
if not all_models or not tasks:
|
|
@@ -195,4 +233,4 @@ def create_data_viewer_side_by_side_tab():
|
|
| 195 |
model_b_dd.change(fetch_side_by_side_data, inputs=[task_dd, model_a_dd, model_b_dd],
|
| 196 |
outputs=[user_task_display_md, article_a_md, scores_a_html, article_b_md, scores_b_html])
|
| 197 |
|
| 198 |
-
return on_load, [task_dd, model_a_dd, model_b_dd, user_task_display_md, article_a_md, scores_a_html, article_b_md, scores_b_html]
|
|
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
# -*- coding: utf-8 -*-
|
| 3 |
"""
|
| 4 |
+
Data-Viewer Side-by-Side tab for ICBCBench.
|
| 5 |
"""
|
| 6 |
|
| 7 |
import gradio as gr
|
|
|
|
| 10 |
|
| 11 |
from tabs.shared_data import get_entries_for_task, get_index
|
| 12 |
|
| 13 |
+
|
| 14 |
def make_user_task_markdown(item_id, prompt):
|
| 15 |
return f"""### User Task 🎯
|
| 16 |
|
|
|
|
| 18 |
|
| 19 |
**Description:** {prompt}"""
|
| 20 |
|
| 21 |
+
|
| 22 |
def make_article_markdown(article: str) -> str:
|
| 23 |
if article and isinstance(article, str):
|
| 24 |
processed_article = re.sub(r'\n{2,}', '\n\n', article)
|
| 25 |
table_pattern = r'(\|[^\n]*\n(?:[\|\s\-:]+\n)?(?:\|[^\n]*\n)*)'
|
| 26 |
tables = []
|
| 27 |
+
|
| 28 |
def replace_table(match):
|
| 29 |
tables.append(match.group(1))
|
| 30 |
return f'__TABLE_PLACEHOLDER_{len(tables)-1}__'
|
| 31 |
+
|
| 32 |
processed_article = re.sub(table_pattern, replace_table, processed_article)
|
| 33 |
processed_article = re.sub(r'(?<!\n)\*\s*\*\*([^*]+?)\*\*:', r'\n\n* **\1**:', processed_article)
|
| 34 |
processed_article = re.sub(r'\*\s*\*\*([^*]+?)\*\*:\s*([^*]*?)\s*\*\s*\*\*', r'* **\1**: \2\n * **', processed_article)
|
| 35 |
+
processed_article = re.sub(r'(?<!\n)\[\d+[^\]]*\]\*\s*\*\*', r'\n\n* **', processed_article)
|
| 36 |
lines = processed_article.split('\n')
|
| 37 |
result_lines = []
|
| 38 |
for i, line in enumerate(lines):
|
| 39 |
result_lines.append(line)
|
| 40 |
+
if (i < len(lines) - 1 and
|
| 41 |
+
line.strip() and
|
| 42 |
lines[i + 1].strip() and
|
| 43 |
not line.strip().startswith('*') and
|
| 44 |
not lines[i + 1].strip().startswith('*') and
|
|
|
|
| 54 |
|
| 55 |
{processed_article}"""
|
| 56 |
|
| 57 |
+
|
| 58 |
+
def make_scores_html(entry: dict) -> str:
|
| 59 |
+
"""Build score cards for ICBCBench side-by-side viewer."""
|
| 60 |
+
track = entry.get("track", "subjective")
|
| 61 |
+
|
| 62 |
+
overall = entry.get("overall_score")
|
| 63 |
+
objective = entry.get("objective_score")
|
| 64 |
+
subjective = entry.get("subjective_score")
|
| 65 |
+
expert = entry.get("expert_score")
|
| 66 |
+
citation = entry.get("citation_score")
|
| 67 |
+
source = entry.get("source_quality_score")
|
| 68 |
+
confidence = entry.get("confidence")
|
| 69 |
+
correct = entry.get("correct")
|
| 70 |
+
|
| 71 |
+
comp = entry.get("comprehensiveness_score")
|
| 72 |
+
insight = entry.get("insight_score")
|
| 73 |
+
inst = entry.get("instruction_following_score")
|
| 74 |
+
read = entry.get("readability_score")
|
| 75 |
+
|
| 76 |
+
def fmt(val):
|
| 77 |
+
if val is None:
|
| 78 |
+
return "N/A"
|
| 79 |
+
try:
|
| 80 |
+
return f"{float(val):.2f}"
|
| 81 |
+
except (TypeError, ValueError):
|
| 82 |
+
return str(val)
|
| 83 |
+
|
| 84 |
+
if track == "objective":
|
| 85 |
+
scores_data = [
|
| 86 |
+
("Overall<br>Score", fmt(overall)),
|
| 87 |
+
("Objective<br>Score", fmt(objective)),
|
| 88 |
+
("Confidence", fmt(confidence)),
|
| 89 |
+
("Correct", "Yes" if correct is True else ("No" if correct is False else "N/A")),
|
| 90 |
+
]
|
| 91 |
+
else:
|
| 92 |
+
scores_data = [
|
| 93 |
+
("Overall<br>Score", fmt(overall)),
|
| 94 |
+
("Subjective<br>Score", fmt(subjective)),
|
| 95 |
+
("Expert<br>Score", fmt(expert)),
|
| 96 |
+
("Citation", fmt(citation)),
|
| 97 |
+
("Source<br>Quality", fmt(source)),
|
| 98 |
+
]
|
| 99 |
+
if subjective is None and any(v is not None for v in [comp, insight, inst, read]):
|
| 100 |
+
scores_data = [
|
| 101 |
+
("Overall<br>Score", fmt(overall)),
|
| 102 |
+
("Comprehen-<br>siveness", fmt(comp)),
|
| 103 |
+
("Insight<br>Score", fmt(insight)),
|
| 104 |
+
("Instruction<br>Following", fmt(inst)),
|
| 105 |
+
("Readability<br>Score", fmt(read)),
|
| 106 |
+
]
|
| 107 |
+
|
| 108 |
html_items_str = ""
|
| 109 |
for title, score in scores_data:
|
|
|
|
| 110 |
html_items_str += f"""
|
| 111 |
<div style="text-align: center; padding: 10px 3px; flex-grow: 1; flex-basis: 19%; min-width: 0;">
|
| 112 |
<h4 style="margin: 0 0 5px 0; font-size: 1em; color: #4a4a4a; font-weight: 600; line-height: 1.2;">{title}</h4>
|
| 113 |
+
<p style="margin: 0; font-size: 1.1em; font-weight: bold; color: #333;">{score}</p>
|
| 114 |
</div>
|
| 115 |
"""
|
| 116 |
return f"""
|
|
|
|
| 120 |
</div>
|
| 121 |
</div>"""
|
| 122 |
|
| 123 |
+
|
| 124 |
# ---------- 生成 Tab ----------
|
| 125 |
def create_data_viewer_side_by_side_tab():
|
| 126 |
with gr.Tab("⚔️Side-by-Side Viewer"):
|
| 127 |
gr.HTML(
|
| 128 |
"""<style>
|
| 129 |
.card{background:#fff;border:1px solid #e0e0e0;border-radius:8px;padding:22px 24px;margin:18px 0;box-shadow:0 2px 4px rgba(0,0,0,.06);}
|
| 130 |
+
.scrollable-sm{max-height:180px;overflow-y:auto;}
|
| 131 |
+
.scrollable-lg{max-height:550px;overflow-y:auto;}
|
| 132 |
.card p{color:#424242 !important;line-height:1.75;margin:0 0 14px 0;text-align:justify;}
|
| 133 |
.card ul,.card ol{margin:12px 0 12px 24px;color:#424242 !important;}
|
| 134 |
.card li{margin:4px 0;color:#424242 !important;}
|
|
|
|
| 159 |
|
| 160 |
def fetch_side_by_side_data(selected_task_display, model_a_name, model_b_name):
|
| 161 |
empty_article = make_article_markdown("")
|
| 162 |
+
empty_scores = make_scores_html({})
|
| 163 |
|
| 164 |
if not selected_task_display:
|
| 165 |
no_task_msg = "请选择一个任务。"
|
|
|
|
| 172 |
(task.get("prompt", "") for task in index.get("tasks", []) if str(task.get("id")) == item_id_str),
|
| 173 |
"任务描述未找到。"
|
| 174 |
)
|
| 175 |
+
user_task_md_content = make_user_task_markdown(item_id_str, task_prompt)
|
|
|
|
|
|
|
|
|
|
| 176 |
|
| 177 |
outputs_a = [make_article_markdown("模型A未选择或数据未找到"), empty_scores]
|
| 178 |
outputs_b = [make_article_markdown("模型B未选择或数据未找到"), empty_scores]
|
|
|
|
| 182 |
if model_a_name:
|
| 183 |
entry_a = entries.get(model_a_name)
|
| 184 |
if entry_a:
|
| 185 |
+
outputs_a[0] = make_article_markdown(entry_a.get("article", ""))
|
| 186 |
+
outputs_a[1] = make_scores_html(entry_a)
|
|
|
|
|
|
|
|
|
|
| 187 |
|
| 188 |
if model_b_name:
|
| 189 |
entry_b = entries.get(model_b_name)
|
| 190 |
if entry_b:
|
| 191 |
+
outputs_b[0] = make_article_markdown(entry_b.get("article", ""))
|
| 192 |
+
outputs_b[1] = make_scores_html(entry_b)
|
|
|
|
|
|
|
|
|
|
| 193 |
|
| 194 |
return user_task_md_content, outputs_a[0], outputs_a[1], outputs_b[0], outputs_b[1]
|
| 195 |
|
|
|
|
| 202 |
def on_load():
|
| 203 |
index = get_index()
|
| 204 |
empty_article = make_article_markdown("")
|
| 205 |
+
empty_scores = make_scores_html({})
|
| 206 |
all_models = index.get("models", [])
|
| 207 |
tasks = index.get("tasks", [])
|
| 208 |
if not all_models or not tasks:
|
|
|
|
| 233 |
model_b_dd.change(fetch_side_by_side_data, inputs=[task_dd, model_a_dd, model_b_dd],
|
| 234 |
outputs=[user_task_display_md, article_a_md, scores_a_html, article_b_md, scores_b_html])
|
| 235 |
|
| 236 |
+
return on_load, [task_dd, model_a_dd, model_b_dd, user_task_display_md, article_a_md, scores_a_html, article_b_md, scores_b_html]
|
tabs/data_viewer_tab.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
# -*- coding: utf-8 -*-
|
| 3 |
"""
|
| 4 |
-
Data-Viewer tab
|
| 5 |
"""
|
| 6 |
|
| 7 |
import gradio as gr
|
|
@@ -10,6 +10,7 @@ import re
|
|
| 10 |
|
| 11 |
from tabs.shared_data import get_entry, get_index
|
| 12 |
|
|
|
|
| 13 |
def make_user_task_markdown(item_id, prompt):
|
| 14 |
return f"""### User Task 🎯
|
| 15 |
|
|
@@ -17,81 +18,109 @@ def make_user_task_markdown(item_id, prompt):
|
|
| 17 |
|
| 18 |
**Description:** {prompt}"""
|
| 19 |
|
|
|
|
| 20 |
def make_article_markdown(article: str) -> str:
|
| 21 |
if article and isinstance(article, str):
|
| 22 |
-
# 首先,标准化已经存在的多个换行符
|
| 23 |
processed_article = re.sub(r'\n{2,}', '\n\n', article)
|
| 24 |
-
|
| 25 |
-
# 保护表格区域
|
| 26 |
table_pattern = r'(\|[^\n]*\n(?:[\|\s\-:]+\n)?(?:\|[^\n]*\n)*)'
|
| 27 |
tables = []
|
|
|
|
| 28 |
def replace_table(match):
|
| 29 |
tables.append(match.group(1))
|
| 30 |
return f'__TABLE_PLACEHOLDER_{len(tables)-1}__'
|
| 31 |
-
|
| 32 |
processed_article = re.sub(table_pattern, replace_table, processed_article)
|
| 33 |
-
|
| 34 |
-
# 处理列表格式:识别 * ** 模式并确保前面有换行
|
| 35 |
-
# 匹配模式:* **标题:** 内容
|
| 36 |
processed_article = re.sub(r'(?<!\n)\*\s*\*\*([^*]+?)\*\*:', r'\n\n* **\1**:', processed_article)
|
| 37 |
-
|
| 38 |
-
# 处理嵌套列表:识别 * ** 后跟 * ** 的模式
|
| 39 |
processed_article = re.sub(r'\*\s*\*\*([^*]+?)\*\*:\s*([^*]*?)\s*\*\s*\*\*', r'* **\1**: \2\n * **', processed_article)
|
| 40 |
-
|
| 41 |
-
# 在引用标记前确保有适当的换行
|
| 42 |
processed_article = re.sub(r'(?<!\n)\[\d+[^\]]*\]\*\s*\*\*', r'\n\n* **', processed_article)
|
| 43 |
-
|
| 44 |
-
# 处理其他孤立的换行符(避免破坏我们刚创建的格式)
|
| 45 |
-
# 但要小心不要影响列表结构
|
| 46 |
lines = processed_article.split('\n')
|
| 47 |
result_lines = []
|
| 48 |
-
|
| 49 |
for i, line in enumerate(lines):
|
| 50 |
result_lines.append(line)
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
line.strip() and
|
| 54 |
lines[i + 1].strip() and
|
| 55 |
not line.strip().startswith('*') and
|
| 56 |
not lines[i + 1].strip().startswith('*') and
|
| 57 |
not line.strip().startswith('#')):
|
| 58 |
-
# 检查是否已经是双换行
|
| 59 |
if i + 1 < len(lines) and lines[i + 1].strip():
|
| 60 |
-
result_lines.append('')
|
| 61 |
-
|
| 62 |
processed_article = '\n'.join(result_lines)
|
| 63 |
-
|
| 64 |
-
# 恢复表格
|
| 65 |
for i, table in enumerate(tables):
|
| 66 |
processed_article = processed_article.replace(f'__TABLE_PLACEHOLDER_{i}__', table)
|
| 67 |
-
|
| 68 |
else:
|
| 69 |
processed_article = article if article is not None else ""
|
| 70 |
-
|
| 71 |
return f"""### Generated Article 📖
|
| 72 |
|
| 73 |
{processed_article}"""
|
| 74 |
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
html_items_str = ""
|
| 85 |
for title, score in scores_data:
|
| 86 |
-
score_value = score if score is not None else "N/A"
|
| 87 |
html_items_str += f"""
|
| 88 |
<div style="text-align: center; padding: 8px 5px; flex-grow: 1; flex-basis: 0;">
|
| 89 |
-
<h4 style="margin: 0 0 6px 0; font-size: 1.
|
| 90 |
-
<p style="margin: 0; font-size: 1.2em; font-weight: bold; color: #333;">{
|
| 91 |
</div>
|
| 92 |
"""
|
| 93 |
-
|
| 94 |
-
# Outer container styled to mimic the .card class from the main CSS block
|
| 95 |
return f"""
|
| 96 |
<div style="background:#fff; border:1px solid #e0e0e0; border-radius:8px; padding: 18px 15px; margin:18px 0; box-shadow:0 2px 4px rgba(0,0,0,.06);">
|
| 97 |
<div style="display: flex; justify-content: space-between; align-items: flex-start;">
|
|
@@ -99,6 +128,7 @@ def make_scores_html(overall, comprehensiveness, insight, instruction, readabili
|
|
| 99 |
</div>
|
| 100 |
</div>"""
|
| 101 |
|
|
|
|
| 102 |
# ---------- 生成 Tab ----------
|
| 103 |
def create_data_viewer_tab():
|
| 104 |
with gr.Tab("🔍Data Viewer"):
|
|
@@ -107,7 +137,7 @@ def create_data_viewer_tab():
|
|
| 107 |
<style>
|
| 108 |
.card{background:#fff;border:1px solid #e0e0e0;border-radius:8px;padding:22px 24px;margin:18px 0;box-shadow:0 2px 4px rgba(0,0,0,.06);}
|
| 109 |
.scrollable-sm{max-height:260px;overflow-y:auto;}
|
| 110 |
-
.scrollable-lg{max-height:700px;overflow-y:auto;}
|
| 111 |
.card p{color:#424242 !important;line-height:1.75;margin:0 0 14px 0;text-align:justify;}
|
| 112 |
.card ul,.card ol{margin:12px 0 12px 24px;color:#424242 !important;}
|
| 113 |
.card li{margin:4px 0;color:#424242 !important;}
|
|
@@ -135,7 +165,7 @@ def create_data_viewer_tab():
|
|
| 135 |
for task in tasks:
|
| 136 |
item_id = str(task["id"])
|
| 137 |
prompt = task.get("prompt", "")
|
| 138 |
-
limit =
|
| 139 |
preview = prompt[:limit] + ("…" if len(prompt) > limit else "")
|
| 140 |
choices.append(f"{item_id}. {preview}")
|
| 141 |
return choices
|
|
@@ -151,14 +181,9 @@ def create_data_viewer_tab():
|
|
| 151 |
err = f"未找到模型 **{model}** 对应任务 **{item_id}** 的内容或分数。"
|
| 152 |
return make_user_task_markdown(item_id, err), make_article_markdown(err), ""
|
| 153 |
|
| 154 |
-
prompt = entry
|
| 155 |
-
article = entry
|
| 156 |
-
|
| 157 |
-
comprehensiveness = entry["comprehensiveness_score"]
|
| 158 |
-
insight = entry["insight_score"]
|
| 159 |
-
instruction = entry["instruction_following_score"]
|
| 160 |
-
readability = entry["readability_score"]
|
| 161 |
-
scores_content = make_scores_html(overall, comprehensiveness, insight, instruction, readability)
|
| 162 |
return make_user_task_markdown(item_id, prompt), make_article_markdown(article), scores_content
|
| 163 |
|
| 164 |
def on_load():
|
|
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
# -*- coding: utf-8 -*-
|
| 3 |
"""
|
| 4 |
+
Data-Viewer tab for ICBCBench.
|
| 5 |
"""
|
| 6 |
|
| 7 |
import gradio as gr
|
|
|
|
| 10 |
|
| 11 |
from tabs.shared_data import get_entry, get_index
|
| 12 |
|
| 13 |
+
|
| 14 |
def make_user_task_markdown(item_id, prompt):
|
| 15 |
return f"""### User Task 🎯
|
| 16 |
|
|
|
|
| 18 |
|
| 19 |
**Description:** {prompt}"""
|
| 20 |
|
| 21 |
+
|
| 22 |
def make_article_markdown(article: str) -> str:
|
| 23 |
if article and isinstance(article, str):
|
|
|
|
| 24 |
processed_article = re.sub(r'\n{2,}', '\n\n', article)
|
| 25 |
+
|
|
|
|
| 26 |
table_pattern = r'(\|[^\n]*\n(?:[\|\s\-:]+\n)?(?:\|[^\n]*\n)*)'
|
| 27 |
tables = []
|
| 28 |
+
|
| 29 |
def replace_table(match):
|
| 30 |
tables.append(match.group(1))
|
| 31 |
return f'__TABLE_PLACEHOLDER_{len(tables)-1}__'
|
| 32 |
+
|
| 33 |
processed_article = re.sub(table_pattern, replace_table, processed_article)
|
|
|
|
|
|
|
|
|
|
| 34 |
processed_article = re.sub(r'(?<!\n)\*\s*\*\*([^*]+?)\*\*:', r'\n\n* **\1**:', processed_article)
|
|
|
|
|
|
|
| 35 |
processed_article = re.sub(r'\*\s*\*\*([^*]+?)\*\*:\s*([^*]*?)\s*\*\s*\*\*', r'* **\1**: \2\n * **', processed_article)
|
|
|
|
|
|
|
| 36 |
processed_article = re.sub(r'(?<!\n)\[\d+[^\]]*\]\*\s*\*\*', r'\n\n* **', processed_article)
|
| 37 |
+
|
|
|
|
|
|
|
| 38 |
lines = processed_article.split('\n')
|
| 39 |
result_lines = []
|
|
|
|
| 40 |
for i, line in enumerate(lines):
|
| 41 |
result_lines.append(line)
|
| 42 |
+
if (i < len(lines) - 1 and
|
| 43 |
+
line.strip() and
|
|
|
|
| 44 |
lines[i + 1].strip() and
|
| 45 |
not line.strip().startswith('*') and
|
| 46 |
not lines[i + 1].strip().startswith('*') and
|
| 47 |
not line.strip().startswith('#')):
|
|
|
|
| 48 |
if i + 1 < len(lines) and lines[i + 1].strip():
|
| 49 |
+
result_lines.append('')
|
| 50 |
+
|
| 51 |
processed_article = '\n'.join(result_lines)
|
|
|
|
|
|
|
| 52 |
for i, table in enumerate(tables):
|
| 53 |
processed_article = processed_article.replace(f'__TABLE_PLACEHOLDER_{i}__', table)
|
|
|
|
| 54 |
else:
|
| 55 |
processed_article = article if article is not None else ""
|
| 56 |
+
|
| 57 |
return f"""### Generated Article 📖
|
| 58 |
|
| 59 |
{processed_article}"""
|
| 60 |
|
| 61 |
+
|
| 62 |
+
def make_scores_html(entry: dict) -> str:
|
| 63 |
+
"""Build score cards for ICBCBench data viewer."""
|
| 64 |
+
track = entry.get("track", "subjective")
|
| 65 |
+
|
| 66 |
+
# ICBCBench fields
|
| 67 |
+
overall = entry.get("overall_score")
|
| 68 |
+
objective = entry.get("objective_score")
|
| 69 |
+
subjective = entry.get("subjective_score")
|
| 70 |
+
expert = entry.get("expert_score")
|
| 71 |
+
citation = entry.get("citation_score")
|
| 72 |
+
source = entry.get("source_quality_score")
|
| 73 |
+
confidence = entry.get("confidence")
|
| 74 |
+
correct = entry.get("correct")
|
| 75 |
+
|
| 76 |
+
# Legacy DeepResearch Bench fields
|
| 77 |
+
comp = entry.get("comprehensiveness_score")
|
| 78 |
+
insight = entry.get("insight_score")
|
| 79 |
+
inst = entry.get("instruction_following_score")
|
| 80 |
+
read = entry.get("readability_score")
|
| 81 |
+
|
| 82 |
+
def fmt(val):
|
| 83 |
+
if val is None:
|
| 84 |
+
return "N/A"
|
| 85 |
+
try:
|
| 86 |
+
return f"{float(val):.2f}"
|
| 87 |
+
except (TypeError, ValueError):
|
| 88 |
+
return str(val)
|
| 89 |
+
|
| 90 |
+
if track == "objective":
|
| 91 |
+
scores_data = [
|
| 92 |
+
("Overall<br>Score", fmt(overall)),
|
| 93 |
+
("Objective<br>Score", fmt(objective)),
|
| 94 |
+
("Confidence", fmt(confidence)),
|
| 95 |
+
("Correct", "Yes" if correct is True else ("No" if correct is False else "N/A")),
|
| 96 |
+
]
|
| 97 |
+
else:
|
| 98 |
+
scores_data = [
|
| 99 |
+
("Overall<br>Score", fmt(overall)),
|
| 100 |
+
("Subjective<br>Score", fmt(subjective)),
|
| 101 |
+
("Expert<br>Score", fmt(expert)),
|
| 102 |
+
("Citation", fmt(citation)),
|
| 103 |
+
("Source<br>Quality", fmt(source)),
|
| 104 |
+
]
|
| 105 |
+
# Add legacy dimensions if ICBCBench fields not available
|
| 106 |
+
if subjective is None and any(v is not None for v in [comp, insight, inst, read]):
|
| 107 |
+
scores_data = [
|
| 108 |
+
("Overall<br>Score", fmt(overall)),
|
| 109 |
+
("Comprehen-<br>siveness", fmt(comp)),
|
| 110 |
+
("Insight<br>Score", fmt(insight)),
|
| 111 |
+
("Instruction<br>Following", fmt(inst)),
|
| 112 |
+
("Readability<br>Score", fmt(read)),
|
| 113 |
+
]
|
| 114 |
+
|
| 115 |
html_items_str = ""
|
| 116 |
for title, score in scores_data:
|
|
|
|
| 117 |
html_items_str += f"""
|
| 118 |
<div style="text-align: center; padding: 8px 5px; flex-grow: 1; flex-basis: 0;">
|
| 119 |
+
<h4 style="margin: 0 0 6px 0; font-size: 1.1em; color: #4a4a4a; font-weight: 600;">{title}</h4>
|
| 120 |
+
<p style="margin: 0; font-size: 1.2em; font-weight: bold; color: #333;">{score}</p>
|
| 121 |
</div>
|
| 122 |
"""
|
| 123 |
+
|
|
|
|
| 124 |
return f"""
|
| 125 |
<div style="background:#fff; border:1px solid #e0e0e0; border-radius:8px; padding: 18px 15px; margin:18px 0; box-shadow:0 2px 4px rgba(0,0,0,.06);">
|
| 126 |
<div style="display: flex; justify-content: space-between; align-items: flex-start;">
|
|
|
|
| 128 |
</div>
|
| 129 |
</div>"""
|
| 130 |
|
| 131 |
+
|
| 132 |
# ---------- 生成 Tab ----------
|
| 133 |
def create_data_viewer_tab():
|
| 134 |
with gr.Tab("🔍Data Viewer"):
|
|
|
|
| 137 |
<style>
|
| 138 |
.card{background:#fff;border:1px solid #e0e0e0;border-radius:8px;padding:22px 24px;margin:18px 0;box-shadow:0 2px 4px rgba(0,0,0,.06);}
|
| 139 |
.scrollable-sm{max-height:260px;overflow-y:auto;}
|
| 140 |
+
.scrollable-lg{max-height:700px;overflow-y:auto;}
|
| 141 |
.card p{color:#424242 !important;line-height:1.75;margin:0 0 14px 0;text-align:justify;}
|
| 142 |
.card ul,.card ol{margin:12px 0 12px 24px;color:#424242 !important;}
|
| 143 |
.card li{margin:4px 0;color:#424242 !important;}
|
|
|
|
| 165 |
for task in tasks:
|
| 166 |
item_id = str(task["id"])
|
| 167 |
prompt = task.get("prompt", "")
|
| 168 |
+
limit = 60
|
| 169 |
preview = prompt[:limit] + ("…" if len(prompt) > limit else "")
|
| 170 |
choices.append(f"{item_id}. {preview}")
|
| 171 |
return choices
|
|
|
|
| 181 |
err = f"未找到模型 **{model}** 对应任务 **{item_id}** 的内容或分数。"
|
| 182 |
return make_user_task_markdown(item_id, err), make_article_markdown(err), ""
|
| 183 |
|
| 184 |
+
prompt = entry.get("prompt", "")
|
| 185 |
+
article = entry.get("article", "")
|
| 186 |
+
scores_content = make_scores_html(entry)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 187 |
return make_user_task_markdown(item_id, prompt), make_article_markdown(article), scores_content
|
| 188 |
|
| 189 |
def on_load():
|
tabs/leaderboard_tab.py
CHANGED
|
@@ -9,294 +9,494 @@ BASE_DIR = Path(__file__).resolve().parent.parent
|
|
| 9 |
DATA_PATH = BASE_DIR / "data" / "leaderboard.csv"
|
| 10 |
|
| 11 |
# 用于标注的常量
|
| 12 |
-
CATEGORY_TO_HIGHLIGHT = "
|
| 13 |
HIGHLIGHT_EMOJI = "🚀"
|
| 14 |
|
| 15 |
-
# 列名重命名映射
|
| 16 |
COLUMN_RENAME_MAP = {
|
| 17 |
-
'
|
| 18 |
-
'
|
| 19 |
-
'
|
| 20 |
-
'
|
| 21 |
-
'
|
| 22 |
-
'
|
| 23 |
-
'
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
}
|
| 25 |
|
|
|
|
| 26 |
MODEL_DISPLAY_NAMES = {
|
| 27 |
-
"
|
| 28 |
-
"
|
| 29 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
"onyx": "Onyx Deep Research",
|
| 31 |
-
"raaa-deep-research": "
|
| 32 |
-
"deepsynth": "
|
| 33 |
-
"
|
| 34 |
-
"nvidia-aiq-nemotron-gpt52-updated": "
|
| 35 |
-
"deepinsight": "CMCC
|
| 36 |
"drb_cellcog": "Cellcog",
|
| 37 |
-
"
|
| 38 |
"RecallRadar": "RecallRadar Intelligence",
|
| 39 |
-
"ms_deepresearch": "MS-Agent Agentic Insight
|
| 40 |
-
"TrajectoryKit": "TrajectoryKit
|
| 41 |
-
"1688AILab-DeepResearch-
|
| 42 |
"zhipu_deep_research": "Zhipu Deep Research",
|
| 43 |
-
"grep-
|
| 44 |
-
"MindDR-V1.5": "LiAuto Mind DeepResearch
|
| 45 |
-
"deepdog": "Deep Dog
|
| 46 |
-
"Link": "iFlow
|
| 47 |
"octen-deepresearch-0508": "Octen Deep Research",
|
| 48 |
-
"ZTE-Nebula-DeepResearch-V20260519": "ZTE
|
| 49 |
-
"WhaleCloud-DocChain": "WhaleCloud
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
}
|
| 51 |
|
| 52 |
# 模型分类映射
|
| 53 |
MODEL_CATEGORIES = {
|
| 54 |
-
"
|
| 55 |
-
"gemini-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
"openai-deepresearch",
|
| 57 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
"langchain-open-deep-research-gpt-5",
|
| 59 |
-
"cellcog",
|
| 60 |
"salesforce-air-deep-research",
|
| 61 |
"thinkdepthai-deepresearch",
|
| 62 |
"tavily-research",
|
| 63 |
-
"qianfan_deepresearch_0430",
|
| 64 |
"onyx",
|
| 65 |
"raaa-deep-research",
|
| 66 |
"deepsynth",
|
| 67 |
-
"
|
| 68 |
"nvidia-aiq-nemotron-gpt52-updated",
|
| 69 |
"deepinsight",
|
| 70 |
"drb_cellcog",
|
| 71 |
-
"
|
| 72 |
"RecallRadar",
|
| 73 |
"ms_deepresearch",
|
| 74 |
"TrajectoryKit",
|
| 75 |
-
"1688AILab-DeepResearch-
|
| 76 |
"zhipu_deep_research",
|
| 77 |
-
"
|
| 78 |
-
"grep-v5",
|
| 79 |
"MindDR-V1.5",
|
| 80 |
"deepdog",
|
| 81 |
"Link",
|
| 82 |
"octen-deepresearch-0508",
|
| 83 |
"ZTE-Nebula-DeepResearch-V20260519",
|
| 84 |
-
"WhaleCloud-DocChain"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
],
|
| 86 |
-
"LLM with Search": [
|
| 87 |
-
]
|
| 88 |
}
|
| 89 |
|
| 90 |
-
# 模型链接映射
|
| 91 |
MODEL_LINKS = {
|
| 92 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
"gemini-2.5-pro-deepresearch": "https://gemini.google/overview/deep-research/",
|
| 94 |
-
"
|
| 95 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
"langchain-open-deep-research-gpt-5": "https://github.com/langchain-ai/open_deep_research",
|
| 97 |
-
"
|
| 98 |
-
"salesforce-air-deep-research": "https://github.com/SalesforceAIResearch/enterprise-deep-research ",
|
| 99 |
"thinkdepthai-deepresearch": "https://github.com/thinkdepthai/Deep_Research",
|
| 100 |
"tavily-research": "https://deepresearch.tavily.com",
|
| 101 |
-
"qianfan_deepresearch_0430": "https://console.bce.baidu.com/qianfan/studio/officialApp/deepResearch/69a72b3f-a18c-2f02-9ba0-007beb95b315",
|
| 102 |
"onyx": "https://github.com/onyx-dot-app/onyx",
|
| 103 |
-
"raaa-deep-research": "https://github.com/wee235929-cmyk/RequirementAgent",
|
| 104 |
"deepsynth": "https://www.publicissapient.com/platforms/bodhi",
|
| 105 |
-
"xiaoyi": "https://xiaoyi.huawei.com/chat/research",
|
| 106 |
-
"nvidia-aiq-nemotron-gpt52-updated": "https://github.com/NVIDIA-AI-Blueprints/aiq/tree/drb1",
|
| 107 |
-
"deepinsight": "http://81.70.174.140:3000/",
|
| 108 |
-
"drb_cellcog": "https://www.cellcog.ai/",
|
| 109 |
-
"cellcog-max": "https://www.cellcog.ai/",
|
| 110 |
-
"RecallRadar": "https://getrecallradar.com",
|
| 111 |
"ms_deepresearch": "https://github.com/modelscope/ms-agent",
|
| 112 |
"TrajectoryKit": "https://github.com/KabakaWilliam/trajectorykit",
|
| 113 |
-
"1688AILab-DeepResearch-0428": "https://air.1688.com/kapp/1688-ai-app/pages/home",
|
| 114 |
"zhipu_deep_research": "https://research-hb.zhipuai-infra.cn/",
|
| 115 |
-
"ms_deepresearch_gpt52mixqwen35_09_edit_restart09_think_medium": "https://github.com/modelscope/ms-agent",
|
| 116 |
-
"grep-v5": "https://grep.ai",
|
| 117 |
"MindDR-V1.5": "https://www.lixiang.com/tech/mindgpt",
|
| 118 |
"deepdog": "https://github.com/beneadie/DeepDog_1",
|
| 119 |
"Link": "https://iflow.cn/public/iflow-researcher.html",
|
| 120 |
-
"octen-deepresearch-0508": "https://octen.ai/platform/deep-research",
|
| 121 |
"ZTE-Nebula-DeepResearch-V20260519": "https://github.com/Adlik/ZTE-Nebula-DeepResearch",
|
| 122 |
"WhaleCloud-DocChain": "https://lab.hjcloud.com/llmdoc/login?redirect=%2Fkagent",
|
| 123 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
}
|
| 125 |
|
| 126 |
# 模型许可证类型映射
|
| 127 |
MODEL_LICENSE_TYPE = {
|
| 128 |
-
|
| 129 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
"openai-deepresearch": "Proprietary",
|
| 131 |
-
"
|
|
|
|
|
|
|
|
|
|
| 132 |
"langchain-open-deep-research-gpt-5": "MIT",
|
| 133 |
-
"
|
| 134 |
-
"salesforce-air-deep-research": "Apache-2.0 license",
|
| 135 |
"thinkdepthai-deepresearch": "MIT",
|
| 136 |
"tavily-research": "Proprietary",
|
| 137 |
-
"qianfan_deepresearch_0430": "Proprietary",
|
| 138 |
"onyx": "MIT (partial EE)",
|
| 139 |
"raaa-deep-research": "Proprietary",
|
| 140 |
"deepsynth": "Proprietary",
|
| 141 |
-
"
|
| 142 |
-
"nvidia-aiq-nemotron-gpt52-updated": "Apache-2.0
|
| 143 |
"deepinsight": "Proprietary",
|
| 144 |
"drb_cellcog": "Proprietary",
|
| 145 |
-
"
|
| 146 |
"RecallRadar": "Proprietary",
|
| 147 |
-
"ms_deepresearch": "Apache-2.0
|
| 148 |
"TrajectoryKit": "MIT",
|
| 149 |
-
"1688AILab-DeepResearch-
|
| 150 |
"zhipu_deep_research": "Proprietary",
|
| 151 |
-
"
|
| 152 |
-
"grep-v5": "Proprietary",
|
| 153 |
"MindDR-V1.5": "Proprietary",
|
| 154 |
"deepdog": "MIT",
|
| 155 |
"Link": "TBD",
|
| 156 |
"octen-deepresearch-0508": "Proprietary",
|
| 157 |
"ZTE-Nebula-DeepResearch-V20260519": "Proprietary",
|
| 158 |
"WhaleCloud-DocChain": "Proprietary",
|
| 159 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 160 |
}
|
| 161 |
|
|
|
|
| 162 |
def load_leaderboard() -> pd.DataFrame:
|
| 163 |
if not DATA_PATH.exists():
|
| 164 |
raise FileNotFoundError(
|
| 165 |
f"Leaderboard file not found: {DATA_PATH}.\n"
|
| 166 |
-
"→ 先运行 rank_leaderboard.py 生成 data/leaderboard.csv"
|
| 167 |
)
|
| 168 |
df = pd.read_csv(DATA_PATH)
|
| 169 |
df.columns = [c.strip() for c in df.columns]
|
| 170 |
-
|
| 171 |
def get_category(model_name):
|
| 172 |
for category, models in MODEL_CATEGORIES.items():
|
| 173 |
if model_name in models:
|
| 174 |
return category
|
|
|
|
|
|
|
|
|
|
|
|
|
| 175 |
return "Others"
|
| 176 |
-
|
| 177 |
def get_license_type(model_name):
|
| 178 |
-
|
| 179 |
-
|
|
|
|
|
|
|
|
|
|
| 180 |
df['category'] = df['model'].apply(get_category)
|
| 181 |
df['license_type'] = df['model'].apply(get_license_type)
|
| 182 |
return df
|
| 183 |
|
|
|
|
| 184 |
def make_ranked(df: pd.DataFrame) -> pd.DataFrame:
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
ranked
|
| 188 |
-
|
|
|
|
| 189 |
medal_styles = {
|
| 190 |
-
1: {'icon': '🥇', 'color': '#FFD700'},
|
| 191 |
-
2: {'icon': '🥈', 'color': '#C0C0C0'},
|
| 192 |
-
3: {'icon': '🥉', 'color': '#CD7F32'}
|
| 193 |
}
|
| 194 |
-
|
| 195 |
def format_rank_with_medal(rank_num):
|
| 196 |
if rank_num in medal_styles:
|
| 197 |
style = medal_styles[rank_num]
|
| 198 |
-
# 数字加粗+颜色,奖牌图标放大
|
| 199 |
return (
|
| 200 |
f'<span style="font-weight: bold; color: {style["color"]};">{rank_num}</span> '
|
| 201 |
f'<span style="font-size: 1.5em;">{style["icon"]}</span>'
|
| 202 |
)
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
ranked['Rank'] = ranked['Rank'].apply(format_rank_with_medal)
|
| 207 |
-
|
| 208 |
# 重命名列名为简写形式
|
| 209 |
ranked = ranked.rename(columns=COLUMN_RENAME_MAP)
|
| 210 |
-
|
| 211 |
-
# 格式化数值列为两位小数,
|
| 212 |
-
numeric_columns =
|
| 213 |
for col in numeric_columns:
|
| 214 |
if col in ranked.columns:
|
| 215 |
-
# 只对数值进行round操作,保持"-"不变
|
| 216 |
ranked[col] = ranked[col].apply(
|
| 217 |
lambda x: round(float(x), 2) if x != "-" and pd.notna(x) else x
|
| 218 |
)
|
| 219 |
-
|
| 220 |
# 为模型添加链接和高亮样式
|
| 221 |
def format_model_name(row):
|
| 222 |
model_name = row['model']
|
| 223 |
-
|
| 224 |
-
display_model_name
|
|
|
|
| 225 |
link = MODEL_LINKS.get(model_name, "")
|
| 226 |
-
|
| 227 |
-
|
|
|
|
| 228 |
if row['category'] == CATEGORY_TO_HIGHLIGHT:
|
| 229 |
-
display_name = f'<span style="color: #
|
| 230 |
else:
|
| 231 |
display_name = display_model_name
|
| 232 |
-
|
| 233 |
-
# 如果有链接,包装成<a>标签
|
| 234 |
if link and link.strip():
|
| 235 |
return f'<a href="{link}" target="_blank" style="text-decoration: none;">{display_name}</a>'
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
return f'<span class="model-name" data-model="{model_name}">{display_name}</span>'
|
| 239 |
-
|
| 240 |
ranked['model'] = ranked.apply(format_model_name, axis=1)
|
| 241 |
-
|
| 242 |
return ranked
|
| 243 |
|
|
|
|
| 244 |
def filter_data(search_text: str, selected_categories: list):
|
| 245 |
df = load_leaderboard()
|
| 246 |
-
|
| 247 |
if search_text.strip():
|
| 248 |
df = df[df['model'].str.contains(search_text.strip(), case=False, na=False)]
|
| 249 |
-
|
| 250 |
if selected_categories:
|
| 251 |
df = df[df['category'].isin(selected_categories)]
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
|
| 256 |
def create_leaderboard_tab():
|
| 257 |
-
with gr.Tab("🏆Leaderboard
|
| 258 |
with gr.Row():
|
| 259 |
with gr.Column(scale=1):
|
| 260 |
search_box = gr.Textbox(
|
| 261 |
-
label="Model Search",
|
| 262 |
placeholder="Entering model name to search...",
|
| 263 |
value=""
|
| 264 |
)
|
| 265 |
with gr.Column(scale=2):
|
| 266 |
category_checkboxes = gr.CheckboxGroup(
|
| 267 |
label="Model Categories",
|
| 268 |
-
choices=list(MODEL_CATEGORIES.keys()),
|
| 269 |
-
value=list(MODEL_CATEGORIES.keys())
|
| 270 |
)
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
column_count = len(initial_df.columns)
|
| 277 |
datatypes = ["str"] * column_count
|
| 278 |
model_col_index = initial_df.columns.get_loc('model')
|
| 279 |
rank_col_index = initial_df.columns.get_loc('Rank')
|
| 280 |
datatypes[model_col_index] = "html"
|
| 281 |
-
datatypes[rank_col_index] = "html"
|
| 282 |
-
|
| 283 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 284 |
table = gr.Dataframe(
|
| 285 |
value=initial_df,
|
| 286 |
-
datatype=datatypes,
|
| 287 |
-
max_height=600,
|
| 288 |
-
show_label=False,
|
| 289 |
-
elem_id="leaderboard_table",
|
| 290 |
-
interactive=False,
|
| 291 |
-
wrap=True,
|
| 292 |
-
column_widths=
|
| 293 |
)
|
| 294 |
|
| 295 |
def update_display(search_text, selected_categories):
|
| 296 |
-
|
| 297 |
-
return df
|
| 298 |
|
| 299 |
-
# 绑定搜索框和复选框的变化事件
|
| 300 |
search_box.change(
|
| 301 |
fn=update_display,
|
| 302 |
inputs=[search_box, category_checkboxes],
|
|
@@ -307,24 +507,26 @@ def create_leaderboard_tab():
|
|
| 307 |
inputs=[search_box, category_checkboxes],
|
| 308 |
outputs=table
|
| 309 |
)
|
| 310 |
-
|
| 311 |
# 在底部添加说明
|
| 312 |
with gr.Row():
|
| 313 |
gr.Markdown(f"""
|
| 314 |
### 📊 Column Descriptions
|
| 315 |
- **Rank**: Model ranking based on overall score
|
| 316 |
-
- **model**: Model name (<span style="color: #
|
| 317 |
-
- **overall**:
|
| 318 |
-
- **
|
| 319 |
-
- **
|
| 320 |
-
- **
|
| 321 |
-
- **
|
| 322 |
-
- **
|
| 323 |
-
- **
|
|
|
|
|
|
|
| 324 |
- **category**: Model category
|
| 325 |
- **license_type**: The software license type of the model/service
|
| 326 |
-
|
| 327 |
-
💡 **Tip**: Model names are clickable when links are available. Visit the
|
| 328 |
""")
|
| 329 |
-
|
| 330 |
return search_box
|
|
|
|
| 9 |
DATA_PATH = BASE_DIR / "data" / "leaderboard.csv"
|
| 10 |
|
| 11 |
# 用于标注的常量
|
| 12 |
+
CATEGORY_TO_HIGHLIGHT = "Closed-source DRA"
|
| 13 |
HIGHLIGHT_EMOJI = "🚀"
|
| 14 |
|
| 15 |
+
# 列名重命名映射(CSV column -> display column)
|
| 16 |
COLUMN_RENAME_MAP = {
|
| 17 |
+
'overall': 'overall',
|
| 18 |
+
'objective_en': 'obj.EN',
|
| 19 |
+
'objective_zh': 'obj.ZH',
|
| 20 |
+
'objective_avg': 'obj.avg',
|
| 21 |
+
'subjective_en': 'subj.EN',
|
| 22 |
+
'subjective_zh': 'subj.ZH',
|
| 23 |
+
'subjective_avg': 'subj.avg',
|
| 24 |
+
'expert_avg': 'expert',
|
| 25 |
+
'citation_score': 'citation',
|
| 26 |
+
'source_quality': 'source',
|
| 27 |
+
'rmsce': 'RMSCE',
|
| 28 |
}
|
| 29 |
|
| 30 |
+
# 模型显示名称(ICBCBench 参赛模型)
|
| 31 |
MODEL_DISPLAY_NAMES = {
|
| 32 |
+
"deerflow-gpt55": "DeerFlow (+GPT-5.5)",
|
| 33 |
+
"DeerFlow(+GPT-5.5)": "DeerFlow (+GPT-5.5)",
|
| 34 |
+
"openclaw-gpt55": "OpenClaw (+GPT-5.5)",
|
| 35 |
+
"OpenClaw(+GPT-5.5)": "OpenClaw (+GPT-5.5)",
|
| 36 |
+
"openclaw-deepseek-v4-pro": "OpenClaw (+DeepSeek-V4-Pro)",
|
| 37 |
+
"OpenClaw(+DeepSeek-V4-Pro)": "OpenClaw (+DeepSeek-V4-Pro)",
|
| 38 |
+
"deerflow-deepseek-v4-pro": "DeerFlow (+DeepSeek-V4-Pro)",
|
| 39 |
+
"DeerFlow(+DeepSeek-V4-Pro)": "DeerFlow (+DeepSeek-V4-Pro)",
|
| 40 |
+
"gemini-deep-research": "Gemini Deep Research",
|
| 41 |
+
"Gemini-deep-research": "Gemini Deep Research",
|
| 42 |
+
"openai-o3-deep-research": "OpenAI o3 Deep Research",
|
| 43 |
+
"OpenAI-o3-deep-research": "OpenAI o3 Deep Research",
|
| 44 |
+
"kimi-deep-research": "Kimi Deep Research",
|
| 45 |
+
"doubao-deep-research": "Doubao Deep Research",
|
| 46 |
+
"gpt-5.5": "GPT-5.5",
|
| 47 |
+
"GPT-5.5": "GPT-5.5",
|
| 48 |
+
"claude-opus-4-7": "Claude Opus 4.7",
|
| 49 |
+
"Claude-opus-4-7": "Claude Opus 4.7",
|
| 50 |
+
"perplexity-deep-research": "Perplexity Deep Research",
|
| 51 |
+
"Perplexity-deep-research": "Perplexity Deep Research",
|
| 52 |
+
"gemini-3.1-pro-preview": "Gemini 3.1 Pro Preview",
|
| 53 |
+
"Gemini-3.1-pro-preview": "Gemini 3.1 Pro Preview",
|
| 54 |
+
"grok-3-deepsearch": "Grok 3 DeepSearch",
|
| 55 |
+
"Grok-3-deepsearch": "Grok 3 DeepSearch",
|
| 56 |
+
"qwen-deep-research": "Qwen Deep Research",
|
| 57 |
+
"Qwen-deep-research": "Qwen Deep Research",
|
| 58 |
+
"openai-deepresearch": "OpenAI Deep Research",
|
| 59 |
+
"gemini-2.5-pro-deepresearch": "Gemini 2.5 Pro Deep Research",
|
| 60 |
+
"perplexity-Research": "Perplexity Research",
|
| 61 |
+
"grok-deeper-search": "Grok Deeper Search",
|
| 62 |
+
"mirothinker": "MiroThinker",
|
| 63 |
+
"MiroThinker": "MiroThinker",
|
| 64 |
+
"jina-deepsearch": "Jina DeepSearch",
|
| 65 |
+
"Jina-deepsearch": "Jina DeepSearch",
|
| 66 |
+
"kimi-k2.5": "Kimi K2.5",
|
| 67 |
+
"Kimi-k2.5": "Kimi K2.5",
|
| 68 |
+
"deepseek-v4-pro": "DeepSeek V4 Pro",
|
| 69 |
+
"DeepSeek-V4-Pro": "DeepSeek V4 Pro",
|
| 70 |
+
"tongyi-deepresearch-30b-a3b": "Tongyi Deep Research 30B-A3B",
|
| 71 |
+
"Tongyi-deepresearch-30b-a3b": "Tongyi Deep Research 30B-A3B",
|
| 72 |
+
"langchain-open-deep-research": "LangChain Open Deep Research",
|
| 73 |
+
"langchain-open-deep-research-gpt-5": "LangChain Open Deep Research (GPT-5)",
|
| 74 |
+
"salesforce-air-deep-research": "Salesforce AIR Deep Research",
|
| 75 |
+
"thinkdepthai-deepresearch": "ThinkDepthAI Deep Research",
|
| 76 |
+
"tavily-research": "Tavily Research",
|
| 77 |
"onyx": "Onyx Deep Research",
|
| 78 |
+
"raaa-deep-research": "RAAA Deep Research",
|
| 79 |
+
"deepsynth": "Deepsynth / Bodhi",
|
| 80 |
+
"xiaoyi_research_agent_0304": "Xiaoyi Deep Research",
|
| 81 |
+
"nvidia-aiq-nemotron-gpt52-updated": "NVIDIA AIQ (Nemotron)",
|
| 82 |
+
"deepinsight": "CMCC DeepInsight",
|
| 83 |
"drb_cellcog": "Cellcog",
|
| 84 |
+
"drb_cellcog_max": "Cellcog Max",
|
| 85 |
"RecallRadar": "RecallRadar Intelligence",
|
| 86 |
+
"ms_deepresearch": "MS-Agent Agentic Insight",
|
| 87 |
+
"TrajectoryKit": "TrajectoryKit",
|
| 88 |
+
"1688AILab-DeepResearch-0325": "1688AI Lab Deep Research",
|
| 89 |
"zhipu_deep_research": "Zhipu Deep Research",
|
| 90 |
+
"grep-v4": "Grep Deep Research",
|
| 91 |
+
"MindDR-V1.5": "LiAuto Mind DeepResearch",
|
| 92 |
+
"deepdog": "Deep Dog",
|
| 93 |
+
"Link": "iFlow Researcher",
|
| 94 |
"octen-deepresearch-0508": "Octen Deep Research",
|
| 95 |
+
"ZTE-Nebula-DeepResearch-V20260519": "ZTE Nebula DeepResearch",
|
| 96 |
+
"WhaleCloud-DocChain": "WhaleCloud DocChain",
|
| 97 |
+
"qianfan_deepresearch_0430": "Baidu Qianfan DeepResearch",
|
| 98 |
+
"doubao-deepresearch": "Doubao Deep Research",
|
| 99 |
+
"kimi-researcher": "Kimi Researcher",
|
| 100 |
+
"claude-research": "Claude Research",
|
| 101 |
+
"dr-tulu": "DR-Tulu",
|
| 102 |
+
"nvidia-aiq-research-assistant": "NVIDIA AIQ Research Assistant",
|
| 103 |
+
"tongyi-deepresearch-30B-A3B": "Tongyi Deep Research",
|
| 104 |
+
"sonar-reasoning-pro": "Sonar Reasoning Pro",
|
| 105 |
+
"sonar-reasoning": "Sonar Reasoning",
|
| 106 |
+
"sonar-pro": "Sonar Pro",
|
| 107 |
+
"claude-3-7-sonnet-with-search": "Claude 3.7 Sonnet with Search",
|
| 108 |
+
"gpt-4o-search-preview": "GPT-4o Search Preview",
|
| 109 |
}
|
| 110 |
|
| 111 |
# 模型分类映射
|
| 112 |
MODEL_CATEGORIES = {
|
| 113 |
+
"Closed-source DRA": [
|
| 114 |
+
"gemini-deep-research",
|
| 115 |
+
"Gemini-deep-research",
|
| 116 |
+
"openai-o3-deep-research",
|
| 117 |
+
"OpenAI-o3-deep-research",
|
| 118 |
+
"kimi-deep-research",
|
| 119 |
+
"Kimi-deep-research",
|
| 120 |
+
"doubao-deep-research",
|
| 121 |
+
"Doubao-deep-research",
|
| 122 |
+
"gpt-5.5",
|
| 123 |
+
"GPT-5.5",
|
| 124 |
+
"claude-opus-4-7",
|
| 125 |
+
"Claude-opus-4-7",
|
| 126 |
+
"perplexity-deep-research",
|
| 127 |
+
"Perplexity-deep-research",
|
| 128 |
+
"gemini-3.1-pro-preview",
|
| 129 |
+
"Gemini-3.1-pro-preview",
|
| 130 |
+
"grok-3-deepsearch",
|
| 131 |
+
"Grok-3-deepsearch",
|
| 132 |
+
"qwen-deep-research",
|
| 133 |
+
"Qwen-deep-research",
|
| 134 |
+
# Legacy DeepResearch Bench models
|
| 135 |
"openai-deepresearch",
|
| 136 |
+
"gemini-2.5-pro-deepresearch",
|
| 137 |
+
"perplexity-Research",
|
| 138 |
+
"grok-deeper-search",
|
| 139 |
+
"Grok-deeper-search",
|
| 140 |
+
"claude-3-7-sonnet-with-search",
|
| 141 |
+
"gpt-4o-search-preview",
|
| 142 |
+
],
|
| 143 |
+
"Open Agentic Framework": [
|
| 144 |
+
"deerflow-gpt55",
|
| 145 |
+
"DeerFlow(+GPT-5.5)",
|
| 146 |
+
"openclaw-gpt55",
|
| 147 |
+
"OpenClaw(+GPT-5.5)",
|
| 148 |
+
"openclaw-deepseek-v4-pro",
|
| 149 |
+
"OpenClaw(+DeepSeek-V4-Pro)",
|
| 150 |
+
"deerflow-deepseek-v4-pro",
|
| 151 |
+
"DeerFlow(+DeepSeek-V4-Pro)",
|
| 152 |
+
"mirothinker",
|
| 153 |
+
"MiroThinker",
|
| 154 |
+
"jina-deepsearch",
|
| 155 |
+
"Jina-deepsearch",
|
| 156 |
+
# Legacy DeepResearch Bench models
|
| 157 |
+
"langchain-open-deep-research",
|
| 158 |
"langchain-open-deep-research-gpt-5",
|
|
|
|
| 159 |
"salesforce-air-deep-research",
|
| 160 |
"thinkdepthai-deepresearch",
|
| 161 |
"tavily-research",
|
|
|
|
| 162 |
"onyx",
|
| 163 |
"raaa-deep-research",
|
| 164 |
"deepsynth",
|
| 165 |
+
"xiaoyi_research_agent_0304",
|
| 166 |
"nvidia-aiq-nemotron-gpt52-updated",
|
| 167 |
"deepinsight",
|
| 168 |
"drb_cellcog",
|
| 169 |
+
"drb_cellcog_max",
|
| 170 |
"RecallRadar",
|
| 171 |
"ms_deepresearch",
|
| 172 |
"TrajectoryKit",
|
| 173 |
+
"1688AILab-DeepResearch-0325",
|
| 174 |
"zhipu_deep_research",
|
| 175 |
+
"grep-v4",
|
|
|
|
| 176 |
"MindDR-V1.5",
|
| 177 |
"deepdog",
|
| 178 |
"Link",
|
| 179 |
"octen-deepresearch-0508",
|
| 180 |
"ZTE-Nebula-DeepResearch-V20260519",
|
| 181 |
+
"WhaleCloud-DocChain",
|
| 182 |
+
"qianfan_deepresearch_0430",
|
| 183 |
+
],
|
| 184 |
+
"Open-weight LLM": [
|
| 185 |
+
"kimi-k2.5",
|
| 186 |
+
"Kimi-k2.5",
|
| 187 |
+
"deepseek-v4-pro",
|
| 188 |
+
"DeepSeek-V4-Pro",
|
| 189 |
+
"tongyi-deepresearch-30b-a3b",
|
| 190 |
+
"Tongyi-deepresearch-30b-a3b",
|
| 191 |
+
# Legacy DeepResearch Bench models
|
| 192 |
+
"doubao-deepresearch",
|
| 193 |
+
"kimi-researcher",
|
| 194 |
+
"claude-research",
|
| 195 |
+
"dr-tulu",
|
| 196 |
+
"nvidia-aiq-research-assistant",
|
| 197 |
+
"tongyi-deepresearch-30B-A3B",
|
| 198 |
+
"sonar-reasoning-pro",
|
| 199 |
+
"sonar-reasoning",
|
| 200 |
+
"sonar-pro",
|
| 201 |
+
"claude-3-7-sonnet-with-search",
|
| 202 |
+
"gpt-4o-search-preview",
|
| 203 |
],
|
|
|
|
|
|
|
| 204 |
}
|
| 205 |
|
| 206 |
+
# 模型链接映射
|
| 207 |
MODEL_LINKS = {
|
| 208 |
+
"gemini-deep-research": "https://gemini.google/overview/deep-research/",
|
| 209 |
+
"Gemini-deep-research": "https://gemini.google/overview/deep-research/",
|
| 210 |
+
"openai-o3-deep-research": "https://openai.com/index/introducing-deep-research/",
|
| 211 |
+
"OpenAI-o3-deep-research": "https://openai.com/index/introducing-deep-research/",
|
| 212 |
+
"kimi-deep-research": "https://kimi.moonshot.cn/",
|
| 213 |
+
"Kimi-deep-research": "https://kimi.moonshot.cn/",
|
| 214 |
+
"doubao-deep-research": "https://www.doubao.com/chat/",
|
| 215 |
+
"Doubao-deep-research": "https://www.doubao.com/chat/",
|
| 216 |
+
"gpt-5.5": "https://openai.com/",
|
| 217 |
+
"GPT-5.5": "https://openai.com/",
|
| 218 |
+
"claude-opus-4-7": "https://www.anthropic.com/",
|
| 219 |
+
"Claude-opus-4-7": "https://www.anthropic.com/",
|
| 220 |
+
"perplexity-deep-research": "https://www.perplexity.ai/",
|
| 221 |
+
"Perplexity-deep-research": "https://www.perplexity.ai/",
|
| 222 |
+
"gemini-3.1-pro-preview": "https://gemini.google/",
|
| 223 |
+
"Gemini-3.1-pro-preview": "https://gemini.google/",
|
| 224 |
+
"grok-3-deepsearch": "https://x.ai/grok",
|
| 225 |
+
"Grok-3-deepsearch": "https://x.ai/grok",
|
| 226 |
+
"qwen-deep-research": "https://tongyi.aliyun.com/",
|
| 227 |
+
"Qwen-deep-research": "https://tongyi.aliyun.com/",
|
| 228 |
+
"openai-deepresearch": "https://openai.com/index/introducing-deep-research/",
|
| 229 |
"gemini-2.5-pro-deepresearch": "https://gemini.google/overview/deep-research/",
|
| 230 |
+
"perplexity-Research": "https://www.perplexity.ai/",
|
| 231 |
+
"grok-deeper-search": "https://x.ai/grok",
|
| 232 |
+
"mirothinker": "https://miro.com/",
|
| 233 |
+
"MiroThinker": "https://miro.com/",
|
| 234 |
+
"jina-deepsearch": "https://jina.ai/",
|
| 235 |
+
"Jina-deepsearch": "https://jina.ai/",
|
| 236 |
+
"kimi-k2.5": "https://kimi.moonshot.cn/",
|
| 237 |
+
"Kimi-k2.5": "https://kimi.moonshot.cn/",
|
| 238 |
+
"deepseek-v4-pro": "https://www.deepseek.com/",
|
| 239 |
+
"DeepSeek-V4-Pro": "https://www.deepseek.com/",
|
| 240 |
+
"tongyi-deepresearch-30b-a3b": "https://tongyi.aliyun.com/",
|
| 241 |
+
"Tongyi-deepresearch-30b-a3b": "https://tongyi.aliyun.com/",
|
| 242 |
+
"langchain-open-deep-research": "https://github.com/langchain-ai/open_deep_research",
|
| 243 |
"langchain-open-deep-research-gpt-5": "https://github.com/langchain-ai/open_deep_research",
|
| 244 |
+
"salesforce-air-deep-research": "https://github.com/SalesforceAIResearch/enterprise-deep-research",
|
|
|
|
| 245 |
"thinkdepthai-deepresearch": "https://github.com/thinkdepthai/Deep_Research",
|
| 246 |
"tavily-research": "https://deepresearch.tavily.com",
|
|
|
|
| 247 |
"onyx": "https://github.com/onyx-dot-app/onyx",
|
|
|
|
| 248 |
"deepsynth": "https://www.publicissapient.com/platforms/bodhi",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 249 |
"ms_deepresearch": "https://github.com/modelscope/ms-agent",
|
| 250 |
"TrajectoryKit": "https://github.com/KabakaWilliam/trajectorykit",
|
|
|
|
| 251 |
"zhipu_deep_research": "https://research-hb.zhipuai-infra.cn/",
|
|
|
|
|
|
|
| 252 |
"MindDR-V1.5": "https://www.lixiang.com/tech/mindgpt",
|
| 253 |
"deepdog": "https://github.com/beneadie/DeepDog_1",
|
| 254 |
"Link": "https://iflow.cn/public/iflow-researcher.html",
|
|
|
|
| 255 |
"ZTE-Nebula-DeepResearch-V20260519": "https://github.com/Adlik/ZTE-Nebula-DeepResearch",
|
| 256 |
"WhaleCloud-DocChain": "https://lab.hjcloud.com/llmdoc/login?redirect=%2Fkagent",
|
| 257 |
+
"qianfan_deepresearch_0430": "https://console.bce.baidu.com/qianfan/studio/officialApp/deepResearch/69a72b3f-a18c-2f02-9ba0-007beb95b315",
|
| 258 |
+
"doubao-deepresearch": "https://www.doubao.com/chat/",
|
| 259 |
+
"kimi-researcher": "https://kimi.moonshot.cn/",
|
| 260 |
+
"claude-research": "https://www.anthropic.com/",
|
| 261 |
+
"tongyi-deepresearch-30B-A3B": "https://tongyi.aliyun.com/",
|
| 262 |
+
"sonar-reasoning-pro": "https://perplexity.ai/",
|
| 263 |
+
"sonar-reasoning": "https://perplexity.ai/",
|
| 264 |
+
"sonar-pro": "https://perplexity.ai/",
|
| 265 |
+
"claude-3-7-sonnet-with-search": "https://www.anthropic.com/",
|
| 266 |
+
"gpt-4o-search-preview": "https://openai.com/",
|
| 267 |
}
|
| 268 |
|
| 269 |
# 模型许可证类型映射
|
| 270 |
MODEL_LICENSE_TYPE = {
|
| 271 |
+
"gemini-deep-research": "Proprietary",
|
| 272 |
+
"Gemini-deep-research": "Proprietary",
|
| 273 |
+
"openai-o3-deep-research": "Proprietary",
|
| 274 |
+
"OpenAI-o3-deep-research": "Proprietary",
|
| 275 |
+
"kimi-deep-research": "Proprietary",
|
| 276 |
+
"Kimi-deep-research": "Proprietary",
|
| 277 |
+
"doubao-deep-research": "Proprietary",
|
| 278 |
+
"Doubao-deep-research": "Proprietary",
|
| 279 |
+
"gpt-5.5": "Proprietary",
|
| 280 |
+
"GPT-5.5": "Proprietary",
|
| 281 |
+
"claude-opus-4-7": "Proprietary",
|
| 282 |
+
"Claude-opus-4-7": "Proprietary",
|
| 283 |
+
"perplexity-deep-research": "Proprietary",
|
| 284 |
+
"Perplexity-deep-research": "Proprietary",
|
| 285 |
+
"gemini-3.1-pro-preview": "Proprietary",
|
| 286 |
+
"Gemini-3.1-pro-preview": "Proprietary",
|
| 287 |
+
"grok-3-deepsearch": "Proprietary",
|
| 288 |
+
"Grok-3-deepsearch": "Proprietary",
|
| 289 |
+
"qwen-deep-research": "Proprietary",
|
| 290 |
+
"Qwen-deep-research": "Proprietary",
|
| 291 |
+
"deerflow-gpt55": "Apache-2.0",
|
| 292 |
+
"DeerFlow(+GPT-5.5)": "Apache-2.0",
|
| 293 |
+
"openclaw-gpt55": "Apache-2.0",
|
| 294 |
+
"OpenClaw(+GPT-5.5)": "Apache-2.0",
|
| 295 |
+
"openclaw-deepseek-v4-pro": "Apache-2.0",
|
| 296 |
+
"OpenClaw(+DeepSeek-V4-Pro)": "Apache-2.0",
|
| 297 |
+
"deerflow-deepseek-v4-pro": "Apache-2.0",
|
| 298 |
+
"DeerFlow(+DeepSeek-V4-Pro)": "Apache-2.0",
|
| 299 |
+
"mirothinker": "Apache-2.0",
|
| 300 |
+
"MiroThinker": "Apache-2.0",
|
| 301 |
+
"jina-deepsearch": "Apache-2.0",
|
| 302 |
+
"Jina-deepsearch": "Apache-2.0",
|
| 303 |
+
"kimi-k2.5": "Proprietary",
|
| 304 |
+
"Kimi-k2.5": "Proprietary",
|
| 305 |
+
"deepseek-v4-pro": "MIT",
|
| 306 |
+
"DeepSeek-V4-Pro": "MIT",
|
| 307 |
+
"tongyi-deepresearch-30b-a3b": "Proprietary",
|
| 308 |
+
"Tongyi-deepresearch-30b-a3b": "Proprietary",
|
| 309 |
"openai-deepresearch": "Proprietary",
|
| 310 |
+
"gemini-2.5-pro-deepresearch": "Proprietary",
|
| 311 |
+
"perplexity-Research": "Proprietary",
|
| 312 |
+
"grok-deeper-search": "Proprietary",
|
| 313 |
+
"langchain-open-deep-research": "MIT",
|
| 314 |
"langchain-open-deep-research-gpt-5": "MIT",
|
| 315 |
+
"salesforce-air-deep-research": "Apache-2.0",
|
|
|
|
| 316 |
"thinkdepthai-deepresearch": "MIT",
|
| 317 |
"tavily-research": "Proprietary",
|
|
|
|
| 318 |
"onyx": "MIT (partial EE)",
|
| 319 |
"raaa-deep-research": "Proprietary",
|
| 320 |
"deepsynth": "Proprietary",
|
| 321 |
+
"xiaoyi_research_agent_0304": "Proprietary",
|
| 322 |
+
"nvidia-aiq-nemotron-gpt52-updated": "Apache-2.0",
|
| 323 |
"deepinsight": "Proprietary",
|
| 324 |
"drb_cellcog": "Proprietary",
|
| 325 |
+
"drb_cellcog_max": "Proprietary",
|
| 326 |
"RecallRadar": "Proprietary",
|
| 327 |
+
"ms_deepresearch": "Apache-2.0",
|
| 328 |
"TrajectoryKit": "MIT",
|
| 329 |
+
"1688AILab-DeepResearch-0325": "Proprietary",
|
| 330 |
"zhipu_deep_research": "Proprietary",
|
| 331 |
+
"grep-v4": "Proprietary",
|
|
|
|
| 332 |
"MindDR-V1.5": "Proprietary",
|
| 333 |
"deepdog": "MIT",
|
| 334 |
"Link": "TBD",
|
| 335 |
"octen-deepresearch-0508": "Proprietary",
|
| 336 |
"ZTE-Nebula-DeepResearch-V20260519": "Proprietary",
|
| 337 |
"WhaleCloud-DocChain": "Proprietary",
|
| 338 |
+
"qianfan_deepresearch_0430": "Proprietary",
|
| 339 |
+
"doubao-deepresearch": "Proprietary",
|
| 340 |
+
"kimi-researcher": "Proprietary",
|
| 341 |
+
"claude-research": "Proprietary",
|
| 342 |
+
"dr-tulu": "Proprietary",
|
| 343 |
+
"nvidia-aiq-research-assistant": "Apache-2.0",
|
| 344 |
+
"tongyi-deepresearch-30B-A3B": "Proprietary",
|
| 345 |
+
"sonar-reasoning-pro": "Proprietary",
|
| 346 |
+
"sonar-reasoning": "Proprietary",
|
| 347 |
+
"sonar-pro": "Proprietary",
|
| 348 |
+
"claude-3-7-sonnet-with-search": "Proprietary",
|
| 349 |
+
"gpt-4o-search-preview": "Proprietary",
|
| 350 |
}
|
| 351 |
|
| 352 |
+
|
| 353 |
def load_leaderboard() -> pd.DataFrame:
|
| 354 |
if not DATA_PATH.exists():
|
| 355 |
raise FileNotFoundError(
|
| 356 |
f"Leaderboard file not found: {DATA_PATH}.\n"
|
| 357 |
+
"→ 先运行 utils/rank_leaderboard.py 生成 data/leaderboard.csv"
|
| 358 |
)
|
| 359 |
df = pd.read_csv(DATA_PATH)
|
| 360 |
df.columns = [c.strip() for c in df.columns]
|
| 361 |
+
|
| 362 |
def get_category(model_name):
|
| 363 |
for category, models in MODEL_CATEGORIES.items():
|
| 364 |
if model_name in models:
|
| 365 |
return category
|
| 366 |
+
# Fallback to case-insensitive matching
|
| 367 |
+
for category, models in MODEL_CATEGORIES.items():
|
| 368 |
+
if model_name.lower() in {m.lower() for m in models}:
|
| 369 |
+
return category
|
| 370 |
return "Others"
|
| 371 |
+
|
| 372 |
def get_license_type(model_name):
|
| 373 |
+
lic = MODEL_LICENSE_TYPE.get(model_name)
|
| 374 |
+
if lic is None:
|
| 375 |
+
lic = MODEL_LICENSE_TYPE.get(model_name.lower())
|
| 376 |
+
return lic if lic is not None else "Unknown"
|
| 377 |
+
|
| 378 |
df['category'] = df['model'].apply(get_category)
|
| 379 |
df['license_type'] = df['model'].apply(get_license_type)
|
| 380 |
return df
|
| 381 |
|
| 382 |
+
|
| 383 |
def make_ranked(df: pd.DataFrame) -> pd.DataFrame:
|
| 384 |
+
tiebreak_cols = ['objective_avg', 'subjective_avg', 'objective_en', 'subjective_en']
|
| 385 |
+
sort_cols = ['overall'] + [c for c in tiebreak_cols if c in df.columns]
|
| 386 |
+
ranked = df.sort_values(by=sort_cols, ascending=False).reset_index(drop=True)
|
| 387 |
+
ranked.insert(0, "Rank", ranked['overall'].rank(method='min', ascending=False).astype(int))
|
| 388 |
+
|
| 389 |
medal_styles = {
|
| 390 |
+
1: {'icon': '🥇', 'color': '#FFD700'},
|
| 391 |
+
2: {'icon': '🥈', 'color': '#C0C0C0'},
|
| 392 |
+
3: {'icon': '🥉', 'color': '#CD7F32'}
|
| 393 |
}
|
| 394 |
+
|
| 395 |
def format_rank_with_medal(rank_num):
|
| 396 |
if rank_num in medal_styles:
|
| 397 |
style = medal_styles[rank_num]
|
|
|
|
| 398 |
return (
|
| 399 |
f'<span style="font-weight: bold; color: {style["color"]};">{rank_num}</span> '
|
| 400 |
f'<span style="font-size: 1.5em;">{style["icon"]}</span>'
|
| 401 |
)
|
| 402 |
+
return str(rank_num)
|
| 403 |
+
|
|
|
|
| 404 |
ranked['Rank'] = ranked['Rank'].apply(format_rank_with_medal)
|
| 405 |
+
|
| 406 |
# 重命名列名为简写形式
|
| 407 |
ranked = ranked.rename(columns=COLUMN_RENAME_MAP)
|
| 408 |
+
|
| 409 |
+
# 格式化数值列为两位小数,跳过包含"-"的值
|
| 410 |
+
numeric_columns = list(COLUMN_RENAME_MAP.values())
|
| 411 |
for col in numeric_columns:
|
| 412 |
if col in ranked.columns:
|
|
|
|
| 413 |
ranked[col] = ranked[col].apply(
|
| 414 |
lambda x: round(float(x), 2) if x != "-" and pd.notna(x) else x
|
| 415 |
)
|
| 416 |
+
|
| 417 |
# 为模型添加链接和高亮样式
|
| 418 |
def format_model_name(row):
|
| 419 |
model_name = row['model']
|
| 420 |
+
display_model_name = MODEL_DISPLAY_NAMES.get(model_name)
|
| 421 |
+
if display_model_name is None:
|
| 422 |
+
display_model_name = MODEL_DISPLAY_NAMES.get(model_name.lower(), model_name)
|
| 423 |
link = MODEL_LINKS.get(model_name, "")
|
| 424 |
+
if not link:
|
| 425 |
+
link = MODEL_LINKS.get(model_name.lower(), "")
|
| 426 |
+
|
| 427 |
if row['category'] == CATEGORY_TO_HIGHLIGHT:
|
| 428 |
+
display_name = f'<span style="color: #C41E3A;">{HIGHLIGHT_EMOJI} {display_model_name}</span>'
|
| 429 |
else:
|
| 430 |
display_name = display_model_name
|
| 431 |
+
|
|
|
|
| 432 |
if link and link.strip():
|
| 433 |
return f'<a href="{link}" target="_blank" style="text-decoration: none;">{display_name}</a>'
|
| 434 |
+
return f'<span class="model-name" data-model="{model_name}">{display_name}</span>'
|
| 435 |
+
|
|
|
|
|
|
|
| 436 |
ranked['model'] = ranked.apply(format_model_name, axis=1)
|
|
|
|
| 437 |
return ranked
|
| 438 |
|
| 439 |
+
|
| 440 |
def filter_data(search_text: str, selected_categories: list):
|
| 441 |
df = load_leaderboard()
|
| 442 |
+
|
| 443 |
if search_text.strip():
|
| 444 |
df = df[df['model'].str.contains(search_text.strip(), case=False, na=False)]
|
| 445 |
+
|
| 446 |
if selected_categories:
|
| 447 |
df = df[df['category'].isin(selected_categories)]
|
| 448 |
+
|
| 449 |
+
return make_ranked(df)
|
| 450 |
+
|
| 451 |
|
| 452 |
def create_leaderboard_tab():
|
| 453 |
+
with gr.Tab("🏆Leaderboard"):
|
| 454 |
with gr.Row():
|
| 455 |
with gr.Column(scale=1):
|
| 456 |
search_box = gr.Textbox(
|
| 457 |
+
label="Model Search",
|
| 458 |
placeholder="Entering model name to search...",
|
| 459 |
value=""
|
| 460 |
)
|
| 461 |
with gr.Column(scale=2):
|
| 462 |
category_checkboxes = gr.CheckboxGroup(
|
| 463 |
label="Model Categories",
|
| 464 |
+
choices=list(MODEL_CATEGORIES.keys()) + ["Others"],
|
| 465 |
+
value=list(MODEL_CATEGORIES.keys()) + ["Others"]
|
| 466 |
)
|
| 467 |
+
|
| 468 |
+
all_categories = list(MODEL_CATEGORIES.keys()) + ["Others"]
|
| 469 |
+
|
| 470 |
+
initial_df = filter_data("", all_categories)
|
| 471 |
+
|
| 472 |
column_count = len(initial_df.columns)
|
| 473 |
datatypes = ["str"] * column_count
|
| 474 |
model_col_index = initial_df.columns.get_loc('model')
|
| 475 |
rank_col_index = initial_df.columns.get_loc('Rank')
|
| 476 |
datatypes[model_col_index] = "html"
|
| 477 |
+
datatypes[rank_col_index] = "html"
|
| 478 |
+
|
| 479 |
+
# 根据列数动态设置列宽
|
| 480 |
+
base_widths = ["80px", "380px"] # Rank, model
|
| 481 |
+
metric_width = "95px"
|
| 482 |
+
info_widths = ["180px", "150px"] # category, license_type
|
| 483 |
+
metric_cols = column_count - 4 # Rank + model + category + license_type
|
| 484 |
+
column_widths = base_widths + [metric_width] * metric_cols + info_widths
|
| 485 |
+
|
| 486 |
table = gr.Dataframe(
|
| 487 |
value=initial_df,
|
| 488 |
+
datatype=datatypes,
|
| 489 |
+
max_height=600,
|
| 490 |
+
show_label=False,
|
| 491 |
+
elem_id="leaderboard_table",
|
| 492 |
+
interactive=False,
|
| 493 |
+
wrap=True,
|
| 494 |
+
column_widths=column_widths
|
| 495 |
)
|
| 496 |
|
| 497 |
def update_display(search_text, selected_categories):
|
| 498 |
+
return filter_data(search_text, selected_categories)
|
|
|
|
| 499 |
|
|
|
|
| 500 |
search_box.change(
|
| 501 |
fn=update_display,
|
| 502 |
inputs=[search_box, category_checkboxes],
|
|
|
|
| 507 |
inputs=[search_box, category_checkboxes],
|
| 508 |
outputs=table
|
| 509 |
)
|
| 510 |
+
|
| 511 |
# 在底部添加说明
|
| 512 |
with gr.Row():
|
| 513 |
gr.Markdown(f"""
|
| 514 |
### 📊 Column Descriptions
|
| 515 |
- **Rank**: Model ranking based on overall score
|
| 516 |
+
- **model**: Model name (<span style="color: #C41E3A;">{HIGHLIGHT_EMOJI} = {CATEGORY_TO_HIGHLIGHT}</span>)
|
| 517 |
+
- **overall**: Average of EN and ZH overall scores (mean of Objective & Subjective per language)
|
| 518 |
+
- **obj.EN / obj.ZH**: Objective scores on Global (English) / Chinese tasks
|
| 519 |
+
- **obj.avg**: Average objective score across both language tracks
|
| 520 |
+
- **subj.EN / subj.ZH**: Subjective report scores on Global (English) / Chinese tasks
|
| 521 |
+
- **subj.avg**: Average subjective score across both language tracks
|
| 522 |
+
- **expert**: Average expert rubric score across EN & ZH subjective tasks (0-100)
|
| 523 |
+
- **citation**: Citation consistency score (auxiliary, scaled 0-100)
|
| 524 |
+
- **source**: Source quality score (authority & timeliness, scaled 0-100)
|
| 525 |
+
- **RMSCE**: Root Mean Square Calibration Error for objective confidence (lower is better)
|
| 526 |
- **category**: Model category
|
| 527 |
- **license_type**: The software license type of the model/service
|
| 528 |
+
|
| 529 |
+
💡 **Tip**: Model names are clickable when links are available. Visit the project repositories for more details!
|
| 530 |
""")
|
| 531 |
+
|
| 532 |
return search_box
|
tabs/shared_data.py
CHANGED
|
@@ -22,8 +22,16 @@ DATA_VIEWER_FILE = (
|
|
| 22 |
)
|
| 23 |
DATA_VIEWER_INDEX_FILE = BASE_DIR / "data" / "data_viewer_index.json"
|
| 24 |
|
|
|
|
| 25 |
_REQUIRED_COLS = [
|
| 26 |
-
"model_name", "id", "prompt", "article",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
"comprehensiveness_score", "insight_score",
|
| 28 |
"instruction_following_score", "readability_score",
|
| 29 |
]
|
|
@@ -32,6 +40,56 @@ _cache: pd.DataFrame | None = None
|
|
| 32 |
_index_cache: dict | None = None
|
| 33 |
|
| 34 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
def get_data() -> pd.DataFrame:
|
| 36 |
global _cache
|
| 37 |
if _cache is not None:
|
|
@@ -45,13 +103,13 @@ def get_data() -> pd.DataFrame:
|
|
| 45 |
if not line:
|
| 46 |
continue
|
| 47 |
try:
|
| 48 |
-
records.append(json.loads(line))
|
| 49 |
except json.JSONDecodeError:
|
| 50 |
continue
|
| 51 |
|
| 52 |
df = pd.DataFrame(records)
|
| 53 |
if df.empty or not all(c in df.columns for c in _REQUIRED_COLS):
|
| 54 |
-
_cache = pd.DataFrame(columns=_REQUIRED_COLS)
|
| 55 |
else:
|
| 56 |
df["id"] = df["id"].astype(str)
|
| 57 |
_cache = df
|
|
@@ -81,6 +139,7 @@ def get_index() -> dict:
|
|
| 81 |
item = json.loads(line)
|
| 82 |
except json.JSONDecodeError:
|
| 83 |
continue
|
|
|
|
| 84 |
model = item.get("model_name")
|
| 85 |
item_id = str(item.get("id"))
|
| 86 |
prompt = item.get("prompt") or ""
|
|
@@ -93,7 +152,7 @@ def get_index() -> dict:
|
|
| 93 |
"models": sorted(models),
|
| 94 |
"tasks": [
|
| 95 |
{"id": item_id, "prompt": tasks[item_id]}
|
| 96 |
-
for item_id in sorted(tasks, key=lambda value: int(value))
|
| 97 |
],
|
| 98 |
}
|
| 99 |
return _index_cache
|
|
@@ -112,7 +171,7 @@ def get_entry(model_name: str, item_id: str) -> dict | None:
|
|
| 112 |
fh.seek(offset)
|
| 113 |
line = fh.read(length).decode("utf-8")
|
| 114 |
try:
|
| 115 |
-
item = json.loads(line)
|
| 116 |
if item.get("model_name") == model_name and str(item.get("id")) == item_id:
|
| 117 |
return item
|
| 118 |
except json.JSONDecodeError:
|
|
@@ -123,7 +182,7 @@ def get_entry(model_name: str, item_id: str) -> dict | None:
|
|
| 123 |
if not line.strip():
|
| 124 |
continue
|
| 125 |
try:
|
| 126 |
-
item = json.loads(line)
|
| 127 |
except json.JSONDecodeError:
|
| 128 |
continue
|
| 129 |
if item.get("model_name") == model_name and str(item.get("id")) == item_id:
|
|
@@ -148,7 +207,7 @@ def get_entries_for_task(item_id: str, model_names: set[str]) -> dict[str, dict]
|
|
| 148 |
for model, (offset, length) in locations.items():
|
| 149 |
fh.seek(offset)
|
| 150 |
try:
|
| 151 |
-
item = json.loads(fh.read(length).decode("utf-8"))
|
| 152 |
if item.get("model_name") == model and str(item.get("id")) == item_id:
|
| 153 |
found[model] = item
|
| 154 |
except json.JSONDecodeError:
|
|
@@ -162,7 +221,7 @@ def get_entries_for_task(item_id: str, model_names: set[str]) -> dict[str, dict]
|
|
| 162 |
if not line.strip():
|
| 163 |
continue
|
| 164 |
try:
|
| 165 |
-
item = json.loads(line)
|
| 166 |
except json.JSONDecodeError:
|
| 167 |
continue
|
| 168 |
model = item.get("model_name")
|
|
|
|
| 22 |
)
|
| 23 |
DATA_VIEWER_INDEX_FILE = BASE_DIR / "data" / "data_viewer_index.json"
|
| 24 |
|
| 25 |
+
# ICBCBench data viewer schema: dual-track scores + optional legacy DeepResearch Bench fields
|
| 26 |
_REQUIRED_COLS = [
|
| 27 |
+
"model_name", "id", "prompt", "article",
|
| 28 |
+
"overall_score", "track", # track: "objective" or "subjective"
|
| 29 |
+
]
|
| 30 |
+
_OPTIONAL_SCORE_COLS = [
|
| 31 |
+
"objective_score", "subjective_score",
|
| 32 |
+
"expert_score", "citation_score", "source_quality_score",
|
| 33 |
+
"confidence", "correct",
|
| 34 |
+
# Legacy DeepResearch Bench fields (kept for backward compatibility)
|
| 35 |
"comprehensiveness_score", "insight_score",
|
| 36 |
"instruction_following_score", "readability_score",
|
| 37 |
]
|
|
|
|
| 40 |
_index_cache: dict | None = None
|
| 41 |
|
| 42 |
|
| 43 |
+
def _normalize_record(item: dict) -> dict:
|
| 44 |
+
"""Ensure every record has the ICBCBench score fields, falling back to legacy fields."""
|
| 45 |
+
record = dict(item)
|
| 46 |
+
|
| 47 |
+
# Determine track
|
| 48 |
+
if "track" not in record or not record["track"]:
|
| 49 |
+
# Heuristic: if article is present and non-empty, treat as subjective
|
| 50 |
+
article = record.get("article") or ""
|
| 51 |
+
record["track"] = "subjective" if isinstance(article, str) and article.strip() else "objective"
|
| 52 |
+
|
| 53 |
+
# Normalize scores to 0-100 scale if they look like ratios
|
| 54 |
+
for key in ["overall_score", "objective_score", "subjective_score",
|
| 55 |
+
"expert_score", "citation_score", "source_quality_score",
|
| 56 |
+
"comprehensiveness_score", "insight_score",
|
| 57 |
+
"instruction_following_score", "readability_score"]:
|
| 58 |
+
val = record.get(key)
|
| 59 |
+
if isinstance(val, (int, float)) and val is not None and val <= 1.0:
|
| 60 |
+
record[key] = val * 100
|
| 61 |
+
|
| 62 |
+
# If the new ICBCBench fields are missing but legacy fields exist, synthesize them.
|
| 63 |
+
if record.get("subjective_score") is None:
|
| 64 |
+
legacy = []
|
| 65 |
+
for k in ["comprehensiveness_score", "insight_score",
|
| 66 |
+
"instruction_following_score", "readability_score"]:
|
| 67 |
+
val = record.get(k)
|
| 68 |
+
if val is None:
|
| 69 |
+
continue
|
| 70 |
+
try:
|
| 71 |
+
legacy.append(float(val))
|
| 72 |
+
except (TypeError, ValueError):
|
| 73 |
+
pass
|
| 74 |
+
if legacy:
|
| 75 |
+
record["subjective_score"] = sum(legacy) / len(legacy)
|
| 76 |
+
|
| 77 |
+
if record.get("overall_score") is None:
|
| 78 |
+
candidates = []
|
| 79 |
+
for k in ["objective_score", "subjective_score"]:
|
| 80 |
+
val = record.get(k)
|
| 81 |
+
if val is None:
|
| 82 |
+
continue
|
| 83 |
+
try:
|
| 84 |
+
candidates.append(float(val))
|
| 85 |
+
except (TypeError, ValueError):
|
| 86 |
+
pass
|
| 87 |
+
if candidates:
|
| 88 |
+
record["overall_score"] = sum(candidates) / len(candidates)
|
| 89 |
+
|
| 90 |
+
return record
|
| 91 |
+
|
| 92 |
+
|
| 93 |
def get_data() -> pd.DataFrame:
|
| 94 |
global _cache
|
| 95 |
if _cache is not None:
|
|
|
|
| 103 |
if not line:
|
| 104 |
continue
|
| 105 |
try:
|
| 106 |
+
records.append(_normalize_record(json.loads(line)))
|
| 107 |
except json.JSONDecodeError:
|
| 108 |
continue
|
| 109 |
|
| 110 |
df = pd.DataFrame(records)
|
| 111 |
if df.empty or not all(c in df.columns for c in _REQUIRED_COLS):
|
| 112 |
+
_cache = pd.DataFrame(columns=_REQUIRED_COLS + _OPTIONAL_SCORE_COLS)
|
| 113 |
else:
|
| 114 |
df["id"] = df["id"].astype(str)
|
| 115 |
_cache = df
|
|
|
|
| 139 |
item = json.loads(line)
|
| 140 |
except json.JSONDecodeError:
|
| 141 |
continue
|
| 142 |
+
item = _normalize_record(item)
|
| 143 |
model = item.get("model_name")
|
| 144 |
item_id = str(item.get("id"))
|
| 145 |
prompt = item.get("prompt") or ""
|
|
|
|
| 152 |
"models": sorted(models),
|
| 153 |
"tasks": [
|
| 154 |
{"id": item_id, "prompt": tasks[item_id]}
|
| 155 |
+
for item_id in sorted(tasks, key=lambda value: int(value) if value.isdigit() else value)
|
| 156 |
],
|
| 157 |
}
|
| 158 |
return _index_cache
|
|
|
|
| 171 |
fh.seek(offset)
|
| 172 |
line = fh.read(length).decode("utf-8")
|
| 173 |
try:
|
| 174 |
+
item = _normalize_record(json.loads(line))
|
| 175 |
if item.get("model_name") == model_name and str(item.get("id")) == item_id:
|
| 176 |
return item
|
| 177 |
except json.JSONDecodeError:
|
|
|
|
| 182 |
if not line.strip():
|
| 183 |
continue
|
| 184 |
try:
|
| 185 |
+
item = _normalize_record(json.loads(line))
|
| 186 |
except json.JSONDecodeError:
|
| 187 |
continue
|
| 188 |
if item.get("model_name") == model_name and str(item.get("id")) == item_id:
|
|
|
|
| 207 |
for model, (offset, length) in locations.items():
|
| 208 |
fh.seek(offset)
|
| 209 |
try:
|
| 210 |
+
item = _normalize_record(json.loads(fh.read(length).decode("utf-8")))
|
| 211 |
if item.get("model_name") == model and str(item.get("id")) == item_id:
|
| 212 |
found[model] = item
|
| 213 |
except json.JSONDecodeError:
|
|
|
|
| 221 |
if not line.strip():
|
| 222 |
continue
|
| 223 |
try:
|
| 224 |
+
item = _normalize_record(json.loads(line))
|
| 225 |
except json.JSONDecodeError:
|
| 226 |
continue
|
| 227 |
model = item.get("model_name")
|
utils/merge_raw_data.py
CHANGED
|
@@ -1,8 +1,20 @@
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
|
| 4 |
import json
|
| 5 |
-
import os
|
| 6 |
from pathlib import Path
|
| 7 |
|
| 8 |
EXCLUDED_SLUGS = {
|
|
@@ -11,44 +23,62 @@ EXCLUDED_SLUGS = {
|
|
| 11 |
}
|
| 12 |
|
| 13 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
def load_scores_for_model(model_results_dir: Path):
|
|
|
|
| 15 |
scores_by_id = {}
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
return scores_by_id
|
| 21 |
|
| 22 |
-
print(f" 正在从 {model_results_dir.name}/
|
| 23 |
-
with open(
|
| 24 |
for i, line in enumerate(f):
|
| 25 |
try:
|
| 26 |
data = json.loads(line.strip())
|
| 27 |
article_id = str(data.get('id'))
|
| 28 |
if not article_id:
|
| 29 |
-
print(f" 警告: {model_results_dir.name} 第 {i+1} 行缺少ID,已跳过。")
|
| 30 |
continue
|
| 31 |
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
'
|
| 42 |
-
|
| 43 |
-
'
|
| 44 |
-
'
|
| 45 |
-
'
|
|
|
|
| 46 |
}
|
|
|
|
| 47 |
except json.JSONDecodeError as e:
|
| 48 |
-
print(f" 错误: 解析JSON时出错 (
|
| 49 |
except Exception as e:
|
| 50 |
-
print(f" 错误: 处理数据时出错 (
|
| 51 |
-
|
|
|
|
| 52 |
return scores_by_id
|
| 53 |
|
| 54 |
|
|
@@ -57,73 +87,77 @@ def merge_jsonl_files():
|
|
| 57 |
raw_data_dir = project_root / "data" / "raw_data"
|
| 58 |
raw_results_dir = project_root / "data" / "raw_results"
|
| 59 |
output_file = project_root / "data" / "data_viewer.jsonl"
|
| 60 |
-
|
| 61 |
input_files = list(raw_data_dir.glob("*.jsonl"))
|
| 62 |
print(f"在 {raw_data_dir} 中找到 {len(input_files)} 个模型JSONL文件")
|
| 63 |
-
|
| 64 |
if not input_files:
|
| 65 |
print("未找到任何原始数据文件,已退出。")
|
| 66 |
return
|
| 67 |
|
| 68 |
-
with open(output_file, 'w', encoding='utf-8') as f:
|
| 69 |
-
pass
|
| 70 |
-
|
| 71 |
all_merged_data = []
|
| 72 |
-
|
| 73 |
for raw_data_file in input_files:
|
| 74 |
model_name = raw_data_file.stem
|
| 75 |
if model_name in EXCLUDED_SLUGS:
|
| 76 |
print(f"跳过隐藏模型: {model_name}")
|
| 77 |
continue
|
| 78 |
print(f"正在处理原始数据文件: {raw_data_file.name} (模型: {model_name})")
|
| 79 |
-
|
| 80 |
model_results_dir = raw_results_dir / model_name
|
| 81 |
if not model_results_dir.exists():
|
| 82 |
print(f" 警告: 未找到模型 {model_name} 对应的结果文件夹: {model_results_dir}")
|
| 83 |
continue
|
| 84 |
-
|
| 85 |
scores_for_current_model = load_scores_for_model(model_results_dir)
|
| 86 |
-
|
| 87 |
-
|
| 88 |
with open(raw_data_file, 'r', encoding='utf-8') as f_raw:
|
| 89 |
for i, line in enumerate(f_raw):
|
| 90 |
try:
|
| 91 |
article_data = json.loads(line.strip())
|
| 92 |
article_id = str(article_data.get('id'))
|
| 93 |
-
|
| 94 |
if not article_id:
|
| 95 |
-
print(f" 警告: {raw_data_file.name} 第 {i+1} 行缺少ID,已跳过。")
|
| 96 |
continue
|
| 97 |
-
|
| 98 |
article_scores = scores_for_current_model.get(article_id, {})
|
| 99 |
-
if not article_scores:
|
| 100 |
-
print(f" 警告: 模型 {model_name} 的文章ID {article_id} 未在结果文件中找到分数。")
|
| 101 |
|
| 102 |
merged_item = {
|
| 103 |
'model_name': model_name,
|
| 104 |
'id': article_id,
|
| 105 |
'prompt': article_data.get('prompt'),
|
| 106 |
'article': article_data.get('article'),
|
|
|
|
|
|
|
| 107 |
'overall_score': article_scores.get('overall_score'),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
'comprehensiveness_score': article_scores.get('comprehensiveness_score'),
|
| 109 |
'insight_score': article_scores.get('insight_score'),
|
| 110 |
'instruction_following_score': article_scores.get('instruction_following_score'),
|
| 111 |
-
'readability_score': article_scores.get('readability_score')
|
| 112 |
}
|
| 113 |
all_merged_data.append(merged_item)
|
| 114 |
-
|
| 115 |
except json.JSONDecodeError as e:
|
| 116 |
-
print(f" 错误: 解析原始数据JSON时出错 (
|
| 117 |
except Exception as e:
|
| 118 |
-
print(f" 错误: 处理原始数据时出错 (
|
| 119 |
-
|
| 120 |
-
|
|
|
|
| 121 |
with open(output_file, 'w', encoding='utf-8') as f_out:
|
| 122 |
for item in all_merged_data:
|
| 123 |
f_out.write(json.dumps(item, ensure_ascii=False) + '\n')
|
| 124 |
-
|
| 125 |
print(f"\n成功合并并保存到: {output_file}, 共 {len(all_merged_data)} 条记录")
|
| 126 |
|
|
|
|
| 127 |
if __name__ == "__main__":
|
| 128 |
merge_jsonl_files()
|
| 129 |
-
print("所有文件处理完成!")
|
|
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
# -*- coding: utf-8 -*-
|
| 3 |
+
"""
|
| 4 |
+
Merge per-model raw data and raw result files into data/data_viewer.jsonl.
|
| 5 |
+
Supports ICBCBench dual-track format (objective / subjective) and legacy
|
| 6 |
+
DeepResearch Bench format.
|
| 7 |
+
|
| 8 |
+
Expected raw_data/<model>.jsonl fields:
|
| 9 |
+
id, prompt, article (for subjective), answer (for objective), language, track
|
| 10 |
+
|
| 11 |
+
Expected raw_results/<model>/results.jsonl fields:
|
| 12 |
+
id, score, overall_score, track, language,
|
| 13 |
+
objective_score / subjective_score / expert_score / citation_score / source_quality_score,
|
| 14 |
+
confidence, correct
|
| 15 |
+
"""
|
| 16 |
|
| 17 |
import json
|
|
|
|
| 18 |
from pathlib import Path
|
| 19 |
|
| 20 |
EXCLUDED_SLUGS = {
|
|
|
|
| 23 |
}
|
| 24 |
|
| 25 |
|
| 26 |
+
def _norm_score(val):
|
| 27 |
+
"""Normalize scores to 0-100 scale."""
|
| 28 |
+
if val is None:
|
| 29 |
+
return None
|
| 30 |
+
try:
|
| 31 |
+
val = float(val)
|
| 32 |
+
except (TypeError, ValueError):
|
| 33 |
+
return val
|
| 34 |
+
return val * 100 if val <= 1.0 else val
|
| 35 |
+
|
| 36 |
+
|
| 37 |
def load_scores_for_model(model_results_dir: Path):
|
| 38 |
+
"""Load per-article scores from results.jsonl (ICBCBench) or raw_results.jsonl (legacy)."""
|
| 39 |
scores_by_id = {}
|
| 40 |
+
|
| 41 |
+
# Try ICBCBench results.jsonl first.
|
| 42 |
+
results_file = model_results_dir / "results.jsonl"
|
| 43 |
+
if not results_file.exists():
|
| 44 |
+
results_file = model_results_dir / "raw_results.jsonl"
|
| 45 |
+
|
| 46 |
+
if not results_file.exists():
|
| 47 |
+
print(f" 警告: 未找到模型 {model_results_dir.name} 的结果文件")
|
| 48 |
return scores_by_id
|
| 49 |
|
| 50 |
+
print(f" 正在从 {model_results_dir.name}/{results_file.name} 加载分数...")
|
| 51 |
+
with open(results_file, 'r', encoding='utf-8') as f:
|
| 52 |
for i, line in enumerate(f):
|
| 53 |
try:
|
| 54 |
data = json.loads(line.strip())
|
| 55 |
article_id = str(data.get('id'))
|
| 56 |
if not article_id:
|
|
|
|
| 57 |
continue
|
| 58 |
|
| 59 |
+
scores = {
|
| 60 |
+
'overall_score': _norm_score(data.get('overall_score', data.get('score'))),
|
| 61 |
+
'track': data.get('track', 'subjective'),
|
| 62 |
+
'objective_score': _norm_score(data.get('objective_score')),
|
| 63 |
+
'subjective_score': _norm_score(data.get('subjective_score')),
|
| 64 |
+
'expert_score': _norm_score(data.get('expert_score')),
|
| 65 |
+
'citation_score': _norm_score(data.get('citation_score')),
|
| 66 |
+
'source_quality_score': _norm_score(data.get('source_quality_score')),
|
| 67 |
+
'confidence': _norm_score(data.get('confidence')),
|
| 68 |
+
'correct': data.get('correct'),
|
| 69 |
+
# Legacy fields
|
| 70 |
+
'comprehensiveness_score': _norm_score(data.get('comprehensiveness')),
|
| 71 |
+
'insight_score': _norm_score(data.get('insight')),
|
| 72 |
+
'instruction_following_score': _norm_score(data.get('instruction_following')),
|
| 73 |
+
'readability_score': _norm_score(data.get('readability')),
|
| 74 |
}
|
| 75 |
+
scores_by_id[article_id] = scores
|
| 76 |
except json.JSONDecodeError as e:
|
| 77 |
+
print(f" 错误: 解析JSON时出错 ({model_results_dir.name}, 行 {i+1}): {e}")
|
| 78 |
except Exception as e:
|
| 79 |
+
print(f" 错误: 处理数据时出错 ({model_results_dir.name}, 行 {i+1}): {e}")
|
| 80 |
+
|
| 81 |
+
print(f" 为模型 {model_results_dir.name} 加载了 {len(scores_by_id)} 条结果")
|
| 82 |
return scores_by_id
|
| 83 |
|
| 84 |
|
|
|
|
| 87 |
raw_data_dir = project_root / "data" / "raw_data"
|
| 88 |
raw_results_dir = project_root / "data" / "raw_results"
|
| 89 |
output_file = project_root / "data" / "data_viewer.jsonl"
|
| 90 |
+
|
| 91 |
input_files = list(raw_data_dir.glob("*.jsonl"))
|
| 92 |
print(f"在 {raw_data_dir} 中找到 {len(input_files)} 个模型JSONL文件")
|
| 93 |
+
|
| 94 |
if not input_files:
|
| 95 |
print("未找到任何原始数据文件,已退出。")
|
| 96 |
return
|
| 97 |
|
|
|
|
|
|
|
|
|
|
| 98 |
all_merged_data = []
|
| 99 |
+
|
| 100 |
for raw_data_file in input_files:
|
| 101 |
model_name = raw_data_file.stem
|
| 102 |
if model_name in EXCLUDED_SLUGS:
|
| 103 |
print(f"跳过隐藏模型: {model_name}")
|
| 104 |
continue
|
| 105 |
print(f"正在处理原始数据文件: {raw_data_file.name} (模型: {model_name})")
|
| 106 |
+
|
| 107 |
model_results_dir = raw_results_dir / model_name
|
| 108 |
if not model_results_dir.exists():
|
| 109 |
print(f" 警告: 未找到模型 {model_name} 对应的结果文件夹: {model_results_dir}")
|
| 110 |
continue
|
| 111 |
+
|
| 112 |
scores_for_current_model = load_scores_for_model(model_results_dir)
|
| 113 |
+
|
| 114 |
+
processed_count = 0
|
| 115 |
with open(raw_data_file, 'r', encoding='utf-8') as f_raw:
|
| 116 |
for i, line in enumerate(f_raw):
|
| 117 |
try:
|
| 118 |
article_data = json.loads(line.strip())
|
| 119 |
article_id = str(article_data.get('id'))
|
|
|
|
| 120 |
if not article_id:
|
|
|
|
| 121 |
continue
|
| 122 |
+
|
| 123 |
article_scores = scores_for_current_model.get(article_id, {})
|
|
|
|
|
|
|
| 124 |
|
| 125 |
merged_item = {
|
| 126 |
'model_name': model_name,
|
| 127 |
'id': article_id,
|
| 128 |
'prompt': article_data.get('prompt'),
|
| 129 |
'article': article_data.get('article'),
|
| 130 |
+
'track': article_data.get('track') or article_scores.get('track') or 'subjective',
|
| 131 |
+
'language': article_data.get('language') or 'en',
|
| 132 |
'overall_score': article_scores.get('overall_score'),
|
| 133 |
+
'objective_score': article_scores.get('objective_score'),
|
| 134 |
+
'subjective_score': article_scores.get('subjective_score'),
|
| 135 |
+
'expert_score': article_scores.get('expert_score'),
|
| 136 |
+
'citation_score': article_scores.get('citation_score'),
|
| 137 |
+
'source_quality_score': article_scores.get('source_quality_score'),
|
| 138 |
+
'confidence': article_scores.get('confidence'),
|
| 139 |
+
'correct': article_scores.get('correct'),
|
| 140 |
'comprehensiveness_score': article_scores.get('comprehensiveness_score'),
|
| 141 |
'insight_score': article_scores.get('insight_score'),
|
| 142 |
'instruction_following_score': article_scores.get('instruction_following_score'),
|
| 143 |
+
'readability_score': article_scores.get('readability_score'),
|
| 144 |
}
|
| 145 |
all_merged_data.append(merged_item)
|
| 146 |
+
processed_count += 1
|
| 147 |
except json.JSONDecodeError as e:
|
| 148 |
+
print(f" 错误: 解析原始数据JSON时出错 ({raw_data_file.name}, 行 {i+1}): {e}")
|
| 149 |
except Exception as e:
|
| 150 |
+
print(f" 错误: 处理原始数据时出错 ({raw_data_file.name}, 行 {i+1}): {e}")
|
| 151 |
+
|
| 152 |
+
print(f" 为模型 {model_name} 处理了 {processed_count} 条数据。")
|
| 153 |
+
|
| 154 |
with open(output_file, 'w', encoding='utf-8') as f_out:
|
| 155 |
for item in all_merged_data:
|
| 156 |
f_out.write(json.dumps(item, ensure_ascii=False) + '\n')
|
| 157 |
+
|
| 158 |
print(f"\n成功合并并保存到: {output_file}, 共 {len(all_merged_data)} 条记录")
|
| 159 |
|
| 160 |
+
|
| 161 |
if __name__ == "__main__":
|
| 162 |
merge_jsonl_files()
|
| 163 |
+
print("所有文件处理完成!")
|
utils/parse_icbcbench_tex.py
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
# -*- coding: utf-8 -*-
|
| 3 |
+
"""
|
| 4 |
+
Parse ICBCBench LaTeX result tables and generate data/leaderboard.csv.
|
| 5 |
+
|
| 6 |
+
Reads:
|
| 7 |
+
- main_results.tex EN/ZH Objective/Subjective/Overall
|
| 8 |
+
- results_subset_en.tex EN Expert/Citation/Source details
|
| 9 |
+
- results_subset_zh.tex ZH Expert/Citation/Source details
|
| 10 |
+
- objective_public_CalibErr.tex All-language Accuracy / Calibration Error
|
| 11 |
+
"""
|
| 12 |
+
|
| 13 |
+
from __future__ import annotations
|
| 14 |
+
import re
|
| 15 |
+
import csv
|
| 16 |
+
from pathlib import Path
|
| 17 |
+
from collections import defaultdict
|
| 18 |
+
|
| 19 |
+
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def parse_main_results(tex_file: Path) -> dict[str, dict]:
|
| 23 |
+
"""Parse main_results.tex and return {model: {objective_en, subjective_en, overall_en, objective_zh, subjective_zh, overall_zh}}."""
|
| 24 |
+
text = tex_file.read_text(encoding="utf-8")
|
| 25 |
+
rows = {}
|
| 26 |
+
for line in text.splitlines():
|
| 27 |
+
line = line.strip()
|
| 28 |
+
if not line or line.startswith("\\") or line.startswith("%"):
|
| 29 |
+
continue
|
| 30 |
+
# Match lines like:
|
| 31 |
+
# Gemini-deep-research & 50.00 & 64.77 & \underline{57.38} & 52.50 & \textbf{65.69} & \underline{59.09} \\
|
| 32 |
+
parts = [p.strip() for p in line.split("&")]
|
| 33 |
+
if len(parts) != 7:
|
| 34 |
+
continue
|
| 35 |
+
model = parts[0].strip()
|
| 36 |
+
values = []
|
| 37 |
+
for p in parts[1:]:
|
| 38 |
+
# Strip LaTeX formatting commands but keep their arguments
|
| 39 |
+
val = re.sub(r"\\(textbf|underline|rowcolor)\{([^}]*)\}", r"\2", p)
|
| 40 |
+
# Drop structural LaTeX commands and trailing \
|
| 41 |
+
val = re.sub(r"\\(multicolumn|cmidrule|toprule|midrule|bottomrule).*", "", val)
|
| 42 |
+
val = val.replace("\\", "").replace("{", "").replace("}", "").strip()
|
| 43 |
+
if val in ("", "--"):
|
| 44 |
+
values.append(None)
|
| 45 |
+
else:
|
| 46 |
+
try:
|
| 47 |
+
values.append(float(val))
|
| 48 |
+
except ValueError:
|
| 49 |
+
values.append(None)
|
| 50 |
+
if all(v is not None for v in values):
|
| 51 |
+
rows[model] = {
|
| 52 |
+
"objective_en": values[0],
|
| 53 |
+
"subjective_en": values[1],
|
| 54 |
+
"overall_en": values[2],
|
| 55 |
+
"objective_zh": values[3],
|
| 56 |
+
"subjective_zh": values[4],
|
| 57 |
+
"overall_zh": values[5],
|
| 58 |
+
}
|
| 59 |
+
return rows
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def parse_subset(tex_file: Path) -> dict[str, dict]:
|
| 63 |
+
"""Parse results_subset_en.tex or results_subset_zh.tex.
|
| 64 |
+
Returns {model: {objective_text, objective_all, expert, citation, source, overall}}.
|
| 65 |
+
"""
|
| 66 |
+
text = tex_file.read_text(encoding="utf-8")
|
| 67 |
+
rows = {}
|
| 68 |
+
for line in text.splitlines():
|
| 69 |
+
line = line.strip()
|
| 70 |
+
if not line or line.startswith("\\") or line.startswith("%"):
|
| 71 |
+
continue
|
| 72 |
+
parts = [p.strip() for p in line.split("&")]
|
| 73 |
+
if len(parts) != 7:
|
| 74 |
+
continue
|
| 75 |
+
model = parts[0].strip()
|
| 76 |
+
values = []
|
| 77 |
+
for p in parts[1:]:
|
| 78 |
+
val = re.sub(r"\\(textbf|underline|rowcolor)\{([^}]*)\}", r"\2", p)
|
| 79 |
+
val = re.sub(r"\\(multicolumn|cmidrule|toprule|midrule|bottomrule).*", "", val)
|
| 80 |
+
val = val.replace("\\", "").replace("{", "").replace("}", "").strip()
|
| 81 |
+
if val in ("", "--"):
|
| 82 |
+
values.append(None)
|
| 83 |
+
else:
|
| 84 |
+
try:
|
| 85 |
+
values.append(float(val))
|
| 86 |
+
except ValueError:
|
| 87 |
+
values.append(None)
|
| 88 |
+
if values[-1] is not None: # overall is required
|
| 89 |
+
rows[model] = {
|
| 90 |
+
"objective_text": values[0],
|
| 91 |
+
"objective_all": values[1],
|
| 92 |
+
"expert": values[2],
|
| 93 |
+
"citation": values[3],
|
| 94 |
+
"source": values[4],
|
| 95 |
+
"overall": values[5],
|
| 96 |
+
}
|
| 97 |
+
return rows
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
def parse_calibration(tex_file: Path) -> dict[str, dict]:
|
| 101 |
+
"""Parse objective_public_CalibErr.tex. Returns {model: {accuracy, calibration_error}}."""
|
| 102 |
+
text = tex_file.read_text(encoding="utf-8")
|
| 103 |
+
rows = {}
|
| 104 |
+
for line in text.splitlines():
|
| 105 |
+
line = line.strip()
|
| 106 |
+
if not line or line.startswith("\\") or line.startswith("%"):
|
| 107 |
+
continue
|
| 108 |
+
parts = [p.strip() for p in line.split("&")]
|
| 109 |
+
if len(parts) != 3:
|
| 110 |
+
continue
|
| 111 |
+
model = parts[0].strip()
|
| 112 |
+
values = []
|
| 113 |
+
for p in parts[1:]:
|
| 114 |
+
val = p.replace("\\", "").strip()
|
| 115 |
+
if val in ("", "--"):
|
| 116 |
+
values.append(None)
|
| 117 |
+
else:
|
| 118 |
+
try:
|
| 119 |
+
values.append(float(val))
|
| 120 |
+
except ValueError:
|
| 121 |
+
values.append(None)
|
| 122 |
+
if all(v is not None for v in values):
|
| 123 |
+
rows[model] = {"accuracy": values[0], "calibration_error": values[1]}
|
| 124 |
+
return rows
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
def merge_scores(
|
| 128 |
+
main: dict[str, dict],
|
| 129 |
+
en_detail: dict[str, dict],
|
| 130 |
+
zh_detail: dict[str, dict],
|
| 131 |
+
calib: dict[str, dict],
|
| 132 |
+
) -> list[dict]:
|
| 133 |
+
models = sorted(main.keys())
|
| 134 |
+
results = []
|
| 135 |
+
for model in models:
|
| 136 |
+
m = main[model]
|
| 137 |
+
en = en_detail.get(model, {})
|
| 138 |
+
zh = zh_detail.get(model, {})
|
| 139 |
+
cal = calib.get(model, {})
|
| 140 |
+
|
| 141 |
+
# Aggregate citation / source from available detailed tables.
|
| 142 |
+
# Prefer EN values when both exist, otherwise use whichever is available.
|
| 143 |
+
citation = en.get("citation") if en.get("citation") is not None else zh.get("citation")
|
| 144 |
+
source = en.get("source") if en.get("source") is not None else zh.get("source")
|
| 145 |
+
|
| 146 |
+
# Expert score: average of EN and ZH expert if both exist.
|
| 147 |
+
experts = [v for v in [en.get("expert"), zh.get("expert")] if v is not None]
|
| 148 |
+
expert_avg = sum(experts) / len(experts) if experts else None
|
| 149 |
+
|
| 150 |
+
objective_avg = (m["objective_en"] + m["objective_zh"]) / 2
|
| 151 |
+
subjective_avg = (m["subjective_en"] + m["subjective_zh"]) / 2
|
| 152 |
+
overall = (m["overall_en"] + m["overall_zh"]) / 2
|
| 153 |
+
|
| 154 |
+
results.append({
|
| 155 |
+
"model": model,
|
| 156 |
+
"overall": overall,
|
| 157 |
+
"objective_en": m["objective_en"],
|
| 158 |
+
"objective_zh": m["objective_zh"],
|
| 159 |
+
"objective_avg": objective_avg,
|
| 160 |
+
"subjective_en": m["subjective_en"],
|
| 161 |
+
"subjective_zh": m["subjective_zh"],
|
| 162 |
+
"subjective_avg": subjective_avg,
|
| 163 |
+
"expert_avg": expert_avg,
|
| 164 |
+
"citation_score": citation,
|
| 165 |
+
"source_quality": source,
|
| 166 |
+
"rmsce": cal.get("calibration_error"),
|
| 167 |
+
})
|
| 168 |
+
|
| 169 |
+
# Sort by overall descending, then objective_avg, then subjective_avg.
|
| 170 |
+
results.sort(
|
| 171 |
+
key=lambda x: (x["overall"], x["objective_avg"], x["subjective_avg"]),
|
| 172 |
+
reverse=True,
|
| 173 |
+
)
|
| 174 |
+
return results
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
def write_leaderboard(results: list[dict], output_file: Path):
|
| 178 |
+
fieldnames = [
|
| 179 |
+
"model", "overall", "objective_en", "objective_zh", "objective_avg",
|
| 180 |
+
"subjective_en", "subjective_zh", "subjective_avg",
|
| 181 |
+
"expert_avg", "citation_score", "source_quality", "rmsce",
|
| 182 |
+
]
|
| 183 |
+
with open(output_file, "w", newline="", encoding="utf-8") as f:
|
| 184 |
+
writer = csv.DictWriter(f, fieldnames=fieldnames)
|
| 185 |
+
writer.writeheader()
|
| 186 |
+
for r in results:
|
| 187 |
+
row = {k: r[k] for k in fieldnames}
|
| 188 |
+
for k in fieldnames:
|
| 189 |
+
if k == "model":
|
| 190 |
+
continue
|
| 191 |
+
val = row[k]
|
| 192 |
+
row[k] = f"{val:.2f}" if val is not None else "-"
|
| 193 |
+
writer.writerow(row)
|
| 194 |
+
print(f"Wrote {len(results)} models to {output_file}")
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
def main():
|
| 198 |
+
main_file = PROJECT_ROOT / "main_results.tex"
|
| 199 |
+
en_file = PROJECT_ROOT / "results_subset_en.tex"
|
| 200 |
+
zh_file = PROJECT_ROOT / "results_subset_zh.tex"
|
| 201 |
+
calib_file = PROJECT_ROOT / "objective_public_CalibErr.tex"
|
| 202 |
+
output_file = PROJECT_ROOT / "data" / "leaderboard.csv"
|
| 203 |
+
|
| 204 |
+
main = parse_main_results(main_file)
|
| 205 |
+
en_detail = parse_subset(en_file)
|
| 206 |
+
zh_detail = parse_subset(zh_file)
|
| 207 |
+
calib = parse_calibration(calib_file)
|
| 208 |
+
|
| 209 |
+
print(f"Parsed {len(main)} models from main results")
|
| 210 |
+
print(f"Parsed {len(en_detail)} EN detailed rows")
|
| 211 |
+
print(f"Parsed {len(zh_detail)} ZH detailed rows")
|
| 212 |
+
print(f"Parsed {len(calib)} calibration rows")
|
| 213 |
+
|
| 214 |
+
results = merge_scores(main, en_detail, zh_detail, calib)
|
| 215 |
+
write_leaderboard(results, output_file)
|
| 216 |
+
|
| 217 |
+
|
| 218 |
+
if __name__ == "__main__":
|
| 219 |
+
main()
|
utils/rank_leaderboard.py
CHANGED
|
@@ -1,8 +1,18 @@
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
|
| 4 |
import json
|
| 5 |
-
import os
|
| 6 |
import csv
|
| 7 |
from pathlib import Path
|
| 8 |
from collections import defaultdict
|
|
@@ -14,9 +24,8 @@ EXCLUDED_SLUGS = {
|
|
| 14 |
|
| 15 |
|
| 16 |
def parse_race_result(race_result_file):
|
| 17 |
-
"""
|
| 18 |
scores = {}
|
| 19 |
-
|
| 20 |
with open(race_result_file, 'r', encoding='utf-8') as f:
|
| 21 |
for line in f:
|
| 22 |
line = line.strip()
|
|
@@ -24,7 +33,6 @@ def parse_race_result(race_result_file):
|
|
| 24 |
key, value = line.split(':', 1)
|
| 25 |
key = key.strip()
|
| 26 |
value = float(value.strip())
|
| 27 |
-
|
| 28 |
if key == 'Comprehensiveness':
|
| 29 |
scores['comprehensiveness'] = value * 100
|
| 30 |
elif key == 'Insight':
|
|
@@ -35,17 +43,14 @@ def parse_race_result(race_result_file):
|
|
| 35 |
scores['readability'] = value * 100
|
| 36 |
elif key == 'Overall Score':
|
| 37 |
scores['overall_score'] = value * 100
|
| 38 |
-
|
| 39 |
return scores
|
| 40 |
|
| 41 |
|
| 42 |
def parse_fact_result(fact_result_file):
|
| 43 |
-
"""
|
| 44 |
citation_scores = {}
|
| 45 |
-
|
| 46 |
if not fact_result_file.exists():
|
| 47 |
return citation_scores
|
| 48 |
-
|
| 49 |
with open(fact_result_file, 'r', encoding='utf-8') as f:
|
| 50 |
for line in f:
|
| 51 |
line = line.strip()
|
|
@@ -53,78 +58,196 @@ def parse_fact_result(fact_result_file):
|
|
| 53 |
key, value = line.split(':', 1)
|
| 54 |
key = key.strip()
|
| 55 |
value = float(value.strip())
|
| 56 |
-
|
| 57 |
if key == 'valid_rate':
|
| 58 |
citation_scores['citation_accuracy'] = value * 100
|
| 59 |
-
elif key == 'total_valid_citations':
|
| 60 |
-
citation_scores['effective_citations'] = value
|
| 61 |
elif key == 'supported_per_task':
|
| 62 |
citation_scores['effective_citations'] = value
|
| 63 |
-
|
| 64 |
return citation_scores
|
| 65 |
|
| 66 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
def process_model_data(model_dir):
|
| 68 |
-
"""
|
| 69 |
model_name = model_dir.name
|
|
|
|
| 70 |
race_result_file = model_dir / "race_result.txt"
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
print(f" -
|
| 83 |
return None
|
| 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 |
def rank_leaderboard():
|
| 116 |
-
"""
|
| 117 |
project_root = Path(__file__).parent.parent
|
| 118 |
input_dir = project_root / "data" / "raw_results"
|
| 119 |
output_file = project_root / "data" / "leaderboard.csv"
|
| 120 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
model_dirs = [d for d in input_dir.iterdir() if d.is_dir() and d.name not in EXCLUDED_SLUGS]
|
| 122 |
-
print(f"
|
| 123 |
-
|
| 124 |
if not model_dirs:
|
| 125 |
-
print("
|
| 126 |
return
|
| 127 |
-
|
| 128 |
model_results = []
|
| 129 |
for model_dir in model_dirs:
|
| 130 |
try:
|
|
@@ -132,37 +255,44 @@ def rank_leaderboard():
|
|
| 132 |
if result:
|
| 133 |
model_results.append(result)
|
| 134 |
except Exception as e:
|
| 135 |
-
print(f"
|
| 136 |
continue
|
| 137 |
-
|
| 138 |
-
#
|
| 139 |
-
model_results.sort(
|
| 140 |
-
|
| 141 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 142 |
with open(output_file, 'w', newline='', encoding='utf-8') as csvfile:
|
| 143 |
-
fieldnames = ['model', 'overall_score', 'comprehensiveness', 'insight', 'instruction_following', 'readability', 'citation_accuracy', 'effective_citations']
|
| 144 |
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
|
| 145 |
-
|
| 146 |
writer.writeheader()
|
| 147 |
-
|
| 148 |
for result in model_results:
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
'effective_citations': f"{result['effective_citations']:.2f}" if result['effective_citations'] is not None else "-"
|
| 159 |
-
}
|
| 160 |
writer.writerow(row)
|
| 161 |
-
|
| 162 |
-
print(f"\
|
| 163 |
-
print(f"
|
| 164 |
|
| 165 |
|
| 166 |
if __name__ == "__main__":
|
| 167 |
rank_leaderboard()
|
| 168 |
-
print("
|
|
|
|
| 1 |
#!/usr/bin/env python3
|
| 2 |
# -*- coding: utf-8 -*-
|
| 3 |
+
"""
|
| 4 |
+
Rank leaderboard for ICBCBench.
|
| 5 |
+
Reads per-model result files from data/raw_results/ and writes data/leaderboard.csv.
|
| 6 |
+
|
| 7 |
+
Expected input formats:
|
| 8 |
+
1. ICBCBench JSONL: data/raw_results/<model>/results.jsonl
|
| 9 |
+
Each line contains {"language": "en"|"zh", "track": "objective"|"subjective",
|
| 10 |
+
"score": float, "citation_score": float (optional),
|
| 11 |
+
"source_quality_score": float (optional), ...}
|
| 12 |
+
2. Legacy DeepResearch Bench race_result.txt + fact_result.txt (kept for compatibility).
|
| 13 |
+
"""
|
| 14 |
|
| 15 |
import json
|
|
|
|
| 16 |
import csv
|
| 17 |
from pathlib import Path
|
| 18 |
from collections import defaultdict
|
|
|
|
| 24 |
|
| 25 |
|
| 26 |
def parse_race_result(race_result_file):
|
| 27 |
+
"""Legacy DeepResearch Bench: parse race_result.txt."""
|
| 28 |
scores = {}
|
|
|
|
| 29 |
with open(race_result_file, 'r', encoding='utf-8') as f:
|
| 30 |
for line in f:
|
| 31 |
line = line.strip()
|
|
|
|
| 33 |
key, value = line.split(':', 1)
|
| 34 |
key = key.strip()
|
| 35 |
value = float(value.strip())
|
|
|
|
| 36 |
if key == 'Comprehensiveness':
|
| 37 |
scores['comprehensiveness'] = value * 100
|
| 38 |
elif key == 'Insight':
|
|
|
|
| 43 |
scores['readability'] = value * 100
|
| 44 |
elif key == 'Overall Score':
|
| 45 |
scores['overall_score'] = value * 100
|
|
|
|
| 46 |
return scores
|
| 47 |
|
| 48 |
|
| 49 |
def parse_fact_result(fact_result_file):
|
| 50 |
+
"""Legacy DeepResearch Bench: parse fact_result.txt."""
|
| 51 |
citation_scores = {}
|
|
|
|
| 52 |
if not fact_result_file.exists():
|
| 53 |
return citation_scores
|
|
|
|
| 54 |
with open(fact_result_file, 'r', encoding='utf-8') as f:
|
| 55 |
for line in f:
|
| 56 |
line = line.strip()
|
|
|
|
| 58 |
key, value = line.split(':', 1)
|
| 59 |
key = key.strip()
|
| 60 |
value = float(value.strip())
|
|
|
|
| 61 |
if key == 'valid_rate':
|
| 62 |
citation_scores['citation_accuracy'] = value * 100
|
|
|
|
|
|
|
| 63 |
elif key == 'supported_per_task':
|
| 64 |
citation_scores['effective_citations'] = value
|
|
|
|
| 65 |
return citation_scores
|
| 66 |
|
| 67 |
|
| 68 |
+
def parse_icbcbench_jsonl(results_file):
|
| 69 |
+
"""Parse ICBCBench results.jsonl and aggregate scores."""
|
| 70 |
+
scores_by_lang_track = defaultdict(list)
|
| 71 |
+
citation_scores = []
|
| 72 |
+
source_quality_scores = []
|
| 73 |
+
confidence_pairs = [] # (correct, confidence)
|
| 74 |
+
|
| 75 |
+
with open(results_file, 'r', encoding='utf-8') as f:
|
| 76 |
+
for line in f:
|
| 77 |
+
line = line.strip()
|
| 78 |
+
if not line:
|
| 79 |
+
continue
|
| 80 |
+
try:
|
| 81 |
+
item = json.loads(line)
|
| 82 |
+
except json.JSONDecodeError:
|
| 83 |
+
continue
|
| 84 |
+
|
| 85 |
+
language = (item.get("language") or item.get("lang") or "en").lower()
|
| 86 |
+
track = (item.get("track") or "subjective").lower()
|
| 87 |
+
score = item.get("score")
|
| 88 |
+
|
| 89 |
+
if score is not None:
|
| 90 |
+
try:
|
| 91 |
+
score = float(score)
|
| 92 |
+
if score <= 1.0:
|
| 93 |
+
score *= 100
|
| 94 |
+
scores_by_lang_track[(language, track)].append(score)
|
| 95 |
+
except (TypeError, ValueError):
|
| 96 |
+
pass
|
| 97 |
+
|
| 98 |
+
if "citation_score" in item and item["citation_score"] is not None:
|
| 99 |
+
try:
|
| 100 |
+
val = float(item["citation_score"])
|
| 101 |
+
if val <= 1.0:
|
| 102 |
+
val *= 100
|
| 103 |
+
citation_scores.append(val)
|
| 104 |
+
except (TypeError, ValueError):
|
| 105 |
+
pass
|
| 106 |
+
|
| 107 |
+
if "source_quality_score" in item and item["source_quality_score"] is not None:
|
| 108 |
+
try:
|
| 109 |
+
val = float(item["source_quality_score"])
|
| 110 |
+
if val <= 1.0:
|
| 111 |
+
val *= 100
|
| 112 |
+
source_quality_scores.append(val)
|
| 113 |
+
except (TypeError, ValueError):
|
| 114 |
+
pass
|
| 115 |
+
|
| 116 |
+
if track == "objective":
|
| 117 |
+
correct = item.get("correct")
|
| 118 |
+
confidence = item.get("confidence")
|
| 119 |
+
if correct is not None and confidence is not None:
|
| 120 |
+
try:
|
| 121 |
+
confidence = float(confidence)
|
| 122 |
+
if confidence <= 1.0:
|
| 123 |
+
confidence *= 100
|
| 124 |
+
confidence_pairs.append((bool(correct), confidence))
|
| 125 |
+
except (TypeError, ValueError):
|
| 126 |
+
pass
|
| 127 |
+
|
| 128 |
+
def avg(vals):
|
| 129 |
+
return sum(vals) / len(vals) if vals else None
|
| 130 |
+
|
| 131 |
+
def compute_rmsce(pairs):
|
| 132 |
+
"""Compute RMSCE from (correct, confidence) pairs using 10-sample bins."""
|
| 133 |
+
if not pairs:
|
| 134 |
+
return None
|
| 135 |
+
pairs = sorted(pairs, key=lambda x: x[1])
|
| 136 |
+
n = len(pairs)
|
| 137 |
+
if n == 0:
|
| 138 |
+
return None
|
| 139 |
+
bin_size = max(1, min(10, n))
|
| 140 |
+
num_bins = max(1, n // bin_size)
|
| 141 |
+
rmsce = 0.0
|
| 142 |
+
count = 0
|
| 143 |
+
for i in range(num_bins):
|
| 144 |
+
start = i * bin_size
|
| 145 |
+
end = start + bin_size if i < num_bins - 1 else n
|
| 146 |
+
bin_pairs = pairs[start:end]
|
| 147 |
+
acc = sum(1 for c, _ in bin_pairs if c) / len(bin_pairs)
|
| 148 |
+
conf = sum(conf for _, conf in bin_pairs) / len(bin_pairs)
|
| 149 |
+
rmsce += len(bin_pairs) * (acc - conf) ** 2
|
| 150 |
+
count += len(bin_pairs)
|
| 151 |
+
return (rmsce / count) ** 0.5 if count > 0 else None
|
| 152 |
+
|
| 153 |
+
result = {
|
| 154 |
+
'objective_en': avg(scores_by_lang_track.get(("en", "objective"), [])),
|
| 155 |
+
'objective_zh': avg(scores_by_lang_track.get(("zh", "objective"), [])),
|
| 156 |
+
'subjective_en': avg(scores_by_lang_track.get(("en", "subjective"), [])),
|
| 157 |
+
'subjective_zh': avg(scores_by_lang_track.get(("zh", "subjective"), [])),
|
| 158 |
+
'citation_score': avg(citation_scores),
|
| 159 |
+
'source_quality': avg(source_quality_scores),
|
| 160 |
+
'rmsce': compute_rmsce(confidence_pairs),
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
# Compute averages and overall.
|
| 164 |
+
obj_scores = [v for v in [result['objective_en'], result['objective_zh']] if v is not None]
|
| 165 |
+
subj_scores = [v for v in [result['subjective_en'], result['subjective_zh']] if v is not None]
|
| 166 |
+
result['objective_avg'] = avg(obj_scores)
|
| 167 |
+
result['subjective_avg'] = avg(subj_scores)
|
| 168 |
+
|
| 169 |
+
# Overall = weighted average of available objective and subjective averages.
|
| 170 |
+
overall_parts = []
|
| 171 |
+
if result['objective_avg'] is not None:
|
| 172 |
+
overall_parts.append(result['objective_avg'])
|
| 173 |
+
if result['subjective_avg'] is not None:
|
| 174 |
+
overall_parts.append(result['subjective_avg'])
|
| 175 |
+
result['overall'] = avg(overall_parts)
|
| 176 |
+
|
| 177 |
+
return result
|
| 178 |
+
|
| 179 |
+
|
| 180 |
def process_model_data(model_dir):
|
| 181 |
+
"""Process a single model directory and return aggregated scores."""
|
| 182 |
model_name = model_dir.name
|
| 183 |
+
results_file = model_dir / "results.jsonl"
|
| 184 |
race_result_file = model_dir / "race_result.txt"
|
| 185 |
+
|
| 186 |
+
print(f"Processing model: {model_name}")
|
| 187 |
+
|
| 188 |
+
# Prefer ICBCBench JSONL format.
|
| 189 |
+
if results_file.exists():
|
| 190 |
+
try:
|
| 191 |
+
result = parse_icbcbench_jsonl(results_file)
|
| 192 |
+
result['model'] = model_name
|
| 193 |
+
print(f" - overall: {result['overall']:.2f}" if result['overall'] else " - overall: N/A")
|
| 194 |
+
return result
|
| 195 |
+
except Exception as e:
|
| 196 |
+
print(f" - error parsing results.jsonl: {e}")
|
| 197 |
return None
|
| 198 |
+
|
| 199 |
+
# Fallback to legacy DeepResearch Bench format.
|
| 200 |
+
if race_result_file.exists():
|
| 201 |
+
try:
|
| 202 |
+
scores = parse_race_result(race_result_file)
|
| 203 |
+
if not scores:
|
| 204 |
+
print(f" - warning: no valid scores in race_result.txt")
|
| 205 |
+
return None
|
| 206 |
+
|
| 207 |
+
project_root = Path(__file__).parent.parent
|
| 208 |
+
fact_result_file = project_root / "data" / "fact_results" / model_name / "fact_result.txt"
|
| 209 |
+
citation_scores = parse_fact_result(fact_result_file)
|
| 210 |
+
|
| 211 |
+
result = {
|
| 212 |
+
'model': model_name,
|
| 213 |
+
'overall': scores['overall_score'],
|
| 214 |
+
'objective_en': None,
|
| 215 |
+
'objective_zh': None,
|
| 216 |
+
'objective_avg': None,
|
| 217 |
+
'subjective_en': None,
|
| 218 |
+
'subjective_zh': None,
|
| 219 |
+
'subjective_avg': scores['overall_score'],
|
| 220 |
+
'citation_score': citation_scores.get('citation_accuracy'),
|
| 221 |
+
'source_quality': None,
|
| 222 |
+
'rmsce': None,
|
| 223 |
+
}
|
| 224 |
+
print(f" - overall (legacy): {result['overall']:.2f}")
|
| 225 |
+
return result
|
| 226 |
+
except Exception as e:
|
| 227 |
+
print(f" - error: {e}")
|
| 228 |
+
return None
|
| 229 |
+
|
| 230 |
+
print(f" - warning: no results.jsonl or race_result.txt found")
|
| 231 |
+
return None
|
| 232 |
|
| 233 |
|
| 234 |
def rank_leaderboard():
|
| 235 |
+
"""Compute leaderboard and save to CSV."""
|
| 236 |
project_root = Path(__file__).parent.parent
|
| 237 |
input_dir = project_root / "data" / "raw_results"
|
| 238 |
output_file = project_root / "data" / "leaderboard.csv"
|
| 239 |
+
|
| 240 |
+
if not input_dir.exists():
|
| 241 |
+
print(f"Input directory not found: {input_dir}")
|
| 242 |
+
return
|
| 243 |
+
|
| 244 |
model_dirs = [d for d in input_dir.iterdir() if d.is_dir() and d.name not in EXCLUDED_SLUGS]
|
| 245 |
+
print(f"Found {len(model_dirs)} model directories (excluded {len(EXCLUDED_SLUGS)})")
|
| 246 |
+
|
| 247 |
if not model_dirs:
|
| 248 |
+
print("No model data found.")
|
| 249 |
return
|
| 250 |
+
|
| 251 |
model_results = []
|
| 252 |
for model_dir in model_dirs:
|
| 253 |
try:
|
|
|
|
| 255 |
if result:
|
| 256 |
model_results.append(result)
|
| 257 |
except Exception as e:
|
| 258 |
+
print(f"Error processing {model_dir.name}: {e}")
|
| 259 |
continue
|
| 260 |
+
|
| 261 |
+
# Sort by overall, then objective_avg, then subjective_avg.
|
| 262 |
+
model_results.sort(
|
| 263 |
+
key=lambda x: (
|
| 264 |
+
x['overall'] if x['overall'] is not None else -1,
|
| 265 |
+
x['objective_avg'] if x['objective_avg'] is not None else -1,
|
| 266 |
+
x['subjective_avg'] if x['subjective_avg'] is not None else -1,
|
| 267 |
+
),
|
| 268 |
+
reverse=True
|
| 269 |
+
)
|
| 270 |
+
|
| 271 |
+
fieldnames = [
|
| 272 |
+
'model', 'overall', 'objective_en', 'objective_zh',
|
| 273 |
+
'objective_avg', 'subjective_en', 'subjective_zh',
|
| 274 |
+
'subjective_avg', 'citation_score', 'source_quality', 'rmsce'
|
| 275 |
+
]
|
| 276 |
+
|
| 277 |
with open(output_file, 'w', newline='', encoding='utf-8') as csvfile:
|
|
|
|
| 278 |
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
|
|
|
|
| 279 |
writer.writeheader()
|
|
|
|
| 280 |
for result in model_results:
|
| 281 |
+
row = {k: result.get(k) for k in fieldnames}
|
| 282 |
+
for k in fieldnames:
|
| 283 |
+
if k == 'model':
|
| 284 |
+
continue
|
| 285 |
+
val = row[k]
|
| 286 |
+
if val is None:
|
| 287 |
+
row[k] = "-"
|
| 288 |
+
else:
|
| 289 |
+
row[k] = f"{float(val):.2f}"
|
|
|
|
|
|
|
| 290 |
writer.writerow(row)
|
| 291 |
+
|
| 292 |
+
print(f"\nLeaderboard saved to: {output_file}")
|
| 293 |
+
print(f"Total models: {len(model_results)}")
|
| 294 |
|
| 295 |
|
| 296 |
if __name__ == "__main__":
|
| 297 |
rank_leaderboard()
|
| 298 |
+
print("Done!")
|