envloop-cli 1.29.0__tar.gz → 1.30.0__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.
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/PKG-INFO +3 -3
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/README.pypi.md +2 -2
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/envloop/auth_cli.py +3 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/envloop/auth_login.py +4 -1
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/envloop/cli_errors.py +7 -3
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/envloop/lifecycle_cli.py +3 -1
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/data/envloop_data/data_cli.py +6 -2
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/data/envloop_data/resource_cli.py +7 -6
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/cli.py +8 -1
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/train_log_cli.py +4 -1
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/train_metrics_cli.py +4 -3
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/pyproject.toml +2 -2
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/.gitignore +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/envloop/__init__.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/envloop/_optional.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/envloop/auth_workspace.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/envloop/cli.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/envloop/cli_output.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/envloop/client.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/envloop/config.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/envloop/downloads.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/envloop/http.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/envloop/navigation_resources.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/envloop/profile_secrets.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/envloop/resource_cli.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/envloop/resources.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/envloop/terminal.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/envloop/utils/__init__.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/envloop/utils/logging.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/data/envloop_data/__init__.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/data/envloop_data/cli.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/data/envloop_data/cli_output.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/data/envloop_data/client.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/data/envloop_data/data.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/data/envloop_data/data_publications.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/data/envloop_data/data_upload.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/data/envloop_data/http.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/data/envloop_data/navigation_resources.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/data/envloop_data/profile_updates.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/data/envloop_data/recipes.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/data/envloop_data/runner_packages.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/__init__.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/cli_output.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/client.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/decision.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/http.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/inference.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/job_cost.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/job_logs.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/job_spec_cli.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/navigation_resources.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/resource_cli.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/resources.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/runtime_params.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/sandbox.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/train.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/train_progress.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/train_runner_cli.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/training/__init__.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/rsi-loop/envloop_rsi_loop/__init__.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/rsi-loop/envloop_rsi_loop/agent_runs.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/rsi-loop/envloop_rsi_loop/cli.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/rsi-loop/envloop_rsi_loop/cli_output.py +0 -0
- {envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/rsi-loop/envloop_rsi_loop/rsi_cli.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: envloop-cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.30.0
|
|
4
4
|
Summary: EnvLoop CLI and Python SDK for jobs, training, checkpoints and inference
|
|
5
5
|
Project-URL: Homepage, https://envloop.ai
|
|
6
6
|
Project-URL: Documentation, https://docs.envloop.ai
|
|
@@ -249,7 +249,7 @@ The platform starts the published Revision’s `main.sh` / `main.py` using the R
|
|
|
249
249
|
CLI output uses compact timestamps and highlights statuses and log levels by default on terminals.
|
|
250
250
|
Use `el --no-color job list` or append `--no-color` to a command to disable highlighting, including errors.
|
|
251
251
|
`NO_COLOR=1`, `TERM=dumb`, pipes, and redirected output also disable colors. JSON preserves original values.
|
|
252
|
-
List times
|
|
252
|
+
List and table times are UTC with a `UTC` label; Job logs retain the recorded timezone and mark lines collected only after the Job ended with `(backfilled)`. Full precision, offsets and the `backfilled` field are available through `--json`.
|
|
253
253
|
`el job run live RUN --follow --json` emits complete response frames as NDJSON; Ctrl-C stops reading without stopping the Run.
|
|
254
254
|
|
|
255
255
|
## Job-bound decision plugins
|
|
@@ -283,7 +283,7 @@ download a complete registered ANS object directory through EnvHub. Datasets req
|
|
|
283
283
|
authorization the SDK uses GET URLs valid for up to five minutes, verifies file size/SHA-256,
|
|
284
284
|
and installs the directory atomically. Matching platform support is required.
|
|
285
285
|
|
|
286
|
-
A compatible server
|
|
286
|
+
A compatible server is required. Data uses the permission of the resource it touches: read (for example `datasets.read`) to query and download, write to publish and upload, delete to retire; a key from `el auth login` follows your project role. The current login selects the base URL, workspace and project; there is no `--env` flag. After configuring the Key using `el auth config`:
|
|
287
287
|
|
|
288
288
|
```bash
|
|
289
289
|
el data list --type dataset --json
|
|
@@ -232,7 +232,7 @@ The platform starts the published Revision’s `main.sh` / `main.py` using the R
|
|
|
232
232
|
CLI output uses compact timestamps and highlights statuses and log levels by default on terminals.
|
|
233
233
|
Use `el --no-color job list` or append `--no-color` to a command to disable highlighting, including errors.
|
|
234
234
|
`NO_COLOR=1`, `TERM=dumb`, pipes, and redirected output also disable colors. JSON preserves original values.
|
|
235
|
-
List times
|
|
235
|
+
List and table times are UTC with a `UTC` label; Job logs retain the recorded timezone and mark lines collected only after the Job ended with `(backfilled)`. Full precision, offsets and the `backfilled` field are available through `--json`.
|
|
236
236
|
`el job run live RUN --follow --json` emits complete response frames as NDJSON; Ctrl-C stops reading without stopping the Run.
|
|
237
237
|
|
|
238
238
|
## Job-bound decision plugins
|
|
@@ -266,7 +266,7 @@ download a complete registered ANS object directory through EnvHub. Datasets req
|
|
|
266
266
|
authorization the SDK uses GET URLs valid for up to five minutes, verifies file size/SHA-256,
|
|
267
267
|
and installs the directory atomically. Matching platform support is required.
|
|
268
268
|
|
|
269
|
-
A compatible server
|
|
269
|
+
A compatible server is required. Data uses the permission of the resource it touches: read (for example `datasets.read`) to query and download, write to publish and upload, delete to retire; a key from `el auth login` follows your project role. The current login selects the base URL, workspace and project; there is no `--env` flag. After configuring the Key using `el auth config`:
|
|
270
270
|
|
|
271
271
|
```bash
|
|
272
272
|
el data list --type dataset --json
|
|
@@ -20,6 +20,9 @@ def add_auth_commands(groups: "argparse._SubParsersAction[argparse.ArgumentParse
|
|
|
20
20
|
login.add_argument("--no-browser", action="store_true", help="Print the login link without opening a browser")
|
|
21
21
|
login.add_argument("--timeout-seconds", type=int, choices=range(1, 601), metavar="SECONDS", default=300,
|
|
22
22
|
help="Wait up to 1–600 seconds (default: 300)")
|
|
23
|
+
login.add_argument("--read-only", action="store_true",
|
|
24
|
+
help="Issue a read-only key, for example for CI that only reads results. "
|
|
25
|
+
"Without it the key follows your project role")
|
|
23
26
|
configure = commands.add_parser("config", help="Save Base URL and API key for CLI and SDK")
|
|
24
27
|
configure.add_argument("--no-check", action="store_true", help="Save without API verification (offline; key remains unverified)")
|
|
25
28
|
actions = configure.add_subparsers(dest="config_action")
|
|
@@ -76,7 +76,8 @@ def login(args: argparse.Namespace, store: Any) -> int:
|
|
|
76
76
|
def digest(value: str) -> str:
|
|
77
77
|
return hashlib.sha256(value.encode()).hexdigest()
|
|
78
78
|
response = _request(url, 'start', {'key_hash': digest(key), 'poll_hash': digest(poll_token),
|
|
79
|
-
'code_hash': digest(code)
|
|
79
|
+
'code_hash': digest(code), **({'read_only': True} if args.read_only else {})},
|
|
80
|
+
min(5, args.timeout_seconds))
|
|
80
81
|
identifier, uri = _verification_url(url, response)
|
|
81
82
|
interval, expires = response.get('interval'), response.get('expires_in')
|
|
82
83
|
if (isinstance(interval, bool) or not isinstance(interval, (int, float)) or not 0 < interval <= 10 or
|
|
@@ -88,6 +89,8 @@ def login(args: argparse.Namespace, store: Any) -> int:
|
|
|
88
89
|
try:
|
|
89
90
|
print(f'Base URL: {url}\nConfirmation code: {code}\nOpen: {uri}', flush=True)
|
|
90
91
|
print('Sign in with Clerk, enter this code, and authorize your workspace/project.', flush=True)
|
|
92
|
+
print('The key will be read only.' if args.read_only else
|
|
93
|
+
'The key follows your project role. The page lists its permissions before you authorize.', flush=True)
|
|
91
94
|
if not args.no_browser:
|
|
92
95
|
try:
|
|
93
96
|
opened = webbrowser.open(uri, new=1)
|
|
@@ -27,10 +27,10 @@ def _register(codes: str, message: str) -> None:
|
|
|
27
27
|
_MESSAGES[code] = message
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
_register("
|
|
30
|
+
_register("DATA_ACCESS_REQUIRED", "This key lacks the permission named above on the Dataset, Runner, Profile, Checkpoint or Trial artifact. Run el auth login to issue a new key that follows your project role: every role can read, and Developer or Admin can also write and delete (create, update, delete, upload, runner register). A key from el auth login --read-only cannot write; log in again without --read-only. Or give a key Read or Edit on that page in the Console under CONFIGURE > API Keys. Existing keys never gain permissions.")
|
|
31
31
|
_register("DATA_NATIVE_INCOMPLETE DATA_NATIVE_UNAVAILABLE", "Native catalog registration is incomplete. Retry the same input after the platform is ready.")
|
|
32
32
|
_register("DATA_DEPENDENCIES_ACTIVE DATA_DEPENDENCIES_UNKNOWN DATA_DEPENDENCIES_CHANGED DATA_DELETE_CONFLICT", "Retirement requires a complete, current dependency check. Resolve active or unknown dependencies, then retry the same resource.")
|
|
33
|
-
_register("DATA_PUBLICATION_INVALID", "Check the immutable ANS URI, native manifest and exact file sizes and SHA-256 values.")
|
|
33
|
+
_register("DATA_PUBLICATION_INVALID", "Check the immutable ANS URI, native manifest and exact file sizes and SHA-256 values. A record Dataset source is https://github.com/OWNER/REPO or https://huggingface.co/datasets/ORG/NAME with the full commit, not a branch or tag.")
|
|
34
34
|
_register("DATA_PIN_CONFLICT DATA_REVISION_REQUIRED", "Publish a new immutable revision; existing and revoked references cannot be overwritten.")
|
|
35
35
|
_register("DATA_QUOTA_EXCEEDED DATA_QUOTA_UNCONFIGURED", "Ask a project administrator to configure publication storage quota. Revoking publications does not free retained content.")
|
|
36
36
|
_register("DATA_UPLOAD_CONFLICT DATA_UPLOAD_INCOMPLETE DATA_UPLOAD_RATE_LIMITED", "Inspect the upload with data upload get; resume matching parts or explicitly abort it.")
|
|
@@ -329,7 +329,11 @@ def upload_recovery(error: BaseException) -> str:
|
|
|
329
329
|
if isinstance(upload_id, str) and re.fullmatch(r'upload_[a-f0-9]{32}', upload_id):
|
|
330
330
|
lines.extend([f' Upload retained: {upload_id}', f' Inspect with: el data upload get {upload_id}'])
|
|
331
331
|
generated = getattr(error, 'generated_idempotency_key', None)
|
|
332
|
-
|
|
332
|
+
# A definite refusal (permission, validation, not found) fails the same way again; offer the key only
|
|
333
|
+
# when the outcome is unknown or retryable.
|
|
334
|
+
status = getattr(error, 'status_code', None)
|
|
335
|
+
retryable = not isinstance(status, int) or status >= 500 or status in {408, 429}
|
|
336
|
+
if retryable and isinstance(generated, str) and re.fullmatch(r'[a-f0-9]{32}', generated):
|
|
333
337
|
lines.append(f' Retry the same input with --idempotency-key {generated}')
|
|
334
338
|
return '\n'.join(lines)
|
|
335
339
|
|
|
@@ -18,7 +18,9 @@ def add_management_commands(settings: "argparse._SubParsersAction[argparse.Argum
|
|
|
18
18
|
if action == 'wait':
|
|
19
19
|
command.add_argument('--timeout-seconds', type=float, default=600)
|
|
20
20
|
cleanup = settings.add_parser('cleanup', help='Clean an explicit set of Project resource IDs')
|
|
21
|
-
cleanup.add_argument('--file', type=Path, required=True,
|
|
21
|
+
cleanup.add_argument('--file', type=Path, required=True,
|
|
22
|
+
help='JSON array of {"kind": KIND, "id": ID} objects; KIND is job (a Train Job), endpoint, adapter '
|
|
23
|
+
'or checkpoint, for example [{"kind": "job", "id": "job_..."}]')
|
|
22
24
|
cleanup.add_argument('--request-id', required=True)
|
|
23
25
|
wait_options(cleanup)
|
|
24
26
|
scopes = settings.add_parser('resource-scope', help='Manage temporary resources with server TTL').add_subparsers(dest='scope_action', required=True)
|
|
@@ -83,15 +83,19 @@ def add_commands(groups: "argparse._SubParsersAction[argparse.ArgumentParser]",
|
|
|
83
83
|
command.add_argument('--json', action='store_true')
|
|
84
84
|
|
|
85
85
|
|
|
86
|
+
MANIFEST_HELP = ('Native JSON content manifest; a record Dataset source.repository is https://github.com/OWNER/REPO '
|
|
87
|
+
'or https://huggingface.co/datasets/ORG/NAME, with source.commit the full commit')
|
|
88
|
+
|
|
89
|
+
|
|
86
90
|
def publication_arguments(command: argparse.ArgumentParser, *, source: bool = False):
|
|
87
91
|
command.add_argument('--ref', required=not source, help='Exact immutable ANS URI')
|
|
88
92
|
if source:
|
|
89
93
|
inputs = command.add_mutually_exclusive_group(required=True)
|
|
90
|
-
inputs.add_argument('--manifest', type=Path, help=
|
|
94
|
+
inputs.add_argument('--manifest', type=Path, help=MANIFEST_HELP)
|
|
91
95
|
inputs.add_argument('--model', help='Public Hugging Face model namespace/name; metadata only')
|
|
92
96
|
command.add_argument('--revision', help='Explicit HF source ref; the server freezes its full commit')
|
|
93
97
|
else:
|
|
94
|
-
command.add_argument('--manifest', type=Path, required=True, help=
|
|
98
|
+
command.add_argument('--manifest', type=Path, required=True, help=MANIFEST_HELP)
|
|
95
99
|
command.add_argument('--directory', type=Path, required=not source, help='Directory containing exactly the declared files')
|
|
96
100
|
command.add_argument('--executable', action='append', dest='executables', default=[],
|
|
97
101
|
help='Declared Runner file that must be executable; repeat for each path')
|
|
@@ -33,8 +33,8 @@ def add_resource_commands(groups: "argparse._SubParsersAction[argparse.ArgumentP
|
|
|
33
33
|
'immutable Runner revision NAME@REVISION. The platform stores the bytes, computes the package and index '
|
|
34
34
|
'SHA-256, creates the Runner on first use and registers the revision; identical retries are idempotent and '
|
|
35
35
|
'changed content for an existing revision is refused. This is el data create --type runner with the native '
|
|
36
|
-
'manifest built for you; use that command to publish an existing manifest. The key needs
|
|
37
|
-
'
|
|
36
|
+
'manifest built for you; use that command to publish an existing manifest. The key needs runners.write '
|
|
37
|
+
'(el auth login issues it to a Developer or Admin), and publish permission on a Runner owned by another Project.')
|
|
38
38
|
register.add_argument('directory', metavar='DIRECTORY', type=Path, help='Runner package directory; every file in it is published')
|
|
39
39
|
register.add_argument('--revision', required=True, help='Full 40- or 64-character source commit, for example $(git rev-parse HEAD)')
|
|
40
40
|
register.add_argument('--name', help='NAMESPACE/NAME; must equal [runner] name in runner.toml (default: that name)')
|
|
@@ -88,9 +88,10 @@ def execute(args: argparse.Namespace, client: Any):
|
|
|
88
88
|
cursor=args.cursor, limit=args.limit)
|
|
89
89
|
|
|
90
90
|
|
|
91
|
-
RUNNER_WRITE_HINT = ('Registering a Runner revision needs a project key with
|
|
92
|
-
'
|
|
93
|
-
'
|
|
91
|
+
RUNNER_WRITE_HINT = ('Registering a Runner revision needs a project key with Runners write (runners.write), and '
|
|
92
|
+
'publish or admin permission when another project owns the Runner. Existing keys keep their '
|
|
93
|
+
'original capabilities; run el auth login without --read-only (Developer or Admin role) to issue '
|
|
94
|
+
'a new key with it.')
|
|
94
95
|
|
|
95
96
|
|
|
96
97
|
def register_runner(client: Any, args: argparse.Namespace):
|
|
@@ -102,7 +103,7 @@ def register_runner(client: Any, args: argparse.Namespace):
|
|
|
102
103
|
executables=args.executables, source_ref=args.source_ref,
|
|
103
104
|
source_ref_kind=args.source_ref_kind, dry_run=args.dry_run)
|
|
104
105
|
except RemoteError as error:
|
|
105
|
-
if error.code
|
|
106
|
+
if error.code in {'ACCESS_DENIED', 'DATA_ACCESS_REQUIRED'} and not error.hint:
|
|
106
107
|
error.hint = RUNNER_WRITE_HINT
|
|
107
108
|
raise
|
|
108
109
|
if not args.dry_run:
|
|
@@ -5,6 +5,9 @@ import sys
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
|
+
# Single-resource reads and actions print the response as JSON already; scripts may still pass --json.
|
|
9
|
+
JSON_DEFAULT_HELP = "Output the complete response as JSON (the default; accepted for consistency with list commands)"
|
|
10
|
+
|
|
8
11
|
|
|
9
12
|
def dataset_reference(value: str):
|
|
10
13
|
name, separator, version = value.rpartition("@")
|
|
@@ -155,6 +158,8 @@ def add_commands(groups: "argparse._SubParsersAction[argparse.ArgumentParser]"):
|
|
|
155
158
|
if action == 'event':
|
|
156
159
|
command.add_argument('--json', action='store_true')
|
|
157
160
|
command.set_defaults(_list_view=True)
|
|
161
|
+
else:
|
|
162
|
+
command.add_argument("--json", action="store_true", help=JSON_DEFAULT_HELP)
|
|
158
163
|
regrade = jobs.add_parser("regrade", help="Re-run only the verifier of sealed compose Trials, optionally with a reward set",
|
|
159
164
|
allow_abbrev=False)
|
|
160
165
|
regrade_source = regrade.add_mutually_exclusive_group(required=True)
|
|
@@ -218,7 +223,9 @@ def add_commands(groups: "argparse._SubParsersAction[argparse.ArgumentParser]"):
|
|
|
218
223
|
snapshot = tasks.add_parser("snapshot")
|
|
219
224
|
snapshot.add_argument("trial_id", metavar="TRIAL_ID")
|
|
220
225
|
for action in ("get", "stop", "result"):
|
|
221
|
-
tasks.add_parser(action)
|
|
226
|
+
command = tasks.add_parser(action)
|
|
227
|
+
command.add_argument("trial_id", metavar="TRIAL_ID")
|
|
228
|
+
command.add_argument("--json", action="store_true", help=JSON_DEFAULT_HELP)
|
|
222
229
|
listing.add_argument("--json", action="store_true", help="Output the complete response as JSON")
|
|
223
230
|
artifacts = tasks.add_parser("artifact", help="List or download a Trial's artifacts").add_subparsers(dest="artifact_action", required=True)
|
|
224
231
|
artifact_list = artifacts.add_parser("list", help="List one page of artifacts")
|
{envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/train_log_cli.py
RENAMED
|
@@ -30,10 +30,13 @@ def log_line(item: dict[str, Any], *, color: bool = False):
|
|
|
30
30
|
if item.get("phase"):
|
|
31
31
|
diagnostic += f"; phase={safe_text(item['phase'])}"
|
|
32
32
|
message = f"[{diagnostic}] {message}"
|
|
33
|
+
# The line had no write time and was read after the Job ended: its time is when it was collected.
|
|
34
|
+
backfilled = "(backfilled)" if item.get("backfilled") is True else ""
|
|
33
35
|
if color:
|
|
34
36
|
timestamp = style(timestamp, "2") if timestamp else ""
|
|
37
|
+
backfilled = style(backfilled, "2") if backfilled else ""
|
|
35
38
|
level = status_text(level)
|
|
36
|
-
prefix = " ".join(part for part in (timestamp, level) if part)
|
|
39
|
+
prefix = " ".join(part for part in (timestamp, backfilled, level) if part)
|
|
37
40
|
return f"{prefix} {message}"
|
|
38
41
|
|
|
39
42
|
|
|
@@ -6,7 +6,7 @@ import math
|
|
|
6
6
|
import re
|
|
7
7
|
import sys
|
|
8
8
|
import time
|
|
9
|
-
from datetime import datetime
|
|
9
|
+
from datetime import datetime, timezone
|
|
10
10
|
from typing import Any
|
|
11
11
|
|
|
12
12
|
from envloop.cli_output import display, table
|
|
@@ -20,7 +20,7 @@ FOLLOW_RETRIES = 3
|
|
|
20
20
|
TERMINAL = {"completed", "succeeded", "failed", "cancelled", "SUCCEEDED", "FAILED", "CANCELLED"}
|
|
21
21
|
HEADERS = ("STEP", "LOSS", "LEARNING RATE", "GRAD NORM", "EPOCH", "TOKENS", "SECONDS/STEP", "OBSERVED AT")
|
|
22
22
|
# Column widths of --follow, which prints rows before later ones are known.
|
|
23
|
-
WIDTHS = (6, 10, 13, 10, 8, 12, 12,
|
|
23
|
+
WIDTHS = (6, 10, 13, 10, 8, 12, 12, 23)
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
def _number(value: Any) -> float | None:
|
|
@@ -41,8 +41,9 @@ def _value(value: Any, spec: str) -> str | None:
|
|
|
41
41
|
|
|
42
42
|
|
|
43
43
|
def _observed(value: Any) -> str | None:
|
|
44
|
+
"""``at`` is Unix time; like every table time it is shown in UTC, labelled (--json keeps the number)."""
|
|
44
45
|
number = _number(value)
|
|
45
|
-
return None if number is None else datetime.fromtimestamp(number).strftime("%Y-%m-%d %H:%M:%S")
|
|
46
|
+
return None if number is None else datetime.fromtimestamp(number, timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")
|
|
46
47
|
|
|
47
48
|
|
|
48
49
|
def step_row(point: dict[str, Any], *, model: bool = False) -> tuple[object, ...]:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "envloop-cli"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.30.0"
|
|
4
4
|
description = "EnvLoop CLI and Python SDK for jobs, training, checkpoints and inference"
|
|
5
5
|
requires-python = ">=3.12"
|
|
6
6
|
readme = "README.pypi.md"
|
|
@@ -65,7 +65,7 @@ combine-as-imports = true
|
|
|
65
65
|
known-first-party = ["envloop", "envloop_data", "envloop_experiment", "envloop_rsi_loop"]
|
|
66
66
|
|
|
67
67
|
[tool.uv.sources]
|
|
68
|
-
envloop-envhub = { git = "ssh://git@github.com/EnvLoop/EnvHub.git", rev = "
|
|
68
|
+
envloop-envhub = { git = "ssh://git@github.com/EnvLoop/EnvHub.git", rev = "db09a00dc6cc2962e599b3c8adbab320e647b578" }
|
|
69
69
|
|
|
70
70
|
[tool.pyrefly]
|
|
71
71
|
preset = "strict"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/data/envloop_data/navigation_resources.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/__init__.py
RENAMED
|
File without changes
|
{envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/cli_output.py
RENAMED
|
File without changes
|
|
File without changes
|
{envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/decision.py
RENAMED
|
File without changes
|
|
File without changes
|
{envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/inference.py
RENAMED
|
File without changes
|
{envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/job_cost.py
RENAMED
|
File without changes
|
{envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/job_logs.py
RENAMED
|
File without changes
|
{envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/job_spec_cli.py
RENAMED
|
File without changes
|
|
File without changes
|
{envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/resource_cli.py
RENAMED
|
File without changes
|
{envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/resources.py
RENAMED
|
File without changes
|
{envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/runtime_params.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/train_progress.py
RENAMED
|
File without changes
|
{envloop_cli-1.29.0 → envloop_cli-1.30.0}/packages/experiment/envloop_experiment/train_runner_cli.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|