cassis-cli 0.3.0__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.
- {cassis_cli-0.3.0 → cassis_cli-0.4.0}/PKG-INFO +16 -1
- {cassis_cli-0.3.0 → cassis_cli-0.4.0}/README.md +15 -0
- {cassis_cli-0.3.0 → cassis_cli-0.4.0}/cassis_cli/__init__.py +1 -1
- {cassis_cli-0.3.0 → cassis_cli-0.4.0}/cassis_cli/api.py +36 -0
- {cassis_cli-0.3.0 → cassis_cli-0.4.0}/cassis_cli/ontology.py +91 -0
- {cassis_cli-0.3.0 → cassis_cli-0.4.0}/pyproject.toml +1 -1
- {cassis_cli-0.3.0 → cassis_cli-0.4.0}/cassis_cli/common.py +0 -0
- {cassis_cli-0.3.0 → cassis_cli-0.4.0}/cassis_cli/eval.py +0 -0
- {cassis_cli-0.3.0 → cassis_cli-0.4.0}/cassis_cli/main.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cassis-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Cassis CLI — run Cassis actions (ontology validation, upload and publish) from your CI pipelines
|
|
5
5
|
License: Proprietary
|
|
6
6
|
Keywords: cassis,ontology,ci,text-to-sql
|
|
@@ -23,6 +23,7 @@ Description-Content-Type: text/markdown
|
|
|
23
23
|
Run Cassis actions from your CI pipelines:
|
|
24
24
|
|
|
25
25
|
- `cassis ontology check` validates the ontology files in your repository with the exact same checks as the Cassis GitHub PR check (YAML parsing, round-trip, import validation) — so you can gate merges in any CI system, not just GitHub.
|
|
26
|
+
- `cassis ontology fmt` rewrites the ontology files in canonical form (think `black`/`gofmt` for the ontology), so hand or agent edits pass the round-trip check.
|
|
26
27
|
- `cassis ontology upload` uploads the ontology files to a Cassis project (full replace) and, by default, publishes them immediately as a new version — so a merge to your main branch can go live in one CI step.
|
|
27
28
|
- `cassis ontology pull` downloads the project's unpublished ontology into your repository checkout (full sync — stale local YAML files are pruned), so you can start editing from the current state, or bootstrap a repo that isn't git-synced (e.g. Bitbucket).
|
|
28
29
|
- `cassis eval run` runs the project's eval suite against your local ontology files (scored in-memory — nothing is pushed to Cassis) and prints per-question results, so you can test the changes on your git branch before merging.
|
|
@@ -95,6 +96,20 @@ and Ctrl-C cancels the run (exit 130). It prints a deep link to the run's page
|
|
|
95
96
|
in the Evals UI; `--app-url` / `CASSIS_APP_URL` overrides the link's base URL
|
|
96
97
|
when the webapp is not served from the API host (defaults to `--api-url`).
|
|
97
98
|
|
|
99
|
+
### Formatting
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# Rewrite the ontology files in canonical form (in place)
|
|
103
|
+
cassis ontology fmt
|
|
104
|
+
|
|
105
|
+
# CI mode: fail (exit 1) if any file is not canonical, write nothing
|
|
106
|
+
cassis ontology fmt --check
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`fmt` uses the exact serializer the validation round-trip compares against, so a formatted tree cannot fail that stage. Formatting does not run import validation — `check` remains the pass/fail gate for semantic problems (dangling references, incomplete metrics).
|
|
110
|
+
|
|
111
|
+
**Review the diff before committing**: canonical form keeps exactly the fields Cassis understands. Unknown fields (typos) are dropped — the rewrite makes them visible in `git diff` instead of losing them silently at sync time. Files with duplicate YAML keys are rejected (fix them by hand: the formatter can't know which value you meant).
|
|
112
|
+
|
|
98
113
|
### Exit codes
|
|
99
114
|
|
|
100
115
|
| Code | Meaning |
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
Run Cassis actions from your CI pipelines:
|
|
4
4
|
|
|
5
5
|
- `cassis ontology check` validates the ontology files in your repository with the exact same checks as the Cassis GitHub PR check (YAML parsing, round-trip, import validation) — so you can gate merges in any CI system, not just GitHub.
|
|
6
|
+
- `cassis ontology fmt` rewrites the ontology files in canonical form (think `black`/`gofmt` for the ontology), so hand or agent edits pass the round-trip check.
|
|
6
7
|
- `cassis ontology upload` uploads the ontology files to a Cassis project (full replace) and, by default, publishes them immediately as a new version — so a merge to your main branch can go live in one CI step.
|
|
7
8
|
- `cassis ontology pull` downloads the project's unpublished ontology into your repository checkout (full sync — stale local YAML files are pruned), so you can start editing from the current state, or bootstrap a repo that isn't git-synced (e.g. Bitbucket).
|
|
8
9
|
- `cassis eval run` runs the project's eval suite against your local ontology files (scored in-memory — nothing is pushed to Cassis) and prints per-question results, so you can test the changes on your git branch before merging.
|
|
@@ -75,6 +76,20 @@ and Ctrl-C cancels the run (exit 130). It prints a deep link to the run's page
|
|
|
75
76
|
in the Evals UI; `--app-url` / `CASSIS_APP_URL` overrides the link's base URL
|
|
76
77
|
when the webapp is not served from the API host (defaults to `--api-url`).
|
|
77
78
|
|
|
79
|
+
### Formatting
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# Rewrite the ontology files in canonical form (in place)
|
|
83
|
+
cassis ontology fmt
|
|
84
|
+
|
|
85
|
+
# CI mode: fail (exit 1) if any file is not canonical, write nothing
|
|
86
|
+
cassis ontology fmt --check
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`fmt` uses the exact serializer the validation round-trip compares against, so a formatted tree cannot fail that stage. Formatting does not run import validation — `check` remains the pass/fail gate for semantic problems (dangling references, incomplete metrics).
|
|
90
|
+
|
|
91
|
+
**Review the diff before committing**: canonical form keeps exactly the fields Cassis understands. Unknown fields (typos) are dropped — the rewrite makes them visible in `git diff` instead of losing them silently at sync time. Files with duplicate YAML keys are rejected (fix them by hand: the formatter can't know which value you meant).
|
|
92
|
+
|
|
78
93
|
### Exit codes
|
|
79
94
|
|
|
80
95
|
| Code | Meaning |
|
|
@@ -284,3 +284,39 @@ def post_eval_run_cancel(
|
|
|
284
284
|
raise _project_scope_error(response)
|
|
285
285
|
if response.status_code >= 400:
|
|
286
286
|
raise ApiError(f"Cassis API returned HTTP {response.status_code}: {response.text[:500]}")
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def post_ontology_fmt(
|
|
290
|
+
*,
|
|
291
|
+
api_url: str,
|
|
292
|
+
api_key: str,
|
|
293
|
+
files: dict[str, str],
|
|
294
|
+
transport: Optional[httpx.BaseTransport] = None,
|
|
295
|
+
) -> dict[str, Any]:
|
|
296
|
+
"""POST the ontology tree to /api/ci/ontology-fmt and return the response body."""
|
|
297
|
+
url = api_url.rstrip("/") + "/api/ci/ontology-fmt"
|
|
298
|
+
try:
|
|
299
|
+
with httpx.Client(timeout=TIMEOUT_SECONDS, transport=transport) as client:
|
|
300
|
+
response = client.post(
|
|
301
|
+
url,
|
|
302
|
+
json={"files": files},
|
|
303
|
+
headers={"Authorization": f"Bearer {api_key}"},
|
|
304
|
+
)
|
|
305
|
+
except httpx.HTTPError as exc:
|
|
306
|
+
raise ApiError(f"Could not reach the Cassis API at {url}: {exc}") from exc
|
|
307
|
+
|
|
308
|
+
if response.status_code == 401:
|
|
309
|
+
raise AuthError("The Cassis API rejected the API key (invalid or expired).")
|
|
310
|
+
if response.status_code >= 400:
|
|
311
|
+
raise ApiError(f"Cassis API returned HTTP {response.status_code}: {response.text[:500]}")
|
|
312
|
+
result = _parse_json_response(response, url)
|
|
313
|
+
if (
|
|
314
|
+
not isinstance(result, dict)
|
|
315
|
+
or "ok" not in result
|
|
316
|
+
or not isinstance(result.get("findings"), list)
|
|
317
|
+
or not isinstance(result.get("changed_paths"), list)
|
|
318
|
+
or not isinstance(result.get("removed_paths"), list)
|
|
319
|
+
or (result["ok"] and not isinstance(result.get("files"), dict))
|
|
320
|
+
):
|
|
321
|
+
raise ApiError(f"Unexpected response shape from the Cassis API at {url}.")
|
|
322
|
+
return result
|
|
@@ -15,6 +15,7 @@ from cassis_cli.api import (
|
|
|
15
15
|
UploadValidationError,
|
|
16
16
|
get_ontology_export,
|
|
17
17
|
post_ontology_check,
|
|
18
|
+
post_ontology_fmt,
|
|
18
19
|
post_ontology_import,
|
|
19
20
|
)
|
|
20
21
|
from cassis_cli.common import (
|
|
@@ -281,3 +282,93 @@ def upload(
|
|
|
281
282
|
fg=typer.colors.GREEN,
|
|
282
283
|
)
|
|
283
284
|
raise typer.Exit(EXIT_OK)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
@app.command()
|
|
288
|
+
def fmt(
|
|
289
|
+
path: Path = typer.Argument(
|
|
290
|
+
Path("."),
|
|
291
|
+
help="Repository checkout root (the directory containing the ontology export path).",
|
|
292
|
+
),
|
|
293
|
+
api_key: Optional[str] = typer.Option(
|
|
294
|
+
None,
|
|
295
|
+
"--api-key",
|
|
296
|
+
envvar="CASSIS_API_KEY",
|
|
297
|
+
help="Cassis API key (sk-k6-...). Create one in Organization settings -> API keys.",
|
|
298
|
+
),
|
|
299
|
+
api_url: str = typer.Option(
|
|
300
|
+
DEFAULT_API_URL,
|
|
301
|
+
"--api-url",
|
|
302
|
+
envvar="CASSIS_API_URL",
|
|
303
|
+
help="Cassis API base URL.",
|
|
304
|
+
),
|
|
305
|
+
base_path: str = typer.Option(
|
|
306
|
+
DEFAULT_BASE_PATH,
|
|
307
|
+
"--base-path",
|
|
308
|
+
envvar="CASSIS_BASE_PATH",
|
|
309
|
+
help="Repository directory the ontology is exported under (the project's git-sync Path setting).",
|
|
310
|
+
),
|
|
311
|
+
check_only: bool = typer.Option(
|
|
312
|
+
False,
|
|
313
|
+
"--check",
|
|
314
|
+
help="Do not write anything; exit 1 if any file would change.",
|
|
315
|
+
),
|
|
316
|
+
) -> None:
|
|
317
|
+
"""Rewrite the ontology files in canonical form (think `black` for the ontology).
|
|
318
|
+
|
|
319
|
+
Uses the exact serializer the validation round-trip compares against, so a
|
|
320
|
+
formatted tree cannot fail that stage of `cassis ontology check` or the
|
|
321
|
+
GitHub PR check. Unknown fields are dropped by canonicalization — review
|
|
322
|
+
the diff before committing; duplicate YAML keys are rejected (the
|
|
323
|
+
formatter cannot know which value was intended). Exits 0 on success
|
|
324
|
+
(1 with --check when changes are needed), 1 when the tree cannot be
|
|
325
|
+
parsed, 2 on usage errors, 3 on transport/API errors.
|
|
326
|
+
"""
|
|
327
|
+
api_key = _require_api_key(api_key)
|
|
328
|
+
files, base_path = _collect_tree(path, base_path)
|
|
329
|
+
ontology_dir = path / Path(base_path)
|
|
330
|
+
|
|
331
|
+
try:
|
|
332
|
+
result = post_ontology_fmt(api_url=api_url, api_key=api_key, files=files)
|
|
333
|
+
except AuthError as exc:
|
|
334
|
+
typer.secho(str(exc), fg=typer.colors.RED, err=True)
|
|
335
|
+
raise typer.Exit(EXIT_TRANSPORT) from exc
|
|
336
|
+
except ApiError as exc:
|
|
337
|
+
typer.secho(str(exc), fg=typer.colors.RED, err=True)
|
|
338
|
+
raise typer.Exit(EXIT_TRANSPORT) from exc
|
|
339
|
+
|
|
340
|
+
if not result["ok"]:
|
|
341
|
+
typer.secho("Cannot format: the tree does not parse.", fg=typer.colors.RED, bold=True, err=True)
|
|
342
|
+
for finding in result["findings"]:
|
|
343
|
+
location = f"{base_path}/{finding.get('path')}: " if finding.get("path") else ""
|
|
344
|
+
typer.echo(f" {location}{finding.get('message', '')}", err=True)
|
|
345
|
+
raise typer.Exit(EXIT_VALIDATION_FAILED)
|
|
346
|
+
|
|
347
|
+
changed = result["changed_paths"]
|
|
348
|
+
removed = result["removed_paths"]
|
|
349
|
+
if not changed and not removed:
|
|
350
|
+
typer.secho(f"✓ {len(files)} file(s) already canonical.", fg=typer.colors.GREEN)
|
|
351
|
+
raise typer.Exit(EXIT_OK)
|
|
352
|
+
|
|
353
|
+
if check_only:
|
|
354
|
+
for p in changed:
|
|
355
|
+
typer.echo(f"would rewrite {base_path}/{p}")
|
|
356
|
+
for p in removed:
|
|
357
|
+
typer.echo(f"would remove {base_path}/{p}")
|
|
358
|
+
raise typer.Exit(EXIT_VALIDATION_FAILED)
|
|
359
|
+
|
|
360
|
+
for p in changed:
|
|
361
|
+
target = ontology_dir / p
|
|
362
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
363
|
+
target.write_text(result["files"][p], encoding="utf-8")
|
|
364
|
+
typer.echo(f"rewrote {base_path}/{p}")
|
|
365
|
+
for p in removed:
|
|
366
|
+
(ontology_dir / p).unlink(missing_ok=True)
|
|
367
|
+
typer.echo(f"removed {base_path}/{p}")
|
|
368
|
+
typer.secho(
|
|
369
|
+
f"Formatted {len(changed)} file(s)"
|
|
370
|
+
+ (f", removed {len(removed)}" if removed else "")
|
|
371
|
+
+ ". Review the diff: fields Cassis does not recognize are dropped.",
|
|
372
|
+
fg=typer.colors.YELLOW,
|
|
373
|
+
)
|
|
374
|
+
raise typer.Exit(EXIT_OK)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|