qualia-cli 0.1.2__tar.gz → 0.2.0rc2__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.
Files changed (34) hide show
  1. {qualia_cli-0.1.2 → qualia_cli-0.2.0rc2}/.gitignore +25 -3
  2. {qualia_cli-0.1.2 → qualia_cli-0.2.0rc2}/PKG-INFO +40 -15
  3. qualia_cli-0.2.0rc2/README.md +80 -0
  4. {qualia_cli-0.1.2 → qualia_cli-0.2.0rc2}/pyproject.toml +12 -3
  5. qualia_cli-0.2.0rc2/qualia-cli.spec.md +40 -0
  6. qualia_cli-0.2.0rc2/src/qualia_cli/__init__.py +3 -0
  7. qualia_cli-0.2.0rc2/src/qualia_cli/client.py +25 -0
  8. {qualia_cli-0.1.2 → qualia_cli-0.2.0rc2}/src/qualia_cli/commands/auth.py +17 -10
  9. qualia_cli-0.2.0rc2/src/qualia_cli/commands/datasets.py +570 -0
  10. {qualia_cli-0.1.2 → qualia_cli-0.2.0rc2}/src/qualia_cli/commands/finetune.py +99 -17
  11. qualia_cli-0.2.0rc2/src/qualia_cli/commands/git.py +105 -0
  12. qualia_cli-0.2.0rc2/src/qualia_cli/config.py +140 -0
  13. {qualia_cli-0.1.2 → qualia_cli-0.2.0rc2}/src/qualia_cli/main.py +20 -3
  14. qualia_cli-0.2.0rc2/tests/commands/__init__.py +0 -0
  15. qualia_cli-0.2.0rc2/tests/commands/test_datasets.py +452 -0
  16. qualia_cli-0.2.0rc2/tests/commands/test_finetune.py +58 -0
  17. qualia_cli-0.2.0rc2/tests/commands/test_git.py +47 -0
  18. qualia_cli-0.2.0rc2/tests/test_client.py +76 -0
  19. qualia_cli-0.2.0rc2/tests/test_config.py +107 -0
  20. qualia_cli-0.2.0rc2/tests/test_main.py +61 -0
  21. qualia_cli-0.2.0rc2/tests/test_sdk_floor.py +85 -0
  22. {qualia_cli-0.1.2 → qualia_cli-0.2.0rc2}/uv.lock +54 -16
  23. qualia_cli-0.1.2/README.md +0 -56
  24. qualia_cli-0.1.2/src/qualia_cli/__init__.py +0 -3
  25. qualia_cli-0.1.2/src/qualia_cli/client.py +0 -29
  26. qualia_cli-0.1.2/src/qualia_cli/commands/datasets.py +0 -85
  27. qualia_cli-0.1.2/src/qualia_cli/config.py +0 -63
  28. {qualia_cli-0.1.2 → qualia_cli-0.2.0rc2}/src/qualia_cli/commands/__init__.py +0 -0
  29. {qualia_cli-0.1.2 → qualia_cli-0.2.0rc2}/src/qualia_cli/commands/credits.py +0 -0
  30. {qualia_cli-0.1.2 → qualia_cli-0.2.0rc2}/src/qualia_cli/commands/instances.py +0 -0
  31. {qualia_cli-0.1.2 → qualia_cli-0.2.0rc2}/src/qualia_cli/commands/models.py +0 -0
  32. {qualia_cli-0.1.2 → qualia_cli-0.2.0rc2}/src/qualia_cli/commands/projects.py +0 -0
  33. {qualia_cli-0.1.2 → qualia_cli-0.2.0rc2}/src/qualia_cli/output.py +0 -0
  34. {qualia_cli-0.1.2 → qualia_cli-0.2.0rc2}/tests/__init__.py +0 -0
@@ -17,6 +17,8 @@ wheels/
17
17
  .coverage
18
18
  .coverage.*
19
19
  htmlcov/
