aipmodel 0.2.64__tar.gz → 0.2.65__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.
@@ -1,23 +1,16 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aipmodel
3
- Version: 0.2.64
3
+ Version: 0.2.65
4
4
  Summary: SDK for model registration, versioning, and storage
5
5
  Home-page: https://github.com/AIP-MLOPS/model-registry
6
6
  Author: AIP MLOPS Team
7
7
  Author-email: mohmmadweb@gmail.com
8
8
  Requires-Python: >=3.6
9
9
  Requires-Dist: boto3==1.34.0
10
- Requires-Dist: clearml==2.0.2
11
- Requires-Dist: fastapi==0.128.2
12
- Requires-Dist: hf_xet==1.2.0
13
10
  Requires-Dist: huggingface-hub==0.36.2
14
- Requires-Dist: pydantic==2.12.5
15
11
  Requires-Dist: python-dotenv==1.2.1
16
12
  Requires-Dist: requests==2.32.5
17
- Requires-Dist: tabulate==0.9.0
18
13
  Requires-Dist: tqdm==4.67.1
19
- Requires-Dist: ruff==0.14.7
20
- Requires-Dist: uvicorn[standard]==0.38.0
21
14
  Dynamic: author
22
15
  Dynamic: author-email
23
16
  Dynamic: home-page
