ai-forever commited on
Commit
3c19b11
·
1 Parent(s): 1f2b823

Import Web Agent Bench leaderboard from agent-bench

Browse files
Files changed (44) hide show
  1. .gitattributes +1 -0
  2. .gitignore +4 -0
  3. Makefile +13 -0
  4. PUBLISH.md +32 -0
  5. README.md +22 -5
  6. app.py +429 -0
  7. assets/pinchbench.css +864 -0
  8. bench_config.json +232 -0
  9. docs/description.md +75 -0
  10. pyproject.toml +12 -0
  11. requirements.txt +4 -0
  12. results/.gitkeep +0 -0
  13. results/deepseek_deepseek-v4-flash__browser-use.json +205 -0
  14. results/deepseek_deepseek-v4-flash__openhands.json +205 -0
  15. results/deepseek_deepseek-v4-flash__openmanus.json +205 -0
  16. results/deepseek_deepseek-v4-flash__ouroboros-cut.json +205 -0
  17. results/deepseek_deepseek-v4-flash__ouroboros-full-evolving.json +205 -0
  18. results/deepseek_deepseek-v4-flash__ouroboros-full-isolated.json +205 -0
  19. results/google_gemini-2.5-flash__browser-use.json +205 -0
  20. results/google_gemini-2.5-flash__openhands.json +205 -0
  21. results/google_gemini-2.5-flash__openmanus.json +205 -0
  22. results/google_gemini-2.5-flash__ouroboros-cut.json +205 -0
  23. results/google_gemini-2.5-flash__ouroboros-full-evolving.json +205 -0
  24. results/google_gemini-2.5-flash__ouroboros-full-isolated.json +205 -0
  25. results/raw/deepseek_deepseek-v4-flash__browser-use/results.json +3 -0
  26. results/raw/deepseek_deepseek-v4-flash__openhands/results.json +3 -0
  27. results/raw/deepseek_deepseek-v4-flash__openmanus/results.json +3 -0
  28. results/raw/deepseek_deepseek-v4-flash__ouroboros-cut/results.json +3 -0
  29. results/raw/deepseek_deepseek-v4-flash__ouroboros-full-evolving/results.json +3 -0
  30. results/raw/deepseek_deepseek-v4-flash__ouroboros-full-isolated/results.json +3 -0
  31. results/raw/google_gemini-2.5-flash__browser-use/results.json +3 -0
  32. results/raw/google_gemini-2.5-flash__openhands/results.json +3 -0
  33. results/raw/google_gemini-2.5-flash__openmanus/results.json +3 -0
  34. results/raw/google_gemini-2.5-flash__ouroboros-cut/results.json +3 -0
  35. results/raw/google_gemini-2.5-flash__ouroboros-full-evolving/results.json +3 -0
  36. results/raw/google_gemini-2.5-flash__ouroboros-full-isolated/results.json +3 -0
  37. src/__init__.py +0 -0
  38. src/bench_config.py +53 -0
  39. src/display.py +47 -0
  40. src/export_results.py +366 -0
  41. src/load_entries.py +93 -0
  42. src/models.py +252 -0
  43. src/tables.py +100 -0
  44. src/ui.py +455 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ results/raw/**/results.json filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ hf_token
