flyai-bench 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flyai_bench-0.1.0/.github/workflows/validate.yml +53 -0
- flyai_bench-0.1.0/.gitignore +6 -0
- flyai_bench-0.1.0/LICENSE +21 -0
- flyai_bench-0.1.0/PKG-INFO +200 -0
- flyai_bench-0.1.0/README.md +185 -0
- flyai_bench-0.1.0/agent.py +162 -0
- flyai_bench-0.1.0/benchmark.yaml +49 -0
- flyai_bench-0.1.0/eval_config.yaml +35 -0
- flyai_bench-0.1.0/experiments/README.md +93 -0
- flyai_bench-0.1.0/experiments/evaluation/e_commerce/.gitkeep +0 -0
- flyai_bench-0.1.0/experiments/evaluation/travel/.gitkeep +0 -0
- flyai_bench-0.1.0/experiments/evaluation/travel/20260831_deepseek-v4-flash_mini-swe-agent/metadata.yaml +22 -0
- flyai_bench-0.1.0/experiments/evaluation/travel/20260831_deepseek-v4-flash_mini-swe-agent/scores.jsonl +6 -0
- flyai_bench-0.1.0/experiments/evaluation/travel/20260831_deepseek-v4-flash_mini-swe-agent/summary.json +23 -0
- flyai_bench-0.1.0/experiments/leaderboard.json +1 -0
- flyai_bench-0.1.0/leaderboard_space/README.md +14 -0
- flyai_bench-0.1.0/leaderboard_space/app.py +118 -0
- flyai_bench-0.1.0/leaderboard_space/index.html +319 -0
- flyai_bench-0.1.0/leaderboard_space/leaderboard.json +1405 -0
- flyai_bench-0.1.0/leaderboard_space/requirements.txt +2 -0
- flyai_bench-0.1.0/mini_swe_agent.py +193 -0
- flyai_bench-0.1.0/pyproject.toml +28 -0
- flyai_bench-0.1.0/run_eval.py +634 -0
- flyai_bench-0.1.0/sandbox_setup.sh +113 -0
- flyai_bench-0.1.0/src/flyai_bench/__init__.py +2 -0
- flyai_bench-0.1.0/src/flyai_bench/agent.py +162 -0
- flyai_bench-0.1.0/src/flyai_bench/benchmark.yaml +49 -0
- flyai_bench-0.1.0/src/flyai_bench/cli.py +707 -0
- flyai_bench-0.1.0/src/flyai_bench/eval_config.yaml +35 -0
- flyai_bench-0.1.0/src/flyai_bench/mini_swe_agent.py +193 -0
- flyai_bench-0.1.0/src/flyai_bench/sandbox_setup.sh +113 -0
- flyai_bench-0.1.0/src/flyai_bench/tool_server.py +86 -0
- flyai_bench-0.1.0/src/flyai_bench/validate.py +374 -0
- flyai_bench-0.1.0/tool_server.py +86 -0
- flyai_bench-0.1.0/validate_submission.py +374 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
name: Validate Submission
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
paths:
|
|
6
|
+
- 'experiments/evaluation/**'
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
validate:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
with:
|
|
14
|
+
fetch-depth: 0
|
|
15
|
+
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: '3.12'
|
|
19
|
+
|
|
20
|
+
- name: Install dependencies
|
|
21
|
+
run: pip install pyyaml
|
|
22
|
+
|
|
23
|
+
- name: Validate new submissions
|
|
24
|
+
run: python validate_submission.py check-pr
|
|
25
|
+
|
|
26
|
+
update-leaderboard:
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
if: github.event.pull_request.merged == true
|
|
29
|
+
needs: validate
|
|
30
|
+
permissions:
|
|
31
|
+
contents: write
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v4
|
|
34
|
+
with:
|
|
35
|
+
ref: main
|
|
36
|
+
|
|
37
|
+
- uses: actions/setup-python@v5
|
|
38
|
+
with:
|
|
39
|
+
python-version: '3.12'
|
|
40
|
+
|
|
41
|
+
- name: Install dependencies
|
|
42
|
+
run: pip install pyyaml
|
|
43
|
+
|
|
44
|
+
- name: Rebuild leaderboard
|
|
45
|
+
run: python validate_submission.py rebuild-leaderboard
|
|
46
|
+
|
|
47
|
+
- name: Commit leaderboard update
|
|
48
|
+
run: |
|
|
49
|
+
git config user.name "github-actions[bot]"
|
|
50
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
51
|
+
git add experiments/leaderboard.json experiments/README.md
|
|
52
|
+
git diff --cached --quiet || git commit -m "Update leaderboard"
|
|
53
|
+
git push
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 FlyaiLab
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: flyai-bench
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: E-Commerce Last Exam: Agent benchmark for travel and e-commerce tool-use tasks
|
|
5
|
+
Author-email: FlyaiLab <flyaimainsite@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Requires-Dist: huggingface-hub>=0.20
|
|
10
|
+
Requires-Dist: openai>=1.0
|
|
11
|
+
Requires-Dist: pyyaml>=6.0
|
|
12
|
+
Provides-Extra: dev
|
|
13
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# flyai-bench
|
|
17
|
+
|
|
18
|
+

