Skip to content

Commit cec4973

Browse files
committed
user-facing milestone commands
1 parent bfc4b98 commit cec4973

File tree

2 files changed

+146
-1
lines changed

2 files changed

+146
-1
lines changed

src/kernelbot/cogs/leaderboard_cog.py

Lines changed: 139 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import math
12
from datetime import datetime, timedelta
23
from io import StringIO
34
from typing import TYPE_CHECKING, List, Optional
@@ -22,8 +23,9 @@
2223
RunItem,
2324
SubmissionItem,
2425
)
26+
from libkernelbot.report import make_benchmark_log
2527
from libkernelbot.submission import SubmissionRequest, prepare_submission
26-
from libkernelbot.utils import format_time, setup_logging
28+
from libkernelbot.utils import format_time, run_item_to_run_result, setup_logging
2729

2830
if TYPE_CHECKING:
2931
from kernelbot.main import ClusterBot
@@ -308,6 +310,13 @@ def __init__(self, bot: "ClusterBot"):
308310
name="template", description="Get a starter template file for a task"
309311
)(self.get_task_template)
310312

313+
self.get_task_milestones = bot.leaderboard_group.command(
314+
name="milestones", description="Show milestone performances"
315+
)(self.get_task_milestones)
316+
self.show_milestone_result = bot.leaderboard_group.command(
317+
name="milestone-result", description="Show detailed results of a milestone run"
318+
)(self.show_milestone_result)
319+
311320
self.get_submission_by_id = bot.leaderboard_group.command(
312321
name="get-submission", description="Retrieve one of your past submissions"
313322
)(self.get_submission_by_id)
@@ -588,6 +597,135 @@ async def get_task_template(
588597
)
589598
return
590599

600+
@app_commands.describe(
601+
leaderboard_name="Name of Leaderboard",
602+
gpu="Select GPU. Leave empty for all GPUs.",
603+
)
604+
@app_commands.autocomplete(leaderboard_name=leaderboard_name_autocomplete)
605+
@with_error_handling
606+
async def get_task_milestones(
607+
self,
608+
interaction: discord.Interaction,
609+
leaderboard_name: str,
610+
gpu: Optional[str] = None,
611+
):
612+
await interaction.response.defer(ephemeral=True)
613+
614+
message = f"# Milestones for `{leaderboard_name}`\n"
615+
616+
try:
617+
with self.bot.leaderboard_db as db:
618+
lb = db.get_leaderboard(leaderboard_name)
619+
milestones = db.get_leaderboard_milestones(leaderboard_id=lb["id"])
620+
621+
if len(milestones) == 0:
622+
await send_discord_message(
623+
interaction,
624+
f"Leaderboard `{leaderboard_name}` does not provide any milestones",
625+
ephemeral=True,
626+
)
627+
return
628+
629+
for milestone in milestones:
630+
message += f"## {milestone['name']}\n"
631+
message += milestone["description"] + "\n"
632+
with self.bot.leaderboard_db as db:
633+
runs = db.get_runs_generic(milestone_id=milestone["id"])
634+
635+
runs = [r for r in runs if r["mode"] == SubmissionMode.LEADERBOARD.value]
636+
637+
if len(runs) == 0:
638+
message += "⚠️ No runs available. Maybe they haven't been triggered yet?\n"
639+
640+
if gpu is not None:
641+
runs = [r for r in runs if r["runner"] == gpu]
642+
if len(runs) == 0:
643+
message += f"⚠️ No runs available for GPU {gpu}\n"
644+
645+
max_len = 0
646+
min_val = float("inf")
647+
for run in runs:
648+
max_len = max(max_len, len(run["runner"]))
649+
min_val = min(min_val, run["score"])
650+
651+
digits = max(0, 1 - math.floor(math.log10(min_val)))
652+
653+
message += "```\n"
654+
for run in runs:
655+
message += f" {run['runner']:<{max_len}}: {run['score']:.{digits}f}\n"
656+
message += "```\n\n"
657+
658+
await send_discord_message(
659+
interaction,
660+
message,
661+
ephemeral=True,
662+
)
663+
664+
except Exception as E:
665+
logger.exception("Error fetching milestones for %s", leaderboard_name, exc_info=E)
666+
await send_discord_message(
667+
interaction,
668+
f"Could not fetch milestones for leaderboard `{leaderboard_name}`",
669+
ephemeral=True,
670+
)
671+
return
672+
673+
@app_commands.describe(
674+
leaderboard_name="Name of Leaderboard",
675+
milestone_name="Name of Milestone",
676+
gpu="Select GPU. Leave empty for all GPUs.",
677+
)
678+
@app_commands.autocomplete(leaderboard_name=leaderboard_name_autocomplete)
679+
@with_error_handling
680+
async def show_milestone_result(
681+
self,
682+
interaction: discord.Interaction,
683+
leaderboard_name: str,
684+
milestone_name: str,
685+
gpu: Optional[str] = None,
686+
):
687+
await interaction.response.defer(ephemeral=True)
688+
with self.bot.leaderboard_db as db:
689+
lb = db.get_leaderboard(leaderboard_name)
690+
milestones = db.get_leaderboard_milestones(leaderboard_id=lb["id"])
691+
692+
selected = None
693+
for milestone in milestones:
694+
if milestone["name"].lower() == milestone_name.lower():
695+
selected = milestone
696+
break
697+
698+
if selected is None:
699+
await send_discord_message(
700+
interaction,
701+
f"Could not find milestone `{milestone_name}` for leaderboard `{leaderboard_name}`",
702+
ephemeral=True,
703+
)
704+
return
705+
706+
with self.bot.leaderboard_db as db:
707+
runs = db.get_runs_generic(milestone_id=selected["id"])
708+
709+
runs = [r for r in runs if r["mode"] == SubmissionMode.LEADERBOARD.value]
710+
711+
if len(runs) == 0:
712+
await send_discord_message(
713+
interaction,
714+
f"⚠️ No runs available for milestone `{milestone_name}`."
715+
"Maybe they haven't been triggered yet?",
716+
ephemeral=True,
717+
)
718+
return
719+
720+
for run in runs:
721+
log = make_benchmark_log(run_item_to_run_result(run))
722+
message = f"{milestone_name} on {run['runner']}\n```{log}```\n"
723+
await send_discord_message(
724+
interaction,
725+
message,
726+
ephemeral=True,
727+
)
728+
591729
@discord.app_commands.describe(leaderboard_name="Name of the leaderboard")
592730
@app_commands.autocomplete(leaderboard_name=leaderboard_name_autocomplete)
593731
@with_error_handling

src/libkernelbot/utils.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
import subprocess
33
from typing import Any, Optional
44

5+
from libkernelbot.db_types import RunItem
6+
from libkernelbot.run_eval import RunResult
7+
58

69
def setup_logging(name: Optional[str] = None):
710
"""Configure and setup logging for the application"""
@@ -143,3 +146,7 @@ def limit_length(text: str, maxlen: int):
143146
return text[: maxlen - 6] + " [...]"
144147
else:
145148
return text
149+
150+
151+
def run_item_to_run_result(item: RunItem) -> RunResult:
152+
return RunResult(**item["meta"], result=item["result"], passed=item["passed"])

0 commit comments

Comments
 (0)