modelstudio-sdk 0.0.0.dev0__py3-none-any.whl

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.
@@ -0,0 +1,180 @@
1
+ """Metrics and statistics models (OverviewDto + 11 nested types)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from pydantic import BaseModel
8
+
9
+
10
+ class DescriptiveStats(BaseModel):
11
+ """Reusable descriptive statistics for any numeric distribution."""
12
+
13
+ count: int = 0
14
+ min: float = 0.0
15
+ max: float = 0.0
16
+ mean: float = 0.0
17
+ median: float = 0.0
18
+ mode: float | None = None
19
+ std_dev: float = 0.0
20
+ variance: float = 0.0
21
+ q1: float = 0.0
22
+ q3: float = 0.0
23
+ iqr: float = 0.0
24
+ p5: float = 0.0
25
+ p95: float = 0.0
26
+ skewness: float | None = None
27
+ outlier_count: int = 0
28
+
29
+
30
+ class AspectRatioDistribution(BaseModel):
31
+ """Aspect ratio distribution for images."""
32
+
33
+ portrait: int = 0
34
+ landscape: int = 0
35
+ square: int = 0
36
+ min_ratio: float = 0.0
37
+ max_ratio: float = 0.0
38
+ avg_ratio: float = 0.0
39
+ median_ratio: float = 0.0
40
+
41
+
42
+ class ImageDimensionStats(BaseModel):
43
+ """Image dimension statistics."""
44
+
45
+ min_width: int = 0
46
+ max_width: int = 0
47
+ avg_width: float = 0.0
48
+ median_width: float = 0.0
49
+ mode_width: int | None = None
50
+ std_dev_width: float = 0.0
51
+ q1_width: float = 0.0
52
+ q3_width: float = 0.0
53
+ p5_width: float = 0.0
54
+ p95_width: float = 0.0
55
+ outlier_count_width: int = 0
56
+ min_height: int = 0
57
+ max_height: int = 0
58
+ avg_height: float = 0.0
59
+ median_height: float = 0.0
60
+ mode_height: int | None = None
61
+ std_dev_height: float = 0.0
62
+ q1_height: float = 0.0
63
+ q3_height: float = 0.0
64
+ p5_height: float = 0.0
65
+ p95_height: float = 0.0
66
+ outlier_count_height: int = 0
67
+ aspect_ratio: AspectRatioDistribution | None = None
68
+
69
+
70
+ class SyntheticBreakdown(BaseModel):
71
+ """Breakdown of real vs synthetic images."""
72
+
73
+ real_images: int = 0
74
+ synthetic_images: int = 0
75
+ synthetic_ratio: float = 0.0
76
+
77
+
78
+ class AnnotationDistribution(BaseModel):
79
+ """Annotation count distribution across images."""
80
+
81
+ per_image_stats: DescriptiveStats | None = None
82
+ buckets: dict[str, int] = {}
83
+
84
+
85
+ class CategoryStats(BaseModel):
86
+ """Per-category statistics with bbox dimension statistics."""
87
+
88
+ category_name: str | None = None
89
+ annotation_count: int = 0
90
+ bbox_width_stats: DescriptiveStats | None = None
91
+ bbox_height_stats: DescriptiveStats | None = None
92
+ bbox_area_stats: DescriptiveStats | None = None
93
+ zero_area_count: int = 0
94
+ out_of_bounds_count: int = 0
95
+ crowd_count: int = 0
96
+ ignored_count: int = 0
97
+ avg_coverage_percent: float = 0.0
98
+
99
+
100
+ class SegmentationCategoryStats(BaseModel):
101
+ """Per-category pixel coverage stats for segmentation datasets."""
102
+
103
+ category_name: str | None = None
104
+ image_count: int = 0
105
+ total_pixel_count: int = 0
106
+ pixel_percentage: float = 0.0
107
+ avg_coverage_percent: float = 0.0
108
+ pixel_count_stats: DescriptiveStats | None = None
109
+ area_m2: float | None = None
110
+ avg_area_m2_per_image: float | None = None
111
+
112
+
113
+ class DataQualityMetrics(BaseModel):
114
+ """Data quality metrics for a split."""
115
+
116
+ empty_image_count: int = 0
117
+ empty_image_percent: float = 0.0
118
+ crowd_annotation_count: int = 0
119
+ crowd_annotation_percent: float = 0.0
120
+ ignored_annotation_count: int = 0
121
+ ignored_annotation_percent: float = 0.0
122
+ annotations_with_segmentation: int = 0
123
+ annotations_with_keypoints: int = 0
124
+
125
+
126
+ class ClassImbalance(BaseModel):
127
+ """Class imbalance information for a split."""
128
+
129
+ top_class_name: str | None = None
130
+ top_class_count: int = 0
131
+ top_class_share: float = 0.0
132
+ imbalance_score: float = 0.0
133
+
134
+
135
+ class SplitStatistics(BaseModel):
136
+ """Complete statistics for a single split."""
137
+
138
+ image_count: int = 0
139
+ annotation_count: int = 0
140
+ category_count: int = 0
141
+ image_dimensions: ImageDimensionStats | None = None
142
+ images_by_format: dict[str, int] = {}
143
+ synthetic_breakdown: SyntheticBreakdown | None = None
144
+ annotation_distribution: AnnotationDistribution | None = None
145
+ per_category_stats: dict[str, CategoryStats] = {}
146
+ data_quality: DataQualityMetrics | None = None
147
+ class_imbalance: ClassImbalance | None = None
148
+ segmentation_category_stats: dict[str, SegmentationCategoryStats] | None = None
149
+
150
+
151
+ class DatasetSummary(BaseModel):
152
+ """Dataset-level summary derived from per-split statistics."""
153
+
154
+ total_images: int = 0
155
+ total_annotations: int = 0
156
+ total_categories: int = 0
157
+ distinct_classes: int = 0
158
+
159
+
160
+ class OverviewModel(BaseModel):
161
+ """Comprehensive dataset overview from GET /datasets/{id}/metrics/overview."""
162
+
163
+ summary: DatasetSummary | None = None
164
+ splits: dict[str, SplitStatistics] = {}
165
+ category_co_occurrence: dict[str, dict[str, int]] = {}
166
+
167
+
168
+ class ClassDistributionModel(BaseModel):
169
+ """Response from GET /datasets/{id}/metrics/class-distribution."""
170
+
171
+ classes: list[dict[str, Any]] = []
172
+ totals: dict[str, Any] = {}
173
+
174
+
175
+ class BinnedModel(BaseModel):
176
+ """Response from GET /datasets/{id}/metrics/bbox-area."""
177
+
178
+ bins: list[dict[str, Any]] = []
179
+ bin_type: str | None = None
180
+ image_area_norm: bool = False
@@ -0,0 +1,41 @@
1
+ """Oversample models."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+ from uuid import UUID
7
+
8
+ from pydantic import BaseModel
9
+
10
+
11
+ class OversampleRequest(BaseModel):
12
+ """Request for POST /datasets/{id}/oversample."""
13
+
14
+ target_ratio: float
15
+ strategy: str = "PREFER_ANNOTATED"
16
+ seed: int | None = None
17
+ target_split_id: UUID | None = None
18
+
19
+
20
+ class OversampleAnalysisModel(BaseModel):
21
+ """Response from POST /datasets/{id}/oversample/analyze."""
22
+
23
+ current_real_images: int = 0
24
+ current_synthetic_images: int = 0
25
+ current_ratio: float = 0.0
26
+ target_ratio: float = 0.0
27
+ oversample_target: int | str | None = None
28
+ images_to_duplicate: int = 0
29
+ selected_images: list[dict[str, Any]] = []
30
+
31
+
32
+ class OversampleResponseModel(BaseModel):
33
+ """Response from POST /datasets/{id}/oversample."""
34
+
35
+ success: bool = False
36
+ message: str | None = None
37
+ images_duplicated: int = 0
38
+ annotations_duplicated: int = 0
39
+ new_real_count: int = 0
40
+ new_synthetic_count: int = 0
41
+ new_ratio: float = 0.0
@@ -0,0 +1,112 @@
1
+ """Split-related models."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from datetime import datetime
6
+ from typing import Any
7
+ from uuid import UUID
8
+
9
+ from pydantic import BaseModel
10
+
11
+
12
+ class SplitModel(BaseModel):
13
+ """Represents a dataset split."""
14
+
15
+ split_id: UUID
16
+ split_type: str | None = None
17
+ tao_id: UUID | None = None
18
+ name: str | None = None
19
+ description: str | None = None
20
+ created_on: datetime | None = None
21
+ locked: bool = False
22
+ uploaded: bool = False
23
+ import_status: str | None = None
24
+ import_progress: int | None = None
25
+ import_error: str | None = None
26
+ import_started_at: datetime | None = None
27
+ import_finished_at: datetime | None = None
28
+ import_source: str | None = None
29
+
30
+
31
+ class CreateSplitsResponse(BaseModel):
32
+ """Response from POST /datasets/{id}/splits/create."""
33
+
34
+ success: bool
35
+ message: str | None = None
36
+ splits: list[SplitModel] = []
37
+ summary: dict[str, Any] | None = None
38
+
39
+
40
+ class RedistributeResponse(BaseModel):
41
+ """Response from POST /datasets/{id}/splits/redistribute."""
42
+
43
+ previous_distribution: dict[str, Any] | None = None
44
+ new_distribution: dict[str, Any] | None = None
45
+
46
+
47
+ class SmartRedistributeResponse(BaseModel):
48
+ """Response from POST /datasets/{id}/splits/smart-redistribute."""
49
+
50
+ previous_distribution: dict[str, Any] | None = None
51
+ new_distribution: dict[str, Any] | None = None
52
+ class_distribution_error: dict[str, Any] | None = None
53
+ iterations_used: int | None = None
54
+ converged: bool | None = None
55
+
56
+
57
+ class LeakageResponse(BaseModel):
58
+ """Response from POST /datasets/{id}/splits/check-leakage."""
59
+
60
+ has_leakage: bool = False
61
+ leakage_count: int = 0
62
+ method: str | None = None
63
+ leakages: list[dict[str, Any]] = []
64
+ summary: dict[str, Any] | None = None
65
+
66
+
67
+ # ── Class-aware redistribute (replaces smart-redistribute) ───────────────
68
+
69
+
70
+ class SplitStats(BaseModel):
71
+ """Per-split statistics in a class-aware redistribute response."""
72
+
73
+ image_count: int = 0
74
+ annotation_count: int = 0
75
+ ratio: float = 0.0
76
+
77
+
78
+ class ClassError(BaseModel):
79
+ """Per-class distribution error."""
80
+
81
+ category_name: str | None = None
82
+ target_ratio: float = 0.0
83
+ actual_ratio: float = 0.0
84
+ error: float = 0.0
85
+
86
+
87
+ class DistributionError(BaseModel):
88
+ """Per-split distribution error breakdown."""
89
+
90
+ split_name: str | None = None
91
+ mean_error: float = 0.0
92
+ max_error: float = 0.0
93
+ class_errors: list[ClassError] = []
94
+
95
+
96
+ class AlgorithmStats(BaseModel):
97
+ """Algorithm statistics from class-aware redistribution."""
98
+
99
+ iterations_used: int = 0
100
+ converged: bool = False
101
+ final_error: float = 0.0
102
+ elapsed_ms: int | None = None
103
+
104
+
105
+ class ClassAwareRedistributeResponse(BaseModel):
106
+ """Response from POST /datasets/{id}/splits/class-aware-redistribute."""
107
+
108
+ previous_distribution: dict[str, Any] | None = None
109
+ new_distribution: dict[str, Any] | None = None
110
+ split_stats: dict[str, SplitStats] | None = None
111
+ distribution_errors: list[DistributionError] = []
112
+ algorithm_stats: AlgorithmStats | None = None
@@ -0,0 +1,89 @@
1
+ """Validation and QA models."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+ from uuid import UUID
7
+
8
+ from pydantic import BaseModel
9
+
10
+
11
+ class DuplicateItem(BaseModel):
12
+ """A single image in a duplicate group."""
13
+
14
+ image_id: UUID | None = None
15
+ file_name: str | None = None
16
+ split_name: str | None = None
17
+ hash: str | None = None
18
+ uri: str | None = None
19
+
20
+
21
+ class DuplicateGroup(BaseModel):
22
+ """A group of duplicate images."""
23
+
24
+ group_id: int = 0
25
+ duplicate_type: str | None = None
26
+ similarity: float = 0.0
27
+ items: list[DuplicateItem] = []
28
+
29
+
30
+ class DuplicateCheckModel(BaseModel):
31
+ """Response from POST /datasets/{id}/validate/duplicates."""
32
+
33
+ has_duplicates: bool = False
34
+ duplicate_group_count: int = 0
35
+ total_duplicate_images: int = 0
36
+ check_type: str | None = None
37
+ hash_threshold: float = 0.95
38
+ duplicate_groups: list[DuplicateGroup] = []
39
+ summary: dict[str, Any] = {}
40
+
41
+
42
+ class ConflictAnnotation(BaseModel):
43
+ """Annotation in a temporal conflict cluster."""
44
+
45
+ split_id: UUID | None = None
46
+ annotation_id: int = 0
47
+ image_id: UUID | None = None
48
+ image_file_name: str | None = None
49
+ category_name: str | None = None
50
+ category_id: int = 0
51
+ bbox: list[float] = []
52
+ capture_timestamp: str | None = None
53
+ is_outlier: bool = False
54
+
55
+
56
+ class ConflictCluster(BaseModel):
57
+ """A temporal conflict cluster."""
58
+
59
+ cluster_id: int = 0
60
+ tile_id: str | None = None
61
+ annotations: list[ConflictAnnotation] = []
62
+ distinct_categories: int = 0
63
+ suggested_category: str | None = None
64
+ confidence: float = 0.0
65
+
66
+
67
+ class TemporalConflictModel(BaseModel):
68
+ """Response from POST /datasets/{id}/qa/temporal-conflicts."""
69
+
70
+ has_conflicts: bool = False
71
+ clusters: list[ConflictCluster] = []
72
+
73
+
74
+ class ResolveTemporalConflictResponse(BaseModel):
75
+ """Response from POST /datasets/{id}/qa/temporal-conflicts/resolve."""
76
+
77
+ success: bool = False
78
+ message: str | None = None
79
+ dataset_id: str | None = None
80
+ clusters_resolved: int = 0
81
+ annotations_updated: int = 0
82
+
83
+
84
+ class ValidationResultsModel(BaseModel):
85
+ """Generic validation results."""
86
+
87
+ valid: bool = True
88
+ errors: list[dict[str, Any]] = []
89
+ warnings: list[dict[str, Any]] = []
@@ -0,0 +1 @@
1
+ """Resource objects that map to API endpoints."""