Skip to content

Commit fa8084a

Browse files
author
Mark Saroufim
committed
update
1 parent d11eafb commit fa8084a

5 files changed

Lines changed: 68 additions & 24 deletions

File tree

‎.buildkite/pipeline.yml‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
# Environment variables passed from BuildkiteLauncher:
1010
# - SUBMISSION_PAYLOAD: Base64-encoded, zlib-compressed submission config
1111
# - GPU_QUEUE: Queue name for agent routing
12+
#
13+
# Note: timeout_in_minutes is set high (60) as a safety net.
14+
# The BuildkiteLauncher handles dynamic timeouts based on submission mode
15+
# and will cancel jobs that exceed their configured timeout.
1216

1317
steps:
1418
- label: ":gpu: Run Kernel Submission"
@@ -19,7 +23,8 @@ steps:
1923
agents:
2024
# Route to agent with matching queue tag
2125
queue: "${GPU_QUEUE}"
22-
timeout_in_minutes: 15
26+
# Safety timeout - BuildkiteLauncher handles actual timeout enforcement
27+
timeout_in_minutes: 60
2328
artifact_paths:
2429
- "result.json"
2530
- "profile_data/**/*"

‎src/libkernelbot/consts.py‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,14 @@ class RankCriterion(Enum):
135135
"MI300": None,
136136
"MI300x8": None,
137137
"MI250": None,
138+
# Buildkite GPU types (vendor-prefixed queue names)
139+
"nvidia-h100": "90a",
140+
"nvidia-b200": "100",
141+
"nvidia-a100": "80",
142+
"nebius-h100": "90a",
143+
"amd-mi300": None,
144+
"amd-mi250": None,
145+
"google-tpu": None,
138146
}
139147

140148

‎src/libkernelbot/launchers/buildkite.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,8 @@ async def _wait_for_completion(
221221
if state in ("passed", "failed", "canceled", "blocked"):
222222
if state != "passed":
223223
logger.warning(f"Build #{build_number} finished with state: {state}")
224-
return
224+
raise RuntimeError(f"Build #{build_number} {state}")
225+
return state
225226

226227
await callback(state, elapsed)
227228
await asyncio.sleep(10) # Poll every 10 seconds

‎src/runners/buildkite-runner.py‎

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
import base64
1414
import json
1515
import os
16+
import sys
17+
import traceback
1618
import zlib
1719
from dataclasses import asdict
1820
from datetime import datetime
@@ -28,28 +30,52 @@ def serialize(obj: object):
2830
raise TypeError(f"Type {type(obj)} not serializable")
2931

3032

33+
def write_error_result(error_message: str):
34+
"""Write an error result to result.json when execution fails."""
35+
error_result = {
36+
"success": False,
37+
"error": error_message,
38+
"runs": {},
39+
"system": {},
40+
}
41+
Path("result.json").write_text(json.dumps(error_result, default=serialize))
42+
43+
3144
def main():
32-
# Get payload from environment variable
33-
payload_b64 = os.environ.get("SUBMISSION_PAYLOAD")
34-
if not payload_b64:
35-
raise RuntimeError("SUBMISSION_PAYLOAD environment variable not set")
36-
37-
# Decompress and parse config
38-
payload = zlib.decompress(base64.b64decode(payload_b64)).decode("utf-8")
39-
config = json.loads(payload)
40-
41-
# Run the submission
42-
result = run_config(config)
43-
44-
# Write result to file for artifact upload
45-
result_dict = asdict(result)
46-
Path("result.json").write_text(json.dumps(result_dict, default=serialize))
47-
48-
# Create profile_data directory if profiling was enabled
49-
# (profile artifacts will be written there by run_config)
50-
profile_dir = Path("profile_data")
51-
if profile_dir.exists():
52-
print(f"Profile data available in {profile_dir}")
45+
try:
46+
# Get payload from environment variable
47+
payload_b64 = os.environ.get("SUBMISSION_PAYLOAD")
48+
if not payload_b64:
49+
write_error_result("SUBMISSION_PAYLOAD environment variable not set")
50+
sys.exit(1)
51+
52+
# Decompress and parse config
53+
try:
54+
payload = zlib.decompress(base64.b64decode(payload_b64)).decode("utf-8")
55+
config = json.loads(payload)
56+
except Exception as e:
57+
write_error_result(f"Failed to decompress/parse payload: {e}")
58+
sys.exit(1)
59+
60+
# Run the submission
61+
result = run_config(config)
62+
63+
# Write result to file for artifact upload
64+
result_dict = asdict(result)
65+
Path("result.json").write_text(json.dumps(result_dict, default=serialize))
66+
67+
# Create profile_data directory if profiling was enabled
68+
# (profile artifacts will be written there by run_config)
69+
profile_dir = Path("profile_data")
70+
if profile_dir.exists():
71+
print(f"Profile data available in {profile_dir}")
72+
73+
except Exception as e:
74+
# Catch any unexpected errors and write them to result.json
75+
error_msg = f"Runner error: {e}\n{traceback.format_exc()}"
76+
print(error_msg, file=sys.stderr)
77+
write_error_result(error_msg)
78+
sys.exit(1)
5379

5480

5581
if __name__ == "__main__":

‎tests/test_buildkite.py‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ def mock_config(self):
7070
return {
7171
"lang": "py",
7272
"mode": "test",
73-
"files": {"main.py": "print('hello')"},
73+
"main": "main.py",
74+
"sources": {"main.py": "print('hello')"},
7475
"tests": [],
7576
"benchmarks": [],
7677
"test_timeout": 180,
@@ -141,6 +142,9 @@ async def test_run_submission_creates_build(self, launcher, mock_config, gpu_typ
141142
assert "SUBMISSION_PAYLOAD" in body["env"]
142143
assert body["env"]["GPU_QUEUE"] == "nvidia-h100"
143144

145+
# Verify result
146+
assert result.success is True
147+
144148
@pytest.mark.asyncio
145149
async def test_run_submission_handles_api_error(self, launcher, mock_config, gpu_type):
146150
"""Test that API errors are handled gracefully."""

0 commit comments

Comments
 (0)