@@ -0,0 +1,326 @@
1
+ # AIP Model SDK
2
+
3
+ This SDK provides a robust interface for registering, uploading, downloading, listing, and deleting machine learning models using ClearML and S3 (Ceph) as storage. It supports **automatic versioning**, **categorization**, **tagging**, **metadata transparency**, **web link generation**, and fail-fast connectivity checks.
4
+
5
+ ---
6
+
7
+ ## Installation
8
+
9
+ Install from PyPI:
10
+
11
+ ```bash
12
+ pip install aipmodel
13
+
14
+ ```
15
+
16
+ ---
17
+
18
+ ## Configuration & Authentication
19
+
20
+ To use the SDK, you must set up your environment variables. You can create a `.env` file in your project root:
21
+
22
+ ```env
23
+ # ClearML API Credentials
24
+ CLEARML_API_HOST="http://your-clearml-server:8008"
25
+ CLEARML_WEB_HOST="http://your-clearml-web-ui:8080" # Required for generating model web links
26
+ CLEARML_ACCESS_KEY="your_access_key"
27
+ CLEARML_SECRET_KEY="your_secret_key"
28
+
29
+ # Ceph / S3 Storage
30
+ CEPH_ENDPOINT_URL="https://your-ceph-endpoint.com"
31
+
32
+ # User Management (Internal)
33
+ USER_MANAGEMENT_API="http://your-user-manager:30009"
34
+ USER_TOKEN="your_bearer_token" # Or pass explicitly in code
35
+
36
+ # Public bucket (used by public=True / set_model_public)
37
+ CEPH_PUBLIC_BUCKET_NAME="aip-public"
38
+ CEPH_ADMIN_ACCESS_KEY="your_public_bucket_access_key"
39
+ CEPH_ADMIN_SECRET_KEY="your_public_bucket_secret_key"
40
+
41
+ # ACL (Access Control) - optional, see "Access Control (ACL)" below
42
+ ACL_ENABLED="false"
43
+ ACL_API_HOST="http://your-acl-server:30011"
44
+
45
+ # Optional overrides (all have sane defaults if omitted)
46
+ SDK_REQUEST_TIMEOUT="30"
47
+ MODEL_ALLOWED_CATEGORIES="Text Generation,Image Classification"
48
+ S3_MULTIPART_THRESHOLD_MB="64"
49
+ S3_MULTIPART_CHUNKSIZE_MB="16"
50
+ S3_MAX_CONCURRENCY="10"
51
+ S3_UPLOAD_WORKERS="4"
52
+ S3_DOWNLOAD_WORKERS="4"
53
+
54
+ ```
55
+
56
+ > **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.
57
+
58
+ > Every constant the SDK relies on (timeouts, categories, transfer tuning, ACL) is overridable via `.env` — nothing important is hardcoded.
59
+
60
+ ---
61
+
62
+ ## Logging Verbosity
63
+
64
+ `MLOpsManager(..., verbose=False)` — **`False` is the default.**
65
+
66
+ - `verbose=False`: only essential output is printed — operation start/result, successes, failures, and warnings that affect state (e.g. overwriting a version, falling back to a new "latest" version).
67
+ - `verbose=True`: every internal step is also printed (`[SDK_DEBUG]`/`[SDK_INFO]` narration), including the full raw metadata for transparency.
68
+
69
+ 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
+
71
+ ## Access Control (ACL)
72
+
73
+ ACL is **off by default** and fully optional. Toggle it with one env var:
74
+
75
+ ```env
76
+ ACL_ENABLED="true"
77
+ ACL_API_HOST="http://your-acl-server:30011"
78
+ ```
79
+
80
+ - `ACL_ENABLED=false` (default): identical behavior to a build with no ACL at all — every call bypasses permission checks.
81
+ - `ACL_ENABLED=true`: `add_model`, `get_model`, `get_model_info`, `delete_model`, `set_model_public`, `set_latest_version`, and `list_models` all enforce permission checks against the ACL service, and `add_model`/`delete_model`/`set_model_public` keep the ACL service's record of each model in sync.
82
+
83
+ ### Sharing a model
84
+
85
+ ```python
86
+ manager.share_model(model_name="demo_model_local", target_user="colleague@example.com", access_level="write")
87
+ # access_level: "read" | "write" | "delete" | "admin" | "owner"
88
+ ```
89
+
90
+ Requires `ACL_ENABLED=true` and that you have `admin`-level access on the model. **Note:** the underlying ACL endpoint contract for sharing was inferred from the existing ACL client conventions and hasn't been verified against the live ACL service — confirm the request shape before relying on it in production.
91
+
92
+ ---
93
+
94
+ ## Cross-User Visibility (Public Models)
95
+
96
+ Every model lives in its owner's own ClearML project (`project_<owner_username>`) — that part hasn't changed. What changed is **how `list_models`/`get_model_id_by_name`/`get_model_info`/`get_model` look things up**:
97
+
98
+ - **`list_models()`** (default `include_public=True`): returns your own models (private + public) **plus** every other user's model that is marked `public=True`. Other users' **private** models are never returned, under any setting. Each entry's `owner` field is resolved from the model's actual project, so models from other users show their real owner, not yours.
99
+ - Pass `include_public=False` to get the exact pre-existing behavior: your own project only, nothing from other users.
100
+ - `public_only=True` / `private_only=True` now filter across this combined set too.
101
+ - **`get_model_id_by_name(name, owner=None)`**, **`get_model(model_name, local_dest, version=None, owner=None)`**, **`get_model_info(identifier, owner=None)`**: all gained an optional `owner` argument. Omit it and behavior is identical to before (your own project only). Pass `owner="some_username"` to resolve a model by name from *that* user's project — useful once `list_models()` has shown you a public model that isn't yours.
102
+ - **`get_model_link`** now resolves the link using the model's *actual* owning project (previously it always used your own project ID, which silently produced a wrong link for any model you didn't own — this never came up before because nothing could return another user's model anyway).
103
+ - Write operations (`add_model`, `delete_model`, `set_model_public`, `set_latest_version`) are **unchanged** — they still only ever operate within your own project by name. To act on a model you don't own (e.g. as an ACL-granted admin), pass its `model_id` directly (already supported) rather than its name.
104
+
105
+ **Performance note:** when `include_public=True` (the default), `list_models()` makes one extra unscoped ClearML query (`models.get_all` with no project filter) to find other users' public models — on a very large ClearML deployment this returns every model company-wide before filtering down to public ones. If that ever becomes slow, set `include_public=False` and only fetch other users' models on demand via `owner=`.
106
+
107
+ **Backward compatibility:** `get_model_id_by_name`, `get_model`, `get_model_info` keep their exact old signatures plus one new optional trailing argument — no existing call site breaks. `list_models()`'s default *output* now includes more rows than before (other users' public models) since that was the explicit point of this change; pass `include_public=False` if your code depends on the exact old row set.
108
+
109
+ ---
110
+
111
+ ## Example Usage
112
+
113
+ This example demonstrates how to use the new features: **Auto-Versioning**, **Categories**, **Tags**, and **Web Links**.
114
+
115
+ ```python
116
+ import os
117
+ from dotenv import load_dotenv
118
+ from aipmodel.model_registry import MLOpsManager
119
+
120
+ load_dotenv()
121
+
122
+ # --- STEP 1: Initialize MLOps Manager ---
123
+ print("\n[STEP 1] Initialize MLOps Manager")
124
+ # verbose=True enables detailed logging and transparency mode.
125
+ manager = MLOpsManager(
126
+ user_token=os.getenv("USER_TOKEN"),
127
+ CLEARML_API_HOST=os.getenv("CLEARML_API_HOST"),
128
+ CLEARML_WEB_HOST=os.getenv("CLEARML_WEB_HOST"), # For generating UI links
129
+ CEPH_ENDPOINT_URL=os.getenv("CEPH_ENDPOINT_URL"),
130
+ USER_MANAGEMENT_API=os.getenv("USER_MANAGEMENT_API"),
131
+ verbose=True
132
+ )
133
+
134
+ # --- STEP 2: Add Private Local Model (Explicit Version) ---
135
+ print("\n[STEP 2] Add Private Model (Explicit Version + Metadata)")
136
+ local_model_id = manager.add_model(
137
+ source_type="local",
138
+ model_name="demo_model_local",
139
+ version="v1.0",
140
+ source_path="./my_models/v1",
141
+ code_path="./my_models/train.py",
142
+ category="Text Generation",
143
+ tags=["production", "base-model"],
144
+ public=False # Default is False (Private Bucket)
145
+ )
146
+ print(f" -> Registered ID: {local_model_id}")
147
+
148
+ # --- STEP 3: Add Auto-Versioned Model ---
149
+ print("\n[STEP 3] Add Local Model (Auto-Versioning)")
150
+ # Passing version=None triggers auto-generation (e.g., _v2, _v3 based on history)
151
+ auto_ver_id = manager.add_model(
152
+ source_type="local",
153
+ model_name="demo_model_local",
154
+ version=None,
155
+ source_path="./my_models/v2_finetuned",
156
+ tags=["experimental", "auto-versioned"]
157
+ )
158
+ print(f" -> Registered ID: {auto_ver_id}")
159
+
160
+ # --- STEP 4: Add Public Model (Direct to Public Bucket) ---
161
+ print("\n[STEP 4] Add Public Model")
162
+ # Setting public=True uploads directly to the shared/public bucket
163
+ public_model_id = manager.add_model(
164
+ source_type="local",
165
+ model_name="demo_model_public",
166
+ version="v1.0",
167
+ source_path="./my_models/public_release",
168
+ tags=["community", "public"],
169
+ public=True
170
+ )
171
+ print(f" -> Registered ID: {public_model_id}")
172
+
173
+ # --- STEP 5: Add Hugging Face Model ---
174
+ print("\n[STEP 5] Add Model from Hugging Face")
175
+ hf_model_id = manager.add_model(
176
+ source_type="hf",
177
+ model_name="demo_model_hf",
178
+ version="v1",
179
+ source_path="facebook/wav2vec2-base-960h",
180
+ category="Image Classification",
181
+ tags=["imported", "audio"]
182
+ )
183
+ print(f" -> Registered ID: {hf_model_id}")
184
+
185
+ # --- STEP 6: Get Detailed Info ---
186
+ print("\n[STEP 6] Get Detailed Model Info")
187
+ # Displays ID, Public status, Total Versions, Size, and ClearML Web Link
188
+ manager.get_model_info("demo_model_local")
189
+
190
+ # --- STEP 7: List All Models ---
191
+ print("\n[STEP 7] List All Models")
192
+ # Check the 'Public' column (T/F) and 'Latest' version column.
193
+ # By default this also includes OTHER users' public models (each tagged with
194
+ # its real 'owner') - pass include_public=False to see only your own, like before.
195
+ manager.list_models(verbose=False)
196
+ manager.list_models(category="Text Generation") # filter by category
197
+ manager.list_models(tag="production") # filter by tag
198
+ manager.list_models(public_only=True) # only public models (yours + others')
199
+ manager.list_models(include_public=False) # only your own project, old behavior
200
+
201
+ # --- STEP 7B: Look Up a Model You Don't Own ---
202
+ print("\n[STEP 7B] Inspect/Download a Public Model From Another User")
203
+ # Suppose list_models() above showed a public model named "shared_model" owned by "colleague_username"
204
+ # other_info = manager.get_model_info("shared_model", owner="colleague_username")
205
+ # manager.get_model(model_name="shared_model", local_dest="./downloads/shared", owner="colleague_username")
206
+
207
+ # --- STEP 8: Management & Visibility Operations ---
208
+ print("\n[STEP 8] Management Operations")
209
+
210
+ # A. Change Visibility (Private -> Public)
211
+ # Moves files physically from User Bucket to Public Bucket
212
+ manager.set_model_public("demo_model_local", public=True)
213
+
214
+ # B. Get Latest Version Name
215
+ # Returns the string tag of the latest version (e.g., "_v2")
216
+ latest_ver = manager.get_latest_version("demo_model_local")
217
+ print(f"Latest version is: {latest_ver}")
218
+
219
+ # C. Set Manual Latest Version
220
+ # Points the 'latest' tag to a specific version
221
+ manager.set_latest_version(model_name="demo_model_local", version="v1.0")
222
+
223
+ # --- STEP 9: Download Operations ---
224
+ print("\n[STEP 9] Download Model")
225
+
226
+ # Download latest version
227
+ # NOTE: The SDK automatically appends the Model ID to the path.
228
+ # Destination will be: ./downloads/latest/<MODEL_ID>/<VERSION_NAME>/
229
+ manager.get_model(
230
+ model_name="demo_model_local",
231
+ local_dest="./downloads/latest"
232
+ )
233
+
234
+ # --- STEP 10: Deletion ---
235
+ print("\n[STEP 10] Deletion")
236
+
237
+ # Delete a specific version (Removes files + Metadata entry)
238
+ manager.delete_model(model_name="demo_model_local", version="v1.0")
239
+
240
+ # Delete the entire model (Removes all versions + ClearML ID)
241
+ manager.delete_model(model_name="demo_model_hf")
242
+
243
+ ```
244
+
245
+ ---
246
+
247
+ ## API Reference
248
+
249
+ ### `MLOpsManager` Methods
250
+
251
+ | Function | Input Arguments | Description |
252
+ | --- | --- | --- |
253
+ | **`__init__`** | `user_token`, `CLEARML_API_HOST`, `CLEARML_WEB_HOST`, `CEPH_ENDPOINT_URL`, `USER_MANAGEMENT_API`, `verbose`, `ACL_API_HOST`, `ACL_ENABLED` | Initializes connections. Performs fail-fast health checks. `ACL_*` args (or env vars) toggle ACL enforcement (default: disabled). |
254
+ | **`add_model`** | `source_type`, `model_name`, `version`, `source_path`, `code_path`, `category`, `tags`, `external_ceph_*`, `model_folder_name` | Uploads a model. `version`: if `None`, generates `_v{n}` automatically. `category`: must be one of `get_allowed_categories()` (defaults to "Text Generation"/"Image Classification", overridable via `MODEL_ALLOWED_CATEGORIES`). `tags`: list of strings. `model_folder_name`: optional explicit folder name for the uploaded model; if omitted, it's derived automatically as before (backward compatible). |
255
+ | **`get_model`** | `model_name`, `local_dest`, `version`, `owner` | Downloads model files. If `version` is omitted, downloads the version marked as 'latest'. `owner` (optional): resolve `model_name` from another user's project, e.g. to download a public model that isn't yours. |
256
+ | **`get_model_info`** | `identifier` (Name or ID), `owner` | Prints detailed info including **Owner**, **Web Link**, Tags, Category, and Version History. `owner` (optional): resolve a name-based `identifier` from another user's project. |
257
+ | **`get_allowed_categories`** | *(none)* | Returns the list of valid `category` values for `add_model`. |
258
+ | **`list_models`** | `verbose`, `category`, `tag`, `public_only`, `private_only`, `include_public` | Lists your own models **plus** (by default, `include_public=True`) every other user's `public=True` model — never another user's private model. Each entry includes a real `owner` field. `verbose=True` shows full JSON metadata. `category`/`tag` filter by exact match; `public_only`/`private_only` filter by visibility, applied across the combined set. `include_public=False` restores the pre-existing own-project-only behavior. When ACL is enabled, results are additionally filtered to models you can access (plus public ones). See "Cross-User Visibility" above. |
259
+ | **`get_model_id_by_name`** | `name`, `owner` | Resolves a model name to its ClearML ID. `owner` (optional): look it up in another user's project instead of your own. |
260
+ | **`get_model_name_by_id`** | `model_id` | Resolves a ClearML ID back to its model name (works for any model, regardless of owner). |
261
+ | **`get_latest_version`** | `model_identifier` (Name or ID) | Returns the version tag currently marked 'latest'. |
262
+ | **`set_latest_version`** | `model_name`, `version` | Updates the 'latest' pointer to a specific version without modifying files. |
263
+ | **`set_model_public`** | `model_name`, `public` | Moves a model's files between the private and public buckets (transactional, parallel server-side copy). |
264
+ | **`share_model`** | `model_name`, `target_user`, `access_level` | Grants another user access to a model. Requires `ACL_ENABLED=true`. |
265
+ | **`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. |
266
+
267
+ ---
268
+
269
+ ## HTTP API
270
+
271
+ The SDK ships a companion **HTTP API** service (`api/model-registry-api`) that exposes every SDK operation over HTTP for non-Python clients.
272
+
273
+ Full documentation for that service — endpoints, auth, request/response shapes, ACL toggle, curl examples — lives in its own repo:
274
+
275
+ > **`api/model-registry-api/README.md`**
276
+
277
+ The short version: run `uvicorn main:app --host 0.0.0.0 --port 8080`, pass `Authorization: Bearer <token>` on every call, and toggle ACL with `ACL_ENABLED=true/false` in the API service's `.env`.
278
+
279
+ ---
280
+
281
+ ## Constraints & Validations
282
+
283
+ 1. **Model Names:** Cannot start with `tmp_`, `_tmp_`, or `#`.
284
+ 2. **Version Names:**
285
+ * Manual versions cannot start with `_` (reserved for auto-generated versions).
286
+ * Auto-generated versions follow the pattern `_v1`, `_v2`, etc.
287
+
288
+
289
+ 3. **Categories:** Only categories returned by `manager.get_allowed_categories()` are allowed. Defaults to:
290
+ * `Text Generation`
291
+ * `Image Classification`
292
+
293
+ Override the full list via `MODEL_ALLOWED_CATEGORIES` in `.env` (comma-separated).
294
+
295
+
296
+
297
+ ---
298
+
299
+ ## Testing
300
+
301
+ - `test_model_registry.py` / `test_ceph_s3_manager.py`: automated unit tests (mock the ClearML/S3 boundary, no live infra needed). Run with:
302
+ ```bash
303
+ pip install pytest
304
+ pytest test_model_registry.py test_ceph_s3_manager.py -v
305
+ ```
306
+ - `test.py`: a manual/live-integration demo script covering every scenario above against **real** ClearML/Ceph/ACL services — everything is commented out by default; uncomment only what you intend to run.
307
+
308
+ ---
309
+
310
+ ## Admin Instructions: Auto-Publishing to PyPI
311
+
312
+ This SDK uses a GitHub Actions workflow (`.github/workflows/publish.yaml`) for automatic versioning and PyPI publishing.
313
+
314
+ ### Trigger Conditions
315
+
316
+ * Push to `main` branch.
317
+ * Commit message contains `pipy commit -push`.
318
+ * GitHub variable `PUBLISH_TO_PYPI` is set to `true`.
319
+
320
+ ### Commit Message Format
321
+
322
+ | Keyword | Action |
323
+ | --- | --- |
324
+ | `pipy commit -push major` | Increments **Major** version (e.g., 1.0.0 -> 2.0.0) |
325
+ | `pipy commit -push minor` | Increments **Minor** version (e.g., 0.1.0 -> 0.2.0) |
326
+ | `pipy commit -push` | Increments **Patch** version (e.g., 0.1.1 -> 0.1.2) |