kiteml-cli 0.3.1__tar.gz → 0.4.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.
Files changed (23) hide show
  1. {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/PKG-INFO +10 -8
  2. {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/README.md +9 -7
  3. {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/__init__.py +1 -1
  4. {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/archive.py +1 -1
  5. {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/cli/commands/augment.py +75 -5
  6. kiteml_cli-0.3.1/src/kite_cli/cli/commands/sim.py → kiteml_cli-0.4.0/src/kite_cli/cli/commands/evaluations.py +51 -49
  7. {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/cli/main.py +4 -4
  8. {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/mcp/server.py +74 -59
  9. {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/.gitignore +0 -0
  10. {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/pyproject.toml +0 -0
  11. {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/auth.py +0 -0
  12. {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/cli/__init__.py +0 -0
  13. {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/cli/commands/__init__.py +0 -0
  14. {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/cli/commands/auth_cmd.py +0 -0
  15. {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/cli/commands/config_cmd.py +0 -0
  16. {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/cli/commands/rl.py +0 -0
  17. {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/cli/commands/twin.py +0 -0
  18. {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/client.py +0 -0
  19. {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/config.py +0 -0
  20. {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/mcp/__init__.py +0 -0
  21. {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/mcp/common.py +0 -0
  22. {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/output.py +0 -0
  23. {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/progress.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: kiteml-cli
3
- Version: 0.3.1
3
+ Version: 0.4.0
4
4
  Summary: Kite CLI and MCP server: train robot policies with reinforcement learning in simulation, augment LeRobot datasets, and build MuJoCo digital twins, from your terminal or an AI agent.
5
5
  Project-URL: Homepage, https://kiteml.com
6
6
  Project-URL: Documentation, https://kite-ml.mintlify.app/tools/cli-and-mcp
@@ -59,8 +59,9 @@ Model Context Protocol (MCP) client.
59
59
  Hugging Face.
60
60
  - **Digital twins (beta).** Point Kite at one episode of a LeRobot dataset and get an interactable MuJoCo scene of the
61
61
  room, with the objects the robot handles built to size.
62
- - **Sim runs (beta).** Run a Kite SDK spec on Kite's machines, on an NVIDIA L4 for a GPU policy: every episode traced, every
63
- evaluator scored. You get a scorecard and the run directory, which `kite verify` checks.
62
+ - **Evaluations (beta).** Evaluate a policy by running a Kite SDK spec on Kite's machines, on an NVIDIA L4 for a GPU
63
+ policy: every episode traced, every evaluator scored. You get a scorecard and the run directory, which
64
+ `kite verify` checks.
64
65
 
65
66
  ## Install
66
67
 
@@ -108,7 +109,7 @@ lerobot/pusht to look like this photo".
108
109
  | `kite_rl_status`, `kite_rl_metrics`, `kite_rl_report`, `kite_rl_list` | Follow training and read each run's evaluation verdict |
109
110
  | `kite_augment_create`, `kite_augment_status`, `kite_augment_list`, `kite_augment_cancel` | Augment LeRobot datasets |
110
111
  | `kite_twin_validate`, `kite_twin_create`, `kite_twin_status`, `kite_twin_list`, `kite_twin_cancel`, `kite_twin_resume` | Build MuJoCo digital twins (beta) |
111
- | `kite_sim_validate`, `kite_sim_run`, `kite_sim_status`, `kite_sim_list`, `kite_sim_report`, `kite_sim_logs`, `kite_sim_cancel`, `kite_sim_resume` | Run Kite SDK specs on Kite's machines and read their scorecards (beta) |
112
+ | `kite_eval_validate`, `kite_eval_run`, `kite_eval_status`, `kite_eval_list`, `kite_eval_report`, `kite_eval_logs`, `kite_eval_cancel`, `kite_eval_resume` | Evaluate a policy on a Kite SDK spec on Kite's machines and read the scorecard (beta) |
112
113
  | `kite_doctor` | Check connectivity and authentication |
113
114
 
114
115
  ## Use it from the terminal
@@ -137,11 +138,11 @@ Build a digital twin (beta, enabled per account):
137
138
  kite twin create lerobot/svla_so101_pickplace --out ./twins # -> ./twins/twin_.../scene.xml
138
139
  ```
139
140
 
140
- Run a Kite SDK spec on Kite's machines (beta):
141
+ Evaluate a policy on a Kite SDK spec, on Kite's machines (beta):
141
142
 
142
143
  ```bash
143
- kite sim validate specs/smoke.yaml # free: the plan and what a run reserves
144
- kite sim run specs/smoke.yaml --out ./runs # -> ./runs/srun_.../scorecard.json
144
+ kite eval validate specs/smoke.yaml # free: the plan and what a run reserves
145
+ kite eval run specs/smoke.yaml --out ./runs # -> ./runs/eval_.../scorecard.json
145
146
  ```
146
147
 
147
148
  Every command prints JSON (`{"ok": true, "data": ...}`) so scripts and agents can parse it. Run `kite --help` for
@@ -152,7 +153,8 @@ the full list.
152
153
  - Docs: [CLI and MCP](https://kite-ml.mintlify.app/tools/cli-and-mcp) ·
153
154
  [RL runs](https://kite-ml.mintlify.app/platform-api/rl-runs) ·
154
155
  [Augmentations](https://kite-ml.mintlify.app/platform-api/augmentation) ·
155
- [Twins](https://kite-ml.mintlify.app/platform-api/twins)
156
+ [Twins](https://kite-ml.mintlify.app/platform-api/twins) ·
157
+ [Evaluations](https://kite-ml.mintlify.app/platform-api/evaluations)
156
158
  - Dashboard: [app.kiteml.com](https://app.kiteml.com)
157
159
  - Website: [kiteml.com](https://kiteml.com)
158
160
  - Support: [raul@kiteml.com](mailto:raul@kiteml.com)
@@ -17,8 +17,9 @@ Model Context Protocol (MCP) client.
17
17
  Hugging Face.
18
18
  - **Digital twins (beta).** Point Kite at one episode of a LeRobot dataset and get an interactable MuJoCo scene of the
19
19
  room, with the objects the robot handles built to size.
20
- - **Sim runs (beta).** Run a Kite SDK spec on Kite's machines, on an NVIDIA L4 for a GPU policy: every episode traced, every
21
- evaluator scored. You get a scorecard and the run directory, which `kite verify` checks.
20
+ - **Evaluations (beta).** Evaluate a policy by running a Kite SDK spec on Kite's machines, on an NVIDIA L4 for a GPU
21
+ policy: every episode traced, every evaluator scored. You get a scorecard and the run directory, which
22
+ `kite verify` checks.
22
23
 
23
24
  ## Install
24
25
 
@@ -66,7 +67,7 @@ lerobot/pusht to look like this photo".
66
67
  | `kite_rl_status`, `kite_rl_metrics`, `kite_rl_report`, `kite_rl_list` | Follow training and read each run's evaluation verdict |
67
68
  | `kite_augment_create`, `kite_augment_status`, `kite_augment_list`, `kite_augment_cancel` | Augment LeRobot datasets |
68
69
  | `kite_twin_validate`, `kite_twin_create`, `kite_twin_status`, `kite_twin_list`, `kite_twin_cancel`, `kite_twin_resume` | Build MuJoCo digital twins (beta) |
69
- | `kite_sim_validate`, `kite_sim_run`, `kite_sim_status`, `kite_sim_list`, `kite_sim_report`, `kite_sim_logs`, `kite_sim_cancel`, `kite_sim_resume` | Run Kite SDK specs on Kite's machines and read their scorecards (beta) |
70
+ | `kite_eval_validate`, `kite_eval_run`, `kite_eval_status`, `kite_eval_list`, `kite_eval_report`, `kite_eval_logs`, `kite_eval_cancel`, `kite_eval_resume` | Evaluate a policy on a Kite SDK spec on Kite's machines and read the scorecard (beta) |
70
71
  | `kite_doctor` | Check connectivity and authentication |
71
72
 
72
73
  ## Use it from the terminal
@@ -95,11 +96,11 @@ Build a digital twin (beta, enabled per account):
95
96
  kite twin create lerobot/svla_so101_pickplace --out ./twins # -> ./twins/twin_.../scene.xml
96
97
  ```
97
98
 
98
- Run a Kite SDK spec on Kite's machines (beta):
99
+ Evaluate a policy on a Kite SDK spec, on Kite's machines (beta):
99
100
 
100
101
  ```bash
101
- kite sim validate specs/smoke.yaml # free: the plan and what a run reserves
102
- kite sim run specs/smoke.yaml --out ./runs # -> ./runs/srun_.../scorecard.json
102
+ kite eval validate specs/smoke.yaml # free: the plan and what a run reserves
103
+ kite eval run specs/smoke.yaml --out ./runs # -> ./runs/eval_.../scorecard.json
103
104
  ```
104
105
 
105
106
  Every command prints JSON (`{"ok": true, "data": ...}`) so scripts and agents can parse it. Run `kite --help` for
@@ -110,7 +111,8 @@ the full list.
110
111
  - Docs: [CLI and MCP](https://kite-ml.mintlify.app/tools/cli-and-mcp) ·
111
112
  [RL runs](https://kite-ml.mintlify.app/platform-api/rl-runs) ·
112
113
  [Augmentations](https://kite-ml.mintlify.app/platform-api/augmentation) ·
113
- [Twins](https://kite-ml.mintlify.app/platform-api/twins)
114
+ [Twins](https://kite-ml.mintlify.app/platform-api/twins) ·
115
+ [Evaluations](https://kite-ml.mintlify.app/platform-api/evaluations)
114
116
  - Dashboard: [app.kiteml.com](https://app.kiteml.com)
115
117
  - Website: [kiteml.com](https://kiteml.com)
116
118
  - Support: [raul@kiteml.com](mailto:raul@kiteml.com)
@@ -1,3 +1,3 @@
1
1
  """Kite CLI and MCP server for the Kite robotics training platform."""
2
2
 
3
- __version__ = "0.3.1"
3
+ __version__ = "0.4.0"
@@ -1,4 +1,4 @@
1
- """Fetching a finished resource's `.tar.gz` (a twin, a sim run): to `<out>/<id>.tar.gz`, sha256-verified, and unpacked
1
+ """Fetching a finished resource's `.tar.gz` (a twin, an evaluation): to `<out>/<id>.tar.gz`, sha256-verified, and unpacked
2
2
  only inside `<out>/<id>/`.
3
3
 
4
4
  A signed storage URL is fetched with a plain HTTP client, never with the Kite key; the API's own archive route
@@ -1,7 +1,8 @@
1
1
  """`kite augment` — dataset augmentation jobs via the public /v1 API.
2
2
 
3
- create / status / download / cancel / list. Authenticates with KITE_API_KEY like the
4
- rest of the CLI; calls the bare `/v1/augmentations` surface.
3
+ create / status / download / cancel / list, and `environments list` (what the background and
4
+ world engines put behind the work cell). Authenticates with KITE_API_KEY like the rest of the
5
+ CLI; calls the bare `/v1/augmentations` surface.
5
6
  """
6
7
 
7
8
  from __future__ import annotations
@@ -18,6 +19,9 @@ from kite_cli.output import print_error, print_json
18
19
  from kite_cli.progress import wait_for_completion
19
20
 
20
21
  app = typer.Typer(no_args_is_help=True, help="Dataset augmentation jobs")
22
+ environments_app = typer.Typer(no_args_is_help=True, help="Environments the background and world engines put "
23
+ "behind the work cell")
24
+ app.add_typer(environments_app, name="environments")
21
25
 
22
26
  _TERMINAL = {"succeeded", "failed", "canceled"}
23
27
  _MEDIA_TYPES = {".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".png": "image/png", ".webp": "image/webp"}
@@ -33,13 +37,57 @@ def reference_image(path: str, camera: Optional[str] = None) -> dict:
33
37
  return {"camera": camera, **entry} if camera else entry
34
38
 
35
39
 
40
+ def scene_config(episodes, model: Optional[str], variants: Optional[int] = None,
41
+ preview: Optional[bool] = None, allow_video_fallback: Optional[bool] = None) -> dict:
42
+ """The /v1 ``config`` for an episode selection ("all" or a count) and the scene engines' options: background /
43
+ world augment whole datasets and take ``episodes``; every other model takes a count as ``episode_count`` ("all"
44
+ sends none: relight covers every episode, video and auto keep their default)."""
45
+ config: dict = {}
46
+ if episodes is not None:
47
+ if str(episodes) != "all" and not str(episodes).isdigit():
48
+ raise typer.BadParameter("a count or 'all'", param_hint="--episodes")
49
+ if model in ("background", "world"):
50
+ config["episodes"] = "all" if str(episodes) == "all" else int(episodes)
51
+ elif str(episodes) != "all":
52
+ config["episode_count"] = int(episodes)
53
+ if variants is not None:
54
+ config["variants"] = variants
55
+ if preview is not None:
56
+ config["preview_first"] = preview
57
+ if allow_video_fallback:
58
+ config["allow_video_fallback"] = True
59
+ return config
60
+
61
+
36
62
  @app.command()
37
63
  def create(
38
64
  repo_id: str = typer.Option(..., "--repo-id", help="Source HF dataset id, e.g. lerobot/pusht"),
39
65
  instructions: str = typer.Option(..., "-i", "--instructions", help="Augmentation instruction"),
40
- episodes: int = typer.Option(3, "-n", "--episodes", help="Episodes to generate with video augmentation (1-50); relight covers every episode"),
66
+ episodes: Optional[str] = typer.Option(
67
+ None, "-n", "--episodes",
68
+ help="A count (video: 1-50, default 3) or 'all' (background/world, their default); relight covers every "
69
+ "episode",
70
+ ),
41
71
  model: Optional[str] = typer.Option(
42
- None, "--model", help="auto (default: pick relight or video) | relight (colour/exposure, every episode) | video (generative)"
72
+ None, "--model",
73
+ help="auto (default: the planner picks per camera) | relight (colour/exposure) | background (a panorama behind "
74
+ "the work cell) | world (a 3D world, for moving cameras) | video (generative)",
75
+ ),
76
+ environments: Optional[str] = typer.Option(
77
+ None, "--environments", help="Background/world: comma-separated environment ids (aenv_...) or library ids"
78
+ ),
79
+ env_count: Optional[int] = typer.Option(None, "--env-count", help="Background/world: environments to pick for the rig"),
80
+ must_keep: Optional[List[str]] = typer.Option(
81
+ None, "--must-keep", help="Something that must keep its recorded pixels (repeatable)"
82
+ ),
83
+ variants: Optional[int] = typer.Option(None, "--variants", help="Background/world: environments per source episode"),
84
+ preview: Optional[bool] = typer.Option(
85
+ None, "--preview/--no-preview", help="Background/world: check one episode's quality gates before the rest"
86
+ ),
87
+ allow_video_fallback: bool = typer.Option(
88
+ False, "--allow-video-fallback",
89
+ help="auto: use generative video (far more tokens, no pixel guarantee) where no scene engine can run, "
90
+ "instead of failing",
43
91
  ),
44
92
  reference_images: Optional[List[str]] = typer.Option(
45
93
  None, "--reference-image", help="Photo of the target look, [camera=]PATH (repeatable)"
@@ -59,13 +107,18 @@ def create(
59
107
  body: dict = {
60
108
  "source": {"type": "huggingface", "repo_id": repo_id},
61
109
  "instructions": instructions,
62
- "config": {"episode_count": episodes},
110
+ "config": scene_config(episodes, model, variants, preview, allow_video_fallback),
63
111
  "output": {"type": output, "private": private},
64
112
  }
65
113
  if hf_repo:
66
114
  body["output"]["repo_id"] = hf_repo
67
115
  if model:
68
116
  body["model"] = model
117
+ if environments or env_count:
118
+ body["environments"] = {k: v for k, v in (
119
+ ("ids", [e.strip() for e in (environments or "").split(",") if e.strip()]), ("count", env_count)) if v}
120
+ if must_keep:
121
+ body["must_keep"] = list(must_keep)
69
122
  try:
70
123
  if relight:
71
124
  body["config"]["relight"] = json.loads(relight)
@@ -135,6 +188,23 @@ def cancel(aug_id: str = typer.Argument(..., help="Augmentation id (aug_...)")):
135
188
  print_error(e.message, code=e.code, details=e.details)
136
189
 
137
190
 
191
+ @environments_app.command("list")
192
+ def list_environments(
193
+ owner: str = typer.Option("user", "--owner", help="user (yours) or kite (the Kite library)"),
194
+ kind: Optional[str] = typer.Option(None, "--kind", help="panorama or world"),
195
+ category: Optional[str] = typer.Option(None, "--category", help="workshop, warehouse, hangar, lab, ..."),
196
+ lighting: Optional[str] = typer.Option(None, "--lighting", help="daylight or artificial"),
197
+ limit: int = typer.Option(20, "--limit", help="Max rows (1-100)"),
198
+ ):
199
+ """List environments: yours, or the Kite library's (--owner kite)."""
200
+ params = {"owner": owner, "kind": kind, "category": category, "lighting": lighting, "limit": limit}
201
+ try:
202
+ client = KiteClient()
203
+ print_json(client.get("/v1/augmentations/environments", params={k: v for k, v in params.items() if v}))
204
+ except KiteAPIError as e:
205
+ print_error(e.message, code=e.code, details=e.details)
206
+
207
+
138
208
  @app.command()
139
209
  def download(
140
210
  aug_id: str = typer.Argument(..., help="Augmentation id (aug_...)"),
@@ -1,22 +1,23 @@
1
- """`kite sim` — a Kite SDK spec run on Kite's machines via the public /v1/sim_runs API: the spec in, a scorecard and a
2
- verifiable run directory out. The policy runs on an NVIDIA L4 if its device is `cuda`, on a CPU machine otherwise.
3
-
4
- kite sim validate specs/smoke.yaml [--set episodes.count=20] free: the plan, and what a run reserves
5
- kite sim run specs/smoke.yaml --wait --out ./runs -> ./runs/srun_.../scorecard.json
6
- kite sim status srun_01K... [--wait]
7
- kite sim list [--limit 20] [--after srun_...]
8
- kite sim report srun_01K... the scorecard
9
- kite sim logs srun_01K... [--tail 200]
10
- kite sim files srun_01K... [--limit 100] [--after PATH] [--all]
11
- kite sim download srun_01K... [--out DIR] [--no-extract]
12
- kite sim cancel srun_01K... -> canceling, then canceled once its job has stopped
13
- kite sim resume srun_01K...
1
+ """`kite eval` — an evaluation: a Kite SDK spec run on Kite's machines via the public /v1/evaluations API, the spec
2
+ in, a scorecard and a verifiable run directory out. The policy runs on an NVIDIA L4 if its device is `cuda`, on a CPU
3
+ machine otherwise. (Until 2026-10-18 this was `kite sim`, over /v1/sim_runs.)
4
+
5
+ kite eval validate specs/smoke.yaml [--set episodes.count=20] free: the plan, and what a run reserves
6
+ kite eval run specs/smoke.yaml --wait --out ./runs -> ./runs/eval_.../scorecard.json
7
+ kite eval status eval_01K... [--wait]
8
+ kite eval list [--limit 20] [--after eval_...]
9
+ kite eval report eval_01K... the scorecard
10
+ kite eval logs eval_01K... [--tail 200]
11
+ kite eval files eval_01K... [--limit 100] [--after PATH] [--all]
12
+ kite eval download eval_01K... [--out DIR] [--no-extract]
13
+ kite eval cancel eval_01K... -> canceling, then canceled once its job has stopped
14
+ kite eval resume eval_01K...
14
15
 
15
16
  SPEC is the file `kite run` takes (kite_spec_version "2"). The command sends its text, asks the API which paths it
16
17
  names (`files_required`), and sends each from this disk: a file or folder Kite ships by its sha256 alone, anything
17
18
  else inline (about 15 MB in all). A file on the Hugging Face Hub, such as a checkpoint's weights, goes as a
18
19
  reference: `--hf PATH=OWNER/NAME@COMMIT:FILE`, PATH as the spec names it (a folder's file as FOLDER/FILE).
19
- Where runs go: --out DIR, else $KITE_SIM_RUNS_DIR, else ./runs (`~` works). Paths printed are absolute. A run
20
+ Where runs go: --out DIR, else $KITE_EVAL_RUNS_DIR, else ./runs (`~` works). Paths printed are absolute. A run
20
21
  directory extracts to <out>/<run_id>/; the SDK's `kite verify <out>/<run_id> --evals DIR` checks it, DIR being the
21
22
  evaluators the spec named (the run records where its job read them, not your folder).
22
23
  """
@@ -42,7 +43,7 @@ from kite_cli.client import KiteAPIError, KiteClient, post_create
42
43
  from kite_cli.output import print_error, print_json
43
44
  from kite_cli.progress import wait_for_completion
44
45
 
45
- app = typer.Typer(no_args_is_help=True, help="Sim runs: a Kite SDK spec on Kite's machines -> a scorecard and a "
46
+ app = typer.Typer(no_args_is_help=True, help="Evaluations: a Kite SDK spec on Kite's machines -> a scorecard and a "
46
47
  "verifiable run directory")
47
48
 
48
49
  _TERMINAL = {"succeeded", "failed", "canceled"} # not canceling: the cancel was accepted, the job has not stopped
@@ -104,37 +105,37 @@ def run(
104
105
  ("verify_determinism", verify_determinism)) if v}
105
106
  body = {**body, **({"options": options} if options else {}), **({"label": label} if label else {})}
106
107
  try:
107
- srun = post_create(client, "/v1/sim_runs", body, key)
108
+ run = post_create(client, "/v1/evaluations", body, key)
108
109
  except (httpx.TimeoutException, httpx.NetworkError) as e:
109
110
  print_error(f"network error ({e}); re-run with --idempotency-key {key} to retry without running twice",
110
111
  code="network_error", details={"idempotency_key": key})
111
112
  return
112
- if not (wait or out) or not srun.get("id"):
113
- print_json(srun)
113
+ if not (wait or out) or not run.get("id"):
114
+ print_json(run)
114
115
  return
115
- srun = wait_for_completion(client, f"/v1/sim_runs/{srun['id']}", terminal_states=_TERMINAL,
116
- poll_interval=_POLL_S)
117
- if srun.get("status") != "succeeded":
118
- print_json(srun, success=False)
116
+ run = wait_for_completion(client, f"/v1/evaluations/{run['id']}", terminal_states=_TERMINAL,
117
+ poll_interval=_POLL_S)
118
+ if run.get("status") != "succeeded":
119
+ print_json(run, success=False)
119
120
  raise typer.Exit(1)
120
121
  if out is None:
121
- print_json(srun)
122
+ print_json(run)
122
123
  return
123
- saved = _save(client, srun, _out_dir(out), extract=True)
124
- print_json({"ok": True, "data": srun, "run_dir": saved["run_dir"], "archive": saved["archive"]})
124
+ saved = _save(client, run, _out_dir(out), extract=True)
125
+ print_json({"ok": True, "data": run, "run_dir": saved["run_dir"], "archive": saved["archive"]})
125
126
  except KiteAPIError as e:
126
127
  print_error(e.message, code=e.code, details=e.details)
127
128
 
128
129
 
129
130
  @app.command()
130
131
  def status(
131
- run_id: str = typer.Argument(..., help="Sim run id (srun_...)"),
132
+ run_id: str = typer.Argument(..., help="Evaluation id (eval_...)"),
132
133
  wait: bool = typer.Option(False, "--wait", help="Poll until succeeded | failed | canceled"),
133
134
  ):
134
135
  """A run's live status, phase, episodes done, and once it has succeeded its result and `download`."""
135
136
  try:
136
137
  client = KiteClient()
137
- path = f"/v1/sim_runs/{run_id}"
138
+ path = f"/v1/evaluations/{run_id}"
138
139
  if wait:
139
140
  print_json(wait_for_completion(client, path, terminal_states=_TERMINAL, poll_interval=_POLL_S))
140
141
  else:
@@ -148,43 +149,43 @@ def list_runs(
148
149
  limit: int = typer.Option(20, "--limit", help="Max rows (1-100)"),
149
150
  after: Optional[str] = typer.Option(None, "--after", help="Cursor: the previous page's next_cursor"),
150
151
  ):
151
- """List your sim runs (newest first, last status seen; `status` has live progress)."""
152
+ """List your evaluations (newest first, last status seen; `status` has live progress)."""
152
153
  try:
153
- print_json(KiteClient().get("/v1/sim_runs", params=_page(limit, after)))
154
+ print_json(KiteClient().get("/v1/evaluations", params=_page(limit, after)))
154
155
  except KiteAPIError as e:
155
156
  print_error(e.message, code=e.code, details=e.details)
156
157
 
157
158
 
158
159
  @app.command()
159
- def report(run_id: str = typer.Argument(..., help="Sim run id (srun_...)")):
160
+ def report(run_id: str = typer.Argument(..., help="Evaluation id (eval_...)")):
160
161
  """A succeeded run's scorecard: every evaluator's value, interval and count, and what was not measured."""
161
162
  try:
162
- print_json(KiteClient().get(f"/v1/sim_runs/{run_id}/report"))
163
+ print_json(KiteClient().get(f"/v1/evaluations/{run_id}/report"))
163
164
  except KiteAPIError as e:
164
165
  print_error(e.message, code=e.code, details=e.details)
165
166
 
166
167
 
167
168
  @app.command()
168
169
  def logs(
169
- run_id: str = typer.Argument(..., help="Sim run id (srun_...)"),
170
+ run_id: str = typer.Argument(..., help="Evaluation id (eval_...)"),
170
171
  tail: int = typer.Option(200, "--tail", help="The last N lines"),
171
172
  ):
172
173
  """What the SDK printed, oldest first: readable while the run builds and after."""
173
174
  try:
174
- print_json(KiteClient().get(f"/v1/sim_runs/{run_id}/logs", params={"tail": tail}))
175
+ print_json(KiteClient().get(f"/v1/evaluations/{run_id}/logs", params={"tail": tail}))
175
176
  except KiteAPIError as e:
176
177
  print_error(e.message, code=e.code, details=e.details)
177
178
 
178
179
 
179
180
  @app.command("files")
180
181
  def list_files(
181
- run_id: str = typer.Argument(..., help="Sim run id (srun_...)"),
182
+ run_id: str = typer.Argument(..., help="Evaluation id (eval_...)"),
182
183
  limit: Optional[int] = typer.Option(None, "--limit", help="Files per page, 1-100 (default 100)"),
183
184
  after: Optional[str] = typer.Option(None, "--after", help="Cursor: the previous page's next_cursor (a file path)"),
184
185
  all_pages: bool = typer.Option(False, "--all", help="Follow next_cursor through every page; print one list"),
185
186
  ):
186
187
  """The files of a succeeded run's directory (path, bytes, sha256), sorted by path."""
187
- path = f"/v1/sim_runs/{run_id}/files"
188
+ path = f"/v1/evaluations/{run_id}/files"
188
189
  size = limit or 100
189
190
  try:
190
191
  client = KiteClient()
@@ -206,8 +207,8 @@ def list_files(
206
207
 
207
208
  @app.command()
208
209
  def download(
209
- run_id: str = typer.Argument(..., help="Sim run id (srun_...)"),
210
- out: Optional[Path] = typer.Option(None, "-o", "--out", help="Output directory (default: $KITE_SIM_RUNS_DIR, "
210
+ run_id: str = typer.Argument(..., help="Evaluation id (eval_...)"),
211
+ out: Optional[Path] = typer.Option(None, "-o", "--out", help="Output directory (default: $KITE_EVAL_RUNS_DIR, "
211
212
  "else ./runs)"),
212
213
  extract: bool = typer.Option(True, "--extract/--no-extract", help="Unpack into <out>/<run_id>/, or keep only "
213
214
  "the .tar.gz"),
@@ -215,26 +216,26 @@ def download(
215
216
  """Download a succeeded run: <out>/<run_id>.tar.gz, sha256-verified, unpacked to <out>/<run_id>/."""
216
217
  try:
217
218
  client = KiteClient(timeout=120)
218
- print_json(_save(client, client.get(f"/v1/sim_runs/{run_id}"), _out_dir(out), extract=extract))
219
+ print_json(_save(client, client.get(f"/v1/evaluations/{run_id}"), _out_dir(out), extract=extract))
219
220
  except KiteAPIError as e:
220
221
  print_error(e.message, code=e.code, details=e.details)
221
222
 
222
223
 
223
224
  @app.command()
224
- def cancel(run_id: str = typer.Argument(..., help="Sim run id (srun_...)")):
225
+ def cancel(run_id: str = typer.Argument(..., help="Evaluation id (eval_...)")):
225
226
  """Stop a queued or running run: canceling until its job has stopped, then canceled. It pays the minutes it
226
227
  ran."""
227
228
  try:
228
- print_json(KiteClient().post(f"/v1/sim_runs/{run_id}/cancel"))
229
+ print_json(KiteClient().post(f"/v1/evaluations/{run_id}/cancel"))
229
230
  except KiteAPIError as e:
230
231
  print_error(e.message, code=e.code, details=e.details)
231
232
 
232
233
 
233
234
  @app.command()
234
- def resume(run_id: str = typer.Argument(..., help="Sim run id (srun_...)")):
235
+ def resume(run_id: str = typer.Argument(..., help="Evaluation id (eval_...)")):
235
236
  """Run a failed or canceled run again from its request, as a new attempt: it reserves its cap again."""
236
237
  try:
237
- print_json(KiteClient().post(f"/v1/sim_runs/{run_id}/resume"))
238
+ print_json(KiteClient().post(f"/v1/evaluations/{run_id}/resume"))
238
239
  except KiteAPIError as e:
239
240
  print_error(e.message, code=e.code, details=e.details)
240
241
 
@@ -265,7 +266,7 @@ def request(client: KiteClient, spec: Path, overrides: List[str], hf: Dict[str,
265
266
  body["kite_version"] = kite_version
266
267
  for _ in range(_ROUNDS):
267
268
  try:
268
- return body, client.post("/v1/sim_runs/validate", json_data=body), sent
269
+ return body, client.post("/v1/evaluations/validate", json_data=body), sent
269
270
  except KiteAPIError as e:
270
271
  if e.code == "files_required" and e.details.get("files"):
271
272
  for item in e.details["files"]:
@@ -415,16 +416,17 @@ def _page(limit: int, after: Optional[str]) -> dict:
415
416
 
416
417
 
417
418
  def _out_dir(out: Optional[Path]) -> Path:
418
- """--out, else $KITE_SIM_RUNS_DIR, else ./runs; absolute, so what is printed says exactly where the run is."""
419
- return Path(out or os.environ.get("KITE_SIM_RUNS_DIR") or "./runs").expanduser().resolve()
419
+ """--out, else $KITE_EVAL_RUNS_DIR, else ./runs; absolute, so what is printed says exactly where the run is."""
420
+ return Path(out or os.environ.get("KITE_EVAL_RUNS_DIR") or "./runs").expanduser().resolve()
420
421
 
421
422
 
422
- def _save(client: KiteClient, srun: dict, out: Path, *, extract: bool) -> dict:
423
+ def _save(client: KiteClient, run: dict, out: Path, *, extract: bool) -> dict:
423
424
  """Fetch a succeeded run's archive to <out>/<run_id>.tar.gz, verify its sha256, and (extract) unpack the run
424
425
  directory into <out>/<run_id>/."""
425
- rid, d = str(srun.get("id") or ""), srun.get("download")
426
- if srun.get("status") != "succeeded" or not d:
427
- raise KiteAPIError(409, f"Sim run '{rid}' is {srun.get('status')}, not succeeded.", code="sim_run_not_ready")
426
+ rid, d = str(run.get("id") or ""), run.get("download")
427
+ if run.get("status") != "succeeded" or not d:
428
+ raise KiteAPIError(409, f"Evaluation '{rid}' is {run.get('status')}, not succeeded.",
429
+ code="evaluation_not_ready")
428
430
  archive = verified(client, d, out, rid, storage=_storage_client)
429
431
  saved = {"id": rid, "archive": str(archive), "bytes": archive.stat().st_size, "sha256": str(d["sha256"]).lower()}
430
432
  if extract:
@@ -5,7 +5,7 @@ Usage:
5
5
  kite auth login
6
6
  kite rl plan open_duck_mini_v2 "walk forward at a steady pace" -o walk.json
7
7
  kite augment create --repo-id lerobot/pusht -i "white marble table" --wait
8
- kite sim run specs/smoke.yaml --wait --out ./runs
8
+ kite eval run specs/smoke.yaml --wait --out ./runs
9
9
  kite doctor
10
10
  """
11
11
 
@@ -16,7 +16,7 @@ from typing import Optional
16
16
  import typer
17
17
 
18
18
  from kite_cli import __version__
19
- from kite_cli.cli.commands import augment, auth_cmd, config_cmd, rl, sim, twin
19
+ from kite_cli.cli.commands import augment, auth_cmd, config_cmd, evaluations, rl, twin
20
20
 
21
21
  app = typer.Typer(
22
22
  name="kite",
@@ -31,8 +31,8 @@ app.add_typer(config_cmd.app, name="config", help="CLI configuration")
31
31
  app.add_typer(augment.app, name="augment", help="Dataset augmentation: relighting + video augmentation of LeRobot datasets")
32
32
  app.add_typer(twin.app, name="twin", help="Digital twins (beta): one robot episode -> an interactable MuJoCo scene")
33
33
  app.add_typer(rl.app, name="rl", help="RL training in simulation: a task spec -> a trained, verified policy")
34
- app.add_typer(sim.app, name="sim", help="Sim runs (beta): a Kite SDK spec on Kite's machines -> a scorecard and a "
35
- "verifiable run directory")
34
+ app.add_typer(evaluations.app, name="eval", help="Evaluations (beta): a Kite SDK spec on Kite's machines -> a "
35
+ "scorecard and a verifiable run directory")
36
36
 
37
37
  # Kite's own install adds the dashboard's commands (projects, training, deployment, sensors); the published package
38
38
  # has only the /v1 commands above.
@@ -86,8 +86,8 @@ def _twin_view(twin: dict, client: KiteClient) -> dict:
86
86
  return out
87
87
 
88
88
 
89
- def _sim_view(run: dict, client: KiteClient) -> dict:
90
- """A sim run as an agent should see it: the resource plus `next`, the one thing to do now. A hosted MCP server
89
+ def _eval_view(run: dict, client: KiteClient) -> dict:
90
+ """An evaluation as an agent should see it: the resource plus `next`, the one thing to do now. A hosted MCP server
91
91
  cannot write to the agent's disk, so a finished run's `next` is the exact command that fetches and extracts its
92
92
  run directory."""
93
93
  out = dict(run)
@@ -101,26 +101,26 @@ def _sim_view(run: dict, client: KiteClient) -> dict:
101
101
  else:
102
102
  fetch = f'curl -fL "{d["url"]}" -o {fname}'
103
103
  out["next"] = (f"{fetch} && tar xzf {fname} # -> {rid}/, the run directory (`kite verify {rid} --evals <the spec's evaluators>` checks it)"
104
- + (f" (link valid until {d['expires_at']}; call kite_sim_status again for a fresh one)"
105
- if d.get("expires_at") else "") + f"; kite_sim_report {rid} has the scorecard")
104
+ + (f" (link valid until {d['expires_at']}; call kite_eval_status again for a fresh one)"
105
+ if d.get("expires_at") else "") + f"; kite_eval_report {rid} has the scorecard")
106
106
  elif status in ("queued", "processing"):
107
107
  eps = run.get("episodes") or {}
108
108
  where = (f" (phase {run['phase']}" + (f", {eps['done']}/{eps['total']} episodes" if eps.get("total") else "")
109
109
  + ")") if run.get("phase") else ""
110
- out["next"] = f"call kite_sim_status again in a minute{where}"
110
+ out["next"] = f"call kite_eval_status again in a minute{where}"
111
111
  elif status == "canceling":
112
- out["next"] = ("call kite_sim_status again in a few seconds: the cancel was accepted and the job is "
112
+ out["next"] = ("call kite_eval_status again in a few seconds: the cancel was accepted and the job is "
113
113
  "stopping; it then reads canceled")
114
114
  elif status in ("failed", "canceled"):
115
- out["next"] = (f"kite_sim_resume {rid} to run it again" if (run.get("error") or {}).get("resumable", True)
115
+ out["next"] = (f"kite_eval_resume {rid} to run it again" if (run.get("error") or {}).get("resumable", True)
116
116
  else "fix the spec as the error says, and run it again")
117
117
  return out
118
118
 
119
119
 
120
- def _sim_request(args: dict, client: KiteClient) -> tuple[dict, dict | None]:
121
- """The request body a kite_sim_* tool sends, and the validation that accepted it when it was worked out here:
120
+ def _eval_request(args: dict, client: KiteClient) -> tuple[dict, dict | None]:
121
+ """The request body a kite_eval_* tool sends, and the validation that accepted it when it was worked out here:
122
122
  from `spec_yaml` and `files` as given, or (the local server only) from `spec`, a spec file on this machine,
123
- whose files are sent as `kite sim run` sends them."""
123
+ whose files are sent as `kite eval run` sends them."""
124
124
  if args.get("spec_yaml"):
125
125
  keys = ("spec_yaml", "spec_path", "files", "overrides", "kite_version")
126
126
  return {k: args[k] for k in keys if args.get(k)}, None
@@ -131,7 +131,7 @@ def _sim_request(args: dict, client: KiteClient) -> tuple[dict, dict | None]:
131
131
  raise ValueError("`spec` only works with the local (stdio) Kite MCP server; send `spec_yaml` and `files`")
132
132
  from pathlib import Path
133
133
 
134
- from kite_cli.cli.commands.sim import hf_refs, request
134
+ from kite_cli.cli.commands.evaluations import hf_refs, request
135
135
 
136
136
  hf = hf_refs([f"{k}={v}" for k, v in (args.get("hf") or {}).items()])
137
137
  body, validation, _ = request(client, Path(args["spec"]), args.get("overrides") or [], hf, args.get("kite_version"))
@@ -190,7 +190,7 @@ _RL_SPEC = {
190
190
  }
191
191
  _RL_RUN_ID = {"run_id": {"type": "string", "description": "RL run id (rlr_...)"}}
192
192
 
193
- _SIM_SPEC = {
193
+ _EVAL_SPEC = {
194
194
  "spec": {"type": "string", "description": "A Kite SDK spec file on this machine, best as an absolute path (the local server only): every path it names is sent from this disk, a file Kite ships by its hash alone"},
195
195
  "spec_yaml": {"type": "string", "description": "The spec's text, instead of `spec` (the hosted server): send `spec_path` and `files` with it"},
196
196
  "spec_path": {"type": "string", "description": "With spec_yaml: its path in your workspace, e.g. specs/smoke.yaml. Paths in the spec are read relative to it"},
@@ -199,7 +199,7 @@ _SIM_SPEC = {
199
199
  "hf": {"type": "object", "description": "With spec: {PATH: \"OWNER/NAME@COMMIT:FILE\"}, the paths that are a public Hugging Face file (a checkpoint's weights). Optional"},
200
200
  "kite_version": {"type": "string", "description": "The Kite release you expect; refused if it is not the one served. Optional"},
201
201
  }
202
- _SIM_RUN_ID = {"run_id": {"type": "string", "description": "Sim run id (srun_...)"}}
202
+ _EVAL_ID = {"run_id": {"type": "string", "description": "Evaluation id (eval_...)"}}
203
203
 
204
204
  # ---------------------------------------------------------------------------
205
205
  # Tool definitions — every tool has title + annotations per Claude directory
@@ -208,15 +208,26 @@ _SIM_RUN_ID = {"run_id": {"type": "string", "description": "Sim run id (srun_...
208
208
  TOOLS = [
209
209
  # Augmentations (public /v1 API — relighting + video augmentation)
210
210
  Tool(name="kite_augment_create", title="Create Augmentation",
211
- description="Create a dataset augmentation run from a HuggingFace LeRobot dataset. model 'auto' (default) lets a planner pick 'relight' (cheap, exact colour/exposure/warmth transform of every episode — e.g. match a darker, warmer deployment scene) or 'video' (generative video augmentation of content: textures, objects, backgrounds). Pass reference_images of the target look. Returns the augmentation resource with its id (aug_...) and, once planned, the chosen plan.",
211
+ description="Create a dataset augmentation run from a HuggingFace LeRobot dataset. model 'auto' (default) lets a planner pick per camera: 'relight' (cheap, exact colour/exposure/warmth transform of every episode — e.g. match a darker, warmer deployment scene), 'background' (a new place behind the work cell from a panorama; the robot, objects and table keep their recorded pixels), 'world' (a 3D world rendered from each camera's pose, for moving cameras) or 'video' (generative video augmentation of content: textures, objects). background and world run on whole datasets and may not be enabled for every account. Pass reference_images of the target look. Returns the augmentation resource with its id (aug_...) and, once planned, the chosen plan.",
212
212
  inputSchema={"type": "object", "properties": {
213
213
  "repo_id": {"type": "string", "description": "Source HF dataset id, e.g. lerobot/pusht"},
214
214
  "instructions": {"type": "string", "description": "Augmentation instruction"},
215
- "episode_count": {"type": "integer", "description": "Episodes to generate (1-50, default 3)"},
215
+ "episode_count": {"type": "integer", "description": "Video: episodes to generate (1-50, default 3)"},
216
+ "episodes": {"anyOf": [{"type": "integer", "minimum": 1}, {"type": "string", "enum": ["all"]}],
217
+ "description": "model background/world: source episodes to augment, 'all' (their default) or the first N. Any other model takes a count as episode_count; 'all' there sends none (relight covers every episode)"},
216
218
  "output_type": {"type": "string", "enum": ["download", "huggingface"], "description": "Where results go (default download)"},
217
219
  "hf_repo": {"type": "string", "description": "Target HF repo id (for output_type huggingface)"},
218
220
  "variation_prompts": {"type": "array", "items": {"type": "string"}, "description": "Per-episode prompts (overrides episode_count)"},
219
- "model": {"type": "string", "enum": ["auto", "relight", "video"], "description": "Engine (default auto)"},
221
+ "model": {"type": "string", "enum": ["auto", "relight", "background", "world", "video"], "description": "Engine (default auto)"},
222
+ "environments": {"type": "object", "description": "background/world: what to put behind the work cell (default: 3 matched to the rig). One of ids, prompt, or filter/count", "properties": {
223
+ "ids": {"type": "array", "items": {"type": "string"}, "description": "Environment ids (aenv_...) or library ids"},
224
+ "filter": {"type": "object", "properties": {"category": {"type": "string"}, "lighting": {"type": "string", "enum": ["daylight", "artificial"]}}},
225
+ "prompt": {"type": "string", "description": "Make new environments from a description (minutes)"},
226
+ "count": {"type": "integer", "description": "How many to pick or make"}}},
227
+ "must_keep": {"type": "array", "items": {"type": "string"}, "description": "Things that must keep their recorded pixels, besides the robot and what it handles"},
228
+ "variants": {"type": "integer", "description": "background/world: environments per source episode (default 1)"},
229
+ "preview_first": {"type": "boolean", "description": "background/world: check one episode's quality gates (windows at its start, middle and end) before charging for the rest (default true)"},
230
+ "allow_video_fallback": {"type": "boolean", "description": "auto: where no scene engine can run, use generative video (far more tokens, no pixel guarantee) instead of failing with the reasons and the video price (default false)"},
220
231
  "reference_images": {"type": "array", "maxItems": 6, "description": "Photos of the target look", "items": {
221
232
  "type": "object", "properties": {
222
233
  "camera": {"type": "string", "description": "Camera it shows (e.g. 'top'); omit for all cameras"},
@@ -276,42 +287,42 @@ TOOLS = [
276
287
  "twin_id": {"type": "string", "description": "Twin id (twin_...)"}},
277
288
  "required": ["twin_id"]}, annotations=_WRITE),
278
289
 
279
- # Sim runs (public /v1 API): a Kite SDK spec on Kite's machines -> a scorecard and a verifiable run directory
280
- Tool(name="kite_sim_validate", title="Validate Sim Run",
290
+ # Evaluations (public /v1 API): a Kite SDK spec on Kite's machines -> a scorecard and a verifiable run directory
291
+ Tool(name="kite_eval_validate", title="Validate Evaluation",
281
292
  description="Free check of a Kite SDK spec (kite_spec_version \"2\") before it runs on Kite's machines, in seconds: the spec parses and plans (episodes, each evaluator, the estimate), every file it names is accounted for, and the price (price.reserved tokens at price.tokens_per_hour: an NVIDIA L4 for a cuda policy, a CPU machine otherwise). Creates nothing and charges nothing. Errors keep the SDK's own code, field, fix and line (spec_invalid, files_required listing each missing path, code_not_allowed for Python Kite does not ship).",
282
- inputSchema={"type": "object", "properties": _SIM_SPEC}, annotations=_READ),
283
- Tool(name="kite_sim_run", title="Start Sim Run",
284
- description="Run a Kite SDK spec on Kite's machines: the policy in the spec's world for its episodes, every episode traced and every evaluator scored. Returns the queued run (id srun_...) with a `next` instruction; follow it with kite_sim_status. The run's cap (max_minutes, default 60 on CPU and 120 on an L4) is reserved now at its tier's rate, and the run is charged the minutes its job ran; a failed run costs nothing (insufficient_tokens when the balance is short). A finished run has a scorecard (kite_sim_report), its manifest hash (the run's identity, shared by a local `kite run` of the same spec) and the run directory as one .tar.gz.",
293
+ inputSchema={"type": "object", "properties": _EVAL_SPEC}, annotations=_READ),
294
+ Tool(name="kite_eval_run", title="Start Evaluation",
295
+ description="Run a Kite SDK spec on Kite's machines: the policy in the spec's world for its episodes, every episode traced and every evaluator scored. Returns the queued run (id eval_...) with a `next` instruction; follow it with kite_eval_status. The run's cap (max_minutes, default 60 on CPU and 120 on an L4) is reserved now at its tier's rate, and the run is charged the minutes its job ran; a failed run costs nothing (insufficient_tokens when the balance is short). A finished run has a scorecard (kite_eval_report), its manifest hash (the run's identity, shared by a local `kite run` of the same spec) and the run directory as one .tar.gz.",
285
296
  inputSchema={"type": "object", "properties": {
286
- **_SIM_SPEC,
297
+ **_EVAL_SPEC,
287
298
  "max_minutes": {"type": "integer", "description": "The run's cap, 1-720: what it reserves and the most it can cost. Optional"},
288
299
  "label": {"type": "string", "description": "A short name for your own listing. Optional"},
289
300
  "no_eval": {"type": "boolean", "description": "Run the episodes only: no evaluators, no scorecard. Optional"},
290
301
  "verify_determinism": {"type": "boolean", "description": "Re-run episode 0 and fail the run on a differing hash. Optional"},
291
302
  "idempotency_key": {"type": "string", "description": "Reuse the same key when retrying, so a retry never runs twice. Optional"}}},
292
303
  annotations=_WRITE),
293
- Tool(name="kite_sim_status", title="Get Sim Run Status",
294
- description="Get a sim run's status (queued/processing/canceling/succeeded/failed/canceled), its phase (materializing, planning, fetching, running, verifying, packaging), the episodes done, the tokens reserved and refunded, and a `next` instruction. queued includes waiting for a machine: an L4 can take minutes to find. When succeeded it carries the result (manifest_hash, run_hash, verified, each evaluator's metric with its interval) and `next` is the exact command that fetches and extracts the run directory. A failed run carries the SDK's error (code, param, details.kite_code, details.remediation).",
295
- inputSchema={"type": "object", "properties": _SIM_RUN_ID, "required": ["run_id"]}, annotations=_READ),
296
- Tool(name="kite_sim_list", title="List Sim Runs",
297
- description="List your sim runs, newest first, with their last known status (kite_sim_status has live progress).",
304
+ Tool(name="kite_eval_status", title="Get Evaluation Status",
305
+ description="Get an evaluation's status (queued/processing/canceling/succeeded/failed/canceled), its phase (materializing, planning, fetching, running, verifying, packaging), the episodes done, the tokens reserved and refunded, and a `next` instruction. queued includes waiting for a machine: an L4 can take minutes to find. When succeeded it carries the result (manifest_hash, run_hash, verified, each evaluator's metric with its interval) and `next` is the exact command that fetches and extracts the run directory. A failed run carries the SDK's error (code, param, details.kite_code, details.remediation).",
306
+ inputSchema={"type": "object", "properties": _EVAL_ID, "required": ["run_id"]}, annotations=_READ),
307
+ Tool(name="kite_eval_list", title="List Evaluations",
308
+ description="List your evaluations, newest first, with their last known status (kite_eval_status has live progress).",
298
309
  inputSchema={"type": "object", "properties": {
299
310
  "limit": {"type": "integer", "description": "Max rows (1-100, default 20)"}}}, annotations=_READ),
300
- Tool(name="kite_sim_report", title="Get Sim Run Scorecard",
301
- description="Get a succeeded sim run's scorecard, the SDK's scorecard.json: every evaluator's value, confidence interval and count, the calibration it was scored under, and what was not measured.",
302
- inputSchema={"type": "object", "properties": _SIM_RUN_ID, "required": ["run_id"]}, annotations=_READ),
303
- Tool(name="kite_sim_logs", title="Get Sim Run Logs",
304
- description="Get the last lines the SDK printed for a sim run, oldest first; readable while it runs and after. Use it to see why a run failed.",
311
+ Tool(name="kite_eval_report", title="Get Evaluation Scorecard",
312
+ description="Get a succeeded evaluation's scorecard, the SDK's scorecard.json: every evaluator's value, confidence interval and count, the calibration it was scored under, and what was not measured.",
313
+ inputSchema={"type": "object", "properties": _EVAL_ID, "required": ["run_id"]}, annotations=_READ),
314
+ Tool(name="kite_eval_logs", title="Get Evaluation Logs",
315
+ description="Get the last lines the SDK printed for an evaluation, oldest first; readable while it runs and after. Use it to see why a run failed.",
305
316
  inputSchema={"type": "object", "properties": {
306
- **_SIM_RUN_ID,
317
+ **_EVAL_ID,
307
318
  "tail": {"type": "integer", "description": "How many of the last lines (default 200)"}},
308
319
  "required": ["run_id"]}, annotations=_READ),
309
- Tool(name="kite_sim_cancel", title="Cancel Sim Run",
310
- description="Stop a queued or running sim run. It answers canceling, and reads canceled once its job has stopped (check with kite_sim_status). It is charged the minutes it ran.",
311
- inputSchema={"type": "object", "properties": _SIM_RUN_ID, "required": ["run_id"]}, annotations=_DESTRUCTIVE),
312
- Tool(name="kite_sim_resume", title="Resume Sim Run",
313
- description="Run a failed or canceled sim run again from its request, as a new attempt: it reserves its cap again and is queued.",
314
- inputSchema={"type": "object", "properties": _SIM_RUN_ID, "required": ["run_id"]}, annotations=_WRITE),
320
+ Tool(name="kite_eval_cancel", title="Cancel Evaluation",
321
+ description="Stop a queued or running evaluation. It answers canceling, and reads canceled once its job has stopped (check with kite_eval_status). It is charged the minutes it ran.",
322
+ inputSchema={"type": "object", "properties": _EVAL_ID, "required": ["run_id"]}, annotations=_DESTRUCTIVE),
323
+ Tool(name="kite_eval_resume", title="Resume Evaluation",
324
+ description="Run a failed or canceled evaluation again from its request, as a new attempt: it reserves its cap again and is queued.",
325
+ inputSchema={"type": "object", "properties": _EVAL_ID, "required": ["run_id"]}, annotations=_WRITE),
315
326
 
316
327
  # RL runs (public /v1 API): train a robot policy in simulation from a spec, one run per seed
317
328
  Tool(name="kite_rl_catalog", title="RL Training Catalog",
@@ -402,11 +413,15 @@ async def _dispatch(name: str, args: dict, client: KiteClient) -> list[TextConte
402
413
 
403
414
  # --- Augmentations (public /v1 API) ---
404
415
  if name == "kite_augment_create":
416
+ from kite_cli.cli.commands.augment import scene_config
417
+
405
418
  body = {
406
419
  "source": {"type": "huggingface", "repo_id": args["repo_id"]},
407
420
  "instructions": args["instructions"],
408
- "config": {"episode_count": args.get("episode_count", 3)},
421
+ "config": scene_config(args.get("episodes", args.get("episode_count")), args.get("model"),
422
+ args.get("variants"), args.get("preview_first"), args.get("allow_video_fallback")),
409
423
  "output": {"type": args.get("output_type", "download")},
424
+ **{k: args[k] for k in ("environments", "must_keep") if args.get(k)},
410
425
  }
411
426
  if args.get("hf_repo"):
412
427
  body["output"]["repo_id"] = args["hf_repo"]
@@ -453,32 +468,32 @@ async def _dispatch(name: str, args: dict, client: KiteClient) -> list[TextConte
453
468
  if name == "kite_twin_resume":
454
469
  return _ok(_twin_view(client.post(f"/v1/twins/{args['twin_id']}/resume"), client))
455
470
 
456
- # --- Sim runs (public /v1 API) ---
457
- if name in ("kite_sim_validate", "kite_sim_run"):
471
+ # --- Evaluations (public /v1 API) ---
472
+ if name in ("kite_eval_validate", "kite_eval_run"):
458
473
  try:
459
- body, validation = _sim_request(args, client)
474
+ body, validation = _eval_request(args, client)
460
475
  except ValueError as e:
461
476
  return _err(str(e))
462
- if name == "kite_sim_validate":
463
- return _ok(validation or client.post("/v1/sim_runs/validate", json_data=body))
464
- if name == "kite_sim_run":
477
+ if name == "kite_eval_validate":
478
+ return _ok(validation or client.post("/v1/evaluations/validate", json_data=body))
479
+ if name == "kite_eval_run":
465
480
  options = {k: args[k] for k in ("max_minutes", "no_eval", "verify_determinism") if args.get(k)}
466
481
  body = {**body, **({"options": options} if options else {}),
467
482
  **({"label": args["label"]} if args.get("label") else {})}
468
483
  headers = {"Idempotency-Key": args["idempotency_key"]} if args.get("idempotency_key") else None
469
- return _ok(_sim_view(client.post("/v1/sim_runs", json_data=body, extra_headers=headers), client))
470
- if name == "kite_sim_status":
471
- return _ok(_sim_view(client.get(f"/v1/sim_runs/{args['run_id']}"), client))
472
- if name == "kite_sim_list":
473
- return _ok(client.get("/v1/sim_runs", params={"limit": args.get("limit", 20)}))
474
- if name == "kite_sim_report":
475
- return _ok(client.get(f"/v1/sim_runs/{args['run_id']}/report"))
476
- if name == "kite_sim_logs":
477
- return _ok(client.get(f"/v1/sim_runs/{args['run_id']}/logs", params={"tail": args.get("tail", 200)}))
478
- if name == "kite_sim_cancel":
479
- return _ok(_sim_view(client.post(f"/v1/sim_runs/{args['run_id']}/cancel"), client))
480
- if name == "kite_sim_resume":
481
- return _ok(_sim_view(client.post(f"/v1/sim_runs/{args['run_id']}/resume"), client))
484
+ return _ok(_eval_view(client.post("/v1/evaluations", json_data=body, extra_headers=headers), client))
485
+ if name == "kite_eval_status":
486
+ return _ok(_eval_view(client.get(f"/v1/evaluations/{args['run_id']}"), client))
487
+ if name == "kite_eval_list":
488
+ return _ok(client.get("/v1/evaluations", params={"limit": args.get("limit", 20)}))
489
+ if name == "kite_eval_report":
490
+ return _ok(client.get(f"/v1/evaluations/{args['run_id']}/report"))
491
+ if name == "kite_eval_logs":
492
+ return _ok(client.get(f"/v1/evaluations/{args['run_id']}/logs", params={"tail": args.get("tail", 200)}))
493
+ if name == "kite_eval_cancel":
494
+ return _ok(_eval_view(client.post(f"/v1/evaluations/{args['run_id']}/cancel"), client))
495
+ if name == "kite_eval_resume":
496
+ return _ok(_eval_view(client.post(f"/v1/evaluations/{args['run_id']}/resume"), client))
482
497
 
483
498
  # --- RL runs (public /v1 API) ---
484
499
  if name == "kite_rl_catalog":
File without changes
File without changes