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.
- {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/PKG-INFO +10 -8
- {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/README.md +9 -7
- {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/__init__.py +1 -1
- {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/archive.py +1 -1
- {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/cli/commands/augment.py +75 -5
- 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
- {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/cli/main.py +4 -4
- {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/mcp/server.py +74 -59
- {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/.gitignore +0 -0
- {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/pyproject.toml +0 -0
- {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/auth.py +0 -0
- {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/cli/__init__.py +0 -0
- {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/cli/commands/__init__.py +0 -0
- {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/cli/commands/auth_cmd.py +0 -0
- {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/cli/commands/config_cmd.py +0 -0
- {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/cli/commands/rl.py +0 -0
- {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/cli/commands/twin.py +0 -0
- {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/client.py +0 -0
- {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/config.py +0 -0
- {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/mcp/__init__.py +0 -0
- {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/mcp/common.py +0 -0
- {kiteml_cli-0.3.1 → kiteml_cli-0.4.0}/src/kite_cli/output.py +0 -0
- {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
|
+
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
|
-
- **
|
|
63
|
-
evaluator scored. You get a scorecard and the run directory, which
|
|
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
|
-
| `
|
|
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
|
-
|
|
141
|
+
Evaluate a policy on a Kite SDK spec, on Kite's machines (beta):
|
|
141
142
|
|
|
142
143
|
```bash
|
|
143
|
-
kite
|
|
144
|
-
kite
|
|
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
|
-
- **
|
|
21
|
-
evaluator scored. You get a scorecard and the run directory, which
|
|
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
|
-
| `
|
|
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
|
-
|
|
99
|
+
Evaluate a policy on a Kite SDK spec, on Kite's machines (beta):
|
|
99
100
|
|
|
100
101
|
```bash
|
|
101
|
-
kite
|
|
102
|
-
kite
|
|
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,4 +1,4 @@
|
|
|
1
|
-
"""Fetching a finished resource's `.tar.gz` (a twin,
|
|
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
|
|
4
|
-
|
|
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:
|
|
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",
|
|
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":
|
|
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
|
|
2
|
-
verifiable run directory out. The policy runs on an NVIDIA L4 if its device is `cuda`, on a CPU
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
kite
|
|
6
|
-
kite
|
|
7
|
-
kite
|
|
8
|
-
kite
|
|
9
|
-
kite
|
|
10
|
-
kite
|
|
11
|
-
kite
|
|
12
|
-
kite
|
|
13
|
-
kite
|
|
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 $
|
|
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="
|
|
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
|
-
|
|
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
|
|
113
|
-
print_json(
|
|
113
|
+
if not (wait or out) or not run.get("id"):
|
|
114
|
+
print_json(run)
|
|
114
115
|
return
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
if
|
|
118
|
-
print_json(
|
|
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(
|
|
122
|
+
print_json(run)
|
|
122
123
|
return
|
|
123
|
-
saved = _save(client,
|
|
124
|
-
print_json({"ok": True, "data":
|
|
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="
|
|
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/
|
|
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
|
|
152
|
+
"""List your evaluations (newest first, last status seen; `status` has live progress)."""
|
|
152
153
|
try:
|
|
153
|
-
print_json(KiteClient().get("/v1/
|
|
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="
|
|
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/
|
|
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="
|
|
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/
|
|
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="
|
|
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/
|
|
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="
|
|
210
|
-
out: Optional[Path] = typer.Option(None, "-o", "--out", help="Output directory (default: $
|
|
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/
|
|
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="
|
|
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/
|
|
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="
|
|
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/
|
|
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/
|
|
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 $
|
|
419
|
-
return Path(out or os.environ.get("
|
|
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,
|
|
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(
|
|
426
|
-
if
|
|
427
|
-
raise KiteAPIError(409, f"
|
|
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
|
|
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,
|
|
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(
|
|
35
|
-
|
|
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
|
|
90
|
-
"""
|
|
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
|
|
105
|
-
if d.get("expires_at") else "") + f";
|
|
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
|
|
110
|
+
out["next"] = f"call kite_eval_status again in a minute{where}"
|
|
111
111
|
elif status == "canceling":
|
|
112
|
-
out["next"] = ("call
|
|
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"
|
|
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
|
|
121
|
-
"""The request body a
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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": "
|
|
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
|
-
#
|
|
280
|
-
Tool(name="
|
|
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":
|
|
283
|
-
Tool(name="
|
|
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
|
|
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
|
-
**
|
|
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="
|
|
294
|
-
description="Get
|
|
295
|
-
inputSchema={"type": "object", "properties":
|
|
296
|
-
Tool(name="
|
|
297
|
-
description="List your
|
|
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="
|
|
301
|
-
description="Get a succeeded
|
|
302
|
-
inputSchema={"type": "object", "properties":
|
|
303
|
-
Tool(name="
|
|
304
|
-
description="Get the last lines the SDK printed for
|
|
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
|
-
**
|
|
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="
|
|
310
|
-
description="Stop a queued or running
|
|
311
|
-
inputSchema={"type": "object", "properties":
|
|
312
|
-
Tool(name="
|
|
313
|
-
description="Run a failed or canceled
|
|
314
|
-
inputSchema={"type": "object", "properties":
|
|
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":
|
|
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
|
-
# ---
|
|
457
|
-
if name in ("
|
|
471
|
+
# --- Evaluations (public /v1 API) ---
|
|
472
|
+
if name in ("kite_eval_validate", "kite_eval_run"):
|
|
458
473
|
try:
|
|
459
|
-
body, validation =
|
|
474
|
+
body, validation = _eval_request(args, client)
|
|
460
475
|
except ValueError as e:
|
|
461
476
|
return _err(str(e))
|
|
462
|
-
if name == "
|
|
463
|
-
return _ok(validation or client.post("/v1/
|
|
464
|
-
if name == "
|
|
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(
|
|
470
|
-
if name == "
|
|
471
|
-
return _ok(
|
|
472
|
-
if name == "
|
|
473
|
-
return _ok(client.get("/v1/
|
|
474
|
-
if name == "
|
|
475
|
-
return _ok(client.get(f"/v1/
|
|
476
|
-
if name == "
|
|
477
|
-
return _ok(client.get(f"/v1/
|
|
478
|
-
if name == "
|
|
479
|
-
return _ok(
|
|
480
|
-
if name == "
|
|
481
|
-
return _ok(
|
|
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
|
|
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
|