20
+ # Non-secret GitLab job receipts collected by `artifacts:` declarations.
21
+ /artifacts/
20
22
 
21
23
  # virtual envs
22
24
  .venv
@@ -46,7 +48,6 @@ easytier.log
46
48
  reset.sql
47
49
 
48
50
  # generated docs
49
- ui/docs/src/assets/openapi.json
50
51
 
51
52
  # nix
52
53
  .direnv
@@ -58,7 +59,11 @@ result
58
59
 
59
60
  # Windows
60
61
  nul
61
- lerobot
62
+
63
+ # local upstream lerobot checkout (for debugging / vendor comparisons);
64
+ # our ported pipeline_lib_train_lerobot lives at api/pipeline/lib/train/lerobot/
65
+ # and must NOT be ignored.
66
+ /lerobot
62
67
 
63
68
  # inference setup.sh artifact
64
69
  inference/inference/examples/sim/openpi
@@ -89,8 +94,25 @@ dev_access_token.txt
89
94
  .ripgreprc
90
95
  .zprofile
91
96
  .zshrc
97
+ .claude/scheduled_tasks.lock
92
98
  /HEAD
93
99
  /config
94
100
  /hooks
95
101
  /objects
96
- /refs
102
+ /refs
103
+
104
+ # test data etc
105
+ api/qds/benchmarks/*
106
+ # Bench run artifacts. `benchmarks/` above never covered `bench/results/`, which
107
+ # is where the harness actually writes, so 6k lines of logs rode into a commit.
108
+ api/qds/bench/results/
109
+ api/qds/bench/RESULTS.md
110
+ api/qds/tests/validation/data/
111
+ api/qds/tests/quality_vlm_validation/data/
112
+ api/qds/tests/e2e/fixtures/data/
113
+
114
+ # Rust build artifacts
115
+ target/
116
+ **/target/
117
+ *.rlib
118
+ *.rmeta
@@ -1,7 +1,7 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: qualia-cli
3
- Version: 0.1.2
4
- Summary: CLI for the Qualia Studios VLA fine-tuning platform
3
+ Version: 0.2.0rc2
4
+ Summary: CLI for the Qualia robotics data and VLA platform
5
5
  Project-URL: Homepage, https://qualiastudios.dev
6
6
  Project-URL: Documentation, https://docs.qualiastudios.dev
7
7
  Author: Qualia Studios
@@ -16,14 +16,15 @@ Classifier: Programming Language :: Python :: 3.11
16
16
  Classifier: Programming Language :: Python :: 3.12
17
17
  Classifier: Typing :: Typed
18
18
  Requires-Python: >=3.10
19
- Requires-Dist: qualia-sdk>=0.2.0
19
+ Requires-Dist: click>=8.1.0
20
+ Requires-Dist: qualia-sdk>=0.7.0rc2
20
21
  Requires-Dist: rich>=13.0.0
21
22
  Requires-Dist: typer>=0.15.0
22
23
  Description-Content-Type: text/markdown
23
24
 
24
25
  # Qualia CLI
25
26
 
