aipmodel 0.2.71__tar.gz → 0.2.73__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.
- {aipmodel-0.2.71 → aipmodel-0.2.73}/PKG-INFO +1 -1
- {aipmodel-0.2.71 → aipmodel-0.2.73}/README.md +91 -1
- {aipmodel-0.2.71 → aipmodel-0.2.73}/aipmodel/__init__.py +1 -1
- {aipmodel-0.2.71 → aipmodel-0.2.73}/aipmodel/model_registry.py +49 -2
- {aipmodel-0.2.71 → aipmodel-0.2.73}/aipmodel.egg-info/PKG-INFO +1 -1
- {aipmodel-0.2.71 → aipmodel-0.2.73}/setup.py +1 -1
- {aipmodel-0.2.71 → aipmodel-0.2.73}/MANIFEST.in +0 -0
- {aipmodel-0.2.71 → aipmodel-0.2.73}/aipmodel/CephS3Manager.py +0 -0
- {aipmodel-0.2.71 → aipmodel-0.2.73}/aipmodel/acl_manager.py +0 -0
- {aipmodel-0.2.71 → aipmodel-0.2.73}/aipmodel/exceptions.py +0 -0
- {aipmodel-0.2.71 → aipmodel-0.2.73}/aipmodel/template.py +0 -0
- {aipmodel-0.2.71 → aipmodel-0.2.73}/aipmodel/update_checker.py +0 -0
- {aipmodel-0.2.71 → aipmodel-0.2.73}/aipmodel.egg-info/SOURCES.txt +0 -0
- {aipmodel-0.2.71 → aipmodel-0.2.73}/aipmodel.egg-info/dependency_links.txt +0 -0
- {aipmodel-0.2.71 → aipmodel-0.2.73}/aipmodel.egg-info/requires.txt +0 -0
- {aipmodel-0.2.71 → aipmodel-0.2.73}/aipmodel.egg-info/top_level.txt +0 -0
- {aipmodel-0.2.71 → aipmodel-0.2.73}/requirements.txt +0 -0
- {aipmodel-0.2.71 → aipmodel-0.2.73}/setup.cfg +0 -0
|
@@ -120,6 +120,71 @@ reset). The SDK now **retries these automatically**, so `add_model`,
|
|
|
120
120
|
worker) is fully protected; for multiple workers/replicas add a distributed
|
|
121
121
|
lock (Mlops-Core/Redis).
|
|
122
122
|
|
|
123
|
+
## Registry ↔ Storage Consistency (Integrity, Repair & Disaster Recovery)
|
|
124
|
+
|
|
125
|
+
A model lives in **two systems at once**: its metadata in the registry
|
|
126
|
+
(Mlops-Core) and its files in S3/Ceph. If either side changes behind the SDK's
|
|
127
|
+
back (someone deletes a folder in Ceph by hand, or the registry loses data),
|
|
128
|
+
you get a **zombie** (registry entry pointing at missing files) or an
|
|
129
|
+
**orphan** (files no registry entry references). The SDK detects, repairs, and
|
|
130
|
+
recovers all of these:
|
|
131
|
+
|
|
132
|
+
### Recovery sidecar: `_meta.json`
|
|
133
|
+
|
|
134
|
+
Every `add_model` writes a small `_meta.json` next to each version's files
|
|
135
|
+
(model name, version, number, category, tags, owner, size, created, public).
|
|
136
|
+
Storage is therefore **self-describing** — a registry entry can be rebuilt from
|
|
137
|
+
S3 alone.
|
|
138
|
+
|
|
139
|
+
### Detect
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
report = manager.verify_model_integrity(model_name="my_model")
|
|
143
|
+
# {"status": "ok" | "missing_files" | "orphan_files",
|
|
144
|
+
# "versions": [{"version": "v1.0", "files_exist": True, ...}],
|
|
145
|
+
# "orphan_folders": [...]}
|
|
146
|
+
|
|
147
|
+
manager.verify_all_models() # one report per model in your project
|
|
148
|
+
manager.find_orphan_models() # S3 model folders unknown to the registry
|
|
149
|
+
# (each flagged recoverable=True when _meta.json exists)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Repair (each guarded against misuse)
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
# Zombie version (files deleted in Ceph) -> remove it from the registry.
|
|
156
|
+
# REFUSES if the files actually still exist (use delete_model for a real delete).
|
|
157
|
+
manager.remove_missing_version("my_model", "v1.0")
|
|
158
|
+
|
|
159
|
+
# Stray folders under a model that no version references -> delete them.
|
|
160
|
+
manager.purge_orphan_folders(model_name="my_model")
|
|
161
|
+
|
|
162
|
+
# S3 model folder the registry doesn't know -> delete it.
|
|
163
|
+
# REFUSES if the id IS registered.
|
|
164
|
+
manager.purge_orphan_model(model_id)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Disaster recovery (registry data lost, S3 survives)
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
for orphan in manager.find_orphan_models():
|
|
171
|
+
if orphan["recoverable"]:
|
|
172
|
+
manager.recover_model_from_storage(orphan["model_id"])
|
|
173
|
+
# Rebuilds the registry entry (name, all versions, latest, sizes, category,
|
|
174
|
+
# tags) from the _meta.json sidecars. Files stay where they are.
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Consistency is also protected *proactively*: `add_model` rolls back files,
|
|
178
|
+
registry entry and ACL record together on failure, and `delete_model` updates
|
|
179
|
+
the registry **before** deleting S3 (a crash can leave a harmless orphan, never
|
|
180
|
+
a zombie). Versions added before the sidecar feature (< 0.2.69) have no
|
|
181
|
+
`_meta.json` and are reported as non-recoverable.
|
|
182
|
+
|
|
183
|
+
> **S3-compat note:** the client is configured to stay compatible with
|
|
184
|
+
> MinIO/Ceph RGW across boto3 versions (legacy `Content-MD5` checksums on
|
|
185
|
+
> newer boto3, with a per-object delete fallback if a batch delete is
|
|
186
|
+
> rejected) — no user action needed.
|
|
187
|
+
|
|
123
188
|
## Access Control (ACL)
|
|
124
189
|
|
|
125
190
|
ACL is **off by default** and fully optional. Toggle it with one env var:
|
|
@@ -314,7 +379,32 @@ manager.delete_model(model_name="demo_model_hf")
|
|
|
314
379
|
| **`set_latest_version`** | `model_name`, `version` | Updates the 'latest' pointer to a specific version without modifying files. |
|
|
315
380
|
| **`set_model_public`** | `model_name`, `public` | Moves a model's files between the private and public buckets (transactional, parallel server-side copy). |
|
|
316
381
|
| **`share_model`** | `model_name`, `target_user`, `access_level` | Grants another user access to a model. Requires `ACL_ENABLED=true`. |
|
|
317
|
-
| **`delete_model`** | `model_name`, `version` | If `version` provided: deletes that version (and rolls "latest" back to the next one if it was the latest). If that was the *last* version, the model container is removed too. If `version` is omitted: deletes the entire model in one call — this already is "delete all versions", no need to delete one by one. |
|
|
382
|
+
| **`delete_model`** | `model_name`, `version` | If `version` provided: deletes that version (and rolls "latest" back to the next one if it was the latest). If that was the *last* version, the model container is removed too. If `version` is omitted: deletes the entire model in one call — this already is "delete all versions", no need to delete one by one. Registry metadata is updated **before** S3 files are removed (crash-safe ordering). |
|
|
383
|
+
| **`verify_model_integrity`** | `model_name` or `model_id` | Cross-checks one model between registry and S3. Returns `{status: ok\|missing_files\|orphan_files, versions: [...], orphan_folders: [...]}`. |
|
|
384
|
+
| **`verify_all_models`** | — | Runs `verify_model_integrity` over every model in your project; returns a list of reports. |
|
|
385
|
+
| **`find_orphan_models`** | — | Lists S3 model folders the registry doesn't know about, each flagged `recoverable` when a `_meta.json` sidecar exists. |
|
|
386
|
+
| **`remove_missing_version`** | `model_name`, `version` | Sync-repair for a zombie version whose S3 files were deleted: removes it from the registry. Refuses if the files still exist. |
|
|
387
|
+
| **`purge_orphan_folders`** | `model_name` or `model_id` | Deletes S3 folders under a model that no registry version references. Returns the purged folder names. |
|
|
388
|
+
| **`purge_orphan_model`** | `model_id` | Deletes the S3 storage of a model unknown to the registry. Refuses for registered ids. |
|
|
389
|
+
| **`recover_model_from_storage`** | `model_id` | Disaster recovery: rebuilds the registry entry (all versions, latest, category, tags) from the `_meta.json` sidecars in S3. Returns the new registry id. |
|
|
390
|
+
|
|
391
|
+
### Backward compatibility
|
|
392
|
+
|
|
393
|
+
The ClearML → Mlops-Core rename kept the old public names working, so code
|
|
394
|
+
written against the pre-0.2.65 SDK does not break:
|
|
395
|
+
|
|
396
|
+
- **Health-check methods** — `check_clearml_service()` and `check_clearml_auth()`
|
|
397
|
+
still exist as thin aliases of `check_mlops_core_service()` /
|
|
398
|
+
`check_mlops_core_auth()`.
|
|
399
|
+
- **Instance attributes** — `mgr.CLEARML_API_HOST`, `CLEARML_WEB_HOST`,
|
|
400
|
+
`CLEARML_USERNAME`, `CLEARML_ACCESS_KEY`, `CLEARML_SECRET_KEY` are kept as
|
|
401
|
+
aliases of the canonical `MLOPS_CORE_*` attributes.
|
|
402
|
+
- **Constructor / env** — the `CLEARML_API_HOST` / `CLEARML_WEB_HOST` kwargs and
|
|
403
|
+
env vars are accepted as deprecated fallbacks (the `MLOPS_CORE_*` names win if
|
|
404
|
+
both are set).
|
|
405
|
+
- **Method signatures are additive only** — every new parameter (`owner`,
|
|
406
|
+
`model_folder_name`, the `list_models` filters) is optional with a default
|
|
407
|
+
that reproduces the old behavior; nothing was removed or reordered.
|
|
318
408
|
|
|
319
409
|
---
|
|
320
410
|
|
|
@@ -434,7 +434,17 @@ class MLOpsManager:
|
|
|
434
434
|
# from the user-management wire format — do not rename them.
|
|
435
435
|
self.MLOPS_CORE_ACCESS_KEY = user_info["clearml_access_key"]
|
|
436
436
|
self.MLOPS_CORE_SECRET_KEY = user_info["clearml_secret_key"]
|
|
437
|
-
|
|
437
|
+
|
|
438
|
+
# Backward-compatibility attribute aliases (pre-0.2.65 names). Callers
|
|
439
|
+
# that read mgr.CLEARML_API_HOST / CLEARML_WEB_HOST / CLEARML_USERNAME /
|
|
440
|
+
# CLEARML_ACCESS_KEY / CLEARML_SECRET_KEY keep working after the
|
|
441
|
+
# Mlops-Core rename — the new MLOPS_CORE_* names are canonical.
|
|
442
|
+
self.CLEARML_API_HOST = self.MLOPS_CORE_API_HOST
|
|
443
|
+
self.CLEARML_WEB_HOST = self.MLOPS_CORE_WEB_HOST
|
|
444
|
+
self.CLEARML_USERNAME = self.MLOPS_CORE_USERNAME
|
|
445
|
+
self.CLEARML_ACCESS_KEY = self.MLOPS_CORE_ACCESS_KEY
|
|
446
|
+
self.CLEARML_SECRET_KEY = self.MLOPS_CORE_SECRET_KEY
|
|
447
|
+
|
|
438
448
|
if not skip_connection_check:
|
|
439
449
|
if self.verbose:
|
|
440
450
|
print("[SDK_INFO] Performing health checks...")
|
|
@@ -608,6 +618,16 @@ class MLOpsManager:
|
|
|
608
618
|
print(f"[SDK_FAIL] Mlops-Core Auth Check Failed: {e}")
|
|
609
619
|
return False
|
|
610
620
|
|
|
621
|
+
# Backward-compatibility aliases. These methods were renamed
|
|
622
|
+
# check_clearml_* -> check_mlops_core_* as part of the Mlops-Core rename;
|
|
623
|
+
# the old names are kept as thin forwarders so pre-0.2.65 callers that do
|
|
624
|
+
# mgr.check_clearml_service() / mgr.check_clearml_auth() keep working.
|
|
625
|
+
def check_clearml_service(self):
|
|
626
|
+
return self.check_mlops_core_service()
|
|
627
|
+
|
|
628
|
+
def check_clearml_auth(self):
|
|
629
|
+
return self.check_mlops_core_auth()
|
|
630
|
+
|
|
611
631
|
def get_model_id_by_name(self, name, owner=None):
|
|
612
632
|
"""
|
|
613
633
|
Resolves a model name to its Mlops-Core ID.
|
|
@@ -1657,7 +1677,8 @@ class MLOpsManager:
|
|
|
1657
1677
|
|
|
1658
1678
|
Returns:
|
|
1659
1679
|
{
|
|
1660
|
-
"model_id", "model_name",
|
|
1680
|
+
"model_id", "model_name",
|
|
1681
|
+
"status": "ok"|"missing_files"|"orphan_files"|"empty",
|
|
1661
1682
|
"versions": [{"version","path","files_exist","status"}...],
|
|
1662
1683
|
"orphan_folders": [<S3 folders under the model not referenced by any version>],
|
|
1663
1684
|
}
|
|
@@ -1713,6 +1734,15 @@ class MLOpsManager:
|
|
|
1713
1734
|
report["status"] = "missing_files"
|
|
1714
1735
|
elif report["orphan_folders"]:
|
|
1715
1736
|
report["status"] = "orphan_files"
|
|
1737
|
+
elif not versions_map:
|
|
1738
|
+
# Registry holds the model container but NO versions and NO files.
|
|
1739
|
+
# This is the shell an interrupted add leaves behind when the process
|
|
1740
|
+
# is hard-killed mid-upload (pod rollout, OOM, node eviction) — the
|
|
1741
|
+
# in-process rollback in add_model never runs in that case. Surface it
|
|
1742
|
+
# (it was previously reported "ok") so it can be cleaned up or the add
|
|
1743
|
+
# retried. Note: a version add still in flight shows its temp folder as
|
|
1744
|
+
# an orphan_folder above, so it reports "orphan_files", not "empty".
|
|
1745
|
+
report["status"] = "empty"
|
|
1716
1746
|
return report
|
|
1717
1747
|
|
|
1718
1748
|
def verify_all_models(self):
|
|
@@ -1793,6 +1823,23 @@ class MLOpsManager:
|
|
|
1793
1823
|
self.delete_model(model_name=model_name, version=version)
|
|
1794
1824
|
print(f"[SDK_SUCCESS] Synced: removed zombie version '{version}' of '{model_name}' from registry.")
|
|
1795
1825
|
|
|
1826
|
+
def remove_empty_model(self, model_name=None, model_id=None):
|
|
1827
|
+
"""
|
|
1828
|
+
Sync-repair for an EMPTY model container: the registry holds the model
|
|
1829
|
+
but it has no versions and no stored files — the shell an interrupted /
|
|
1830
|
+
hard-killed add leaves behind. Deletes the container so the registry is
|
|
1831
|
+
clean. Refuses to run if the model actually has versions or files (so a
|
|
1832
|
+
real model, or an add still in flight, is never removed by mistake).
|
|
1833
|
+
"""
|
|
1834
|
+
report = self.verify_model_integrity(model_name=model_name, model_id=model_id)
|
|
1835
|
+
if report["status"] != "empty":
|
|
1836
|
+
raise ValueError(
|
|
1837
|
+
f"Model '{report['model_name']}' is not empty (status={report['status']}); "
|
|
1838
|
+
f"refusing to remove it. Use delete_model for a real delete."
|
|
1839
|
+
)
|
|
1840
|
+
self.delete_model(model_id=report["model_id"])
|
|
1841
|
+
print(f"[SDK_SUCCESS] Removed empty model shell '{report['model_name']}'.")
|
|
1842
|
+
|
|
1796
1843
|
def purge_orphan_folders(self, model_name=None, model_id=None):
|
|
1797
1844
|
"""
|
|
1798
1845
|
Sync-repair for ORPHAN folders inside a registered model: S3 folders
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|