aipmodel 0.2.65__tar.gz → 0.2.66__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.65 → aipmodel-0.2.66}/PKG-INFO +1 -1
- {aipmodel-0.2.65 → aipmodel-0.2.66}/README.md +45 -0
- {aipmodel-0.2.65 → aipmodel-0.2.66}/aipmodel/__init__.py +1 -1
- {aipmodel-0.2.65 → aipmodel-0.2.66}/aipmodel/model_registry.py +42 -0
- {aipmodel-0.2.65 → aipmodel-0.2.66}/aipmodel.egg-info/PKG-INFO +1 -1
- {aipmodel-0.2.65 → aipmodel-0.2.66}/setup.py +1 -1
- {aipmodel-0.2.65 → aipmodel-0.2.66}/MANIFEST.in +0 -0
- {aipmodel-0.2.65 → aipmodel-0.2.66}/aipmodel/CephS3Manager.py +0 -0
- {aipmodel-0.2.65 → aipmodel-0.2.66}/aipmodel/acl_manager.py +0 -0
- {aipmodel-0.2.65 → aipmodel-0.2.66}/aipmodel/template.py +0 -0
- {aipmodel-0.2.65 → aipmodel-0.2.66}/aipmodel/update_checker.py +0 -0
- {aipmodel-0.2.65 → aipmodel-0.2.66}/aipmodel.egg-info/SOURCES.txt +0 -0
- {aipmodel-0.2.65 → aipmodel-0.2.66}/aipmodel.egg-info/dependency_links.txt +0 -0
- {aipmodel-0.2.65 → aipmodel-0.2.66}/aipmodel.egg-info/requires.txt +0 -0
- {aipmodel-0.2.65 → aipmodel-0.2.66}/aipmodel.egg-info/top_level.txt +0 -0
- {aipmodel-0.2.65 → aipmodel-0.2.66}/requirements.txt +0 -0
- {aipmodel-0.2.65 → aipmodel-0.2.66}/setup.cfg +0 -0
|
@@ -51,6 +51,12 @@ S3_MAX_CONCURRENCY="10"
|
|
|
51
51
|
S3_UPLOAD_WORKERS="4"
|
|
52
52
|
S3_DOWNLOAD_WORKERS="4"
|
|
53
53
|
|
|
54
|
+
# Auto-retry for transient connection drops during large transfers
|
|
55
|
+
S3_MAX_RETRIES="5" # per-file retries before giving up
|
|
56
|
+
S3_RETRY_BACKOFF_SECONDS="2" # base backoff, grows exponentially per attempt
|
|
57
|
+
S3_RETRY_MAX_DELAY_SECONDS="30" # cap on a single backoff sleep
|
|
58
|
+
S3_BOTO_MAX_ATTEMPTS="10" # botocore request-level retry attempts (adaptive)
|
|
59
|
+
|
|
54
60
|
```
|
|
55
61
|
|
|
56
62
|
> **Note:** `CLEARML_WEB_HOST` is new. If not provided, the SDK will default to the API host, which might result in incorrect clickable links in the output.
|
|
@@ -68,6 +74,45 @@ S3_DOWNLOAD_WORKERS="4"
|
|
|
68
74
|
|
|
69
75
|
Nothing is ever logged to a file or hidden — all output goes to stdout via `print()`, so it's visible wherever the process runs.
|
|
70
76
|
|
|
77
|
+
## Resilient Transfers (Auto-Retry)
|
|
78
|
+
|
|
79
|
+
Large multi-GB uploads/downloads over Ceph/RGW occasionally hit a **transient**
|
|
80
|
+
connection drop mid-stream (`IncompleteRead` / `ProtocolError` / connection
|
|
81
|
+
reset). The SDK now **retries these automatically**, so `add_model`,
|
|
82
|
+
`get_model` and `download` complete without manual intervention:
|
|
83
|
+
|
|
84
|
+
- Each **individual file** transfer is retried on a transient error with
|
|
85
|
+
exponential backoff (`S3_RETRY_BACKOFF_SECONDS`, capped by
|
|
86
|
+
`S3_RETRY_MAX_DELAY_SECONDS`), up to `S3_MAX_RETRIES` times. Downloads reset
|
|
87
|
+
the partial file before each retry so no corrupt bytes remain.
|
|
88
|
+
- The underlying boto3 client also uses botocore's **adaptive** request-level
|
|
89
|
+
retries (`S3_BOTO_MAX_ATTEMPTS`).
|
|
90
|
+
- **Non-transient** errors (bad input, auth, not-found) are **not** retried —
|
|
91
|
+
they fail immediately as before.
|
|
92
|
+
- Fully backward compatible: method signatures, return values and the final
|
|
93
|
+
`ValueError` raised on ultimate failure are unchanged. All knobs are
|
|
94
|
+
`.env`-overridable with sane defaults.
|
|
95
|
+
|
|
96
|
+
## Concurrent Adds
|
|
97
|
+
|
|
98
|
+
`add_model` is **concurrency-safe** — you can run many adds at the same time:
|
|
99
|
+
|
|
100
|
+
- Adds with **different model names** run **fully in parallel** (own ClearML id,
|
|
101
|
+
own S3 prefix — no interference).
|
|
102
|
+
- Adds with the **same `(project, model_name)`** are **serialized** by a
|
|
103
|
+
process-wide lock. Otherwise two simultaneous same-name adds would race on the
|
|
104
|
+
existence check + auto version-number computation and could create a
|
|
105
|
+
**duplicate model** or **overwrite a version**. With the lock the second add
|
|
106
|
+
waits for the first, then correctly continues to the next version
|
|
107
|
+
(`_v2` → `_v3`, …) — no duplicates, no lost versions.
|
|
108
|
+
- Uploads are **staged**: files go to a temp S3 path first and are only moved to
|
|
109
|
+
the final version path once the whole upload succeeds; any failure rolls back
|
|
110
|
+
(deletes the new model if it was new + the temp folder) and re-raises, so a
|
|
111
|
+
crashed/aborted add never leaves a half-written version.
|
|
112
|
+
- The lock is **per-process**. One server process (e.g. a single `uvicorn`
|
|
113
|
+
worker) is fully protected; for multiple workers/replicas add a distributed
|
|
114
|
+
lock (ClearML/Redis).
|
|
115
|
+
|
|
71
116
|
## Access Control (ACL)
|
|
72
117
|
|
|
73
118
|
ACL is **off by default** and fully optional. Toggle it with one env var:
|
|
@@ -10,6 +10,7 @@ from huggingface_hub import snapshot_download
|
|
|
10
10
|
from typing import Optional, List, Dict, Any
|
|
11
11
|
import json
|
|
12
12
|
import logging
|
|
13
|
+
import threading
|
|
13
14
|
import time
|
|
14
15
|
import re
|
|
15
16
|
from tqdm import tqdm
|
|
@@ -22,6 +23,25 @@ load_dotenv()
|
|
|
22
23
|
|
|
23
24
|
logger = logging.getLogger(__name__)
|
|
24
25
|
|
|
26
|
+
# Process-wide locks that serialize concurrent add_model calls targeting the SAME
|
|
27
|
+
# (project, model_name). Two simultaneous adds of the same name would otherwise
|
|
28
|
+
# race on the existence check + version-number computation and could create a
|
|
29
|
+
# duplicate model or clobber a version. Different names lock on different keys and
|
|
30
|
+
# still run fully in parallel. NOTE: per-process — with multiple API workers /
|
|
31
|
+
# replicas a distributed lock would be required for the same guarantee.
|
|
32
|
+
_ADD_MODEL_LOCKS = {}
|
|
33
|
+
_ADD_MODEL_LOCKS_GUARD = threading.Lock()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _get_add_model_lock(project_id, model_name):
|
|
37
|
+
key = (project_id, model_name)
|
|
38
|
+
with _ADD_MODEL_LOCKS_GUARD:
|
|
39
|
+
lock = _ADD_MODEL_LOCKS.get(key)
|
|
40
|
+
if lock is None:
|
|
41
|
+
lock = threading.Lock()
|
|
42
|
+
_ADD_MODEL_LOCKS[key] = lock
|
|
43
|
+
return lock
|
|
44
|
+
|
|
25
45
|
class ProjectsAPI:
|
|
26
46
|
def __init__(self, post, verbose):
|
|
27
47
|
self.verbose = verbose
|
|
@@ -670,6 +690,28 @@ class MLOpsManager:
|
|
|
670
690
|
def add_model(self, source_type, model_name=None, version=None, source_path=None, code_path=None, category=None, tags=None,
|
|
671
691
|
public=False, external_ceph_endpoint_url=None, external_ceph_bucket_name=None, external_ceph_access_key=None, external_ceph_secret_key=None,
|
|
672
692
|
model_folder_name=None):
|
|
693
|
+
"""
|
|
694
|
+
Register/add a model version. Concurrency-safe: adds that target the SAME
|
|
695
|
+
(project, model_name) are serialized by a process-wide lock so simultaneous
|
|
696
|
+
same-name adds cannot create a duplicate model or collide on the auto
|
|
697
|
+
version number; adds with different names run fully in parallel. All logic
|
|
698
|
+
lives in _add_model_impl — signature, behavior and return value are
|
|
699
|
+
unchanged (this wrapper only adds the lock).
|
|
700
|
+
"""
|
|
701
|
+
with _get_add_model_lock(self.project_id, model_name):
|
|
702
|
+
return self._add_model_impl(
|
|
703
|
+
source_type, model_name=model_name, version=version, source_path=source_path,
|
|
704
|
+
code_path=code_path, category=category, tags=tags, public=public,
|
|
705
|
+
external_ceph_endpoint_url=external_ceph_endpoint_url,
|
|
706
|
+
external_ceph_bucket_name=external_ceph_bucket_name,
|
|
707
|
+
external_ceph_access_key=external_ceph_access_key,
|
|
708
|
+
external_ceph_secret_key=external_ceph_secret_key,
|
|
709
|
+
model_folder_name=model_folder_name,
|
|
710
|
+
)
|
|
711
|
+
|
|
712
|
+
def _add_model_impl(self, source_type, model_name=None, version=None, source_path=None, code_path=None, category=None, tags=None,
|
|
713
|
+
public=False, external_ceph_endpoint_url=None, external_ceph_bucket_name=None, external_ceph_access_key=None, external_ceph_secret_key=None,
|
|
714
|
+
model_folder_name=None):
|
|
673
715
|
|
|
674
716
|
if self.verbose:
|
|
675
717
|
print(f"[SDK_INFO] Adding model: {model_name} (Source: {source_type}, Public: {public})")
|
|
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
|