2
+ __pycache__/
3
+ *.pyc
4
+ .venv/
Makefile ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .PHONY: style format
2
+
3
+
4
+ style:
5
+ python -m black --line-length 119 .
6
+ python -m isort .
7
+ ruff check --fix .
8
+
9
+
10
+ quality:
11
+ python -m black --check --line-length 119 .
12
+ python -m isort --check-only .
13
+ ruff check .
PUBLISH.md ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Публикация на Hugging Face
2
+
3
+ Скопируйте **всё содержимое** `liderboard/` в репозиторий Space [MERA-evaluation/WebAgentBench](https://huggingface.co/spaces/MERA-evaluation/WebAgentBench).
4
+
5
+ ## Перед публикацией
6
+
7
+ Из корня `agent-bench`:
8
+
9
+ ```bash
10
+ python liderboard/src/export_results.py
11
+ ```
12
+
13
+ Это обновит `liderboard/results/` из последних прогонов в `tests/eval/` (та же логика discovery, что у `scripts/render_eval_aggregate.py`). Старые файлы в `results/` удаляются перед экспортом.
14
+
15
+ ## Push
16
+
17
+ Синхронизировать в локальный клон Space только файлы из последнего коммита:
18
+
19
+ ```bash
20
+ ./scripts/sync_liderboard_to_webagentbench.sh /path/to/WebAgentBench
21
+ ```
22
+
23
+ Затем в каталоге WebAgentBench:
24
+
25
+ ```bash
26
+ cd /path/to/WebAgentBench
27
+ git add -A
28
+ git commit -m "Update Web Agent Bench leaderboard"
29
+ git push -u origin main
30
+ ```
31
+
32
+ Используйте [HF write token](https://huggingface.co/settings/tokens) вместо пароля.
README.md CHANGED
@@ -1,13 +1,30 @@
1
  ---
2
  title: WebAgentBench
3
- emoji: 🐠
4
- colorFrom: pink
5
  colorTo: green
6
  sdk: gradio
7
- sdk_version: 6.19.0
8
- python_version: '3.13'
9
  app_file: app.py
10
  pinned: false
 
 
 
 
11
  ---
12
 
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  title: WebAgentBench
3
+ emoji: 🌐
4
+ colorFrom: blue
5
  colorTo: green
6
  sdk: gradio
7
+ sdk_version: 6.9.0
 
8
  app_file: app.py
9
  pinned: false
10
+ license: mit
11
+ tags:
12
+ - leaderboard
13
+ short_description: Web Agent Bench leaderboard
14
  ---
15
 
16
+ # Web Agent Bench Leaderboard
17
+
18
+ Источник для Hugging Face Space. Полная документация: [agent-bench/liderboard](https://github.com/ai-forever/agent-bench/tree/liderboard/liderboard).
19
+
20
+ ## Генерация
21
+
22
+ ```bash
23
+ # из корня agent-bench — после прогона eval
24
+ ./scripts/export_leaderboard.sh
25
+ cd liderboard && python app.py
26
+ ```
27
+
28
+ ## Метрики
29
+
30
+ Основная — **DAB Success Rate** (`run.success_rate` из `results.json`). См. [EVAL_README.md](https://github.com/ai-forever/agent-bench/blob/open_bench/docs/EVAL_README.md).
app.py ADDED
@@ -0,0 +1,429 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Web Agent Bench leaderboard (Gradio Space source)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import html
6
+ import json
7
+ from pathlib import Path
8
+
9
+ import gradio as gr
10
+ import pandas as pd
11
+
12
+ from src.bench_config import load_bench_config, section_order, taxonomy_order
13
+ from src.load_entries import entry_by_id, entry_choices, load_entries, load_raw_results
14
+ from src.display import dataframe_to_html
15
+ from src.tables import (
16
+ category_description_html,
17
+ section_leaderboard_dataframe,
18
+ taxonomy_leaderboard_dataframe,
19
+ )
20
+ from src.ui import build_board, build_nav_js, build_shell_top, load_css
21
+
22
+
23
+ def _category_table(entries, kind: str, item_id: str):
24
+ if kind == "section":
25
+ desc, df = category_description_html("section", item_id), section_leaderboard_dataframe(entries, item_id)
26
+ else:
27
+ desc, df = category_description_html("taxonomy", item_id), taxonomy_leaderboard_dataframe(entries, item_id)
28
+ return desc, dataframe_to_html(df)
29
+
30
+
31
+ def _raw_tasks_dataframe(raw: dict | None) -> pd.DataFrame:
32
+ if not raw:
33
+ return pd.DataFrame()
34
+ rows = []
35
+ config = load_bench_config()
36
+ section_labels = {section_id: meta["label"] for section_id, meta in config["sections"].items()}
37
+ for test in raw.get("tests") or []:
38
+ ui = test.get("ui_taxonomy") or {}
39
+ domain = ui.get("domain") or "—"
40
+ rows.append(
41
+ {
42
+ "Section": section_labels.get(domain, domain),
43
+ "Task": test.get("test_name") or "—",
44
+ "Success": "✓" if test.get("success") else "✗",
45
+ "Duration": test.get("duration_seconds"),
46
+ "Steps": test.get("agent_steps"),
47
+ }
48
+ )
49
+ return pd.DataFrame(rows)
50
+
51
+
52
+ def _run_metrics_dataframe(entry, raw: dict | None) -> pd.DataFrame:
53
+ run = (raw or {}).get("run") or {}
54
+ rows = [
55
+ {"Metric": "Success %", "Value": entry.success_pct},
56
+ {"Metric": "Tasks passed", "Value": f"{entry.passed_tasks}/{entry.total_tasks}"},
57
+ {"Metric": "Section avg %", "Value": round(entry.section_avg_rate * 100, 1) if entry.section_avg_rate is not None else None},
58
+ {"Metric": "Harness", "Value": entry.harness},
59
+ {"Metric": "Provider", "Value": entry.provider or run.get("provider")},
60
+ {"Metric": "Mock", "Value": entry.mock or run.get("mock")},
61
+ {"Metric": "Finished", "Value": entry.finished},
62
+ {"Metric": "Submitted at", "Value": entry.submitted_at or run.get("finished_at") or run.get("started_at")},
63
+ {"Metric": "Avg duration (s)", "Value": entry.avg_duration_seconds or run.get("avg_duration_seconds")},
64
+ {"Metric": "Avg steps", "Value": entry.avg_agent_steps or run.get("avg_agent_steps")},
65
+ {"Metric": "Tokens / task", "Value": entry.avg_tokens_per_task or run.get("avg_tokens_per_task")},
66
+ {"Metric": "Total tokens", "Value": entry.total_tokens or run.get("total_tokens")},
67
+ {"Metric": "Total $", "Value": entry.total_cost_usd},
68
+ {"Metric": "Pass@k", "Value": entry.pass_at_k},
69
+ {"Metric": "Agent completion", "Value": entry.agent_completion_rate},
70
+ {"Metric": "Agent/DAB agreement", "Value": entry.agent_dab_agreement_rate},
71
+ {"Metric": "Source", "Value": entry.source_path},
72
+ {"Metric": "Raw results", "Value": entry.raw_results_path},
73
+ ]
74
+ return pd.DataFrame(rows)
75
+
76
+
77
+ def _ui_taxonomy_dataframe(raw: dict | None) -> pd.DataFrame:
78
+ if not raw:
79
+ return pd.DataFrame()
80
+ stats = raw.get("ui_taxonomy_stats") or {}
81
+ by_primary = stats.get("by_primary") or {}
82
+ rows = []
83
+ for class_id, row in sorted(by_primary.items()):
84
+ if not isinstance(row, dict):
85
+ continue
86
+ rows.append(
87
+ {
88
+ "Class": class_id,
89
+ "Success %": round(float(row["success_rate"]) * 100, 1) if isinstance(row.get("success_rate"), (int, float)) else None,
90
+ "Passed": row.get("passed"),
91
+ "Failed": row.get("failed"),
92
+ "Total": row.get("total"),
93
+ }
94
+ )
95
+ return pd.DataFrame(rows)
96
+
97
+
98
+ def _submission_summary(entry) -> str:
99
+ section_note = ""
100
+ if entry.section_avg_rate is not None and entry.success_rate is not None:
101
+ section_pct = round(entry.section_avg_rate * 100, 1)
102
+ success_pct = entry.success_pct
103
+ if abs(section_pct - (success_pct or 0)) >= 0.5:
104
+ section_note = (
105
+ f'<p class="wab-submission-note">Section-average score: <strong>{section_pct}%</strong>. '
106
+ f"Primary leaderboard metric is DAB task pass rate: "
107
+ f"<strong>{success_pct}%</strong> ({entry.passed_tasks}/{entry.total_tasks}).</p>"
108
+ )
109
+
110
+ badges = ", ".join(html.escape(badge) for badge in entry.ui_badges) if entry.ui_badges else "—"
111
+ return (
112
+ '<div class="wab-submission-summary">'
113
+ f'<h3 class="wab-submission-title">{html.escape(entry.model)}</h3>'
114
+ f'<p><strong>Harness:</strong> <span class="wab-inline-mono">{html.escape(entry.harness)}</span></p>'
115
+ f"<p><strong>Success:</strong> {entry.success_pct}% "
116
+ f"({entry.passed_tasks}/{entry.total_tasks} tasks)</p>"
117
+ f"<p><strong>UI badges:</strong> {badges}</p>"
118
+ f"{section_note}"
119
+ "</div>"
120
+ )
121
+
122
+
123
+ def _submission_tables(entry_id: str, entries):
124
+ entry = entry_by_id(entries, entry_id)
125
+ if entry is None:
126
+ empty = dataframe_to_html(pd.DataFrame())
127
+ return "No submission selected.", empty, empty, empty
128
+
129
+ summary = _submission_summary(entry)
130
+ raw = load_raw_results(entry)
131
+ if raw is None:
132
+ return (
133
+ summary
134
+ + '<p class="wab-submission-note">Raw <span class="wab-inline-mono">results.json</span> not found. '
135
+ "Run <span class=\"wab-inline-mono\">./scripts/export_leaderboard.sh</span> after eval.</p>",
136
+ dataframe_to_html(_run_metrics_dataframe(entry, None)),
137
+ dataframe_to_html(pd.DataFrame(), empty_note="Raw results not available."),
138
+ dataframe_to_html(pd.DataFrame(), empty_note="Raw results not available."),
139
+ )
140
+
141
+ return (
142
+ summary,
143
+ dataframe_to_html(_run_metrics_dataframe(entry, raw)),
144
+ dataframe_to_html(_raw_tasks_dataframe(raw)),
145
+ dataframe_to_html(_ui_taxonomy_dataframe(raw)),
146
+ )
147
+
148
+
149
+ def _raw_json_placeholder(entry_id: str, entries) -> str:
150
+ entry = entry_by_id(entries, entry_id)
151
+ if entry is None:
152
+ return "{}"
153
+ path = entry.raw_results_path or f"results/raw/{entry_id}/results.json"
154
+ return (
155
+ f"// Raw results for {entry.model} × {entry.harness}\n"
156
+ f"// File: {path}\n"
157
+ '// Click "Load full JSON" to fetch the exported results.json.'
158
+ )
159
+
160
+
161
+ def _raw_json_full(entry_id: str, entries) -> str:
162
+ entry = entry_by_id(entries, entry_id)
163
+ if entry is None:
164
+ return "{}"
165
+ raw = load_raw_results(entry)
166
+ if raw is None:
167
+ return "{}"
168
+ return json.dumps(raw, ensure_ascii=False, indent=2)
169
+
170
+
171
+ _DETAIL_TAB_IDS = ("submission", "sections", "taxonomy", "metrics", "about")
172
+
173
+
174
+ def _parse_nav_payload(payload: str) -> tuple[str, str]:
175
+ tab, category = (payload.split(":", 1) + [""])[:2]
176
+ return tab, category
177
+
178
+
179
+ def build_demo(entries=None):
180
+ config = load_bench_config()
181
+ entries = entries if entries is not None else load_entries()
182
+ view_choices = [(f'{view["emoji"]} {view["label"]}', view["id"]) for view in config["views"]]
183
+ submission_choices = entry_choices(entries)
184
+ default_submission = submission_choices[0][1] if submission_choices else None
185
+ section_choices = [(config["sections"][section_id]["label"], section_id) for section_id in section_order()]
186
+ taxonomy_choices = [(config["ui_classes"][class_id]["label"], class_id) for class_id in taxonomy_order()]
187
+ default_section = section_choices[0][1] if section_choices else None
188
+ default_taxonomy = taxonomy_choices[0][1] if taxonomy_choices else None
189
+
190
+ theme = gr.themes.Base(
191
+ primary_hue=gr.themes.colors.orange,
192
+ neutral_hue=gr.themes.colors.gray,
193
+ ).set(
194
+ body_background_fill="*neutral_950",
195
+ body_background_fill_dark="*neutral_950",
196
+ block_background_fill="*neutral_900",
197
+ block_background_fill_dark="*neutral_900",
198
+ block_border_color="*neutral_800",
199
+ block_border_color_dark="*neutral_800",
200
+ body_text_color="*neutral_50",
201
+ body_text_color_dark="*neutral_50",
202
+ )
203
+
204
+ with gr.Blocks(title="Web Agent Bench Leaderboard", elem_classes=["wab-root"]) as demo:
205
+ gr.HTML(build_shell_top(entries))
206
+ view = gr.Radio(
207
+ choices=view_choices,
208
+ value="success",
209
+ show_label=False,
210
+ elem_classes=["wab-view-nav-radio"],
211
+ container=False,
212
+ )
213
+ board = gr.HTML(build_board(entries, "success"))
214
+
215
+ def on_view_change(view_id: str):
216
+ return build_board(entries, view_id)
217
+
218
+ view.change(on_view_change, inputs=view, outputs=board)
219
+
220
+ gr.HTML('<div id="wab-detail-anchor" class="wab-detail-anchor"></div>')
221
+
222
+ with gr.Tabs(elem_id="wab-detail-tabs", elem_classes=["wab-tabs"]):
223
+ with gr.Tab("Submission", elem_classes=["wab-tab-panel"]):
224
+ gr.HTML(
225
+ '<p class="wab-detail-note">Inspect the full exported '
226
+ '<span class="wab-inline-mono">results.json</span> for a model × harness run.</p>'
227
+ )
228
+ submission = gr.Dropdown(
229
+ choices=submission_choices,
230
+ value=default_submission,
231
+ label="Submission",
232
+ interactive=True,
233
+ filterable=True,
234
+ elem_id="wab-submission-dropdown",
235
+ elem_classes=["wab-submission-dropdown"],
236
+ )
237
+ initial_detail = _submission_tables(default_submission, entries) if default_submission else (
238
+ "No submissions yet.",
239
+ dataframe_to_html(pd.DataFrame()),
240
+ dataframe_to_html(pd.DataFrame()),
241
+ dataframe_to_html(pd.DataFrame()),
242
+ )
243
+ detail_summary = gr.HTML(value=initial_detail[0], elem_classes=["wab-submission-summary-wrap"])
244
+ with gr.Accordion("Run metrics", open=True, elem_classes=["wab-accordion"]):
245
+ detail_metrics = gr.HTML(value=initial_detail[1], elem_classes=["wab-detail-html"])
246
+ with gr.Accordion("Tasks", open=False, elem_classes=["wab-accordion"]):
247
+ detail_tasks = gr.HTML(value=initial_detail[2], elem_classes=["wab-detail-html"])
248
+ with gr.Accordion("UI taxonomy", open=False, elem_classes=["wab-accordion"]):
249
+ detail_ui = gr.HTML(value=initial_detail[3], elem_classes=["wab-detail-html"])
250
+ with gr.Accordion("Raw JSON", open=False, elem_classes=["wab-accordion"]):
251
+ gr.Markdown(
252
+ '<p class="wab-detail-note">Large files load on demand so tables above stay responsive.</p>'
253
+ )
254
+ load_json_btn = gr.Button("Load full JSON", elem_classes=["wab-load-json-btn"])
255
+ detail_json = gr.Code(
256
+ value=_raw_json_placeholder(default_submission, entries) if default_submission else "{}",
257
+ language="json",
258
+ lines=20,
259
+ elem_classes=["wab-json"],
260
+ )
261
+
262
+ def on_submission_change(entry_id: str):
263
+ return _submission_tables(entry_id, entries) + (_raw_json_placeholder(entry_id, entries),)
264
+
265
+ submission.change(
266
+ on_submission_change,
267
+ inputs=[submission],
268
+ outputs=[detail_summary, detail_metrics, detail_tasks, detail_ui, detail_json],
269
+ )
270
+
271
+ def on_load_full_json(entry_id: str):
272
+ return _raw_json_full(entry_id, entries)
273
+
274
+ load_json_btn.click(
275
+ on_load_full_json,
276
+ inputs=[submission],
277
+ outputs=[detail_json],
278
+ )
279
+
280
+ with gr.Tab("Sections", elem_classes=["wab-tab-panel"]):
281
+ gr.HTML(
282
+ '<p class="wab-detail-note">Leaderboard по доменам бенчмарка. '
283
+ "Выберите категорию — описание появится под кнопками.</p>"
284
+ )
285
+ section_pick = gr.Radio(
286
+ choices=section_choices,
287
+ value=default_section,
288
+ show_label=False,
289
+ elem_id="wab-section-pick",
290
+ elem_classes=["wab-category-radio"],
291
+ container=False,
292
+ )
293
+ section_initial = _category_table(entries, "section", default_section) if default_section else ("", "")
294
+ section_desc = gr.HTML(value=section_initial[0], elem_classes=["wab-category-desc-wrap"])
295
+ section_table = gr.HTML(value=section_initial[1], elem_classes=["wab-detail-html"])
296
+
297
+ def on_section_change(section_id: str):
298
+ return _category_table(entries, "section", section_id)
299
+
300
+ section_pick.change(
301
+ on_section_change,
302
+ inputs=[section_pick],
303
+ outputs=[section_desc, section_table],
304
+ )
305
+
306
+ with gr.Tab("UI taxonomy", elem_classes=["wab-tab-panel"]):
307
+ gr.HTML(
308
+ '<p class="wab-detail-note">Leaderboard по UI-классам (primary). '
309
+ "Выберите класс — описание появится под кнопками.</p>"
310
+ )
311
+ taxonomy_pick = gr.Radio(
312
+ choices=taxonomy_choices,
313
+ value=default_taxonomy,
314
+ show_label=False,
315
+ elem_id="wab-taxonomy-pick",
316
+ elem_classes=["wab-category-radio"],
317
+ container=False,
318
+ )
319
+ taxonomy_initial = _category_table(entries, "taxonomy", default_taxonomy) if default_taxonomy else ("", "")
320
+ taxonomy_desc = gr.HTML(value=taxonomy_initial[0], elem_classes=["wab-category-desc-wrap"])
321
+ taxonomy_table = gr.HTML(value=taxonomy_initial[1], elem_classes=["wab-detail-html"])
322
+
323
+ def on_taxonomy_change(class_id: str):
324
+ return _category_table(entries, "taxonomy", class_id)
325
+
326
+ taxonomy_pick.change(
327
+ on_taxonomy_change,
328
+ inputs=[taxonomy_pick],
329
+ outputs=[taxonomy_desc, taxonomy_table],
330
+ )
331
+
332
+ with gr.Tab("Metrics", elem_classes=["wab-tab-panel"]):
333
+ metric_rows = [
334
+ {
335
+ "Model": entry.model,
336
+ "Harness": entry.harness,
337
+ "Success %": entry.success_pct,
338
+ "Tasks": f"{entry.passed_tasks}/{entry.total_tasks}",
339
+ "Avg duration": entry.avg_duration_seconds,
340
+ "Avg steps": entry.avg_agent_steps,
341
+ "Tokens/task": entry.avg_tokens_per_task,
342
+ "Total $": entry.total_cost_usd,
343
+ "Pass@k": entry.pass_at_k,
344
+ }
345
+ for entry in entries
346
+ ]
347
+ gr.HTML(
348
+ value=dataframe_to_html(pd.DataFrame(metric_rows)),
349
+ elem_classes=["wab-detail-html"],
350
+ )
351
+
352
+ with gr.Tab("About", elem_classes=["wab-tab-panel"]):
353
+ gr.Markdown((Path(__file__).parent / "docs" / "description.md").read_text(encoding="utf-8"))
354
+
355
+ nav_payload = gr.Textbox(
356
+ value="",
357
+ show_label=False,
358
+ visible="hidden",
359
+ elem_id="wab-nav-payload",
360
+ elem_classes=["wab-nav-trigger-hidden"],
361
+ )
362
+ nav_btn = gr.Button(
363
+ "Navigate",
364
+ visible="hidden",
365
+ elem_id="wab-nav-go",
366
+ elem_classes=["wab-nav-trigger-hidden"],
367
+ )
368
+
369
+ def on_badge_nav(payload: str = ""):
370
+ no_update = gr.update()
371
+ if not payload:
372
+ return (no_update,) * 6
373
+
374
+ tab, category = _parse_nav_payload(payload)
375
+ section_pick_up = section_desc_up = section_table_up = no_update
376
+ tax_pick_up = tax_desc_up = tax_table_up = no_update
377
+
378
+ if tab == "sections" and category:
379
+ section_pick_up = gr.update(value=category)
380
+ desc, table = _category_table(entries, "section", category)
381
+ section_desc_up = gr.update(value=desc)
382
+ section_table_up = gr.update(value=table)
383
+ elif tab == "taxonomy" and category:
384
+ tax_pick_up = gr.update(value=category)
385
+ desc, table = _category_table(entries, "taxonomy", category)
386
+ tax_desc_up = gr.update(value=desc)
387
+ tax_table_up = gr.update(value=table)
388
+
389
+ return (
390
+ section_pick_up,
391
+ section_desc_up,
392
+ section_table_up,
393
+ tax_pick_up,
394
+ tax_desc_up,
395
+ tax_table_up,
396
+ )
397
+
398
+ nav_btn.click(
399
+ on_badge_nav,
400
+ inputs=[nav_payload],
401
+ outputs=[
402
+ section_pick,
403
+ section_desc,
404
+ section_table,
405
+ taxonomy_pick,
406
+ taxonomy_desc,
407
+ taxonomy_table,
408
+ ],
409
+ js=(
410
+ "(payload) => {"
411
+ "const p = window.__wabPendingNav || payload || '';"
412
+ "const tab = (p.split(':')[0] || 'submission');"
413
+ "if (window.wabOpenDetailTab) window.wabOpenDetailTab(tab);"
414
+ "document.getElementById('wab-detail-anchor')"
415
+ "?.scrollIntoView({behavior:'smooth', block:'start'});"
416
+ "return p;"
417
+ "}"
418
+ ),
419
+ )
420
+
421
+ demo.load(lambda: None, None, None, js=build_nav_js())
422
+
423
+ return demo, theme, load_css()
424
+
425
+
426
+ demo, theme, css = build_demo()
427
+
428
+ if __name__ == "__main__":
429
+ demo.launch(theme=theme, css=css, share=False)
assets/pinchbench.css ADDED
@@ -0,0 +1,864 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ --bg: #121212;
3
+ --bg-card: #171717;
4
+ --bg-elevated: #1f1f1f;
5
+ --border: #2a2a2a;
6
+ --text: #fafafa;
7
+ --text-muted: #9a9a9a;
8
+ --primary: #f97316;
9
+ --primary-soft: rgba(249, 115, 22, 0.14);
10
+ --success: #22c55e;
11
+ --warning: #f59e0b;
12
+ --danger: #ef4444;
13
+ --radius: 10px;
14
+ --mono: "SF Mono", "JetBrains Mono", "Fira Code", ui-monospace, monospace;
15
+ --sans: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
16
+ }
17
+
18
+ .gradio-container {
19
+ background: var(--bg) !important;
20
+ color: var(--text) !important;
21
+ font-family: var(--sans) !important;
22
+ max-width: 1200px !important;
23
+ width: 100% !important;
24
+ margin: 0 auto !important;
25
+ }
26
+
27
+ .gradio-container .wab-root,
28
+ .gradio-container .wab-tabs,
29
+ .gradio-container .wab-tab-panel,
30
+ .gradio-container .wab-detail-html,
31
+ .gradio-container .wab-submission-summary-wrap,
32
+ .gradio-container .html-container {
33
+ width: 100% !important;
34
+ max-width: 100% !important;
35
+ box-sizing: border-box !important;
36
+ }
37
+
38
+ .gradio-container .markdown code,
39
+ .gradio-container .prose code,
40
+ .gradio-container code {
41
+ background: var(--bg-elevated) !important;
42
+ color: #fdba74 !important;
43
+ border: 1px solid var(--border) !important;
44
+ border-radius: 6px !important;
45
+ padding: 0.1rem 0.35rem !important;
46
+ }
47
+
48
+ .wab-inline-mono {
49
+ font-family: var(--mono);
50
+ font-size: 0.86rem;
51
+ color: #fdba74;
52
+ background: var(--bg-elevated);
53
+ border: 1px solid var(--border);
54
+ border-radius: 6px;
55
+ padding: 0.1rem 0.35rem;
56
+ }
57
+
58
+ .wab-submission-summary {
59
+ margin: 0.5rem 0 1rem;
60
+ padding: 1rem 1.1rem;
61
+ background: var(--bg-card);
62
+ border: 1px solid var(--border);
63
+ border-radius: var(--radius);
64
+ }
65
+
66
+ .wab-submission-title {
67
+ margin: 0 0 0.65rem;
68
+ font-size: 1.15rem;
69
+ font-weight: 700;
70
+ color: var(--text);
71
+ }
72
+
73
+ .wab-submission-summary p {
74
+ margin: 0.35rem 0;
75
+ color: var(--text);
76
+ line-height: 1.45;
77
+ }
78
+
79
+ .wab-submission-note {
80
+ margin: 0.75rem 0 0 !important;
81
+ padding: 0.75rem 0.85rem;
82
+ background: var(--bg-elevated);
83
+ border-left: 3px solid var(--primary);
84
+ border-radius: 0 8px 8px 0;
85
+ color: var(--text-muted) !important;
86
+ font-size: 0.88rem;
87
+ }
88
+
89
+ .wab-category-desc,
90
+ .wab-category-desc-wrap p {
91
+ margin: 0 0 0.85rem !important;
92
+ color: var(--text-muted);
93
+ font-size: 0.9rem;
94
+ line-height: 1.45;
95
+ }
96
+
97
+ .wab-category-desc strong,
98
+ .wab-category-desc-wrap strong {
99
+ color: var(--text);
100
+ }
101
+
102
+ .wab-shell {
103
+ color: var(--text);
104
+ padding: 0.25rem 0 2rem;
105
+ }
106
+
107
+ .wab-topbar {
108
+ display: flex;
109
+ align-items: center;
110
+ justify-content: space-between;
111
+ gap: 1rem;
112
+ margin-bottom: 1.5rem;
113
+ flex-wrap: wrap;
114
+ }
115
+
116
+ .wab-brand {
117
+ display: flex;
118
+ align-items: center;
119
+ gap: 0.75rem;
120
+ }
121
+
122
+ .wab-logo {
123
+ width: 42px;
124
+ height: 42px;
125
+ border-radius: 12px;
126
+ background: linear-gradient(135deg, #fb923c, #ea580c);
127
+ display: grid;
128
+ place-items: center;
129
+ font-size: 1.35rem;
130
+ box-shadow: 0 8px 24px rgba(249, 115, 22, 0.25);
131
+ }
132
+
133
+ .wab-brand h1 {
134
+ margin: 0;
135
+ font-size: 1.35rem;
136
+ font-weight: 700;
137
+ letter-spacing: -0.02em;
138
+ }
139
+
140
+ .wab-brand p {
141
+ margin: 0.15rem 0 0;
142
+ color: var(--text-muted);
143
+ font-size: 0.85rem;
144
+ }
145
+
146
+ .wab-stats {
147
+ display: flex;
148
+ gap: 0.75rem;
149
+ flex-wrap: wrap;
150
+ }
151
+
152
+ .wab-stat {
153
+ background: var(--bg-card);
154
+ border: 1px solid var(--border);
155
+ border-radius: 999px;
156
+ padding: 0.45rem 0.85rem;
157
+ font-size: 0.82rem;
158
+ color: var(--text-muted);
159
+ }
160
+
161
+ .wab-stat strong {
162
+ color: var(--text);
163
+ }
164
+
165
+ .wab-hero {
166
+ margin-bottom: 1.75rem;
167
+ }
168
+
169
+ .wab-hero h2 {
170
+ margin: 0 0 0.35rem;
171
+ font-size: clamp(1.6rem, 3vw, 2.2rem);
172
+ font-weight: 700;
173
+ letter-spacing: -0.03em;
174
+ }
175
+
176
+ .wab-hero p {
177
+ margin: 0;
178
+ color: var(--text-muted);
179
+ font-size: 1rem;
180
+ }
181
+
182
+ .wab-section-label {
183
+ color: var(--text-muted);
184
+ font-size: 0.78rem;
185
+ text-transform: uppercase;
186
+ letter-spacing: 0.08em;
187
+ margin: 0 0 0.75rem;
188
+ }
189
+
190
+ .wab-quick-grid {
191
+ display: grid;
192
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
193
+ gap: 0.9rem;
194
+ margin-bottom: 2rem;
195
+ }
196
+
197
+ .wab-quick-card {
198
+ background: var(--bg-card);
199
+ border: 1px solid var(--border);
200
+ border-radius: var(--radius);
201
+ padding: 1rem 1rem 0.95rem;
202
+ transition: border-color 0.15s ease, transform 0.15s ease;
203
+ }
204
+
205
+ .wab-quick-card:hover {
206
+ border-color: rgba(249, 115, 22, 0.45);
207
+ transform: translateY(-1px);
208
+ }
209
+
210
+ .wab-quick-kicker {
211
+ color: var(--primary);
212
+ font-size: 0.76rem;
213
+ font-weight: 700;
214
+ text-transform: uppercase;
215
+ letter-spacing: 0.06em;
216
+ margin-bottom: 0.35rem;
217
+ }
218
+
219
+ .wab-quick-score {
220
+ font-size: 1.65rem;
221
+ font-weight: 700;
222
+ line-height: 1.1;
223
+ margin-bottom: 0.35rem;
224
+ }
225
+
226
+ .wab-quick-model {
227
+ font-family: var(--mono);
228
+ font-size: 0.82rem;
229
+ color: var(--text);
230
+ word-break: break-word;
231
+ }
232
+
233
+ .wab-quick-meta {
234
+ margin-top: 0.55rem;
235
+ color: var(--text-muted);
236
+ font-size: 0.78rem;
237
+ line-height: 1.35;
238
+ }
239
+
240
+ .wab-panel {
241
+ background: var(--bg-card);
242
+ border: 1px solid var(--border);
243
+ border-radius: calc(var(--radius) + 2px);
244
+ overflow: hidden;
245
+ margin-bottom: 1.5rem;
246
+ }
247
+
248
+ .wab-panel-head {
249
+ padding: 1.15rem 1.2rem 0.9rem;
250
+ border-bottom: 1px solid var(--border);
251
+ }
252
+
253
+ .wab-panel-head h3 {
254
+ margin: 0 0 0.35rem;
255
+ font-size: 1.2rem;
256
+ display: flex;
257
+ align-items: center;
258
+ gap: 0.45rem;
259
+ }
260
+
261
+ .wab-panel-head p {
262
+ margin: 0;
263
+ color: var(--text-muted);
264
+ font-size: 0.9rem;
265
+ }
266
+
267
+ .wab-table-wrap {
268
+ overflow-x: auto;
269
+ }
270
+
271
+ .wab-table {
272
+ width: 100%;
273
+ border-collapse: collapse;
274
+ }
275
+
276
+ .wab-board .wab-table {
277
+ min-width: 720px;
278
+ }
279
+
280
+ .wab-table th {
281
+ text-align: left;
282
+ font-size: 0.72rem;
283
+ text-transform: uppercase;
284
+ letter-spacing: 0.06em;
285
+ color: var(--text-muted);
286
+ padding: 0.85rem 1rem;
287
+ border-bottom: 1px solid var(--border);
288
+ background: var(--bg-elevated);
289
+ white-space: nowrap;
290
+ }
291
+
292
+ .wab-table td {
293
+ padding: 0.9rem 1rem;
294
+ border-bottom: 1px solid var(--border);
295
+ vertical-align: middle;
296
+ }
297
+
298
+ .wab-table tr:last-child td {
299
+ border-bottom: none;
300
+ }
301
+
302
+ .wab-table tbody tr:hover {
303
+ background: rgba(255, 255, 255, 0.02);
304
+ }
305
+
306
+ .wab-rank {
307
+ width: 2rem;
308
+ font-size: 1.1rem;
309
+ }
310
+
311
+ .wab-model-cell {
312
+ min-width: 220px;
313
+ }
314
+
315
+ .wab-model-name {
316
+ font-family: var(--mono);
317
+ font-size: 0.86rem;
318
+ font-weight: 600;
319
+ }
320
+
321
+ .wab-badges {
322
+ display: flex;
323
+ flex-wrap: wrap;
324
+ gap: 0.35rem;
325
+ margin-top: 0.4rem;
326
+ pointer-events: auto;
327
+ }
328
+
329
+ .wab-board .html-container,
330
+ .wab-board .prose {
331
+ pointer-events: auto !important;
332
+ }
333
+
334
+ .wab-badge {
335
+ display: inline-flex;
336
+ align-items: center;
337
+ gap: 0.25rem;
338
+ border-radius: 999px;
339
+ padding: 0.15rem 0.55rem;
340
+ font-size: 0.68rem;
341
+ font-weight: 600;
342
+ background: var(--bg-elevated);
343
+ border: 1px solid var(--border);
344
+ color: var(--text-muted);
345
+ line-height: 1.25;
346
+ }
347
+
348
+ .wab-badge-link {
349
+ cursor: pointer;
350
+ transition: border-color 0.15s ease, background 0.15s ease, opacity 0.15s ease;
351
+ }
352
+
353
+ .wab-badge-link:hover {
354
+ opacity: 0.92;
355
+ }
356
+
357
+ .wab-badge-stat {
358
+ opacity: 0.72;
359
+ font-weight: 500;
360
+ }
361
+
362
+ .wab-badge.domain {
363
+ color: #fdba74;
364
+ border-color: rgba(249, 115, 22, 0.35);
365
+ background: var(--primary-soft);
366
+ }
367
+
368
+ .wab-badge.domain:hover {
369
+ border-color: rgba(249, 115, 22, 0.55);
370
+ }
371
+
372
+ .wab-badge.taxonomy {
373
+ color: #93c5fd;
374
+ border-color: rgba(59, 130, 246, 0.35);
375
+ background: rgba(59, 130, 246, 0.12);
376
+ }
377
+
378
+ .wab-badge.taxonomy:hover {
379
+ border-color: rgba(59, 130, 246, 0.55);
380
+ }
381
+
382
+ .wab-hero-links {
383
+ margin: 0.85rem 0 0;
384
+ }
385
+
386
+ .wab-jump-link {
387
+ color: var(--primary);
388
+ text-decoration: none;
389
+ font-size: 0.92rem;
390
+ font-weight: 600;
391
+ }
392
+
393
+ .wab-jump-link:hover {
394
+ text-decoration: underline;
395
+ }
396
+
397
+ .wab-detail-anchor {
398
+ scroll-margin-top: 1.25rem;
399
+ height: 0;
400
+ }
401
+
402
+ .wab-nav-trigger-hidden {
403
+ position: absolute !important;
404
+ width: 1px !important;
405
+ height: 1px !important;
406
+ padding: 0 !important;
407
+ margin: 0 !important;
408
+ overflow: hidden !important;
409
+ clip: rect(0, 0, 0, 0) !important;
410
+ white-space: nowrap !important;
411
+ border: 0 !important;
412
+ }
413
+
414
+ .wab-harness {
415
+ font-family: var(--mono);
416
+ font-size: 0.78rem;
417
+ color: var(--text-muted);
418
+ }
419
+
420
+ .wab-category-legend {
421
+ display: flex;
422
+ flex-wrap: wrap;
423
+ gap: 0.45rem;
424
+ margin: 0 0 0.85rem;
425
+ }
426
+
427
+ .wab-legend-item {
428
+ display: inline-flex;
429
+ align-items: center;
430
+ border-radius: 999px;
431
+ padding: 0.2rem 0.6rem;
432
+ font-size: 0.72rem;
433
+ color: var(--text-muted);
434
+ border: 1px dashed var(--border);
435
+ cursor: help;
436
+ }
437
+
438
+ .wab-category-radio {
439
+ margin: 0 0 0.85rem !important;
440
+ padding: 0 !important;
441
+ background: transparent !important;
442
+ border: none !important;
443
+ box-shadow: none !important;
444
+ }
445
+
446
+ .wab-category-radio fieldset,
447
+ .wab-category-radio > .wrap {
448
+ display: flex !important;
449
+ gap: 0.5rem !important;
450
+ flex-wrap: wrap !important;
451
+ border: none !important;
452
+ padding: 0 !important;
453
+ margin: 0 !important;
454
+ background: transparent !important;
455
+ }
456
+
457
+ .wab-category-radio label {
458
+ display: inline-flex !important;
459
+ align-items: center !important;
460
+ border: 1px solid var(--border) !important;
461
+ border-radius: 999px !important;
462
+ padding: 0.4rem 0.85rem !important;
463
+ font-size: 0.82rem !important;
464
+ color: var(--text-muted) !important;
465
+ background: var(--bg-card) !important;
466
+ cursor: pointer !important;
467
+ margin: 0 !important;
468
+ box-shadow: none !important;
469
+ }
470
+
471
+ .wab-category-radio label.selected,
472
+ .wab-category-radio input:checked + label,
473
+ .wab-category-radio .selected {
474
+ color: var(--primary) !important;
475
+ border-color: rgba(249, 115, 22, 0.45) !important;
476
+ background: var(--primary-soft) !important;
477
+ }
478
+
479
+ .wab-category-radio input {
480
+ display: none !important;
481
+ }
482
+
483
+ .wab-provider {
484
+ font-size: 0.8rem;
485
+ color: var(--text-muted);
486
+ text-transform: lowercase;
487
+ }
488
+
489
+ .wab-score-cell {
490
+ min-width: 150px;
491
+ }
492
+
493
+ .wab-score-value {
494
+ font-weight: 700;
495
+ font-size: 0.95rem;
496
+ margin-bottom: 0.35rem;
497
+ }
498
+
499
+ .wab-bar {
500
+ height: 7px;
501
+ border-radius: 999px;
502
+ background: #2b2b2b;
503
+ overflow: hidden;
504
+ }
505
+
506
+ .wab-bar > span {
507
+ display: block;
508
+ height: 100%;
509
+ border-radius: 999px;
510
+ }
511
+
512
+ .wab-score-success { color: var(--success); }
513
+ .wab-score-warning { color: var(--warning); }
514
+ .wab-score-danger { color: var(--danger); }
515
+ .wab-bar-success { background: linear-gradient(90deg, #16a34a, #22c55e); }
516
+ .wab-bar-warning { background: linear-gradient(90deg, #d97706, #f59e0b); }
517
+ .wab-bar-danger { background: linear-gradient(90deg, #dc2626, #ef4444); }
518
+
519
+ .wab-footnote {
520
+ padding: 0.85rem 1.2rem 1rem;
521
+ color: var(--text-muted);
522
+ font-size: 0.82rem;
523
+ border-top: 1px solid var(--border);
524
+ }
525
+
526
+ .wab-links a {
527
+ color: var(--primary);
528
+ text-decoration: none;
529
+ }
530
+
531
+ .wab-links a:hover {
532
+ text-decoration: underline;
533
+ }
534
+
535
+ .wab-tabs .tab-nav button {
536
+ background: var(--bg-card) !important;
537
+ color: var(--text-muted) !important;
538
+ border: 1px solid var(--border) !important;
539
+ border-radius: 999px !important;
540
+ margin-right: 0.45rem !important;
541
+ pointer-events: auto !important;
542
+ cursor: pointer !important;
543
+ }
544
+
545
+ .wab-tabs .tab-nav {
546
+ pointer-events: auto !important;
547
+ position: relative;
548
+ z-index: 2;
549
+ }
550
+
551
+ .wab-tabs .tab-nav button.selected {
552
+ background: var(--primary-soft) !important;
553
+ color: var(--primary) !important;
554
+ border-color: rgba(249, 115, 22, 0.45) !important;
555
+ }
556
+
557
+ .wab-tabs .tabitem {
558
+ border: none !important;
559
+ background: transparent !important;
560
+ padding-top: 1rem !important;
561
+ overflow: visible !important;
562
+ }
563
+
564
+ .wab-tab-panel {
565
+ padding-top: 0.25rem !important;
566
+ }
567
+
568
+ .wab-accordion {
569
+ margin-bottom: 0.65rem !important;
570
+ border: 1px solid var(--border) !important;
571
+ border-radius: var(--radius) !important;
572
+ background: var(--bg-card) !important;
573
+ }
574
+
575
+ .wab-accordion > .label-wrap {
576
+ background: var(--bg-elevated) !important;
577
+ color: var(--text) !important;
578
+ }
579
+
580
+ .wab-accordion .accordion {
581
+ background: var(--bg-card) !important;
582
+ }
583
+
584
+ .wab-detail-table-wrap {
585
+ margin: 0;
586
+ width: 100%;
587
+ }
588
+
589
+ .wab-detail-table {
590
+ min-width: 0 !important;
591
+ width: 100% !important;
592
+ table-layout: auto;
593
+ }
594
+
595
+ .wab-detail-table td,
596
+ .wab-detail-table th {
597
+ font-size: 0.84rem;
598
+ color: var(--text) !important;
599
+ background: var(--bg-card) !important;
600
+ border-color: var(--border) !important;
601
+ }
602
+
603
+ .wab-detail-table th {
604
+ color: var(--text-muted) !important;
605
+ background: var(--bg-elevated) !important;
606
+ }
607
+
608
+ .wab-metric-value {
609
+ font-weight: 700;
610
+ color: #86efac !important;
611
+ }
612
+
613
+ .wab-mono-cell {
614
+ font-family: var(--mono);
615
+ font-size: 0.8rem;
616
+ color: var(--text-muted) !important;
617
+ }
618
+
619
+ .wab-detail-html {
620
+ width: 100%;
621
+ overflow-x: auto;
622
+ }
623
+
624
+ .wab-detail-html .html-container {
625
+ padding: 0 !important;
626
+ overflow: visible !important;
627
+ }
628
+
629
+ .wab-load-json-btn {
630
+ margin-bottom: 0.65rem !important;
631
+ }
632
+
633
+ .wab-load-json-btn button {
634
+ background: var(--bg-elevated) !important;
635
+ color: var(--text) !important;
636
+ border: 1px solid var(--border) !important;
637
+ border-radius: 999px !important;
638
+ font-size: 0.82rem !important;
639
+ }
640
+
641
+ .wab-load-json-btn button:hover {
642
+ border-color: rgba(249, 115, 22, 0.45) !important;
643
+ color: var(--primary) !important;
644
+ }
645
+
646
+ .wab-accordion .tabitem,
647
+ .wab-accordion > .wrap,
648
+ .wab-accordion .form,
649
+ .wab-accordion .html-container {
650
+ overflow: visible !important;
651
+ height: auto !important;
652
+ max-height: none !important;
653
+ }
654
+
655
+ .wab-submission-dropdown,
656
+ #wab-submission-dropdown {
657
+ width: 100% !important;
658
+ }
659
+
660
+ .wab-submission-dropdown label,
661
+ .wab-submission-dropdown .wrap,
662
+ .wab-submission-dropdown input,
663
+ .wab-submission-dropdown textarea,
664
+ .wab-submission-dropdown .single-select,
665
+ .wab-submission-dropdown .secondary-wrap,
666
+ .wab-submission-dropdown [data-testid="textbox"] input,
667
+ #wab-submission-dropdown input,
668
+ #wab-submission-dropdown textarea {
669
+ color: var(--text) !important;
670
+ -webkit-text-fill-color: var(--text) !important;
671
+ background: var(--bg-card) !important;
672
+ border-color: var(--border) !important;
673
+ }
674
+
675
+ .wab-submission-dropdown input::placeholder,
676
+ #wab-submission-dropdown input::placeholder {
677
+ color: var(--text-muted) !important;
678
+ -webkit-text-fill-color: var(--text-muted) !important;
679
+ }
680
+
681
+ .gradio-container ul.options,
682
+ .gradio-container .options,
683
+ .gradio-container [role="listbox"],
684
+ .gradio-container .gr-dropdown-list,
685
+ .gradio-container .dropdown-options {
686
+ background: var(--bg-card) !important;
687
+ border: 1px solid var(--border) !important;
688
+ color: var(--text) !important;
689
+ }
690
+
691
+ .gradio-container ul.options li,
692
+ .gradio-container .options li,
693
+ .gradio-container [role="listbox"] [role="option"],
694
+ .gradio-container .dropdown-option,
695
+ .gradio-container .item {
696
+ color: var(--text) !important;
697
+ background: var(--bg-card) !important;
698
+ }
699
+
700
+ .gradio-container ul.options li:hover,
701
+ .gradio-container .options li:hover,
702
+ .gradio-container [role="listbox"] [role="option"]:hover,
703
+ .gradio-container .dropdown-option:hover,
704
+ .gradio-container .item:hover {
705
+ color: var(--primary) !important;
706
+ background: var(--bg-elevated) !important;
707
+ }
708
+
709
+ .gradio-container ul.options li.selected,
710
+ .gradio-container [role="listbox"] [role="option"][aria-selected="true"] {
711
+ color: var(--primary) !important;
712
+ background: var(--primary-soft) !important;
713
+ }
714
+
715
+ .wab-dataframe,
716
+ .wab-dataframe .wrap,
717
+ .wab-dataframe .dataframe-wrap,
718
+ .wab-dataframe table,
719
+ .wab-dataframe thead,
720
+ .wab-dataframe tbody,
721
+ .wab-dataframe tr,
722
+ .wab-dataframe td,
723
+ .wab-dataframe th,
724
+ .wab-dataframe input,
725
+ .wab-dataframe textarea,
726
+ .wab-dataframe .cell-wrap,
727
+ .wab-dataframe .svelte-drgcpz,
728
+ .wab-dataframe [data-testid="dataframe"] table,
729
+ .wab-dataframe [data-testid="dataframe"] td,
730
+ .wab-dataframe [data-testid="dataframe"] th {
731
+ background: var(--bg-card) !important;
732
+ color: var(--text) !important;
733
+ border-color: var(--border) !important;
734
+ }
735
+
736
+ .wab-dataframe th {
737
+ color: var(--text-muted) !important;
738
+ background: var(--bg-elevated) !important;
739
+ }
740
+
741
+ .wab-dataframe .table-wrap {
742
+ border: 1px solid var(--border) !important;
743
+ border-radius: var(--radius) !important;
744
+ overflow: auto !important;
745
+ }
746
+
747
+ .wab-view-nav {
748
+ display: flex;
749
+ gap: 0.5rem;
750
+ flex-wrap: wrap;
751
+ margin-bottom: 1.25rem;
752
+ }
753
+
754
+ .wab-view-nav-radio {
755
+ margin: 0 0 1.25rem !important;
756
+ padding: 0 !important;
757
+ background: transparent !important;
758
+ border: none !important;
759
+ box-shadow: none !important;
760
+ }
761
+
762
+ .wab-view-nav-radio fieldset,
763
+ .wab-view-nav-radio > .wrap {
764
+ display: flex !important;
765
+ gap: 0.5rem !important;
766
+ flex-wrap: wrap !important;
767
+ border: none !important;
768
+ padding: 0 !important;
769
+ margin: 0 !important;
770
+ background: transparent !important;
771
+ }
772
+
773
+ .wab-view-nav-radio label {
774
+ display: inline-flex !important;
775
+ align-items: center !important;
776
+ gap: 0.35rem !important;
777
+ border: 1px solid var(--border) !important;
778
+ border-radius: 999px !important;
779
+ padding: 0.4rem 0.85rem !important;
780
+ font-size: 0.82rem !important;
781
+ color: var(--text-muted) !important;
782
+ background: var(--bg-card) !important;
783
+ cursor: pointer !important;
784
+ margin: 0 !important;
785
+ box-shadow: none !important;
786
+ }
787
+
788
+ .wab-view-nav-radio label:hover {
789
+ border-color: rgba(249, 115, 22, 0.35) !important;
790
+ color: var(--text) !important;
791
+ }
792
+
793
+ .wab-view-nav-radio label.selected,
794
+ .wab-view-nav-radio input:checked + label,
795
+ .wab-view-nav-radio .selected {
796
+ color: var(--primary) !important;
797
+ border-color: rgba(249, 115, 22, 0.45) !important;
798
+ background: var(--primary-soft) !important;
799
+ }
800
+
801
+ .wab-view-nav-radio input {
802
+ display: none !important;
803
+ }
804
+
805
+ .wab-view-pill {
806
+ display: inline-flex;
807
+ align-items: center;
808
+ gap: 0.35rem;
809
+ border: 1px solid var(--border);
810
+ border-radius: 999px;
811
+ padding: 0.4rem 0.85rem;
812
+ font-size: 0.82rem;
813
+ color: var(--text-muted);
814
+ background: var(--bg-card);
815
+ }
816
+
817
+ .wab-view-pill.active {
818
+ color: var(--primary);
819
+ border-color: rgba(249, 115, 22, 0.45);
820
+ background: var(--primary-soft);
821
+ }
822
+
823
+ .wab-board {
824
+ color: var(--text);
825
+ }
826
+
827
+ .wab-detail-note {
828
+ color: var(--text-muted);
829
+ font-size: 0.88rem;
830
+ margin: 0 0 0.75rem;
831
+ }
832
+
833
+ .wab-json textarea,
834
+ .wab-json pre {
835
+ font-family: var(--mono) !important;
836
+ font-size: 0.78rem !important;
837
+ background: var(--bg-card) !important;
838
+ color: var(--text) !important;
839
+ }
840
+
841
+ #arena_leaderboard_dataframe,
842
+ #arena_leaderboard_dataframe table,
843
+ [data-testid="dataframe"] table,
844
+ [data-testid="dataframe"] td,
845
+ [data-testid="dataframe"] th {
846
+ background: var(--bg-card) !important;
847
+ color: var(--text) !important;
848
+ }
849
+
850
+ #arena_leaderboard_dataframe th,
851
+ [data-testid="dataframe"] th {
852
+ background: var(--bg-elevated) !important;
853
+ color: var(--text-muted) !important;
854
+ border-color: var(--border) !important;
855
+ }
856
+
857
+ #arena_leaderboard_dataframe td,
858
+ [data-testid="dataframe"] td {
859
+ border-color: var(--border) !important;
860
+ }
861
+
862
+ footer {
863
+ display: none !important;
864
+ }
bench_config.json ADDED
@@ -0,0 +1,232 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "benchmark": {
3
+ "name": "Web Agent Bench",
4
+ "mock": "bench",
5
+ "task_count": 51,
6
+ "full_bench_min_tasks": 6,
7
+ "primary_metric": "success_rate",
8
+ "primary_metric_label": "DAB Success Rate",
9
+ "primary_metric_help": "Доля задач, где все conditions из dab_check прошли (all_passed). Не путать с Agent Completion.",
10
+ "docs_url": "https://github.com/ai-forever/agent-bench/blob/open_bench/docs/EVAL_README.md"
11
+ },
12
+ "views": [
13
+ {
14
+ "id": "success",
15
+ "label": "Success Rate",
16
+ "emoji": "🦀",
17
+ "sort_key": "success_rate",
18
+ "ascending": false
19
+ },
20
+ {
21
+ "id": "speed",
22
+ "label": "Speed",
23
+ "emoji": "⚡",
24
+ "sort_key": "avg_duration_seconds",
25
+ "ascending": true
26
+ },
27
+ {
28
+ "id": "cost",
29
+ "label": "Cost",
30
+ "emoji": "💰",
31
+ "sort_key": "total_cost_usd",
32
+ "ascending": true
33
+ }
34
+ ],
35
+ "quick_picks": {
36
+ "success": [
37
+ {
38
+ "id": "best_overall",
39
+ "label": "Best Overall",
40
+ "metric": "success_rate",
41
+ "subtitle": "Highest DAB success rate on full bench (>5 tasks)."
42
+ },
43
+ {
44
+ "id": "best_full_bench",
45
+ "label": "Best Full Bench",
46
+ "metric": "full_bench_success_rate",
47
+ "subtitle": "Best success on runs with all 51 tasks."
48
+ }
49
+ ],
50
+ "speed": [
51
+ {
52
+ "id": "fastest",
53
+ "label": "Fastest",
54
+ "metric": "avg_duration_seconds",
55
+ "subtitle": "Lowest average task duration (seconds)."
56
+ }
57
+ ],
58
+ "cost": [
59
+ {
60
+ "id": "best_budget",
61
+ "label": "Best Budget",
62
+ "metric": "total_cost_usd",
63
+ "subtitle": "Lowest total LLM cost for the run."
64
+ },
65
+ {
66
+ "id": "best_value",
67
+ "label": "Best Value",
68
+ "metric": "value_score",
69
+ "subtitle": "Highest success rate per dollar spent."
70
+ }
71
+ ]
72
+ },
73
+ "ui_classes": {
74
+ "NAV": {
75
+ "label": "NAV",
76
+ "description": "Навигация: переходы между разделами, меню, вкладки хаба, карточки каталога."
77
+ },
78
+ "BASKET": {
79
+ "label": "BASKET",
80
+ "description": "Корзина: добавление, удаление и изменение количества товаров."
81
+ },
82
+ "FAV": {
83
+ "label": "FAV",
84
+ "description": "Избранное: добавление и удаление товаров из списка желаний."
85
+ },
86
+ "GOV": {
87
+ "label": "GOV",
88
+ "description": "Портал услуг: заказ, просмотр и скачивание документов."
89
+ },
90
+ "SEARCH": {
91
+ "label": "SEARCH",
92
+ "description": "Поиск: ввод запроса, подсказки и отправка формы поиска."
93
+ },
94
+ "DATE": {
95
+ "label": "DATE",
96
+ "description": "Выбор даты и периода в формах поиска и бронирования."
97
+ },
98
+ "SELECT_AC": {
99
+ "label": "SELECT",
100
+ "description": "Выбор из списка: станции, города, подсказки autocomplete."
101
+ },
102
+ "CARD": {
103
+ "label": "CARD",
104
+ "description": "Карточки в каталоге: выбор позиции из сетки или списка."
105
+ },
106
+ "PAY": {
107
+ "label": "PAY",
108
+ "description": "Оплата: выбор способа оплаты и подтверждение платежа."
109
+ },
110
+ "AUTH": {
111
+ "label": "AUTH",
112
+ "description": "Авторизация: логин, телефон и подтверждение кода."
113
+ },
114
+ "FILTER": {
115
+ "label": "FILTER",
116
+ "description": "Фильтры и сортировка в каталогах и списках."
117
+ },
118
+ "BTN": {
119
+ "label": "BTN",
120
+ "description": "Кнопки и ссылки: CTA, «В корзину», «Найти»."
121
+ }
122
+ },
123
+ "sections": {
124
+ "shop": {
125
+ "label": "Маркет",
126
+ "description": "Каталог маркетплейса: корзина, избранное, поиск и фильтрация.",
127
+ "core": true,
128
+ "domain": "shop",
129
+ "tasks": [
130
+ "ecommerce_basket_and_favorites",
131
+ "ecommerce_basket_any_product",
132
+ "ecommerce_basket_multiple",
133
+ "ecommerce_basket_named_product",
134
+ "ecommerce_basket_named_product_02",
135
+ "ecommerce_basket_named_product_03",
136
+ "ecommerce_basket_price_10k",
137
+ "ecommerce_basket_price_1k",
138
+ "ecommerce_basket_price_constraint",
139
+ "ecommerce_basket_typo",
140
+ "ecommerce_favorites",
141
+ "ecommerce_favorites_02",
142
+ "ecommerce_favorites_any_product"
143
+ ]
144
+ },
145
+ "books": {
146
+ "label": "Книги",
147
+ "description": "Каталог цифровых книг: поиск, корзина и избранное.",
148
+ "core": false,
149
+ "domain": "books",
150
+ "tasks": [
151
+ "digital_books_audio_basket",
152
+ "digital_books_author_cheapest_basket",
153
+ "digital_books_author_cheapest_favorites",
154
+ "digital_books_author_expensive_basket",
155
+ "digital_books_named_product_basket",
156
+ "digital_books_named_product_basket_02",
157
+ "digital_books_named_product_favorites"
158
+ ]
159
+ },
160
+ "grocery": {
161
+ "label": "Продукты",
162
+ "description": "Каталог продуктов: навигация по разделам и корзина.",
163
+ "core": false,
164
+ "domain": "grocery",
165
+ "tasks": [
166
+ "bench_grocery_navigation",
167
+ "grocery_basket_any_product",
168
+ "grocery_basket_named_product",
169
+ "grocery_basket_named_product_02"
170
+ ]
171
+ },
172
+ "rail": {
173
+ "label": "Поезда",
174
+ "description": "Бронирование поездок: маршрут, даты, места, пассажиры и оплата.",
175
+ "core": true,
176
+ "domain": "rail",
177
+ "tasks": [
178
+ "rail_atomic_select_city_from",
179
+ "rail_atomic_select_city_to",
180
+ "rail_atomic_select_date",
181
+ "rail_atomic_submit_search",
182
+ "rail_book_and_pay",
183
+ "rail_book_and_pay_2_passengers",
184
+ "rail_book_to_cart",
185
+ "rail_book_to_cart_2_passengers",
186
+ "rail_book_to_cart_business"
187
+ ]
188
+ },
189
+ "hotels": {
190
+ "label": "Отели",
191
+ "description": "Бронирование размещения: город, даты, гости и выбор объекта.",
192
+ "core": false,
193
+ "domain": "hotels",
194
+ "tasks": [
195
+ "hotel_atomic_select_city",
196
+ "hotel_atomic_select_end_date",
197
+ "hotel_atomic_select_guests",
198
+ "hotel_atomic_select_start_date",
199
+ "hotel_search_scenario",
200
+ "hotel_search_scenario_miami",
201
+ "hotel_search_scenario_typo",
202
+ "hotel_search_scenario_with_hotel"
203
+ ]
204
+ },
205
+ "gov": {
206
+ "label": "Услуги",
207
+ "description": "Портал услуг: заказ, просмотр и скачивание документов.",
208
+ "core": false,
209
+ "domain": "gov",
210
+ "tasks": [
211
+ "government_download_certificate",
212
+ "government_download_certificate_techno",
213
+ "government_order_certificate",
214
+ "government_view_certificate",
215
+ "government_view_certificate_2025",
216
+ "government_view_certificate_casual"
217
+ ]
218
+ },
219
+ "hub": {
220
+ "label": "Главная",
221
+ "description": "Стартовый хаб: переходы между разделами обезличенного бенчмарка.",
222
+ "core": true,
223
+ "domain": "hub",
224
+ "tasks": [
225
+ "bench_hub_navigation_books",
226
+ "bench_hub_navigation_gov",
227
+ "bench_hub_navigation_rail",
228
+ "bench_hub_navigation_shop"
229
+ ]
230
+ }
231
+ }
232
+ }
docs/description.md ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Web Agent Bench — лидерборд
2
+
3
+ Платформа оценки веб-агентов на **обезличенном** бенчмарке (**51** задача, **7** разделов хаба). Все mock-сайты и формулировки в UI не привязаны к реальным брендам. Подробности: [EVAL_README.md](https://github.com/ai-forever/agent-bench/blob/open_bench/docs/EVAL_README.md).
4
+
5
+ ## Бенчмарк
6
+
7
+ | Раздел | Ключ | Задач |
8
+ |--------|------|------:|
9
+ | Маркет | `shop` | 13 |
10
+ | Книги | `books` | 7 |
11
+ | Продукты | `grocery` | 4 |
12
+ | Поезда | `rail` | 9 |
13
+ | Отели | `hotels` | 8 |
14
+ | Услуги | `gov` | 6 |
15
+ | Главная | `hub` | 4 |
16
+
17
+ Агент работает через harness (`browser-use`, `ouroboros-cut`, `ouroboros-full-*`, `hermes-ouroboros`) с выбранной LLM на mock-сайтах DAB.
18
+
19
+ ## Метрики лидерборда
20
+
21
+ ### Основная: DAB Success Rate
22
+
23
+ | Поле | Источник | Описание |
24
+ |------|----------|----------|
25
+ | `success_rate` | `run.success_rate` | `passed_tasks / total_tasks` |
26
+ | Успех задачи | `tests[].success` | Все `conditions` прошли (`dab_check.all_passed`) |
27
+
28
+ Не путать с **Agent Completion** — агент мог вызвать `done`, но условия DAB не выполнены.
29
+
30
+ ### Скорость и эффективность
31
+
32
+ | Поле | Описание |
33
+ |------|----------|
34
+ | `avg_duration_seconds` | Среднее время задачи (create_track + agent + check) |
35
+ | `avg_agent_steps` | Среднее число шагов browser-use |
36
+ | `avg_tokens_per_task` | Средние токены на задачу |
37
+ | `total_cost_usd` | Оценка стоимости LLM (`bench_eval.llm_cost`) |
38
+
39
+ ### Надёжность
40
+
41
+ | Поле | Описание |
42
+ |------|----------|
43
+ | `pass_at_k` | Pass@k по историческим прогонам (extended_metrics) |
44
+ | `agent_completion_rate` | Доля задач с `agent_is_done` |
45
+ | `agent_dab_agreement_rate` | Согласие completion и DAB |
46
+
47
+ ### UI-таксономия
48
+
49
+ Агрегаты `ui_taxonomy_stats` в `results.json`:
50
+
51
+ - `by_checked_class` — классы из `conditions` (BASKET, DATE, NAV, …)
52
+ - `by_primary` — главный UI-класс задачи
53
+ - `by_ui_pattern` — паттерны виджетов (`date:popup_grid`, …)
54
+
55
+ На лидерборде показываются топ UI-бейджи из `by_primary`.
56
+
57
+ ## Виды лидерборда (как PinchBench)
58
+
59
+ 1. **Success Rate** — DAB success, основной рейтинг
60
+ 2. **Speed** — `avg_duration_seconds` (меньше — лучше)
61
+ 3. **Cost** — стоимость и value (success / $)
62
+
63
+ ## Экспорт результатов
64
+
65
+ ```bash
66
+ python liderboard/src/export_results.py
67
+ ```
68
+
69
+ Берёт последний full-bench прогон (>5 задач) для каждой пары model × harness из `tests/eval/`.
70
+
71
+ ## Ссылки
72
+
73
+ - [Репозиторий](https://github.com/ai-forever/agent-bench)
74
+ - [Задачи бенчмарка](https://github.com/ai-forever/agent-bench/tree/open_bench/tests/bench)
75
+ - [Таксономия](https://github.com/ai-forever/agent-bench/blob/open_bench/docs/TAXONOMY.md)
pyproject.toml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [tool.ruff]
2
+ select = ["E", "F"]
3
+ ignore = ["E501"]
4
+ line-length = 119
5
+ fixable = ["A", "B", "C", "D", "E", "F", "G", "I", "N", "Q", "S", "T", "W", "ANN", "ARG", "BLE", "COM", "DJ", "DTZ", "EM", "ERA", "EXE", "FBT", "ICN", "INP", "ISC", "NPY", "PD", "PGH", "PIE", "PL", "PT", "PTH", "PYI", "RET", "RSE", "RUF", "SIM", "SLF", "TCH", "TID", "TRY", "UP", "YTT"]
6
+
7
+ [tool.isort]
8
+ profile = "black"
9
+ line_length = 119
10
+
11
+ [tool.black]
12
+ line-length = 119
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ gradio==6.9.0
2
+ jinja2>=3.1.5
3
+ numpy
4
+ pandas
results/.gitkeep ADDED
File without changes
results/deepseek_deepseek-v4-flash__browser-use.json ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model": "deepseek/deepseek-v4-flash",
3
+ "harness": "browser-use",
4
+ "provider": "openrouter",
5
+ "mock": "bench",
6
+ "finished": true,
7
+ "source_path": "/home/jovyan/emelyanov/agent-bench/tests/eval/deepseek_deepseek-v4-flash/browser-use/2026-06-23_21-48-53__ebfee145/results.json",
8
+ "submitted_at": "2026-06-24T00:59:05.569517+00:00",
9
+ "metrics": {
10
+ "success_rate": 0.45098039215686275,
11
+ "passed_tasks": 23,
12
+ "failed_tasks": 28,
13
+ "total_tasks": 51,
14
+ "avg_duration_seconds": 430.82946465441995,
15
+ "avg_agent_steps": 12.07843137254902,
16
+ "avg_tokens_per_task": 136423.29411764705,
17
+ "total_tokens": 6957588,
18
+ "total_cost_usd": 0.681866178,
19
+ "avg_cost_per_task_usd": 0.01336992505882353,
20
+ "pass_at_k": null,
21
+ "agent_completion_rate": 0.8235294117647058,
22
+ "agent_dab_agreement_rate": 0.6274509803921569,
23
+ "section_avg_rate": 0.5166143380429095
24
+ },
25
+ "sections": {
26
+ "shop": {
27
+ "label": "Маркет",
28
+ "success_rate": 0.46153846153846156,
29
+ "passed": 6,
30
+ "total": 13,
31
+ "tasks": {
32
+ "ecommerce_basket_and_favorites": false,
33
+ "ecommerce_basket_any_product": false,
34
+ "ecommerce_basket_multiple": false,
35
+ "ecommerce_basket_named_product": true,
36
+ "ecommerce_basket_named_product_02": true,
37
+ "ecommerce_basket_named_product_03": false,
38
+ "ecommerce_basket_price_10k": false,
39
+ "ecommerce_basket_price_1k": false,
40
+ "ecommerce_basket_price_constraint": true,
41
+ "ecommerce_basket_typo": false,
42
+ "ecommerce_favorites": true,
43
+ "ecommerce_favorites_02": true,
44
+ "ecommerce_favorites_any_product": true
45
+ }
46
+ },
47
+ "books": {
48
+ "label": "Книги",
49
+ "success_rate": 0.5714285714285714,
50
+ "passed": 4,
51
+ "total": 7,
52
+ "tasks": {
53
+ "digital_books_audio_basket": true,
54
+ "digital_books_author_cheapest_basket": true,
55
+ "digital_books_author_cheapest_favorites": false,
56
+ "digital_books_author_expensive_basket": false,
57
+ "digital_books_named_product_basket": true,
58
+ "digital_books_named_product_basket_02": false,
59
+ "digital_books_named_product_favorites": true
60
+ }
61
+ },
62
+ "grocery": {
63
+ "label": "Продукты",
64
+ "success_rate": 0.75,
65
+ "passed": 3,
66
+ "total": 4,
67
+ "tasks": {
68
+ "bench_grocery_navigation": true,
69
+ "grocery_basket_any_product": true,
70
+ "grocery_basket_named_product": true,
71
+ "grocery_basket_named_product_02": false
72
+ }
73
+ },
74
+ "rail": {
75
+ "label": "Поезда",
76
+ "success_rate": 0.0,
77
+ "passed": 0,
78
+ "total": 9,
79
+ "tasks": {
80
+ "rail_atomic_select_city_from": false,
81
+ "rail_atomic_select_city_to": false,
82
+ "rail_atomic_select_date": false,
83
+ "rail_atomic_submit_search": false,
84
+ "rail_book_and_pay": false,
85
+ "rail_book_and_pay_2_passengers": false,
86
+ "rail_book_to_cart": false,
87
+ "rail_book_to_cart_2_passengers": false,
88
+ "rail_book_to_cart_business": false
89
+ }
90
+ },
91
+ "hotels": {
92
+ "label": "Отели",
93
+ "success_rate": 0.5,
94
+ "passed": 4,
95
+ "total": 8,
96
+ "tasks": {
97
+ "hotel_atomic_select_city": true,
98
+ "hotel_atomic_select_end_date": false,
99
+ "hotel_atomic_select_guests": true,
100
+ "hotel_atomic_select_start_date": true,
101
+ "hotel_search_scenario": false,
102
+ "hotel_search_scenario_miami": true,
103
+ "hotel_search_scenario_typo": false,
104
+ "hotel_search_scenario_with_hotel": false
105
+ }
106
+ },
107
+ "gov": {
108
+ "label": "Услуги",
109
+ "success_rate": 0.3333333333333333,
110
+ "passed": 2,
111
+ "total": 6,
112
+ "tasks": {
113
+ "government_download_certificate": false,
114
+ "government_download_certificate_techno": true,
115
+ "government_order_certificate": true,
116
+ "government_view_certificate": false,
117
+ "government_view_certificate_2025": false,
118
+ "government_view_certificate_casual": false
119
+ }
120
+ },
121
+ "hub": {
122
+ "label": "Главная",
123
+ "success_rate": 1.0,
124
+ "passed": 4,
125
+ "total": 4,
126
+ "tasks": {
127
+ "bench_hub_navigation_books": true,
128
+ "bench_hub_navigation_gov": true,
129
+ "bench_hub_navigation_rail": true,
130
+ "bench_hub_navigation_shop": true
131
+ }
132
+ }
133
+ },
134
+ "ui_badges": [
135
+ "NAV",
136
+ "FAV",
137
+ "COUNTER",
138
+ "SELECT_AC"
139
+ ],
140
+ "ui_classes": {
141
+ "BASKET": {
142
+ "success_rate": 0.4090909090909091,
143
+ "passed": 9,
144
+ "total": 22,
145
+ "failed": 13
146
+ },
147
+ "NAV": {
148
+ "success_rate": 1.0,
149
+ "passed": 4,
150
+ "total": 4,
151
+ "failed": 0
152
+ },
153
+ "FAV": {
154
+ "success_rate": 0.8,
155
+ "passed": 4,
156
+ "total": 5,
157
+ "failed": 1
158
+ },
159
+ "GOV": {
160
+ "success_rate": 0.3333333333333333,
161
+ "passed": 2,
162
+ "total": 6,
163
+ "failed": 4
164
+ },
165
+ "SELECT_AC": {
166
+ "success_rate": 0.5,
167
+ "passed": 1,
168
+ "total": 2,
169
+ "failed": 1
170
+ },
171
+ "DATE": {
172
+ "success_rate": 0.25,
173
+ "passed": 1,
174
+ "total": 4,
175
+ "failed": 3
176
+ },
177
+ "COUNTER": {
178
+ "success_rate": 0.6666666666666666,
179
+ "passed": 2,
180
+ "total": 3,
181
+ "failed": 1
182
+ },
183
+ "CARD": {
184
+ "success_rate": 0.0,
185
+ "passed": 0,
186
+ "total": 1,
187
+ "failed": 1
188
+ },
189
+ "SELECT_LIST": {
190
+ "success_rate": 0.0,
191
+ "passed": 0,
192
+ "total": 2,
193
+ "failed": 2
194
+ },
195
+ "PAY": {
196
+ "success_rate": 0.0,
197
+ "passed": 0,
198
+ "total": 2,
199
+ "failed": 2
200
+ }
201
+ },
202
+ "section_avg_rate": 0.5166143380429095,
203
+ "raw_results_path": "results/raw/deepseek_deepseek-v4-flash__browser-use/results.json",
204
+ "entry_id": "deepseek_deepseek-v4-flash__browser-use"
205
+ }
results/deepseek_deepseek-v4-flash__openhands.json ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model": "deepseek/deepseek-v4-flash",
3
+ "harness": "openhands",
4
+ "provider": "openrouter",
5
+ "mock": "bench",
6
+ "finished": true,
7
+ "source_path": "/home/jovyan/emelyanov/agent-bench/tests/eval/deepseek_deepseek-v4-flash/openhands/2026-06-23_21-49-06__1bd3084d/results.json",
8
+ "submitted_at": "2026-06-23T23:25:08.701701+00:00",
9
+ "metrics": {
10
+ "success_rate": 0.3333333333333333,
11
+ "passed_tasks": 17,
12
+ "failed_tasks": 34,
13
+ "total_tasks": 51,
14
+ "avg_duration_seconds": 214.01237942468302,
15
+ "avg_agent_steps": 18.96078431372549,
16
+ "avg_tokens_per_task": 515313.09803921566,
17
+ "total_tokens": 26280968,
18
+ "total_cost_usd": 3.7022001800000006,
19
+ "avg_cost_per_task_usd": 0.07259216039215688,
20
+ "pass_at_k": null,
21
+ "agent_completion_rate": 0.49019607843137253,
22
+ "agent_dab_agreement_rate": 0.7647058823529411,
23
+ "section_avg_rate": 0.3830455259026687
24
+ },
25
+ "sections": {
26
+ "shop": {
27
+ "label": "Маркет",
28
+ "success_rate": 0.5384615384615384,
29
+ "passed": 7,
30
+ "total": 13,
31
+ "tasks": {
32
+ "ecommerce_basket_and_favorites": false,
33
+ "ecommerce_basket_any_product": false,
34
+ "ecommerce_basket_multiple": true,
35
+ "ecommerce_basket_named_product": true,
36
+ "ecommerce_basket_named_product_02": true,
37
+ "ecommerce_basket_named_product_03": false,
38
+ "ecommerce_basket_price_10k": false,
39
+ "ecommerce_basket_price_1k": false,
40
+ "ecommerce_basket_price_constraint": true,
41
+ "ecommerce_basket_typo": false,
42
+ "ecommerce_favorites": true,
43
+ "ecommerce_favorites_02": true,
44
+ "ecommerce_favorites_any_product": true
45
+ }
46
+ },
47
+ "books": {
48
+ "label": "Книги",
49
+ "success_rate": 0.14285714285714285,
50
+ "passed": 1,
51
+ "total": 7,
52
+ "tasks": {
53
+ "digital_books_audio_basket": false,
54
+ "digital_books_author_cheapest_basket": false,
55
+ "digital_books_author_cheapest_favorites": false,
56
+ "digital_books_author_expensive_basket": false,
57
+ "digital_books_named_product_basket": false,
58
+ "digital_books_named_product_basket_02": false,
59
+ "digital_books_named_product_favorites": true
60
+ }
61
+ },
62
+ "grocery": {
63
+ "label": "Продукты",
64
+ "success_rate": 0.75,
65
+ "passed": 3,
66
+ "total": 4,
67
+ "tasks": {
68
+ "bench_grocery_navigation": true,
69
+ "grocery_basket_any_product": true,
70
+ "grocery_basket_named_product": true,
71
+ "grocery_basket_named_product_02": false
72
+ }
73
+ },
74
+ "rail": {
75
+ "label": "Поезда",
76
+ "success_rate": 0.0,
77
+ "passed": 0,
78
+ "total": 9,
79
+ "tasks": {
80
+ "rail_atomic_select_city_from": false,
81
+ "rail_atomic_select_city_to": false,
82
+ "rail_atomic_select_date": false,
83
+ "rail_atomic_submit_search": false,
84
+ "rail_book_and_pay": false,
85
+ "rail_book_and_pay_2_passengers": false,
86
+ "rail_book_to_cart": false,
87
+ "rail_book_to_cart_2_passengers": false,
88
+ "rail_book_to_cart_business": false
89
+ }
90
+ },
91
+ "hotels": {
92
+ "label": "Отели",
93
+ "success_rate": 0.25,
94
+ "passed": 2,
95
+ "total": 8,
96
+ "tasks": {
97
+ "hotel_atomic_select_city": true,
98
+ "hotel_atomic_select_end_date": false,
99
+ "hotel_atomic_select_guests": true,
100
+ "hotel_atomic_select_start_date": false,
101
+ "hotel_search_scenario": false,
102
+ "hotel_search_scenario_miami": false,
103
+ "hotel_search_scenario_typo": false,
104
+ "hotel_search_scenario_with_hotel": false
105
+ }
106
+ },
107
+ "gov": {
108
+ "label": "Услуги",
109
+ "success_rate": 0.0,
110
+ "passed": 0,
111
+ "total": 6,
112
+ "tasks": {
113
+ "government_download_certificate": false,
114
+ "government_download_certificate_techno": false,
115
+ "government_order_certificate": false,
116
+ "government_view_certificate": false,
117
+ "government_view_certificate_2025": false,
118
+ "government_view_certificate_casual": false
119
+ }
120
+ },
121
+ "hub": {
122
+ "label": "Главная",
123
+ "success_rate": 1.0,
124
+ "passed": 4,
125
+ "total": 4,
126
+ "tasks": {
127
+ "bench_hub_navigation_books": true,
128
+ "bench_hub_navigation_gov": true,
129
+ "bench_hub_navigation_rail": true,
130
+ "bench_hub_navigation_shop": true
131
+ }
132
+ }
133
+ },
134
+ "ui_badges": [
135
+ "NAV",
136
+ "FAV",
137
+ "SELECT_AC",
138
+ "COUNTER"
139
+ ],
140
+ "ui_classes": {
141
+ "BASKET": {
142
+ "success_rate": 0.3181818181818182,
143
+ "passed": 7,
144
+ "total": 22,
145
+ "failed": 15
146
+ },
147
+ "NAV": {
148
+ "success_rate": 1.0,
149
+ "passed": 4,
150
+ "total": 4,
151
+ "failed": 0
152
+ },
153
+ "FAV": {
154
+ "success_rate": 0.8,
155
+ "passed": 4,
156
+ "total": 5,
157
+ "failed": 1
158
+ },
159
+ "GOV": {
160
+ "success_rate": 0.0,
161
+ "passed": 0,
162
+ "total": 6,
163
+ "failed": 6
164
+ },
165
+ "SELECT_AC": {
166
+ "success_rate": 0.5,
167
+ "passed": 1,
168
+ "total": 2,
169
+ "failed": 1
170
+ },
171
+ "DATE": {
172
+ "success_rate": 0.0,
173
+ "passed": 0,
174
+ "total": 4,
175
+ "failed": 4
176
+ },
177
+ "COUNTER": {
178
+ "success_rate": 0.3333333333333333,
179
+ "passed": 1,
180
+ "total": 3,
181
+ "failed": 2
182
+ },
183
+ "CARD": {
184
+ "success_rate": 0.0,
185
+ "passed": 0,
186
+ "total": 1,
187
+ "failed": 1
188
+ },
189
+ "SELECT_LIST": {
190
+ "success_rate": 0.0,
191
+ "passed": 0,
192
+ "total": 2,
193
+ "failed": 2
194
+ },
195
+ "PAY": {
196
+ "success_rate": 0.0,
197
+ "passed": 0,
198
+ "total": 2,
199
+ "failed": 2
200
+ }
201
+ },
202
+ "section_avg_rate": 0.3830455259026687,
203
+ "raw_results_path": "results/raw/deepseek_deepseek-v4-flash__openhands/results.json",
204
+ "entry_id": "deepseek_deepseek-v4-flash__openhands"
205
+ }
results/deepseek_deepseek-v4-flash__openmanus.json ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model": "deepseek/deepseek-v4-flash",
3
+ "harness": "openmanus",
4
+ "provider": "openrouter",
5
+ "mock": "bench",
6
+ "finished": true,
7
+ "source_path": "/home/jovyan/emelyanov/agent-bench/tests/eval/deepseek_deepseek-v4-flash/openmanus/2026-06-23_21-49-14__b1d56b67/results.json",
8
+ "submitted_at": "2026-06-24T00:24:03.691109+00:00",
9
+ "metrics": {
10
+ "success_rate": 0.5490196078431373,
11
+ "passed_tasks": 28,
12
+ "failed_tasks": 23,
13
+ "total_tasks": 51,
14
+ "avg_duration_seconds": 355.42385389261386,
15
+ "avg_agent_steps": 11.803921568627452,
16
+ "avg_tokens_per_task": 135782.68,
17
+ "total_tokens": 6789134,
18
+ "total_cost_usd": 1.0452048600000003,
19
+ "avg_cost_per_task_usd": 0.02049421294117648,
20
+ "pass_at_k": null,
21
+ "agent_completion_rate": 0.82,
22
+ "agent_dab_agreement_rate": 0.74,
23
+ "section_avg_rate": 0.5972004186289901
24
+ },
25
+ "sections": {
26
+ "shop": {
27
+ "label": "Маркет",
28
+ "success_rate": 0.6923076923076923,
29
+ "passed": 9,
30
+ "total": 13,
31
+ "tasks": {
32
+ "ecommerce_basket_and_favorites": false,
33
+ "ecommerce_basket_any_product": true,
34
+ "ecommerce_basket_multiple": true,
35
+ "ecommerce_basket_named_product": true,
36
+ "ecommerce_basket_named_product_02": true,
37
+ "ecommerce_basket_named_product_03": false,
38
+ "ecommerce_basket_price_10k": false,
39
+ "ecommerce_basket_price_1k": false,
40
+ "ecommerce_basket_price_constraint": true,
41
+ "ecommerce_basket_typo": true,
42
+ "ecommerce_favorites": true,
43
+ "ecommerce_favorites_02": true,
44
+ "ecommerce_favorites_any_product": true
45
+ }
46
+ },
47
+ "books": {
48
+ "label": "Книги",
49
+ "success_rate": 0.5714285714285714,
50
+ "passed": 4,
51
+ "total": 7,
52
+ "tasks": {
53
+ "digital_books_audio_basket": true,
54
+ "digital_books_author_cheapest_basket": false,
55
+ "digital_books_author_cheapest_favorites": false,
56
+ "digital_books_author_expensive_basket": false,
57
+ "digital_books_named_product_basket": true,
58
+ "digital_books_named_product_basket_02": true,
59
+ "digital_books_named_product_favorites": true
60
+ }
61
+ },
62
+ "grocery": {
63
+ "label": "Продукты",
64
+ "success_rate": 0.75,
65
+ "passed": 3,
66
+ "total": 4,
67
+ "tasks": {
68
+ "bench_grocery_navigation": true,
69
+ "grocery_basket_any_product": true,
70
+ "grocery_basket_named_product": true,
71
+ "grocery_basket_named_product_02": false
72
+ }
73
+ },
74
+ "rail": {
75
+ "label": "Поезда",
76
+ "success_rate": 0.0,
77
+ "passed": 0,
78
+ "total": 9,
79
+ "tasks": {
80
+ "rail_atomic_select_city_from": false,
81
+ "rail_atomic_select_city_to": false,
82
+ "rail_atomic_select_date": false,
83
+ "rail_atomic_submit_search": false,
84
+ "rail_book_and_pay": false,
85
+ "rail_book_and_pay_2_passengers": false,
86
+ "rail_book_to_cart": false,
87
+ "rail_book_to_cart_2_passengers": false,
88
+ "rail_book_to_cart_business": false
89
+ }
90
+ },
91
+ "hotels": {
92
+ "label": "Отели",
93
+ "success_rate": 0.5,
94
+ "passed": 4,
95
+ "total": 8,
96
+ "tasks": {
97
+ "hotel_atomic_select_city": true,
98
+ "hotel_atomic_select_end_date": true,
99
+ "hotel_atomic_select_guests": true,
100
+ "hotel_atomic_select_start_date": true,
101
+ "hotel_search_scenario": false,
102
+ "hotel_search_scenario_miami": false,
103
+ "hotel_search_scenario_typo": false,
104
+ "hotel_search_scenario_with_hotel": false
105
+ }
106
+ },
107
+ "gov": {
108
+ "label": "Услуги",
109
+ "success_rate": 0.6666666666666666,
110
+ "passed": 4,
111
+ "total": 6,
112
+ "tasks": {
113
+ "government_download_certificate": true,
114
+ "government_download_certificate_techno": true,
115
+ "government_order_certificate": true,
116
+ "government_view_certificate": false,
117
+ "government_view_certificate_2025": false,
118
+ "government_view_certificate_casual": true
119
+ }
120
+ },
121
+ "hub": {
122
+ "label": "Главная",
123
+ "success_rate": 1.0,
124
+ "passed": 4,
125
+ "total": 4,
126
+ "tasks": {
127
+ "bench_hub_navigation_books": true,
128
+ "bench_hub_navigation_gov": true,
129
+ "bench_hub_navigation_rail": true,
130
+ "bench_hub_navigation_shop": true
131
+ }
132
+ }
133
+ },
134
+ "ui_badges": [
135
+ "NAV",
136
+ "FAV",
137
+ "GOV",
138
+ "BASKET"
139
+ ],
140
+ "ui_classes": {
141
+ "BASKET": {
142
+ "success_rate": 0.5454545454545454,
143
+ "passed": 12,
144
+ "total": 22,
145
+ "failed": 10
146
+ },
147
+ "NAV": {
148
+ "success_rate": 1.0,
149
+ "passed": 4,
150
+ "total": 4,
151
+ "failed": 0
152
+ },
153
+ "FAV": {
154
+ "success_rate": 0.8,
155
+ "passed": 4,
156
+ "total": 5,
157
+ "failed": 1
158
+ },
159
+ "GOV": {
160
+ "success_rate": 0.6666666666666666,
161
+ "passed": 4,
162
+ "total": 6,
163
+ "failed": 2
164
+ },
165
+ "SELECT_AC": {
166
+ "success_rate": 0.5,
167
+ "passed": 1,
168
+ "total": 2,
169
+ "failed": 1
170
+ },
171
+ "DATE": {
172
+ "success_rate": 0.5,
173
+ "passed": 2,
174
+ "total": 4,
175
+ "failed": 2
176
+ },
177
+ "COUNTER": {
178
+ "success_rate": 0.3333333333333333,
179
+ "passed": 1,
180
+ "total": 3,
181
+ "failed": 2
182
+ },
183
+ "CARD": {
184
+ "success_rate": 0.0,
185
+ "passed": 0,
186
+ "total": 1,
187
+ "failed": 1
188
+ },
189
+ "SELECT_LIST": {
190
+ "success_rate": 0.0,
191
+ "passed": 0,
192
+ "total": 2,
193
+ "failed": 2
194
+ },
195
+ "PAY": {
196
+ "success_rate": 0.0,
197
+ "passed": 0,
198
+ "total": 2,
199
+ "failed": 2
200
+ }
201
+ },
202
+ "section_avg_rate": 0.5972004186289901,
203
+ "raw_results_path": "results/raw/deepseek_deepseek-v4-flash__openmanus/results.json",
204
+ "entry_id": "deepseek_deepseek-v4-flash__openmanus"
205
+ }
results/deepseek_deepseek-v4-flash__ouroboros-cut.json ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model": "deepseek/deepseek-v4-flash",
3
+ "harness": "ouroboros-cut",
4
+ "provider": "openrouter",
5
+ "mock": "bench",
6
+ "finished": true,
7
+ "source_path": "/home/jovyan/emelyanov/agent-bench/tests/eval/deepseek_deepseek-v4-flash/ouroboros-cut/2026-06-23_22-23-19__07dd12ff/results.json",
8
+ "submitted_at": "2026-06-24T01:55:26.128559+00:00",
9
+ "metrics": {
10
+ "success_rate": 0.49019607843137253,
11
+ "passed_tasks": 25,
12
+ "failed_tasks": 26,
13
+ "total_tasks": 51,
14
+ "avg_duration_seconds": 490.06015126830806,
15
+ "avg_agent_steps": 11.607843137254902,
16
+ "avg_tokens_per_task": 124507.70588235294,
17
+ "total_tokens": 6349893,
18
+ "total_cost_usd": 0.5917200799999999,
19
+ "avg_cost_per_task_usd": 0.011602354509803918,
20
+ "pass_at_k": null,
21
+ "agent_completion_rate": 0.8235294117647058,
22
+ "agent_dab_agreement_rate": 0.6666666666666666,
23
+ "section_avg_rate": 0.5385923600209315
24
+ },
25
+ "sections": {
26
+ "shop": {
27
+ "label": "Маркет",
28
+ "success_rate": 0.6153846153846154,
29
+ "passed": 8,
30
+ "total": 13,
31
+ "tasks": {
32
+ "ecommerce_basket_and_favorites": false,
33
+ "ecommerce_basket_any_product": true,
34
+ "ecommerce_basket_multiple": true,
35
+ "ecommerce_basket_named_product": false,
36
+ "ecommerce_basket_named_product_02": true,
37
+ "ecommerce_basket_named_product_03": false,
38
+ "ecommerce_basket_price_10k": false,
39
+ "ecommerce_basket_price_1k": false,
40
+ "ecommerce_basket_price_constraint": true,
41
+ "ecommerce_basket_typo": true,
42
+ "ecommerce_favorites": true,
43
+ "ecommerce_favorites_02": true,
44
+ "ecommerce_favorites_any_product": true
45
+ }
46
+ },
47
+ "books": {
48
+ "label": "Книги",
49
+ "success_rate": 0.5714285714285714,
50
+ "passed": 4,
51
+ "total": 7,
52
+ "tasks": {
53
+ "digital_books_audio_basket": false,
54
+ "digital_books_author_cheapest_basket": true,
55
+ "digital_books_author_cheapest_favorites": false,
56
+ "digital_books_author_expensive_basket": false,
57
+ "digital_books_named_product_basket": true,
58
+ "digital_books_named_product_basket_02": true,
59
+ "digital_books_named_product_favorites": true
60
+ }
61
+ },
62
+ "grocery": {
63
+ "label": "Продукты",
64
+ "success_rate": 0.75,
65
+ "passed": 3,
66
+ "total": 4,
67
+ "tasks": {
68
+ "bench_grocery_navigation": true,
69
+ "grocery_basket_any_product": true,
70
+ "grocery_basket_named_product": true,
71
+ "grocery_basket_named_product_02": false
72
+ }
73
+ },
74
+ "rail": {
75
+ "label": "Поезда",
76
+ "success_rate": 0.0,
77
+ "passed": 0,
78
+ "total": 9,
79
+ "tasks": {
80
+ "rail_atomic_select_city_from": false,
81
+ "rail_atomic_select_city_to": false,
82
+ "rail_atomic_select_date": false,
83
+ "rail_atomic_submit_search": false,
84
+ "rail_book_and_pay": false,
85
+ "rail_book_and_pay_2_passengers": false,
86
+ "rail_book_to_cart": false,
87
+ "rail_book_to_cart_2_passengers": false,
88
+ "rail_book_to_cart_business": false
89
+ }
90
+ },
91
+ "hotels": {
92
+ "label": "Отели",
93
+ "success_rate": 0.5,
94
+ "passed": 4,
95
+ "total": 8,
96
+ "tasks": {
97
+ "hotel_atomic_select_city": true,
98
+ "hotel_atomic_select_end_date": true,
99
+ "hotel_atomic_select_guests": true,
100
+ "hotel_atomic_select_start_date": true,
101
+ "hotel_search_scenario": false,
102
+ "hotel_search_scenario_miami": false,
103
+ "hotel_search_scenario_typo": false,
104
+ "hotel_search_scenario_with_hotel": false
105
+ }
106
+ },
107
+ "gov": {
108
+ "label": "Услуги",
109
+ "success_rate": 0.3333333333333333,
110
+ "passed": 2,
111
+ "total": 6,
112
+ "tasks": {
113
+ "government_download_certificate": false,
114
+ "government_download_certificate_techno": true,
115
+ "government_order_certificate": true,
116
+ "government_view_certificate": false,
117
+ "government_view_certificate_2025": false,
118
+ "government_view_certificate_casual": false
119
+ }
120
+ },
121
+ "hub": {
122
+ "label": "Главная",
123
+ "success_rate": 1.0,
124
+ "passed": 4,
125
+ "total": 4,
126
+ "tasks": {
127
+ "bench_hub_navigation_books": true,
128
+ "bench_hub_navigation_gov": true,
129
+ "bench_hub_navigation_rail": true,
130
+ "bench_hub_navigation_shop": true
131
+ }
132
+ }
133
+ },
134
+ "ui_badges": [
135
+ "NAV",
136
+ "FAV",
137
+ "SELECT_AC",
138
+ "DATE"
139
+ ],
140
+ "ui_classes": {
141
+ "BASKET": {
142
+ "success_rate": 0.5,
143
+ "passed": 11,
144
+ "total": 22,
145
+ "failed": 11
146
+ },
147
+ "NAV": {
148
+ "success_rate": 1.0,
149
+ "passed": 4,
150
+ "total": 4,
151
+ "failed": 0
152
+ },
153
+ "FAV": {
154
+ "success_rate": 0.8,
155
+ "passed": 4,
156
+ "total": 5,
157
+ "failed": 1
158
+ },
159
+ "GOV": {
160
+ "success_rate": 0.3333333333333333,
161
+ "passed": 2,
162
+ "total": 6,
163
+ "failed": 4
164
+ },
165
+ "SELECT_AC": {
166
+ "success_rate": 0.5,
167
+ "passed": 1,
168
+ "total": 2,
169
+ "failed": 1
170
+ },
171
+ "DATE": {
172
+ "success_rate": 0.5,
173
+ "passed": 2,
174
+ "total": 4,
175
+ "failed": 2
176
+ },
177
+ "COUNTER": {
178
+ "success_rate": 0.3333333333333333,
179
+ "passed": 1,
180
+ "total": 3,
181
+ "failed": 2
182
+ },
183
+ "CARD": {
184
+ "success_rate": 0.0,
185
+ "passed": 0,
186
+ "total": 1,
187
+ "failed": 1
188
+ },
189
+ "SELECT_LIST": {
190
+ "success_rate": 0.0,
191
+ "passed": 0,
192
+ "total": 2,
193
+ "failed": 2
194
+ },
195
+ "PAY": {
196
+ "success_rate": 0.0,
197
+ "passed": 0,
198
+ "total": 2,
199
+ "failed": 2
200
+ }
201
+ },
202
+ "section_avg_rate": 0.5385923600209315,
203
+ "raw_results_path": "results/raw/deepseek_deepseek-v4-flash__ouroboros-cut/results.json",
204
+ "entry_id": "deepseek_deepseek-v4-flash__ouroboros-cut"
205
+ }
results/deepseek_deepseek-v4-flash__ouroboros-full-evolving.json ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model": "deepseek/deepseek-v4-flash",
3
+ "harness": "ouroboros-full-evolving",
4
+ "provider": "openrouter",
5
+ "mock": "bench",
6
+ "finished": true,
7
+ "source_path": "/home/jovyan/emelyanov/agent-bench/tests/eval/deepseek_deepseek-v4-flash/ouroboros-full-evolving/2026-06-23_22-23-32__lm-mpi-job-90bbb689-8f98-4242-a478-9ed4d96171f3-mpimaster-0-1168-18362-1782253409/results.json",
8
+ "submitted_at": "2026-06-24T12:17:31.606961+00:00",
9
+ "metrics": {
10
+ "success_rate": 0.49019607843137253,
11
+ "passed_tasks": 25,
12
+ "failed_tasks": 26,
13
+ "total_tasks": 51,
14
+ "avg_duration_seconds": 980.6634050323902,
15
+ "avg_agent_steps": 22.725490196078432,
16
+ "avg_tokens_per_task": 1672382.5227272727,
17
+ "total_tokens": 73584831,
18
+ "total_cost_usd": 3.50092,
19
+ "avg_cost_per_task_usd": 0.06864549019607842,
20
+ "pass_at_k": null,
21
+ "agent_completion_rate": 0.7058823529411765,
22
+ "agent_dab_agreement_rate": 0.7843137254901961,
23
+ "section_avg_rate": 0.5300889586603873
24
+ },
25
+ "sections": {
26
+ "shop": {
27
+ "label": "Маркет",
28
+ "success_rate": 0.6153846153846154,
29
+ "passed": 8,
30
+ "total": 13,
31
+ "tasks": {
32
+ "ecommerce_basket_and_favorites": false,
33
+ "ecommerce_basket_any_product": true,
34
+ "ecommerce_basket_multiple": true,
35
+ "ecommerce_basket_named_product": true,
36
+ "ecommerce_basket_named_product_02": true,
37
+ "ecommerce_basket_named_product_03": false,
38
+ "ecommerce_basket_price_10k": false,
39
+ "ecommerce_basket_price_1k": false,
40
+ "ecommerce_basket_price_constraint": true,
41
+ "ecommerce_basket_typo": true,
42
+ "ecommerce_favorites": true,
43
+ "ecommerce_favorites_02": true,
44
+ "ecommerce_favorites_any_product": false
45
+ }
46
+ },
47
+ "books": {
48
+ "label": "Книги",
49
+ "success_rate": 0.42857142857142855,
50
+ "passed": 3,
51
+ "total": 7,
52
+ "tasks": {
53
+ "digital_books_audio_basket": true,
54
+ "digital_books_author_cheapest_basket": true,
55
+ "digital_books_author_cheapest_favorites": false,
56
+ "digital_books_author_expensive_basket": false,
57
+ "digital_books_named_product_basket": false,
58
+ "digital_books_named_product_basket_02": true,
59
+ "digital_books_named_product_favorites": false
60
+ }
61
+ },
62
+ "grocery": {
63
+ "label": "Продукты",
64
+ "success_rate": 0.5,
65
+ "passed": 2,
66
+ "total": 4,
67
+ "tasks": {
68
+ "bench_grocery_navigation": true,
69
+ "grocery_basket_any_product": true,
70
+ "grocery_basket_named_product": false,
71
+ "grocery_basket_named_product_02": false
72
+ }
73
+ },
74
+ "rail": {
75
+ "label": "Поезда",
76
+ "success_rate": 0.0,
77
+ "passed": 0,
78
+ "total": 9,
79
+ "tasks": {
80
+ "rail_atomic_select_city_from": false,
81
+ "rail_atomic_select_city_to": false,
82
+ "rail_atomic_select_date": false,
83
+ "rail_atomic_submit_search": false,
84
+ "rail_book_and_pay": false,
85
+ "rail_book_and_pay_2_passengers": false,
86
+ "rail_book_to_cart": false,
87
+ "rail_book_to_cart_2_passengers": false,
88
+ "rail_book_to_cart_business": false
89
+ }
90
+ },
91
+ "hotels": {
92
+ "label": "Отели",
93
+ "success_rate": 0.5,
94
+ "passed": 4,
95
+ "total": 8,
96
+ "tasks": {
97
+ "hotel_atomic_select_city": true,
98
+ "hotel_atomic_select_end_date": false,
99
+ "hotel_atomic_select_guests": true,
100
+ "hotel_atomic_select_start_date": true,
101
+ "hotel_search_scenario": false,
102
+ "hotel_search_scenario_miami": false,
103
+ "hotel_search_scenario_typo": true,
104
+ "hotel_search_scenario_with_hotel": false
105
+ }
106
+ },
107
+ "gov": {
108
+ "label": "Услуги",
109
+ "success_rate": 0.6666666666666666,
110
+ "passed": 4,
111
+ "total": 6,
112
+ "tasks": {
113
+ "government_download_certificate": true,
114
+ "government_download_certificate_techno": false,
115
+ "government_order_certificate": true,
116
+ "government_view_certificate": false,
117
+ "government_view_certificate_2025": true,
118
+ "government_view_certificate_casual": true
119
+ }
120
+ },
121
+ "hub": {
122
+ "label": "Главная",
123
+ "success_rate": 1.0,
124
+ "passed": 4,
125
+ "total": 4,
126
+ "tasks": {
127
+ "bench_hub_navigation_books": true,
128
+ "bench_hub_navigation_gov": true,
129
+ "bench_hub_navigation_rail": true,
130
+ "bench_hub_navigation_shop": true
131
+ }
132
+ }
133
+ },
134
+ "ui_badges": [
135
+ "SELECT_AC",
136
+ "NAV",
137
+ "GOV",
138
+ "BASKET"
139
+ ],
140
+ "ui_classes": {
141
+ "BASKET": {
142
+ "success_rate": 0.5,
143
+ "passed": 11,
144
+ "total": 22,
145
+ "failed": 11
146
+ },
147
+ "NAV": {
148
+ "success_rate": 1.0,
149
+ "passed": 4,
150
+ "total": 4,
151
+ "failed": 0
152
+ },
153
+ "FAV": {
154
+ "success_rate": 0.4,
155
+ "passed": 2,
156
+ "total": 5,
157
+ "failed": 3
158
+ },
159
+ "GOV": {
160
+ "success_rate": 0.6666666666666666,
161
+ "passed": 4,
162
+ "total": 6,
163
+ "failed": 2
164
+ },
165
+ "SELECT_AC": {
166
+ "success_rate": 1.0,
167
+ "passed": 2,
168
+ "total": 2,
169
+ "failed": 0
170
+ },
171
+ "DATE": {
172
+ "success_rate": 0.25,
173
+ "passed": 1,
174
+ "total": 4,
175
+ "failed": 3
176
+ },
177
+ "COUNTER": {
178
+ "success_rate": 0.3333333333333333,
179
+ "passed": 1,
180
+ "total": 3,
181
+ "failed": 2
182
+ },
183
+ "CARD": {
184
+ "success_rate": 0.0,
185
+ "passed": 0,
186
+ "total": 1,
187
+ "failed": 1
188
+ },
189
+ "SELECT_LIST": {
190
+ "success_rate": 0.0,
191
+ "passed": 0,
192
+ "total": 2,
193
+ "failed": 2
194
+ },
195
+ "PAY": {
196
+ "success_rate": 0.0,
197
+ "passed": 0,
198
+ "total": 2,
199
+ "failed": 2
200
+ }
201
+ },
202
+ "section_avg_rate": 0.5300889586603873,
203
+ "raw_results_path": "results/raw/deepseek_deepseek-v4-flash__ouroboros-full-evolving/results.json",
204
+ "entry_id": "deepseek_deepseek-v4-flash__ouroboros-full-evolving"
205
+ }
results/deepseek_deepseek-v4-flash__ouroboros-full-isolated.json ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model": "deepseek/deepseek-v4-flash",
3
+ "harness": "ouroboros-full-isolated",
4
+ "provider": "openrouter",
5
+ "mock": "bench",
6
+ "finished": true,
7
+ "source_path": "/home/jovyan/emelyanov/agent-bench/tests/eval/deepseek_deepseek-v4-flash/ouroboros-full-isolated/2026-06-24_11-36-03__lm-mpi-job-5f3144e2-b474-4212-aa8e-67a910368cec-mpimaster-0-485-8835-1782300960/results.json",
8
+ "submitted_at": "2026-06-24T15:22:04.805657+00:00",
9
+ "metrics": {
10
+ "success_rate": 0.5882352941176471,
11
+ "passed_tasks": 30,
12
+ "failed_tasks": 21,
13
+ "total_tasks": 51,
14
+ "avg_duration_seconds": 529.9419007587667,
15
+ "avg_agent_steps": 16.980392156862745,
16
+ "avg_tokens_per_task": 971737.2,
17
+ "total_tokens": 34010802,
18
+ "total_cost_usd": 1.6644430000000001,
19
+ "avg_cost_per_task_usd": 0.03263613725490196,
20
+ "pass_at_k": null,
21
+ "agent_completion_rate": 0.6862745098039216,
22
+ "agent_dab_agreement_rate": 0.7450980392156863,
23
+ "section_avg_rate": 0.64141810570382
24
+ },
25
+ "sections": {
26
+ "shop": {
27
+ "label": "Маркет",
28
+ "success_rate": 0.6923076923076923,
29
+ "passed": 9,
30
+ "total": 13,
31
+ "tasks": {
32
+ "ecommerce_basket_and_favorites": false,
33
+ "ecommerce_basket_any_product": true,
34
+ "ecommerce_basket_multiple": true,
35
+ "ecommerce_basket_named_product": true,
36
+ "ecommerce_basket_named_product_02": true,
37
+ "ecommerce_basket_named_product_03": false,
38
+ "ecommerce_basket_price_10k": false,
39
+ "ecommerce_basket_price_1k": false,
40
+ "ecommerce_basket_price_constraint": true,
41
+ "ecommerce_basket_typo": true,
42
+ "ecommerce_favorites": true,
43
+ "ecommerce_favorites_02": true,
44
+ "ecommerce_favorites_any_product": true
45
+ }
46
+ },
47
+ "books": {
48
+ "label": "Книги",
49
+ "success_rate": 0.7142857142857143,
50
+ "passed": 5,
51
+ "total": 7,
52
+ "tasks": {
53
+ "digital_books_audio_basket": true,
54
+ "digital_books_author_cheapest_basket": true,
55
+ "digital_books_author_cheapest_favorites": false,
56
+ "digital_books_author_expensive_basket": false,
57
+ "digital_books_named_product_basket": true,
58
+ "digital_books_named_product_basket_02": true,
59
+ "digital_books_named_product_favorites": true
60
+ }
61
+ },
62
+ "grocery": {
63
+ "label": "Продукты",
64
+ "success_rate": 0.75,
65
+ "passed": 3,
66
+ "total": 4,
67
+ "tasks": {
68
+ "bench_grocery_navigation": true,
69
+ "grocery_basket_any_product": true,
70
+ "grocery_basket_named_product": true,
71
+ "grocery_basket_named_product_02": false
72
+ }
73
+ },
74
+ "rail": {
75
+ "label": "Поезда",
76
+ "success_rate": 0.0,
77
+ "passed": 0,
78
+ "total": 9,
79
+ "tasks": {
80
+ "rail_atomic_select_city_from": false,
81
+ "rail_atomic_select_city_to": false,
82
+ "rail_atomic_select_date": false,
83
+ "rail_atomic_submit_search": false,
84
+ "rail_book_and_pay": false,
85
+ "rail_book_and_pay_2_passengers": false,
86
+ "rail_book_to_cart": false,
87
+ "rail_book_to_cart_2_passengers": false,
88
+ "rail_book_to_cart_business": false
89
+ }
90
+ },
91
+ "hotels": {
92
+ "label": "Отели",
93
+ "success_rate": 0.5,
94
+ "passed": 4,
95
+ "total": 8,
96
+ "tasks": {
97
+ "hotel_atomic_select_city": true,
98
+ "hotel_atomic_select_end_date": true,
99
+ "hotel_atomic_select_guests": true,
100
+ "hotel_atomic_select_start_date": true,
101
+ "hotel_search_scenario": false,
102
+ "hotel_search_scenario_miami": false,
103
+ "hotel_search_scenario_typo": false,
104
+ "hotel_search_scenario_with_hotel": false
105
+ }
106
+ },
107
+ "gov": {
108
+ "label": "Услуги",
109
+ "success_rate": 0.8333333333333334,
110
+ "passed": 5,
111
+ "total": 6,
112
+ "tasks": {
113
+ "government_download_certificate": true,
114
+ "government_download_certificate_techno": true,
115
+ "government_order_certificate": true,
116
+ "government_view_certificate": false,
117
+ "government_view_certificate_2025": true,
118
+ "government_view_certificate_casual": true
119
+ }
120
+ },
121
+ "hub": {
122
+ "label": "Главная",
123
+ "success_rate": 1.0,
124
+ "passed": 4,
125
+ "total": 4,
126
+ "tasks": {
127
+ "bench_hub_navigation_books": true,
128
+ "bench_hub_navigation_gov": true,
129
+ "bench_hub_navigation_rail": true,
130
+ "bench_hub_navigation_shop": true
131
+ }
132
+ }
133
+ },
134
+ "ui_badges": [
135
+ "NAV",
136
+ "GOV",
137
+ "FAV",
138
+ "BASKET"
139
+ ],
140
+ "ui_classes": {
141
+ "BASKET": {
142
+ "success_rate": 0.5909090909090909,
143
+ "passed": 13,
144
+ "total": 22,
145
+ "failed": 9
146
+ },
147
+ "NAV": {
148
+ "success_rate": 1.0,
149
+ "passed": 4,
150
+ "total": 4,
151
+ "failed": 0
152
+ },
153
+ "FAV": {
154
+ "success_rate": 0.8,
155
+ "passed": 4,
156
+ "total": 5,
157
+ "failed": 1
158
+ },
159
+ "GOV": {
160
+ "success_rate": 0.8333333333333334,
161
+ "passed": 5,
162
+ "total": 6,
163
+ "failed": 1
164
+ },
165
+ "SELECT_AC": {
166
+ "success_rate": 0.5,
167
+ "passed": 1,
168
+ "total": 2,
169
+ "failed": 1
170
+ },
171
+ "DATE": {
172
+ "success_rate": 0.5,
173
+ "passed": 2,
174
+ "total": 4,
175
+ "failed": 2
176
+ },
177
+ "COUNTER": {
178
+ "success_rate": 0.3333333333333333,
179
+ "passed": 1,
180
+ "total": 3,
181
+ "failed": 2
182
+ },
183
+ "CARD": {
184
+ "success_rate": 0.0,
185
+ "passed": 0,
186
+ "total": 1,
187
+ "failed": 1
188
+ },
189
+ "SELECT_LIST": {
190
+ "success_rate": 0.0,
191
+ "passed": 0,
192
+ "total": 2,
193
+ "failed": 2
194
+ },
195
+ "PAY": {
196
+ "success_rate": 0.0,
197
+ "passed": 0,
198
+ "total": 2,
199
+ "failed": 2
200
+ }
201
+ },
202
+ "section_avg_rate": 0.64141810570382,
203
+ "raw_results_path": "results/raw/deepseek_deepseek-v4-flash__ouroboros-full-isolated/results.json",
204
+ "entry_id": "deepseek_deepseek-v4-flash__ouroboros-full-isolated"
205
+ }
results/google_gemini-2.5-flash__browser-use.json ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model": "google/gemini-2.5-flash",
3
+ "harness": "browser-use",
4
+ "provider": "openrouter",
5
+ "mock": "bench",
6
+ "finished": true,
7
+ "source_path": "/home/jovyan/emelyanov/agent-bench/tests/eval/google_gemini-2.5-flash/browser-use/2026-06-23_21-48-40__30b21cb0/results.json",
8
+ "submitted_at": "2026-06-23T23:26:25.408974+00:00",
9
+ "metrics": {
10
+ "success_rate": 0.4117647058823529,
11
+ "passed_tasks": 21,
12
+ "failed_tasks": 30,
13
+ "total_tasks": 51,
14
+ "avg_duration_seconds": 216.41764206044815,
15
+ "avg_agent_steps": 14.176470588235293,
16
+ "avg_tokens_per_task": 174671.92156862744,
17
+ "total_tokens": 8908268,
18
+ "total_cost_usd": 3.3214496000000002,
19
+ "avg_cost_per_task_usd": 0.06512646274509805,
20
+ "pass_at_k": null,
21
+ "agent_completion_rate": 0.8235294117647058,
22
+ "agent_dab_agreement_rate": 0.5882352941176471,
23
+ "section_avg_rate": 0.45866038723181585
24
+ },
25
+ "sections": {
26
+ "shop": {
27
+ "label": "Маркет",
28
+ "success_rate": 0.6153846153846154,
29
+ "passed": 8,
30
+ "total": 13,
31
+ "tasks": {
32
+ "ecommerce_basket_and_favorites": false,
33
+ "ecommerce_basket_any_product": false,
34
+ "ecommerce_basket_multiple": false,
35
+ "ecommerce_basket_named_product": true,
36
+ "ecommerce_basket_named_product_02": true,
37
+ "ecommerce_basket_named_product_03": true,
38
+ "ecommerce_basket_price_10k": false,
39
+ "ecommerce_basket_price_1k": false,
40
+ "ecommerce_basket_price_constraint": true,
41
+ "ecommerce_basket_typo": true,
42
+ "ecommerce_favorites": true,
43
+ "ecommerce_favorites_02": true,
44
+ "ecommerce_favorites_any_product": true
45
+ }
46
+ },
47
+ "books": {
48
+ "label": "Книги",
49
+ "success_rate": 0.42857142857142855,
50
+ "passed": 3,
51
+ "total": 7,
52
+ "tasks": {
53
+ "digital_books_audio_basket": true,
54
+ "digital_books_author_cheapest_basket": true,
55
+ "digital_books_author_cheapest_favorites": false,
56
+ "digital_books_author_expensive_basket": false,
57
+ "digital_books_named_product_basket": false,
58
+ "digital_books_named_product_basket_02": true,
59
+ "digital_books_named_product_favorites": false
60
+ }
61
+ },
62
+ "grocery": {
63
+ "label": "Продукты",
64
+ "success_rate": 0.75,
65
+ "passed": 3,
66
+ "total": 4,
67
+ "tasks": {
68
+ "bench_grocery_navigation": true,
69
+ "grocery_basket_any_product": true,
70
+ "grocery_basket_named_product": true,
71
+ "grocery_basket_named_product_02": false
72
+ }
73
+ },
74
+ "rail": {
75
+ "label": "Поезда",
76
+ "success_rate": 0.0,
77
+ "passed": 0,
78
+ "total": 9,
79
+ "tasks": {
80
+ "rail_atomic_select_city_from": false,
81
+ "rail_atomic_select_city_to": false,
82
+ "rail_atomic_select_date": false,
83
+ "rail_atomic_submit_search": false,
84
+ "rail_book_and_pay": false,
85
+ "rail_book_and_pay_2_passengers": false,
86
+ "rail_book_to_cart": false,
87
+ "rail_book_to_cart_2_passengers": false,
88
+ "rail_book_to_cart_business": false
89
+ }
90
+ },
91
+ "hotels": {
92
+ "label": "Отели",
93
+ "success_rate": 0.25,
94
+ "passed": 2,
95
+ "total": 8,
96
+ "tasks": {
97
+ "hotel_atomic_select_city": true,
98
+ "hotel_atomic_select_end_date": false,
99
+ "hotel_atomic_select_guests": true,
100
+ "hotel_atomic_select_start_date": false,
101
+ "hotel_search_scenario": false,
102
+ "hotel_search_scenario_miami": false,
103
+ "hotel_search_scenario_typo": false,
104
+ "hotel_search_scenario_with_hotel": false
105
+ }
106
+ },
107
+ "gov": {
108
+ "label": "Услуги",
109
+ "success_rate": 0.16666666666666666,
110
+ "passed": 1,
111
+ "total": 6,
112
+ "tasks": {
113
+ "government_download_certificate": false,
114
+ "government_download_certificate_techno": true,
115
+ "government_order_certificate": false,
116
+ "government_view_certificate": false,
117
+ "government_view_certificate_2025": false,
118
+ "government_view_certificate_casual": false
119
+ }
120
+ },
121
+ "hub": {
122
+ "label": "Главная",
123
+ "success_rate": 1.0,
124
+ "passed": 4,
125
+ "total": 4,
126
+ "tasks": {
127
+ "bench_hub_navigation_books": true,
128
+ "bench_hub_navigation_gov": true,
129
+ "bench_hub_navigation_rail": true,
130
+ "bench_hub_navigation_shop": true
131
+ }
132
+ }
133
+ },
134
+ "ui_badges": [
135
+ "NAV",
136
+ "FAV",
137
+ "SELECT_AC",
138
+ "BASKET"
139
+ ],
140
+ "ui_classes": {
141
+ "BASKET": {
142
+ "success_rate": 0.5,
143
+ "passed": 11,
144
+ "total": 22,
145
+ "failed": 11
146
+ },
147
+ "NAV": {
148
+ "success_rate": 1.0,
149
+ "passed": 4,
150
+ "total": 4,
151
+ "failed": 0
152
+ },
153
+ "FAV": {
154
+ "success_rate": 0.6,
155
+ "passed": 3,
156
+ "total": 5,
157
+ "failed": 2
158
+ },
159
+ "GOV": {
160
+ "success_rate": 0.16666666666666666,
161
+ "passed": 1,
162
+ "total": 6,
163
+ "failed": 5
164
+ },
165
+ "SELECT_AC": {
166
+ "success_rate": 0.5,
167
+ "passed": 1,
168
+ "total": 2,
169
+ "failed": 1
170
+ },
171
+ "DATE": {
172
+ "success_rate": 0.0,
173
+ "passed": 0,
174
+ "total": 4,
175
+ "failed": 4
176
+ },
177
+ "COUNTER": {
178
+ "success_rate": 0.3333333333333333,
179
+ "passed": 1,
180
+ "total": 3,
181
+ "failed": 2
182
+ },
183
+ "CARD": {
184
+ "success_rate": 0.0,
185
+ "passed": 0,
186
+ "total": 1,
187
+ "failed": 1
188
+ },
189
+ "SELECT_LIST": {
190
+ "success_rate": 0.0,
191
+ "passed": 0,
192
+ "total": 2,
193
+ "failed": 2
194
+ },
195
+ "PAY": {
196
+ "success_rate": 0.0,
197
+ "passed": 0,
198
+ "total": 2,
199
+ "failed": 2
200
+ }
201
+ },
202
+ "section_avg_rate": 0.45866038723181585,
203
+ "raw_results_path": "results/raw/google_gemini-2.5-flash__browser-use/results.json",
204
+ "entry_id": "google_gemini-2.5-flash__browser-use"
205
+ }
results/google_gemini-2.5-flash__openhands.json ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model": "google/gemini-2.5-flash",
3
+ "harness": "openhands",
4
+ "provider": "openrouter",
5
+ "mock": "bench",
6
+ "finished": true,
7
+ "source_path": "/home/jovyan/emelyanov/agent-bench/tests/eval/google_gemini-2.5-flash/openhands/2026-06-24_11-35-27__55edfa0a/results.json",
8
+ "submitted_at": "2026-06-24T14:41:37.265464+00:00",
9
+ "metrics": {
10
+ "success_rate": 0.13725490196078433,
11
+ "passed_tasks": 7,
12
+ "failed_tasks": 44,
13
+ "total_tasks": 51,
14
+ "avg_duration_seconds": 218.3109522406103,
15
+ "avg_agent_steps": 10.254901960784315,
16
+ "avg_tokens_per_task": 197287.19607843139,
17
+ "total_tokens": 10061647,
18
+ "total_cost_usd": 3.126107099999999,
19
+ "avg_cost_per_task_usd": 0.06129621764705881,
20
+ "pass_at_k": null,
21
+ "agent_completion_rate": 0.9607843137254902,
22
+ "agent_dab_agreement_rate": 0.13725490196078433,
23
+ "section_avg_rate": 0.2099686028257457
24
+ },
25
+ "sections": {
26
+ "shop": {
27
+ "label": "Маркет",
28
+ "success_rate": 0.07692307692307693,
29
+ "passed": 1,
30
+ "total": 13,
31
+ "tasks": {
32
+ "ecommerce_basket_and_favorites": false,
33
+ "ecommerce_basket_any_product": false,
34
+ "ecommerce_basket_multiple": false,
35
+ "ecommerce_basket_named_product": false,
36
+ "ecommerce_basket_named_product_02": true,
37
+ "ecommerce_basket_named_product_03": false,
38
+ "ecommerce_basket_price_10k": false,
39
+ "ecommerce_basket_price_1k": false,
40
+ "ecommerce_basket_price_constraint": false,
41
+ "ecommerce_basket_typo": false,
42
+ "ecommerce_favorites": false,
43
+ "ecommerce_favorites_02": false,
44
+ "ecommerce_favorites_any_product": false
45
+ }
46
+ },
47
+ "books": {
48
+ "label": "Книги",
49
+ "success_rate": 0.14285714285714285,
50
+ "passed": 1,
51
+ "total": 7,
52
+ "tasks": {
53
+ "digital_books_audio_basket": false,
54
+ "digital_books_author_cheapest_basket": true,
55
+ "digital_books_author_cheapest_favorites": false,
56
+ "digital_books_author_expensive_basket": false,
57
+ "digital_books_named_product_basket": false,
58
+ "digital_books_named_product_basket_02": false,
59
+ "digital_books_named_product_favorites": false
60
+ }
61
+ },
62
+ "grocery": {
63
+ "label": "Продукты",
64
+ "success_rate": 0.25,
65
+ "passed": 1,
66
+ "total": 4,
67
+ "tasks": {
68
+ "bench_grocery_navigation": true,
69
+ "grocery_basket_any_product": false,
70
+ "grocery_basket_named_product": false,
71
+ "grocery_basket_named_product_02": false
72
+ }
73
+ },
74
+ "rail": {
75
+ "label": "Поезда",
76
+ "success_rate": 0.0,
77
+ "passed": 0,
78
+ "total": 9,
79
+ "tasks": {
80
+ "rail_atomic_select_city_from": false,
81
+ "rail_atomic_select_city_to": false,
82
+ "rail_atomic_select_date": false,
83
+ "rail_atomic_submit_search": false,
84
+ "rail_book_and_pay": false,
85
+ "rail_book_and_pay_2_passengers": false,
86
+ "rail_book_to_cart": false,
87
+ "rail_book_to_cart_2_passengers": false,
88
+ "rail_book_to_cart_business": false
89
+ }
90
+ },
91
+ "hotels": {
92
+ "label": "Отели",
93
+ "success_rate": 0.0,
94
+ "passed": 0,
95
+ "total": 8,
96
+ "tasks": {
97
+ "hotel_atomic_select_city": false,
98
+ "hotel_atomic_select_end_date": false,
99
+ "hotel_atomic_select_guests": false,
100
+ "hotel_atomic_select_start_date": false,
101
+ "hotel_search_scenario": false,
102
+ "hotel_search_scenario_miami": false,
103
+ "hotel_search_scenario_typo": false,
104
+ "hotel_search_scenario_with_hotel": false
105
+ }
106
+ },
107
+ "gov": {
108
+ "label": "Услуги",
109
+ "success_rate": 0.0,
110
+ "passed": 0,
111
+ "total": 6,
112
+ "tasks": {
113
+ "government_download_certificate": false,
114
+ "government_download_certificate_techno": false,
115
+ "government_order_certificate": false,
116
+ "government_view_certificate": false,
117
+ "government_view_certificate_2025": false,
118
+ "government_view_certificate_casual": false
119
+ }
120
+ },
121
+ "hub": {
122
+ "label": "Главная",
123
+ "success_rate": 1.0,
124
+ "passed": 4,
125
+ "total": 4,
126
+ "tasks": {
127
+ "bench_hub_navigation_books": true,
128
+ "bench_hub_navigation_gov": true,
129
+ "bench_hub_navigation_rail": true,
130
+ "bench_hub_navigation_shop": true
131
+ }
132
+ }
133
+ },
134
+ "ui_badges": [
135
+ "NAV",
136
+ "BASKET",
137
+ "SELECT_LIST",
138
+ "SELECT_AC"
139
+ ],
140
+ "ui_classes": {
141
+ "BASKET": {
142
+ "success_rate": 0.13636363636363635,
143
+ "passed": 3,
144
+ "total": 22,
145
+ "failed": 19
146
+ },
147
+ "NAV": {
148
+ "success_rate": 1.0,
149
+ "passed": 4,
150
+ "total": 4,
151
+ "failed": 0
152
+ },
153
+ "FAV": {
154
+ "success_rate": 0.0,
155
+ "passed": 0,
156
+ "total": 5,
157
+ "failed": 5
158
+ },
159
+ "GOV": {
160
+ "success_rate": 0.0,
161
+ "passed": 0,
162
+ "total": 6,
163
+ "failed": 6
164
+ },
165
+ "SELECT_AC": {
166
+ "success_rate": 0.0,
167
+ "passed": 0,
168
+ "total": 2,
169
+ "failed": 2
170
+ },
171
+ "DATE": {
172
+ "success_rate": 0.0,
173
+ "passed": 0,
174
+ "total": 4,
175
+ "failed": 4
176
+ },
177
+ "COUNTER": {
178
+ "success_rate": 0.0,
179
+ "passed": 0,
180
+ "total": 3,
181
+ "failed": 3
182
+ },
183
+ "CARD": {
184
+ "success_rate": 0.0,
185
+ "passed": 0,
186
+ "total": 1,
187
+ "failed": 1
188
+ },
189
+ "SELECT_LIST": {
190
+ "success_rate": 0.0,
191
+ "passed": 0,
192
+ "total": 2,
193
+ "failed": 2
194
+ },
195
+ "PAY": {
196
+ "success_rate": 0.0,
197
+ "passed": 0,
198
+ "total": 2,
199
+ "failed": 2
200
+ }
201
+ },
202
+ "section_avg_rate": 0.2099686028257457,
203
+ "raw_results_path": "results/raw/google_gemini-2.5-flash__openhands/results.json",
204
+ "entry_id": "google_gemini-2.5-flash__openhands"
205
+ }
results/google_gemini-2.5-flash__openmanus.json ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model": "google/gemini-2.5-flash",
3
+ "harness": "openmanus",
4
+ "provider": "openrouter",
5
+ "mock": "bench",
6
+ "finished": true,
7
+ "source_path": "/home/jovyan/emelyanov/agent-bench/tests/eval/google_gemini-2.5-flash/openmanus/2026-06-23_23-34-21__60885ccc/results.json",
8
+ "submitted_at": "2026-06-24T01:10:34.661145+00:00",
9
+ "metrics": {
10
+ "success_rate": 0.47058823529411764,
11
+ "passed_tasks": 24,
12
+ "failed_tasks": 27,
13
+ "total_tasks": 51,
14
+ "avg_duration_seconds": 212.98681739418237,
15
+ "avg_agent_steps": 12.92156862745098,
16
+ "avg_tokens_per_task": 163701.80392156861,
17
+ "total_tokens": 8348792,
18
+ "total_cost_usd": 3.0831540000000004,
19
+ "avg_cost_per_task_usd": 0.06045400000000001,
20
+ "pass_at_k": null,
21
+ "agent_completion_rate": 0.8235294117647058,
22
+ "agent_dab_agreement_rate": 0.6470588235294118,
23
+ "section_avg_rate": 0.5173338566195709
24
+ },
25
+ "sections": {
26
+ "shop": {
27
+ "label": "Маркет",
28
+ "success_rate": 0.6153846153846154,
29
+ "passed": 8,
30
+ "total": 13,
31
+ "tasks": {
32
+ "ecommerce_basket_and_favorites": false,
33
+ "ecommerce_basket_any_product": false,
34
+ "ecommerce_basket_multiple": true,
35
+ "ecommerce_basket_named_product": true,
36
+ "ecommerce_basket_named_product_02": true,
37
+ "ecommerce_basket_named_product_03": true,
38
+ "ecommerce_basket_price_10k": false,
39
+ "ecommerce_basket_price_1k": false,
40
+ "ecommerce_basket_price_constraint": true,
41
+ "ecommerce_basket_typo": true,
42
+ "ecommerce_favorites": true,
43
+ "ecommerce_favorites_02": false,
44
+ "ecommerce_favorites_any_product": true
45
+ }
46
+ },
47
+ "books": {
48
+ "label": "Книги",
49
+ "success_rate": 0.7142857142857143,
50
+ "passed": 5,
51
+ "total": 7,
52
+ "tasks": {
53
+ "digital_books_audio_basket": true,
54
+ "digital_books_author_cheapest_basket": true,
55
+ "digital_books_author_cheapest_favorites": false,
56
+ "digital_books_author_expensive_basket": false,
57
+ "digital_books_named_product_basket": true,
58
+ "digital_books_named_product_basket_02": true,
59
+ "digital_books_named_product_favorites": true
60
+ }
61
+ },
62
+ "grocery": {
63
+ "label": "Продукты",
64
+ "success_rate": 0.75,
65
+ "passed": 3,
66
+ "total": 4,
67
+ "tasks": {
68
+ "bench_grocery_navigation": true,
69
+ "grocery_basket_any_product": true,
70
+ "grocery_basket_named_product": true,
71
+ "grocery_basket_named_product_02": false
72
+ }
73
+ },
74
+ "rail": {
75
+ "label": "Поезда",
76
+ "success_rate": 0.0,
77
+ "passed": 0,
78
+ "total": 9,
79
+ "tasks": {
80
+ "rail_atomic_select_city_from": false,
81
+ "rail_atomic_select_city_to": false,
82
+ "rail_atomic_select_date": false,
83
+ "rail_atomic_submit_search": false,
84
+ "rail_book_and_pay": false,
85
+ "rail_book_and_pay_2_passengers": false,
86
+ "rail_book_to_cart": false,
87
+ "rail_book_to_cart_2_passengers": false,
88
+ "rail_book_to_cart_business": false
89
+ }
90
+ },
91
+ "hotels": {
92
+ "label": "Отели",
93
+ "success_rate": 0.375,
94
+ "passed": 3,
95
+ "total": 8,
96
+ "tasks": {
97
+ "hotel_atomic_select_city": true,
98
+ "hotel_atomic_select_end_date": false,
99
+ "hotel_atomic_select_guests": true,
100
+ "hotel_atomic_select_start_date": false,
101
+ "hotel_search_scenario": false,
102
+ "hotel_search_scenario_miami": false,
103
+ "hotel_search_scenario_typo": true,
104
+ "hotel_search_scenario_with_hotel": false
105
+ }
106
+ },
107
+ "gov": {
108
+ "label": "Услуги",
109
+ "success_rate": 0.16666666666666666,
110
+ "passed": 1,
111
+ "total": 6,
112
+ "tasks": {
113
+ "government_download_certificate": false,
114
+ "government_download_certificate_techno": true,
115
+ "government_order_certificate": false,
116
+ "government_view_certificate": false,
117
+ "government_view_certificate_2025": false,
118
+ "government_view_certificate_casual": false
119
+ }
120
+ },
121
+ "hub": {
122
+ "label": "Главная",
123
+ "success_rate": 1.0,
124
+ "passed": 4,
125
+ "total": 4,
126
+ "tasks": {
127
+ "bench_hub_navigation_books": true,
128
+ "bench_hub_navigation_gov": true,
129
+ "bench_hub_navigation_rail": true,
130
+ "bench_hub_navigation_shop": true
131
+ }
132
+ }
133
+ },
134
+ "ui_badges": [
135
+ "SELECT_AC",
136
+ "NAV",
137
+ "FAV",
138
+ "BASKET"
139
+ ],
140
+ "ui_classes": {
141
+ "BASKET": {
142
+ "success_rate": 0.5909090909090909,
143
+ "passed": 13,
144
+ "total": 22,
145
+ "failed": 9
146
+ },
147
+ "NAV": {
148
+ "success_rate": 1.0,
149
+ "passed": 4,
150
+ "total": 4,
151
+ "failed": 0
152
+ },
153
+ "FAV": {
154
+ "success_rate": 0.6,
155
+ "passed": 3,
156
+ "total": 5,
157
+ "failed": 2
158
+ },
159
+ "GOV": {
160
+ "success_rate": 0.16666666666666666,
161
+ "passed": 1,
162
+ "total": 6,
163
+ "failed": 5
164
+ },
165
+ "SELECT_AC": {
166
+ "success_rate": 1.0,
167
+ "passed": 2,
168
+ "total": 2,
169
+ "failed": 0
170
+ },
171
+ "DATE": {
172
+ "success_rate": 0.0,
173
+ "passed": 0,
174
+ "total": 4,
175
+ "failed": 4
176
+ },
177
+ "COUNTER": {
178
+ "success_rate": 0.3333333333333333,
179
+ "passed": 1,
180
+ "total": 3,
181
+ "failed": 2
182
+ },
183
+ "CARD": {
184
+ "success_rate": 0.0,
185
+ "passed": 0,
186
+ "total": 1,
187
+ "failed": 1
188
+ },
189
+ "SELECT_LIST": {
190
+ "success_rate": 0.0,
191
+ "passed": 0,
192
+ "total": 2,
193
+ "failed": 2
194
+ },
195
+ "PAY": {
196
+ "success_rate": 0.0,
197
+ "passed": 0,
198
+ "total": 2,
199
+ "failed": 2
200
+ }
201
+ },
202
+ "section_avg_rate": 0.5173338566195709,
203
+ "raw_results_path": "results/raw/google_gemini-2.5-flash__openmanus/results.json",
204
+ "entry_id": "google_gemini-2.5-flash__openmanus"
205
+ }
results/google_gemini-2.5-flash__ouroboros-cut.json ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model": "google/gemini-2.5-flash",
3
+ "harness": "ouroboros-cut",
4
+ "provider": "openrouter",
5
+ "mock": "bench",
6
+ "finished": true,
7
+ "source_path": "/home/jovyan/emelyanov/agent-bench/tests/eval/google_gemini-2.5-flash/ouroboros-cut/2026-06-23_22-23-17__5359c150/results.json",
8
+ "submitted_at": "2026-06-23T23:54:44.480931+00:00",
9
+ "metrics": {
10
+ "success_rate": 0.43137254901960786,
11
+ "passed_tasks": 22,
12
+ "failed_tasks": 29,
13
+ "total_tasks": 51,
14
+ "avg_duration_seconds": 206.01449627326983,
15
+ "avg_agent_steps": 14.235294117647058,
16
+ "avg_tokens_per_task": 183224.0,
17
+ "total_tokens": 9344424,
18
+ "total_cost_usd": 3.4392526,
19
+ "avg_cost_per_task_usd": 0.06743632549019608,
20
+ "pass_at_k": null,
21
+ "agent_completion_rate": 0.8235294117647058,
22
+ "agent_dab_agreement_rate": 0.6078431372549019,
23
+ "section_avg_rate": 0.4696493982208268
24
+ },
25
+ "sections": {
26
+ "shop": {
27
+ "label": "Маркет",
28
+ "success_rate": 0.6923076923076923,
29
+ "passed": 9,
30
+ "total": 13,
31
+ "tasks": {
32
+ "ecommerce_basket_and_favorites": false,
33
+ "ecommerce_basket_any_product": true,
34
+ "ecommerce_basket_multiple": false,
35
+ "ecommerce_basket_named_product": true,
36
+ "ecommerce_basket_named_product_02": true,
37
+ "ecommerce_basket_named_product_03": true,
38
+ "ecommerce_basket_price_10k": false,
39
+ "ecommerce_basket_price_1k": false,
40
+ "ecommerce_basket_price_constraint": true,
41
+ "ecommerce_basket_typo": true,
42
+ "ecommerce_favorites": true,
43
+ "ecommerce_favorites_02": true,
44
+ "ecommerce_favorites_any_product": true
45
+ }
46
+ },
47
+ "books": {
48
+ "label": "Книги",
49
+ "success_rate": 0.42857142857142855,
50
+ "passed": 3,
51
+ "total": 7,
52
+ "tasks": {
53
+ "digital_books_audio_basket": true,
54
+ "digital_books_author_cheapest_basket": true,
55
+ "digital_books_author_cheapest_favorites": false,
56
+ "digital_books_author_expensive_basket": false,
57
+ "digital_books_named_product_basket": false,
58
+ "digital_books_named_product_basket_02": false,
59
+ "digital_books_named_product_favorites": true
60
+ }
61
+ },
62
+ "grocery": {
63
+ "label": "Продукты",
64
+ "success_rate": 0.75,
65
+ "passed": 3,
66
+ "total": 4,
67
+ "tasks": {
68
+ "bench_grocery_navigation": true,
69
+ "grocery_basket_any_product": true,
70
+ "grocery_basket_named_product": true,
71
+ "grocery_basket_named_product_02": false
72
+ }
73
+ },
74
+ "rail": {
75
+ "label": "Поезда",
76
+ "success_rate": 0.0,
77
+ "passed": 0,
78
+ "total": 9,
79
+ "tasks": {
80
+ "rail_atomic_select_city_from": false,
81
+ "rail_atomic_select_city_to": false,
82
+ "rail_atomic_select_date": false,
83
+ "rail_atomic_submit_search": false,
84
+ "rail_book_and_pay": false,
85
+ "rail_book_and_pay_2_passengers": false,
86
+ "rail_book_to_cart": false,
87
+ "rail_book_to_cart_2_passengers": false,
88
+ "rail_book_to_cart_business": false
89
+ }
90
+ },
91
+ "hotels": {
92
+ "label": "Отели",
93
+ "success_rate": 0.25,
94
+ "passed": 2,
95
+ "total": 8,
96
+ "tasks": {
97
+ "hotel_atomic_select_city": true,
98
+ "hotel_atomic_select_end_date": false,
99
+ "hotel_atomic_select_guests": true,
100
+ "hotel_atomic_select_start_date": false,
101
+ "hotel_search_scenario": false,
102
+ "hotel_search_scenario_miami": false,
103
+ "hotel_search_scenario_typo": false,
104
+ "hotel_search_scenario_with_hotel": false
105
+ }
106
+ },
107
+ "gov": {
108
+ "label": "Услуги",
109
+ "success_rate": 0.16666666666666666,
110
+ "passed": 1,
111
+ "total": 6,
112
+ "tasks": {
113
+ "government_download_certificate": false,
114
+ "government_download_certificate_techno": false,
115
+ "government_order_certificate": false,
116
+ "government_view_certificate": true,
117
+ "government_view_certificate_2025": false,
118
+ "government_view_certificate_casual": false
119
+ }
120
+ },
121
+ "hub": {
122
+ "label": "Главная",
123
+ "success_rate": 1.0,
124
+ "passed": 4,
125
+ "total": 4,
126
+ "tasks": {
127
+ "bench_hub_navigation_books": true,
128
+ "bench_hub_navigation_gov": true,
129
+ "bench_hub_navigation_rail": true,
130
+ "bench_hub_navigation_shop": true
131
+ }
132
+ }
133
+ },
134
+ "ui_badges": [
135
+ "NAV",
136
+ "FAV",
137
+ "SELECT_AC",
138
+ "BASKET"
139
+ ],
140
+ "ui_classes": {
141
+ "BASKET": {
142
+ "success_rate": 0.5,
143
+ "passed": 11,
144
+ "total": 22,
145
+ "failed": 11
146
+ },
147
+ "NAV": {
148
+ "success_rate": 1.0,
149
+ "passed": 4,
150
+ "total": 4,
151
+ "failed": 0
152
+ },
153
+ "FAV": {
154
+ "success_rate": 0.8,
155
+ "passed": 4,
156
+ "total": 5,
157
+ "failed": 1
158
+ },
159
+ "GOV": {
160
+ "success_rate": 0.16666666666666666,
161
+ "passed": 1,
162
+ "total": 6,
163
+ "failed": 5
164
+ },
165
+ "SELECT_AC": {
166
+ "success_rate": 0.5,
167
+ "passed": 1,
168
+ "total": 2,
169
+ "failed": 1
170
+ },
171
+ "DATE": {
172
+ "success_rate": 0.0,
173
+ "passed": 0,
174
+ "total": 4,
175
+ "failed": 4
176
+ },
177
+ "COUNTER": {
178
+ "success_rate": 0.3333333333333333,
179
+ "passed": 1,
180
+ "total": 3,
181
+ "failed": 2
182
+ },
183
+ "CARD": {
184
+ "success_rate": 0.0,
185
+ "passed": 0,
186
+ "total": 1,
187
+ "failed": 1
188
+ },
189
+ "SELECT_LIST": {
190
+ "success_rate": 0.0,
191
+ "passed": 0,
192
+ "total": 2,
193
+ "failed": 2
194
+ },
195
+ "PAY": {
196
+ "success_rate": 0.0,
197
+ "passed": 0,
198
+ "total": 2,
199
+ "failed": 2
200
+ }
201
+ },
202
+ "section_avg_rate": 0.4696493982208268,
203
+ "raw_results_path": "results/raw/google_gemini-2.5-flash__ouroboros-cut/results.json",
204
+ "entry_id": "google_gemini-2.5-flash__ouroboros-cut"
205
+ }
results/google_gemini-2.5-flash__ouroboros-full-evolving.json ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model": "google/gemini-2.5-flash",
3
+ "harness": "ouroboros-full-evolving",
4
+ "provider": "openrouter",
5
+ "mock": "bench",
6
+ "finished": true,
7
+ "source_path": "/home/jovyan/emelyanov/agent-bench/tests/eval/google_gemini-2.5-flash/ouroboros-full-evolving/2026-06-23_22-23-55__lm-mpi-job-c08a12ec-da72-4952-ade5-c65163a4ee8e-mpimaster-0-1169-31430-1782253432/results.json",
8
+ "submitted_at": "2026-06-24T00:53:05.022768+00:00",
9
+ "metrics": {
10
+ "success_rate": 0.13725490196078433,
11
+ "passed_tasks": 7,
12
+ "failed_tasks": 44,
13
+ "total_tasks": 51,
14
+ "avg_duration_seconds": 175.00107505621716,
15
+ "avg_agent_steps": 11.352941176470589,
16
+ "avg_tokens_per_task": 727298.4090909091,
17
+ "total_tokens": 32001130,
18
+ "total_cost_usd": 5.1799219999999995,
19
+ "avg_cost_per_task_usd": 0.10156709803921568,
20
+ "pass_at_k": null,
21
+ "agent_completion_rate": 0.6862745098039216,
22
+ "agent_dab_agreement_rate": 0.45098039215686275,
23
+ "section_avg_rate": 0.18524332810047098
24
+ },
25
+ "sections": {
26
+ "shop": {
27
+ "label": "Маркет",
28
+ "success_rate": 0.15384615384615385,
29
+ "passed": 2,
30
+ "total": 13,
31
+ "tasks": {
32
+ "ecommerce_basket_and_favorites": false,
33
+ "ecommerce_basket_any_product": false,
34
+ "ecommerce_basket_multiple": false,
35
+ "ecommerce_basket_named_product": false,
36
+ "ecommerce_basket_named_product_02": true,
37
+ "ecommerce_basket_named_product_03": false,
38
+ "ecommerce_basket_price_10k": false,
39
+ "ecommerce_basket_price_1k": false,
40
+ "ecommerce_basket_price_constraint": false,
41
+ "ecommerce_basket_typo": true,
42
+ "ecommerce_favorites": false,
43
+ "ecommerce_favorites_02": false,
44
+ "ecommerce_favorites_any_product": false
45
+ }
46
+ },
47
+ "books": {
48
+ "label": "Книги",
49
+ "success_rate": 0.14285714285714285,
50
+ "passed": 1,
51
+ "total": 7,
52
+ "tasks": {
53
+ "digital_books_audio_basket": false,
54
+ "digital_books_author_cheapest_basket": false,
55
+ "digital_books_author_cheapest_favorites": false,
56
+ "digital_books_author_expensive_basket": false,
57
+ "digital_books_named_product_basket": true,
58
+ "digital_books_named_product_basket_02": false,
59
+ "digital_books_named_product_favorites": false
60
+ }
61
+ },
62
+ "grocery": {
63
+ "label": "Продукты",
64
+ "success_rate": 0.0,
65
+ "passed": 0,
66
+ "total": 4,
67
+ "tasks": {
68
+ "bench_grocery_navigation": false,
69
+ "grocery_basket_any_product": false,
70
+ "grocery_basket_named_product": false,
71
+ "grocery_basket_named_product_02": false
72
+ }
73
+ },
74
+ "rail": {
75
+ "label": "Поезда",
76
+ "success_rate": 0.0,
77
+ "passed": 0,
78
+ "total": 9,
79
+ "tasks": {
80
+ "rail_atomic_select_city_from": false,
81
+ "rail_atomic_select_city_to": false,
82
+ "rail_atomic_select_date": false,
83
+ "rail_atomic_submit_search": false,
84
+ "rail_book_and_pay": false,
85
+ "rail_book_and_pay_2_passengers": false,
86
+ "rail_book_to_cart": false,
87
+ "rail_book_to_cart_2_passengers": false,
88
+ "rail_book_to_cart_business": false
89
+ }
90
+ },
91
+ "hotels": {
92
+ "label": "Отели",
93
+ "success_rate": 0.0,
94
+ "passed": 0,
95
+ "total": 8,
96
+ "tasks": {
97
+ "hotel_atomic_select_city": false,
98
+ "hotel_atomic_select_end_date": false,
99
+ "hotel_atomic_select_guests": false,
100
+ "hotel_atomic_select_start_date": false,
101
+ "hotel_search_scenario": false,
102
+ "hotel_search_scenario_miami": false,
103
+ "hotel_search_scenario_typo": false,
104
+ "hotel_search_scenario_with_hotel": false
105
+ }
106
+ },
107
+ "gov": {
108
+ "label": "Услуги",
109
+ "success_rate": 0.0,
110
+ "passed": 0,
111
+ "total": 6,
112
+ "tasks": {
113
+ "government_download_certificate": false,
114
+ "government_download_certificate_techno": false,
115
+ "government_order_certificate": false,
116
+ "government_view_certificate": false,
117
+ "government_view_certificate_2025": false,
118
+ "government_view_certificate_casual": false
119
+ }
120
+ },
121
+ "hub": {
122
+ "label": "Главная",
123
+ "success_rate": 1.0,
124
+ "passed": 4,
125
+ "total": 4,
126
+ "tasks": {
127
+ "bench_hub_navigation_books": true,
128
+ "bench_hub_navigation_gov": true,
129
+ "bench_hub_navigation_rail": true,
130
+ "bench_hub_navigation_shop": true
131
+ }
132
+ }
133
+ },
134
+ "ui_badges": [
135
+ "NAV",
136
+ "BASKET",
137
+ "SELECT_LIST",
138
+ "SELECT_AC"
139
+ ],
140
+ "ui_classes": {
141
+ "BASKET": {
142
+ "success_rate": 0.13636363636363635,
143
+ "passed": 3,
144
+ "total": 22,
145
+ "failed": 19
146
+ },
147
+ "NAV": {
148
+ "success_rate": 1.0,
149
+ "passed": 4,
150
+ "total": 4,
151
+ "failed": 0
152
+ },
153
+ "FAV": {
154
+ "success_rate": 0.0,
155
+ "passed": 0,
156
+ "total": 5,
157
+ "failed": 5
158
+ },
159
+ "GOV": {
160
+ "success_rate": 0.0,
161
+ "passed": 0,
162
+ "total": 6,
163
+ "failed": 6
164
+ },
165
+ "SELECT_AC": {
166
+ "success_rate": 0.0,
167
+ "passed": 0,
168
+ "total": 2,
169
+ "failed": 2
170
+ },
171
+ "DATE": {
172
+ "success_rate": 0.0,
173
+ "passed": 0,
174
+ "total": 4,
175
+ "failed": 4
176
+ },
177
+ "COUNTER": {
178
+ "success_rate": 0.0,
179
+ "passed": 0,
180
+ "total": 3,
181
+ "failed": 3
182
+ },
183
+ "CARD": {
184
+ "success_rate": 0.0,
185
+ "passed": 0,
186
+ "total": 1,
187
+ "failed": 1
188
+ },
189
+ "SELECT_LIST": {
190
+ "success_rate": 0.0,
191
+ "passed": 0,
192
+ "total": 2,
193
+ "failed": 2
194
+ },
195
+ "PAY": {
196
+ "success_rate": 0.0,
197
+ "passed": 0,
198
+ "total": 2,
199
+ "failed": 2
200
+ }
201
+ },
202
+ "section_avg_rate": 0.18524332810047098,
203
+ "raw_results_path": "results/raw/google_gemini-2.5-flash__ouroboros-full-evolving/results.json",
204
+ "entry_id": "google_gemini-2.5-flash__ouroboros-full-evolving"
205
+ }
results/google_gemini-2.5-flash__ouroboros-full-isolated.json ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model": "google/gemini-2.5-flash",
3
+ "harness": "ouroboros-full-isolated",
4
+ "provider": "openrouter",
5
+ "mock": "bench",
6
+ "finished": true,
7
+ "source_path": "/home/jovyan/emelyanov/agent-bench/tests/eval/google_gemini-2.5-flash/ouroboros-full-isolated/2026-06-24_11-35-56__lm-mpi-job-b9be367c-3bfc-46dc-b65d-c9e762e3781f-mpimaster-0-462-24504-1782300952/results.json",
8
+ "submitted_at": "2026-06-24T16:02:38.468021+00:00",
9
+ "metrics": {
10
+ "success_rate": 0.09803921568627451,
11
+ "passed_tasks": 5,
12
+ "failed_tasks": 46,
13
+ "total_tasks": 51,
14
+ "avg_duration_seconds": 607.7635685334019,
15
+ "avg_agent_steps": 13.313725490196079,
16
+ "avg_tokens_per_task": 717052.7,
17
+ "total_tokens": 21511581,
18
+ "total_cost_usd": 3.039380000000001,
19
+ "avg_cost_per_task_usd": 0.05959568627450982,
20
+ "pass_at_k": null,
21
+ "agent_completion_rate": 0.5882352941176471,
22
+ "agent_dab_agreement_rate": 0.47058823529411764,
23
+ "section_avg_rate": 0.17857142857142858
24
+ },
25
+ "sections": {
26
+ "shop": {
27
+ "label": "Маркет",
28
+ "success_rate": 0.0,
29
+ "passed": 0,
30
+ "total": 13,
31
+ "tasks": {
32
+ "ecommerce_basket_and_favorites": false,
33
+ "ecommerce_basket_any_product": false,
34
+ "ecommerce_basket_multiple": false,
35
+ "ecommerce_basket_named_product": false,
36
+ "ecommerce_basket_named_product_02": false,
37
+ "ecommerce_basket_named_product_03": false,
38
+ "ecommerce_basket_price_10k": false,
39
+ "ecommerce_basket_price_1k": false,
40
+ "ecommerce_basket_price_constraint": false,
41
+ "ecommerce_basket_typo": false,
42
+ "ecommerce_favorites": false,
43
+ "ecommerce_favorites_02": false,
44
+ "ecommerce_favorites_any_product": false
45
+ }
46
+ },
47
+ "books": {
48
+ "label": "Книги",
49
+ "success_rate": 0.0,
50
+ "passed": 0,
51
+ "total": 7,
52
+ "tasks": {
53
+ "digital_books_audio_basket": false,
54
+ "digital_books_author_cheapest_basket": false,
55
+ "digital_books_author_cheapest_favorites": false,
56
+ "digital_books_author_expensive_basket": false,
57
+ "digital_books_named_product_basket": false,
58
+ "digital_books_named_product_basket_02": false,
59
+ "digital_books_named_product_favorites": false
60
+ }
61
+ },
62
+ "grocery": {
63
+ "label": "Продукты",
64
+ "success_rate": 0.25,
65
+ "passed": 1,
66
+ "total": 4,
67
+ "tasks": {
68
+ "bench_grocery_navigation": false,
69
+ "grocery_basket_any_product": true,
70
+ "grocery_basket_named_product": false,
71
+ "grocery_basket_named_product_02": false
72
+ }
73
+ },
74
+ "rail": {
75
+ "label": "Поезда",
76
+ "success_rate": 0.0,
77
+ "passed": 0,
78
+ "total": 9,
79
+ "tasks": {
80
+ "rail_atomic_select_city_from": false,
81
+ "rail_atomic_select_city_to": false,
82
+ "rail_atomic_select_date": false,
83
+ "rail_atomic_submit_search": false,
84
+ "rail_book_and_pay": false,
85
+ "rail_book_and_pay_2_passengers": false,
86
+ "rail_book_to_cart": false,
87
+ "rail_book_to_cart_2_passengers": false,
88
+ "rail_book_to_cart_business": false
89
+ }
90
+ },
91
+ "hotels": {
92
+ "label": "Отели",
93
+ "success_rate": 0.0,
94
+ "passed": 0,
95
+ "total": 8,
96
+ "tasks": {
97
+ "hotel_atomic_select_city": false,
98
+ "hotel_atomic_select_end_date": false,
99
+ "hotel_atomic_select_guests": false,
100
+ "hotel_atomic_select_start_date": false,
101
+ "hotel_search_scenario": false,
102
+ "hotel_search_scenario_miami": false,
103
+ "hotel_search_scenario_typo": false,
104
+ "hotel_search_scenario_with_hotel": false
105
+ }
106
+ },
107
+ "gov": {
108
+ "label": "Услуги",
109
+ "success_rate": 0.0,
110
+ "passed": 0,
111
+ "total": 6,
112
+ "tasks": {
113
+ "government_download_certificate": false,
114
+ "government_download_certificate_techno": false,
115
+ "government_order_certificate": false,
116
+ "government_view_certificate": false,
117
+ "government_view_certificate_2025": false,
118
+ "government_view_certificate_casual": false
119
+ }
120
+ },
121
+ "hub": {
122
+ "label": "Главная",
123
+ "success_rate": 1.0,
124
+ "passed": 4,
125
+ "total": 4,
126
+ "tasks": {
127
+ "bench_hub_navigation_books": true,
128
+ "bench_hub_navigation_gov": true,
129
+ "bench_hub_navigation_rail": true,
130
+ "bench_hub_navigation_shop": true
131
+ }
132
+ }
133
+ },
134
+ "ui_badges": [
135
+ "NAV",
136
+ "BASKET",
137
+ "SELECT_LIST",
138
+ "SELECT_AC"
139
+ ],
140
+ "ui_classes": {
141
+ "BASKET": {
142
+ "success_rate": 0.045454545454545456,
143
+ "passed": 1,
144
+ "total": 22,
145
+ "failed": 21
146
+ },
147
+ "NAV": {
148
+ "success_rate": 1.0,
149
+ "passed": 4,
150
+ "total": 4,
151
+ "failed": 0
152
+ },
153
+ "FAV": {
154
+ "success_rate": 0.0,
155
+ "passed": 0,
156
+ "total": 5,
157
+ "failed": 5
158
+ },
159
+ "GOV": {
160
+ "success_rate": 0.0,
161
+ "passed": 0,
162
+ "total": 6,
163
+ "failed": 6
164
+ },
165
+ "SELECT_AC": {
166
+ "success_rate": 0.0,
167
+ "passed": 0,
168
+ "total": 2,
169
+ "failed": 2
170
+ },
171
+ "DATE": {
172
+ "success_rate": 0.0,
173
+ "passed": 0,
174
+ "total": 4,
175
+ "failed": 4
176
+ },
177
+ "COUNTER": {
178
+ "success_rate": 0.0,
179
+ "passed": 0,
180
+ "total": 3,
181
+ "failed": 3
182
+ },
183
+ "CARD": {
184
+ "success_rate": 0.0,
185
+ "passed": 0,
186
+ "total": 1,
187
+ "failed": 1
188
+ },
189
+ "SELECT_LIST": {
190
+ "success_rate": 0.0,
191
+ "passed": 0,
192
+ "total": 2,
193
+ "failed": 2
194
+ },
195
+ "PAY": {
196
+ "success_rate": 0.0,
197
+ "passed": 0,
198
+ "total": 2,
199
+ "failed": 2
200
+ }
201
+ },
202
+ "section_avg_rate": 0.17857142857142858,
203
+ "raw_results_path": "results/raw/google_gemini-2.5-flash__ouroboros-full-isolated/results.json",
204
+ "entry_id": "google_gemini-2.5-flash__ouroboros-full-isolated"
205
+ }
results/raw/deepseek_deepseek-v4-flash__browser-use/results.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ad648b18592c0967f0d20be1c458588472f52f9cbf1be7b6c13c8de692b62931
3
+ size 15125158
results/raw/deepseek_deepseek-v4-flash__openhands/results.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:97926f7ef63e7d5243a25ea85954ac905d56a45a3757a3fea73bfb686b5ceee1
3
+ size 1039593
results/raw/deepseek_deepseek-v4-flash__openmanus/results.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:db173307fac9c66172d7975ca2aabbf9a68029cec87e5a3ce77ad6931ba37722
3
+ size 14604809
results/raw/deepseek_deepseek-v4-flash__ouroboros-cut/results.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a4926b0169e027f5326c0df10c0eb7ceebe8a6256e577b7bcb8454403846c4af
3
+ size 14613326
results/raw/deepseek_deepseek-v4-flash__ouroboros-full-evolving/results.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:739ea1997acc50bd12448ec22acf220cded58afd8a7c3f3b21546ce6050ec95f
3
+ size 810416
results/raw/deepseek_deepseek-v4-flash__ouroboros-full-isolated/results.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d6d0fcc67278d52cfa73704b18a96e38bfe82c1410d56e6fbae107315983276a
3
+ size 1070384
results/raw/google_gemini-2.5-flash__browser-use/results.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2b65d8973d0217faf5f988f4ebbf789fa83b2ac4ff50f64549b58f11d8fa7adb
3
+ size 15555946
results/raw/google_gemini-2.5-flash__openhands/results.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:556cc1d2a5eb86867aaa1ed5c76169a6308e3f9a69f93460f3d8a84dea55d709
3
+ size 829166
results/raw/google_gemini-2.5-flash__openmanus/results.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:821e31b5c78216797d8e821e974c19bca72f18c11d432f28b1d79190dfe82c9a
3
+ size 14381161
results/raw/google_gemini-2.5-flash__ouroboros-cut/results.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e5cb7fb93f9992dc4ed79226d707627bb12e9704ddbe7355f72c3c98f8e8faaa
3
+ size 16236181
results/raw/google_gemini-2.5-flash__ouroboros-full-evolving/results.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0bfed04d3567844b17e98cb2872bf1f97385cbd38dfd7feab991bf1266408610
3
+ size 684024
results/raw/google_gemini-2.5-flash__ouroboros-full-isolated/results.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0d4e1b519827d61dc194cbad7e250d49e5cc08bd259ca6076eaa2f8b7b33b618
3
+ size 869193
src/__init__.py ADDED
File without changes
src/bench_config.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Load Web Agent Bench leaderboard configuration."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from functools import lru_cache
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+
11
+ def config_path() -> Path:
12
+ return Path(__file__).resolve().parent.parent / "bench_config.json"
13
+
14
+
15
+ @lru_cache(maxsize=1)
16
+ def load_bench_config() -> dict[str, Any]:
17
+ return json.loads(config_path().read_text(encoding="utf-8"))
18
+
19
+
20
+ def section_order() -> list[str]:
21
+ config = load_bench_config()
22
+ core = [key for key, meta in config["sections"].items() if meta.get("core")]
23
+ rest = [key for key in config["sections"] if key not in core]
24
+ return core + rest
25
+
26
+
27
+ def section_label(section_id: str) -> str:
28
+ return load_bench_config()["sections"][section_id]["label"]
29
+
30
+
31
+ def section_description(section_id: str) -> str:
32
+ return load_bench_config()["sections"][section_id].get("description") or ""
33
+
34
+
35
+ def taxonomy_order() -> list[str]:
36
+ return list(load_bench_config().get("ui_classes", {}).keys())
37
+
38
+
39
+ def taxonomy_label(class_id: str) -> str:
40
+ meta = load_bench_config().get("ui_classes", {}).get(class_id) or {}
41
+ return str(meta.get("label") or class_id)
42
+
43
+
44
+ def taxonomy_description(class_id: str) -> str:
45
+ meta = load_bench_config().get("ui_classes", {}).get(class_id) or {}
46
+ return str(meta.get("description") or "")
47
+
48
+
49
+ def task_count() -> int:
50
+ config = load_bench_config()
51
+ return int(config["benchmark"].get("task_count") or sum(
52
+ len(meta["tasks"]) for meta in config["sections"].values()
53
+ ))
src/display.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Display helpers for Gradio tables."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import html
6
+
7
+ import pandas as pd
8
+
9
+
10
+ def display_dataframe(df: pd.DataFrame | None) -> pd.DataFrame:
11
+ if df is None or df.empty:
12
+ return pd.DataFrame([{"—": "No data"}])
13
+ out = df.copy().astype(object)
14
+ return out.where(pd.notnull(out), "—")
15
+
16
+
17
+ def _cell_text(value: object) -> str:
18
+ if value is None or (isinstance(value, float) and pd.isna(value)):
19
+ return "—"
20
+ if pd.isna(value):
21
+ return "—"
22
+ return str(value)
23
+
24
+
25
+ def dataframe_to_html(df: pd.DataFrame | None, *, empty_note: str = "No data") -> str:
26
+ if df is None or df.empty:
27
+ return f'<p class="wab-detail-note">{html.escape(empty_note)}</p>'
28
+
29
+ headers = "".join(f"<th>{html.escape(str(col))}</th>" for col in df.columns)
30
+ body_rows = []
31
+ for row in df.itertuples(index=False, name=None):
32
+ cells = []
33
+ for col, value in zip(df.columns, row, strict=True):
34
+ text = _cell_text(value)
35
+ css_class = ""
36
+ if col in {"Success %", "Success"} and text not in {"—", "✓", "✗"}:
37
+ css_class = ' class="wab-metric-value"'
38
+ elif col in {"Tasks", "Metric", "Harness", "Model", "Class", "Task"}:
39
+ css_class = ' class="wab-mono-cell"'
40
+ cells.append(f"<td{css_class}>{html.escape(text)}</td>")
41
+ body_rows.append(f"<tr>{''.join(cells)}</tr>")
42
+ rows = "".join(body_rows)
43
+ return (
44
+ '<div class="wab-table-wrap wab-detail-table-wrap">'
45
+ f'<table class="wab-table wab-detail-table"><thead><tr>{headers}</tr></thead>'
46
+ f"<tbody>{rows}</tbody></table></div>"
47
+ )
src/export_results.py ADDED
@@ -0,0 +1,366 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Export latest eval results.json into liderboard/results/ (leaderboard feed)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import os
8
+ import re
9
+ import shutil
10
+ import sys
11
+ from collections import defaultdict
12
+ from pathlib import Path
13
+ from typing import Any
14
+
15
+ from src.bench_config import load_bench_config
16
+ from src.models import LeaderboardEntry, SectionStats, reconcile_success_metrics
17
+
18
+
19
+ def _sanitize_name(value: str) -> str:
20
+ safe = re.sub(r"[^\w.\-]+", "_", value.strip())
21
+ return safe or "unknown"
22
+
23
+
24
+ def _space_root() -> Path:
25
+ return Path(__file__).resolve().parent.parent
26
+
27
+
28
+ def _repo_root() -> Path:
29
+ return _space_root().parent
30
+
31
+
32
+ def _ensure_repo_on_path() -> Path:
33
+ root = _repo_root()
34
+ root_str = str(root)
35
+ if root_str not in sys.path:
36
+ sys.path.insert(0, root_str)
37
+ return root
38
+
39
+
40
+ def _domain_for_test(test: dict[str, Any], config: dict[str, Any]) -> str | None:
41
+ ui = test.get("ui_taxonomy") or {}
42
+ domain = ui.get("domain")
43
+ if domain:
44
+ return str(domain)
45
+ patterns = ui.get("ui_patterns") or {}
46
+ if patterns.get("domain"):
47
+ return str(patterns["domain"])
48
+ name = test.get("test_name") or ""
49
+ for section_id, meta in config["sections"].items():
50
+ for task in meta["tasks"]:
51
+ if task == name:
52
+ return section_id
53
+ if name.startswith("bench_hub"):
54
+ return "hub"
55
+ if name.startswith("digital_books"):
56
+ return "books"
57
+ if name.startswith("grocery") or name.startswith("bench_grocery"):
58
+ return "grocery"
59
+ if name.startswith("hotel"):
60
+ return "hotels"
61
+ if name.startswith("government"):
62
+ return "gov"
63
+ if name.startswith("rail"):
64
+ return "rail"
65
+ if name.startswith("ecommerce"):
66
+ return "shop"
67
+ return None
68
+
69
+
70
+ def _section_avg_rate(sections: dict[str, SectionStats]) -> float | None:
71
+ rates = [stats.success_rate for stats in sections.values() if stats.success_rate is not None]
72
+ return (sum(rates) / len(rates)) if rates else None
73
+
74
+
75
+ def raw_results_path_for(entry: LeaderboardEntry, root: Path | None = None) -> Path:
76
+ base = (root or _space_root()) / "results" / "raw"
77
+ return base / entry.entry_id / "results.json"
78
+
79
+
80
+ def _ui_classes(payload: dict[str, Any]) -> dict[str, dict[str, Any]]:
81
+ stats = payload.get("ui_taxonomy_stats") or {}
82
+ by_primary = stats.get("by_primary") or {}
83
+ classes: dict[str, dict[str, Any]] = {}
84
+ for class_id, row in by_primary.items():
85
+ if not isinstance(row, dict):
86
+ continue
87
+ classes[str(class_id)] = {
88
+ "success_rate": row.get("success_rate"),
89
+ "passed": row.get("passed"),
90
+ "total": row.get("total"),
91
+ "failed": row.get("failed"),
92
+ }
93
+ return classes
94
+
95
+
96
+ ui_classes_from_payload = _ui_classes
97
+
98
+
99
+ def _ui_badges(payload: dict[str, Any], limit: int = 4) -> list[str]:
100
+ stats = payload.get("ui_taxonomy_stats") or {}
101
+ by_primary = stats.get("by_primary") or {}
102
+ ranked: list[tuple[float, str]] = []
103
+ for class_id, row in by_primary.items():
104
+ if not isinstance(row, dict):
105
+ continue
106
+ rate = row.get("success_rate")
107
+ if isinstance(rate, (int, float)):
108
+ ranked.append((float(rate), str(class_id)))
109
+ ranked.sort(reverse=True)
110
+ return [class_id for _, class_id in ranked[:limit]]
111
+
112
+
113
+ def _cost_from_payload(payload: dict[str, Any]) -> tuple[float | None, float | None]:
114
+ _ensure_repo_on_path()
115
+ try:
116
+ from bench_eval.llm_cost import compute_results_cost
117
+
118
+ cost = compute_results_cost(payload)
119
+ return cost.total_cost_usd, cost.avg_cost_per_task_usd
120
+ except Exception:
121
+ return None, None
122
+
123
+
124
+ def export_entry(payload: dict[str, Any], *, source_path: str | None = None) -> LeaderboardEntry:
125
+ config = load_bench_config()
126
+ run = payload.get("run") or {}
127
+ tests = payload.get("tests") or []
128
+
129
+ by_section: dict[str, dict[str, list[bool]]] = defaultdict(lambda: defaultdict(list))
130
+ for test in tests:
131
+ section_id = _domain_for_test(test, config)
132
+ if not section_id:
133
+ continue
134
+ task = test.get("test_name") or "unknown"
135
+ by_section[section_id][task].append(bool(test.get("success")))
136
+
137
+ sections: dict[str, SectionStats] = {}
138
+ for section_id, meta in config["sections"].items():
139
+ task_map = by_section.get(section_id, {})
140
+ tasks: dict[str, bool] = {}
141
+ for task in meta["tasks"]:
142
+ values = task_map.get(task)
143
+ if values:
144
+ tasks[task] = sum(values) / len(values) >= 0.5
145
+ passed = sum(1 for ok in tasks.values() if ok)
146
+ total = len(tasks)
147
+ success_rate = (passed / total) if total else None
148
+ sections[section_id] = SectionStats(
149
+ section_id=section_id,
150
+ label=meta["label"],
151
+ success_rate=success_rate,
152
+ passed=passed,
153
+ total=total,
154
+ tasks=tasks,
155
+ )
156
+
157
+ extended = run.get("extended_metrics") if isinstance(run.get("extended_metrics"), dict) else {}
158
+ pass_at_k = extended.get("pass_at_k")
159
+ pass_overall = pass_at_k.get("overall") if isinstance(pass_at_k, dict) else None
160
+ total_cost, avg_cost = _cost_from_payload(payload)
161
+
162
+ entry = LeaderboardEntry(
163
+ model=str(run.get("model") or "unknown"),
164
+ harness=str(run.get("agent_harness") or run.get("harness") or "unknown"),
165
+ provider=run.get("provider"),
166
+ mock=run.get("mock"),
167
+ finished=bool(run.get("finished", True)),
168
+ source_path=source_path,
169
+ submitted_at=run.get("finished_at") or run.get("started_at"),
170
+ success_rate=run.get("success_rate"),
171
+ passed_tasks=int(run.get("passed_tasks") or 0),
172
+ failed_tasks=int(run.get("failed_tasks") or 0),
173
+ total_tasks=int(run.get("total_tasks") or 0),
174
+ avg_duration_seconds=run.get("avg_duration_seconds"),
175
+ avg_agent_steps=run.get("avg_agent_steps"),
176
+ avg_tokens_per_task=run.get("avg_tokens_per_task"),
177
+ total_tokens=run.get("total_tokens"),
178
+ total_cost_usd=total_cost,
179
+ avg_cost_per_task_usd=avg_cost,
180
+ pass_at_k=float(pass_overall) if isinstance(pass_overall, (int, float)) else None,
181
+ agent_completion_rate=extended.get("agent_completion_rate"),
182
+ agent_dab_agreement_rate=extended.get("agent_dab_agreement_rate"),
183
+ sections=sections,
184
+ ui_badges=_ui_badges(payload),
185
+ ui_classes=_ui_classes(payload),
186
+ section_avg_rate=_section_avg_rate(sections),
187
+ )
188
+ reconcile_success_metrics(entry)
189
+ return entry
190
+
191
+
192
+ def default_output_path(entry: LeaderboardEntry, output_dir: Path) -> Path:
193
+ model = _sanitize_name(entry.model)
194
+ harness = _sanitize_name(entry.harness)
195
+ return output_dir / f"{model}__{harness}.json"
196
+
197
+
198
+ def clean_results_dir(output_dir: Path) -> None:
199
+ output_dir.mkdir(parents=True, exist_ok=True)
200
+ for child in output_dir.iterdir():
201
+ if child.name == ".gitkeep":
202
+ continue
203
+ if child.is_dir():
204
+ shutil.rmtree(child)
205
+ else:
206
+ child.unlink()
207
+
208
+
209
+ def export_results_file(
210
+ results_json: Path,
211
+ *,
212
+ output_dir: Path,
213
+ ) -> Path:
214
+ payload = json.loads(results_json.read_text(encoding="utf-8"))
215
+ if not payload.get("run"):
216
+ raise ValueError(f"Not an eval results.json (missing run block): {results_json}")
217
+ entry = export_entry(payload, source_path=str(results_json.resolve()))
218
+ raw_path = raw_results_path_for(entry, output_dir.parent)
219
+ raw_path.parent.mkdir(parents=True, exist_ok=True)
220
+ shutil.copy2(results_json, raw_path)
221
+ entry.raw_results_path = str(raw_path.relative_to(output_dir.parent))
222
+ target = default_output_path(entry, output_dir)
223
+ target.parent.mkdir(parents=True, exist_ok=True)
224
+ target.write_text(json.dumps(entry.to_json(), ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
225
+ print(f"Wrote {target} ({results_json})")
226
+ return target
227
+
228
+
229
+ def export_latest_runs(
230
+ base_dir: Path,
231
+ *,
232
+ output_dir: Path,
233
+ require_finished: bool = False,
234
+ min_total_tasks: int | None = None,
235
+ ) -> list[Path]:
236
+ _ensure_repo_on_path()
237
+ from bench_eval.aggregate_report import discover_latest_runs
238
+
239
+ discovered = discover_latest_runs(
240
+ base_dir,
241
+ require_finished=require_finished,
242
+ min_total_tasks=min_total_tasks,
243
+ )
244
+ clean_results_dir(output_dir)
245
+ written: list[Path] = []
246
+ for item in discovered:
247
+ written.append(export_results_file(item.results_path, output_dir=output_dir))
248
+ return written
249
+
250
+
251
+ def backfill_summary_ui_classes(results_dir: Path | None = None) -> int:
252
+ """Write ui_classes into summary JSON from raw results when missing."""
253
+ root = results_dir or (_space_root() / "results")
254
+ if not root.is_dir():
255
+ return 0
256
+ updated = 0
257
+ for path in sorted(root.glob("*.json")):
258
+ payload = json.loads(path.read_text(encoding="utf-8"))
259
+ if payload.get("ui_classes"):
260
+ continue
261
+ if "metrics" not in payload:
262
+ continue
263
+ entry = LeaderboardEntry.from_json(payload)
264
+ raw_path = raw_results_path_for(entry, root.parent)
265
+ if not raw_path.is_file() and entry.raw_results_path:
266
+ raw_path = root.parent / entry.raw_results_path
267
+ if not raw_path.is_file():
268
+ continue
269
+ raw = json.loads(raw_path.read_text(encoding="utf-8"))
270
+ classes = _ui_classes(raw)
271
+ if not classes:
272
+ continue
273
+ entry.ui_classes = classes
274
+ path.write_text(json.dumps(entry.to_json(), ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
275
+ updated += 1
276
+ return updated
277
+
278
+
279
+ def main() -> None:
280
+ _ensure_repo_on_path()
281
+ from bench_eval.aggregate_report import FULL_BENCH_MIN_TASKS, discover_latest_runs
282
+
283
+ parser = argparse.ArgumentParser(
284
+ description=(
285
+ "Export latest eval results.json for every harness × model "
286
+ "into liderboard/results/ (summary + copied raw results.json)"
287
+ ),
288
+ )
289
+ parser.add_argument(
290
+ "--base-dir",
291
+ default=os.getenv("EVAL_RESULTS_BASE_DIR", "tests/eval"),
292
+ help="Base directory with eval runs (default: tests/eval)",
293
+ )
294
+ parser.add_argument(
295
+ "--output-dir",
296
+ default=None,
297
+ help="Leaderboard results directory (default: liderboard/results)",
298
+ )
299
+ parser.add_argument(
300
+ "--require-finished",
301
+ action="store_true",
302
+ help="Skip combinations whose latest run has finished=false",
303
+ )
304
+ parser.add_argument(
305
+ "--min-tasks-for-aggregate",
306
+ type=int,
307
+ default=FULL_BENCH_MIN_TASKS,
308
+ metavar="N",
309
+ help=(
310
+ "Include only runs with at least N tasks "
311
+ f"(default: {FULL_BENCH_MIN_TASKS}, i.e. full bench; smoke runs excluded). "
312
+ "Use 0 to include all runs."
313
+ ),
314
+ )
315
+ parser.add_argument(
316
+ "--backfill-ui-classes",
317
+ action="store_true",
318
+ help="Add ui_classes to existing summary JSON from copied raw results.json",
319
+ )
320
+ parser.add_argument(
321
+ "--list",
322
+ action="store_true",
323
+ help="Print discovered harness × model paths and exit without writing results",
324
+ )
325
+ args = parser.parse_args()
326
+
327
+ base_dir = Path(args.base_dir)
328
+ if not base_dir.is_absolute():
329
+ base_dir = _repo_root() / base_dir
330
+ min_total_tasks = args.min_tasks_for_aggregate or None
331
+ output_dir = Path(args.output_dir) if args.output_dir else _space_root() / "results"
332
+
333
+ if args.backfill_ui_classes:
334
+ count = backfill_summary_ui_classes(output_dir)
335
+ print(f"Backfilled ui_classes in {count} summary file(s) under {output_dir}")
336
+ return
337
+
338
+ if args.list:
339
+ discovered = discover_latest_runs(
340
+ base_dir,
341
+ require_finished=args.require_finished,
342
+ min_total_tasks=min_total_tasks,
343
+ )
344
+ if not discovered:
345
+ print(f"No results under {base_dir}")
346
+ raise SystemExit(1)
347
+ for item in sorted(discovered, key=lambda row: (row.harness, row.model_dir)):
348
+ status = "finished" if item.finished else "unfinished"
349
+ print(f"{item.harness}\t{item.model}\t{status}\t{item.results_path}")
350
+ return
351
+
352
+ written = export_latest_runs(
353
+ base_dir,
354
+ output_dir=output_dir,
355
+ require_finished=args.require_finished,
356
+ min_total_tasks=min_total_tasks,
357
+ )
358
+ if not written:
359
+ clean_results_dir(output_dir)
360
+ print(f"No runs exported from {base_dir} (min_tasks={min_total_tasks})")
361
+ raise SystemExit(1)
362
+ print(f"Exported {len(written)} harness × model combination(s) to {output_dir}")
363
+
364
+
365
+ if __name__ == "__main__":
366
+ main()
src/load_entries.py ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Load leaderboard entries from liderboard/results."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from src.bench_config import load_bench_config
10
+ from src.export_results import raw_results_path_for, ui_classes_from_payload
11
+ from src.models import LeaderboardEntry
12
+
13
+
14
+ def space_root(root: Path | None = None) -> Path:
15
+ return root or Path(__file__).resolve().parent.parent
16
+
17
+
18
+ def results_dir(root: Path | None = None) -> Path:
19
+ return space_root(root) / "results"
20
+
21
+
22
+ def load_entry_file(path: Path) -> LeaderboardEntry | None:
23
+ payload = json.loads(path.read_text(encoding="utf-8"))
24
+ if "metrics" not in payload:
25
+ return None
26
+ entry = LeaderboardEntry.from_json(payload)
27
+ if not entry.raw_results_path:
28
+ raw_path = raw_results_path_for(entry, space_root(path.parent.parent))
29
+ if raw_path.is_file():
30
+ entry.raw_results_path = str(raw_path.relative_to(space_root(path.parent.parent)))
31
+ if not entry.ui_classes:
32
+ raw = load_raw_results(entry, space_root(path.parent.parent))
33
+ if raw:
34
+ entry.ui_classes = ui_classes_from_payload(raw)
35
+ return entry
36
+
37
+
38
+ def load_entries(results_path: Path | None = None) -> list[LeaderboardEntry]:
39
+ root = results_path or results_dir()
40
+ if not root.is_dir():
41
+ return []
42
+ entries: list[LeaderboardEntry] = []
43
+ for path in sorted(root.glob("*.json")):
44
+ entry = load_entry_file(path)
45
+ if entry is not None:
46
+ entries.append(entry)
47
+ return sorted(
48
+ entries,
49
+ key=lambda entry: entry.success_rate if entry.success_rate is not None else -1,
50
+ reverse=True,
51
+ )
52
+
53
+
54
+ def entry_label(entry: LeaderboardEntry) -> str:
55
+ return f"{entry.model} · {entry.harness}"
56
+
57
+
58
+ def entry_choices(entries: list[LeaderboardEntry]) -> list[tuple[str, str]]:
59
+ return [(entry_label(entry), entry.entry_id) for entry in entries]
60
+
61
+
62
+ def entry_by_id(entries: list[LeaderboardEntry], entry_id: str) -> LeaderboardEntry | None:
63
+ for entry in entries:
64
+ if entry.entry_id == entry_id:
65
+ return entry
66
+ return None
67
+
68
+
69
+ def load_raw_results(entry: LeaderboardEntry, root: Path | None = None) -> dict[str, Any] | None:
70
+ base = space_root(root)
71
+ if entry.raw_results_path:
72
+ raw_path = base / entry.raw_results_path
73
+ if raw_path.is_file():
74
+ return json.loads(raw_path.read_text(encoding="utf-8"))
75
+ fallback = raw_results_path_for(entry, base)
76
+ if fallback.is_file():
77
+ return json.loads(fallback.read_text(encoding="utf-8"))
78
+ return None
79
+
80
+
81
+ def sort_entries(entries: list[LeaderboardEntry], view_id: str) -> list[LeaderboardEntry]:
82
+ config = load_bench_config()
83
+ view = next((row for row in config["views"] if row["id"] == view_id), config["views"][0])
84
+ key = view["sort_key"]
85
+ reverse = not view.get("ascending", False)
86
+
87
+ def sort_value(entry: LeaderboardEntry) -> float:
88
+ value = entry.metric(key)
89
+ if value is None:
90
+ return float("inf") if view.get("ascending") else -1.0
91
+ return float(value)
92
+
93
+ return sorted(entries, key=sort_value, reverse=reverse)
src/models.py ADDED
@@ -0,0 +1,252 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Leaderboard entry schema for Web Agent Bench."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from dataclasses import dataclass, field
7
+ from typing import Any
8
+
9
+
10
+ def entry_id_for(model: str, harness: str) -> str:
11
+ safe = re.sub(r"[^\w.\-]+", "_", f"{model}__{harness}".strip())
12
+ return safe or "unknown"
13
+
14
+
15
+ def reconcile_success_metrics(entry: LeaderboardEntry) -> None:
16
+ """Primary success rate is always passed_tasks / total_tasks (DAB)."""
17
+ section_passed = sum(section.passed for section in entry.sections.values())
18
+ section_total = sum(section.total for section in entry.sections.values())
19
+ if section_total > 0 and entry.total_tasks <= 0:
20
+ entry.total_tasks = section_total
21
+ entry.passed_tasks = section_passed
22
+ entry.failed_tasks = section_total - section_passed
23
+ if entry.total_tasks > 0:
24
+ entry.success_rate = entry.passed_tasks / entry.total_tasks
25
+ entry.failed_tasks = entry.total_tasks - entry.passed_tasks
26
+
27
+
28
+ @dataclass
29
+ class SectionStats:
30
+ section_id: str
31
+ label: str
32
+ success_rate: float | None
33
+ passed: int
34
+ total: int
35
+ tasks: dict[str, bool] = field(default_factory=dict)
36
+
37
+
38
+ @dataclass
39
+ class LeaderboardEntry:
40
+ model: str
41
+ harness: str
42
+ provider: str | None = None
43
+ mock: str | None = None
44
+ finished: bool = True
45
+ source_path: str | None = None
46
+ submitted_at: str | None = None
47
+
48
+ success_rate: float | None = None
49
+ passed_tasks: int = 0
50
+ failed_tasks: int = 0
51
+ total_tasks: int = 0
52
+
53
+ avg_duration_seconds: float | None = None
54
+ avg_agent_steps: float | None = None
55
+ avg_tokens_per_task: float | None = None
56
+ total_tokens: int | None = None
57
+ total_cost_usd: float | None = None
58
+ avg_cost_per_task_usd: float | None = None
59
+
60
+ pass_at_k: float | None = None
61
+ agent_completion_rate: float | None = None
62
+ agent_dab_agreement_rate: float | None = None
63
+
64
+ sections: dict[str, SectionStats] = field(default_factory=dict)
65
+ ui_badges: list[str] = field(default_factory=list)
66
+ ui_classes: dict[str, dict[str, Any]] = field(default_factory=dict)
67
+ section_avg_rate: float | None = None
68
+ raw_results_path: str | None = None
69
+
70
+ @property
71
+ def entry_id(self) -> str:
72
+ return entry_id_for(self.model, self.harness)
73
+
74
+ @property
75
+ def success_pct(self) -> float | None:
76
+ if self.success_rate is None:
77
+ return None
78
+ return round(self.success_rate * 100, 1)
79
+
80
+ @property
81
+ def full_bench_success_rate(self) -> float | None:
82
+ if self.total_tasks >= 51:
83
+ return self.success_rate
84
+ return None
85
+
86
+ @property
87
+ def value_score(self) -> float | None:
88
+ if self.success_rate is None or not self.total_cost_usd or self.total_cost_usd <= 0:
89
+ return None
90
+ return self.success_rate / self.total_cost_usd
91
+
92
+ def metric(self, name: str) -> float | None:
93
+ if name.startswith("domain:"):
94
+ section_id = name.split(":", 1)[1]
95
+ section = self.sections.get(section_id)
96
+ return section.success_rate if section else None
97
+ if name.startswith("taxonomy:"):
98
+ class_id = name.split(":", 1)[1]
99
+ row = self.ui_classes.get(class_id)
100
+ if not row:
101
+ return None
102
+ rate = row.get("success_rate")
103
+ return float(rate) if isinstance(rate, (int, float)) else None
104
+ if name == "success_rate":
105
+ return self.success_rate
106
+ if name == "full_bench_success_rate":
107
+ return self.full_bench_success_rate
108
+ if name == "avg_duration_seconds":
109
+ return self.avg_duration_seconds
110
+ if name == "total_cost_usd":
111
+ return self.total_cost_usd
112
+ if name == "value_score":
113
+ return self.value_score
114
+ return getattr(self, name, None)
115
+
116
+ def to_json(self) -> dict[str, Any]:
117
+ return {
118
+ "model": self.model,
119
+ "harness": self.harness,
120
+ "provider": self.provider,
121
+ "mock": self.mock,
122
+ "finished": self.finished,
123
+ "source_path": self.source_path,
124
+ "submitted_at": self.submitted_at,
125
+ "metrics": {
126
+ "success_rate": self.success_rate,
127
+ "passed_tasks": self.passed_tasks,
128
+ "failed_tasks": self.failed_tasks,
129
+ "total_tasks": self.total_tasks,
130
+ "avg_duration_seconds": self.avg_duration_seconds,
131
+ "avg_agent_steps": self.avg_agent_steps,
132
+ "avg_tokens_per_task": self.avg_tokens_per_task,
133
+ "total_tokens": self.total_tokens,
134
+ "total_cost_usd": self.total_cost_usd,
135
+ "avg_cost_per_task_usd": self.avg_cost_per_task_usd,
136
+ "pass_at_k": self.pass_at_k,
137
+ "agent_completion_rate": self.agent_completion_rate,
138
+ "agent_dab_agreement_rate": self.agent_dab_agreement_rate,
139
+ "section_avg_rate": self.section_avg_rate,
140
+ },
141
+ "sections": {
142
+ section_id: {
143
+ "label": stats.label,
144
+ "success_rate": stats.success_rate,
145
+ "passed": stats.passed,
146
+ "total": stats.total,
147
+ "tasks": stats.tasks,
148
+ }
149
+ for section_id, stats in self.sections.items()
150
+ },
151
+ "ui_badges": self.ui_badges,
152
+ "ui_classes": self.ui_classes,
153
+ "section_avg_rate": self.section_avg_rate,
154
+ "raw_results_path": self.raw_results_path,
155
+ "entry_id": self.entry_id,
156
+ }
157
+
158
+ @classmethod
159
+ def from_json(cls, payload: dict[str, Any]) -> LeaderboardEntry:
160
+ if "metrics" in payload:
161
+ metrics = payload.get("metrics") or {}
162
+ sections_raw = payload.get("sections") or {}
163
+ sections = {
164
+ section_id: SectionStats(
165
+ section_id=section_id,
166
+ label=(data.get("label") or section_id),
167
+ success_rate=data.get("success_rate"),
168
+ passed=int(data.get("passed") or 0),
169
+ total=int(data.get("total") or 0),
170
+ tasks={k: bool(v) for k, v in (data.get("tasks") or {}).items()},
171
+ )
172
+ for section_id, data in sections_raw.items()
173
+ }
174
+ entry = cls(
175
+ model=str(payload.get("model") or "unknown"),
176
+ harness=str(payload.get("harness") or "unknown"),
177
+ provider=payload.get("provider"),
178
+ mock=payload.get("mock"),
179
+ finished=bool(payload.get("finished", True)),
180
+ source_path=payload.get("source_path"),
181
+ submitted_at=payload.get("submitted_at"),
182
+ success_rate=metrics.get("success_rate"),
183
+ passed_tasks=int(metrics.get("passed_tasks") or 0),
184
+ failed_tasks=int(metrics.get("failed_tasks") or 0),
185
+ total_tasks=int(metrics.get("total_tasks") or 0),
186
+ avg_duration_seconds=metrics.get("avg_duration_seconds"),
187
+ avg_agent_steps=metrics.get("avg_agent_steps"),
188
+ avg_tokens_per_task=metrics.get("avg_tokens_per_task"),
189
+ total_tokens=metrics.get("total_tokens"),
190
+ total_cost_usd=metrics.get("total_cost_usd"),
191
+ avg_cost_per_task_usd=metrics.get("avg_cost_per_task_usd"),
192
+ pass_at_k=metrics.get("pass_at_k"),
193
+ agent_completion_rate=metrics.get("agent_completion_rate"),
194
+ agent_dab_agreement_rate=metrics.get("agent_dab_agreement_rate"),
195
+ sections=sections,
196
+ ui_badges=list(payload.get("ui_badges") or []),
197
+ ui_classes=dict(payload.get("ui_classes") or {}),
198
+ section_avg_rate=metrics.get("section_avg_rate") or payload.get("section_avg_rate"),
199
+ raw_results_path=payload.get("raw_results_path"),
200
+ )
201
+ reconcile_success_metrics(entry)
202
+ return entry
203
+ entry = from_legacy_payload(payload)
204
+ reconcile_success_metrics(entry)
205
+ return entry
206
+
207
+
208
+ def from_legacy_payload(payload: dict[str, Any]) -> LeaderboardEntry:
209
+ """Convert pre-refactor LIBRA-style JSON."""
210
+ from src.bench_config import load_bench_config
211
+
212
+ config = load_bench_config()
213
+ sections: dict[str, SectionStats] = {}
214
+ for section_id, meta in config["sections"].items():
215
+ block = payload.get(section_id) or payload.get(meta["domain"]) or {}
216
+ if not isinstance(block, dict):
217
+ continue
218
+ tasks = {
219
+ task: bool(block.get(task, 0) >= 0.5)
220
+ for task in meta["tasks"]
221
+ if isinstance(block.get(task), (int, float))
222
+ }
223
+ passed = sum(1 for ok in tasks.values() if ok)
224
+ total = len(tasks)
225
+ domain_total = block.get("domain_total_score")
226
+ success_rate = float(domain_total) if isinstance(domain_total, (int, float)) else None
227
+ if success_rate is None and total:
228
+ success_rate = passed / total
229
+ sections[section_id] = SectionStats(
230
+ section_id=section_id,
231
+ label=meta["label"],
232
+ success_rate=success_rate,
233
+ passed=passed,
234
+ total=total,
235
+ tasks=tasks,
236
+ )
237
+
238
+ section_avg_rate = payload.get("total_score")
239
+ if not isinstance(section_avg_rate, (int, float)) and sections:
240
+ rates = [s.success_rate for s in sections.values() if s.success_rate is not None]
241
+ section_avg_rate = sum(rates) / len(rates) if rates else None
242
+
243
+ entry = LeaderboardEntry(
244
+ model=str(payload.get("model") or "unknown"),
245
+ harness=str(payload.get("harness") or "unknown"),
246
+ total_tasks=sum(section.total for section in sections.values()),
247
+ passed_tasks=sum(section.passed for section in sections.values()),
248
+ sections=sections,
249
+ section_avg_rate=float(section_avg_rate) if isinstance(section_avg_rate, (int, float)) else None,
250
+ )
251
+ reconcile_success_metrics(entry)
252
+ return entry
src/tables.py ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Category leaderboard tables and helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import html
6
+
7
+ import pandas as pd
8
+
9
+ from src.bench_config import (
10
+ load_bench_config,
11
+ section_description,
12
+ section_label,
13
+ section_order,
14
+ taxonomy_description,
15
+ taxonomy_label,
16
+ taxonomy_order,
17
+ )
18
+ from src.models import LeaderboardEntry
19
+
20
+
21
+ def section_leaderboard_dataframe(entries: list[LeaderboardEntry], section_id: str) -> pd.DataFrame:
22
+ rows = []
23
+ for entry in entries:
24
+ stats = entry.sections.get(section_id)
25
+ rate = stats.success_rate if stats else None
26
+ rows.append(
27
+ {
28
+ "Model": entry.model,
29
+ "Harness": entry.harness,
30
+ "Tasks": f"{stats.passed}/{stats.total}" if stats else None,
31
+ "Success %": round(rate * 100, 1) if rate is not None else None,
32
+ }
33
+ )
34
+ if not rows:
35
+ return pd.DataFrame()
36
+ return pd.DataFrame(rows).sort_values("Success %", ascending=False, na_position="last")
37
+
38
+
39
+ def taxonomy_leaderboard_dataframe(entries: list[LeaderboardEntry], class_id: str) -> pd.DataFrame:
40
+ rows = []
41
+ for entry in entries:
42
+ row = entry.ui_classes.get(class_id) or {}
43
+ rate = row.get("success_rate")
44
+ passed = row.get("passed")
45
+ total = row.get("total")
46
+ rows.append(
47
+ {
48
+ "Model": entry.model,
49
+ "Harness": entry.harness,
50
+ "Tasks": f"{passed}/{total}" if passed is not None and total is not None else None,
51
+ "Success %": round(float(rate) * 100, 1) if isinstance(rate, (int, float)) else None,
52
+ }
53
+ )
54
+ if not rows:
55
+ return pd.DataFrame()
56
+ return pd.DataFrame(rows).sort_values("Success %", ascending=False, na_position="last")
57
+
58
+
59
+ def category_legend_html(kind: str) -> str:
60
+ config = load_bench_config()
61
+ if kind == "section":
62
+ items = [
63
+ (section_label(section_id), section_description(section_id))
64
+ for section_id in section_order()
65
+ ]
66
+ else:
67
+ items = [
68
+ (taxonomy_label(class_id), taxonomy_description(class_id))
69
+ for class_id in taxonomy_order()
70
+ ]
71
+ spans = "".join(
72
+ f'<span class="wab-legend-item" title="{html.escape(desc)}">{html.escape(label)}</span>'
73
+ for label, desc in items
74
+ )
75
+ return f'<div class="wab-category-legend">{spans}</div>'
76
+
77
+
78
+ def category_description_html(kind: str, item_id: str) -> str:
79
+ if kind == "section":
80
+ desc = section_description(item_id)
81
+ label = section_label(item_id)
82
+ else:
83
+ desc = taxonomy_description(item_id)
84
+ label = taxonomy_label(item_id)
85
+ body = f"<strong>{html.escape(label)}</strong>"
86
+ if desc:
87
+ body += f" — {html.escape(desc)}"
88
+ return f'<p class="wab-category-desc">{body}</p>'
89
+
90
+
91
+ def category_description(kind: str, item_id: str) -> str:
92
+ if kind == "section":
93
+ desc = section_description(item_id)
94
+ label = section_label(item_id)
95
+ else:
96
+ desc = taxonomy_description(item_id)
97
+ label = taxonomy_label(item_id)
98
+ if not desc:
99
+ return f"**{label}**"
100
+ return f"**{label}** — {desc}"
src/ui.py ADDED
@@ -0,0 +1,455 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """PinchBench-inspired HTML UI for Web Agent Bench."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import html
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from src.bench_config import load_bench_config, task_count
10
+ from src.load_entries import sort_entries
11
+ from src.models import LeaderboardEntry
12
+
13
+ RANK_ICONS = ("🦞", "🦐", "🦀")
14
+
15
+ _DETAIL_TAB_LABELS = {
16
+ "submission": "Submission",
17
+ "sections": "Sections",
18
+ "taxonomy": "UI taxonomy",
19
+ "metrics": "Metrics",
20
+ "about": "About",
21
+ }
22
+
23
+
24
+ def load_css() -> str:
25
+ return (Path(__file__).resolve().parent.parent / "assets" / "pinchbench.css").read_text(encoding="utf-8")
26
+
27
+
28
+ def score_tier(pct: float | None) -> str:
29
+ if pct is None:
30
+ return "danger"
31
+ if pct >= 70:
32
+ return "success"
33
+ if pct >= 40:
34
+ return "warning"
35
+ return "danger"
36
+
37
+
38
+ def fmt_pct(value: float | None) -> str:
39
+ if value is None:
40
+ return "—"
41
+ return f"{value * 100:.1f}%" if value <= 1 else f"{value:.1f}%"
42
+
43
+
44
+ def fmt_pct_display(entry: LeaderboardEntry) -> str:
45
+ if entry.success_rate is None:
46
+ return "—"
47
+ return f"{entry.success_rate * 100:.1f}%"
48
+
49
+
50
+ def fmt_duration(seconds: float | None) -> str:
51
+ if seconds is None:
52
+ return "—"
53
+ if seconds < 60:
54
+ return f"{seconds:.1f}s"
55
+ return f"{seconds / 60:.1f}m"
56
+
57
+
58
+ def fmt_cost(value: float | None) -> str:
59
+ if value is None:
60
+ return "—"
61
+ if value <= 0:
62
+ return "FREE"
63
+ return f"${value:.3f}"
64
+
65
+
66
+ def provider_from_model(model: str, provider: str | None = None) -> str:
67
+ if provider:
68
+ return provider
69
+ if "/" in model:
70
+ return model.split("/", 1)[0]
71
+ return model.split("-", 1)[0]
72
+
73
+
74
+ def _badge_link(label: str, passed: object, total: object, css_class: str, title: str, nav: str) -> str:
75
+ stat_html = ""
76
+ try:
77
+ passed_i = int(passed) if passed is not None else None
78
+ total_i = int(total) if total is not None else None
79
+ except (TypeError, ValueError):
80
+ passed_i = total_i = None
81
+ if passed_i is not None and total_i is not None:
82
+ stat_html = f'<span class="wab-badge-stat">{passed_i}/{total_i}</span>'
83
+
84
+ return (
85
+ f'<span role="button" tabindex="0" class="wab-badge wab-badge-link {css_class}" '
86
+ f'title="{html.escape(title)}" data-wab-nav="{html.escape(nav)}">'
87
+ f"{html.escape(label)}{stat_html}</span>"
88
+ )
89
+
90
+
91
+ def _badge(label: str, css_class: str, title: str) -> str:
92
+ return (
93
+ f'<span class="wab-badge {css_class}" title="{html.escape(title)}">'
94
+ f"{html.escape(label)}</span>"
95
+ )
96
+
97
+
98
+ def _row_badges(entry: LeaderboardEntry) -> str:
99
+ config = load_bench_config()
100
+ badges: list[str] = []
101
+
102
+ ranked_sections = sorted(
103
+ (
104
+ (stats.success_rate, stats.section_id, stats.label, stats.passed, stats.total)
105
+ for stats in entry.sections.values()
106
+ if stats.success_rate is not None
107
+ ),
108
+ reverse=True,
109
+ )[:2]
110
+ for _, section_id, label, passed, total in ranked_sections:
111
+ desc = config["sections"].get(section_id, {}).get("description", label)
112
+ nav = f"sections:{section_id}"
113
+ badges.append(_badge_link(label, passed, total, "domain", str(desc), nav))
114
+
115
+ ranked_taxonomy = sorted(
116
+ (
117
+ (float(row.get("success_rate")), class_id)
118
+ for class_id, row in entry.ui_classes.items()
119
+ if isinstance(row.get("success_rate"), (int, float))
120
+ ),
121
+ reverse=True,
122
+ )[:2]
123
+ if not ranked_taxonomy and entry.ui_badges:
124
+ for class_id in entry.ui_badges[:2]:
125
+ meta = config.get("ui_classes", {}).get(class_id, {})
126
+ label = str(meta.get("label") or class_id)
127
+ row = entry.ui_classes.get(class_id) or {}
128
+ nav = f"taxonomy:{class_id}"
129
+ badges.append(
130
+ _badge_link(
131
+ label,
132
+ row.get("passed"),
133
+ row.get("total"),
134
+ "taxonomy",
135
+ str(meta.get("description") or class_id),
136
+ nav,
137
+ )
138
+ )
139
+ else:
140
+ for _, class_id in ranked_taxonomy:
141
+ meta = config.get("ui_classes", {}).get(class_id, {})
142
+ row = entry.ui_classes.get(class_id) or {}
143
+ label = str(meta.get("label") or class_id)
144
+ nav = f"taxonomy:{class_id}"
145
+ badges.append(
146
+ _badge_link(
147
+ label,
148
+ row.get("passed"),
149
+ row.get("total"),
150
+ "taxonomy",
151
+ str(meta.get("description") or class_id),
152
+ nav,
153
+ )
154
+ )
155
+
156
+ return "".join(badges)
157
+
158
+
159
+ def top_section_badges(entry: LeaderboardEntry, limit: int = 2) -> list[str]:
160
+ ranked = [
161
+ (stats.success_rate, stats.label)
162
+ for stats in entry.sections.values()
163
+ if stats.success_rate is not None
164
+ ]
165
+ ranked.sort(reverse=True)
166
+ return [label for _, label in ranked[:limit]]
167
+
168
+
169
+ def pick_winner(entries: list[LeaderboardEntry], metric: str, *, ascending: bool = False) -> LeaderboardEntry | None:
170
+ if not entries:
171
+ return None
172
+ values = [(entry, entry.metric(metric)) for entry in entries]
173
+ values = [(entry, value) for entry, value in values if value is not None]
174
+ if not values:
175
+ return None
176
+ return min(values, key=lambda pair: pair[1])[0] if ascending else max(values, key=lambda pair: pair[1])[0]
177
+
178
+
179
+ def _quick_card(label: str, headline: str, entry: LeaderboardEntry, subtitle: str) -> str:
180
+ return f"""
181
+ <article class="wab-quick-card">
182
+ <div class="wab-quick-kicker">{html.escape(label)}</div>
183
+ <div class="wab-quick-score">{html.escape(headline)}</div>
184
+ <div class="wab-quick-model">{html.escape(entry.model)}</div>
185
+ <div class="wab-quick-meta">{html.escape(subtitle)}<br>{html.escape(entry.harness)}</div>
186
+ </article>
187
+ """
188
+
189
+
190
+ def build_quick_picks(entries: list[LeaderboardEntry], view_id: str) -> str:
191
+ config = load_bench_config()
192
+ picks = config["quick_picks"].get(view_id, [])
193
+ cards: list[str] = []
194
+ for pick in picks:
195
+ metric = pick["metric"]
196
+ ascending = metric in {"avg_duration_seconds", "total_cost_usd"}
197
+ winner = pick_winner(entries, metric, ascending=ascending)
198
+ if winner is None:
199
+ continue
200
+ if metric == "success_rate":
201
+ headline = fmt_pct_display(winner)
202
+ elif metric == "avg_duration_seconds":
203
+ headline = fmt_duration(winner.avg_duration_seconds)
204
+ elif metric in {"total_cost_usd", "avg_cost_per_task_usd"}:
205
+ headline = fmt_cost(winner.total_cost_usd)
206
+ elif metric == "value_score":
207
+ value = winner.value_score
208
+ headline = f"{value:.1f}" if value is not None else "—"
209
+ elif metric.startswith("domain:"):
210
+ rate = winner.metric(metric)
211
+ headline = fmt_pct(rate)
212
+ else:
213
+ headline = fmt_pct_display(winner)
214
+ cards.append(_quick_card(pick["label"], headline, winner, pick["subtitle"]))
215
+ if not cards:
216
+ return ""
217
+ return f'<div class="wab-quick-grid">{"".join(cards)}</div>'
218
+
219
+
220
+ def _success_row(idx: int, entry: LeaderboardEntry) -> str:
221
+ pct = entry.success_pct or 0
222
+ tier = score_tier(pct)
223
+ badges = _row_badges(entry)
224
+ return f"""
225
+ <tr>
226
+ <td class="wab-rank">{RANK_ICONS[idx] if idx < len(RANK_ICONS) else idx + 1}</td>
227
+ <td class="wab-model-cell">
228
+ <div class="wab-model-name">{html.escape(entry.model)}</div>
229
+ <div class="wab-badges">{badges}</div>
230
+ </td>
231
+ <td><span class="wab-harness">{html.escape(entry.harness)}</span></td>
232
+ <td>{entry.passed_tasks}/{entry.total_tasks}</td>
233
+ <td class="wab-score-cell">
234
+ <div class="wab-score-value wab-score-{tier}">{html.escape(fmt_pct_display(entry))}</div>
235
+ <div class="wab-bar"><span class="wab-bar-{tier}" style="width:{max(pct, 0):.1f}%"></span></div>
236
+ </td>
237
+ </tr>
238
+ """
239
+
240
+
241
+ def _speed_row(idx: int, entry: LeaderboardEntry) -> str:
242
+ pct = entry.success_pct or 0
243
+ tier = score_tier(pct)
244
+ return f"""
245
+ <tr>
246
+ <td class="wab-rank">{RANK_ICONS[idx] if idx < len(RANK_ICONS) else idx + 1}</td>
247
+ <td class="wab-model-cell"><div class="wab-model-name">{html.escape(entry.model)}</div>
248
+ <div class="wab-badges"><span class="wab-badge">{html.escape(entry.harness)}</span></div></td>
249
+ <td>{html.escape(fmt_duration(entry.avg_duration_seconds))}</td>
250
+ <td>{entry.avg_agent_steps if entry.avg_agent_steps is not None else "—"}</td>
251
+ <td class="wab-score-cell">
252
+ <div class="wab-score-value wab-score-{tier}">{html.escape(fmt_pct_display(entry))}</div>
253
+ <div class="wab-bar"><span class="wab-bar-{tier}" style="width:{max(pct, 0):.1f}%"></span></div>
254
+ </td>
255
+ </tr>
256
+ """
257
+
258
+
259
+ def _cost_row(idx: int, entry: LeaderboardEntry) -> str:
260
+ pct = entry.success_pct or 0
261
+ tier = score_tier(pct)
262
+ value = entry.value_score
263
+ return f"""
264
+ <tr>
265
+ <td class="wab-rank">{RANK_ICONS[idx] if idx < len(RANK_ICONS) else idx + 1}</td>
266
+ <td class="wab-model-cell"><div class="wab-model-name">{html.escape(entry.model)}</div>
267
+ <div class="wab-badges"><span class="wab-badge">{html.escape(entry.harness)}</span></div></td>
268
+ <td>{html.escape(fmt_cost(entry.total_cost_usd))}</td>
269
+ <td>{html.escape(fmt_cost(entry.avg_cost_per_task_usd))}</td>
270
+ <td>{f"{value:.1f}" if value is not None else "—"}</td>
271
+ <td class="wab-score-cell">
272
+ <div class="wab-score-value wab-score-{tier}">{html.escape(fmt_pct_display(entry))}</div>
273
+ <div class="wab-bar"><span class="wab-bar-{tier}" style="width:{max(pct, 0):.1f}%"></span></div>
274
+ </td>
275
+ </tr>
276
+ """
277
+
278
+
279
+ def build_table(entries: list[LeaderboardEntry], view_id: str) -> str:
280
+ config = load_bench_config()
281
+ view = next(row for row in config["views"] if row["id"] == view_id)
282
+ sorted_entries = sort_entries(entries, view_id)
283
+
284
+ if view_id == "speed":
285
+ head = "<tr><th>#</th><th>Model</th><th>Avg time</th><th>Steps</th><th>Success %</th></tr>"
286
+ rows = [_speed_row(i, entry) for i, entry in enumerate(sorted_entries)]
287
+ subtitle = "Average task duration from results.json run.avg_duration_seconds."
288
+ elif view_id == "cost":
289
+ head = "<tr><th>#</th><th>Model</th><th>Total $</th><th>$/task</th><th>Value</th><th>Success %</th></tr>"
290
+ rows = [_cost_row(i, entry) for i, entry in enumerate(sorted_entries)]
291
+ subtitle = "LLM cost estimated via bench_eval.llm_cost. Value = success_rate / total_cost_usd."
292
+ else:
293
+ head = "<tr><th>#</th><th>Model</th><th>Harness</th><th>Tasks</th><th>Success %</th></tr>"
294
+ rows = [_success_row(i, entry) for i, entry in enumerate(sorted_entries)]
295
+ subtitle = (
296
+ "DAB success rate: passed_tasks / total_tasks where dab_check.all_passed is true. "
297
+ "See docs/EVAL_README.md."
298
+ )
299
+
300
+ body = "".join(rows) if rows else f'<tr><td colspan="6">No results yet. Run export_results.py.</td></tr>'
301
+ return f"""
302
+ <div class="wab-panel">
303
+ <div class="wab-panel-head">
304
+ <h3>{html.escape(view["emoji"])} {html.escape(view["label"])} by model</h3>
305
+ <p>{html.escape(subtitle)}</p>
306
+ </div>
307
+ <div class="wab-table-wrap">
308
+ <table class="wab-table"><thead>{head}</thead><tbody>{body}</tbody></table>
309
+ </div>
310
+ <div class="wab-footnote">
311
+ Tasks and grading criteria:
312
+ <span class="wab-links"><a href="https://github.com/ai-forever/agent-bench/tree/open_bench/tests/bench" target="_blank">tests/bench</a></span>
313
+ · Metrics:
314
+ <span class="wab-links"><a href="https://github.com/ai-forever/agent-bench/blob/open_bench/docs/EVAL_README.md" target="_blank">EVAL_README.md</a></span>
315
+ </div>
316
+ </div>
317
+ """
318
+
319
+
320
+ def build_shell_top(entries: list[LeaderboardEntry]) -> str:
321
+ """Header + hero (view switcher is a separate Gradio control)."""
322
+ config = load_bench_config()
323
+ benchmark = config["benchmark"]
324
+ return f"""
325
+ <div class="wab-shell">
326
+ <div class="wab-topbar">
327
+ <div class="wab-brand">
328
+ <div class="wab-logo">🌐</div>
329
+ <div>
330
+ <h1>{html.escape(benchmark["name"])}</h1>
331
+ <p>Web agent benchmark leaderboard</p>
332
+ </div>
333
+ </div>
334
+ <div class="wab-stats">
335
+ <div class="wab-stat"><strong>{len(entries)}</strong> models</div>
336
+ <div class="wab-stat"><strong>{task_count()}</strong> tasks</div>
337
+ <div class="wab-stat"><strong>{len(config["sections"])}</strong> sections</div>
338
+ </div>
339
+ </div>
340
+
341
+ <section class="wab-hero">
342
+ <h2>The best models for your web agent.</h2>
343
+ <p>Compare harness × LLM combinations on anonymized mock websites with DAB condition checks.</p>
344
+ <p class="wab-hero-links">
345
+ <a href="#wab-detail-anchor" class="wab-jump-link">Sections &amp; UI taxonomy breakdown ↓</a>
346
+ </p>
347
+ </section>
348
+ </div>
349
+ """
350
+
351
+
352
+ def build_board(entries: list[LeaderboardEntry], view_id: str = "success") -> str:
353
+ return f"""
354
+ <div class="wab-board">
355
+ <section>
356
+ <div class="wab-section-label">Quick Picks</div>
357
+ {build_quick_picks(entries, view_id)}
358
+ </section>
359
+ {build_table(entries, view_id)}
360
+ </div>
361
+ """
362
+
363
+
364
+ def build_shell(entries: list[LeaderboardEntry], view_id: str = "success") -> str:
365
+ return build_shell_top(entries) + build_board(entries, view_id)
366
+
367
+
368
+ def build_nav_js() -> str:
369
+ """Client-side navigation for badge deep links (passed to demo.load js=)."""
370
+ labels = ", ".join(f'"{key}": "{value}"' for key, value in _DETAIL_TAB_LABELS.items())
371
+ return f"""
372
+ () => {{
373
+ if (window.__wabNavInit) return [];
374
+ window.__wabNavInit = true;
375
+
376
+ const TAB_LABELS = {{{labels}}};
377
+
378
+ function setPayload(value) {{
379
+ const root = document.getElementById("wab-nav-payload");
380
+ if (!root) return;
381
+ const input = root.querySelector("textarea, input");
382
+ if (!input) return;
383
+ const proto = input.tagName === "TEXTAREA" ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype;
384
+ const setter = Object.getOwnPropertyDescriptor(proto, "value")?.set;
385
+ if (setter) setter.call(input, value);
386
+ else input.value = value;
387
+ input.dispatchEvent(new Event("input", {{ bubbles: true }}));
388
+ }}
389
+
390
+ window.wabOpenDetailTab = function (tabId) {{
391
+ const label = TAB_LABELS[tabId];
392
+ if (!label) return;
393
+ const root = document.getElementById("wab-detail-tabs") || document.querySelector(".wab-tabs");
394
+ if (!root) return;
395
+ const buttons = root.querySelectorAll("button");
396
+ for (const btn of buttons) {{
397
+ if ((btn.textContent || "").trim() === label) {{
398
+ btn.click();
399
+ return;
400
+ }}
401
+ }}
402
+ const order = ["submission", "sections", "taxonomy", "metrics", "about"];
403
+ const idx = order.indexOf(tabId);
404
+ if (idx >= 0 && buttons[idx]) buttons[idx].click();
405
+ }};
406
+
407
+ function clickNavButton() {{
408
+ const root = document.getElementById("wab-nav-go");
409
+ if (!root) return false;
410
+ const btn = root.querySelector("button");
411
+ if (!btn) return false;
412
+ btn.click();
413
+ return true;
414
+ }}
415
+
416
+ function scrollToDetail() {{
417
+ const anchor = document.getElementById("wab-detail-anchor");
418
+ if (anchor) anchor.scrollIntoView({{ behavior: "smooth", block: "start" }});
419
+ }}
420
+
421
+ window.wabNavigate = function (payload) {{
422
+ if (!payload) return;
423
+ window.__wabPendingNav = payload;
424
+ setPayload(payload);
425
+ const tab = payload.split(":", 1)[0] || "submission";
426
+ window.wabOpenDetailTab(tab);
427
+ scrollToDetail();
428
+ setTimeout(clickNavButton, 60);
429
+ }};
430
+
431
+ document.addEventListener("click", function (event) {{
432
+ const jump = event.target.closest(".wab-jump-link");
433
+ if (jump) {{
434
+ event.preventDefault();
435
+ scrollToDetail();
436
+ return;
437
+ }}
438
+ const badge = event.target.closest("[data-wab-nav]");
439
+ if (!badge) return;
440
+ event.preventDefault();
441
+ event.stopPropagation();
442
+ window.wabNavigate(badge.getAttribute("data-wab-nav"));
443
+ }});
444
+
445
+ document.addEventListener("keydown", function (event) {{
446
+ if (event.key !== "Enter" && event.key !== " ") return;
447
+ const badge = event.target.closest("[data-wab-nav]");
448
+ if (!badge) return;
449
+ event.preventDefault();
450
+ window.wabNavigate(badge.getAttribute("data-wab-nav"));
451
+ }});
452
+
453
+ return [];
454
+ }}
455
+ """