lumen-resources 0.2.0__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.
- lumen_resources/__init__.py +89 -0
- lumen_resources/cli.py +402 -0
- lumen_resources/downloader.py +449 -0
- lumen_resources/exceptions.py +110 -0
- lumen_resources/lumen_config.py +459 -0
- lumen_resources/lumen_config_validator.py +270 -0
- lumen_resources/model_info.py +233 -0
- lumen_resources/model_info_validator.py +257 -0
- lumen_resources/platform.py +270 -0
- lumen_resources/result_schemas/README.md +14 -0
- lumen_resources/result_schemas/__init__.py +14 -0
- lumen_resources/result_schemas/embedding_v1.py +29 -0
- lumen_resources/result_schemas/face_v1.py +55 -0
- lumen_resources/result_schemas/labels_v1.py +39 -0
- lumen_resources/schemas/config-schema.yaml +249 -0
- lumen_resources/schemas/model_info-schema.json +166 -0
- lumen_resources/schemas/result_schemas/embedding_v1.json +35 -0
- lumen_resources/schemas/result_schemas/face_v1.json +61 -0
- lumen_resources/schemas/result_schemas/labels_v1.json +49 -0
- lumen_resources-0.2.0.dist-info/METADATA +133 -0
- lumen_resources-0.2.0.dist-info/RECORD +24 -0
- lumen_resources-0.2.0.dist-info/WHEEL +5 -0
- lumen_resources-0.2.0.dist-info/entry_points.txt +2 -0
- lumen_resources-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Validator for model_info.json files using JSON Schema and Pydantic.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from jsonschema import Draft7Validator
|
|
10
|
+
from pydantic import ValidationError
|
|
11
|
+
|
|
12
|
+
from .model_info import ModelInfo
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ModelInfoValidator:
|
|
16
|
+
"""Validator for model_info.json files.
|
|
17
|
+
|
|
18
|
+
Provides comprehensive validation for model information JSON files using
|
|
19
|
+
both JSON Schema and Pydantic models. Ensures model metadata integrity
|
|
20
|
+
and compliance with the Lumen model specification.
|
|
21
|
+
|
|
22
|
+
Attributes:
|
|
23
|
+
schema: Loaded JSON Schema for validation.
|
|
24
|
+
validator: Draft7Validator instance for JSON Schema validation.
|
|
25
|
+
|
|
26
|
+
Example:
|
|
27
|
+
>>> validator = ModelInfoValidator()
|
|
28
|
+
>>> is_valid, errors = validator.validate_file("model_info.json")
|
|
29
|
+
>>> if not is_valid:
|
|
30
|
+
... for error in errors:
|
|
31
|
+
... print(f"Validation error: {error}")
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(self, schema_path: str | Path | None = None):
|
|
35
|
+
"""Initialize validator with JSON schema.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
schema_path: Path to model_info-schema.json file. If None,
|
|
39
|
+
uses the bundled schema from docs/schemas/model_info-schema.json.
|
|
40
|
+
|
|
41
|
+
Raises:
|
|
42
|
+
FileNotFoundError: If the schema file is not found.
|
|
43
|
+
json.JSONDecodeError: If the schema file contains invalid JSON.
|
|
44
|
+
|
|
45
|
+
Example:
|
|
46
|
+
>>> validator = ModelInfoValidator() # Uses bundled schema
|
|
47
|
+
>>> validator = ModelInfoValidator(Path("custom-schema.json"))
|
|
48
|
+
"""
|
|
49
|
+
if schema_path is None:
|
|
50
|
+
schema_path = Path(__file__).parent / "schemas" / "model_info-schema.json"
|
|
51
|
+
else:
|
|
52
|
+
schema_path = Path(schema_path)
|
|
53
|
+
|
|
54
|
+
if not schema_path.exists():
|
|
55
|
+
raise FileNotFoundError(f"Schema file not found: {schema_path}")
|
|
56
|
+
|
|
57
|
+
with open(schema_path, "r", encoding="utf-8") as f:
|
|
58
|
+
self.schema: dict[str, Any] = json.load(f)
|
|
59
|
+
|
|
60
|
+
self.validator = Draft7Validator(self.schema)
|
|
61
|
+
|
|
62
|
+
def validate_file(
|
|
63
|
+
self, path: str | Path, strict: bool = True
|
|
64
|
+
) -> tuple[bool, list[str]]:
|
|
65
|
+
"""Validate a model_info.json file.
|
|
66
|
+
|
|
67
|
+
Performs validation of model information JSON files using either
|
|
68
|
+
JSON Schema validation (flexible) or Pydantic validation (strict).
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
path: Path to model_info.json file.
|
|
72
|
+
strict: If True, use Pydantic validation with custom validators.
|
|
73
|
+
If False, use JSON Schema validation only.
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Tuple of (is_valid, error_messages) where is_valid indicates
|
|
77
|
+
if the file passes validation, and error_messages contains
|
|
78
|
+
detailed validation error messages.
|
|
79
|
+
|
|
80
|
+
Example:
|
|
81
|
+
>>> validator = ModelInfoValidator()
|
|
82
|
+
>>> is_valid, errors = validator.validate_file("model_info.json", strict=True)
|
|
83
|
+
>>> if not is_valid:
|
|
84
|
+
... for error in errors:
|
|
85
|
+
... print(f"Error: {error}")
|
|
86
|
+
"""
|
|
87
|
+
path = Path(path)
|
|
88
|
+
if not path.exists():
|
|
89
|
+
return False, [f"File not found: {path}"]
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
93
|
+
data = json.load(f)
|
|
94
|
+
except json.JSONDecodeError as e:
|
|
95
|
+
return False, [f"Invalid JSON: {e}"]
|
|
96
|
+
except Exception as e:
|
|
97
|
+
return False, [f"Error reading file: {e}"]
|
|
98
|
+
|
|
99
|
+
if strict:
|
|
100
|
+
return self._validate_with_pydantic(data)
|
|
101
|
+
else:
|
|
102
|
+
return self._validate_with_jsonschema(data)
|
|
103
|
+
|
|
104
|
+
def _validate_with_jsonschema(self, data: dict[str, Any]) -> tuple[bool, list[str]]:
|
|
105
|
+
"""Validate model info data using JSON Schema.
|
|
106
|
+
|
|
107
|
+
Performs flexible validation using the JSON Schema specification.
|
|
108
|
+
This method provides basic structural validation for model information.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
data: Parsed model_info.json data dictionary.
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
Tuple of (is_valid, error_messages) where is_valid indicates
|
|
115
|
+
if the data passes JSON Schema validation.
|
|
116
|
+
|
|
117
|
+
Example:
|
|
118
|
+
>>> validator = ModelInfoValidator()
|
|
119
|
+
>>> is_valid, errors = validator._validate_with_jsonschema(data)
|
|
120
|
+
"""
|
|
121
|
+
errors = sorted(self.validator.iter_errors(data), key=lambda e: e.path)
|
|
122
|
+
|
|
123
|
+
if not errors:
|
|
124
|
+
return True, []
|
|
125
|
+
|
|
126
|
+
error_messages = []
|
|
127
|
+
for error in errors:
|
|
128
|
+
path = ".".join(str(p) for p in error.path) if error.path else "root"
|
|
129
|
+
error_messages.append(f"{error.message} (at: {path})")
|
|
130
|
+
|
|
131
|
+
return False, error_messages
|
|
132
|
+
|
|
133
|
+
def _validate_with_pydantic(self, data: dict[str, Any]) -> tuple[bool, list[str]]:
|
|
134
|
+
"""Validate model info data using Pydantic models.
|
|
135
|
+
|
|
136
|
+
Performs strict validation using Pydantic models with custom validators.
|
|
137
|
+
This provides comprehensive validation including type checking,
|
|
138
|
+
pattern matching, and model-specific business rules.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
data: Parsed model_info.json data dictionary.
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
Tuple of (is_valid, error_messages) where is_valid indicates
|
|
145
|
+
if the data passes Pydantic model validation.
|
|
146
|
+
|
|
147
|
+
Example:
|
|
148
|
+
>>> validator = ModelInfoValidator()
|
|
149
|
+
>>> is_valid, errors = validator._validate_with_pydantic(data)
|
|
150
|
+
"""
|
|
151
|
+
try:
|
|
152
|
+
ModelInfo.model_validate(data)
|
|
153
|
+
return True, []
|
|
154
|
+
except ValidationError as e:
|
|
155
|
+
# Parse pydantic validation errors
|
|
156
|
+
error_messages = []
|
|
157
|
+
for error in e.errors():
|
|
158
|
+
loc = ".".join(str(loc_part) for loc_part in error["loc"])
|
|
159
|
+
msg = error["msg"]
|
|
160
|
+
error_messages.append(f"{msg} (at: {loc})")
|
|
161
|
+
return False, error_messages
|
|
162
|
+
except Exception as e:
|
|
163
|
+
return False, [f"Validation error: {e}"]
|
|
164
|
+
|
|
165
|
+
def validate_and_load(self, path: str | Path) -> ModelInfo:
|
|
166
|
+
"""Validate and load model_info.json file.
|
|
167
|
+
|
|
168
|
+
Performs strict validation using Pydantic models and returns a validated
|
|
169
|
+
ModelInfo instance if successful. This is the recommended method
|
|
170
|
+
for loading model information in production code.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
path: Path to model_info.json file.
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
Validated ModelInfo instance with all data properly typed
|
|
177
|
+
and validated.
|
|
178
|
+
|
|
179
|
+
Raises:
|
|
180
|
+
ValueError: If validation fails or file cannot be loaded.
|
|
181
|
+
FileNotFoundError: If the model_info.json file does not exist.
|
|
182
|
+
json.JSONDecodeError: If the file contains invalid JSON.
|
|
183
|
+
|
|
184
|
+
Example:
|
|
185
|
+
>>> validator = ModelInfoValidator()
|
|
186
|
+
>>> model_info = validator.validate_and_load("model_info.json")
|
|
187
|
+
>>> print(model_info.name)
|
|
188
|
+
'ViT-B-32'
|
|
189
|
+
"""
|
|
190
|
+
path = Path(path)
|
|
191
|
+
is_valid, errors = self.validate_file(path, strict=True)
|
|
192
|
+
|
|
193
|
+
if not is_valid:
|
|
194
|
+
error_msg = "Model info validation failed:\n" + "\n".join(
|
|
195
|
+
f" - {err}" for err in errors
|
|
196
|
+
)
|
|
197
|
+
raise ValueError(error_msg)
|
|
198
|
+
|
|
199
|
+
with open(path, "r", encoding="utf-8") as f:
|
|
200
|
+
data = json.load(f)
|
|
201
|
+
|
|
202
|
+
return ModelInfo.model_validate(data)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def load_and_validate_model_info(path: str | Path) -> ModelInfo:
|
|
206
|
+
"""Load and validate a model_info.json file.
|
|
207
|
+
|
|
208
|
+
This is the recommended way to load model information in production.
|
|
209
|
+
Combines validation and loading into a single operation for convenience
|
|
210
|
+
and ensures that only validated model information is returned.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
path: Path to model_info.json file.
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
Validated ModelInfo instance with all data properly typed and
|
|
217
|
+
validated against the model specification.
|
|
218
|
+
|
|
219
|
+
Raises:
|
|
220
|
+
ValueError: If validation fails or file cannot be loaded.
|
|
221
|
+
FileNotFoundError: If the model_info.json file does not exist.
|
|
222
|
+
json.JSONDecodeError: If the file contains invalid JSON.
|
|
223
|
+
|
|
224
|
+
Example:
|
|
225
|
+
>>> from lumen_resources.model_info_validator import load_and_validate_model_info
|
|
226
|
+
>>> model_info = load_and_validate_model_info("model_info.json")
|
|
227
|
+
>>> print(model_info.version)
|
|
228
|
+
'1.0.0'
|
|
229
|
+
"""
|
|
230
|
+
validator = ModelInfoValidator()
|
|
231
|
+
return validator.validate_and_load(path)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def validate_file(path: str | Path, strict: bool = True) -> tuple[bool, list[str]]:
|
|
235
|
+
"""Convenience function to validate a model_info.json file.
|
|
236
|
+
|
|
237
|
+
Simple one-line function for validating model information JSON files.
|
|
238
|
+
Uses strict validation by default for maximum reliability.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
path: Path to model_info.json file.
|
|
242
|
+
strict: If True, use Pydantic validation with custom validators.
|
|
243
|
+
If False, use JSON Schema validation only. Defaults to True.
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
Tuple of (is_valid, error_messages) where is_valid is True if
|
|
247
|
+
the file passes validation, and error_messages contains detailed
|
|
248
|
+
validation errors if validation fails.
|
|
249
|
+
|
|
250
|
+
Example:
|
|
251
|
+
>>> is_valid, errors = validate_file("model_info.json")
|
|
252
|
+
>>> if not is_valid:
|
|
253
|
+
... for error in errors:
|
|
254
|
+
... print(f"Error: {error}")
|
|
255
|
+
"""
|
|
256
|
+
validator = ModelInfoValidator()
|
|
257
|
+
return validator.validate_file(path, strict=strict)
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Platform Adapter for Model Repository Access
|
|
3
|
+
|
|
4
|
+
This module provides a unified interface for downloading models from HuggingFace Hub
|
|
5
|
+
and ModelScope Hub with efficient file filtering capabilities.
|
|
6
|
+
|
|
7
|
+
Features:
|
|
8
|
+
- Unified API for both HuggingFace and ModelScope platforms
|
|
9
|
+
- File pattern filtering during download (not post-download)
|
|
10
|
+
- Automatic cache management and file organization
|
|
11
|
+
- Force download and cache invalidation support
|
|
12
|
+
- Supports two-phase dataset downloads used by the Downloader:
|
|
13
|
+
1) First pass downloads runtime-specific files plus JSON metadata (model_info.json).
|
|
14
|
+
2) Second pass optionally fetches dataset files using the exact relative path from
|
|
15
|
+
model_info.json's "datasets" mapping via allow_patterns=[relative_path].
|
|
16
|
+
|
|
17
|
+
@requires: Platform-specific SDK installed (huggingface_hub or modelscope)
|
|
18
|
+
@returns: Downloaded model files in local cache with filtering applied
|
|
19
|
+
@errors: DownloadError, PlatformUnavailableError
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import shutil
|
|
23
|
+
from enum import Enum
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
from types import ModuleType
|
|
26
|
+
|
|
27
|
+
from .exceptions import DownloadError, PlatformUnavailableError
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class PlatformType(str, Enum):
|
|
31
|
+
"""Supported model repository platforms.
|
|
32
|
+
|
|
33
|
+
Defines the platforms that can be used for downloading models.
|
|
34
|
+
Each platform has its own SDK and API requirements.
|
|
35
|
+
|
|
36
|
+
Attributes:
|
|
37
|
+
HUGGINGFACE: Hugging Face Hub platform.
|
|
38
|
+
MODELSCOPE: ModelScope Hub platform.
|
|
39
|
+
|
|
40
|
+
Example:
|
|
41
|
+
>>> platform_type = PlatformType.HUGGINGFACE
|
|
42
|
+
>>> print(platform_type.value)
|
|
43
|
+
'huggingface'
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
HUGGINGFACE = "huggingface"
|
|
47
|
+
MODELSCOPE = "modelscope"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class Platform:
|
|
51
|
+
"""Unified platform adapter for HuggingFace and ModelScope.
|
|
52
|
+
|
|
53
|
+
Provides a consistent interface for downloading models from different
|
|
54
|
+
repositories while handling platform-specific requirements and optimizations.
|
|
55
|
+
Supports efficient file filtering during download to minimize bandwidth usage.
|
|
56
|
+
|
|
57
|
+
Attributes:
|
|
58
|
+
platform_type: The type of platform (HUGGINGFACE or MODELSCOPE).
|
|
59
|
+
owner: Organization/owner name for model repositories.
|
|
60
|
+
|
|
61
|
+
Example:
|
|
62
|
+
>>> platform = Platform(PlatformType.HUGGINGFACE, "openai")
|
|
63
|
+
>>> model_path = platform.download_model(
|
|
64
|
+
... repo_name="clip-vit-base-patch32",
|
|
65
|
+
... cache_dir=Path("/cache"),
|
|
66
|
+
... allow_patterns=["*.json", "*.pt"]
|
|
67
|
+
... )
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
def __init__(self, platform_type: PlatformType, owner: str):
|
|
71
|
+
"""Initialize platform adapter.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
platform_type: Type of platform (HUGGINGFACE or MODELSCOPE).
|
|
75
|
+
owner: Organization/owner name on the platform.
|
|
76
|
+
|
|
77
|
+
Raises:
|
|
78
|
+
PlatformUnavailableError: If required SDK is not installed.
|
|
79
|
+
"""
|
|
80
|
+
self.platform_type: PlatformType = platform_type
|
|
81
|
+
self.owner: str = owner
|
|
82
|
+
self._check_availability()
|
|
83
|
+
|
|
84
|
+
def _check_availability(self) -> None:
|
|
85
|
+
"""Check if the required platform SDK is available.
|
|
86
|
+
|
|
87
|
+
Validates that the appropriate SDK (huggingface_hub or modelscope)
|
|
88
|
+
is installed and imports the necessary functions for the platform.
|
|
89
|
+
|
|
90
|
+
Raises:
|
|
91
|
+
PlatformUnavailableError: If the required SDK is not installed.
|
|
92
|
+
|
|
93
|
+
Example:
|
|
94
|
+
>>> platform = Platform(PlatformType.HUGGINGFACE, "owner")
|
|
95
|
+
>>> # If huggingface_hub is not installed, raises PlatformUnavailableError
|
|
96
|
+
"""
|
|
97
|
+
if self.platform_type == PlatformType.HUGGINGFACE:
|
|
98
|
+
try:
|
|
99
|
+
import huggingface_hub
|
|
100
|
+
|
|
101
|
+
self._hf_hub: ModuleType = huggingface_hub
|
|
102
|
+
except ImportError:
|
|
103
|
+
raise PlatformUnavailableError(
|
|
104
|
+
"HuggingFace Hub SDK not available. "
|
|
105
|
+
+ "Install with: pip install huggingface_hub"
|
|
106
|
+
)
|
|
107
|
+
elif self.platform_type == PlatformType.MODELSCOPE:
|
|
108
|
+
try:
|
|
109
|
+
from modelscope.hub.snapshot_download import snapshot_download
|
|
110
|
+
|
|
111
|
+
self._ms_snapshot_download = snapshot_download
|
|
112
|
+
except ImportError:
|
|
113
|
+
raise PlatformUnavailableError(
|
|
114
|
+
"ModelScope SDK not available. Install with: pip install modelscope"
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
def download_model(
|
|
118
|
+
self,
|
|
119
|
+
repo_name: str,
|
|
120
|
+
cache_dir: Path,
|
|
121
|
+
allow_patterns: list[str],
|
|
122
|
+
force: bool = False,
|
|
123
|
+
) -> Path:
|
|
124
|
+
"""Download model files from the platform with efficient filtering.
|
|
125
|
+
|
|
126
|
+
Downloads model files using pattern-based filtering to minimize bandwidth
|
|
127
|
+
usage. Supports both HuggingFace and ModelScope platforms with their
|
|
128
|
+
respective SDKs while providing a unified interface.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
repo_name: Repository name (without owner prefix).
|
|
132
|
+
cache_dir: Local cache directory for storing downloaded models.
|
|
133
|
+
allow_patterns: List of glob patterns for files to download.
|
|
134
|
+
Examples: ['*.json', '*.bin', 'tokenizer/*', 'model_info.json'].
|
|
135
|
+
force: Force re-download even if cached.
|
|
136
|
+
- HuggingFace: Uses native force_download parameter.
|
|
137
|
+
- ModelScope: Clears cache directory before download.
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
Path to the downloaded model directory.
|
|
141
|
+
|
|
142
|
+
Raises:
|
|
143
|
+
DownloadError: If download fails for any reason.
|
|
144
|
+
|
|
145
|
+
Example:
|
|
146
|
+
>>> platform = Platform(PlatformType.HUGGINGFACE, "openai")
|
|
147
|
+
>>> model_path = platform.download_model(
|
|
148
|
+
... repo_name="clip-vit-base-patch32",
|
|
149
|
+
... cache_dir=Path("/cache"),
|
|
150
|
+
... allow_patterns=["*.json", "*.pt"],
|
|
151
|
+
... force=True
|
|
152
|
+
... )
|
|
153
|
+
>>> print(model_path.name)
|
|
154
|
+
'clip-vit-base-patch32'
|
|
155
|
+
"""
|
|
156
|
+
repo_id = f"{self.owner}/{repo_name}"
|
|
157
|
+
target_dir = cache_dir / "models" / repo_name
|
|
158
|
+
|
|
159
|
+
try:
|
|
160
|
+
if self.platform_type == PlatformType.HUGGINGFACE:
|
|
161
|
+
return self._download_from_huggingface(
|
|
162
|
+
repo_id, target_dir, allow_patterns, force
|
|
163
|
+
)
|
|
164
|
+
elif self.platform_type == PlatformType.MODELSCOPE:
|
|
165
|
+
return self._download_from_modelscope(
|
|
166
|
+
repo_id, target_dir, allow_patterns, force
|
|
167
|
+
)
|
|
168
|
+
else:
|
|
169
|
+
raise DownloadError(f"Unsupported platform type: {self.platform_type}")
|
|
170
|
+
except Exception as e:
|
|
171
|
+
raise DownloadError(f"Failed to download {repo_id}: {e}") from e
|
|
172
|
+
|
|
173
|
+
def _download_from_huggingface(
|
|
174
|
+
self,
|
|
175
|
+
repo_id: str,
|
|
176
|
+
cache_dir: Path,
|
|
177
|
+
allow_patterns: list[str],
|
|
178
|
+
force: bool,
|
|
179
|
+
) -> Path:
|
|
180
|
+
"""Download from HuggingFace Hub.
|
|
181
|
+
|
|
182
|
+
Uses the huggingface_hub library to download model files with
|
|
183
|
+
pattern-based filtering and optional force re-download.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
repo_id: Full repository ID (owner/repo).
|
|
187
|
+
cache_dir: Local cache directory for storing files.
|
|
188
|
+
allow_patterns: File patterns to download.
|
|
189
|
+
force: Whether to force re-download ignoring cache.
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
Path to the downloaded model directory.
|
|
193
|
+
|
|
194
|
+
Example:
|
|
195
|
+
>>> platform = Platform(PlatformType.HUGGINGFACE, "owner")
|
|
196
|
+
>>> path = platform._download_from_huggingface(
|
|
197
|
+
... "owner/repo", Path("/cache"), ["*.json"], False
|
|
198
|
+
... )
|
|
199
|
+
"""
|
|
200
|
+
_ = self._hf_hub.snapshot_download(
|
|
201
|
+
repo_id=repo_id,
|
|
202
|
+
allow_patterns=allow_patterns,
|
|
203
|
+
local_dir=cache_dir,
|
|
204
|
+
local_files_only=False,
|
|
205
|
+
force_download=force,
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
return cache_dir
|
|
209
|
+
|
|
210
|
+
def _download_from_modelscope(
|
|
211
|
+
self,
|
|
212
|
+
repo_id: str,
|
|
213
|
+
cache_dir: Path,
|
|
214
|
+
allow_patterns: list[str],
|
|
215
|
+
force: bool,
|
|
216
|
+
) -> Path:
|
|
217
|
+
"""Download from ModelScope Hub.
|
|
218
|
+
|
|
219
|
+
Uses the ModelScope SDK to download model files with pattern-based filtering.
|
|
220
|
+
Implements force download by clearing the cache directory before download.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
repo_id: Full repository ID (owner/repo).
|
|
224
|
+
cache_dir: Local cache directory for storing files.
|
|
225
|
+
allow_patterns: File patterns to download.
|
|
226
|
+
force: Force re-download by clearing cache first.
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
Path to the downloaded model directory.
|
|
230
|
+
|
|
231
|
+
Example:
|
|
232
|
+
>>> platform = Platform(PlatformType.MODELSCOPE, "owner")
|
|
233
|
+
>>> path = platform._download_from_modelscope(
|
|
234
|
+
... "owner/repo", Path("/cache"), ["*.json"], False
|
|
235
|
+
... )
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
# Handle force download by clearing ModelScope cache
|
|
239
|
+
if force:
|
|
240
|
+
if cache_dir.exists():
|
|
241
|
+
shutil.rmtree(cache_dir)
|
|
242
|
+
|
|
243
|
+
# ModelScope supports allow_patterns parameter (HuggingFace compatible)
|
|
244
|
+
_ = self._ms_snapshot_download(
|
|
245
|
+
model_id=repo_id,
|
|
246
|
+
local_dir=str(cache_dir),
|
|
247
|
+
allow_patterns=allow_patterns,
|
|
248
|
+
local_files_only=False,
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
return cache_dir
|
|
252
|
+
|
|
253
|
+
def cleanup_model(self, repo_name: str, cache_dir: Path) -> None:
|
|
254
|
+
"""Remove a model directory from cache.
|
|
255
|
+
|
|
256
|
+
Used for cleanup when download/validation fails or for manual cache management.
|
|
257
|
+
Removes the entire model directory including all downloaded files.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
repo_name: Repository name (without owner prefix).
|
|
261
|
+
cache_dir: Base cache directory containing models.
|
|
262
|
+
|
|
263
|
+
Example:
|
|
264
|
+
>>> platform = Platform(PlatformType.HUGGINGFACE, "owner")
|
|
265
|
+
>>> platform.cleanup_model("model-name", Path("/cache"))
|
|
266
|
+
>>> # Model directory removed if it existed
|
|
267
|
+
"""
|
|
268
|
+
target_dir = cache_dir / "models" / repo_name
|
|
269
|
+
if target_dir.exists():
|
|
270
|
+
shutil.rmtree(target_dir)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
```bash
|
|
2
|
+
datamodel-codegen \
|
|
3
|
+
--input src/lumen_resources/schemas/result_schemas \
|
|
4
|
+
--output src/lumen_resources/result_schemas/ \
|
|
5
|
+
--use-schema-description \
|
|
6
|
+
--use-field-description \
|
|
7
|
+
--target-python-version 3.10 \
|
|
8
|
+
--use-standard-collections \
|
|
9
|
+
--use-union-operator \
|
|
10
|
+
--output-model-type pydantic_v2.BaseModel \
|
|
11
|
+
--field-constraints \
|
|
12
|
+
--input-file-type jsonschema
|
|
13
|
+
```
|
|
14
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# generated by datamodel-codegen:
|
|
2
|
+
# filename: result_schemas
|
|
3
|
+
# timestamp: 2025-11-28T17:04:43+00:00
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from .embedding_v1 import EmbeddingV1
|
|
7
|
+
from .face_v1 import FaceV1
|
|
8
|
+
from .labels_v1 import LabelsV1
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"FaceV1",
|
|
12
|
+
"EmbeddingV1",
|
|
13
|
+
"LabelsV1",
|
|
14
|
+
]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# generated by datamodel-codegen:
|
|
2
|
+
# filename: embedding_v1.json
|
|
3
|
+
# timestamp: 2025-11-28T17:04:43+00:00
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class EmbeddingV1(BaseModel):
|
|
11
|
+
"""
|
|
12
|
+
Universal schema for embedding responses across all Lumen services (face, clip, ocr, etc.)
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
model_config = ConfigDict(
|
|
16
|
+
extra='forbid',
|
|
17
|
+
)
|
|
18
|
+
vector: list[float] = Field(..., min_length=1)
|
|
19
|
+
"""
|
|
20
|
+
Embedding vector
|
|
21
|
+
"""
|
|
22
|
+
dim: int = Field(..., ge=1)
|
|
23
|
+
"""
|
|
24
|
+
Embedding dimension
|
|
25
|
+
"""
|
|
26
|
+
model_id: str = Field(..., min_length=1)
|
|
27
|
+
"""
|
|
28
|
+
Model identifier that generated the embedding
|
|
29
|
+
"""
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# generated by datamodel-codegen:
|
|
2
|
+
# filename: face_v1.json
|
|
3
|
+
# timestamp: 2025-11-28T17:04:43+00:00
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, Field, RootModel
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BboxItem(RootModel[float]):
|
|
11
|
+
root: float = Field(..., ge=0.0)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Face(BaseModel):
|
|
15
|
+
model_config = ConfigDict(
|
|
16
|
+
extra='forbid',
|
|
17
|
+
)
|
|
18
|
+
bbox: list[BboxItem] = Field(..., max_length=4, min_length=4)
|
|
19
|
+
"""
|
|
20
|
+
Bounding box coordinates [x1, y1, x2, y2] where (x1, y1) is top-left corner and (x2, y2) is bottom-right corner
|
|
21
|
+
"""
|
|
22
|
+
confidence: float = Field(..., ge=0.0, le=1.0)
|
|
23
|
+
"""
|
|
24
|
+
Detection confidence score
|
|
25
|
+
"""
|
|
26
|
+
landmarks: list[float] | None = None
|
|
27
|
+
"""
|
|
28
|
+
Facial landmark points (optional)
|
|
29
|
+
"""
|
|
30
|
+
embedding: list[float] | None = None
|
|
31
|
+
"""
|
|
32
|
+
Face embedding vector for recognition/comparison (optional)
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class FaceV1(BaseModel):
|
|
37
|
+
"""
|
|
38
|
+
Universal schema for face detection and embedding responses across Lumen services
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
model_config = ConfigDict(
|
|
42
|
+
extra='forbid',
|
|
43
|
+
)
|
|
44
|
+
faces: list[Face]
|
|
45
|
+
"""
|
|
46
|
+
Detected faces with bounding boxes and metadata
|
|
47
|
+
"""
|
|
48
|
+
count: int = Field(..., ge=0)
|
|
49
|
+
"""
|
|
50
|
+
Number of detected faces
|
|
51
|
+
"""
|
|
52
|
+
model_id: str = Field(..., min_length=1)
|
|
53
|
+
"""
|
|
54
|
+
Model identifier
|
|
55
|
+
"""
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# generated by datamodel-codegen:
|
|
2
|
+
# filename: labels_v1.json
|
|
3
|
+
# timestamp: 2025-11-28T17:04:43+00:00
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Label(BaseModel):
|
|
11
|
+
model_config = ConfigDict(
|
|
12
|
+
extra='forbid',
|
|
13
|
+
)
|
|
14
|
+
label: str
|
|
15
|
+
"""
|
|
16
|
+
The classification label or class name
|
|
17
|
+
"""
|
|
18
|
+
score: float
|
|
19
|
+
"""
|
|
20
|
+
Confidence score for this label
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class LabelsV1(BaseModel):
|
|
25
|
+
"""
|
|
26
|
+
Classification response schema for Lumen services. Returns ranked labels with confidence scores.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
model_config = ConfigDict(
|
|
30
|
+
extra='forbid',
|
|
31
|
+
)
|
|
32
|
+
labels: list[Label]
|
|
33
|
+
"""
|
|
34
|
+
Array of classification results
|
|
35
|
+
"""
|
|
36
|
+
model_id: str = Field(..., min_length=1)
|
|
37
|
+
"""
|
|
38
|
+
Identifier of the model that generated the classification
|
|
39
|
+
"""
|