26
- Terminal interface for the [Qualia](https://qualiastudios.dev) VLA fine-tuning platform.
27
+ Terminal interface for the [Qualia](https://qualiastudios.dev) robotics data and VLA platform.
27
28
 
28
29
  ## Install
29
30
 
@@ -37,6 +38,23 @@ Or with [uv](https://docs.astral.sh/uv/):
37
38
  uv tool install qualia-cli
38
39
  ```
39
40
 
41
+ ### Pre-release builds
42
+
43
+ Deployments running ahead of the public release need the matching pre-release
44
+ wheel — the upload protocol is negotiated exactly, so the CLI and the endpoint
45
+ it talks to have to be the same generation. Pin it:
46
+
47
+ ```sh
48
+ pip install 'qualia-cli==0.2.0rc1'
49
+ ```
50
+
51
+ uv does not resolve a pre-release through a dependency, even one that names it,
52
+ so uv needs the flag as well as the pin:
53
+
54
+ ```sh
55
+ uv tool install --prerelease=allow 'qualia-cli==0.2.0rc1'
56
+ ```
57
+
40
58
  ## Quick start
41
59
 
42
60
  ```sh
@@ -46,27 +64,34 @@ export QUALIA_API_KEY="your-api-key" # env var
46
64
  qualia --token <KEY> <command> # inline, for CI/scripts
47
65
 
48
66
  # List available models
49
- qualia models list
67
+ qualia model list
50
68
 
51
69
  # List GPU instances
52
- qualia instances list
70
+ qualia instance list
53
71
 
54
72
  # Create a project
55
- qualia projects create "My Robot"
73
+ qualia project create "My Robot"
56
74
 
57
75
  # Start a fine-tuning job
58
- qualia finetune create \
76
+ qualia job launch \
59
77
  --project-id <id> \
60
78
  --vla-type smolvla \
61
79
  --model-id lerobot/smolvla_base \
62
- --dataset-id lerobot/pusht \
63
- --dataset-last-modified "2025-01-15T10:00:00Z" \
64
- --model-last-modified "2025-01-15T10:00:00Z" \
80
+ --dataset-version-id <DATASET_VERSION_UUID> \
65
81
  --hours 2.0 \
66
82
  --camera-mappings '{"cam_1": "observation.images.top"}'
67
83
 
68
84
  # Check job status
69
- qualia finetune get <job-id>
85
+ qualia job get <job-id>
86
+
87
+ # Import an exact Hugging Face dataset commit. HF_TOKEN is read from the
88
+ # environment or the Hub's protected local credential store, never an argv flag.
89
+ export HF_TOKEN="..." # omit for public datasets
90
+ qualia dataset import-hf \
91
+ qualia-robotics/openarm-cube-in-box-v1 \
92
+ 17c29c487d886878ddcb3c5649101b2860fbc047 \
93
+ openarm-cube-in-box \
94
+ --source-format lerobot_v3
70
95
  ```
71
96
 
72
97
  ## JSON output
@@ -74,6 +99,6 @@ qualia finetune get <job-id>
74
99
  All commands support `--json` for machine-readable output:
75
100
 
76
101
  ```sh
77
- qualia projects list --json
78
- qualia credits --json
102
+ qualia project list --json
103
+ qualia credit --json
79
104
  ```
@@ -0,0 +1,80 @@
1
+ # Qualia CLI
2
+
3
+ Terminal interface for the [Qualia](https://qualiastudios.dev) robotics data and VLA platform.
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ pip install qualia-cli
9
+ ```
10
+
11
+ Or with [uv](https://docs.astral.sh/uv/):
12
+
13
+ ```sh
14
+ uv tool install qualia-cli
15
+ ```
16
+
17
+ ### Pre-release builds
18
+
19
+ Deployments running ahead of the public release need the matching pre-release
20
+ wheel — the upload protocol is negotiated exactly, so the CLI and the endpoint
21
+ it talks to have to be the same generation. Pin it:
22
+
23
+ ```sh
24
+ pip install 'qualia-cli==0.2.0rc1'
25
+ ```
26
+
27
+ uv does not resolve a pre-release through a dependency, even one that names it,
28
+ so uv needs the flag as well as the pin:
29
+
30
+ ```sh
31
+ uv tool install --prerelease=allow 'qualia-cli==0.2.0rc1'
32
+ ```
33
+
34
+ ## Quick start
35
+
36
+ ```sh
37
+ # Authenticate (pick one)
38
+ qualia auth login # interactive, saves to config
39
+ export QUALIA_API_KEY="your-api-key" # env var
40
+ qualia --token <KEY> <command> # inline, for CI/scripts
41
+
42
+ # List available models
43
+ qualia model list
44
+
45
+ # List GPU instances
46
+ qualia instance list
47
+
48
+ # Create a project
49
+ qualia project create "My Robot"
50
+
51
+ # Start a fine-tuning job
52
+ qualia job launch \
53
+ --project-id <id> \
54
+ --vla-type smolvla \
55
+ --model-id lerobot/smolvla_base \
56
+ --dataset-version-id <DATASET_VERSION_UUID> \
57
+ --hours 2.0 \
58
+ --camera-mappings '{"cam_1": "observation.images.top"}'
59
+
60
+ # Check job status
61
+ qualia job get <job-id>
62
+
63
+ # Import an exact Hugging Face dataset commit. HF_TOKEN is read from the
64
+ # environment or the Hub's protected local credential store, never an argv flag.
65
+ export HF_TOKEN="..." # omit for public datasets
66
+ qualia dataset import-hf \
67
+ qualia-robotics/openarm-cube-in-box-v1 \
68
+ 17c29c487d886878ddcb3c5649101b2860fbc047 \
69
+ openarm-cube-in-box \
70
+ --source-format lerobot_v3
71
+ ```
72
+
73
+ ## JSON output
74
+
75
+ All commands support `--json` for machine-readable output:
76
+
77
+ ```sh
78
+ qualia project list --json
79
+ qualia credit --json
80
+ ```
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "qualia-cli"
3
- version = "0.1.2"
4
- description = "CLI for the Qualia Studios VLA fine-tuning platform"
3
+ version = "0.2.0rc2"
4
+ description = "CLI for the Qualia robotics data and VLA platform"
5
5
  readme = "README.md"
6
6
  license = "MIT"
7
7
  requires-python = ">=3.10"
@@ -20,7 +20,11 @@ classifiers = [
20
20
  "Typing :: Typed",
21
21
  ]
22
22
  dependencies = [
23
- "qualia-sdk>=0.2.0",
23
+ "click>=8.1.0",
24
+ # 0.6.8 moved Hugging Face acquisition behind the server and 0.6.9 is the
25
+ # first wheel that speaks upload protocol v5. The gateway refuses older
26
+ # clients at `begin`, before any bytes move.
27
+ "qualia-sdk>=0.7.0rc2",
24
28
  "typer>=0.15.0",
25
29
  "rich>=13.0.0",
26
30
  ]
@@ -75,6 +79,11 @@ ignore = [
75
79
  "E501", # line too long
76
80
  ]
77
81
 
82
+ [tool.ruff.lint.flake8-bugbear]
83
+ # Typer's Option/Argument defaults are the idiomatic way to declare CLI params;
84
+ # they're effectively immutable sentinels, so don't flag them under B008.
85
+ extend-immutable-calls = ["typer.Option", "typer.Argument"]
86
+
78
87
  [tool.pytest.ini_options]
79
88
  testpaths = ["tests"]
80
89
  python_files = ["test_*.py"]
@@ -0,0 +1,40 @@
1
+ ---
2
+ title: Qualia CLI
3
+ category: product
4
+ surface: cli
5
+ capability: launch-finetune
6
+ maturity: review
7
+ verifies:
8
+ - sdk:contract
9
+ summary: The end-user Typer CLI wrapping the Qualia SDK.
10
+ owners: SDK / DX
11
+ updated: 2026-07-22
12
+ ---
13
+
14
+ ## Overview
15
+
16
+ `qualia` is the end-user command-line interface — a Typer app over the Python SDK, giving the same
17
+ `launch-finetune` capability a terminal surface for scripting and CI. Collocated at `sdk/cli`. Not to
18
+ be confused with `qcore`, the internal developer CLI (see [Engineering →
19
+ qcore CLI is internal tooling]); `qualia` is the public, customer-facing one (QUA-633).
20
+
21
+ ## Surface
22
+
23
+ The CLI mirrors the SDK's resource model: authenticate via `QUALIA_API_KEY`, then create and manage
24
+ finetunes, list the bounded QDS catalog, download a published tree, inspect adapter-pinned features
25
+ for an immutable `DatasetVersion`, list models/instances, and poll job status. Because it is a thin
26
+ wrapper, its behavior and conformance are inherited from the SDK and the public `/v1` Gateway.
27
+
28
+ ## Conformance
29
+
30
+ Signal: `sdk:contract` (shared with the SDK). Production certification is pending the full contract
31
+ and deployed-path gates in the QDS execution plan. The active CLI accepts scoped API keys, uses the
32
+ canonical SDK resources, requires an immutable dataset-version UUID for training features and job
33
+ launches, and exposes the distinct `lerobot_v2_1`, `lerobot_v3`, and `mcap_ros2` catalog filters.
34
+ Historical QUA-795 and QUA-796 notes described an older route/auth surface and are not current
35
+ evidence.
36
+
37
+ ## References
38
+
39
+ - `sdk/cli/`; QUA-633 (end-user CLI shipped)
40
+ - Wraps: [Qualia SDK](/specs/sdk/python/qualia-sdk/) → [Finetune API](/specs/api/gateway/finetune-api/)
@@ -0,0 +1,3 @@
1
+ """Qualia CLI for robotics data and VLA workflows."""
2
+
3
+ __version__ = "0.2.0rc2"
@@ -0,0 +1,25 @@
1
+ """Shared client factory."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from qualia import AuthenticationError, Qualia
6
+
7
+ from qualia_cli.config import load_config
8
+ from qualia_cli.output import print_error
9
+
10
+
11
+ def get_client() -> Qualia:
12
+ from qualia_cli.main import _base_url_override, _token_override
13
+
14
+ try:
15
+ cfg = load_config()
16
+ api_key = _token_override if _token_override is not None else cfg.api_key
17
+ base_url = _base_url_override if _base_url_override is not None else cfg.base_url
18
+ return Qualia(api_key=api_key, base_url=base_url)
19
+ except (AuthenticationError, OSError, UnicodeError, ValueError) as e:
20
+ print_error(str(e))
21
+ print_error(
22
+ "Set valid QUALIA_API_KEY/QUALIA_BASE_URL values or run: "
23
+ "qualia auth login --token <KEY>"
24
+ )
25
+ raise SystemExit(1) from e
@@ -4,7 +4,12 @@ from __future__ import annotations
4
4
 
5
5
  import typer
6
6
 
7
- from qualia_cli.config import load_config, save_config
7
+ from qualia_cli.config import (
8
+ load_config,
9
+ save_config,
10
+ validate_api_key,
11
+ validate_base_url,
12
+ )
8
13
  from qualia_cli.output import print_error
9
14
 
10
15
  app = typer.Typer(help="Authentication.")
@@ -23,7 +28,7 @@ def login(
23
28
  try:
24
29
  path = save_config(api_key, base_url)
25
30
  typer.echo(f"Config saved to {path}")
26
- except OSError as e:
31
+ except (OSError, ValueError) as e:
27
32
  print_error(f"Could not write config: {e}")
28
33
  raise SystemExit(1) from e
29
34
 
@@ -31,13 +36,15 @@ def login(
31
36
  @app.command("status")
32
37
  def status() -> None:
33
38
  """Check current authentication config."""
34
- cfg = load_config()
35
- if cfg.api_key:
36
- typer.echo(f"API key: {cfg.api_key[:8]}...")
37
- else:
38
- typer.echo("Not authenticated. Run: qualia auth login")
39
- raise SystemExit(1)
40
- if cfg.base_url:
41
- typer.echo(f"Base URL: {cfg.base_url}")
39
+ try:
40
+ cfg = load_config()
41
+ validate_api_key(cfg.api_key or "")
42
+ base_url = validate_base_url(cfg.base_url) if cfg.base_url is not None else None
43
+ except (OSError, UnicodeError, ValueError) as e:
44
+ typer.echo(f"Invalid authentication config: {e}")
45
+ raise SystemExit(1) from e
46
+ typer.echo("API key: configured")
47
+ if base_url:
48
+ typer.echo(f"Base URL: {base_url}")
42
49
  else:
43
50
  typer.echo("Base URL: https://api.qualiastudios.dev (default)")