Skip to content

Commit 10d5e9f

Browse files
authored
Merge pull request #2049 from transformerlab/add/cli-task-list-subtype-filter
add --subtype filter to `lab task list`
2 parents add2092 + f7f7f23 commit 10d5e9f

4 files changed

Lines changed: 66 additions & 7 deletions

File tree

cli/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "transformerlab-cli"
7-
version = "0.0.54"
7+
version = "0.0.55"
88
description = "Transformer Lab CLI"
99
requires-python = ">=3.10"
1010
authors = [{ name = "Transformer Lab", email = "hello@transformerlab.ai" }]

cli/src/transformerlab_cli/commands/task.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,18 @@
2424
REQUIRED_TASK_FIELDS = ["name", "type"]
2525

2626

27+
KNOWN_TASK_SUBTYPES = ["interactive"]
28+
29+
30+
def _validate_subtype(value: str | None) -> str | None:
31+
if value is None:
32+
return None
33+
normalized = value.lower()
34+
if normalized not in KNOWN_TASK_SUBTYPES:
35+
raise typer.BadParameter(f"Allowed: {', '.join(KNOWN_TASK_SUBTYPES)}")
36+
return normalized
37+
38+
2739
def _resolve_experiment_id(experiment_id: str | None = None) -> str:
2840
"""Resolve experiment from command override or configured default."""
2941
if experiment_id is not None and str(experiment_id).strip():
@@ -78,13 +90,18 @@ def _extract_error_detail(response: httpx.Response) -> str:
7890
return str(detail)
7991

8092

81-
def list_tasks(output_format: str = "pretty", experiment_id: str = "alpha") -> None:
82-
"""List all REMOTE tasks."""
93+
def list_tasks(output_format: str = "pretty", experiment_id: str = "alpha", subtype: str | None = None) -> None:
94+
"""List all REMOTE tasks, optionally filtered by subtype (e.g. 'interactive')."""
95+
if subtype:
96+
endpoint = f"/experiment/{experiment_id}/task/list_by_subtype_in_experiment?subtype={subtype}&type=REMOTE"
97+
else:
98+
endpoint = f"/experiment/{experiment_id}/task/list_by_type_in_experiment?type=REMOTE"
99+
83100
if output_format != "json":
84101
with console.status("[bold success]Fetching tasks...[/bold success]", spinner="dots"):
85-
response = api.get(f"/experiment/{experiment_id}/task/list_by_type_in_experiment?type=REMOTE")
102+
response = api.get(endpoint)
86103
else:
87-
response = api.get(f"/experiment/{experiment_id}/task/list_by_type_in_experiment?type=REMOTE")
104+
response = api.get(endpoint)
88105

89106
if response.status_code == 200:
90107
tasks = response.json()
@@ -573,10 +590,16 @@ def _validate_task_yaml_file(
573590
@app.command("list")
574591
def command_task_list(
575592
experiment: str | None = typer.Option(None, "--experiment", "-e", help="Override experiment for this command"),
593+
subtype: str | None = typer.Option(
594+
None,
595+
"--subtype",
596+
callback=_validate_subtype,
597+
help=f"Filter to only tasks with this subtype. Allowed: {', '.join(KNOWN_TASK_SUBTYPES)}.",
598+
),
576599
):
577600
"""List all tasks."""
578601
current_experiment = _resolve_experiment_id(experiment)
579-
list_tasks(output_format=cli_state.output_format, experiment_id=current_experiment)
602+
list_tasks(output_format=cli_state.output_format, experiment_id=current_experiment, subtype=subtype)
580603

581604

582605
TASK_INIT_TEMPLATES_DIR = Path(__file__).resolve().parent.parent / "templates" / "task_init"

cli/tests/commands/test_task.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,42 @@ def test_task_list_json_no_spinner(_mock_exp, _mock_api):
8181
json.loads(result.output.strip()) # must not raise
8282

8383

84+
@patch("transformerlab_cli.commands.task.api.get", return_value=_mock_resp(SAMPLE_TASKS))
85+
@patch("transformerlab_cli.commands.task.require_current_experiment", return_value="exp1")
86+
def test_task_list_no_subtype_hits_list_by_type(_mock_exp, mock_api_get):
87+
"""Without --subtype, list calls list_by_type_in_experiment."""
88+
result = runner.invoke(app, ["--format", "json", "task", "list"])
89+
assert result.exit_code == 0
90+
called_url = mock_api_get.call_args.args[0]
91+
assert "list_by_type_in_experiment" in called_url
92+
assert "type=REMOTE" in called_url
93+
assert "subtype=" not in called_url
94+
95+
96+
@patch("transformerlab_cli.commands.task.api.get", return_value=_mock_resp(SAMPLE_TASKS))
97+
@patch("transformerlab_cli.commands.task.require_current_experiment", return_value="exp1")
98+
def test_task_list_with_subtype_hits_list_by_subtype(_mock_exp, mock_api_get):
99+
"""--subtype interactive routes to list_by_subtype_in_experiment with the right query params."""
100+
result = runner.invoke(app, ["--format", "json", "task", "list", "--subtype", "interactive"])
101+
assert result.exit_code == 0
102+
called_url = mock_api_get.call_args.args[0]
103+
assert "list_by_subtype_in_experiment" in called_url
104+
assert "subtype=interactive" in called_url
105+
assert "type=REMOTE" in called_url
106+
107+
108+
@patch("transformerlab_cli.commands.task.api.get", return_value=_mock_resp(SAMPLE_TASKS))
109+
@patch("transformerlab_cli.commands.task.require_current_experiment", return_value="exp1")
110+
def test_task_list_rejects_unknown_subtype(_mock_exp, mock_api_get):
111+
"""--subtype <unknown> exits non-zero with an Allowed: ... error and does not hit the server."""
112+
result = runner.invoke(app, ["task", "list", "--subtype", "cuckoo"])
113+
assert result.exit_code != 0
114+
out = _cli_output(result)
115+
assert "Invalid value for '--subtype'" in out
116+
assert "Allowed: interactive" in out
117+
mock_api_get.assert_not_called()
118+
119+
84120
def test_build_launch_payload_includes_description():
85121
"""build_launch_payload forwards the description to the launch API body."""
86122
task = {"id": "t1", "name": "finetune", "experiment_id": "exp1", "run": "python main.py"}

cli/uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)