Leonnel1220 commited on
Commit
5148820
·
verified ·
1 Parent(s): 10e99ae

Upload folder using huggingface_hub

Browse files
.gitignore CHANGED
@@ -1,10 +1,6 @@
 
1
  __pycache__/
2
- *.py[cod]
3
- *$py.class
4
- .env
5
- .venv
6
- env/
7
- venv/
8
- ENV/
9
  .DS_Store
10
- *.log# trigger
 
 
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: DeepResearch Bench
3
- emoji: 🔍
4
- colorFrom: blue
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
- # DeepResearch Bench Leaderboard
13
 
14
- A comprehensive benchmark and leaderboard for evaluating Deep Research Agents.
15
 
16
  ## Overview
17
- This Hugging Face Space hosts the interactive leaderboard for the DeepResearch Bench project.
18
 
19
- Visit our [project website](https://deepresearch-bench.github.io) for more information.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
- ### Local Development
22
  ```bash
23
  pip install -r requirements.txt
 
 
24
  python app.py
25
  ```
26
 
27
- ## Hugging Face Space Details
28
- - SDK: Docker
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- DeepResearch Bench HF Space 入口文件
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 – v2.1 (Leaderboard · Data Viewer · Prompt-to-Leaderboard)
 
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.leaderboard_tab_gpt55 import create_leaderboard_tab_gpt55
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
  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 21, "02 August 2025"
 
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="DeepResearch Bench") as demo:
39
 