|
|
19
|
+

|
|
20
|
+
|
|
21
|
+
An evaluation framework for LLM agents. It loads evaluation tasks from a
|
|
22
|
+
HuggingFace dataset, runs an agent plus verifier inside isolated Docker
|
|
23
|
+
containers, and produces standardized scores (reward in the range `0.00 – 1.00`).
|
|
24
|
+
|
|
25
|
+
## Prerequisites
|
|
26
|
+
|
|
27
|
+
- Python >= 3.10
|
|
28
|
+
- Docker (with access to the image registry that hosts the benchmark images;
|
|
29
|
+
the target platform is `linux/amd64`)
|
|
30
|
+
- An OpenAI-compatible LLM endpoint for the agent, and one for the judge/verifier
|
|
31
|
+
(they can be the same endpoint)
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# From a clone of this repository
|
|
37
|
+
pip install -e .
|
|
38
|
+
|
|
39
|
+
# Or install the runtime dependencies directly
|
|
40
|
+
pip install pyyaml huggingface_hub openai
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Installing the package exposes a `flyai-bench` console command. The examples
|
|
44
|
+
below also work by invoking `run_eval.py` directly.
|
|
45
|
+
|
|
46
|
+
## Quick start
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
# 1. Copy the default config and fill in your values
|
|
50
|
+
cp eval_config.yaml eval_config.local.yaml
|
|
51
|
+
# edit eval_config.local.yaml: set the image registry, the LLM/judge
|
|
52
|
+
# base URLs, API keys, and the model name
|
|
53
|
+
|
|
54
|
+
# 2. Run the evaluation
|
|
55
|
+
python run_eval.py run --config eval_config.local.yaml
|
|
56
|
+
|
|
57
|
+
# 3. Inspect results
|
|
58
|
+
python run_eval.py report --config eval_config.local.yaml
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Supported benchmarks
|
|
62
|
+
|
|
63
|
+
| Benchmark | Dataset | Tasks | Description |
|
|
64
|
+
|-----------|---------|-------|-------------|
|
|
65
|
+
| ecommerce_last_exam | [FlyaiLab/ecommerce_last_exam](https://huggingface.co/datasets/FlyaiLab/ecommerce_last_exam) | 1000 | Travel + e-commerce agent tool-use evaluation (500 travel, 500 e-commerce) |
|
|
66
|
+
|
|
67
|
+
## Configuration
|
|
68
|
+
|
|
69
|
+
Key fields in `eval_config.yaml` (copy to `eval_config.local.yaml` before editing):
|
|
70
|
+
|
|
71
|
+
| Field | Meaning |
|
|
72
|
+
|-------|---------|
|
|
73
|
+
| `dataset.repo_id` / `dataset.config` / `dataset.split` | HuggingFace dataset, config (`travel` / `e_commerce`), and split |
|
|
74
|
+
| `docker.registry` | Image registry that hosts the benchmark images. Set to your own registry, or override at runtime with `--registry`. Leave empty to use image names as-is. |
|
|
75
|
+
| `docker.platform` | Container platform (default `linux/amd64`) |
|
|
76
|
+
| `agent.cmd` | Command that runs the agent inside the container |
|
|
77
|
+
| `agent.llm_base_url` / `agent.llm_api_key` / `agent.llm_model` | OpenAI-compatible endpoint, key, and model for the agent |
|
|
78
|
+
| `verifier.judge_base_url` / `judge_api_key` / `judge_model` | Endpoint, key, and model for the judge/verifier |
|
|
79
|
+
| `runner.concurrency` / `runner.limit` / `runner.skip_done` | Parallelism, cap on number of instances (`null` = all), and whether to skip instances that already have a reward |
|
|
80
|
+
|
|
81
|
+
> API keys are read from your local config and injected into the containers as
|
|
82
|
+
> environment variables. Never commit `eval_config.local.yaml` — it is already
|
|
83
|
+
> in `.gitignore`.
|
|
84
|
+
|
|
85
|
+
## Evaluation flow
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
HuggingFace Dataset
|
|
89
|
+
│
|
|
90
|
+
▼
|
|
91
|
+
┌─────────────────────────────────┐
|
|
92
|
+
│ For each instance: │
|
|
93
|
+
│ 1. docker pull <image> │
|
|
94
|
+
│ 2. docker run (start env) │
|
|
95
|
+
│ 3. agent calls tools -> answer │
|
|
96
|
+
│ 4. verifier (test.sh) scores │
|
|
97
|
+
│ 5. emit reward.txt (0 – 1) │
|
|
98
|
+
└─────────────────────────────────┘
|
|
99
|
+
│
|
|
100
|
+
▼
|
|
101
|
+
scores.jsonl + summary.json
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## CLI commands
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# Run the evaluation
|
|
108
|
+
python run_eval.py run [--dataset-config travel|e_commerce] [--limit N] [--dry-run]
|
|
109
|
+
|
|
110
|
+
# Check progress
|
|
111
|
+
python run_eval.py status
|
|
112
|
+
|
|
113
|
+
# Generate a report
|
|
114
|
+
python run_eval.py report
|
|
115
|
+
|
|
116
|
+
# Package results for leaderboard submission
|
|
117
|
+
python run_eval.py submit --model deepseek-v4-flash --provider deepseek
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Output
|
|
121
|
+
|
|
122
|
+
| File | Contents |
|
|
123
|
+
|------|----------|
|
|
124
|
+
| `scores.jsonl` | One line per instance: `{instance_id, domain, reward, duration_sec}` |
|
|
125
|
+
| `summary.json` | Aggregate statistics (`avg_reward`, breakdown `by_domain`) |
|
|
126
|
+
|
|
127
|
+
`reward` is a float in `0.00 – 1.00` produced by the verifier for each instance.
|
|
128
|
+
|
|
129
|
+
## Submitting results to the leaderboard
|
|
130
|
+
|
|
131
|
+
1. After evaluating, package the results with `submit`:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
python run_eval.py submit \
|
|
135
|
+
--dataset-config travel \
|
|
136
|
+
--model deepseek-v4-flash \
|
|
137
|
+
--provider deepseek \
|
|
138
|
+
--agent-type mini-swe-agent
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
2. Review `experiments/evaluation/travel/<slug>/metadata.yaml` and complete the
|
|
142
|
+
model and agent details.
|
|
143
|
+
|
|
144
|
+
3. Open a pull request to the `main` branch of the flyai-bench repository.
|
|
145
|
+
|
|
146
|
+
4. CI validates the format automatically; once merged, the leaderboard updates.
|
|
147
|
+
|
|
148
|
+
### Submission package layout
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
evaluation/travel/20260830_deepseek-v4-flash_mini-swe-agent/
|
|
152
|
+
├── metadata.yaml # model/agent info + evaluation statistics
|
|
153
|
+
├── scores.jsonl # per instance: {instance_id, domain, reward, duration_sec}
|
|
154
|
+
└── summary.json # aggregate statistics (avg_reward, by_domain)
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Writing a custom agent
|
|
158
|
+
|
|
159
|
+
Implement a script that, inside the container:
|
|
160
|
+
|
|
161
|
+
1. Reads `/app/tool_defs.json` for the tool definitions
|
|
162
|
+
2. Reads `/app/system.md` + `/app/instruction.md` for the task
|
|
163
|
+
3. Calls `/app/tools/<name> --arg val` to execute a tool
|
|
164
|
+
4. Writes the result to `/app/answer.json`
|
|
165
|
+
|
|
166
|
+
Point the runner at your agent with `--agent-cmd`:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
python run_eval.py run --agent-cmd "python /app/my_agent.py"
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
`agent.py` (a minimal LLM agent) and `mini_swe_agent.py` (a terminal/bash agent)
|
|
173
|
+
are provided as reference implementations.
|
|
174
|
+
|
|
175
|
+
## Project structure
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
flyai-bench/
|
|
179
|
+
├── README.md
|
|
180
|
+
├── pyproject.toml # packaging (installs the `flyai-bench` CLI)
|
|
181
|
+
├── run_eval.py # evaluation CLI (run / status / report / submit)
|
|
182
|
+
├── eval_config.yaml # default config (copy to eval_config.local.yaml)
|
|
183
|
+
├── benchmark.yaml # benchmark metadata
|
|
184
|
+
├── agent.py # reference LLM agent
|
|
185
|
+
├── mini_swe_agent.py # reference terminal agent
|
|
186
|
+
├── tool_server.py # in-sandbox tool proxy (permission isolation)
|
|
187
|
+
├── sandbox_setup.sh # container permission setup
|
|
188
|
+
├── validate_submission.py # submission validation + leaderboard rebuild
|
|
189
|
+
├── experiments/ # submitted results + leaderboard.json
|
|
190
|
+
├── leaderboard_space/ # HuggingFace Space (leaderboard UI)
|
|
191
|
+
└── src/flyai_bench/ # installable package mirror of the above
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
> Note: the top-level scripts and the `src/flyai_bench/` package currently hold
|
|
195
|
+
> parallel copies of the same code. Prefer editing one and keeping them in sync
|
|
196
|
+
> (or consolidating on the package) to avoid drift.
|
|
197
|
+
|
|
198
|
+
## License
|
|
199
|
+
|
|
200
|
+
Released under the MIT license. See [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# flyai-bench
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+
|
|
6
|
+
An evaluation framework for LLM agents. It loads evaluation tasks from a
|
|
7
|
+
HuggingFace dataset, runs an agent plus verifier inside isolated Docker
|
|
8
|
+
containers, and produces standardized scores (reward in the range `0.00 – 1.00`).
|
|
9
|
+
|
|
10
|
+
## Prerequisites
|
|
11
|
+
|
|
12
|
+
- Python >= 3.10
|
|
13
|
+
- Docker (with access to the image registry that hosts the benchmark images;
|
|
14
|
+
the target platform is `linux/amd64`)
|
|
15
|
+
- An OpenAI-compatible LLM endpoint for the agent, and one for the judge/verifier
|
|
16
|
+
(they can be the same endpoint)
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# From a clone of this repository
|
|
22
|
+
pip install -e .
|
|
23
|
+
|
|
24
|
+
# Or install the runtime dependencies directly
|
|
25
|
+
pip install pyyaml huggingface_hub openai
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Installing the package exposes a `flyai-bench` console command. The examples
|
|
29
|
+
below also work by invoking `run_eval.py` directly.
|
|
30
|
+
|
|
31
|
+
## Quick start
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# 1. Copy the default config and fill in your values
|
|
35
|
+
cp eval_config.yaml eval_config.local.yaml
|
|
36
|
+
# edit eval_config.local.yaml: set the image registry, the LLM/judge
|
|
37
|
+
# base URLs, API keys, and the model name
|
|
38
|
+
|
|
39
|
+
# 2. Run the evaluation
|
|
40
|
+
python run_eval.py run --config eval_config.local.yaml
|
|
41
|
+
|
|
42
|
+
# 3. Inspect results
|
|
43
|
+
python run_eval.py report --config eval_config.local.yaml
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Supported benchmarks
|
|
47
|
+
|
|
48
|
+
| Benchmark | Dataset | Tasks | Description |
|
|
49
|
+
|-----------|---------|-------|-------------|
|
|
50
|
+
| ecommerce_last_exam | [FlyaiLab/ecommerce_last_exam](https://huggingface.co/datasets/FlyaiLab/ecommerce_last_exam) | 1000 | Travel + e-commerce agent tool-use evaluation (500 travel, 500 e-commerce) |
|
|
51
|
+
|
|
52
|
+
## Configuration
|
|
53
|
+
|
|
54
|
+
Key fields in `eval_config.yaml` (copy to `eval_config.local.yaml` before editing):
|
|
55
|
+
|
|
56
|
+
| Field | Meaning |
|
|
57
|
+
|-------|---------|
|
|
58
|
+
| `dataset.repo_id` / `dataset.config` / `dataset.split` | HuggingFace dataset, config (`travel` / `e_commerce`), and split |
|
|
59
|
+
| `docker.registry` | Image registry that hosts the benchmark images. Set to your own registry, or override at runtime with `--registry`. Leave empty to use image names as-is. |
|
|
60
|
+
| `docker.platform` | Container platform (default `linux/amd64`) |
|
|
61
|
+
| `agent.cmd` | Command that runs the agent inside the container |
|
|
62
|
+
| `agent.llm_base_url` / `agent.llm_api_key` / `agent.llm_model` | OpenAI-compatible endpoint, key, and model for the agent |
|
|
63
|
+
| `verifier.judge_base_url` / `judge_api_key` / `judge_model` | Endpoint, key, and model for the judge/verifier |
|
|
64
|
+
| `runner.concurrency` / `runner.limit` / `runner.skip_done` | Parallelism, cap on number of instances (`null` = all), and whether to skip instances that already have a reward |
|
|
65
|
+
|
|
66
|
+
> API keys are read from your local config and injected into the containers as
|
|
67
|
+
> environment variables. Never commit `eval_config.local.yaml` — it is already
|
|
68
|
+
> in `.gitignore`.
|
|
69
|
+
|
|
70
|
+
## Evaluation flow
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
HuggingFace Dataset
|
|
74
|
+
│
|
|
75
|
+
▼
|
|
76
|
+
┌─────────────────────────────────┐
|
|
77
|
+
│ For each instance: │
|
|
78
|
+
│ 1. docker pull <image> │
|
|
79
|
+
│ 2. docker run (start env) │
|
|
80
|
+
│ 3. agent calls tools -> answer │
|
|
81
|
+
│ 4. verifier (test.sh) scores │
|
|
82
|
+
│ 5. emit reward.txt (0 – 1) │
|
|
83
|
+
└─────────────────────────────────┘
|
|
84
|
+
│
|
|
85
|
+
▼
|
|
86
|
+
scores.jsonl + summary.json
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## CLI commands
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# Run the evaluation
|
|
93
|
+
python run_eval.py run [--dataset-config travel|e_commerce] [--limit N] [--dry-run]
|
|
94
|
+
|
|
95
|
+
# Check progress
|
|
96
|
+
python run_eval.py status
|
|
97
|
+
|
|
98
|
+
# Generate a report
|
|
99
|
+
python run_eval.py report
|
|
100
|
+
|
|
101
|
+
# Package results for leaderboard submission
|
|
102
|
+
python run_eval.py submit --model deepseek-v4-flash --provider deepseek
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Output
|
|
106
|
+
|
|
107
|
+
| File | Contents |
|
|
108
|
+
|------|----------|
|
|
109
|
+
| `scores.jsonl` | One line per instance: `{instance_id, domain, reward, duration_sec}` |
|
|
110
|
+
| `summary.json` | Aggregate statistics (`avg_reward`, breakdown `by_domain`) |
|
|
111
|
+
|
|
112
|
+
`reward` is a float in `0.00 – 1.00` produced by the verifier for each instance.
|
|
113
|
+
|
|
114
|
+
## Submitting results to the leaderboard
|
|
115
|
+
|
|
116
|
+
1. After evaluating, package the results with `submit`:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
python run_eval.py submit \
|
|
120
|
+
--dataset-config travel \
|
|
121
|
+
--model deepseek-v4-flash \
|
|
122
|
+
--provider deepseek \
|
|
123
|
+
--agent-type mini-swe-agent
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
2. Review `experiments/evaluation/travel/<slug>/metadata.yaml` and complete the
|
|
127
|
+
model and agent details.
|
|
128
|
+
|
|
129
|
+
3. Open a pull request to the `main` branch of the flyai-bench repository.
|
|
130
|
+
|
|
131
|
+
4. CI validates the format automatically; once merged, the leaderboard updates.
|
|
132
|
+
|
|
133
|
+
### Submission package layout
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
evaluation/travel/20260830_deepseek-v4-flash_mini-swe-agent/
|
|
137
|
+
├── metadata.yaml # model/agent info + evaluation statistics
|
|
138
|
+
├── scores.jsonl # per instance: {instance_id, domain, reward, duration_sec}
|
|
139
|
+
└── summary.json # aggregate statistics (avg_reward, by_domain)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Writing a custom agent
|
|
143
|
+
|
|
144
|
+
Implement a script that, inside the container:
|
|
145
|
+
|
|
146
|
+
1. Reads `/app/tool_defs.json` for the tool definitions
|
|
147
|
+
2. Reads `/app/system.md` + `/app/instruction.md` for the task
|
|
148
|
+
3. Calls `/app/tools/<name> --arg val` to execute a tool
|
|
149
|
+
4. Writes the result to `/app/answer.json`
|
|
150
|
+
|
|
151
|
+
Point the runner at your agent with `--agent-cmd`:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
python run_eval.py run --agent-cmd "python /app/my_agent.py"
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
`agent.py` (a minimal LLM agent) and `mini_swe_agent.py` (a terminal/bash agent)
|
|
158
|
+
are provided as reference implementations.
|
|
159
|
+
|
|
160
|
+
## Project structure
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
flyai-bench/
|
|
164
|
+
├── README.md
|
|
165
|
+
├── pyproject.toml # packaging (installs the `flyai-bench` CLI)
|
|
166
|
+
├── run_eval.py # evaluation CLI (run / status / report / submit)
|
|
167
|
+
├── eval_config.yaml # default config (copy to eval_config.local.yaml)
|
|
168
|
+
├── benchmark.yaml # benchmark metadata
|
|
169
|
+
├── agent.py # reference LLM agent
|
|
170
|
+
├── mini_swe_agent.py # reference terminal agent
|
|
171
|
+
├── tool_server.py # in-sandbox tool proxy (permission isolation)
|
|
172
|
+
├── sandbox_setup.sh # container permission setup
|
|
173
|
+
├── validate_submission.py # submission validation + leaderboard rebuild
|
|
174
|
+
├── experiments/ # submitted results + leaderboard.json
|
|
175
|
+
├── leaderboard_space/ # HuggingFace Space (leaderboard UI)
|
|
176
|
+
└── src/flyai_bench/ # installable package mirror of the above
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
> Note: the top-level scripts and the `src/flyai_bench/` package currently hold
|
|
180
|
+
> parallel copies of the same code. Prefer editing one and keeping them in sync
|
|
181
|
+
> (or consolidating on the package) to avoid drift.
|
|
182
|
+
|
|
183
|
+
## License
|
|
184
|
+
|
|
185
|
+
Released under the MIT license. See [LICENSE](LICENSE) for details.
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Minimal reference agent for E-Commerce Last Exam benchmark.
|
|
3
|
+
|
|
4
|
+
This file demonstrates the agent/environment interface. In real evaluations it can be replaced by:
|
|
5
|
+
- mini-swe-agent (general-purpose CLI agent)
|
|
6
|
+
- a custom LLM agent (calling a model via an OpenAI-compatible API)
|
|
7
|
+
|
|
8
|
+
Agent responsibilities:
|
|
9
|
+
1. Read /app/tool_defs.json for the available tool definitions (OpenAI function-calling schema)
|
|
10
|
+
2. Read system.md + instruction.md for the task prompt
|
|
11
|
+
3. Call the LLM to decide -> call tools -> loop until the task is done
|
|
12
|
+
4. Write the final result to /app/answer.json
|
|
13
|
+
|
|
14
|
+
Tool call convention:
|
|
15
|
+
/app/tools/<tool_name> --param1 value1 --param2 value2
|
|
16
|
+
Output is JSON (stdout)
|
|
17
|
+
"""
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
import subprocess
|
|
21
|
+
import sys
|
|
22
|
+
|
|
23
|
+
# Environment file paths (inside the container)
|
|
24
|
+
TOOL_DEFS_PATH = "/app/tool_defs.json"
|
|
25
|
+
SYSTEM_MD_PATH = "/app/system.md"
|
|
26
|
+
INSTRUCTION_MD_PATH = "/app/instruction.md"
|
|
27
|
+
ANSWER_PATH = "/app/answer.json"
|
|
28
|
+
TOOL_CALL_LOG = os.environ.get("TOOL_CALL_LOG", "/app/.tool_calls.jsonl")
|
|
29
|
+
|
|
30
|
+
# Agent LLM config (injected via environment variables)
|
|
31
|
+
LLM_BASE_URL = os.environ.get("LLM_BASE_URL", "http://localhost:4000")
|
|
32
|
+
LLM_API_KEY = os.environ.get("LLM_API_KEY", "")
|
|
33
|
+
LLM_MODEL = os.environ.get("LLM_MODEL", "qwen3.7-plus")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def load_tool_defs():
|
|
37
|
+
with open(TOOL_DEFS_PATH, encoding="utf-8") as f:
|
|
38
|
+
return json.load(f)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def load_prompts():
|
|
42
|
+
system = ""
|
|
43
|
+
instruction = ""
|
|
44
|
+
if os.path.exists(SYSTEM_MD_PATH):
|
|
45
|
+
with open(SYSTEM_MD_PATH, encoding="utf-8") as f:
|
|
46
|
+
system = f.read()
|
|
47
|
+
if os.path.exists(INSTRUCTION_MD_PATH):
|
|
48
|
+
with open(INSTRUCTION_MD_PATH, encoding="utf-8") as f:
|
|
49
|
+
instruction = f.read()
|
|
50
|
+
return system, instruction
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def call_tool(tool_name, arguments):
|
|
54
|
+
"""Execute a single tool call and return the output JSON."""
|
|
55
|
+
tool_path = f"/app/tools/{tool_name}"
|
|
56
|
+
if not os.path.exists(tool_path):
|
|
57
|
+
return {"error": f"tool not found: {tool_name}"}
|
|
58
|
+
|
|
59
|
+
cmd = [tool_path]
|
|
60
|
+
for k, v in arguments.items():
|
|
61
|
+
cmd += [f"--{k}", str(v)]
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
r = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
|
|
65
|
+
if r.returncode != 0:
|
|
66
|
+
return {"error": r.stderr[:500]}
|
|
67
|
+
try:
|
|
68
|
+
return json.loads(r.stdout)
|
|
69
|
+
except json.JSONDecodeError:
|
|
70
|
+
return {"raw_output": r.stdout[:2000]}
|
|
71
|
+
except subprocess.TimeoutExpired:
|
|
72
|
+
return {"error": "tool call timeout"}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def log_tool_call(tool_name, arguments, result):
|
|
76
|
+
"""Log a tool call."""
|
|
77
|
+
entry = {"tool": tool_name, "arguments": arguments, "result": result}
|
|
78
|
+
with open(TOOL_CALL_LOG, "a", encoding="utf-8") as f:
|
|
79
|
+
f.write(json.dumps(entry, ensure_ascii=False) + "\n")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def call_llm(messages, tools):
|
|
83
|
+
"""Call the LLM API (OpenAI-compatible)."""
|
|
84
|
+
try:
|
|
85
|
+
import openai
|
|
86
|
+
except ImportError:
|
|
87
|
+
print("ERROR: openai package not installed", file=sys.stderr)
|
|
88
|
+
sys.exit(1)
|
|
89
|
+
|
|
90
|
+
client = openai.OpenAI(base_url=LLM_BASE_URL, api_key=LLM_API_KEY)
|
|
91
|
+
response = client.chat.completions.create(
|
|
92
|
+
model=LLM_MODEL,
|
|
93
|
+
messages=messages,
|
|
94
|
+
tools=tools,
|
|
95
|
+
tool_choice="auto",
|
|
96
|
+
temperature=0.1,
|
|
97
|
+
)
|
|
98
|
+
return response.choices[0].message
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def run():
|
|
102
|
+
"""Main agent loop."""
|
|
103
|
+
tool_defs = load_tool_defs()
|
|
104
|
+
system_prompt, instruction = load_prompts()
|
|
105
|
+
|
|
106
|
+
# Build the OpenAI tools format
|
|
107
|
+
tools = []
|
|
108
|
+
for td in tool_defs:
|
|
109
|
+
tools.append({
|
|
110
|
+
"type": "function",
|
|
111
|
+
"function": {
|
|
112
|
+
"name": td["name"],
|
|
113
|
+
"description": td.get("description", ""),
|
|
114
|
+
"parameters": td.get("parameters", {}),
|
|
115
|
+
}
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
messages = [
|
|
119
|
+
{"role": "system", "content": system_prompt},
|
|
120
|
+
{"role": "user", "content": instruction},
|
|
121
|
+
]
|
|
122
|
+
|
|
123
|
+
max_turns = 20
|
|
124
|
+
for turn in range(max_turns):
|
|
125
|
+
response = call_llm(messages, tools)
|
|
126
|
+
|
|
127
|
+
if response.tool_calls:
|
|
128
|
+
messages.append(response.model_dump())
|
|
129
|
+
for tc in response.tool_calls:
|
|
130
|
+
fn_name = tc.function.name
|
|
131
|
+
try:
|
|
132
|
+
fn_args = json.loads(tc.function.arguments)
|
|
133
|
+
except json.JSONDecodeError:
|
|
134
|
+
fn_args = {}
|
|
135
|
+
|
|
136
|
+
result = call_tool(fn_name, fn_args)
|
|
137
|
+
log_tool_call(fn_name, fn_args, result)
|
|
138
|
+
|
|
139
|
+
messages.append({
|
|
140
|
+
"role": "tool",
|
|
141
|
+
"tool_call_id": tc.id,
|
|
142
|
+
"content": json.dumps(result, ensure_ascii=False),
|
|
143
|
+
})
|
|
144
|
+
else:
|
|
145
|
+
# Agent done; parse the final reply into answer.json
|
|
146
|
+
content = response.content or ""
|
|
147
|
+
try:
|
|
148
|
+
answer = json.loads(content)
|
|
149
|
+
except json.JSONDecodeError:
|
|
150
|
+
answer = {"raw_response": content}
|
|
151
|
+
|
|
152
|
+
with open(ANSWER_PATH, "w", encoding="utf-8") as f:
|
|
153
|
+
json.dump(answer, f, ensure_ascii=False, indent=2)
|
|
154
|
+
print(f"Agent completed in {turn + 1} turns. Answer written to {ANSWER_PATH}")
|
|
155
|
+
return
|
|
156
|
+
|
|
157
|
+
print(f"WARNING: Agent reached max turns ({max_turns}) without final answer",
|
|
158
|
+
file=sys.stderr)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
if __name__ == "__main__":
|
|
162
|
+
run()
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
name: ecommerce_last_exam
|
|
2
|
+
version: "1.0"
|
|
3
|
+
description: "E-Commerce Last Exam: Agent benchmark for travel and e-commerce tool-use tasks"
|
|
4
|
+
|
|
5
|
+
dataset:
|
|
6
|
+
repo: FlyaiLab/ecommerce_last_exam
|
|
7
|
+
configs:
|
|
8
|
+
- travel # 500 Chinese trip_guide tasks (hotel/traffic/poi)
|
|
9
|
+
- e_commerce # 500 English shopping tasks (fashion/health/electronics/outdoor/home)
|
|
10
|
+
split: test
|
|
11
|
+
|
|
12
|
+
agent:
|
|
13
|
+
type: mini-swe-agent
|
|
14
|
+
timeout_sec: 1800
|
|
15
|
+
interface: tool-calling
|
|
16
|
+
output_file: /app/answer.json
|
|
17
|
+
|
|
18
|
+
verifier:
|
|
19
|
+
timeout_sec: 1200
|
|
20
|
+
test_script: /app/tests/test.sh
|
|
21
|
+
reward_file: /logs/verifier/reward.txt
|
|
22
|
+
env:
|
|
23
|
+
JUDGE_BASE_URL: "${JUDGE_BASE_URL}"
|
|
24
|
+
JUDGE_API_KEY: "${JUDGE_API_KEY}"
|
|
25
|
+
JUDGE_MODEL: "${JUDGE_MODEL}"
|
|
26
|
+
|
|
27
|
+
docker:
|
|
28
|
+
registry: ""
|
|
29
|
+
platform: linux/amd64
|
|
30
|
+
|
|
31
|
+
output:
|
|
32
|
+
scores_file: scores.jsonl
|
|
33
|
+
summary_file: summary.json
|
|
34
|
+
reward_format: float # 0.00 - 1.00
|
|
35
|
+
|
|
36
|
+
submission:
|
|
37
|
+
experiments_dir: experiments/evaluation/
|
|
38
|
+
format:
|
|
39
|
+
required:
|
|
40
|
+
- metadata.yaml # model/agent info + evaluation stats
|
|
41
|
+
- scores.jsonl # per-instance: {instance_id, domain, reward, duration_sec}
|
|
42
|
+
optional:
|
|
43
|
+
- summary.json # aggregate stats (auto-generated if missing)
|
|
44
|
+
- trajs/ # per-instance agent traces
|
|
45
|
+
workflow:
|
|
46
|
+
- "python run_eval.py run --dataset-config travel"
|
|
47
|
+
- "python run_eval.py report --dataset-config travel"
|
|
48
|
+
- "python run_eval.py submit --dataset-config travel --model your-model"
|
|
49
|
+
- "# then PR to experiments repo"
|