kiteml-cli 0.4.0__tar.gz → 0.4.1__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.4.0 → kiteml_cli-0.4.1}/PKG-INFO +1 -1
- {kiteml_cli-0.4.0 → kiteml_cli-0.4.1}/src/kite_cli/__init__.py +1 -1
- {kiteml_cli-0.4.0 → kiteml_cli-0.4.1}/src/kite_cli/cli/commands/augment.py +12 -5
- {kiteml_cli-0.4.0 → kiteml_cli-0.4.1}/src/kite_cli/client.py +11 -0
- {kiteml_cli-0.4.0 → kiteml_cli-0.4.1}/src/kite_cli/mcp/server.py +22 -11
- {kiteml_cli-0.4.0 → kiteml_cli-0.4.1}/.gitignore +0 -0
- {kiteml_cli-0.4.0 → kiteml_cli-0.4.1}/README.md +0 -0
- {kiteml_cli-0.4.0 → kiteml_cli-0.4.1}/pyproject.toml +0 -0
- {kiteml_cli-0.4.0 → kiteml_cli-0.4.1}/src/kite_cli/archive.py +0 -0
- {kiteml_cli-0.4.0 → kiteml_cli-0.4.1}/src/kite_cli/auth.py +0 -0
- {kiteml_cli-0.4.0 → kiteml_cli-0.4.1}/src/kite_cli/cli/__init__.py +0 -0
- {kiteml_cli-0.4.0 → kiteml_cli-0.4.1}/src/kite_cli/cli/commands/__init__.py +0 -0
- {kiteml_cli-0.4.0 → kiteml_cli-0.4.1}/src/kite_cli/cli/commands/auth_cmd.py +0 -0
- {kiteml_cli-0.4.0 → kiteml_cli-0.4.1}/src/kite_cli/cli/commands/config_cmd.py +0 -0
- {kiteml_cli-0.4.0 → kiteml_cli-0.4.1}/src/kite_cli/cli/commands/evaluations.py +0 -0
- {kiteml_cli-0.4.0 → kiteml_cli-0.4.1}/src/kite_cli/cli/commands/rl.py +0 -0
- {kiteml_cli-0.4.0 → kiteml_cli-0.4.1}/src/kite_cli/cli/commands/twin.py +0 -0
- {kiteml_cli-0.4.0 → kiteml_cli-0.4.1}/src/kite_cli/cli/main.py +0 -0
- {kiteml_cli-0.4.0 → kiteml_cli-0.4.1}/src/kite_cli/config.py +0 -0
- {kiteml_cli-0.4.0 → kiteml_cli-0.4.1}/src/kite_cli/mcp/__init__.py +0 -0
- {kiteml_cli-0.4.0 → kiteml_cli-0.4.1}/src/kite_cli/mcp/common.py +0 -0
- {kiteml_cli-0.4.0 → kiteml_cli-0.4.1}/src/kite_cli/output.py +0 -0
- {kiteml_cli-0.4.0 → kiteml_cli-0.4.1}/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.4.
|
|
3
|
+
Version: 0.4.1
|
|
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
|
|
@@ -38,10 +38,11 @@ def reference_image(path: str, camera: Optional[str] = None) -> dict:
|
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
def scene_config(episodes, model: Optional[str], variants: Optional[int] = None,
|
|
41
|
-
preview: Optional[bool] = None, allow_video_fallback: Optional[bool] = None
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
preview: Optional[bool] = None, allow_video_fallback: Optional[bool] = None,
|
|
42
|
+
video_check: Optional[str] = None) -> dict:
|
|
43
|
+
"""The /v1 ``config`` for an episode selection ("all" or a count), the scene engines' options and the video
|
|
44
|
+
check's mode: background / world augment whole datasets and take ``episodes``; every other model takes a count as
|
|
45
|
+
``episode_count`` ("all" sends none: relight covers every episode, video and auto keep their default)."""
|
|
45
46
|
config: dict = {}
|
|
46
47
|
if episodes is not None:
|
|
47
48
|
if str(episodes) != "all" and not str(episodes).isdigit():
|
|
@@ -56,6 +57,8 @@ def scene_config(episodes, model: Optional[str], variants: Optional[int] = None,
|
|
|
56
57
|
config["preview_first"] = preview
|
|
57
58
|
if allow_video_fallback:
|
|
58
59
|
config["allow_video_fallback"] = True
|
|
60
|
+
if video_check:
|
|
61
|
+
config["video_check"] = video_check
|
|
59
62
|
return config
|
|
60
63
|
|
|
61
64
|
|
|
@@ -89,6 +92,10 @@ def create(
|
|
|
89
92
|
help="auto: use generative video (far more tokens, no pixel guarantee) where no scene engine can run, "
|
|
90
93
|
"instead of failing",
|
|
91
94
|
),
|
|
95
|
+
video_check: Optional[str] = typer.Option(
|
|
96
|
+
None, "--video-check", help="Generative video: standard (default: rejects a changed robot or a missing object; "
|
|
97
|
+
"the rest ship as warnings) | strict (every problem rejects) | off (records warnings, never rejects)",
|
|
98
|
+
),
|
|
92
99
|
reference_images: Optional[List[str]] = typer.Option(
|
|
93
100
|
None, "--reference-image", help="Photo of the target look, [camera=]PATH (repeatable)"
|
|
94
101
|
),
|
|
@@ -107,7 +114,7 @@ def create(
|
|
|
107
114
|
body: dict = {
|
|
108
115
|
"source": {"type": "huggingface", "repo_id": repo_id},
|
|
109
116
|
"instructions": instructions,
|
|
110
|
-
"config": scene_config(episodes, model, variants, preview, allow_video_fallback),
|
|
117
|
+
"config": scene_config(episodes, model, variants, preview, allow_video_fallback, video_check),
|
|
111
118
|
"output": {"type": output, "private": private},
|
|
112
119
|
}
|
|
113
120
|
if hf_repo:
|
|
@@ -161,6 +161,17 @@ class KiteClient:
|
|
|
161
161
|
for chunk in resp.iter_bytes(chunk_size=8192):
|
|
162
162
|
f.write(chunk)
|
|
163
163
|
|
|
164
|
+
def redirect_target(self, path: str) -> Optional[str]:
|
|
165
|
+
"""Where an API download path (a run's or a twin's archive) redirects: a fresh signed link, or None when the
|
|
166
|
+
API streams the file itself. Only the response's headers are read."""
|
|
167
|
+
with self._client.stream("GET", path, headers=self._base_headers(), follow_redirects=False) as resp:
|
|
168
|
+
if resp.is_redirect:
|
|
169
|
+
return resp.headers.get("location")
|
|
170
|
+
if resp.status_code >= 400:
|
|
171
|
+
resp.read()
|
|
172
|
+
raise _api_error(resp)
|
|
173
|
+
return None
|
|
174
|
+
|
|
164
175
|
def close(self) -> None:
|
|
165
176
|
self._client.close()
|
|
166
177
|
|
|
@@ -56,6 +56,23 @@ def _twin_source(dataset: str) -> dict:
|
|
|
56
56
|
return {"type": "huggingface", "repo_id": dataset}
|
|
57
57
|
|
|
58
58
|
|
|
59
|
+
def _fetch_command(d: dict, fname: str, client: KiteClient) -> str:
|
|
60
|
+
"""The command that fetches a finished resource's archive. From Kite-Version 2026-10-12 `download.url` is this
|
|
61
|
+
API's path, which needs the key: ask it for the fresh signed link it redirects to, so an agent without the key
|
|
62
|
+
(the hosted server's OAuth) can fetch it directly. Without one, the API path with the key."""
|
|
63
|
+
if d.get("requires_auth"):
|
|
64
|
+
resolve = getattr(client, "redirect_target", None)
|
|
65
|
+
try:
|
|
66
|
+
signed = resolve(d["url"]) if resolve else None
|
|
67
|
+
except Exception: # the link is a convenience: the API path below still works
|
|
68
|
+
signed = None
|
|
69
|
+
if signed:
|
|
70
|
+
return f'curl -fL "{signed}" -o {fname}'
|
|
71
|
+
url = f"{client.base_url.rstrip('/')}{d['url']}"
|
|
72
|
+
return f'curl -fL -H "Authorization: Bearer $KITE_API_KEY" "{url}" -o {fname}'
|
|
73
|
+
return f'curl -fL "{d["url"]}" -o {fname}'
|
|
74
|
+
|
|
75
|
+
|
|
59
76
|
def _twin_view(twin: dict, client: KiteClient) -> dict:
|
|
60
77
|
"""A twin as an agent should see it: the resource plus `next`, the one thing to do now. A hosted MCP server
|
|
61
78
|
cannot write to the agent's disk, so a finished twin's `next` is the exact command that fetches and
|
|
@@ -65,11 +82,7 @@ def _twin_view(twin: dict, client: KiteClient) -> dict:
|
|
|
65
82
|
d = twin.get("download") or {}
|
|
66
83
|
if status == "succeeded" and d:
|
|
67
84
|
fname = d.get("filename") or f"{tid}.tar.gz"
|
|
68
|
-
|
|
69
|
-
url = f"{client.base_url.rstrip('/')}{d['url']}"
|
|
70
|
-
fetch = f'curl -fL -H "Authorization: Bearer $KITE_API_KEY" "{url}" -o {fname}'
|
|
71
|
-
else:
|
|
72
|
-
fetch = f'curl -fL "{d["url"]}" -o {fname}'
|
|
85
|
+
fetch = _fetch_command(d, fname, client)
|
|
73
86
|
out["next"] = (f"{fetch} && tar xzf {fname} # -> {d.get('scene')}"
|
|
74
87
|
+ (f" (link valid until {d['expires_at']}; call kite_twin_status again for a fresh one)"
|
|
75
88
|
if d.get("expires_at") else ""))
|
|
@@ -95,11 +108,7 @@ def _eval_view(run: dict, client: KiteClient) -> dict:
|
|
|
95
108
|
d = run.get("download") or {}
|
|
96
109
|
if status == "succeeded" and d:
|
|
97
110
|
fname = d.get("filename") or f"{rid}.tar.gz"
|
|
98
|
-
|
|
99
|
-
url = f"{client.base_url.rstrip('/')}{d['url']}"
|
|
100
|
-
fetch = f'curl -fL -H "Authorization: Bearer $KITE_API_KEY" "{url}" -o {fname}'
|
|
101
|
-
else:
|
|
102
|
-
fetch = f'curl -fL "{d["url"]}" -o {fname}'
|
|
111
|
+
fetch = _fetch_command(d, fname, client)
|
|
103
112
|
out["next"] = (f"{fetch} && tar xzf {fname} # -> {rid}/, the run directory (`kite verify {rid} --evals <the spec's evaluators>` checks it)"
|
|
104
113
|
+ (f" (link valid until {d['expires_at']}; call kite_eval_status again for a fresh one)"
|
|
105
114
|
if d.get("expires_at") else "") + f"; kite_eval_report {rid} has the scorecard")
|
|
@@ -228,6 +237,7 @@ TOOLS = [
|
|
|
228
237
|
"variants": {"type": "integer", "description": "background/world: environments per source episode (default 1)"},
|
|
229
238
|
"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
239
|
"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)"},
|
|
240
|
+
"video_check": {"type": "string", "enum": ["standard", "strict", "off"], "description": "Generative video: what the check of each clip rejects. standard (default): a changed robot or a missing, duplicated or swapped object; an object redrawn or the table or view redrawn ship as warnings. strict: every problem rejects. off: records warnings, never rejects"},
|
|
231
241
|
"reference_images": {"type": "array", "maxItems": 6, "description": "Photos of the target look", "items": {
|
|
232
242
|
"type": "object", "properties": {
|
|
233
243
|
"camera": {"type": "string", "description": "Camera it shows (e.g. 'top'); omit for all cameras"},
|
|
@@ -419,7 +429,8 @@ async def _dispatch(name: str, args: dict, client: KiteClient) -> list[TextConte
|
|
|
419
429
|
"source": {"type": "huggingface", "repo_id": args["repo_id"]},
|
|
420
430
|
"instructions": args["instructions"],
|
|
421
431
|
"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")
|
|
432
|
+
args.get("variants"), args.get("preview_first"), args.get("allow_video_fallback"),
|
|
433
|
+
args.get("video_check")),
|
|
423
434
|
"output": {"type": args.get("output_type", "download")},
|
|
424
435
|
**{k: args[k] for k in ("environments", "must_keep") if args.get(k)},
|
|
425
436
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|