|
| 1 | +import math |
1 | 2 | from datetime import datetime, timedelta |
2 | 3 | from io import StringIO |
3 | 4 | from typing import TYPE_CHECKING, List, Optional |
|
22 | 23 | RunItem, |
23 | 24 | SubmissionItem, |
24 | 25 | ) |
| 26 | +from libkernelbot.report import make_benchmark_log |
25 | 27 | 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 |
27 | 29 |
|
28 | 30 | if TYPE_CHECKING: |
29 | 31 | from kernelbot.main import ClusterBot |
@@ -308,6 +310,13 @@ def __init__(self, bot: "ClusterBot"): |
308 | 310 | name="template", description="Get a starter template file for a task" |
309 | 311 | )(self.get_task_template) |
310 | 312 |
|
| 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 | + |
311 | 320 | self.get_submission_by_id = bot.leaderboard_group.command( |
312 | 321 | name="get-submission", description="Retrieve one of your past submissions" |
313 | 322 | )(self.get_submission_by_id) |
@@ -588,6 +597,135 @@ async def get_task_template( |
588 | 597 | ) |
589 | 598 | return |
590 | 599 |
|
| 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 | + |
591 | 729 | @discord.app_commands.describe(leaderboard_name="Name of the leaderboard") |
592 | 730 | @app_commands.autocomplete(leaderboard_name=leaderboard_name_autocomplete) |
593 | 731 | @with_error_handling |
|
0 commit comments