ap-client 0.5.1.dev4__tar.gz → 0.5.2.dev0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ap_client-0.5.1.dev4 → ap_client-0.5.2.dev0}/PKG-INFO +4 -3
- {ap_client-0.5.1.dev4 → ap_client-0.5.2.dev0}/ap_client/api.py +12 -4
- {ap_client-0.5.1.dev4 → ap_client-0.5.2.dev0}/ap_client/cli.py +186 -25
- {ap_client-0.5.1.dev4 → ap_client-0.5.2.dev0}/ap_client/dataset_commands.py +53 -27
- {ap_client-0.5.1.dev4 → ap_client-0.5.2.dev0}/ap_client/dataset_register.py +53 -6
- ap_client-0.5.2.dev0/ap_client/exporter.py +987 -0
- {ap_client-0.5.1.dev4 → ap_client-0.5.2.dev0}/ap_client/instance_commands.py +467 -44
- ap_client-0.5.2.dev0/ap_client/instance_options.py +94 -0
- {ap_client-0.5.1.dev4 → ap_client-0.5.2.dev0}/ap_client/irepo_sdk.py +151 -37
- ap_client-0.5.2.dev0/ap_client/managed_ak.py +177 -0
- ap_client-0.5.2.dev0/ap_client/manifest_view.py +105 -0
- ap_client-0.5.2.dev0/ap_client/repo_read_guard.py +45 -0
- ap_client-0.5.2.dev0/ap_client/share_api.py +229 -0
- {ap_client-0.5.1.dev4 → ap_client-0.5.2.dev0}/pyproject.toml +4 -3
- ap_client-0.5.1.dev4/ap_client/exporter.py +0 -934
- ap_client-0.5.1.dev4/ap_client/managed_ak.py +0 -86
- {ap_client-0.5.1.dev4 → ap_client-0.5.2.dev0}/ap_client/__init__.py +0 -0
- {ap_client-0.5.1.dev4 → ap_client-0.5.2.dev0}/ap_client/akpool_commands.py +0 -0
- {ap_client-0.5.1.dev4 → ap_client-0.5.2.dev0}/ap_client/config.py +0 -0
- {ap_client-0.5.1.dev4 → ap_client-0.5.2.dev0}/ap_client/fs_commands.py +0 -0
- {ap_client-0.5.1.dev4 → ap_client-0.5.2.dev0}/ap_client/profile_commands.py +0 -0
- {ap_client-0.5.1.dev4 → ap_client-0.5.2.dev0}/ap_client/split_publish.py +0 -0
- {ap_client-0.5.1.dev4 → ap_client-0.5.2.dev0}/ap_client/tbb.py +0 -0
- {ap_client-0.5.1.dev4 → ap_client-0.5.2.dev0}/ap_client/waiter.py +0 -0
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: ap-client
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.2.dev0
|
|
4
4
|
Summary: Agent Platform API Client & CLI
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
|
+
Requires-Dist: irepo-artifact==0.2.1
|
|
6
7
|
Requires-Dist: pyyaml>=6.0
|
|
7
8
|
Requires-Dist: requests>=2.28.0
|
|
8
9
|
Requires-Dist: rich>=13.0.0
|
|
9
10
|
Requires-Dist: typer>=0.9.0
|
|
10
11
|
Requires-Dist: websockets>=13.0
|
|
11
12
|
Provides-Extra: all
|
|
12
|
-
Requires-Dist: instance-repo[oss]
|
|
13
|
+
Requires-Dist: instance-repo[oss]<2.0.0,>=1.3.5.dev1; extra == 'all'
|
|
13
14
|
Provides-Extra: dataset
|
|
14
|
-
Requires-Dist: instance-repo[oss]
|
|
15
|
+
Requires-Dist: instance-repo[oss]<2.0.0,>=1.3.5.dev1; extra == 'dataset'
|
|
15
16
|
Description-Content-Type: text/markdown
|
|
16
17
|
|
|
17
18
|
A lightweight Python SDK and command line interface for Agent Platform. It provides helpers for configuring API access and managing templates, datasets, jobs, and groups.
|
|
@@ -539,11 +539,11 @@ class APIClient:
|
|
|
539
539
|
|
|
540
540
|
def _post(self, path: str, data: Any, timeout: TimeoutType = None) -> Any:
|
|
541
541
|
"""Send a POST request."""
|
|
542
|
-
from .managed_ak import
|
|
542
|
+
from .managed_ak import parameter_ak_usage, require_parameter_ak_ack
|
|
543
543
|
|
|
544
|
-
references =
|
|
544
|
+
references, selectors = parameter_ak_usage(data) if path == "/jobs" else (set(), set())
|
|
545
545
|
result = self._request("POST", path, json_body=data, timeout=timeout)
|
|
546
|
-
require_parameter_ak_ack(result, references)
|
|
546
|
+
require_parameter_ak_ack(result, references, selectors)
|
|
547
547
|
return result
|
|
548
548
|
|
|
549
549
|
def _delete(self, path: str, *, params: Optional[dict] = None) -> Any:
|
|
@@ -709,13 +709,16 @@ class APIClient:
|
|
|
709
709
|
request_id: str,
|
|
710
710
|
duration_ms: float,
|
|
711
711
|
exc: Exception,
|
|
712
|
+
*,
|
|
713
|
+
error_detail: str | None = None,
|
|
712
714
|
) -> None:
|
|
715
|
+
detail = str(exc) if error_detail is None else error_detail
|
|
713
716
|
_write_stderr(
|
|
714
717
|
[
|
|
715
718
|
f"!! Error {method} {url}",
|
|
716
719
|
f" request_id={request_id}",
|
|
717
720
|
f" duration_ms={duration_ms:.1f}",
|
|
718
|
-
f" error={type(exc).__name__}: {
|
|
721
|
+
f" error={type(exc).__name__}: {detail}",
|
|
719
722
|
]
|
|
720
723
|
)
|
|
721
724
|
|
|
@@ -1878,9 +1881,12 @@ class APIClient:
|
|
|
1878
1881
|
meta_job_type: Optional[str] = None,
|
|
1879
1882
|
meta_teacher_direction: Optional[str] = None,
|
|
1880
1883
|
meta_train_stage: Optional[str] = None,
|
|
1884
|
+
benchmark: Optional[str] = None,
|
|
1881
1885
|
) -> dict:
|
|
1882
1886
|
"""Update group properties.
|
|
1883
1887
|
|
|
1888
|
+
``benchmark`` is omitted when None; pass an empty string to clear it.
|
|
1889
|
+
|
|
1884
1890
|
``tags``(`key:value` 数组)在客户端先与 Group 现有 tags 合并,再以
|
|
1885
1891
|
全量替换语义提交(服务端 PATCH 是整体替换);同名 key 覆盖,未提及
|
|
1886
1892
|
的 key 保留。服务端会从 tags 同步 meta_* 等派生字段。
|
|
@@ -1903,6 +1909,8 @@ class APIClient:
|
|
|
1903
1909
|
body["model"] = model
|
|
1904
1910
|
if scaffold is not None:
|
|
1905
1911
|
body["scaffold"] = scaffold
|
|
1912
|
+
if benchmark is not None:
|
|
1913
|
+
body["benchmark"] = benchmark
|
|
1906
1914
|
meta_attr_tags = _merge_meta_tags(
|
|
1907
1915
|
None,
|
|
1908
1916
|
meta_model=meta_model,
|
|
@@ -25,7 +25,7 @@ from ap_client.api import (
|
|
|
25
25
|
)
|
|
26
26
|
from ap_client.config import ENV_VAR_SPECS, ConfigurationError, _parse_bool, normalize_output_format
|
|
27
27
|
from ap_client.dataset_commands import register as _register_dataset_commands
|
|
28
|
-
from ap_client.exporter import export_group, export_job
|
|
28
|
+
from ap_client.exporter import export_group, export_job, export_jobs
|
|
29
29
|
from ap_client.fs_commands import register as _register_fs_commands
|
|
30
30
|
from ap_client.instance_commands import register as _register_instance_commands
|
|
31
31
|
from ap_client.managed_ak import (
|
|
@@ -359,6 +359,22 @@ def _emit_error(message: str) -> None:
|
|
|
359
359
|
error_console.print(f"[red]error: {message}[/]")
|
|
360
360
|
|
|
361
361
|
|
|
362
|
+
def _read_client(share_token: Optional[str] = None, group_id: Optional[str] = None):
|
|
363
|
+
if group_id and not share_token:
|
|
364
|
+
raise typer.BadParameter("--group-id requires --share-token")
|
|
365
|
+
client = get_client()
|
|
366
|
+
if share_token is None:
|
|
367
|
+
return client
|
|
368
|
+
from ap_client.share_api import ShareAPIClient
|
|
369
|
+
|
|
370
|
+
# Typer verbose tracebacks must not render the share token in locals.
|
|
371
|
+
app.pretty_exceptions_show_locals = False
|
|
372
|
+
try:
|
|
373
|
+
return ShareAPIClient.from_client(client, share_token, group_id=group_id)
|
|
374
|
+
except ValueError as exc:
|
|
375
|
+
raise typer.BadParameter(str(exc)) from exc
|
|
376
|
+
|
|
377
|
+
|
|
362
378
|
def _reject_account_pool_template_credential_cli(
|
|
363
379
|
account_pool: Optional[str],
|
|
364
380
|
*credential_types: Any,
|
|
@@ -3564,8 +3580,8 @@ def job_create(
|
|
|
3564
3580
|
if dry_run:
|
|
3565
3581
|
_emit_dry_run_warnings(dry_run_warnings)
|
|
3566
3582
|
body["dry_run"] = True
|
|
3567
|
-
result = client._post("/jobs", body, timeout=timeout)
|
|
3568
3583
|
try:
|
|
3584
|
+
result = client._post("/jobs", body, timeout=timeout)
|
|
3569
3585
|
require_managed_ak_ack(result, ak_id)
|
|
3570
3586
|
except ManagedAKProtocolError as exc:
|
|
3571
3587
|
_emit_error(str(exc))
|
|
@@ -4427,6 +4443,9 @@ def job_create(
|
|
|
4427
4443
|
@job_app.command("get")
|
|
4428
4444
|
def job_get(
|
|
4429
4445
|
job_id: str = typer.Argument(..., help="Job ID"),
|
|
4446
|
+
share_token: Optional[str] = typer.Option(
|
|
4447
|
+
None, "--share-token", help="Read with a share token"
|
|
4448
|
+
),
|
|
4430
4449
|
include_params: bool = typer.Option(
|
|
4431
4450
|
False,
|
|
4432
4451
|
"--include-params",
|
|
@@ -4463,7 +4482,7 @@ def job_get(
|
|
|
4463
4482
|
include_spec = include_spec or _field_path_requires_spec(field)
|
|
4464
4483
|
if field == "job_spec_data.spec_yaml":
|
|
4465
4484
|
spec_format = "yaml"
|
|
4466
|
-
client =
|
|
4485
|
+
client = _read_client(share_token)
|
|
4467
4486
|
result = client.get_job(
|
|
4468
4487
|
job_id,
|
|
4469
4488
|
include_params=include_params,
|
|
@@ -4489,6 +4508,9 @@ def job_get(
|
|
|
4489
4508
|
@job_app.command("containers")
|
|
4490
4509
|
def job_containers(
|
|
4491
4510
|
job_id: str = typer.Argument(..., help="Job ID"),
|
|
4511
|
+
share_token: Optional[str] = typer.Option(
|
|
4512
|
+
None, "--share-token", help="Read with a share token"
|
|
4513
|
+
),
|
|
4492
4514
|
output_format: str = typer.Option(
|
|
4493
4515
|
None,
|
|
4494
4516
|
"--format",
|
|
@@ -4497,7 +4519,7 @@ def job_containers(
|
|
|
4497
4519
|
):
|
|
4498
4520
|
"""List container names for a job."""
|
|
4499
4521
|
output_format = _normalize_output_format(output_format, keep_table=True)
|
|
4500
|
-
client =
|
|
4522
|
+
client = _read_client(share_token)
|
|
4501
4523
|
result = client.get_job_containers(job_id)
|
|
4502
4524
|
if output_format == "table":
|
|
4503
4525
|
_print_records_table(result)
|
|
@@ -4559,6 +4581,9 @@ def _print_tail_logs(logs, tail: int) -> None:
|
|
|
4559
4581
|
@job_app.command("logs")
|
|
4560
4582
|
def job_logs(
|
|
4561
4583
|
job_id: str = typer.Argument(..., help="Job ID"),
|
|
4584
|
+
share_token: Optional[str] = typer.Option(
|
|
4585
|
+
None, "--share-token", help="Read with a share token"
|
|
4586
|
+
),
|
|
4562
4587
|
container: Optional[str] = typer.Option("main", "--container", "-c", help="Container name"),
|
|
4563
4588
|
tail: Optional[int] = typer.Option(None, "--tail", "-n", help="Show only the last N log lines"),
|
|
4564
4589
|
follow: bool = typer.Option(
|
|
@@ -4570,7 +4595,7 @@ def job_logs(
|
|
|
4570
4595
|
),
|
|
4571
4596
|
):
|
|
4572
4597
|
"""Show job logs."""
|
|
4573
|
-
client =
|
|
4598
|
+
client = _read_client(share_token)
|
|
4574
4599
|
|
|
4575
4600
|
if tail is not None:
|
|
4576
4601
|
if tail <= 0:
|
|
@@ -4634,6 +4659,9 @@ def job_logs(
|
|
|
4634
4659
|
@job_app.command("events")
|
|
4635
4660
|
def job_events(
|
|
4636
4661
|
job_id: str = typer.Argument(..., help="Job ID"),
|
|
4662
|
+
share_token: Optional[str] = typer.Option(
|
|
4663
|
+
None, "--share-token", help="Read with a share token"
|
|
4664
|
+
),
|
|
4637
4665
|
container: Optional[str] = typer.Option(None, "--container", "-c", help="Container name"),
|
|
4638
4666
|
output_format: str = typer.Option(
|
|
4639
4667
|
None,
|
|
@@ -4643,7 +4671,7 @@ def job_events(
|
|
|
4643
4671
|
):
|
|
4644
4672
|
"""Show job events."""
|
|
4645
4673
|
output_format = _normalize_output_format(output_format)
|
|
4646
|
-
client =
|
|
4674
|
+
client = _read_client(share_token)
|
|
4647
4675
|
result = client.get_job_events(job_id, container=container)
|
|
4648
4676
|
if output_format == "plain":
|
|
4649
4677
|
_print_job_events_plain(result)
|
|
@@ -4654,6 +4682,9 @@ def job_events(
|
|
|
4654
4682
|
@job_app.command("metrics")
|
|
4655
4683
|
def job_metrics(
|
|
4656
4684
|
job_id: str = typer.Argument(..., help="Job ID"),
|
|
4685
|
+
share_token: Optional[str] = typer.Option(
|
|
4686
|
+
None, "--share-token", help="Read with a share token"
|
|
4687
|
+
),
|
|
4657
4688
|
output_format: str = typer.Option(
|
|
4658
4689
|
None,
|
|
4659
4690
|
"--format",
|
|
@@ -4662,7 +4693,7 @@ def job_metrics(
|
|
|
4662
4693
|
):
|
|
4663
4694
|
"""Show job metrics."""
|
|
4664
4695
|
output_format = _normalize_output_format(output_format)
|
|
4665
|
-
client =
|
|
4696
|
+
client = _read_client(share_token)
|
|
4666
4697
|
result = client.get_job_metrics(job_id)
|
|
4667
4698
|
if output_format == "plain":
|
|
4668
4699
|
_print_json(result)
|
|
@@ -4859,6 +4890,9 @@ def job_retry(
|
|
|
4859
4890
|
@job_app.command("artifacts")
|
|
4860
4891
|
def job_artifacts(
|
|
4861
4892
|
job_ids: str = typer.Argument(..., help="Job IDs (comma-separated for multiple)"),
|
|
4893
|
+
share_token: Optional[str] = typer.Option(
|
|
4894
|
+
None, "--share-token", help="Read with a share token"
|
|
4895
|
+
),
|
|
4862
4896
|
output_format: str = typer.Option(
|
|
4863
4897
|
None,
|
|
4864
4898
|
"--format",
|
|
@@ -4867,7 +4901,7 @@ def job_artifacts(
|
|
|
4867
4901
|
):
|
|
4868
4902
|
"""Show job artifact download links."""
|
|
4869
4903
|
output_format = _normalize_output_format(output_format, keep_table=True)
|
|
4870
|
-
client =
|
|
4904
|
+
client = _read_client(share_token)
|
|
4871
4905
|
ids = [jid.strip() for jid in job_ids.split(",")]
|
|
4872
4906
|
results = client.get_job_artifacts(ids)
|
|
4873
4907
|
if output_format == "table":
|
|
@@ -4876,10 +4910,50 @@ def job_artifacts(
|
|
|
4876
4910
|
_print_formatted(results, output_format)
|
|
4877
4911
|
|
|
4878
4912
|
|
|
4913
|
+
def _validate_export_timeout(value: float) -> float:
|
|
4914
|
+
import math
|
|
4915
|
+
|
|
4916
|
+
if not math.isfinite(value) or value <= 0:
|
|
4917
|
+
raise typer.BadParameter("Timeout must be a finite number greater than zero")
|
|
4918
|
+
return value
|
|
4919
|
+
|
|
4920
|
+
|
|
4921
|
+
def _export_channel_reporter(emit):
|
|
4922
|
+
"""Report completed Jobs, including callbacks from Group worker threads."""
|
|
4923
|
+
from threading import Lock
|
|
4924
|
+
|
|
4925
|
+
from rich.text import Text
|
|
4926
|
+
|
|
4927
|
+
counts = {"oss": 0, "proxy": 0}
|
|
4928
|
+
lock = Lock()
|
|
4929
|
+
|
|
4930
|
+
def report(job_id, status, channel):
|
|
4931
|
+
with lock:
|
|
4932
|
+
suffix = ""
|
|
4933
|
+
if status == "downloaded" and channel in counts:
|
|
4934
|
+
counts[channel] += 1
|
|
4935
|
+
suffix = f" channel={channel}"
|
|
4936
|
+
emit(Text(f"{job_id} {status}{suffix}"))
|
|
4937
|
+
|
|
4938
|
+
return report, counts
|
|
4939
|
+
|
|
4940
|
+
|
|
4879
4941
|
@job_app.command("export")
|
|
4880
4942
|
def job_export(
|
|
4881
|
-
job_id: str = typer.Argument(..., help="Job ID"),
|
|
4943
|
+
job_id: str = typer.Argument(..., help="Job ID or comma-separated Job IDs"),
|
|
4944
|
+
share_token: Optional[str] = typer.Option(
|
|
4945
|
+
None, "--share-token", help="Export with a share token"
|
|
4946
|
+
),
|
|
4882
4947
|
output: Optional[Path] = typer.Option(None, "--output", "-o", help="Export directory"),
|
|
4948
|
+
workers: int = typer.Option(
|
|
4949
|
+
1, "--workers", "-w", min=1, help="Number of concurrent export workers"
|
|
4950
|
+
),
|
|
4951
|
+
timeout: float = typer.Option(
|
|
4952
|
+
300,
|
|
4953
|
+
"--timeout",
|
|
4954
|
+
callback=_validate_export_timeout,
|
|
4955
|
+
help="SDK request timeout in seconds, not the total export duration",
|
|
4956
|
+
),
|
|
4883
4957
|
logs: bool = typer.Option(False, "--logs", help="Include container logs"),
|
|
4884
4958
|
events: bool = typer.Option(False, "--events", help="Include events"),
|
|
4885
4959
|
no_extract_artifacts: bool = typer.Option(
|
|
@@ -4889,17 +4963,50 @@ def job_export(
|
|
|
4889
4963
|
),
|
|
4890
4964
|
):
|
|
4891
4965
|
"""Export job artifacts to a local directory. Use --logs/--events to include logs and events."""
|
|
4892
|
-
|
|
4966
|
+
ids = list(dict.fromkeys(value.strip() for value in job_id.split(",")))
|
|
4967
|
+
if not all(ids):
|
|
4968
|
+
raise typer.BadParameter("Job IDs must not be empty", param_hint="job_id")
|
|
4969
|
+
client = _read_client(share_token)
|
|
4970
|
+
if len(ids) > 1:
|
|
4971
|
+
report, channels = _export_channel_reporter(print)
|
|
4972
|
+
dest, summary = export_jobs(
|
|
4973
|
+
client,
|
|
4974
|
+
ids,
|
|
4975
|
+
output,
|
|
4976
|
+
workers=workers,
|
|
4977
|
+
result_callback=report,
|
|
4978
|
+
timeout=timeout,
|
|
4979
|
+
include_logs=logs,
|
|
4980
|
+
include_events=events,
|
|
4981
|
+
extract_artifacts=not no_extract_artifacts,
|
|
4982
|
+
)
|
|
4983
|
+
print(
|
|
4984
|
+
f"Jobs exported: downloaded={summary.downloaded} "
|
|
4985
|
+
f"skipped={summary.skipped} failed={len(summary.failures)}"
|
|
4986
|
+
)
|
|
4987
|
+
print(f"channels: oss={channels['oss']} proxy={channels['proxy']}")
|
|
4988
|
+
print(f" path: {dest}")
|
|
4989
|
+
for failed_id, error in summary.failures.items():
|
|
4990
|
+
print(f"[red]Failed job {failed_id}:[/] {error}")
|
|
4991
|
+
if summary.failures:
|
|
4992
|
+
raise typer.Exit(code=1)
|
|
4993
|
+
return
|
|
4994
|
+
job_id = ids[0]
|
|
4995
|
+
result = {}
|
|
4893
4996
|
dest = export_job(
|
|
4894
4997
|
client,
|
|
4895
4998
|
job_id,
|
|
4896
4999
|
output,
|
|
5000
|
+
result_callback=lambda jid, status, channel: result.update(status=status, channel=channel),
|
|
5001
|
+
timeout=timeout,
|
|
4897
5002
|
include_logs=logs,
|
|
4898
5003
|
include_events=events,
|
|
4899
5004
|
extract_artifacts=not no_extract_artifacts,
|
|
4900
5005
|
)
|
|
4901
5006
|
print(f"[green]Job exported:[/] {job_id}")
|
|
4902
5007
|
print(f" path: {dest}")
|
|
5008
|
+
if result.get("channel") in {"oss", "proxy"} and result.get("status") == "downloaded":
|
|
5009
|
+
print(f" channel: {result['channel']}")
|
|
4903
5010
|
|
|
4904
5011
|
|
|
4905
5012
|
@job_app.command("wait")
|
|
@@ -5674,6 +5781,9 @@ def group_list(
|
|
|
5674
5781
|
@group_app.command("get")
|
|
5675
5782
|
def group_get(
|
|
5676
5783
|
group_id: str = typer.Argument(..., help="Group ID"),
|
|
5784
|
+
share_token: Optional[str] = typer.Option(
|
|
5785
|
+
None, "--share-token", help="Read with a share token"
|
|
5786
|
+
),
|
|
5677
5787
|
output_format: str = typer.Option(
|
|
5678
5788
|
None,
|
|
5679
5789
|
"--format",
|
|
@@ -5682,7 +5792,7 @@ def group_get(
|
|
|
5682
5792
|
):
|
|
5683
5793
|
"""Show Group details."""
|
|
5684
5794
|
output_format = _normalize_output_format(output_format, keep_table=True)
|
|
5685
|
-
client =
|
|
5795
|
+
client = _read_client(share_token, group_id if share_token else None)
|
|
5686
5796
|
result = client.get_group(group_id)
|
|
5687
5797
|
if output_format == "table":
|
|
5688
5798
|
_print_detail(result)
|
|
@@ -5956,6 +6066,9 @@ def group_update(
|
|
|
5956
6066
|
scaffold: Optional[str] = typer.Option(
|
|
5957
6067
|
None, "--scaffold", "-s", help="Scaffold/agent name (benchmark perspective)"
|
|
5958
6068
|
),
|
|
6069
|
+
benchmark: Optional[str] = typer.Option(
|
|
6070
|
+
None, "--benchmark", help="Benchmark name; pass an empty string to clear"
|
|
6071
|
+
),
|
|
5959
6072
|
tags: Optional[str] = typer.Option(
|
|
5960
6073
|
None,
|
|
5961
6074
|
"--tags",
|
|
@@ -6017,6 +6130,7 @@ def group_update(
|
|
|
6017
6130
|
dataset,
|
|
6018
6131
|
model_name,
|
|
6019
6132
|
scaffold,
|
|
6133
|
+
benchmark,
|
|
6020
6134
|
tags_list,
|
|
6021
6135
|
)
|
|
6022
6136
|
):
|
|
@@ -6031,6 +6145,7 @@ def group_update(
|
|
|
6031
6145
|
dataset=dataset,
|
|
6032
6146
|
model=model_name,
|
|
6033
6147
|
scaffold=scaffold,
|
|
6148
|
+
**({"benchmark": benchmark} if benchmark is not None else {}),
|
|
6034
6149
|
**({"tags": tags_list} if tags_list is not None else {}),
|
|
6035
6150
|
)
|
|
6036
6151
|
if output_format == "plain":
|
|
@@ -6045,6 +6160,7 @@ def group_update(
|
|
|
6045
6160
|
"dataset",
|
|
6046
6161
|
"model",
|
|
6047
6162
|
"scaffold",
|
|
6163
|
+
"benchmark",
|
|
6048
6164
|
"updated_at",
|
|
6049
6165
|
],
|
|
6050
6166
|
)
|
|
@@ -6162,6 +6278,9 @@ def group_resume(
|
|
|
6162
6278
|
@group_app.command("jobs")
|
|
6163
6279
|
def group_jobs(
|
|
6164
6280
|
group_id: str = typer.Argument(..., help="Group ID"),
|
|
6281
|
+
share_token: Optional[str] = typer.Option(
|
|
6282
|
+
None, "--share-token", help="Read with a share token"
|
|
6283
|
+
),
|
|
6165
6284
|
tag: Optional[list[str]] = typer.Option(
|
|
6166
6285
|
None, "--tag", help="Filter by tag (repeatable; multiple tags are combined with AND)"
|
|
6167
6286
|
),
|
|
@@ -6200,17 +6319,36 @@ def group_jobs(
|
|
|
6200
6319
|
if cursor and skip > 0:
|
|
6201
6320
|
raise typer.BadParameter("--cursor cannot be used with --skip greater than 0")
|
|
6202
6321
|
cursor_request = bool(next_token) or cursor
|
|
6203
|
-
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
6322
|
+
if share_token is not None:
|
|
6323
|
+
unsupported = (
|
|
6324
|
+
("--tag", bool(tag)),
|
|
6325
|
+
("--include-post-process", include_post_process),
|
|
6326
|
+
("--next-token", bool(next_token)),
|
|
6327
|
+
("--cursor", cursor),
|
|
6328
|
+
("--no-total", no_total),
|
|
6329
|
+
)
|
|
6330
|
+
for option, used in unsupported:
|
|
6331
|
+
if used:
|
|
6332
|
+
_emit_error(f"{option} is not supported with --share-token")
|
|
6333
|
+
raise typer.Exit(1)
|
|
6334
|
+
if skip < 0 or not 1 <= limit <= 500:
|
|
6335
|
+
raise typer.BadParameter(
|
|
6336
|
+
"share pagination requires --skip >= 0 and 1 <= --limit <= 500"
|
|
6337
|
+
)
|
|
6338
|
+
client = _read_client(share_token, group_id if share_token else None)
|
|
6339
|
+
result = client.list_jobs(group_id=group_id, skip=skip, limit=limit)
|
|
6340
|
+
else:
|
|
6341
|
+
client = get_client()
|
|
6342
|
+
result = client.list_group_jobs(
|
|
6343
|
+
group_id,
|
|
6344
|
+
tag=tag,
|
|
6345
|
+
include_post_process=include_post_process,
|
|
6346
|
+
skip=skip,
|
|
6347
|
+
limit=limit,
|
|
6348
|
+
**({"next_token": next_token} if next_token else {}),
|
|
6349
|
+
**({"pagination": "cursor"} if cursor_request else {}),
|
|
6350
|
+
include_total=not no_total,
|
|
6351
|
+
)
|
|
6214
6352
|
if output_format == "table":
|
|
6215
6353
|
_print_job_list_plain(result, rich=True)
|
|
6216
6354
|
elif output_format == "plain":
|
|
@@ -6225,6 +6363,9 @@ def group_jobs(
|
|
|
6225
6363
|
@group_app.command("stats")
|
|
6226
6364
|
def group_stats(
|
|
6227
6365
|
group_id: str = typer.Argument(..., help="Group ID"),
|
|
6366
|
+
share_token: Optional[str] = typer.Option(
|
|
6367
|
+
None, "--share-token", help="Read with a share token"
|
|
6368
|
+
),
|
|
6228
6369
|
output_format: str = typer.Option(
|
|
6229
6370
|
None,
|
|
6230
6371
|
"--format",
|
|
@@ -6233,7 +6374,7 @@ def group_stats(
|
|
|
6233
6374
|
):
|
|
6234
6375
|
"""Show Group progress."""
|
|
6235
6376
|
output_format = _normalize_output_format(output_format, keep_table=True)
|
|
6236
|
-
client =
|
|
6377
|
+
client = _read_client(share_token, group_id if share_token else None)
|
|
6237
6378
|
result = client.get_group_stats(group_id)
|
|
6238
6379
|
if output_format == "table":
|
|
6239
6380
|
_print_detail(result)
|
|
@@ -6244,6 +6385,9 @@ def group_stats(
|
|
|
6244
6385
|
@group_app.command("eval")
|
|
6245
6386
|
def group_eval(
|
|
6246
6387
|
group_id: str = typer.Argument(..., help="Group ID"),
|
|
6388
|
+
share_token: Optional[str] = typer.Option(
|
|
6389
|
+
None, "--share-token", help="Read with a share token"
|
|
6390
|
+
),
|
|
6247
6391
|
summary_only: bool = typer.Option(
|
|
6248
6392
|
False,
|
|
6249
6393
|
"--summary-only",
|
|
@@ -6257,7 +6401,7 @@ def group_eval(
|
|
|
6257
6401
|
):
|
|
6258
6402
|
"""Show Group aggregated evaluation results."""
|
|
6259
6403
|
output_format = _normalize_output_format(output_format)
|
|
6260
|
-
client =
|
|
6404
|
+
client = _read_client(share_token, group_id if share_token else None)
|
|
6261
6405
|
result = (
|
|
6262
6406
|
client.get_group_eval_summary(group_id) if summary_only else client.get_group_eval(group_id)
|
|
6263
6407
|
)
|
|
@@ -6370,10 +6514,19 @@ def group_access_list(group_id: str = typer.Argument(..., help="Group ID")):
|
|
|
6370
6514
|
@group_app.command("export")
|
|
6371
6515
|
def group_export(
|
|
6372
6516
|
group_id: str = typer.Argument(..., help="Group ID"),
|
|
6517
|
+
share_token: Optional[str] = typer.Option(
|
|
6518
|
+
None, "--share-token", help="Export with a share token"
|
|
6519
|
+
),
|
|
6373
6520
|
output: Optional[Path] = typer.Option(None, "--output", "-o", help="Export directory"),
|
|
6374
6521
|
workers: int = typer.Option(
|
|
6375
6522
|
4, "--workers", "-w", min=1, help="Number of concurrent export workers"
|
|
6376
6523
|
),
|
|
6524
|
+
timeout: float = typer.Option(
|
|
6525
|
+
300,
|
|
6526
|
+
"--timeout",
|
|
6527
|
+
callback=_validate_export_timeout,
|
|
6528
|
+
help="SDK request timeout in seconds, not the total export duration",
|
|
6529
|
+
),
|
|
6377
6530
|
logs: bool = typer.Option(False, "--logs", help="Include container logs"),
|
|
6378
6531
|
events: bool = typer.Option(False, "--events", help="Include events"),
|
|
6379
6532
|
no_extract_artifacts: bool = typer.Option(
|
|
@@ -6405,7 +6558,11 @@ def group_export(
|
|
|
6405
6558
|
_emit_error("--include-files/--exclude-files cannot be used with --no-extract-artifacts")
|
|
6406
6559
|
raise typer.Exit(1)
|
|
6407
6560
|
|
|
6408
|
-
|
|
6561
|
+
if share_token is not None and (include_file_selectors or exclude_file_selectors):
|
|
6562
|
+
_emit_error("--include-files/--exclude-files are not supported with --share-token")
|
|
6563
|
+
raise typer.Exit(1)
|
|
6564
|
+
|
|
6565
|
+
client = _read_client(share_token, group_id if share_token else None)
|
|
6409
6566
|
from rich.progress import BarColumn, Progress, TaskProgressColumn, TextColumn
|
|
6410
6567
|
|
|
6411
6568
|
with Progress(
|
|
@@ -6434,12 +6591,15 @@ def group_export(
|
|
|
6434
6591
|
current_stage=current_stage,
|
|
6435
6592
|
)
|
|
6436
6593
|
|
|
6594
|
+
report, channels = _export_channel_reporter(progress.console.print)
|
|
6437
6595
|
dest, summary = export_group(
|
|
6438
6596
|
client,
|
|
6439
6597
|
group_id,
|
|
6440
6598
|
output,
|
|
6441
6599
|
progress_callback=_on_progress,
|
|
6442
6600
|
workers=workers,
|
|
6601
|
+
result_callback=report,
|
|
6602
|
+
timeout=timeout,
|
|
6443
6603
|
include_logs=logs,
|
|
6444
6604
|
include_events=events,
|
|
6445
6605
|
extract_artifacts=not no_extract_artifacts,
|
|
@@ -6452,6 +6612,7 @@ def group_export(
|
|
|
6452
6612
|
f" [green]{summary.downloaded}[/] jobs downloaded, "
|
|
6453
6613
|
f"[yellow]{summary.skipped}[/] jobs skipped (no artifact)"
|
|
6454
6614
|
)
|
|
6615
|
+
print(f"channels: oss={channels['oss']} proxy={channels['proxy']}")
|
|
6455
6616
|
if summary.skipped_job_names:
|
|
6456
6617
|
print(f" Skipped: {', '.join(summary.skipped_job_names)}")
|
|
6457
6618
|
|