From 8f5cd3a177ff135548e70ec24afc4b413e73edcd Mon Sep 17 00:00:00 2001 From: Bovard Doerschuk-Tiberi Date: Mon, 13 Apr 2026 19:17:50 +0000 Subject: [PATCH 1/7] Add CLI commands for simulation episodes and competition pages Implement four new competition subcommands: - `competitions episodes` - list episodes for a submission - `competitions episode-replay` - download episode replay - `competitions episode-logs` - download agent logs for an episode - `competitions pages` - list competition pages --- src/kaggle/api/kaggle_api_extended.py | 150 ++++++++++++++++++++++++++ src/kaggle/cli.py | 76 ++++++++++++- 2 files changed, 225 insertions(+), 1 deletion(-) diff --git a/src/kaggle/api/kaggle_api_extended.py b/src/kaggle/api/kaggle_api_extended.py index 78124f86..e185053b 100644 --- a/src/kaggle/api/kaggle_api_extended.py +++ b/src/kaggle/api/kaggle_api_extended.py @@ -84,6 +84,12 @@ ApiDataFile, ApiCreateCodeSubmissionResponse, ApiListCompetitionsResponse, + ApiListSubmissionEpisodesRequest, + ApiListSubmissionEpisodesResponse, + ApiGetEpisodeReplayRequest, + ApiGetEpisodeAgentLogsRequest, + ApiListCompetitionPagesRequest, + ApiListCompetitionPagesResponse, ) from kagglesdk.competitions.types.competition_enums import ( CompetitionListTab, @@ -624,6 +630,9 @@ class KaggleApi: model_instance_labels = ["version", "notes", "created", "size"] model_instance_version_fields = ["versionNumber", "variationSlug", "modelTitle", "isPrivate"] model_instance_version_labels = ["version", "variation", "title", "private"] + episode_fields = ["id", "createTime", "endTime", "state", "type"] + episode_agent_fields = ["submissionId", "index", "reward", "state", "teamName", "teamId"] + competition_page_fields = ["name"] def __init__(self, enable_oauth: bool = False): self.enable_oauth = enable_oauth @@ -1742,6 +1751,147 @@ def competition_leaderboard_cli( else: print("No results found") + def competition_list_episodes(self, submission_id: int): + """List episodes for a submission in a simulation competition. + + Args: + submission_id (int): The submission ID to list episodes for. + + Returns: + list: A list of ApiEpisode objects. + """ + with self.build_kaggle_client() as kaggle: + request = ApiListSubmissionEpisodesRequest() + request.submission_id = submission_id + response = kaggle.competitions.competition_api_client.list_submission_episodes(request) + return response.episodes + + def competition_list_episodes_cli(self, submission_id, csv_display=False, quiet=False): + """CLI wrapper for competition_list_episodes. + + Args: + submission_id (int): The submission ID. + csv_display (bool): If True, print CSV instead of table. + quiet (bool): Suppress verbose output. + """ + episodes = self.competition_list_episodes(submission_id) + if episodes: + if csv_display: + self.print_csv(episodes, self.episode_fields) + else: + self.print_table(episodes, self.episode_fields) + else: + print("No episodes found") + + def competition_episode_replay(self, episode_id: int, path: Optional[str] = None, quiet: bool = True): + """Download the replay for an episode. + + Args: + episode_id (int): The episode ID. + path (Optional[str]): A path to download the file to. + quiet (bool): Suppress verbose output. + """ + with self.build_kaggle_client() as kaggle: + request = ApiGetEpisodeReplayRequest() + request.episode_id = episode_id + response = kaggle.competitions.competition_api_client.get_episode_replay(request) + if path is None: + effective_path = os.getcwd() + else: + effective_path = path + outfile = os.path.join(effective_path, f"episode-{episode_id}-replay.json") + self.download_file(response, outfile, kaggle.http_client(), quiet) + + def competition_episode_replay_cli(self, episode_id, path=None, quiet=False): + """CLI wrapper for competition_episode_replay. + + Args: + episode_id (int): The episode ID. + path (Optional[str]): A path to download the file to. + quiet (bool): Suppress verbose output. + """ + self.competition_episode_replay(episode_id, path, quiet) + + def competition_episode_agent_logs( + self, episode_id: int, agent_index: int, path: Optional[str] = None, quiet: bool = True + ): + """Download logs for a specific agent in an episode. + + Args: + episode_id (int): The episode ID. + agent_index (int): The agent index. + path (Optional[str]): A path to download the file to. + quiet (bool): Suppress verbose output. + """ + with self.build_kaggle_client() as kaggle: + request = ApiGetEpisodeAgentLogsRequest() + request.episode_id = episode_id + request.agent_index = agent_index + response = kaggle.competitions.competition_api_client.get_episode_agent_logs(request) + if path is None: + effective_path = os.getcwd() + else: + effective_path = path + outfile = os.path.join(effective_path, f"episode-{episode_id}-agent-{agent_index}-logs.json") + self.download_file(response, outfile, kaggle.http_client(), quiet) + + def competition_episode_agent_logs_cli(self, episode_id, agent_index, path=None, quiet=False): + """CLI wrapper for competition_episode_agent_logs. + + Args: + episode_id (int): The episode ID. + agent_index (int): The agent index. + path (Optional[str]): A path to download the file to. + quiet (bool): Suppress verbose output. + """ + self.competition_episode_agent_logs(episode_id, agent_index, path, quiet) + + def competition_list_pages(self, competition: str): + """List pages for a competition. + + Args: + competition (str): The competition name. + + Returns: + list: A list of ApiCompetitionPage objects. + """ + with self.build_kaggle_client() as kaggle: + request = ApiListCompetitionPagesRequest() + request.competition_name = competition + response = kaggle.competitions.competition_api_client.list_competition_pages(request) + return response.pages + + def competition_list_pages_cli( + self, competition=None, competition_opt=None, csv_display=False, quiet=False, content=False + ): + """CLI wrapper for competition_list_pages. + + Args: + competition: The competition name. + competition_opt: An alternative competition option provided by cli. + csv_display (bool): If True, print CSV instead of table. + quiet (bool): Suppress verbose output. + content (bool): If True, show full page content. + """ + competition = competition or competition_opt + if competition is None: + competition = self.get_config_value(self.CONFIG_NAME_COMPETITION) + if competition is not None and not quiet: + print("Using competition: " + competition) + + if competition is None: + raise ValueError("No competition specified") + + pages = self.competition_list_pages(competition) + if pages: + fields = ["name", "content"] if content else self.competition_page_fields + if csv_display: + self.print_csv(pages, fields) + else: + self.print_table(pages, fields) + else: + print("No pages found") + def dataset_list( self, sort_by: Optional[str] = None, diff --git a/src/kaggle/cli.py b/src/kaggle/cli.py index 5a4567c0..9d5264c3 100644 --- a/src/kaggle/cli.py +++ b/src/kaggle/cli.py @@ -276,6 +276,73 @@ def parse_competitions(subparsers) -> None: parser_competitions_leaderboard._action_groups.append(parser_competitions_leaderboard_optional) parser_competitions_leaderboard.set_defaults(func=api.competition_leaderboard_cli) + # Competitions list episodes + parser_competitions_episodes = subparsers_competitions.add_parser( + "episodes", formatter_class=argparse.RawTextHelpFormatter, help=Help.command_competitions_episodes + ) + parser_competitions_episodes_optional = parser_competitions_episodes._action_groups.pop() + parser_competitions_episodes_optional.add_argument("submission_id", type=int, help="Submission ID") + parser_competitions_episodes_optional.add_argument( + "-v", "--csv", dest="csv_display", action="store_true", help=Help.param_csv + ) + parser_competitions_episodes_optional.add_argument( + "-q", "--quiet", dest="quiet", action="store_true", help=Help.param_quiet + ) + parser_competitions_episodes._action_groups.append(parser_competitions_episodes_optional) + parser_competitions_episodes.set_defaults(func=api.competition_list_episodes_cli) + + # Competitions episode replay + parser_competitions_episode_replay = subparsers_competitions.add_parser( + "episode-replay", formatter_class=argparse.RawTextHelpFormatter, help=Help.command_competitions_episode_replay + ) + parser_competitions_episode_replay_optional = parser_competitions_episode_replay._action_groups.pop() + parser_competitions_episode_replay_optional.add_argument("episode_id", type=int, help="Episode ID") + parser_competitions_episode_replay_optional.add_argument( + "-p", "--path", dest="path", required=False, help=Help.param_downfolder + ) + parser_competitions_episode_replay_optional.add_argument( + "-q", "--quiet", dest="quiet", action="store_true", help=Help.param_quiet + ) + parser_competitions_episode_replay._action_groups.append(parser_competitions_episode_replay_optional) + parser_competitions_episode_replay.set_defaults(func=api.competition_episode_replay_cli) + + # Competitions episode agent logs + parser_competitions_episode_logs = subparsers_competitions.add_parser( + "episode-logs", formatter_class=argparse.RawTextHelpFormatter, help=Help.command_competitions_episode_logs + ) + parser_competitions_episode_logs_optional = parser_competitions_episode_logs._action_groups.pop() + parser_competitions_episode_logs_optional.add_argument("episode_id", type=int, help="Episode ID") + parser_competitions_episode_logs_optional.add_argument("agent_index", type=int, help="Agent index") + parser_competitions_episode_logs_optional.add_argument( + "-p", "--path", dest="path", required=False, help=Help.param_downfolder + ) + parser_competitions_episode_logs_optional.add_argument( + "-q", "--quiet", dest="quiet", action="store_true", help=Help.param_quiet + ) + parser_competitions_episode_logs._action_groups.append(parser_competitions_episode_logs_optional) + parser_competitions_episode_logs.set_defaults(func=api.competition_episode_agent_logs_cli) + + # Competitions list pages + parser_competitions_pages = subparsers_competitions.add_parser( + "pages", formatter_class=argparse.RawTextHelpFormatter, help=Help.command_competitions_pages + ) + parser_competitions_pages_optional = parser_competitions_pages._action_groups.pop() + parser_competitions_pages_optional.add_argument("competition", nargs="?", default=None, help=Help.param_competition) + parser_competitions_pages_optional.add_argument( + "-c", "--competition", dest="competition_opt", required=False, help=argparse.SUPPRESS + ) + parser_competitions_pages_optional.add_argument( + "-v", "--csv", dest="csv_display", action="store_true", help=Help.param_csv + ) + parser_competitions_pages_optional.add_argument( + "-q", "--quiet", dest="quiet", action="store_true", help=Help.param_quiet + ) + parser_competitions_pages_optional.add_argument( + "--content", dest="content", action="store_true", help="Show full page content" + ) + parser_competitions_pages._action_groups.append(parser_competitions_pages_optional) + parser_competitions_pages.set_defaults(func=api.competition_list_pages_cli) + def parse_datasets(subparsers) -> None: parser_datasets = subparsers.add_parser( @@ -1116,7 +1183,10 @@ class Help(object): "config", "auth", ] - competitions_choices = ["list", "files", "download", "submit", "submissions", "leaderboard"] + competitions_choices = [ + "list", "files", "download", "submit", "submissions", "leaderboard", + "episodes", "episode-replay", "episode-logs", "pages", + ] datasets_choices = ["list", "files", "download", "create", "version", "init", "metadata", "status", "delete"] kernels_choices = ["list", "files", "get", "init", "push", "pull", "output", "status", "update", "delete"] models_choices = ["instances", "i", "variations", "v", "get", "list", "init", "create", "delete", "update"] @@ -1169,6 +1239,10 @@ class Help(object): command_competitions_submit = "Make a new competition submission" command_competitions_submissions = "Show your competition submissions" command_competitions_leaderboard = "Get competition leaderboard information" + command_competitions_episodes = "List episodes for a submission in a simulation competition" + command_competitions_episode_replay = "Download the replay for a simulation episode" + command_competitions_episode_logs = "Download agent logs for a simulation episode" + command_competitions_pages = "List pages for a competition" # Datasets commands command_datasets_list = "List available datasets" From 642005e1dbac74a8fedc87a35ee35940c21de87c Mon Sep 17 00:00:00 2001 From: Bovard Doerschuk-Tiberi Date: Mon, 13 Apr 2026 21:17:30 +0000 Subject: [PATCH 2/7] Add simulation competitions tutorial Document the end-to-end workflow for simulation competitions: finding competitions, viewing pages, submitting agents, listing episodes, and downloading replays and agent logs. --- docs/simulation_competitions.md | 133 ++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 docs/simulation_competitions.md diff --git a/docs/simulation_competitions.md b/docs/simulation_competitions.md new file mode 100644 index 00000000..a92f8aaa --- /dev/null +++ b/docs/simulation_competitions.md @@ -0,0 +1,133 @@ +# Tutorial: Simulation Competitions + +This tutorial walks you through interacting with a Kaggle simulation competition using the CLI — from finding the competition to downloading episode replays and agent logs. + +Simulation competitions (e.g., [Lux AI](https://www.kaggle.com/competitions/lux-ai-season-3), [Santa](https://www.kaggle.com/competitions/santa-2024)) differ from standard competitions. Instead of submitting a CSV of predictions, you submit an agent (code) that plays against other agents in episodes. Each episode contains multiple agents competing against each other. + +## 1. Find and Inspect the Competition + +List available competitions and look for simulation competitions: + +```bash +kaggle competitions list --sort-by latestDeadline +``` + +Once you've identified a competition (e.g., `lux-ai-season-3`), view its pages to read the rules, evaluation criteria, and other details: + +```bash +kaggle competitions pages lux-ai-season-3 +``` + +This lists the available pages (e.g., `description`, `rules`, `evaluation`, `data-description`). To read the full content of a page: + +```bash +kaggle competitions pages lux-ai-season-3 --content +``` + +## 2. Accept the Competition Rules + +Before you can submit or download data, you **must** accept the competition rules on the Kaggle website. Navigate to the competition page (e.g., `https://www.kaggle.com/competitions/lux-ai-season-3`) and click "Join Competition" or "I Understand and Accept". + +You can verify you've joined by checking your entered competitions: + +```bash +kaggle competitions list --group entered +``` + +## 3. Download Competition Data + +Download the competition's starter kit and any provided data: + +```bash +mkdir lux-ai +cd lux-ai +kaggle competitions download lux-ai-season-3 +``` + +## 4. Submit Your Agent + +Simulation competitions use code submissions. First, create and push a notebook with your agent code, then submit it: + +```bash +kaggle competitions submit lux-ai-season-3 -k YOUR_USERNAME/lux-ai-agent -f submission.tar.gz -v 1 -m "First agent submission" +``` + +## 5. Monitor Your Submission + +Check the status of your submissions: + +```bash +kaggle competitions submissions lux-ai-season-3 +``` + +Note the submission ID from the output — you'll need it to view episodes. + +## 6. List Episodes for a Submission + +Once your submission has played some games, list the episodes: + +```bash +kaggle competitions episodes 12345678 +``` + +Replace `12345678` with your submission ID. This shows a table of episodes with columns: `id`, `createTime`, `endTime`, `state`, and `type`. + +To get the output in CSV format for scripting: + +```bash +kaggle competitions episodes 12345678 -v +``` + +## 7. Download an Episode Replay + +To download the replay data for a specific episode (useful for visualizing what happened): + +```bash +kaggle competitions episode-replay 98765432 +``` + +This downloads the replay JSON to your current directory as `episode-98765432-replay.json`. To specify a download location: + +```bash +kaggle competitions episode-replay 98765432 -p ./replays +``` + +## 8. Download Agent Logs + +To debug your agent's behavior, download the logs for a specific agent in an episode. You need the episode ID and the agent's index (0-based): + +```bash +# Download logs for the first agent (index 0) +kaggle competitions episode-logs 98765432 0 + +# Download logs for the second agent (index 1) +kaggle competitions episode-logs 98765432 1 -p ./logs +``` + +This downloads the log file as `episode-98765432-agent-0-logs.json`. + +## Putting It All Together + +Here's a typical workflow for iterating on a simulation competition agent: + +```bash +# Set up +mkdir my-sim-comp && cd my-sim-comp +kaggle competitions download lux-ai-season-3 + +# Submit your agent +kaggle competitions submit lux-ai-season-3 -k YOUR_USERNAME/my-agent -f submission.tar.gz -v 1 -m "v1" + +# Check submission status +kaggle competitions submissions lux-ai-season-3 + +# List episodes (replace with your submission ID) +kaggle competitions episodes 12345678 + +# Download replay and logs for an episode +kaggle competitions episode-replay 98765432 +kaggle competitions episode-logs 98765432 0 + +# Check the leaderboard +kaggle competitions leaderboard lux-ai-season-3 -s +``` From c142b30184937e62b8d7cece9496f186cbe89e5d Mon Sep 17 00:00:00 2001 From: Bovard Doerschuk-Tiberi Date: Mon, 13 Apr 2026 22:11:37 +0000 Subject: [PATCH 3/7] Update simulation tutorial with direct file upload as default Show single-file (main.py) and multi-file (submission.tar.gz) upload as the primary submission methods, with notebook submission as an alternative. --- docs/simulation_competitions.md | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/docs/simulation_competitions.md b/docs/simulation_competitions.md index a92f8aaa..a8cf5c8d 100644 --- a/docs/simulation_competitions.md +++ b/docs/simulation_competitions.md @@ -46,10 +46,25 @@ kaggle competitions download lux-ai-season-3 ## 4. Submit Your Agent -Simulation competitions use code submissions. First, create and push a notebook with your agent code, then submit it: +Simulation competitions require you to submit agent code. You can upload files directly from your local machine. + +**Single file agent** — if your agent is a single `main.py`: + +```bash +kaggle competitions submit lux-ai-season-3 -f main.py -m "Single file agent v1" +``` + +**Multi-file agent** — if your agent spans multiple files, bundle them into a `submission.tar.gz` with `main.py` at the root: + +```bash +tar -czf submission.tar.gz main.py helper.py model_weights.pkl +kaggle competitions submit lux-ai-season-3 -f submission.tar.gz -m "Multi-file agent v1" +``` + +**Notebook submission** — alternatively, you can submit via an existing Kaggle notebook: ```bash -kaggle competitions submit lux-ai-season-3 -k YOUR_USERNAME/lux-ai-agent -f submission.tar.gz -v 1 -m "First agent submission" +kaggle competitions submit lux-ai-season-3 -k YOUR_USERNAME/lux-ai-agent -f submission.tar.gz -v 1 -m "Notebook agent v1" ``` ## 5. Monitor Your Submission @@ -115,8 +130,8 @@ Here's a typical workflow for iterating on a simulation competition agent: mkdir my-sim-comp && cd my-sim-comp kaggle competitions download lux-ai-season-3 -# Submit your agent -kaggle competitions submit lux-ai-season-3 -k YOUR_USERNAME/my-agent -f submission.tar.gz -v 1 -m "v1" +# Submit your agent (single file) +kaggle competitions submit lux-ai-season-3 -f main.py -m "v1" # Check submission status kaggle competitions submissions lux-ai-season-3 From ca21dce1edd14ae47c05403467fad76cec686ba2 Mon Sep 17 00:00:00 2001 From: Bovard Doerschuk-Tiberi Date: Mon, 13 Apr 2026 22:13:47 +0000 Subject: [PATCH 4/7] Improve UX for simulation competition commands - Print output file path after downloading replays and agent logs - Add hint after episodes list pointing to episode-replay/episode-logs - Improve help text for submission_id, episode_id, and agent_index args --- src/kaggle/api/kaggle_api_extended.py | 9 +++++++++ src/kaggle/cli.py | 20 ++++++++++++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/kaggle/api/kaggle_api_extended.py b/src/kaggle/api/kaggle_api_extended.py index e185053b..e92f2111 100644 --- a/src/kaggle/api/kaggle_api_extended.py +++ b/src/kaggle/api/kaggle_api_extended.py @@ -1780,6 +1780,11 @@ def competition_list_episodes_cli(self, submission_id, csv_display=False, quiet= self.print_csv(episodes, self.episode_fields) else: self.print_table(episodes, self.episode_fields) + if not quiet: + print( + '\nUse "kaggle competitions episode-replay " to download a replay, ' + 'or "kaggle competitions episode-logs " for agent logs.' + ) else: print("No episodes found") @@ -1801,6 +1806,8 @@ def competition_episode_replay(self, episode_id: int, path: Optional[str] = None effective_path = path outfile = os.path.join(effective_path, f"episode-{episode_id}-replay.json") self.download_file(response, outfile, kaggle.http_client(), quiet) + if not quiet: + print(f"Replay downloaded to: {outfile}") def competition_episode_replay_cli(self, episode_id, path=None, quiet=False): """CLI wrapper for competition_episode_replay. @@ -1834,6 +1841,8 @@ def competition_episode_agent_logs( effective_path = path outfile = os.path.join(effective_path, f"episode-{episode_id}-agent-{agent_index}-logs.json") self.download_file(response, outfile, kaggle.http_client(), quiet) + if not quiet: + print(f"Agent logs downloaded to: {outfile}") def competition_episode_agent_logs_cli(self, episode_id, agent_index, path=None, quiet=False): """CLI wrapper for competition_episode_agent_logs. diff --git a/src/kaggle/cli.py b/src/kaggle/cli.py index 9d5264c3..23e9f64f 100644 --- a/src/kaggle/cli.py +++ b/src/kaggle/cli.py @@ -281,7 +281,10 @@ def parse_competitions(subparsers) -> None: "episodes", formatter_class=argparse.RawTextHelpFormatter, help=Help.command_competitions_episodes ) parser_competitions_episodes_optional = parser_competitions_episodes._action_groups.pop() - parser_competitions_episodes_optional.add_argument("submission_id", type=int, help="Submission ID") + parser_competitions_episodes_optional.add_argument( + "submission_id", type=int, + help='Submission ID (find yours with "kaggle competitions submissions ")' + ) parser_competitions_episodes_optional.add_argument( "-v", "--csv", dest="csv_display", action="store_true", help=Help.param_csv ) @@ -296,7 +299,10 @@ def parse_competitions(subparsers) -> None: "episode-replay", formatter_class=argparse.RawTextHelpFormatter, help=Help.command_competitions_episode_replay ) parser_competitions_episode_replay_optional = parser_competitions_episode_replay._action_groups.pop() - parser_competitions_episode_replay_optional.add_argument("episode_id", type=int, help="Episode ID") + parser_competitions_episode_replay_optional.add_argument( + "episode_id", type=int, + help='Episode ID (find these with "kaggle competitions episodes ")' + ) parser_competitions_episode_replay_optional.add_argument( "-p", "--path", dest="path", required=False, help=Help.param_downfolder ) @@ -311,8 +317,14 @@ def parse_competitions(subparsers) -> None: "episode-logs", formatter_class=argparse.RawTextHelpFormatter, help=Help.command_competitions_episode_logs ) parser_competitions_episode_logs_optional = parser_competitions_episode_logs._action_groups.pop() - parser_competitions_episode_logs_optional.add_argument("episode_id", type=int, help="Episode ID") - parser_competitions_episode_logs_optional.add_argument("agent_index", type=int, help="Agent index") + parser_competitions_episode_logs_optional.add_argument( + "episode_id", type=int, + help='Episode ID (find these with "kaggle competitions episodes ")' + ) + parser_competitions_episode_logs_optional.add_argument( + "agent_index", type=int, + help="Agent index (0-based position of the agent in the episode)" + ) parser_competitions_episode_logs_optional.add_argument( "-p", "--path", dest="path", required=False, help=Help.param_downfolder ) From 7c31cc728807c0ebaba64490f0c6cd3067e52358 Mon Sep 17 00:00:00 2001 From: Bovard Doerschuk-Tiberi Date: Mon, 13 Apr 2026 22:33:27 +0000 Subject: [PATCH 5/7] Add --page-name filter to competitions pages command Allows fetching a single page by name instead of all pages, e.g. `kaggle competitions pages titanic --page-name rules`. --- src/kaggle/api/kaggle_api_extended.py | 11 ++++++++--- src/kaggle/cli.py | 4 ++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/kaggle/api/kaggle_api_extended.py b/src/kaggle/api/kaggle_api_extended.py index e92f2111..0d05c88d 100644 --- a/src/kaggle/api/kaggle_api_extended.py +++ b/src/kaggle/api/kaggle_api_extended.py @@ -1855,11 +1855,12 @@ def competition_episode_agent_logs_cli(self, episode_id, agent_index, path=None, """ self.competition_episode_agent_logs(episode_id, agent_index, path, quiet) - def competition_list_pages(self, competition: str): + def competition_list_pages(self, competition: str, page_name: Optional[str] = None): """List pages for a competition. Args: competition (str): The competition name. + page_name (Optional[str]): Filter to a specific page by name. Returns: list: A list of ApiCompetitionPage objects. @@ -1867,11 +1868,14 @@ def competition_list_pages(self, competition: str): with self.build_kaggle_client() as kaggle: request = ApiListCompetitionPagesRequest() request.competition_name = competition + if page_name: + request.page_name = page_name response = kaggle.competitions.competition_api_client.list_competition_pages(request) return response.pages def competition_list_pages_cli( - self, competition=None, competition_opt=None, csv_display=False, quiet=False, content=False + self, competition=None, competition_opt=None, csv_display=False, quiet=False, content=False, + page_name=None ): """CLI wrapper for competition_list_pages. @@ -1881,6 +1885,7 @@ def competition_list_pages_cli( csv_display (bool): If True, print CSV instead of table. quiet (bool): Suppress verbose output. content (bool): If True, show full page content. + page_name (Optional[str]): Filter to a specific page by name. """ competition = competition or competition_opt if competition is None: @@ -1891,7 +1896,7 @@ def competition_list_pages_cli( if competition is None: raise ValueError("No competition specified") - pages = self.competition_list_pages(competition) + pages = self.competition_list_pages(competition, page_name=page_name) if pages: fields = ["name", "content"] if content else self.competition_page_fields if csv_display: diff --git a/src/kaggle/cli.py b/src/kaggle/cli.py index 23e9f64f..e13a2fcf 100644 --- a/src/kaggle/cli.py +++ b/src/kaggle/cli.py @@ -352,6 +352,10 @@ def parse_competitions(subparsers) -> None: parser_competitions_pages_optional.add_argument( "--content", dest="content", action="store_true", help="Show full page content" ) + parser_competitions_pages_optional.add_argument( + "--page-name", dest="page_name", required=False, + help='Filter to a specific page (e.g. "description", "rules", "evaluation")' + ) parser_competitions_pages._action_groups.append(parser_competitions_pages_optional) parser_competitions_pages.set_defaults(func=api.competition_list_pages_cli) From 51660803ae6f323a295bb86204cb3ca751f9da11 Mon Sep 17 00:00:00 2001 From: Bovard Doerschuk-Tiberi Date: Wed, 15 Apr 2026 01:43:24 +0000 Subject: [PATCH 6/7] Address PR review feedback - Rename episode-replay/episode-logs to replay/logs for shorter typing - Add pages tests to test_commands.sh - Clarify how to identify simulation competitions in docs - Use -p flag instead of mkdir+cd for downloads in docs --- docs/simulation_competitions.md | 27 ++++++++++++--------------- src/kaggle/api/kaggle_api_extended.py | 4 ++-- src/kaggle/cli.py | 6 +++--- tests/test_commands.sh | 4 ++++ 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/docs/simulation_competitions.md b/docs/simulation_competitions.md index a8cf5c8d..0ac45177 100644 --- a/docs/simulation_competitions.md +++ b/docs/simulation_competitions.md @@ -2,14 +2,14 @@ This tutorial walks you through interacting with a Kaggle simulation competition using the CLI — from finding the competition to downloading episode replays and agent logs. -Simulation competitions (e.g., [Lux AI](https://www.kaggle.com/competitions/lux-ai-season-3), [Santa](https://www.kaggle.com/competitions/santa-2024)) differ from standard competitions. Instead of submitting a CSV of predictions, you submit an agent (code) that plays against other agents in episodes. Each episode contains multiple agents competing against each other. +Simulation competitions (e.g., [Lux AI](https://www.kaggle.com/competitions/lux-ai-season-3), [Santa](https://www.kaggle.com/competitions/santa-2024)) differ from standard competitions. Instead of submitting a CSV of predictions, you submit an agent (code) that plays against other agents in episodes. Each episode contains multiple agents competing against each other. You can identify simulation competitions on the [competitions page](https://www.kaggle.com/competitions) by their "Simulation" tag, or by looking for competitions that mention agents, bots, or game environments in their description. ## 1. Find and Inspect the Competition -List available competitions and look for simulation competitions: +Search for simulation competitions by keyword: ```bash -kaggle competitions list --sort-by latestDeadline +kaggle competitions list -s simulation ``` Once you've identified a competition (e.g., `lux-ai-season-3`), view its pages to read the rules, evaluation criteria, and other details: @@ -39,9 +39,7 @@ kaggle competitions list --group entered Download the competition's starter kit and any provided data: ```bash -mkdir lux-ai -cd lux-ai -kaggle competitions download lux-ai-season-3 +kaggle competitions download lux-ai-season-3 -p lux-ai ``` ## 4. Submit Your Agent @@ -98,13 +96,13 @@ kaggle competitions episodes 12345678 -v To download the replay data for a specific episode (useful for visualizing what happened): ```bash -kaggle competitions episode-replay 98765432 +kaggle competitions replay 98765432 ``` This downloads the replay JSON to your current directory as `episode-98765432-replay.json`. To specify a download location: ```bash -kaggle competitions episode-replay 98765432 -p ./replays +kaggle competitions replay 98765432 -p ./replays ``` ## 8. Download Agent Logs @@ -113,10 +111,10 @@ To debug your agent's behavior, download the logs for a specific agent in an epi ```bash # Download logs for the first agent (index 0) -kaggle competitions episode-logs 98765432 0 +kaggle competitions logs 98765432 0 # Download logs for the second agent (index 1) -kaggle competitions episode-logs 98765432 1 -p ./logs +kaggle competitions logs 98765432 1 -p ./logs ``` This downloads the log file as `episode-98765432-agent-0-logs.json`. @@ -126,9 +124,8 @@ This downloads the log file as `episode-98765432-agent-0-logs.json`. Here's a typical workflow for iterating on a simulation competition agent: ```bash -# Set up -mkdir my-sim-comp && cd my-sim-comp -kaggle competitions download lux-ai-season-3 +# Download competition data +kaggle competitions download lux-ai-season-3 -p lux-ai # Submit your agent (single file) kaggle competitions submit lux-ai-season-3 -f main.py -m "v1" @@ -140,8 +137,8 @@ kaggle competitions submissions lux-ai-season-3 kaggle competitions episodes 12345678 # Download replay and logs for an episode -kaggle competitions episode-replay 98765432 -kaggle competitions episode-logs 98765432 0 +kaggle competitions replay 98765432 +kaggle competitions logs 98765432 0 # Check the leaderboard kaggle competitions leaderboard lux-ai-season-3 -s diff --git a/src/kaggle/api/kaggle_api_extended.py b/src/kaggle/api/kaggle_api_extended.py index 0d05c88d..558b9e2a 100644 --- a/src/kaggle/api/kaggle_api_extended.py +++ b/src/kaggle/api/kaggle_api_extended.py @@ -1782,8 +1782,8 @@ def competition_list_episodes_cli(self, submission_id, csv_display=False, quiet= self.print_table(episodes, self.episode_fields) if not quiet: print( - '\nUse "kaggle competitions episode-replay " to download a replay, ' - 'or "kaggle competitions episode-logs " for agent logs.' + '\nUse "kaggle competitions replay " to download a replay, ' + 'or "kaggle competitions logs " for agent logs.' ) else: print("No episodes found") diff --git a/src/kaggle/cli.py b/src/kaggle/cli.py index e13a2fcf..cda2f870 100644 --- a/src/kaggle/cli.py +++ b/src/kaggle/cli.py @@ -296,7 +296,7 @@ def parse_competitions(subparsers) -> None: # Competitions episode replay parser_competitions_episode_replay = subparsers_competitions.add_parser( - "episode-replay", formatter_class=argparse.RawTextHelpFormatter, help=Help.command_competitions_episode_replay + "replay", formatter_class=argparse.RawTextHelpFormatter, help=Help.command_competitions_episode_replay ) parser_competitions_episode_replay_optional = parser_competitions_episode_replay._action_groups.pop() parser_competitions_episode_replay_optional.add_argument( @@ -314,7 +314,7 @@ def parse_competitions(subparsers) -> None: # Competitions episode agent logs parser_competitions_episode_logs = subparsers_competitions.add_parser( - "episode-logs", formatter_class=argparse.RawTextHelpFormatter, help=Help.command_competitions_episode_logs + "logs", formatter_class=argparse.RawTextHelpFormatter, help=Help.command_competitions_episode_logs ) parser_competitions_episode_logs_optional = parser_competitions_episode_logs._action_groups.pop() parser_competitions_episode_logs_optional.add_argument( @@ -1201,7 +1201,7 @@ class Help(object): ] competitions_choices = [ "list", "files", "download", "submit", "submissions", "leaderboard", - "episodes", "episode-replay", "episode-logs", "pages", + "episodes", "replay", "logs", "pages", ] datasets_choices = ["list", "files", "download", "create", "version", "init", "metadata", "status", "delete"] kernels_choices = ["list", "files", "get", "init", "push", "pull", "output", "status", "update", "delete"] diff --git a/tests/test_commands.sh b/tests/test_commands.sh index f15a1ebe..39f7ce89 100755 --- a/tests/test_commands.sh +++ b/tests/test_commands.sh @@ -24,6 +24,10 @@ kaggle c submissions house-prices-advanced-regression-techniques -v -q echo "kaggle competitions leaderboard" kaggle c leaderboard titanic -v -q -d -p leaders kaggle c leaderboard titanic -s > leaderboard.txt +echo "kaggle competitions pages" +kaggle c pages titanic +kaggle c pages titanic -v -q +kaggle c pages titanic --page-name rules --content rm -r titanic.zip tost sample_submission.csv leaders leaderboard.txt echo "kaggle kernels list" From 11a187177e7ffb0170339f49b10369bbc95602ce Mon Sep 17 00:00:00 2001 From: Bovard Doerschuk-Tiberi Date: Wed, 15 Apr 2026 16:29:45 +0000 Subject: [PATCH 7/7] Use connectx in simulation tutorial examples (evergreen competition) --- docs/simulation_competitions.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/simulation_competitions.md b/docs/simulation_competitions.md index 0ac45177..922f9e34 100644 --- a/docs/simulation_competitions.md +++ b/docs/simulation_competitions.md @@ -2,7 +2,7 @@ This tutorial walks you through interacting with a Kaggle simulation competition using the CLI — from finding the competition to downloading episode replays and agent logs. -Simulation competitions (e.g., [Lux AI](https://www.kaggle.com/competitions/lux-ai-season-3), [Santa](https://www.kaggle.com/competitions/santa-2024)) differ from standard competitions. Instead of submitting a CSV of predictions, you submit an agent (code) that plays against other agents in episodes. Each episode contains multiple agents competing against each other. You can identify simulation competitions on the [competitions page](https://www.kaggle.com/competitions) by their "Simulation" tag, or by looking for competitions that mention agents, bots, or game environments in their description. +Simulation competitions (e.g., [Connect X](https://www.kaggle.com/competitions/connectx), [Lux AI](https://www.kaggle.com/competitions/lux-ai-season-3)) differ from standard competitions. Instead of submitting a CSV of predictions, you submit an agent (code) that plays against other agents in episodes. Each episode contains multiple agents competing against each other. You can identify simulation competitions on the [competitions page](https://www.kaggle.com/competitions) by their "Simulation" tag, or by looking for competitions that mention agents, bots, or game environments in their description. ## 1. Find and Inspect the Competition @@ -12,21 +12,21 @@ Search for simulation competitions by keyword: kaggle competitions list -s simulation ``` -Once you've identified a competition (e.g., `lux-ai-season-3`), view its pages to read the rules, evaluation criteria, and other details: +Once you've identified a competition (e.g., `connectx`), view its pages to read the rules, evaluation criteria, and other details: ```bash -kaggle competitions pages lux-ai-season-3 +kaggle competitions pages connectx ``` This lists the available pages (e.g., `description`, `rules`, `evaluation`, `data-description`). To read the full content of a page: ```bash -kaggle competitions pages lux-ai-season-3 --content +kaggle competitions pages connectx --content ``` ## 2. Accept the Competition Rules -Before you can submit or download data, you **must** accept the competition rules on the Kaggle website. Navigate to the competition page (e.g., `https://www.kaggle.com/competitions/lux-ai-season-3`) and click "Join Competition" or "I Understand and Accept". +Before you can submit or download data, you **must** accept the competition rules on the Kaggle website. Navigate to the competition page (e.g., `https://www.kaggle.com/competitions/connectx`) and click "Join Competition" or "I Understand and Accept". You can verify you've joined by checking your entered competitions: @@ -39,7 +39,7 @@ kaggle competitions list --group entered Download the competition's starter kit and any provided data: ```bash -kaggle competitions download lux-ai-season-3 -p lux-ai +kaggle competitions download connectx -p connectx-data ``` ## 4. Submit Your Agent @@ -49,20 +49,20 @@ Simulation competitions require you to submit agent code. You can upload files d **Single file agent** — if your agent is a single `main.py`: ```bash -kaggle competitions submit lux-ai-season-3 -f main.py -m "Single file agent v1" +kaggle competitions submit connectx -f main.py -m "Single file agent v1" ``` **Multi-file agent** — if your agent spans multiple files, bundle them into a `submission.tar.gz` with `main.py` at the root: ```bash tar -czf submission.tar.gz main.py helper.py model_weights.pkl -kaggle competitions submit lux-ai-season-3 -f submission.tar.gz -m "Multi-file agent v1" +kaggle competitions submit connectx -f submission.tar.gz -m "Multi-file agent v1" ``` **Notebook submission** — alternatively, you can submit via an existing Kaggle notebook: ```bash -kaggle competitions submit lux-ai-season-3 -k YOUR_USERNAME/lux-ai-agent -f submission.tar.gz -v 1 -m "Notebook agent v1" +kaggle competitions submit connectx -k YOUR_USERNAME/connectx-agent -f submission.tar.gz -v 1 -m "Notebook agent v1" ``` ## 5. Monitor Your Submission @@ -70,7 +70,7 @@ kaggle competitions submit lux-ai-season-3 -k YOUR_USERNAME/lux-ai-agent -f subm Check the status of your submissions: ```bash -kaggle competitions submissions lux-ai-season-3 +kaggle competitions submissions connectx ``` Note the submission ID from the output — you'll need it to view episodes. @@ -125,13 +125,13 @@ Here's a typical workflow for iterating on a simulation competition agent: ```bash # Download competition data -kaggle competitions download lux-ai-season-3 -p lux-ai +kaggle competitions download connectx -p connectx-data # Submit your agent (single file) -kaggle competitions submit lux-ai-season-3 -f main.py -m "v1" +kaggle competitions submit connectx -f main.py -m "v1" # Check submission status -kaggle competitions submissions lux-ai-season-3 +kaggle competitions submissions connectx # List episodes (replace with your submission ID) kaggle competitions episodes 12345678 @@ -141,5 +141,5 @@ kaggle competitions replay 98765432 kaggle competitions logs 98765432 0 # Check the leaderboard -kaggle competitions leaderboard lux-ai-season-3 -s +kaggle competitions leaderboard connectx -s ```