aipmodel 0.2.73__tar.gz → 0.2.75__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.73 → aipmodel-0.2.75}/PKG-INFO +1 -1
- {aipmodel-0.2.73 → aipmodel-0.2.75}/README.md +19 -0
- {aipmodel-0.2.73 → aipmodel-0.2.75}/aipmodel/__init__.py +1 -1
- {aipmodel-0.2.73 → aipmodel-0.2.75}/aipmodel/exceptions.py +5 -0
- {aipmodel-0.2.73 → aipmodel-0.2.75}/aipmodel/model_registry.py +71 -8
- {aipmodel-0.2.73 → aipmodel-0.2.75}/aipmodel.egg-info/PKG-INFO +1 -1
- {aipmodel-0.2.73 → aipmodel-0.2.75}/setup.py +1 -1
- {aipmodel-0.2.73 → aipmodel-0.2.75}/MANIFEST.in +0 -0
- {aipmodel-0.2.73 → aipmodel-0.2.75}/aipmodel/CephS3Manager.py +0 -0
- {aipmodel-0.2.73 → aipmodel-0.2.75}/aipmodel/acl_manager.py +0 -0
- {aipmodel-0.2.73 → aipmodel-0.2.75}/aipmodel/template.py +0 -0
- {aipmodel-0.2.73 → aipmodel-0.2.75}/aipmodel/update_checker.py +0 -0
- {aipmodel-0.2.73 → aipmodel-0.2.75}/aipmodel.egg-info/SOURCES.txt +0 -0
- {aipmodel-0.2.73 → aipmodel-0.2.75}/aipmodel.egg-info/dependency_links.txt +0 -0
- {aipmodel-0.2.73 → aipmodel-0.2.75}/aipmodel.egg-info/requires.txt +0 -0
- {aipmodel-0.2.73 → aipmodel-0.2.75}/aipmodel.egg-info/top_level.txt +0 -0
- {aipmodel-0.2.73 → aipmodel-0.2.75}/requirements.txt +0 -0
- {aipmodel-0.2.73 → aipmodel-0.2.75}/setup.cfg +0 -0
|
@@ -44,6 +44,25 @@ CEPH_ADMIN_SECRET_KEY="your_public_bucket_secret_key"
|
|
|
44
44
|
ACL_ENABLED="false"
|
|
45
45
|
ACL_API_HOST="http://your-acl-server:30011"
|
|
46
46
|
|
|
47
|
+
# Where per-user credentials come from. Default true: resolve the user's S3 +
|
|
48
|
+
# Mlops-Core keys from the user-management service using the bearer token.
|
|
49
|
+
# Set false to run WITHOUT user-management (other systems / testing): the SDK
|
|
50
|
+
# then reads the keys straight from the env vars below instead.
|
|
51
|
+
USER_MANAGEMENT_ENABLED="true"
|
|
52
|
+
# Only used when USER_MANAGEMENT_ENABLED="false":
|
|
53
|
+
# CEPH_USER_ACCESS_KEY="..." # the user's S3 access key
|
|
54
|
+
# CEPH_USER_SECRET_KEY="..." # the user's S3 secret key
|
|
55
|
+
# CEPH_USER_BUCKET="..." # the user's bucket
|
|
56
|
+
# MLOPS_CORE_ACCESS_KEY="..." # Mlops-Core basic-auth access key
|
|
57
|
+
# MLOPS_CORE_SECRET_KEY="..." # Mlops-Core basic-auth secret key
|
|
58
|
+
# MLOPS_CORE_USERNAME="..." # display/owner name
|
|
59
|
+
|
|
60
|
+
# Registry<->storage integrity (zombie/orphan) checks - opt-in, default off.
|
|
61
|
+
# When false, verify_model_integrity / verify_all_models / find_orphan_models
|
|
62
|
+
# raise IntegrityDisabledError instead of running the (S3-listing) consistency
|
|
63
|
+
# scan. Also settable per instance: MLOpsManager(..., INTEGRITY_ENABLED=True).
|
|
64
|
+
INTEGRITY_ENABLED="false"
|
|
65
|
+
|
|
47
66
|
# Optional overrides (all have sane defaults if omitted)
|
|
48
67
|
SDK_REQUEST_TIMEOUT="30"
|
|
49
68
|
MODEL_ALLOWED_CATEGORIES="Text Generation,Image Classification"
|
|
@@ -21,3 +21,8 @@ class MetadataError(ModelRegistryError):
|
|
|
21
21
|
|
|
22
22
|
class StorageError(ModelRegistryError):
|
|
23
23
|
"""S3 / Ceph operation failed (upload, download, copy, delete)."""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class IntegrityDisabledError(ModelRegistryError):
|
|
27
|
+
"""An integrity/zombie-check operation was called while INTEGRITY_ENABLED is
|
|
28
|
+
off (the default). Enable it (kwarg or env) to run consistency scans."""
|
|
@@ -18,7 +18,7 @@ from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
|
18
18
|
|
|
19
19
|
from .CephS3Manager import CephS3Manager
|
|
20
20
|
from .acl_manager import AclManager
|
|
21
|
-
from .exceptions import ModelRegistryError, ModelNotFoundError, MetadataError, StorageError
|
|
21
|
+
from .exceptions import ModelRegistryError, ModelNotFoundError, MetadataError, StorageError, IntegrityDisabledError
|
|
22
22
|
|
|
23
23
|
load_dotenv()
|
|
24
24
|
|
|
@@ -368,6 +368,8 @@ class MLOpsManager:
|
|
|
368
368
|
skip_connection_check=False,
|
|
369
369
|
ACL_API_HOST=None,
|
|
370
370
|
ACL_ENABLED=None,
|
|
371
|
+
INTEGRITY_ENABLED=None,
|
|
372
|
+
USER_MANAGEMENT_ENABLED=None,
|
|
371
373
|
MLOPS_CORE_API_HOST=None,
|
|
372
374
|
MLOPS_CORE_WEB_HOST=None
|
|
373
375
|
):
|
|
@@ -407,19 +409,52 @@ class MLOpsManager:
|
|
|
407
409
|
|
|
408
410
|
self.USER_MANAGEMENT_API = USER_MANAGEMENT_API or os.environ.get("USER_MANAGEMENT_API")
|
|
409
411
|
self.CEPH_ENDPOINT_URL = CEPH_ENDPOINT_URL or os.environ.get("CEPH_ENDPOINT_URL")
|
|
410
|
-
|
|
412
|
+
|
|
413
|
+
# Where per-user credentials come from. Default True: resolve the user's
|
|
414
|
+
# S3 + Mlops-Core keys from the user-management service via the bearer
|
|
415
|
+
# token (production). Set USER_MANAGEMENT_ENABLED=false to run on a system
|
|
416
|
+
# WITHOUT user-management — the keys are then read straight from env:
|
|
417
|
+
# CEPH_USER_ACCESS_KEY / CEPH_USER_SECRET_KEY / CEPH_USER_BUCKET /
|
|
418
|
+
# MLOPS_CORE_ACCESS_KEY / MLOPS_CORE_SECRET_KEY / MLOPS_CORE_USERNAME.
|
|
419
|
+
self.USER_MANAGEMENT_ENABLED = self._to_bool(
|
|
420
|
+
USER_MANAGEMENT_ENABLED if USER_MANAGEMENT_ENABLED is not None else os.environ.get("USER_MANAGEMENT_ENABLED"),
|
|
421
|
+
default=True
|
|
422
|
+
)
|
|
423
|
+
|
|
411
424
|
# Public Bucket Configuration
|
|
412
425
|
self.CEPH_PUBLIC_BUCKET_NAME = os.environ.get("CEPH_PUBLIC_BUCKET_NAME", "aip-public")
|
|
413
426
|
self.PUBLIC_ACCESS_KEY = os.environ.get("CEPH_ADMIN_ACCESS_KEY")
|
|
414
427
|
self.PUBLIC_SECRET_KEY = os.environ.get("CEPH_ADMIN_SECRET_KEY")
|
|
415
428
|
|
|
416
|
-
|
|
429
|
+
if self.USER_MANAGEMENT_ENABLED:
|
|
430
|
+
user_info, self.MLOPS_CORE_USERNAME = get_user_metadata(bearer_token=user_token, user_management_url=self.USER_MANAGEMENT_API)
|
|
431
|
+
else:
|
|
432
|
+
# Direct-credential mode — no user-management call; keys come from env.
|
|
433
|
+
self.MLOPS_CORE_USERNAME = os.environ.get("MLOPS_CORE_USERNAME") or os.environ.get("CLEARML_USERNAME") or "local"
|
|
434
|
+
user_info = {
|
|
435
|
+
"s3_access_key": os.environ.get("CEPH_USER_ACCESS_KEY") or os.environ.get("CEPH_ADMIN_ACCESS_KEY"),
|
|
436
|
+
"s3_secret_key": os.environ.get("CEPH_USER_SECRET_KEY") or os.environ.get("CEPH_ADMIN_SECRET_KEY"),
|
|
437
|
+
"s3_bucket": os.environ.get("CEPH_USER_BUCKET"),
|
|
438
|
+
"clearml_access_key": os.environ.get("MLOPS_CORE_ACCESS_KEY") or os.environ.get("CLEARML_ACCESS_KEY"),
|
|
439
|
+
"clearml_secret_key": os.environ.get("MLOPS_CORE_SECRET_KEY") or os.environ.get("CLEARML_SECRET_KEY"),
|
|
440
|
+
}
|
|
441
|
+
missing_creds = [k for k, v in user_info.items() if not v]
|
|
442
|
+
if missing_creds:
|
|
443
|
+
raise ValueError(
|
|
444
|
+
"USER_MANAGEMENT_ENABLED is false but direct credentials are missing from env: "
|
|
445
|
+
+ ", ".join(missing_creds)
|
|
446
|
+
+ " (set CEPH_USER_ACCESS_KEY / CEPH_USER_SECRET_KEY / CEPH_USER_BUCKET / "
|
|
447
|
+
"MLOPS_CORE_ACCESS_KEY / MLOPS_CORE_SECRET_KEY)."
|
|
448
|
+
)
|
|
417
449
|
|
|
418
|
-
|
|
450
|
+
# USER_MANAGEMENT_API is only required when that service is actually used.
|
|
451
|
+
required_cfg = {
|
|
419
452
|
"MLOPS_CORE_API_HOST": self.MLOPS_CORE_API_HOST,
|
|
420
|
-
"USER_MANAGEMENT_API": self.USER_MANAGEMENT_API,
|
|
421
453
|
"CEPH_ENDPOINT_URL": self.CEPH_ENDPOINT_URL,
|
|
422
|
-
}
|
|
454
|
+
}
|
|
455
|
+
if self.USER_MANAGEMENT_ENABLED:
|
|
456
|
+
required_cfg["USER_MANAGEMENT_API"] = self.USER_MANAGEMENT_API
|
|
457
|
+
missing_cfg = [k for k, v in required_cfg.items() if not v]
|
|
423
458
|
if missing_cfg:
|
|
424
459
|
error_msg = f"Missing required configuration: {', '.join(missing_cfg)}"
|
|
425
460
|
print(f"[SDK_FAIL] {error_msg}")
|
|
@@ -448,7 +483,7 @@ class MLOpsManager:
|
|
|
448
483
|
if not skip_connection_check:
|
|
449
484
|
if self.verbose:
|
|
450
485
|
print("[SDK_INFO] Performing health checks...")
|
|
451
|
-
if not self.check_user_management_service():
|
|
486
|
+
if self.USER_MANAGEMENT_ENABLED and not self.check_user_management_service():
|
|
452
487
|
raise ValueError("User Management API unreachable")
|
|
453
488
|
if not self.check_mlops_core_service():
|
|
454
489
|
raise ValueError("Mlops-Core Server down")
|
|
@@ -491,6 +526,16 @@ class MLOpsManager:
|
|
|
491
526
|
)
|
|
492
527
|
self.ACL_API_HOST = ACL_API_HOST or os.environ.get("ACL_API_HOST")
|
|
493
528
|
|
|
529
|
+
# --- Registry <-> Storage integrity checks (zombie/orphan detection) ---
|
|
530
|
+
# Toggle controlled by INTEGRITY_ENABLED (kwarg or .env). When disabled
|
|
531
|
+
# (the default), verify_model_integrity / verify_all_models /
|
|
532
|
+
# find_orphan_models refuse to run — the consistency scan lists S3 for
|
|
533
|
+
# every model, so it is opt-in rather than on by default.
|
|
534
|
+
self.INTEGRITY_ENABLED = self._to_bool(
|
|
535
|
+
INTEGRITY_ENABLED if INTEGRITY_ENABLED is not None else os.environ.get("INTEGRITY_ENABLED"),
|
|
536
|
+
default=False
|
|
537
|
+
)
|
|
538
|
+
|
|
494
539
|
if self.ACL_ENABLED:
|
|
495
540
|
if not self.ACL_API_HOST:
|
|
496
541
|
error_msg = "ACL_ENABLED is true but ACL_API_HOST is not configured"
|
|
@@ -1683,7 +1728,13 @@ class MLOpsManager:
|
|
|
1683
1728
|
"orphan_folders": [<S3 folders under the model not referenced by any version>],
|
|
1684
1729
|
}
|
|
1685
1730
|
Raises ModelNotFoundError if the model is not in the registry at all.
|
|
1731
|
+
Raises IntegrityDisabledError if INTEGRITY_ENABLED is off (the default).
|
|
1686
1732
|
"""
|
|
1733
|
+
if not self.INTEGRITY_ENABLED:
|
|
1734
|
+
raise IntegrityDisabledError(
|
|
1735
|
+
"Integrity checks are disabled. Enable them with INTEGRITY_ENABLED=true "
|
|
1736
|
+
"(constructor kwarg or environment variable)."
|
|
1737
|
+
)
|
|
1687
1738
|
if not model_id:
|
|
1688
1739
|
model_id = self.get_model_id_by_name(model_name)
|
|
1689
1740
|
if not model_id:
|
|
@@ -1748,7 +1799,13 @@ class MLOpsManager:
|
|
|
1748
1799
|
def verify_all_models(self):
|
|
1749
1800
|
"""Run verify_model_integrity over every model in the user's project.
|
|
1750
1801
|
Returns a list of per-model reports (never raises per-model; a model
|
|
1751
|
-
that fails to verify is reported with status "error").
|
|
1802
|
+
that fails to verify is reported with status "error").
|
|
1803
|
+
Raises IntegrityDisabledError if INTEGRITY_ENABLED is off (the default)."""
|
|
1804
|
+
if not self.INTEGRITY_ENABLED:
|
|
1805
|
+
raise IntegrityDisabledError(
|
|
1806
|
+
"Integrity checks are disabled. Enable them with INTEGRITY_ENABLED=true "
|
|
1807
|
+
"(constructor kwarg or environment variable)."
|
|
1808
|
+
)
|
|
1752
1809
|
reports = []
|
|
1753
1810
|
models = self.models.get_all(self.project_id) or []
|
|
1754
1811
|
for m in models:
|
|
@@ -1770,7 +1827,13 @@ class MLOpsManager:
|
|
|
1770
1827
|
Find S3 model folders (under `_models/`) that the registry does NOT
|
|
1771
1828
|
know about — e.g. leftovers after a registry (Mlops-Core) data loss.
|
|
1772
1829
|
Returns [{"model_id", "recoverable" (has _meta.json), "meta": {...}|None}].
|
|
1830
|
+
Raises IntegrityDisabledError if INTEGRITY_ENABLED is off (the default).
|
|
1773
1831
|
"""
|
|
1832
|
+
if not self.INTEGRITY_ENABLED:
|
|
1833
|
+
raise IntegrityDisabledError(
|
|
1834
|
+
"Integrity checks are disabled. Enable them with INTEGRITY_ENABLED=true "
|
|
1835
|
+
"(constructor kwarg or environment variable)."
|
|
1836
|
+
)
|
|
1774
1837
|
mgr = self._get_ceph_manager(False)
|
|
1775
1838
|
registry_ids = {m.get("id") for m in (self.models.get_all(self.project_id) or [])}
|
|
1776
1839
|
s3_ids = set()
|
|
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
|