x4d-devkit 0.48.0rc2__tar.gz → 0.48.0rc3__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.
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/PKG-INFO +1 -1
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/pyproject.toml +1 -1
- x4d_devkit-0.48.0rc3/tests/test_image_reference.py +267 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_sdk_api.py +1 -1
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/client/client.py +2 -0
- x4d_devkit-0.48.0rc3/x4d_devkit/client/image_references.py +147 -0
- x4d_devkit-0.48.0rc3/x4d_devkit/image_reference.py +438 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit.egg-info/PKG-INFO +1 -1
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit.egg-info/SOURCES.txt +3 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/LICENSE +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/README.md +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/setup.cfg +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_annotation_contract.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_annotation_reference.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_assets_client.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_boundary.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_box.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_build_info.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_category_audit.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_clean_basemap.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_clean_basemap_client.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_cli_auth.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_client_json_contract.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_client_projects.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_clip_archives.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_contract_validation.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_coordinate_frames.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_current_state.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_current_state_identity_consumers_example.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_dataset_contract.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_dataset_identity.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_detection_eval.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_evaluate.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_export_bundle.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_image_clip.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_image_geometry.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_image_review.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_image_work_session.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_inference_input.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_inference_schemas.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_inference_service.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_label_schemas.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_lidar_materialization.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_loader.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_matching.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_metrics.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_models.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_platform_pose.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_preannotation_client.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_reconstruction_client.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_release_gate.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_roadmark_preview.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_semantic_scene_authoring.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_semantic_scene_client.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_semantic_scene_contract.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_semantic_scene_independent.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_semantic_scene_native_clip.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_source_attributes.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_time_contract.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_token.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_tracking_eval.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_training_identity.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_transform.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_transform_chain.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_validation.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_vector_map.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_vector_map_geometry.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_vector_map_session.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_work_session.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/tests/test_work_session_validation_reuse.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/__init__.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/_image_contract.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/annotation_contract.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/annotation_reference.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/audit.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/build_info.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/clean_basemap.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/cli.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/client/__init__.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/client/_origin.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/client/annotation_reference.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/client/assets.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/client/bags.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/client/capabilities.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/client/checkpoints.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/client/clean_basemap.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/client/clip_work_sessions.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/client/clips.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/client/dataset_identity.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/client/image_clips.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/client/image_review.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/client/image_work_sessions.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/client/label_schemas.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/client/preannotation.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/client/projects.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/client/reconstruction.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/client/semantic_scene.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/client/semantic_scene_authoring.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/client/services.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/client/test_sets.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/consumer_identity.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/coordinate_contract.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/core/__init__.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/core/loader.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/core/models.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/core/token.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/core/transform.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/current_state.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/dataset_contract.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/dataset_identity.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/eval/__init__.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/eval/api.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/eval/box.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/eval/detection.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/eval/matching.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/eval/metrics.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/eval/tracking.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/export_bundle.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/image_clip.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/image_geometry.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/image_review.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/image_schema.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/image_work_session.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/inference/__init__.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/inference/input.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/inference/models.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/inference/schemas.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/inference/service.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/inference/tasks.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/json_contract.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/label_schema.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/lidar_materialization.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/materialize.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/roadmark_preview.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/semantic_scene_authoring.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/semantic_scene_contract.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/time_contract.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/training_identity.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/validation/__init__.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/validation/contract.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/validation/platform_pose.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/validation/report.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/validation/validator.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/vector_map.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/vector_map_geometry.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit/work_session.py +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit.egg-info/dependency_links.txt +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit.egg-info/entry_points.txt +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit.egg-info/requires.txt +0 -0
- {x4d_devkit-0.48.0rc2 → x4d_devkit-0.48.0rc3}/x4d_devkit.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "x4d-devkit"
|
|
7
|
-
version = "0.48.
|
|
7
|
+
version = "0.48.0rc3"
|
|
8
8
|
description = "Official X-4D external SDK and CLI for datasets, platform APIs, training bridges, and automation"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "Apache-2.0"
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import copy
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
|
|
7
|
+
from tests.image_fixtures import (
|
|
8
|
+
IMAGE_BYTES as FIXTURE_BYTES,
|
|
9
|
+
clip_payload as base_clip_payload,
|
|
10
|
+
line_object,
|
|
11
|
+
)
|
|
12
|
+
from x4d_devkit.image_clip import build_image_identity, parse_image_clip
|
|
13
|
+
from x4d_devkit.image_reference import (
|
|
14
|
+
IMAGE_INFERENCE_PROTOCOL,
|
|
15
|
+
IMAGE_REFERENCE_CONTRACT,
|
|
16
|
+
build_image_model_profile,
|
|
17
|
+
build_image_prediction_input,
|
|
18
|
+
image_reference_source,
|
|
19
|
+
parse_image_model_profile,
|
|
20
|
+
parse_image_prediction,
|
|
21
|
+
parse_image_prediction_input,
|
|
22
|
+
parse_image_reference,
|
|
23
|
+
parse_image_reference_source,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
IMAGE_BYTES = b"\xff\xd8\xff" + FIXTURE_BYTES
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def clip_payload():
|
|
31
|
+
value = base_clip_payload()
|
|
32
|
+
value["sample_data"][0]["asset"].update(
|
|
33
|
+
size=len(IMAGE_BYTES), sha256=hashlib.sha256(IMAGE_BYTES).hexdigest()
|
|
34
|
+
)
|
|
35
|
+
value["identity"] = build_image_identity(value)
|
|
36
|
+
return value
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def profile():
|
|
40
|
+
return build_image_model_profile(
|
|
41
|
+
{
|
|
42
|
+
"model_name": "test",
|
|
43
|
+
"model_version": "1",
|
|
44
|
+
"artifact": {
|
|
45
|
+
"bucket": "models",
|
|
46
|
+
"object_key": "enet/1.tar.gz",
|
|
47
|
+
"object_version": None,
|
|
48
|
+
"archive_sha256": "a" * 64,
|
|
49
|
+
"weights_sha256": "b" * 64,
|
|
50
|
+
},
|
|
51
|
+
"adapter": "test-v1",
|
|
52
|
+
"parameters": {},
|
|
53
|
+
"runtime": "cpu-test",
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def source():
|
|
59
|
+
return image_reference_source(parse_image_clip(clip_payload()))
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def request():
|
|
63
|
+
return build_image_prediction_input(
|
|
64
|
+
source(),
|
|
65
|
+
image_id="image_a",
|
|
66
|
+
view_id="objects",
|
|
67
|
+
profile_id=profile().to_dict()["profile_id"],
|
|
68
|
+
image_bytes=IMAGE_BYTES,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def artifact():
|
|
73
|
+
return {
|
|
74
|
+
"contract_version": IMAGE_REFERENCE_CONTRACT,
|
|
75
|
+
"run_id": "ref_1",
|
|
76
|
+
"source": source().to_dict(),
|
|
77
|
+
"profile": profile().to_dict(),
|
|
78
|
+
"view_id": "objects",
|
|
79
|
+
"coverage": [
|
|
80
|
+
{
|
|
81
|
+
"image_id": "image_a",
|
|
82
|
+
"status": "predicted",
|
|
83
|
+
"objects": [line_object()],
|
|
84
|
+
"error": None,
|
|
85
|
+
}
|
|
86
|
+
],
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def test_source_and_prediction_never_include_human_data_or_its_revision():
|
|
91
|
+
value = clip_payload()
|
|
92
|
+
value["image_annotation"]["objects"] = [line_object()]
|
|
93
|
+
value["identity"] = build_image_identity(value)
|
|
94
|
+
with_labels = image_reference_source(parse_image_clip(value))
|
|
95
|
+
assert with_labels.to_dict() == source().to_dict()
|
|
96
|
+
assert (
|
|
97
|
+
not {"image_annotation", "annotation_revision", "completion"}
|
|
98
|
+
& with_labels.to_dict().keys()
|
|
99
|
+
)
|
|
100
|
+
encoded = request().to_dict()
|
|
101
|
+
assert "image_annotation" not in str(encoded)
|
|
102
|
+
assert "annotation_revision" not in str(encoded)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def test_profile_binds_all_weights_adapter_parameters_and_runtime():
|
|
106
|
+
value = profile().to_dict()
|
|
107
|
+
for key in ("model_version", "adapter", "runtime"):
|
|
108
|
+
changed = copy.deepcopy(value)
|
|
109
|
+
changed[key] += "new"
|
|
110
|
+
with pytest.raises(ValueError, match="fingerprint"):
|
|
111
|
+
parse_image_model_profile(changed)
|
|
112
|
+
value["parameters"]["threshold"] = 0.7
|
|
113
|
+
with pytest.raises(ValueError, match="fingerprint"):
|
|
114
|
+
parse_image_model_profile(value)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@pytest.mark.parametrize(
|
|
118
|
+
"change", ["gt", "digest", "bytes", "size", "schema", "calibration", "view"]
|
|
119
|
+
)
|
|
120
|
+
def test_reject_source_or_prediction_identity_drift(change):
|
|
121
|
+
value = request().to_dict()
|
|
122
|
+
if change == "gt":
|
|
123
|
+
value["objects"] = []
|
|
124
|
+
if change == "digest":
|
|
125
|
+
value["image"]["asset"]["sha256"] = "0" * 64
|
|
126
|
+
if change == "bytes":
|
|
127
|
+
value["image_base64"] = "!invalid"
|
|
128
|
+
if change == "size":
|
|
129
|
+
value["image"]["asset"]["size"] += 1
|
|
130
|
+
if change == "schema":
|
|
131
|
+
value["context"]["schema_fingerprint"] = "sha256:" + "0" * 64
|
|
132
|
+
if change == "calibration":
|
|
133
|
+
value["calibrated_sensor"]["width"] += 1
|
|
134
|
+
if change == "view":
|
|
135
|
+
value["context"]["view_id"] = "semantics"
|
|
136
|
+
with pytest.raises(ValueError):
|
|
137
|
+
parse_image_prediction_input(value)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def test_source_rejects_revision_drift_and_extra_annotation():
|
|
141
|
+
value = source().to_dict()
|
|
142
|
+
value["sample_data"][0]["asset"]["sha256"] = "0" * 64
|
|
143
|
+
with pytest.raises(ValueError, match="source identity"):
|
|
144
|
+
parse_image_reference_source(value)
|
|
145
|
+
value = source().to_dict()
|
|
146
|
+
value["image_annotation"] = {}
|
|
147
|
+
with pytest.raises(ValueError):
|
|
148
|
+
parse_image_reference_source(value)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def test_prediction_echo_and_native_geometry_schema_validation():
|
|
152
|
+
req = request()
|
|
153
|
+
body = req.to_dict()
|
|
154
|
+
output = {
|
|
155
|
+
"protocol_version": IMAGE_INFERENCE_PROTOCOL,
|
|
156
|
+
"context": body["context"],
|
|
157
|
+
"profile_id": body["profile_id"],
|
|
158
|
+
"image_id": "image_a",
|
|
159
|
+
"objects": [line_object()],
|
|
160
|
+
}
|
|
161
|
+
assert parse_image_prediction(output, request=req).to_dict() == output
|
|
162
|
+
output["objects"][0]["geometry"]["vertices"][0]["x"] = 9000
|
|
163
|
+
with pytest.raises(ValueError):
|
|
164
|
+
parse_image_prediction(output, request=req)
|
|
165
|
+
output["objects"] = []
|
|
166
|
+
output["profile_id"] = "sha256:" + "0" * 64
|
|
167
|
+
with pytest.raises(ValueError, match="context"):
|
|
168
|
+
parse_image_prediction(output, request=req)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@pytest.mark.parametrize("state", ["predicted", "empty", "failed", "unprocessed"])
|
|
172
|
+
def test_reference_states_are_explicit_and_not_human_completion(state):
|
|
173
|
+
value = artifact()
|
|
174
|
+
row = value["coverage"][0]
|
|
175
|
+
row["status"] = state
|
|
176
|
+
if state != "predicted":
|
|
177
|
+
row["objects"] = []
|
|
178
|
+
if state == "failed":
|
|
179
|
+
row["error"] = {"code": "model_failed", "message": "Model execution failed"}
|
|
180
|
+
parsed = parse_image_reference(value)
|
|
181
|
+
assert parsed.to_dict() == value
|
|
182
|
+
assert "completion" not in parsed.to_dict()
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
@pytest.mark.parametrize(
|
|
186
|
+
"change",
|
|
187
|
+
[
|
|
188
|
+
"missing",
|
|
189
|
+
"duplicate",
|
|
190
|
+
"foreign-image",
|
|
191
|
+
"wrong-occupancy",
|
|
192
|
+
"missing-error",
|
|
193
|
+
"extra-gt",
|
|
194
|
+
],
|
|
195
|
+
)
|
|
196
|
+
def test_reference_rejects_incomplete_or_misleading_coverage(change):
|
|
197
|
+
value = artifact()
|
|
198
|
+
if change == "missing":
|
|
199
|
+
value["coverage"] = []
|
|
200
|
+
if change == "duplicate":
|
|
201
|
+
value["coverage"] *= 2
|
|
202
|
+
if change == "foreign-image":
|
|
203
|
+
value["coverage"][0]["image_id"] = "foreign"
|
|
204
|
+
if change == "wrong-occupancy":
|
|
205
|
+
value["coverage"][0]["status"] = "empty"
|
|
206
|
+
if change == "missing-error":
|
|
207
|
+
value["coverage"][0].update(status="failed", objects=[])
|
|
208
|
+
if change == "extra-gt":
|
|
209
|
+
value["completion"] = []
|
|
210
|
+
with pytest.raises(ValueError):
|
|
211
|
+
parse_image_reference(value)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def test_typed_client_pins_source_schema_profile_and_rejects_foreign_results():
|
|
215
|
+
from x4d_devkit.client.image_references import ImageReferencesAPI
|
|
216
|
+
|
|
217
|
+
clip = parse_image_clip(clip_payload())
|
|
218
|
+
identity = clip.to_dict()["identity"]
|
|
219
|
+
state = {
|
|
220
|
+
"run_id": "ref_1",
|
|
221
|
+
"project_id": clip.to_dict()["meta"]["project_id"],
|
|
222
|
+
"clip_id": clip.clip_id,
|
|
223
|
+
"service_id": "curb",
|
|
224
|
+
"profile_id": profile().to_dict()["profile_id"],
|
|
225
|
+
"view_id": "objects",
|
|
226
|
+
"source_revision": identity["source_revision"],
|
|
227
|
+
"schema_fingerprint": identity["schema_fingerprint"],
|
|
228
|
+
"status": "completed",
|
|
229
|
+
"progress": {
|
|
230
|
+
"total": 1,
|
|
231
|
+
"processed": 1,
|
|
232
|
+
"predicted": 1,
|
|
233
|
+
"empty": 0,
|
|
234
|
+
"failed": 0,
|
|
235
|
+
"unprocessed": 0,
|
|
236
|
+
},
|
|
237
|
+
"error": None,
|
|
238
|
+
"created_at": "2026-10-26T00:00:00Z",
|
|
239
|
+
"completed_at": "2026-10-26T00:01:00Z",
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
class Transport:
|
|
243
|
+
def post(self, path, *, json):
|
|
244
|
+
assert path == "/image-references/runs"
|
|
245
|
+
assert "base_annotation_revision" not in json
|
|
246
|
+
assert json["idempotency_key"] == "repeat-safe"
|
|
247
|
+
return state
|
|
248
|
+
|
|
249
|
+
def get(self, path, **kwargs):
|
|
250
|
+
return artifact() if path.endswith("/result") else state
|
|
251
|
+
|
|
252
|
+
client = ImageReferencesAPI(Transport())
|
|
253
|
+
assert (
|
|
254
|
+
client.create(
|
|
255
|
+
clip=clip,
|
|
256
|
+
service_id="curb",
|
|
257
|
+
profile_id=state["profile_id"],
|
|
258
|
+
view_id="objects",
|
|
259
|
+
idempotency_key="repeat-safe",
|
|
260
|
+
).to_dict()
|
|
261
|
+
== state
|
|
262
|
+
)
|
|
263
|
+
assert client.result("ref_1").to_dict() == artifact()
|
|
264
|
+
with pytest.raises(ValueError, match="foreign"):
|
|
265
|
+
client.result("ref_other")
|
|
266
|
+
with pytest.raises(ValueError, match="foreign"):
|
|
267
|
+
client.get("ref_other")
|
|
@@ -11,7 +11,7 @@ from x4d_devkit.eval.matching import MetricData
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def test_package_version_is_current_release():
|
|
14
|
-
assert x4d_devkit.__version__ == "0.48.
|
|
14
|
+
assert x4d_devkit.__version__ == "0.48.0rc3"
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
def test_loader_exposes_and_enforces_only_current_schema(tmp_path):
|
|
@@ -25,6 +25,7 @@ from x4d_devkit.client.dataset_identity import DatasetIdentityAPI
|
|
|
25
25
|
from x4d_devkit.client.label_schemas import LabelSchemasAPI
|
|
26
26
|
from x4d_devkit.client.image_clips import ImageClipsAPI
|
|
27
27
|
from x4d_devkit.client.image_work_sessions import ImageWorkSessionsAPI
|
|
28
|
+
from x4d_devkit.client.image_references import ImageReferencesAPI
|
|
28
29
|
from x4d_devkit.client.semantic_scene import SemanticSceneAPI
|
|
29
30
|
from x4d_devkit.client.semantic_scene_authoring import (
|
|
30
31
|
SemanticSceneAuthoringWorkSessionsAPI,
|
|
@@ -74,6 +75,7 @@ class X4DClient:
|
|
|
74
75
|
self.clips = ClipsAPI(self)
|
|
75
76
|
self.image_clips = ImageClipsAPI(self)
|
|
76
77
|
self.image_work_sessions = ImageWorkSessionsAPI(self)
|
|
78
|
+
self.image_references = ImageReferencesAPI(self)
|
|
77
79
|
self.label_schemas = LabelSchemasAPI(self)
|
|
78
80
|
self.semantic_scene = SemanticSceneAPI(self)
|
|
79
81
|
self.semantic_scene_authoring_work_sessions = (
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"""Explicit native 2D reference runs, independent of human annotation bases."""
|
|
2
|
+
|
|
3
|
+
from urllib.parse import quote
|
|
4
|
+
|
|
5
|
+
from x4d_devkit._image_contract import document, fields, identifier, require
|
|
6
|
+
from x4d_devkit.image_clip import ImageClip
|
|
7
|
+
from x4d_devkit.image_reference import parse_image_reference
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def parse_image_reference_run(value):
|
|
11
|
+
fields(
|
|
12
|
+
value,
|
|
13
|
+
{
|
|
14
|
+
"run_id",
|
|
15
|
+
"project_id",
|
|
16
|
+
"clip_id",
|
|
17
|
+
"service_id",
|
|
18
|
+
"profile_id",
|
|
19
|
+
"view_id",
|
|
20
|
+
"source_revision",
|
|
21
|
+
"schema_fingerprint",
|
|
22
|
+
"status",
|
|
23
|
+
"progress",
|
|
24
|
+
"error",
|
|
25
|
+
"created_at",
|
|
26
|
+
"completed_at",
|
|
27
|
+
},
|
|
28
|
+
"image_reference_run",
|
|
29
|
+
)
|
|
30
|
+
identifier(value["run_id"], "run_id")
|
|
31
|
+
from x4d_devkit._image_contract import integer, text
|
|
32
|
+
from x4d_devkit.image_clip import _digest
|
|
33
|
+
|
|
34
|
+
integer(value["project_id"], "project_id", minimum=1)
|
|
35
|
+
for key in ("clip_id", "service_id", "view_id"):
|
|
36
|
+
identifier(value[key], key)
|
|
37
|
+
for key in ("profile_id", "source_revision", "schema_fingerprint"):
|
|
38
|
+
_digest(value[key], key)
|
|
39
|
+
text(value["created_at"], "created_at")
|
|
40
|
+
if value["completed_at"] is not None:
|
|
41
|
+
text(value["completed_at"], "completed_at")
|
|
42
|
+
if value["error"] is not None:
|
|
43
|
+
fields(value["error"], {"code", "message"}, "error")
|
|
44
|
+
for key in ("code", "message"):
|
|
45
|
+
text(value["error"][key], "error." + key)
|
|
46
|
+
require(
|
|
47
|
+
value["status"] in {"queued", "running", "completed", "failed", "cancelled"},
|
|
48
|
+
"invalid run state",
|
|
49
|
+
"status",
|
|
50
|
+
)
|
|
51
|
+
fields(
|
|
52
|
+
value["progress"],
|
|
53
|
+
{"processed", "total", "predicted", "empty", "failed", "unprocessed"},
|
|
54
|
+
"progress",
|
|
55
|
+
)
|
|
56
|
+
from x4d_devkit._image_contract import integer
|
|
57
|
+
|
|
58
|
+
for key, count in value["progress"].items():
|
|
59
|
+
integer(count, "progress." + key)
|
|
60
|
+
p = value["progress"]
|
|
61
|
+
require(
|
|
62
|
+
p["processed"] == p["predicted"] + p["empty"] + p["failed"]
|
|
63
|
+
and p["total"] == p["processed"] + p["unprocessed"],
|
|
64
|
+
"invalid run counters",
|
|
65
|
+
"progress",
|
|
66
|
+
)
|
|
67
|
+
return document(value)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class ImageReferencesAPI:
|
|
71
|
+
def __init__(self, client):
|
|
72
|
+
self._client = client
|
|
73
|
+
|
|
74
|
+
def create(
|
|
75
|
+
self,
|
|
76
|
+
*,
|
|
77
|
+
clip: ImageClip,
|
|
78
|
+
service_id: str,
|
|
79
|
+
profile_id: str,
|
|
80
|
+
view_id: str,
|
|
81
|
+
idempotency_key: str,
|
|
82
|
+
):
|
|
83
|
+
require(type(clip) is ImageClip, "expected parsed native Clip", "clip")
|
|
84
|
+
value = clip.to_dict()
|
|
85
|
+
request = {
|
|
86
|
+
"clip_id": clip.clip_id,
|
|
87
|
+
"service_id": service_id,
|
|
88
|
+
"profile_id": profile_id,
|
|
89
|
+
"view_id": view_id,
|
|
90
|
+
"idempotency_key": idempotency_key,
|
|
91
|
+
"source_revision": value["identity"]["source_revision"],
|
|
92
|
+
"schema_fingerprint": value["identity"]["schema_fingerprint"],
|
|
93
|
+
}
|
|
94
|
+
result = parse_image_reference_run(
|
|
95
|
+
self._client.post("/image-references/runs", json=request)
|
|
96
|
+
)
|
|
97
|
+
for key, expected in request.items():
|
|
98
|
+
if key != "idempotency_key":
|
|
99
|
+
require(
|
|
100
|
+
result.to_dict()[key] == expected,
|
|
101
|
+
"run request context mismatch",
|
|
102
|
+
key,
|
|
103
|
+
)
|
|
104
|
+
require(
|
|
105
|
+
result.to_dict()["project_id"] == value["meta"]["project_id"],
|
|
106
|
+
"run project context mismatch",
|
|
107
|
+
"project_id",
|
|
108
|
+
)
|
|
109
|
+
return result
|
|
110
|
+
|
|
111
|
+
def list(self, *, clip_id: str, page: int = 1, page_size: int = 20) -> dict:
|
|
112
|
+
identifier(clip_id, "clip_id")
|
|
113
|
+
value = self._client.get(
|
|
114
|
+
"/image-references/runs",
|
|
115
|
+
params={"clip_id": clip_id, "page": page, "page_size": page_size},
|
|
116
|
+
)
|
|
117
|
+
fields(value, {"items", "total", "page", "page_size"}, "image_reference_runs")
|
|
118
|
+
for item in value["items"]:
|
|
119
|
+
parsed = parse_image_reference_run(item).to_dict()
|
|
120
|
+
require(parsed["clip_id"] == clip_id, "foreign Clip run", "clip_id")
|
|
121
|
+
return value
|
|
122
|
+
|
|
123
|
+
def get(self, run_id: str):
|
|
124
|
+
identifier(run_id, "run_id")
|
|
125
|
+
result = parse_image_reference_run(
|
|
126
|
+
self._client.get(f"/image-references/runs/{quote(run_id, safe='')}")
|
|
127
|
+
)
|
|
128
|
+
require(result.to_dict()["run_id"] == run_id, "foreign run", "run_id")
|
|
129
|
+
return result
|
|
130
|
+
|
|
131
|
+
def result(self, run_id: str):
|
|
132
|
+
identifier(run_id, "run_id")
|
|
133
|
+
result = parse_image_reference(
|
|
134
|
+
self._client.get(f"/image-references/runs/{quote(run_id, safe='')}/result")
|
|
135
|
+
)
|
|
136
|
+
require(result.to_dict()["run_id"] == run_id, "foreign reference run", "run_id")
|
|
137
|
+
return result
|
|
138
|
+
|
|
139
|
+
def cancel(self, run_id: str):
|
|
140
|
+
identifier(run_id, "run_id")
|
|
141
|
+
result = parse_image_reference_run(
|
|
142
|
+
self._client.post(
|
|
143
|
+
f"/image-references/runs/{quote(run_id, safe='')}/cancel", json={}
|
|
144
|
+
)
|
|
145
|
+
)
|
|
146
|
+
require(result.to_dict()["run_id"] == run_id, "foreign run", "run_id")
|
|
147
|
+
return result
|