40
- # ========= 全局 CSS(仅作用于自定义标题 & 简介) =========
41
  gr.HTML("""
42
  <style>
43
  .title-block{
44
- /* 渐变文字效果 - 改进版 */
45
- background: linear-gradient(to right, #009CFF, #823AFF);
46
- background: -webkit-linear-gradient(to right, #009CFF, #823AFF);
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
- # ========= 顶部标题 & 简介(不使用 Markdown 标题语法) =========
76
  gr.HTML(f"""
77
  <div class="title-block">
78
- DeepResearch Bench: A Comprehensive Benchmark for Deep Research Agents
79
  </div>
80
 
81
  <div class="intro-block">
82
- The research aims to comprehensively evaluate the capabilities of Deep Research Agents.<br>
83
- <a href="https://github.com/Ayanami0730/deep_research_bench" target="_blank">Code</a> |
84
- <a href="https://deepresearch-bench.github.io" target="_blank">Website</a> |
85
- <a href="https://arxiv.org/abs/2506.11763" target="_blank">Paper</a> |
86
- <a href="#" target="_blank">Eval Dataset</a> |
 
87
  Total models: {model_count} | Last Update: {last_update}<br>
88
  <small style="color: #666; font-size: 0.9em;">
89
- Leaderboard tab — Race judge: GPT-5.5 | Fact-check: GPT-5.4-mini<br>
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
- # ========= Tabs =========
96
  with gr.Tabs():
97
- create_leaderboard_tab_gpt55() # 🏆 Leaderboard
98
- create_leaderboard_tab() # 🏆 Leaderboard (Gemini-2.5 Eval)
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
- with gr.Tab("💬Prompt-to-Leaderboard"):
104
- gr.Markdown(
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{du2025deepresearch,
167
- author = {Mingxuan Du and Benfeng Xu and Chiwei Zhu and Xiaorui Wang and Zhendong Mao},
168
- title = {DeepResearch Bench: A Comprehensive Benchmark for Deep Research Agents},
169
- journal = {arXiv preprint},
170
- year = {2025},
171
  }</div>
172
  </div>
173
-
174
  <script>
175
  function copyCitation() {
176
- const citationText = `@article{du2025deepresearch,
177
- author = {Mingxuan Du and Benfeng Xu and Chiwei Zhu and Xiaorui Wang and Zhendong Mao},
178
- title = {DeepResearch Bench: A Comprehensive Benchmark for Deep Research Agents},
179
- journal = {arXiv preprint},
180
- year = {2025},
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 &nbsp;|&nbsp; 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,overall_score,comprehensiveness,insight,instruction_following,readability,citation_accuracy,effective_citations
2
- qianfan_deepresearch_0430,58.03,59.48,61.48,53.87,54.34,-,-
3
- ZTE-Nebula-DeepResearch-V20260519,57.27,58.37,59.76,54.06,54.66,-,-
4
- Link,57.08,58.24,59.74,53.24,55.05,-,-
5
- zhipu_deep_research,57.06,58.15,60.14,53.47,53.88,-,-
6
- xiaoyi,57.00,58.58,59.38,53.58,53.99,-,-
7
- WhaleCloud-DocChain,56.81,57.13,59.30,53.98,54.97,-,-
8
- cellcog-max,56.67,57.40,60.01,53.25,53.21,-,-
9
- 1688AILab-DeepResearch-0428,56.53,57.32,59.27,53.51,53.36,-,-
10
- octen-deepresearch-0508,56.31,56.89,59.00,53.39,53.83,-,-
11
- grep-v5,56.23,56.82,58.92,53.38,53.44,-,-
12
- nvidia-aiq-nemotron-gpt52-updated,55.95,56.90,58.49,52.89,53.43,-,-
13
- 1688AILab-DeepResearch-0325,55.39,55.48,57.59,53.38,53.50,-,-
14
- ms_deepresearch_gpt52mixqwen35_09_edit_restart09_think_medium,55.31,56.76,56.79,53.10,52.28,-,-
15
- drb_cellcog,55.31,55.41,58.21,52.50,53.12,-,-
16
- deepinsight,55.24,55.66,58.70,52.53,50.94,-,-
17
- ms_deepresearch,54.97,56.45,56.22,53.25,51.71,-,-
18
- TrajectoryKit,54.92,54.10,57.90,52.91,52.72,-,-
19
- onyx,54.54,54.67,56.43,53.08,52.02,-,-
20
- deepsynth,54.22,54.23,56.09,52.86,51.81,-,-
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
- def make_scores_html(overall, comprehensiveness, insight, instruction, readability):
54
- scores_data = [
55
- ("Overall<br>Score", overall),
56
- ("Comprehen-<br>siveness", comprehensiveness),
57
- ("Insight<br>Score", insight),
58
- ("Instruction<br>Following", instruction),
59
- ("Readability<br>Score", readability)
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;">{score_value}</p>
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(None, None, None, None, None)
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["article"])
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["article"])
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(None, None, None, None, None)
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
- if (i < len(lines) - 1 and
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
- def make_scores_html(overall, comprehensiveness, insight, instruction, readability):
76
- scores_data = [
77
- ("Overall Score", overall),
78
- ("Comprehensiveness Score", comprehensiveness),
79
- ("Insight Score", insight),
80
- ("Instruction-Following Score", instruction),
81
- ("Readability Score", readability)
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.2em; color: #4a4a4a; font-weight: 600;">{title}</h4>
90
- <p style="margin: 0; font-size: 1.2em; font-weight: bold; color: #333;">{score_value}</p>
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 = 30 if int(item_id) <= 50 else 60
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["prompt"]
155
- article = entry["article"]
156
- overall = entry["overall_score"]
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 = "Deep Research Agent"
13
  HIGHLIGHT_EMOJI = "🚀"
14
 
15
- # 列名重命名映射
16
  COLUMN_RENAME_MAP = {
17
- 'overall_score': 'overall',
18
- 'comprehensiveness': 'comp.',
19
- 'insight': 'insight',
20
- 'instruction_following': 'inst.',
21
- 'readability': 'read.',
22
- 'citation_accuracy': 'c.acc.',
23
- 'effective_citations': 'eff.c.'
 
 
 
 
24
  }
25
 
 
26
  MODEL_DISPLAY_NAMES = {
27
- "gensee-search-gpt-5": "langchain-open-deep-research(GPT-5,with gensee search)",
28
- "langchain-open-deep-research-gpt-5": "langchain-open-deep-research(GPT-5,with Tavily)",
29
- "qianfan_deepresearch_0430": "DuMate/Qianfan-DeepResearch",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  "onyx": "Onyx Deep Research",
31
- "raaa-deep-research": "UESTC-MBSE-RAAA-DeepResearch",
32
- "deepsynth": "Bodhi Deep Research",
33
- "xiaoyi": "Xiaoyi DeepResearch 6.0",
34
- "nvidia-aiq-nemotron-gpt52-updated": "nvidia-aiq (Nemotron 3, GPT 5.2)",
35
- "deepinsight": "CMCC-DeepInsight",
36
  "drb_cellcog": "Cellcog",
37
- "cellcog-max": "Cellcog Max",
38
  "RecallRadar": "RecallRadar Intelligence",
39
- "ms_deepresearch": "MS-Agent Agentic Insight v2\uff08Qwen3.5-Plus\u3001GPT 5\uff09",
40
- "TrajectoryKit": "TrajectoryKit (GPT-OSS, GPT5.4)",
41
- "1688AILab-DeepResearch-0428": "1688AILab-DeepResearch",
42
  "zhipu_deep_research": "Zhipu Deep Research",
43
- "grep-v5": "Grep Deep Research",
44
- "MindDR-V1.5": "LiAuto Mind DeepResearch 1.5",
45
- "deepdog": "Deep Dog 1",
46
- "Link": "iFlow-Researcher",
47
  "octen-deepresearch-0508": "Octen Deep Research",
48
- "ZTE-Nebula-DeepResearch-V20260519": "ZTE-Nebula-DeepResearch",
49
- "WhaleCloud-DocChain": "WhaleCloud-DocChain"
 
 
 
 
 
 
 
 
 
 
 
 
50
  }
51
 
52
  # 模型分类映射
53
  MODEL_CATEGORIES = {
54
- "Deep Research Agent": [
55
- "gemini-2.5-pro-deepresearch",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  "openai-deepresearch",
57
- "gensee-search-gpt-5",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- "xiaoyi",
68
  "nvidia-aiq-nemotron-gpt52-updated",
69
  "deepinsight",
70
  "drb_cellcog",
71
- "cellcog-max",
72
  "RecallRadar",
73
  "ms_deepresearch",
74
  "TrajectoryKit",
75
- "1688AILab-DeepResearch-0428",
76
  "zhipu_deep_research",
77
- "ms_deepresearch_gpt52mixqwen35_09_edit_restart09_think_medium",
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
- # Deep Research Agent
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  "gemini-2.5-pro-deepresearch": "https://gemini.google/overview/deep-research/",
94
- "openai-deepresearch": "https://openai.com/zh-Hans-CN/index/introducing-deep-research/",
95
- "gensee-search-gpt-5": "https://github.com/GenseeAI/open_deep_research",
 
 
 
 
 
 
 
 
 
 
 
96
  "langchain-open-deep-research-gpt-5": "https://github.com/langchain-ai/open_deep_research",
97
- "cellcog": "https://www.cellcog.ai/",
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
- # LLM with Search
 
 
 
 
 
 
 
 
 
124
  }
125
 
126
  # 模型许可证类型映射
127
  MODEL_LICENSE_TYPE = {
128
- # Deep Research Agent
129
- "gemini-2.5-pro-deepresearch": "Proprietary",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  "openai-deepresearch": "Proprietary",
131
- "gensee-search-gpt-5": "MIT",
 
 
 
132
  "langchain-open-deep-research-gpt-5": "MIT",
133
- "cellcog": "Proprietary",
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
- "xiaoyi": "Proprietary",
142
- "nvidia-aiq-nemotron-gpt52-updated": "Apache-2.0 license",
143
  "deepinsight": "Proprietary",
144
  "drb_cellcog": "Proprietary",
145
- "cellcog-max": "Proprietary",
146
  "RecallRadar": "Proprietary",
147
- "ms_deepresearch": "Apache-2.0 license",
148
  "TrajectoryKit": "MIT",
149
- "1688AILab-DeepResearch-0428": "Proprietary",
150
  "zhipu_deep_research": "Proprietary",
151
- "ms_deepresearch_gpt52mixqwen35_09_edit_restart09_think_medium": "Apache-2.0 license",
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
- # LLM with Search
 
 
 
 
 
 
 
 
 
 
 
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
- return MODEL_LICENSE_TYPE.get(model_name, "Unknown")
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
- ranked = df.sort_values(by=['overall_score', 'comprehensiveness', 'insight', 'instruction_following', 'readability'], ascending=False).reset_index(drop=True)
186
- # 使用 min 方法处理并列排名:同分同名次,下一名跳过(如 1,1,3)
187
- ranked.insert(0, "Rank", ranked['overall_score'].rank(method='min', ascending=False).astype(int))
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
- else:
204
- return str(rank_num)
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 = ['overall', 'comp.', 'insight', 'inst.', 'read.', 'c.acc.', 'eff.c.']
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 = MODEL_DISPLAY_NAMES.get(model_name, 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: #823AFF;">{HIGHLIGHT_EMOJI} {display_model_name}</span>'
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
- else:
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
- ranked_df = make_ranked(df)
254
- return ranked_df
255
 
256
  def create_leaderboard_tab():
257
- with gr.Tab("🏆Leaderboard (Gemini-2.5 Eval)"):
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
- initial_df = filter_data("", list(MODEL_CATEGORIES.keys()))
274
-
275
- # 获取列数据类型,将 model 列和 Rank 列设置为 html
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" # Rank 列也需要渲染 HTML
282
-
283
- # 创建 Dataframe 组件
 
 
 
 
 
 
284
  table = gr.Dataframe(
285
  value=initial_df,
286
- datatype=datatypes, # 设置数据类型,model 列为 html
287
- max_height=600, # 设置表格最大高度
288
- show_label=False, # 不显示标签
289
- elem_id="leaderboard_table", # 添加元素ID
290
- interactive=False, # 禁用编辑功能
291
- wrap=True, # 启用自动换行,超长文本会自动换行显示
292
- column_widths=["80px", "380px", "100px", "100px", "100px", "100px", "100px", "100px", "100px", "180px", "150px"] # 设置各列宽度,model列设置为450px以完整显示长名称
293
  )
294
 
295
  def update_display(search_text, selected_categories):
296
- df = filter_data(search_text, selected_categories)
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: #823AFF;">{HIGHLIGHT_EMOJI} = {CATEGORY_TO_HIGHLIGHT}</span>)
317
- - **overall**: Overall Score (weighted average of all metrics)
318
- - **comp.**: Comprehensiveness - How thorough and complete the research is
319
- - **insight**: Insight Quality - Depth and value of analysis
320
- - **inst.**: Instruction Following - Adherence to user instructions
321
- - **read.**: Readability - Clarity and organization of content
322
- - **c.acc.**: Citation Accuracy - Correctness of references
323
- - **eff.c.**: Effective Citations - Relevance and quality of sources
 
 
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 GitHub repositories for more details!
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", "overall_score",
 
 
 
 
 
 
 
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
- raw_results_file = model_results_dir / "raw_results.jsonl"
17
-
18
- if not raw_results_file.exists():
19
- print(f"警告: 未找到模型 {model_results_dir.name} 的结果文件: {raw_results_file}")
 
 
 
 
20
  return scores_by_id
21
 
22
- print(f" 正在从 {model_results_dir.name}/raw_results.jsonl 加载分数...")
23
- with open(raw_results_file, 'r', encoding='utf-8') as f:
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
- overall_score_raw = data.get('overall_score', 0.0)
33
- overall_score_scaled = overall_score_raw * 100
34
-
35
- comprehensiveness_score_raw = data.get('comprehensiveness', 0.0)
36
- insight_score_raw = data.get('insight', 0.0)
37
- instruction_score_raw = data.get('instruction_following', 0.0)
38
- readability_score_raw = data.get('readability', 0.0)
39
-
40
- scores_by_id[article_id] = {
41
- 'overall_score': f"{overall_score_scaled:.2f}",
42
- 'comprehensiveness_score': f"{comprehensiveness_score_raw * 100:.2f}",
43
- 'insight_score': f"{insight_score_raw * 100:.2f}",
44
- 'instruction_following_score': f"{instruction_score_raw * 100:.2f}",
45
- 'readability_score': f"{readability_score_raw * 100:.2f}"
 
46
  }
 
47
  except json.JSONDecodeError as e:
48
- print(f" 错误: 解析JSON时出错 (文件: {model_results_dir.name}, 行: {i+1}): {e}")
49
  except Exception as e:
50
- print(f" 错误: 处理数据时出错 (文件: {model_results_dir.name}, 行: {i+1}): {e}")
51
- print(f" 为模型 {model_results_dir.name} 加载了 {len(scores_by_id)}篇文章的分数")
 
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
- processed_articles_count = 0
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
- processed_articles_count += 1
115
  except json.JSONDecodeError as e:
116
- print(f" 错误: 解析原始数据JSON时出错 (文件: {raw_data_file.name}, 行: {i+1}): {e}")
117
  except Exception as e:
118
- print(f" 错误: 处理原始数据时出错 (文件: {raw_data_file.name}, 行: {i+1}): {e}")
119
- print(f" 为模型 {model_name} 处理了 {processed_articles_count} 篇文章数据。")
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
- """解析race_result.txt文件获取各维度分数"""
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
- """解析fact_result.txt文件获取引用相关指标"""
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
- if not race_result_file.exists():
73
- print(f"警告: 模型 {model_name} 的文件夹中未找到 race_result.txt")
74
- return None
75
-
76
- print(f"正在处理模型: {model_name}")
77
-
78
- try:
79
- scores = parse_race_result(race_result_file)
80
-
81
- if not scores:
82
- print(f" - 警告: 未能解析到有效分数")
83
  return None
84
-
85
- # 查找对应的fact_result.txt文件
86
- project_root = Path(__file__).parent.parent
87
- fact_results_dir = project_root / "data" / "fact_results"
88
- fact_result_file = fact_results_dir / model_name / "fact_result.txt"
89
-
90
- citation_scores = parse_fact_result(fact_result_file)
91
-
92
- if citation_scores:
93
- print(f" - 总分: {scores['overall_score']:.2f}, 引用准确率: {citation_scores.get('citation_accuracy', 'N/A'):.2f}%, 有效引用数: {citation_scores.get('effective_citations', 'N/A')}")
94
- else:
95
- print(f" - 总分: {scores['overall_score']:.2f}, 引用数据: 未找到")
96
-
97
- result = {
98
- 'model': model_name,
99
- 'overall_score': scores['overall_score'],
100
- 'comprehensiveness': scores['comprehensiveness'],
101
- 'insight': scores['insight'],
102
- 'instruction_following': scores['instruction_following'],
103
- 'readability': scores['readability'],
104
- 'citation_accuracy': citation_scores.get('citation_accuracy', None),
105
- 'effective_citations': citation_scores.get('effective_citations', None)
106
- }
107
-
108
- return result
109
-
110
- except Exception as e:
111
- print(f" - 错误: 处理文件时出错: {e}")
112
- return None
 
 
 
 
 
113
 
114
 
115
  def rank_leaderboard():
116
- """计算排行榜并保存到CSV"""
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"找到 {len(model_dirs)} 个模型文件夹(已排除 {len(EXCLUDED_SLUGS)} 个隐藏模型)")
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"处理文件夹 {model_dir.name} 时出错: {e}")
136
  continue
137
-
138
- # 按overall_score排序,同分时按comprehensiveness、insight、instruction_following、readability依次排序
139
- model_results.sort(key=lambda x: (x['overall_score'], x['comprehensiveness'], x['insight'], x['instruction_following'], x['readability']), reverse=True)
140
-
141
- # 写入CSV文件
 
 
 
 
 
 
 
 
 
 
 
 
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
- # 格式化数值,对于None值使用"-"
150
- row = {
151
- 'model': result['model'],
152
- 'overall_score': f"{result['overall_score']:.2f}",
153
- 'comprehensiveness': f"{result['comprehensiveness']:.2f}",
154
- 'insight': f"{result['insight']:.2f}",
155
- 'instruction_following': f"{result['instruction_following']:.2f}",
156
- 'readability': f"{result['readability']:.2f}",
157
- 'citation_accuracy': f"{result['citation_accuracy']:.2f}" if result['citation_accuracy'] is not None else "-",
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"\n排行榜已保存到: {output_file}")
163
- print(f"共处理了 {len(model_results)} 个模型")
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!")