sstudio Python SDK
Python client and command-line interface for model lifecycle APIs.
Requirements
- Python 3.8+
- An API key
- An API base URL
Installation
pip install sstudio
This package installs both the Python SDK and the sstudio command.
Quick Start
import os
from sstudio import SmartStudioClient
with SmartStudioClient(
api_key=os.environ["SSTUDIO_PLATFORM__API_KEY"],
base_url=os.environ["SSTUDIO_PLATFORM__API_ENDPOINT"],
) as client:
me = client.me()
models = client.models.list(page=1, page_size=20)
print(me)
print(models)
The context manager closes the underlying HTTP client automatically.
Authentication
Only the API key and API base URL are required. The SDK manages the two-step authentication flow internally:
| Step | Behavior |
|---|---|
| Login | Exchanges the API key for a short-lived bearer token. |
| Requests | Adds the bearer token to authenticated API requests. |
| Refresh | Refreshes authentication and retries a safe request once after a 401. |
The API key is never sent to pre-signed object storage URLs.
Client Configuration
import os
client = SmartStudioClient(
api_key=os.environ["SSTUDIO_PLATFORM__API_KEY"],
base_url=os.environ["SSTUDIO_PLATFORM__API_ENDPOINT"],
timeout=30.0,
max_retries=2,
default_headers={"X-Request-Source": "integration"},
)
The optional on_token_refresh callback is intended for a host application
that already has an approved secret store. It is omitted here so the example
is runnable and does not encourage saving bearer tokens to ordinary files.
| Option | Required | Description |
|---|---|---|
api_key | Yes | API key exchanged for a bearer token. |
base_url | Yes* | API origin or console URL. May be supplied by environment variable. |
token | No | Existing bearer token managed by the host application. |
timeout | No | Request timeout in seconds. Default: 30. |
max_retries | No | Safe-request retry count. Default: 2. |
default_headers | No | Additional string headers for API requests. |
on_token_refresh | No | Callback invoked when a new bearer token is issued. Store it only in an approved secret store; never log it. |
base_url can also be configured with:
export SSTUDIO_PLATFORM__API_ENDPOINT="https://your-host.example"
export SSTUDIO_PLATFORM__API_KEY="your-api-key"
The CLI additionally accepts SSTUDIO_PLATFORM__API_KEY. Constructor arguments
take precedence, and the SDK has no implicit network endpoint.
An environment origin, a /msp-console URL, and an explicit /msp-api URL are
all normalized to the same API base path.
Return Values and Errors
Resource methods return the response envelope's data value:
{
"code": 200,
"message": "success",
"data": {"id": 42, "status": "RUNNING"}
}
For this response, client.deployments.get(42) returns the object inside
data. Methods without a result return None; text endpoints such as
client.deployments.yaml(42) return str. Use client.request(...) only when
the complete envelope is required.
from sstudio import APIError, AuthenticationError, ValidationError
try:
deployment = client.deployments.get(42)
except AuthenticationError:
print("The API key or session is invalid")
except ValidationError as exc:
print(exc.error_code, exc.details)
except APIError as exc:
print(exc.message)
API exceptions preserve code, message, error_code, and details when
provided by the server.
Module Overview
| Module | Accessor | Typical operations |
|---|---|---|
| Identity | client.me() | Current authenticated user |
| Clusters | client.clusters | Cluster list, capacity, history, resource holds |
| Models | client.models | Model catalog and deployment capabilities |
| My Models | client.my_models | End-to-end model upload and asset lifecycle |
| Datasets | client.datasets | End-to-end upload, list, preview, download, lifecycle |
| AI Dataset | client.datasets.preparations | Preparation, labeling, resource preview, download |
| Deployments | client.deployments | Preflight, create, lifecycle, events, YAML |
| Training | client.training | Preflight, create, wait, cancel, artifacts |
| Evaluations | client.evaluations | Preflight, create, wait, reports, artifacts |
| Jobs | client.jobs | Generic job status, logs, wait, cancel |
| API Keys | client.keys | Platform API key lifecycle |
| Provider Keys | client.provider_keys | BYOK provider credentials and connectivity tests |
| Usage | client.usage | Usage records, summaries, trends |
| Observability | client.observability | Cluster, service, and workload metrics |
Use the API Map to move between the Python, TypeScript, and CLI references.
Module Examples
The following examples assume an initialized client.
Identity
me = client.me()
Clusters
clusters = client.clusters.list()
capacity = client.clusters.capacity(1)
history = client.clusters.timeseries(1, limit=60)
holds = client.clusters.holds(cluster_id=1)
Models
models = client.models.list(keyword="Qwen", page=1, page_size=20)
model = client.models.get("qwen3-4b")
capabilities = client.models.deployment_capabilities(
"qwen3-4b",
cluster_id="1",
)
My Models
upload(...) validates model files, hashes the directory, transfers objects,
completes the upload, and returns the registered model asset.
model = client.my_models.upload(
"./model",
name="example-model",
model_type="LLM",
)
model_id = model["id"]
page = client.my_models.list(page=1, page_size=20)
detail = client.my_models.get(model_id)
client.my_models.update(model_id, {"description": "production candidate"})
Datasets
dataset = client.datasets.upload(
"./train.jsonl",
name="example-training-dataset",
dataset_type="training",
training_category="sft-llm",
)
dataset_id = dataset["id"]
preview = client.datasets.preview(dataset_id, limit=20)
page = client.datasets.list(page_num=1, page_size=20)
download = client.datasets.download_url({"datasetId": dataset_id, "fileIndex": 0})
AI Dataset Preparations
clusters = client.datasets.preparations.cluster_options()
provider_keys = client.datasets.preparations.available_provider_keys()
tasks = client.datasets.preparations.list({"pageNum": 1, "pageSize": 20})
task = client.datasets.preparations.get(id=123)
Create and action payloads are passed as dictionaries matching the API contract:
preview = client.datasets.preparations.preview(resource_preview_request)
task = client.datasets.preparations.create(preparation_request)
client.datasets.preparations.generate_rules(id=task["id"])
rules_ready = client.wait_for_dataset_preparation(
task["id"], until="rules_ready"
)
client.datasets.preparations.start_labeling(labeling_request)
completed = client.wait_for_dataset_preparation(task["id"])
Deployments
The workflow automatically selects the Model Profile or model-asset creation route from the request's model reference.
request = {
"name": "example-deployment",
"modelSource": "gallery",
"modelName": "Qwen3-4B-Instruct-2507-FAST",
"backend": "sglang",
"servingMode": "standard",
"gpuType": "L20",
"replicas": 1,
"clusterId": 1,
}
preview = client.deployments.preview(request)
if preview.get("creatable"):
deployment = client.deployments.create(request)
running = client.wait_for_deployment(deployment["id"])
Lifecycle and diagnostics:
page = client.deployments.list(page=1, page_size=20)
events = client.deployments.events(42)
rendered_yaml = client.deployments.yaml(42)
client.deployments.stop(42)
client.deployments.preview_restart(42)
client.deployments.restart(42)
Training
outputModelName is a suffix, not the complete asset name. Its maximum length
depends on the selected base model because the final deployable name is
<base>-FT-<suffix>; use the limit returned by the active capability.
request = {
"clientToken": "training-request-001",
"displayName": "example-training",
"outputModelName": "example-output",
"recipeId": "recipe-id",
"recipeVersion": "recipe-version",
"baseModelRef": {"type": "recipe_model", "id": "model-id"},
"datasetRefs": [{"datasetId": "dataset-id", "role": "train"}],
"placement": {"clusterId": "1", "resourceSpecId": "resource-spec-id"},
"params": {},
}
client.training.preview({"clusterId": 1, "resourceSpecId": "resource-spec-id"})
job = client.training.create(request)
completed = client.wait_for_training_job(job["jobId"])
artifact = client.training.artifact_download_url(
completed["artifacts"][0]["artifactId"]
)
Evaluations
request = {
"kind": "benchmark",
"modelType": "LLM",
"models": [{
"type": "service",
"msp_api_key_id": "7",
"service_id": "42",
}],
"dataset": "evaluation-dataset",
"maxSamples": 100,
"clusterId": 1,
}
client.evaluations.preview({"clusterId": 1})
job = client.evaluations.create(request)
client.wait_for_evaluation_job(job["jobId"])
report = client.evaluations.report(job["jobId"])
Jobs
jobs = client.jobs.list(type="train", status="RUNNING", page=1, page_size=20)
job = client.jobs.get("job-id")
logs = client.jobs.logs("job-id", tail=200)
client.wait_for_job("job-id")
API Keys
keys = client.keys.list()
created = client.keys.create({
"keyValue": "sk-created-by-caller",
"description": "automation key",
"isActive": True,
})
key_id = str(created["id"])
plaintext = client.keys.reveal(key_id)
client.keys.update(key_id, {"description": "renamed key", "isActive": True})
Treat the value returned by reveal(...) as a secret and never log it.
Provider Keys
providers = client.provider_keys.providers()
keys = client.provider_keys.list()
created = client.provider_keys.create({
"provider": "example-provider",
"apiKey": "provider-api-key",
"description": "integration credential",
})
result = client.provider_keys.test(created["id"])
client.provider_keys.change_status(created["id"], {"status": 1})
Usage
start_seconds = 1782864000
end_seconds = 1785542400
summary = client.usage.summary(start_date=start_seconds, end_date=end_seconds)
records = client.usage.list(
start_date=start_seconds,
end_date=end_seconds,
page=1,
page_size=20,
)
trend = client.usage.trend(
start_date=start_seconds,
end_date=end_seconds,
granularity="day",
)
Observability
overview = client.observability.cluster_overview(1)
services = client.observability.services(cluster_id=1)
snapshot = client.observability.service_snapshot(42)
series = client.observability.service_timeseries(
42,
range_hours=1,
max_points=120,
)
Detailed API Reference
Use the expandable Python SDK section in the left navigation. Every public method has a dedicated module page with its purpose, request parameters or body, response shape, examples, and operational constraints.