Skip to content

Commit 28b80fb

Browse files
feat: support re-estimation from estimate uuids [skip-ci]
1 parent db4aeb8 commit 28b80fb

6 files changed

Lines changed: 187 additions & 30 deletions

File tree

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,10 @@ python -m pytest tests/ -v
240240
- `estimate.sh` がアプリ固有の推定ロジックを実装(`programs/<code>/estimate.sh`
241241
- `estimate_common.sh` が共通関数(API呼び出し、JSON出力等)を提供
242242
- 簡易推定と詳細推定の双方を将来的に受け入れられる設計を前提とする
243-
- UUID指定による再推定もサポート(`estimate_uuid` 変数でトリガー)
243+
- UUID指定による再推定もサポート
244+
- `estimate_result_uuid` を指定すると、その estimate から `source_result_uuid` を引いて再推定
245+
- `result_uuid` を指定すると、元 result を直接指定して再推定
246+
-`estimate_uuid``result_uuid` の互換名として扱う
244247

245248
### 5. BenchPark統合パイプライン
246249
- `benchpark-bridge/config/apps.csv` で監視対象を定義

docs/cx/REESTIMATION_SPEC.md

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,11 @@ Typical use cases include:
5454

5555
再推定では、入力となる benchmark result を明示的に固定しなければならない。
5656
この識別には、少なくとも result の UUID を用いることを基本とする。
57+
ただし、実運用では estimate result UUID を入口にし、その estimate が保持する `source_result_uuid` から元 result を辿れることが望ましい。
5758

5859
In re-estimation, the input benchmark result must be explicitly fixed.
5960
At minimum, the UUID of the result should be used as the primary identifier.
61+
In practical workflows, however, it is desirable to allow an estimate-result UUID as the entry point and resolve the original result through `source_result_uuid`.
6062

6163
### 3.2 比較可能性 / Comparability
6264

@@ -98,25 +100,27 @@ It must not silently ignore missing inputs and still report a successful estimat
98100

99101
BenchKit における再推定の典型フローは以下である。
100102

101-
1. 利用者またはワークフローが対象 benchmark result の UUID を指定する
102-
2. BenchKit が UUID に対応する Result JSON を取得する
103-
3. 対象 app の `estimate.sh` を起動する
104-
4. `Estimate JSON` を生成する
105-
5. 生成結果を保存し、ポータルで参照可能にする
103+
1. 利用者またはワークフローが `estimate_result_uuid` または `result_uuid` を指定する
104+
2. `estimate_result_uuid` の場合、BenchKit は estimate JSON を取得し、そこから `source_result_uuid` を解決する
105+
3. BenchKit が対応する Result JSON を取得する
106+
4. 対象 app の `estimate.sh` を起動する
107+
5. `Estimate JSON` を生成する
108+
6. 生成結果を保存し、ポータルで参照可能にする
106109

107110
The typical re-estimation flow in BenchKit is:
108111

109-
1. a user or workflow specifies the UUID of a benchmark result
110-
2. BenchKit fetches the corresponding Result JSON
111-
3. the app-specific `estimate.sh` is invoked
112-
4. an `Estimate JSON` is generated
113-
5. the generated result is stored and made available through the portal
112+
1. a user or workflow specifies either an `estimate_result_uuid` or a `result_uuid`
113+
2. if an `estimate_result_uuid` is given, BenchKit fetches the estimate JSON and resolves `source_result_uuid`
114+
3. BenchKit fetches the corresponding Result JSON
115+
4. the app-specific `estimate.sh` is invoked
116+
5. an `Estimate JSON` is generated
117+
6. the generated result is stored and made available through the portal
114118

115119
## 5. 入力要件 / Input Requirements
116120

117121
再推定では少なくとも以下を入力として扱う。
118122

119-
- `result_uuid`
123+
- `estimate_result_uuid` または `result_uuid`
120124
- `code`
121125

122126
必要に応じて以下を追加で与えてよい。
@@ -129,7 +133,7 @@ The typical re-estimation flow in BenchKit is:
129133

130134
At minimum, re-estimation uses the following inputs:
131135

132-
- `result_uuid`
136+
- `estimate_result_uuid` or `result_uuid`
133137
- `code`
134138

135139
Optionally, the following may also be supplied:
@@ -164,7 +168,11 @@ In the current implementation, the following already exist:
164168

165169
### 7.1 UUID 取得 API の仕様 / UUID-Based Result Retrieval API
166170

167-
再推定を UUID 起点で運用するには、UUID 指定で Result JSON を取得する API または同等の取得手段が必要である。
171+
再推定を UUID 起点で運用するには、少なくとも次の取得口が必要である。
172+
173+
- estimate result UUID で estimate JSON を返す取得 API
174+
- result UUID で Result JSON を返す取得 API
175+
168176
現時点では、再推定の shell フロー自体は存在しても、その取得口の公開仕様や認証条件は文書としてまだ十分に固定されていない。
169177

170178
したがって、以下を明確化する必要がある。
@@ -179,7 +187,8 @@ At present, the shell-side re-estimation flow exists, but the retrieval endpoint
179187

180188
The following therefore need to be clarified:
181189

182-
- a retrieval API that returns Result JSON by UUID
190+
- a retrieval API that returns Estimate JSON by estimate-result UUID
191+
- a retrieval API that returns Result JSON by result UUID
183192
- whether authentication is required
184193
- how confidential results are handled
185194
- behavior on retrieval failure
@@ -238,7 +247,7 @@ At least the following need to be clarified:
238247

239248
BenchKit における再推定は、少なくとも以下を満たすことが望ましい。
240249

241-
1. UUID 指定で benchmark result を再取得できること
250+
1. `estimate_result_uuid` または `result_uuid` 指定で benchmark result を再取得できること
242251
2. app ごとの `estimate.sh` を同じ入力に対して繰り返し実行できること
243252
3. 再推定結果に、元 benchmark result UUID を残せること
244253
4. 異なる推定方式を同じ benchmark result に対して併存させられること
@@ -247,7 +256,7 @@ BenchKit における再推定は、少なくとも以下を満たすことが
247256

248257
Re-estimation in BenchKit should preferably satisfy at least:
249258

250-
1. the benchmark result can be re-fetched by UUID
259+
1. the benchmark result can be re-fetched from either `estimate_result_uuid` or `result_uuid`
251260
2. app-specific `estimate.sh` can be run repeatedly for the same input
252261
3. the re-estimation result can retain the original benchmark-result UUID
253262
4. different estimation methods can coexist for the same benchmark result
@@ -258,14 +267,14 @@ Re-estimation in BenchKit should preferably satisfy at least:
258267

259268
次に候補となる実装は以下である。
260269

261-
1. UUID 指定取得 API の仕様化と実装確認
270+
1. estimate/result の UUID 指定取得 API の仕様化と実装確認
262271
2. 再推定向けに UUID 指定取得口と認証条件を文書化する
263272
3. 同一 `source_result_uuid` を軸にした比較表示仕様を定義する
264273
4. portal から再推定を起動する要求フローを定義する
265274

266275
Candidate next steps include:
267276

268-
1. specify and verify the UUID-based result retrieval API
277+
1. specify and verify the UUID-based estimate/result retrieval APIs
269278
2. document the retrieval endpoint and authentication conditions for re-estimation
270279
3. define a display specification for comparing re-estimation results using `source_result_uuid`
271280
4. define a portal-driven request flow for starting re-estimation

result_server/routes/api.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,38 @@ def is_valid_uuid(value):
9292
return False
9393

9494

95+
def _load_json_by_uuid(directory, field_path, uuid_value):
96+
"""指定ディレクトリから UUID に一致する JSON を探して返す。"""
97+
json_files = sorted(
98+
[f for f in os.listdir(directory) if f.endswith(".json")],
99+
reverse=True,
100+
)
101+
102+
for json_file in json_files:
103+
path = os.path.join(directory, json_file)
104+
try:
105+
with open(path, "r", encoding="utf-8") as f:
106+
data = json.load(f)
107+
except Exception:
108+
continue
109+
110+
current = data
111+
for key in field_path:
112+
if not isinstance(current, dict):
113+
current = None
114+
break
115+
current = current.get(key)
116+
117+
if current == uuid_value:
118+
return data
119+
120+
# 互換用: ファイル名中のUUIDにもフォールバック
121+
if uuid_value in json_file:
122+
return data
123+
124+
return None
125+
126+
95127
# ==========================================
96128
# 新パス: /api/ingest/*
97129
# ==========================================
@@ -238,6 +270,44 @@ def query_result():
238270
abort(404, description=f"No result found for system={system}, code={code}, exp={exp}")
239271

240272

273+
@api_bp.route("/api/query/result/<uuid_value>", methods=["GET"])
274+
def query_result_by_uuid(uuid_value):
275+
"""UUID で単一 Result JSON を返す。"""
276+
require_api_key()
277+
278+
if not is_valid_uuid(uuid_value):
279+
abort(400, description="Invalid UUID")
280+
281+
data = _load_json_by_uuid(
282+
current_app.config["RECEIVED_DIR"],
283+
["_server_uuid"],
284+
uuid_value,
285+
)
286+
if data is None:
287+
abort(404, description=f"No result found for uuid={uuid_value}")
288+
289+
return jsonify(data), 200
290+
291+
292+
@api_bp.route("/api/query/estimate/<uuid_value>", methods=["GET"])
293+
def query_estimate_by_uuid(uuid_value):
294+
"""UUID で単一 Estimate JSON を返す。"""
295+
require_api_key()
296+
297+
if not is_valid_uuid(uuid_value):
298+
abort(400, description="Invalid UUID")
299+
300+
data = _load_json_by_uuid(
301+
current_app.config["ESTIMATED_DIR"],
302+
["estimate_metadata", "estimation_result_uuid"],
303+
uuid_value,
304+
)
305+
if data is None:
306+
abort(404, description=f"No estimate found for uuid={uuid_value}")
307+
308+
return jsonify(data), 200
309+
310+
241311
# ==========================================
242312
# 互換ルート (deprecated)
243313
# ==========================================

result_server/tests/test_api_routes.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,51 @@ def test_query_missing_api_key_returns_401(self, client):
341341
assert resp.status_code == 401
342342

343343

344+
class TestQueryByUuid:
345+
def _seed_json(self, directory, filename, data):
346+
path = os.path.join(directory, filename)
347+
with open(path, "w", encoding="utf-8") as f:
348+
json.dump(data, f)
349+
350+
def test_query_result_by_uuid(self, client, tmp_dirs):
351+
received, _ = tmp_dirs
352+
data = {"code": "qws", "_server_uuid": "12345678-1234-1234-1234-123456789abc"}
353+
self._seed_json(received, "result_20250101_000000_12345678-1234-1234-1234-123456789abc.json", data)
354+
355+
resp = client.get(
356+
"/api/query/result/12345678-1234-1234-1234-123456789abc",
357+
headers={"X-API-Key": API_KEY},
358+
)
359+
assert resp.status_code == 200
360+
assert resp.get_json()["code"] == "qws"
361+
362+
def test_query_estimate_by_uuid(self, client, tmp_dirs):
363+
_, estimated = tmp_dirs
364+
data = {
365+
"code": "qws",
366+
"estimate_metadata": {
367+
"estimation_result_uuid": "87654321-4321-4321-4321-cba987654321",
368+
"source_result_uuid": "12345678-1234-1234-1234-123456789abc",
369+
},
370+
}
371+
self._seed_json(estimated, "estimate_20250101_000000_87654321-4321-4321-4321-cba987654321.json", data)
372+
373+
resp = client.get(
374+
"/api/query/estimate/87654321-4321-4321-4321-cba987654321",
375+
headers={"X-API-Key": API_KEY},
376+
)
377+
assert resp.status_code == 200
378+
assert resp.get_json()["estimate_metadata"]["source_result_uuid"] == "12345678-1234-1234-1234-123456789abc"
379+
380+
def test_query_result_by_uuid_missing_api_key_returns_401(self, client):
381+
resp = client.get("/api/query/result/12345678-1234-1234-1234-123456789abc")
382+
assert resp.status_code == 401
383+
384+
def test_query_estimate_by_uuid_missing_api_key_returns_401(self, client):
385+
resp = client.get("/api/query/estimate/87654321-4321-4321-4321-cba987654321")
386+
assert resp.status_code == 401
387+
388+
344389
# ============================================================
345390
# ヘルパー
346391
# ============================================================

scripts/fetch_result_by_uuid.sh

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,47 @@
33
# Saves the result to results/result0.json for subsequent estimation.
44
#
55
# Required CI variables:
6-
# estimate_uuid - UUID of the benchmark result to fetch
6+
# result_uuid - UUID of the benchmark result to fetch directly
7+
# estimate_result_uuid - UUID of the estimate result to re-estimate from
8+
# estimate_uuid - legacy alias for result_uuid
79
# code - Program code name (e.g., "qws")
810
# RESULT_SERVER - Base URL of the result server
911
set -euo pipefail
1012

11-
if [[ -z "${estimate_uuid:-}" || -z "${code:-}" ]]; then
12-
echo "ERROR: Both estimate_uuid and code must be specified" >&2
13+
if [[ -z "${code:-}" ]]; then
14+
echo "ERROR: code must be specified" >&2
1315
exit 1
1416
fi
1517

1618
mkdir -p results
1719

18-
echo "Fetching result for UUID: $estimate_uuid"
19-
curl --fail -sS -o "results/result0.json" \
20-
"${RESULT_SERVER}/api/result/${estimate_uuid}"
20+
resolved_result_uuid="${result_uuid:-}"
21+
22+
if [[ -z "$resolved_result_uuid" && -n "${estimate_result_uuid:-}" ]]; then
23+
echo "Fetching estimate for UUID: $estimate_result_uuid"
24+
curl --fail -sS -H "X-API-Key: ${RESULT_SERVER_KEY}" \
25+
-o "results/source_estimate.json" \
26+
"${RESULT_SERVER}/api/query/estimate/${estimate_result_uuid}"
27+
28+
resolved_result_uuid="$(jq -r '.estimate_metadata.source_result_uuid // empty' results/source_estimate.json)"
29+
if [[ -z "$resolved_result_uuid" ]]; then
30+
echo "ERROR: source_result_uuid not found in estimate ${estimate_result_uuid}" >&2
31+
exit 1
32+
fi
33+
echo "Resolved source result UUID: $resolved_result_uuid"
34+
fi
35+
36+
if [[ -z "$resolved_result_uuid" && -n "${estimate_uuid:-}" ]]; then
37+
echo "WARNING: estimate_uuid is deprecated; use result_uuid or estimate_result_uuid" >&2
38+
resolved_result_uuid="$estimate_uuid"
39+
fi
40+
41+
if [[ -z "$resolved_result_uuid" ]]; then
42+
echo "ERROR: one of result_uuid, estimate_result_uuid, or legacy estimate_uuid must be specified" >&2
43+
exit 1
44+
fi
45+
46+
echo "Fetching result for UUID: $resolved_result_uuid"
47+
curl --fail -sS -H "X-API-Key: ${RESULT_SERVER_KEY}" -o "results/result0.json" \
48+
"${RESULT_SERVER}/api/query/result/${resolved_result_uuid}"
2149
echo "Fetched result to results/result0.json"

scripts/generate_estimate_from_uuid.sh

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,18 @@
55
# of a specific benchmark result identified by UUID.
66
#
77
# Required CI variables:
8-
# estimate_uuid - UUID of the benchmark result to re-estimate
8+
# result_uuid - UUID of the benchmark result to re-estimate directly
9+
# estimate_result_uuid - UUID of the estimate result to re-estimate from
10+
# estimate_uuid - legacy alias for result_uuid
911
# code - Program code name (e.g., "qws")
1012
#
1113
# Output: .gitlab-ci.estimate.yml with fetch → estimate → send_estimate stages
1214

1315
set -euo pipefail
1416

1517
# Validate required variables
16-
if [[ -z "${estimate_uuid:-}" ]]; then
17-
echo "ERROR: estimate_uuid must be specified" >&2
18+
if [[ -z "${result_uuid:-}" && -z "${estimate_result_uuid:-}" && -z "${estimate_uuid:-}" ]]; then
19+
echo "ERROR: result_uuid or estimate_result_uuid must be specified" >&2
1820
exit 1
1921
fi
2022

@@ -25,7 +27,7 @@ fi
2527

2628
OUTPUT_FILE=".gitlab-ci.estimate.yml"
2729

28-
echo "Generating estimate pipeline YAML for UUID: $estimate_uuid, code: $code"
30+
echo "Generating estimate pipeline YAML for code: $code"
2931

3032
cat > "$OUTPUT_FILE" <<YAML
3133
stages:
@@ -37,7 +39,7 @@ fetch_result:
3739
stage: fetch
3840
tags: [fncx-curl-jq]
3941
script:
40-
- echo "Fetching result for UUID: \$estimate_uuid"
42+
- echo "Fetching re-estimation input"
4143
- bash scripts/fetch_result_by_uuid.sh
4244
artifacts:
4345
paths:

0 commit comments

Comments
 (0)