modelstudio-sdk 0.0.0.dev0__tar.gz → 0.2.0__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.
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/CLAUDE.md +65 -36
- modelstudio_sdk-0.2.0/PKG-INFO +341 -0
- modelstudio_sdk-0.2.0/README.md +321 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/gl-sast-report.json +7 -7
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/gl-secret-detection-report.json +6 -6
- modelstudio_sdk-0.2.0/src/modelstudio/_version.py +1 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/src/modelstudio/client.py +47 -7
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/src/modelstudio/models/__init__.py +32 -0
- modelstudio_sdk-0.2.0/src/modelstudio/models/experiments.py +39 -0
- modelstudio_sdk-0.2.0/src/modelstudio/models/projects.py +38 -0
- modelstudio_sdk-0.2.0/src/modelstudio/models/runs.py +95 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/src/modelstudio/resources/dataset.py +11 -69
- modelstudio_sdk-0.2.0/src/modelstudio/resources/experiment.py +102 -0
- modelstudio_sdk-0.2.0/src/modelstudio/resources/project.py +144 -0
- modelstudio_sdk-0.2.0/src/modelstudio/resources/run.py +129 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/src/modelstudio/resources/split.py +25 -26
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/tests/helpers.py +3 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/tests/integration/conftest.py +39 -15
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/tests/integration/test_connectivity.py +4 -4
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/tests/integration/test_mutations.py +21 -20
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/tests/integration/test_read_only.py +10 -6
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/tests/test_client.py +26 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/tests/test_pandas.py +22 -20
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/tests/test_resources/test_dataset.py +2 -41
- modelstudio_sdk-0.2.0/tests/test_resources/test_experiment.py +71 -0
- modelstudio_sdk-0.2.0/tests/test_resources/test_project.py +117 -0
- modelstudio_sdk-0.2.0/tests/test_resources/test_run.py +145 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/tests/test_resources/test_split.py +21 -6
- modelstudio_sdk-0.0.0.dev0/PKG-INFO +0 -513
- modelstudio_sdk-0.0.0.dev0/README.md +0 -493
- modelstudio_sdk-0.0.0.dev0/src/modelstudio/_version.py +0 -1
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/.claude/commands/agent-os/discover-standards.md +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/.claude/commands/agent-os/index-standards.md +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/.claude/commands/agent-os/inject-standards.md +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/.claude/commands/agent-os/plan-product.md +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/.claude/commands/agent-os/shape-spec.md +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/.gitignore +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/.gitlab-ci.yml +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/LICENSE +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/Makefile +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/agent-os/specs/2026-03-06-sdk-phase2-pandas-integration/plan.md +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/agent-os/specs/2026-03-06-sdk-phase2-pandas-integration/references.md +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/agent-os/specs/2026-03-06-sdk-phase2-pandas-integration/shape.md +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/agent-os/specs/2026-03-06-sdk-phase2-pandas-integration/standards.md +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/agent-os/specs/2026-03-10-integration-testing-sdk/plan.md +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/agent-os/specs/2026-03-10-integration-testing-sdk/references.md +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/agent-os/specs/2026-03-10-integration-testing-sdk/shape.md +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/agent-os/specs/2026-03-10-integration-testing-sdk/standards.md +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/agent-os/specs/2026-03-11-1500-sdk-deploy-and-notebooks/plan.md +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/agent-os/specs/2026-03-11-1500-sdk-deploy-and-notebooks/references.md +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/agent-os/specs/2026-03-11-1500-sdk-deploy-and-notebooks/shape.md +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/agent-os/specs/2026-03-11-1500-sdk-deploy-and-notebooks/standards.md +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/agent-os/specs/2026-03-11-1900-custom-notebook-ui/plan.md +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/agent-os/specs/2026-03-11-1900-custom-notebook-ui/references.md +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/agent-os/specs/2026-03-11-1900-custom-notebook-ui/shape.md +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/agent-os/specs/2026-03-11-1900-custom-notebook-ui/standards.md +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/agent-os/specs/2026-03-12-sdk-api-realignment/plan.md +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/agent-os/specs/2026-03-12-sdk-api-realignment/references.md +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/agent-os/specs/2026-03-12-sdk-api-realignment/shape.md +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/agent-os/specs/2026-03-12-sdk-api-realignment/standards.md +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/agent-os/specs/2026-03-12-sdk-tutorials/plan.md +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/agent-os/specs/2026-03-12-sdk-tutorials/shape.md +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/agent-os/standards/index.yml +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/develop.sh +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/docker/.env.example +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/docker/Dockerfile.dev +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/docker/docker-compose.yml +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/docker/requirements.txt +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/pyproject.toml +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/scripts/get-token.sh +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/scripts/smoke_test.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/src/modelstudio/__init__.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/src/modelstudio/_http.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/src/modelstudio/_pandas.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/src/modelstudio/_polling.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/src/modelstudio/exceptions.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/src/modelstudio/models/annotations.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/src/modelstudio/models/categories.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/src/modelstudio/models/common.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/src/modelstudio/models/datasets.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/src/modelstudio/models/deletion.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/src/modelstudio/models/exports.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/src/modelstudio/models/few_shot.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/src/modelstudio/models/filters.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/src/modelstudio/models/history.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/src/modelstudio/models/images.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/src/modelstudio/models/imports.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/src/modelstudio/models/media.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/src/modelstudio/models/merge.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/src/modelstudio/models/metrics.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/src/modelstudio/models/oversample.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/src/modelstudio/models/splits.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/src/modelstudio/models/validation.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/src/modelstudio/resources/__init__.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/tests/__init__.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/tests/conftest.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/tests/integration/__init__.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/tests/test_http.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/tests/test_polling.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/tests/test_resources/__init__.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/tests/test_resources/conftest.py +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/tutorials/.ipynb_checkpoints/getting-started-checkpoint.ipynb +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/tutorials/01-getting-started.ipynb +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/tutorials/02-exploring-data.ipynb +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/tutorials/03-category-management.ipynb +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/tutorials/04-import-export.ipynb +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/tutorials/05-annotations.ipynb +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/tutorials/06-advanced-workflows.ipynb +0 -0
- {modelstudio_sdk-0.0.0.dev0 → modelstudio_sdk-0.2.0}/tutorials/README.md +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Project Overview
|
|
4
4
|
|
|
5
|
-
**Model Studio SDK** is a Python package that wraps the Model Studio REST API, providing typed access to
|
|
5
|
+
**Model Studio SDK** is a Python package that wraps the Model Studio REST API, providing typed access to projects, datasets, experiments, runs, annotations, and ML workflow operations. This repo also hosts the **Jupyter Server Docker infrastructure** for the custom notebook UI.
|
|
6
6
|
|
|
7
7
|
**Tech Stack**: Python 3.10+, httpx (HTTP client), pydantic v2 (response models), pandas (optional DataFrame helpers)
|
|
8
8
|
|
|
@@ -22,24 +22,48 @@
|
|
|
22
22
|
### Package Layout (`src/modelstudio/`)
|
|
23
23
|
|
|
24
24
|
```
|
|
25
|
-
client.py # ModelStudioClient entry point (from_env,
|
|
25
|
+
client.py # ModelStudioClient entry point (from_env, projects, project, dataset)
|
|
26
26
|
_http.py # HttpTransport - httpx wrapper with auth + error mapping
|
|
27
27
|
_polling.py # OperationPoller - wait on async ops (clone, import, finalize)
|
|
28
28
|
_pandas.py # Optional pandas helpers (guarded import)
|
|
29
29
|
exceptions.py # Typed exception hierarchy (BadRequestError, NotFoundError, etc.)
|
|
30
30
|
models/ # Pydantic v2 response/request DTOs (match API snake_case)
|
|
31
|
+
projects.py # ProjectModel, CreateProjectRequest, UpdateProjectRequest
|
|
32
|
+
experiments.py # ExperimentModel, CreateExperimentRequest, UpdateExperimentRequest
|
|
33
|
+
runs.py # RunModel, CreateRunRequest, UpdateRunRequest, CloneRunRequest, etc.
|
|
34
|
+
datasets.py # DatasetModel
|
|
35
|
+
annotations.py # AnnotationModel, CreateAnnotationRequest, etc.
|
|
36
|
+
... # categories, images, imports, exports, metrics, filters, etc.
|
|
31
37
|
resources/
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
project.py # ProjectsCollection + Project (datasets, experiments access)
|
|
39
|
+
experiment.py # ExperimentsCollection + Experiment (runs access)
|
|
40
|
+
run.py # RunsCollection + Run (CRUD, submit, cancel, metrics, stages)
|
|
41
|
+
dataset.py # Dataset (~50 methods for per-dataset operations)
|
|
42
|
+
split.py # Split sub-resource (images, annotations, import, export)
|
|
34
43
|
```
|
|
35
44
|
|
|
36
45
|
### Resource Pattern
|
|
37
46
|
|
|
38
47
|
```python
|
|
39
48
|
client = ModelStudioClient.from_env()
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
client.
|
|
49
|
+
|
|
50
|
+
# Projects & datasets (project-scoped)
|
|
51
|
+
client.projects.list() # List projects
|
|
52
|
+
client.project("pid").datasets() # List datasets in project
|
|
53
|
+
client.project("pid").create_dataset(name="...") # Create dataset in project
|
|
54
|
+
|
|
55
|
+
# Per-dataset operations (by dataset ID)
|
|
56
|
+
client.dataset("did").overview() # Dataset-bound methods
|
|
57
|
+
client.dataset("did").split("sid").list_images() # Split sub-resource
|
|
58
|
+
|
|
59
|
+
# Experiments & runs (nested under project)
|
|
60
|
+
client.project("pid").experiments.list()
|
|
61
|
+
client.project("pid").experiment("eid").runs.list()
|
|
62
|
+
client.project("pid").experiment("eid").run("rid").submit()
|
|
63
|
+
|
|
64
|
+
# Merge (global, not project-scoped)
|
|
65
|
+
client.merge_datasets_analyze(["ds-1", "ds-2"])
|
|
66
|
+
client.merge_datasets(request)
|
|
43
67
|
```
|
|
44
68
|
|
|
45
69
|
---
|
|
@@ -47,6 +71,7 @@ client.dataset("uuid").split("sid").list_images() # Split sub-resource
|
|
|
47
71
|
## Code Conventions
|
|
48
72
|
|
|
49
73
|
- **Pydantic v2** models with snake_case fields (matches API JSON directly)
|
|
74
|
+
- **`model_dump(mode="json")`** for request bodies — ensures UUIDs serialize to strings
|
|
50
75
|
- **Typed exceptions** mapped from API error envelope `{"error": "...", "message": "..."}`
|
|
51
76
|
- **Guarded pandas import** in `_pandas.py` - raises helpful error if not installed
|
|
52
77
|
- **`from_env()` contract**: reads `MODEL_STUDIO_API_URL`, `MODEL_STUDIO_JWT`, `MODEL_STUDIO_ORG`
|
|
@@ -58,20 +83,34 @@ client.dataset("uuid").split("sid").list_images() # Split sub-resource
|
|
|
58
83
|
## Key Design Decisions
|
|
59
84
|
|
|
60
85
|
- Resource pattern over flat methods for discoverability
|
|
86
|
+
- Dataset operations are ID-based (`client.dataset(id)`), not project-scoped, since the API supports direct dataset access
|
|
87
|
+
- Project-scoped operations for listing and creating datasets (`client.project(id).datasets()`)
|
|
61
88
|
- Pollers return immediately; call `.wait()` to block
|
|
62
89
|
- Models use `| None` for optional fields with defaults
|
|
63
|
-
- `model_dump(exclude_none=True)` for request bodies to avoid sending null fields
|
|
90
|
+
- `model_dump(mode="json", exclude_none=True)` for request bodies to avoid sending null fields and ensure UUID serialization
|
|
64
91
|
|
|
65
92
|
---
|
|
66
93
|
|
|
67
94
|
## When Making Changes
|
|
68
95
|
|
|
69
96
|
- DO follow pydantic v2 patterns (BaseModel, model_validate, model_dump)
|
|
97
|
+
- DO use `model_dump(mode="json")` for request bodies (not plain `model_dump()`)
|
|
70
98
|
- DO keep httpx sync-only
|
|
71
99
|
- DO map new API error codes to exception classes
|
|
100
|
+
- DO add new resource classes following the existing pattern (Collection + Instance)
|
|
72
101
|
- DON'T break `from_env()` contract
|
|
73
102
|
- DON'T add async httpx
|
|
74
103
|
- DON'T import pandas at module level (always guard behind `_pandas.py`)
|
|
104
|
+
- DON'T use `client.datasets` — that was removed; datasets are listed via projects
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## API Reference
|
|
109
|
+
|
|
110
|
+
The complete SDK method reference lives in the tutorials repo:
|
|
111
|
+
[model-studio-notebooks/tutorials/api-reference.md](https://gitlab.com/orbitalinsight/elements/model-studio/model-studio-notebooks/-/blob/master/tutorials/api-reference.md)
|
|
112
|
+
|
|
113
|
+
This file is mounted into the notebook environment so users can access it directly.
|
|
75
114
|
|
|
76
115
|
---
|
|
77
116
|
|
|
@@ -87,9 +126,16 @@ client.dataset("uuid").split("sid").list_images() # Split sub-resource
|
|
|
87
126
|
- `./develop.sh --mode notebook-server` — start headless Jupyter Server (Docker) on port 8889 for custom notebook UI
|
|
88
127
|
- `./develop.sh --mode docker` — start full JupyterLab (Docker) on port 8888
|
|
89
128
|
- `scripts/get-token.sh` — standalone Keycloak token fetcher (used by develop.sh)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
129
|
+
|
|
130
|
+
### Integration Tests
|
|
131
|
+
|
|
132
|
+
Integration tests run against the live elements-dev API. They expect:
|
|
133
|
+
- **Project**: "SDK Testing"
|
|
134
|
+
- **Datasets**: "sdk-test-read-only" (with splits, images, annotations) and "sdk-test-mutations" (single Train split)
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
./develop.sh --mode test-integration
|
|
138
|
+
```
|
|
93
139
|
|
|
94
140
|
### Custom Notebook UI (cross-repo)
|
|
95
141
|
|
|
@@ -97,9 +143,8 @@ The notebook UI spans this repo (Jupyter Server backend) and the `frontend` repo
|
|
|
97
143
|
|
|
98
144
|
1. **This repo**: `docker/docker-compose.yml` defines `jupyter-server` service (headless, CORS for localhost:5173)
|
|
99
145
|
2. **Frontend**: `vite.config.ts` proxies `/jupyter/*` → `localhost:8889` (includes WebSocket)
|
|
100
|
-
3. **Frontend**: `src/
|
|
101
|
-
4. **Frontend**: `src/
|
|
102
|
-
5. **Frontend**: `src/pages/ModelStudio/Notebooks/NotebookPanel/` — React UI components
|
|
146
|
+
3. **Frontend**: `src/api/notebooks/` — auth, API clients, WebSocket manager, MobX store
|
|
147
|
+
4. **Frontend**: `src/pages/ModelStudio/Notebooks/` — React UI components
|
|
103
148
|
|
|
104
149
|
To run locally: `./develop.sh --mode notebook-server` (this repo) + `yarn start` (frontend repo)
|
|
105
150
|
|
|
@@ -117,31 +162,15 @@ To run locally: `./develop.sh --mode notebook-server` (this repo) + `yarn start`
|
|
|
117
162
|
|
|
118
163
|
| File | Purpose |
|
|
119
164
|
|------|---------|
|
|
120
|
-
| `src/modelstudio/client.py` | Main entry point |
|
|
165
|
+
| `src/modelstudio/client.py` | Main entry point (projects, project, dataset, merge) |
|
|
121
166
|
| `src/modelstudio/_http.py` | HTTP transport with auth |
|
|
122
167
|
| `src/modelstudio/exceptions.py` | Exception hierarchy |
|
|
123
|
-
| `src/modelstudio/resources/
|
|
124
|
-
| `src/modelstudio/resources/
|
|
125
|
-
| `src/modelstudio/
|
|
168
|
+
| `src/modelstudio/resources/project.py` | Project + dataset listing/creation |
|
|
169
|
+
| `src/modelstudio/resources/experiment.py` | Experiment CRUD |
|
|
170
|
+
| `src/modelstudio/resources/run.py` | Run CRUD + submit/cancel/metrics |
|
|
171
|
+
| `src/modelstudio/resources/dataset.py` | All per-dataset operations (~50 methods) |
|
|
172
|
+
| `src/modelstudio/resources/split.py` | Split sub-resource (import, export, images) |
|
|
126
173
|
| `tests/conftest.py` | MockTransport fixture |
|
|
127
174
|
| `develop.sh` | Single-command dev setup (conda + deps + auth) |
|
|
128
175
|
| `scripts/get-token.sh` | Keycloak JWT token fetcher |
|
|
129
176
|
| `docker/docker-compose.yml` | Docker services (notebook + jupyter-server) |
|
|
130
|
-
| `docker/Dockerfile.dev` | Dev image (scipy-notebook + SDK + ML packages) |
|
|
131
|
-
|
|
132
|
-
### Frontend Files (in `../frontend`)
|
|
133
|
-
|
|
134
|
-
| File | Purpose |
|
|
135
|
-
|------|---------|
|
|
136
|
-
| `src/services/jupyter/JupyterService.ts` | REST client for Jupyter Server API |
|
|
137
|
-
| `src/services/jupyter/JupyterWebSocketManager.ts` | WebSocket kernel protocol handler |
|
|
138
|
-
| `src/services/jupyter/messageBuilder.ts` | Jupyter wire-protocol message builder |
|
|
139
|
-
| `src/services/jupyter/types.ts` | Jupyter API + kernel protocol type definitions |
|
|
140
|
-
| `src/store/notebook_store.ts` | MobX store for notebook state + execution |
|
|
141
|
-
| `src/pages/ModelStudio/Notebooks/NotebookPanel/` | All notebook UI components |
|
|
142
|
-
| `src/pages/ModelStudio/Layout/AppBar/AppBar.tsx` | AppBar with Notebook + Agent buttons |
|
|
143
|
-
| `vite.config.ts` | Vite proxy: `/jupyter/*` → localhost:8889 |
|
|
144
|
-
|
|
145
|
-
---
|
|
146
|
-
|
|
147
|
-
**API Reference**: See `model-studio-api/agent-os/standards/api/` for the API contract standards.
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: modelstudio-sdk
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Python SDK for the Model Studio REST API
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Requires-Dist: httpx<1.0,>=0.25.0
|
|
9
|
+
Requires-Dist: pydantic<3.0,>=2.0
|
|
10
|
+
Provides-Extra: dev
|
|
11
|
+
Requires-Dist: mypy>=1.8; extra == 'dev'
|
|
12
|
+
Requires-Dist: pandas>=1.5.0; extra == 'dev'
|
|
13
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
14
|
+
Requires-Dist: pytest-httpx>=0.30.0; extra == 'dev'
|
|
15
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
16
|
+
Requires-Dist: ruff>=0.4.0; extra == 'dev'
|
|
17
|
+
Provides-Extra: pandas
|
|
18
|
+
Requires-Dist: pandas>=1.5.0; extra == 'pandas'
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# Model Studio Python SDK
|
|
22
|
+
|
|
23
|
+
Python SDK for the Model Studio REST API. Provides typed access to projects, datasets, annotations, metrics, and ML workflow operations.
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# From wheel (in notebook containers, pre-installed)
|
|
29
|
+
pip install modelstudio-sdk
|
|
30
|
+
|
|
31
|
+
# Development install
|
|
32
|
+
git clone https://gitlab.com/orbitalinsight/elements/model-studio/modelstudio-sdk.git
|
|
33
|
+
cd modelstudio-sdk
|
|
34
|
+
pip install -e ".[dev]"
|
|
35
|
+
|
|
36
|
+
# With pandas support
|
|
37
|
+
pip install "modelstudio-sdk[pandas]"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Quick Start
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from modelstudio import ModelStudioClient
|
|
44
|
+
|
|
45
|
+
# Auto-configured inside notebooks (reads env vars)
|
|
46
|
+
client = ModelStudioClient.from_env()
|
|
47
|
+
|
|
48
|
+
# Or explicit
|
|
49
|
+
client = ModelStudioClient(
|
|
50
|
+
base_url="http://localhost:8081",
|
|
51
|
+
jwt_token="eyJhbG...",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# List projects and datasets
|
|
55
|
+
projects = client.projects.list()
|
|
56
|
+
project = client.project(str(projects[0].id))
|
|
57
|
+
for ds in project.datasets():
|
|
58
|
+
print(f"{ds.name} ({ds.dataset_type})")
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Environment Variables
|
|
62
|
+
|
|
63
|
+
| Variable | Required | Description |
|
|
64
|
+
|----------|----------|-------------|
|
|
65
|
+
| `MODEL_STUDIO_API_URL` | Yes | API base URL |
|
|
66
|
+
| `MODEL_STUDIO_JWT` | No | JWT authentication token |
|
|
67
|
+
| `MODEL_STUDIO_ORG` | No | Organization name |
|
|
68
|
+
|
|
69
|
+
## Usage Examples
|
|
70
|
+
|
|
71
|
+
### Projects & Datasets
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
# List projects
|
|
75
|
+
projects = client.projects.list()
|
|
76
|
+
|
|
77
|
+
# Create a project
|
|
78
|
+
project = client.projects.create(name="My Project", description="...")
|
|
79
|
+
|
|
80
|
+
# List datasets in a project
|
|
81
|
+
datasets = client.project("project-uuid").datasets()
|
|
82
|
+
|
|
83
|
+
# Create a dataset in a project
|
|
84
|
+
ds_model = client.project("project-uuid").create_dataset(
|
|
85
|
+
name="My Dataset",
|
|
86
|
+
dataset_type="object-detection-coco",
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Work with a specific dataset (by ID, independent of project)
|
|
90
|
+
ds = client.dataset("dataset-uuid")
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Dataset Overview & Metrics
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
ds = client.dataset("dataset-uuid")
|
|
97
|
+
|
|
98
|
+
overview = ds.overview()
|
|
99
|
+
print(f"Images: {overview.summary.total_images}")
|
|
100
|
+
print(f"Annotations: {overview.summary.total_annotations}")
|
|
101
|
+
|
|
102
|
+
for split in ds.splits():
|
|
103
|
+
print(f"{split.name} ({split.split_type})")
|
|
104
|
+
|
|
105
|
+
cats = ds.categories()
|
|
106
|
+
for cat in cats.categories:
|
|
107
|
+
print(f"{cat.name}: {cat.annotation_count} annotations")
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Category Management
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
ds.merge_categories(source_categories=[1, 2, 3], target_category="vehicle")
|
|
114
|
+
ds.rename_category(category_id=5, new_name="truck")
|
|
115
|
+
ds.remove_category(category_id=10)
|
|
116
|
+
ds.consolidate_labels({"car": "vehicle", "van": "vehicle"})
|
|
117
|
+
|
|
118
|
+
# Undo any mutation
|
|
119
|
+
ds.undo()
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Split Operations
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
ds.redistribute(ratios={"train": 0.8, "val": 0.1, "test": 0.1}, seed=42)
|
|
126
|
+
ds.class_aware_redistribute(ratios={"train": 0.8, "val": 0.2}, prevent_tile_leakage=True)
|
|
127
|
+
ds.check_leakage()
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Import & Export
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
split = ds.split("split-uuid")
|
|
134
|
+
|
|
135
|
+
# Import from S3
|
|
136
|
+
queued = split.import_from_source("s3", "coco", {
|
|
137
|
+
"connection_id": "conn-uuid",
|
|
138
|
+
"bucket": "my-bucket",
|
|
139
|
+
"prefix": "datasets/coco/",
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
# Export as COCO JSON
|
|
143
|
+
coco = ds.export_coco()
|
|
144
|
+
split_coco = split.export_coco()
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Cloning & Async Operations
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
cloned = ds.clone(name="My Clone")
|
|
151
|
+
poller = ds.clone_poller(interval=2.0, max_wait=300.0)
|
|
152
|
+
result = poller.wait()
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Annotations
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
from modelstudio.models.annotations import CreateAnnotationRequest
|
|
159
|
+
|
|
160
|
+
# Paginated listing
|
|
161
|
+
page = ds.list_annotations(split_id="...", page=0, size=50)
|
|
162
|
+
|
|
163
|
+
# Create
|
|
164
|
+
ds.create_annotation(CreateAnnotationRequest(
|
|
165
|
+
image_id="img-uuid", category_id=1, bbox=[0, 0, 50, 50], area=2500,
|
|
166
|
+
))
|
|
167
|
+
|
|
168
|
+
# Bulk delete
|
|
169
|
+
ds.delete_annotations(annotation_ids=[1, 2, 3])
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Few-Shot & Oversampling
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
from modelstudio.models.few_shot import FewShotRequest
|
|
176
|
+
from modelstudio.models.oversample import OversampleRequest
|
|
177
|
+
|
|
178
|
+
preview = ds.few_shot_preview(FewShotRequest(num_images=100, method="RANDOM", seed=42))
|
|
179
|
+
result = ds.oversample_execute(OversampleRequest(target_ratio=0.5, strategy="PREFER_ANNOTATED"))
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Dataset Merge
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
from modelstudio.models.merge import MergeDatasetRequest
|
|
186
|
+
|
|
187
|
+
analysis = client.merge_datasets_analyze(["ds-1", "ds-2"])
|
|
188
|
+
result = client.merge_datasets(MergeDatasetRequest(
|
|
189
|
+
source_dataset_ids=["ds-1", "ds-2"],
|
|
190
|
+
target_name="merged-dataset",
|
|
191
|
+
))
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Experiments & Runs
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
project = client.project("project-uuid")
|
|
198
|
+
|
|
199
|
+
# Create experiment
|
|
200
|
+
exp = project.experiments.create(name="YOLOv8 ablation")
|
|
201
|
+
|
|
202
|
+
# Create and submit a run
|
|
203
|
+
from modelstudio.models.runs import CreateRunRequest
|
|
204
|
+
run_model = project.experiment(str(exp.id)).runs.create(CreateRunRequest(
|
|
205
|
+
name="baseline",
|
|
206
|
+
model_architecture="yolov8",
|
|
207
|
+
dataset_id="dataset-uuid",
|
|
208
|
+
))
|
|
209
|
+
run = project.experiment(str(exp.id)).run(str(run_model.id))
|
|
210
|
+
run.submit()
|
|
211
|
+
|
|
212
|
+
# Monitor
|
|
213
|
+
run.stages()
|
|
214
|
+
run.metrics()
|
|
215
|
+
run.metrics_data(name="loss")
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### DataFrame Integration
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
# Requires: pip install "modelstudio-sdk[pandas]"
|
|
222
|
+
ds.images_df(size=100)
|
|
223
|
+
ds.annotations_df(size=100)
|
|
224
|
+
ds.categories_df()
|
|
225
|
+
ds.class_distribution_df()
|
|
226
|
+
split.images_df()
|
|
227
|
+
split.annotations_df()
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Error Handling
|
|
231
|
+
|
|
232
|
+
```python
|
|
233
|
+
from modelstudio.exceptions import NotFoundError, ConflictError, BadRequestError
|
|
234
|
+
|
|
235
|
+
try:
|
|
236
|
+
ds.overview()
|
|
237
|
+
except NotFoundError as e:
|
|
238
|
+
print(f"Not found: {e.message}")
|
|
239
|
+
except BadRequestError as e:
|
|
240
|
+
print(f"Bad request: {e.message}")
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## API Reference
|
|
244
|
+
|
|
245
|
+
For the complete method listing, see the [API Reference](https://gitlab.com/orbitalinsight/elements/model-studio/model-studio-notebooks/-/blob/master/tutorials/api-reference.md) in the tutorials folder (also available in your notebook environment at `tutorials/api-reference.md`).
|
|
246
|
+
|
|
247
|
+
## Architecture
|
|
248
|
+
|
|
249
|
+
### Related Repositories
|
|
250
|
+
|
|
251
|
+
| Repo | Purpose |
|
|
252
|
+
|------|---------|
|
|
253
|
+
| [`model-studio-sdk`](https://gitlab.com/orbitalinsight/elements/model-studio/modelstudio-sdk) | This repo — Python SDK + Jupyter Server Docker |
|
|
254
|
+
| [`frontend`](https://gitlab.com/orbitalinsight/frontend-2.0) | Model Studio React frontend (custom notebook UI lives here) |
|
|
255
|
+
| [`model-studio-api`](https://gitlab.com/orbitalinsight/elements/model-studio/model-studio-api) | Backend REST API the SDK wraps |
|
|
256
|
+
| [`model-studio-notebooks`](https://gitlab.com/orbitalinsight/elements/model-studio/model-studio-notebooks) | JupyterHub + KubeSpawner Helm chart (production multi-user) |
|
|
257
|
+
| [`model-studio-agent`](https://gitlab.com/orbitalinsight/elements/model-studio/model-studio-agent) | Agent chat backend |
|
|
258
|
+
| [`keycloak-config`](https://gitlab.com/orbitalinsight/elements/keycloak-config) | Keycloak realm/client configuration |
|
|
259
|
+
|
|
260
|
+
### Local Development Architecture
|
|
261
|
+
|
|
262
|
+
```
|
|
263
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
264
|
+
│ Browser (http://localhost:5173) │
|
|
265
|
+
│ │
|
|
266
|
+
│ ┌───────────────────────────────────────────────────────┐ │
|
|
267
|
+
│ │ Model Studio Frontend (Vite) │ │
|
|
268
|
+
│ │ ┌──────────────┐ ┌──────────────┐ ┌────────────┐ │ │
|
|
269
|
+
│ │ │ Dataset Pages │ │ Notebook │ │ Agent Chat │ │ │
|
|
270
|
+
│ │ │ │ │ Panel │ │ Panel │ │ │
|
|
271
|
+
│ │ └──────────────┘ └──────┬───────┘ └─────┬──────┘ │ │
|
|
272
|
+
│ └───────────────────────────┼────────────────┼──────────┘ │
|
|
273
|
+
│ │ │ │
|
|
274
|
+
│ Vite Dev Server Proxies: │ │ │
|
|
275
|
+
│ /jupyter/* ─────────────────┘ │ │
|
|
276
|
+
│ /agent/* ────────────────────────────────────┘ │
|
|
277
|
+
└──────────────────────────────┼────────────────┼─────────────┘
|
|
278
|
+
│ │
|
|
279
|
+
┌────────────────┘ │
|
|
280
|
+
▼ ▼
|
|
281
|
+
┌──────────────────────────┐ ┌──────────────────────────┐
|
|
282
|
+
│ Jupyter Server (Docker) │ │ Agent API │
|
|
283
|
+
│ localhost:8889 │ │ localhost:8080 │
|
|
284
|
+
│ │ │ (model-studio-agent) │
|
|
285
|
+
│ ┌────────────────────┐ │ └──────────────────────────┘
|
|
286
|
+
│ │ Python 3.10 Kernel │ │
|
|
287
|
+
│ │ + Model Studio SDK │ │
|
|
288
|
+
│ └────────┬───────────┘ │
|
|
289
|
+
└───────────┼──────────────┘
|
|
290
|
+
│
|
|
291
|
+
▼
|
|
292
|
+
┌──────────────────────────────────────────────────────────┐
|
|
293
|
+
│ Model Studio API │
|
|
294
|
+
│ https://model-studio-api.elements.dev.privateer.com │
|
|
295
|
+
└──────────────────────────────────────────────────────────┘
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
## Development
|
|
299
|
+
|
|
300
|
+
### Prerequisites
|
|
301
|
+
|
|
302
|
+
- [Miniconda](https://docs.conda.io/en/latest/miniconda.html) or Anaconda
|
|
303
|
+
- Docker + Docker Compose (for notebook server)
|
|
304
|
+
- `jq` and `curl` (for Keycloak token fetch)
|
|
305
|
+
|
|
306
|
+
### Quick Start
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
./develop.sh --mode setup # Create conda env, install deps
|
|
310
|
+
./develop.sh --mode test # Run unit tests
|
|
311
|
+
./develop.sh --mode test-integration # Fetch Keycloak token + run integration tests
|
|
312
|
+
./develop.sh --mode lint # Run ruff + mypy
|
|
313
|
+
./develop.sh --mode notebook-server # Start headless Jupyter Server on port 8889
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Make Targets
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
make install # pip install -e ".[dev]"
|
|
320
|
+
make test # Unit tests with coverage
|
|
321
|
+
make test-unit # Unit tests only (no integration)
|
|
322
|
+
make lint # ruff + mypy
|
|
323
|
+
make build # Build wheel
|
|
324
|
+
make notebook-server # Start headless Jupyter Server (port 8889)
|
|
325
|
+
make docker # Start full JupyterLab (port 8888)
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Integration Tests
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
./develop.sh --mode test-integration
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
Integration tests run against the live API (elements-dev). They expect a project named "SDK Testing" with datasets "sdk-test-read-only" and "sdk-test-mutations".
|
|
335
|
+
|
|
336
|
+
## Requirements
|
|
337
|
+
|
|
338
|
+
- Python >= 3.10
|
|
339
|
+
- httpx >= 0.25.0
|
|
340
|
+
- pydantic >= 2.0
|
|
341
|
+
- pandas >= 1.5.0 (optional)
|