Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,7 @@ jobs:
# suppresses the count line — which is the line that would show this job
# quietly shrinking to nothing.
- name: The reference readers load the exports
run: uv run pytest tests/formats/test_yolo_smoke.py tests/formats/test_coco_smoke.py
run: uv run pytest tests/formats/test_ultralytics_roundtrip.py tests/formats/test_coco_smoke.py

# #49's performance benchmark: frame times during pan, zoom and drag over 200
# boxes and 20 polygons of 32 vertices on a 4K asset, plus a CPU-throttling
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,15 @@ nothing was being distributed. This is the first version that is.

### Changed

- **`yolo` format renamed `ultralytics`; `yolo` accepted as an alias until the next release.**
Part of the export-targets epic (#784). The dialect now derives its task from the release -
segment when it holds a polygon, classify when it holds only classification tags, detect
otherwise - and its `data.yaml` carries `path: .`, one key per fold present, and `names` as a
mapping with no `nc`. A second dialect, `yolov5-yaml`, writes the YOLOv5 descriptor grammar
(`./` paths, `nc`, `names` as a list) for YOLOv7. Every exporter now declares the targets it
writes for, and the registry refuses a target declared by two formats or one promising a
geometry its format never writes.

- **A schema version that only widens the contract now moves every open batch onto it** (#381).
A batch is judged against the version it pinned at approval, and that pin used to move only
when somebody asked for it. Publishing an additive version now takes every batch in
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ a deliberate manual run, because each costs minutes or needs its own install.
| Design tokens | part of `pnpm test` — `tests/scripts/design_tokens.test.mjs` refuses a colour inside a class name, and `ui-core`'s `tokens.test.ts` gates the stylesheet against its TypeScript mirror | part of `frontend` |
| Wire action rosters | part of `pnpm test` — `tests/scripts/wire_rosters.test.mjs` holds the two transcriptions of `allowed_actions` (`ui-core`'s `testing/wire.fixtures.ts` and the e2e suite's `_wire.ts`) against the kernel's own answer, `tests/fixtures/wire_capabilities.json`, and against each other, in both directions. Both are typed against the generated union, which catches a withdrawn member and nothing else: the rows are hand-written, and comparing them only to each other once let a member both lacked pass green | part of `frontend` |
| Docs links | part of `pnpm test` — `tests/scripts/docs_links.test.mjs` resolves every internal link and every `#anchor` in every tracked Markdown file, naming the file, line and dead fragment. It reads `git ls-files`, so the set grows with the repository and no list is maintained by hand. External URLs are ignored on purpose: a gate that fails for somebody else's rate limit is one people re-run rather than read. Renaming a heading breaks inbound anchors *silently* — the link just lands at the top of the page — which was a near miss during the `visionset ui` → `visionset server` rename (#329) | part of `frontend` |
| Format smoke (ultralytics, pycocotools) | `uv sync --group yolo --group coco && uv run pytest tests/formats/test_*_smoke.py` — their own groups because ultralytics brings torch **and its wheel ships a top-level `tests` package that shadows this repo's**, so run only those files and `uv sync` again afterwards; skips without them, and CI sets `VISIONSET_REQUIRE_ULTRALYTICS=1` / `VISIONSET_REQUIRE_PYCOCOTOOLS=1` so a broken install goes red | — CI |
| Format smoke (ultralytics, pycocotools) | `uv sync --group yolo --group coco && uv run pytest tests/formats/test_ultralytics_roundtrip.py tests/formats/test_coco_smoke.py` — their own groups because ultralytics brings torch **and its wheel ships a top-level `tests` package that shadows this repo's**, so run only those files and `uv sync` again afterwards; skips without them, and CI sets `VISIONSET_REQUIRE_ULTRALYTICS=1` / `VISIONSET_REQUIRE_PYCOCOTOOLS=1` so a broken install goes red | — CI |
| Inference smoke (local-inference extra) | `uv sync --extra local-inference` then `VISIONSET_REQUIRE_LOCAL_INFERENCE=1 uv run pytest tests/inference tests/architecture/test_optional_runtime.py tests/server/test_inference.py tests/server/test_suggest.py tests/cli/test_inference_commands.py tests/jobs/test_weights_job.py -rs`, and `uv sync` again afterwards. The **with-runtime** half of the matrix — see [the two halves](#the-two-halves-of-the-inference-matrix) below. Roughly two gigabytes of CUDA wheels, which is why it is opt-in locally; CI's `inference-smoke` job runs it | — CI |
| Wheel (build, install, serve) | `bash scripts/build_dist.sh && VISIONSET_REQUIRE_WHEEL=1 uv run pytest tests/packaging` — builds the UI into `_static/`, builds the wheel, installs it in a fresh venv and serves `/app/` from it. Opt-in locally (it costs about a minute); CI's `wheel` job runs it and uploads the artifact | — CI |
| The 30-minute flow | `uv run python examples/thirty_minute_flow.py` — the vision document's success metric end to end. CI's `30-minute flow (wheel, end to end)` job runs it from the **installed wheel** in an empty venv, with `ultralytics` required there | — CI |
Expand Down
6 changes: 3 additions & 3 deletions docs/content/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ GET /ingest-jobs/{job_id} → 200 { "state": "completed", "batch_id":
Export follows the same shape, over the generic surface #328 added:

```
POST /releases/{id}/export?format=yolo → 202 Accepted
POST /releases/{id}/export?format=ultralytics → 202 Accepted
Location: /background-jobs/{job_id}

GET /background-jobs/{job_id} → 200 { "state": "running", "processed": 12, … }
Expand Down Expand Up @@ -607,13 +607,13 @@ argument for branching on `code`.
| Status | Codes |
| --- | --- |
| **401** | `UNAUTHORIZED` — with a `WWW-Authenticate: Bearer` challenge |
| **404** | `PROJECT_NOT_FOUND` · `SCHEMA_NOT_FOUND` · `SCHEMA_DRAFT_NOT_FOUND` · `BATCH_NOT_FOUND` · `JOB_NOT_FOUND` · `INGEST_JOB_NOT_FOUND` · `BACKGROUND_JOB_NOT_FOUND` · `ASSET_NOT_FOUND` · `SOURCE_NOT_FOUND` · `DATASET_NOT_FOUND` · `ANNOTATION_NOT_FOUND` · `RELEASE_NOT_FOUND` · `TOKEN_NOT_FOUND` · `INFERENCE_CONNECTION_NOT_FOUND` · `ASSET_NOT_IN_JOB` · `ASSET_NOT_IN_DATASET` · `NO_SPLIT_RECIPE` · `EXPORT_FORMAT_NOT_FOUND` · `THUMBNAIL_NOT_CACHED` · `NOT_FOUND` (no such route) |
| **404** | `PROJECT_NOT_FOUND` · `SCHEMA_NOT_FOUND` · `SCHEMA_DRAFT_NOT_FOUND` · `BATCH_NOT_FOUND` · `JOB_NOT_FOUND` · `INGEST_JOB_NOT_FOUND` · `BACKGROUND_JOB_NOT_FOUND` · `ASSET_NOT_FOUND` · `SOURCE_NOT_FOUND` · `DATASET_NOT_FOUND` · `ANNOTATION_NOT_FOUND` · `RELEASE_NOT_FOUND` · `TOKEN_NOT_FOUND` · `INFERENCE_CONNECTION_NOT_FOUND` · `ASSET_NOT_IN_JOB` · `ASSET_NOT_IN_DATASET` · `NO_SPLIT_RECIPE` · `EXPORT_FORMAT_NOT_FOUND` · `EXPORT_TARGET_NOT_FOUND` · `THUMBNAIL_NOT_CACHED` · `NOT_FOUND` (no such route) |
| **405** | `METHOD_NOT_ALLOWED` |
| **409** | `PROJECT_NAME_TAKEN` · `RELEASE_TAG_TAKEN` · `TOKEN_NAME_TAKEN` · `INFERENCE_CONNECTION_NAME_TAKEN` · `WORKSPACE_ALREADY_EXISTS` · `WORKSPACE_NOT_EMPTY` · `SCHEMA_VERSION_CONFLICT` · `INVALID_TRANSITION` · `STALE_WRITE` · `BATCH_NOT_EDITABLE` · `BATCH_IMMUTABLE` · `BATCH_NOT_IN_ANNOTATION` · `ASSET_NOT_WRITABLE` · `JOB_FINISHED` · `BATCH_NOT_COMPLETE` · `JOB_NOT_COMPLETE` · `EMPTY_BATCH` · `EMPTY_RELEASE` · `RELEASE_CONTENT_WOULD_VIOLATE_SCHEMA` · `CONFIRMATION_REQUIRED` · `DESTRUCTIVE_SCHEMA_CHANGE` · `SCHEMA_CHANGE_WOULD_ORPHAN` · `SCHEMA_HAS_NO_DETECTABLE_CLASS` · `UNSERIALIZABLE_MANIFEST` · `LOSSY_EXPORT_NOT_CONSENTED` · `EXPORT_SOURCE_UNREADABLE` · `INFERENCE_CONNECTION_NOT_DOWNLOADABLE` · `INFERENCE_CONNECTION_NOT_CHECKABLE` · `INFERENCE_CONNECTION_NOT_TESTABLE` · `INFERENCE_CONNECTION_MODEL_FIXED` · `WEIGHTS_DAMAGED` · `INFERENCE_CONNECTION_NOT_SET_UP` |
| **422** | `VALIDATION_ERROR` · `ASSET_NOT_IN_BATCH` · `ANNOTATION_NOT_FROM_MODEL` · `INVALID_NAME` · `INFERENCE_CONNECTION_INVALID` · `INVALID_SCHEMA` · `UNSUPPORTED_GEOMETRY` · `INVALID_ANNOTATION` · `LABEL_CLASS_NOT_IN_SCHEMA` · `DISALLOWED_GEOMETRY` · `ANNOTATION_GEOMETRY_OUT_OF_BOUNDS` · `DUPLICATE_CLASSIFICATION_TAG` · `MISSING_REQUIRED_ATTRIBUTE` · `UNKNOWN_ATTRIBUTE` · `INVALID_ATTRIBUTE_VALUE` · `INVALID_PARTITION` · `UNKNOWN_JOB_TYPE` · `MEDIA_ERROR` · `UNSUPPORTED_MEDIA` · `CORRUPT_MEDIA` · `UNSUPPORTED_PROMPT` · `PROMPT_POINT_OUT_OF_BOUNDS` · `GEOMETRY_NOT_PRODUCED` |
| **502** | `INFERENCE_ENDPOINT_UNAVAILABLE` |
| **503** | `WORKSPACE_BUSY` |
| **500** | `WORKSPACE_CORRUPT` · `NOT_A_WORKSPACE` · `WORKSPACE_FORMAT_TOO_NEW` · `WORKSPACE_SCHEMA_MISMATCH` · `ENTITY_NOT_FOUND` · `ENTITY_ALREADY_EXISTS` · `CONSTRAINT_VIOLATED` · `MEDIA_TOOL_UNAVAILABLE` · `LOCAL_INFERENCE_UNAVAILABLE` · `INFERENCE_CONNECTION_NOT_RUNNABLE` · `INFERENCE_OUT_OF_MEMORY` · `INTERNAL_ERROR` |
| **500** | `WORKSPACE_CORRUPT` · `NOT_A_WORKSPACE` · `WORKSPACE_FORMAT_TOO_NEW` · `WORKSPACE_SCHEMA_MISMATCH` · `ENTITY_NOT_FOUND` · `ENTITY_ALREADY_EXISTS` · `CONSTRAINT_VIOLATED` · `MEDIA_TOOL_UNAVAILABLE` · `LOCAL_INFERENCE_UNAVAILABLE` · `INFERENCE_CONNECTION_NOT_RUNNABLE` · `INFERENCE_OUT_OF_MEMORY` · `EXPORT_TARGET_CONFLICT` · `INVALID_EXPORT_TARGET` · `INTERNAL_ERROR` |

Every row but `VALIDATION_ERROR`, `NOT_FOUND`, `METHOD_NOT_ALLOWED`, `UNAUTHORIZED` and
`INTERNAL_ERROR` — the five the framework and the auth guard raise — comes from `ERROR_RULES`
Expand Down
5 changes: 3 additions & 2 deletions docs/content/architecture/backend/formats.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,14 @@ typo answers a `VisionSetError` rather than a `KeyError` and a traceback.

## What ships

Ten plugins in [`pyproject.toml`](../../../../pyproject.toml)'s
Eleven plugins in [`pyproject.toml`](../../../../pyproject.toml)'s
`[project.entry-points."visionset.formats"]`:

| Name | Module |
| --- | --- |
| `dummy` | [`_dummy.py`](../../../../src/visionset/formats/_dummy.py) - writes nothing; the registry's own test subject |
| `yolo` | [`yolo/`](../../../../src/visionset/formats/yolo/) |
| `ultralytics` | [`ultralytics/`](../../../../src/visionset/formats/ultralytics/) - `data.yaml` with `path` and `names` as a mapping; `yolo` is accepted as an alias for one release |
| `yolov5-yaml` | [`yolov5_yaml/`](../../../../src/visionset/formats/yolov5_yaml/) - `data.yaml` with `nc` and `names` as a list; shares [`_yolo_writer.py`](../../../../src/visionset/formats/_yolo_writer.py) with `ultralytics` |
| `coco` | [`coco/`](../../../../src/visionset/formats/coco/) |
| `voc` | [`voc/`](../../../../src/visionset/formats/voc/) |
| `classification` | [`classification/`](../../../../src/visionset/formats/classification/) - `labels.csv`, one row per (image, tag) |
Expand Down
4 changes: 2 additions & 2 deletions docs/content/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -444,8 +444,8 @@ that declaration covers attributes, confidence and provenance - none of which is
of which the per-class table can show. So:

```bash
visionset export --check -p road-signs --release v1.0 -f yolo && \
visionset export -p road-signs --release v1.0 -f yolo --out ./out
visionset export --check -p road-signs --release v1.0 -f ultralytics && \
visionset export -p road-signs --release v1.0 -f ultralytics --out ./out
```

means what it looks like. The table is on **stdout** and the summary on stderr, so `| cut` gets
Expand Down
Loading
Loading