sstudio TypeScript SDK
Async TypeScript client and command-line interface for model lifecycle APIs.
Requirements
- Node.js 18+
- An API key
- An API base URL
Installation
npm install sstudio
Install the CLI globally when needed:
npm install --global sstudio
Quick Start
import { SmartStudioClient } from "sstudio";
const client = new SmartStudioClient({
apiKey: process.env.SSTUDIO_PLATFORM__API_KEY!,
baseUrl: process.env.SSTUDIO_PLATFORM__API_ENDPOINT!,
});
try {
const me = await client.me();
const models = await client.models.list({ page: 1, pageSize: 20 });
console.log(me);
console.log(models);
} finally {
client.close();
}
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
const client = new SmartStudioClient({
apiKey: process.env.SSTUDIO_PLATFORM__API_KEY!,
baseUrl: process.env.SSTUDIO_PLATFORM__API_ENDPOINT!,
timeout: 30,
maxRetries: 2,
defaultHeaders: { "X-Request-Source": "integration" },
});
The optional onTokenRefresh 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 |
|---|---|---|
apiKey | Yes | API key exchanged for a bearer token. |
baseUrl | 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. |
maxRetries | No | Safe-request retry count. Default: 2. |
defaultHeaders | No | Additional string headers for API requests. |
onTokenRefresh | No | Callback invoked when a new bearer token is issued. Store it only in an approved secret store; never log it. |
fetchImpl | No | Custom Fetch implementation for the host runtime. |
baseUrl 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 options
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, await client.deployments.get(42) returns the object inside
data. Methods without a result return null; text endpoints such as
client.deployments.yaml(42) return string. Use client.request(...) only
when the complete envelope is required.
import {
APIError,
AuthenticationError,
ValidationError,
} from "sstudio";
try {
const deployment = await client.deployments.get(42);
} catch (error) {
if (error instanceof AuthenticationError) {
console.error("The API key or session is invalid");
} else if (error instanceof ValidationError) {
console.error(error.errorCode, error.details);
} else if (error instanceof APIError) {
console.error(error.message);
}
}
API exceptions preserve code, message, errorCode, 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.myModels | 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.providerKeys | 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
const me = await client.me();
Clusters
const clusters = await client.clusters.list();
const capacity = await client.clusters.capacity(1);
const history = await client.clusters.timeseries(1, { limit: 60 });
const holds = await client.clusters.holds({ clusterId: 1 });
Models
const models = await client.models.list({ keyword: "Qwen", page: 1, pageSize: 20 });
const model = await client.models.get("qwen3-4b");
const capabilities = await client.models.deploymentCapabilities(
"qwen3-4b",
{ clusterId: "1" },
);
My Models
upload(...) validates model files, hashes the directory, transfers objects,
completes the upload, and returns the registered model asset.
const model = await client.myModels.upload<{ id: number }>("./model", {
name: "example-model",
modelType: "LLM",
});
const page = await client.myModels.list({ page: 1, pageSize: 20 });
const detail = await client.myModels.get(model.id);
await client.myModels.update(model.id, { description: "production candidate" });
Datasets
const dataset = await client.datasets.upload<{ id: number }>("./train.jsonl", {
name: "example-training-dataset",
datasetType: "training",
trainingCategory: "sft-llm",
});
const preview = await client.datasets.preview(dataset.id, { limit: 20 });
const page = await client.datasets.list({ pageNum: 1, pageSize: 20 });
const download = await client.datasets.downloadUrl({
datasetId: dataset.id,
fileIndex: 0,
});
AI Dataset Preparations
const clusters = await client.datasets.preparations.clusterOptions();
const providerKeys = await client.datasets.preparations.availableProviderKeys();
const tasks = await client.datasets.preparations.list({ pageNum: 1, pageSize: 20 });
const task = await client.datasets.preparations.get({ id: 123 });
Create and action payloads are passed as objects matching the API contract:
const preview = await client.datasets.preparations.preview(resourcePreviewRequest);
const task = await client.datasets.preparations.create<{ id: number }>(preparationRequest);
await client.datasets.preparations.generateRules({ id: task.id });
const rulesReady = await client.waitForDatasetPreparation(task.id, {
until: "rules_ready",
});
await client.datasets.preparations.startLabeling(labelingRequest);
const completed = await client.waitForDatasetPreparation(task.id);
Deployments
The workflow automatically selects the Model Profile or model-asset creation route from the request's model reference.
const request = {
name: "example-deployment",
modelSource: "gallery",
modelName: "Qwen3-4B-Instruct-2507-FAST",
backend: "sglang",
servingMode: "standard",
gpuType: "L20",
replicas: 1,
clusterId: 1,
};
const preview = await client.deployments.preview<{ creatable?: boolean }>(request);
if (preview.creatable) {
const deployment = await client.deployments.create<{ id: number }>(request);
const running = await client.waitForDeployment(deployment.id);
}
Lifecycle and diagnostics:
const page = await client.deployments.list({ page: 1, pageSize: 20 });
const events = await client.deployments.events(42);
const renderedYaml = await client.deployments.yaml(42);
await client.deployments.stop(42);
await client.deployments.previewRestart(42);
await 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.
const 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: {},
};
await client.training.preview({ clusterId: 1, resourceSpecId: "resource-spec-id" });
const job = await client.training.create<{ jobId: string }>(request);
const completed = await client.waitForTrainingJob(job.jobId) as {
artifacts: Array<{ artifactId: string }>;
};
const artifact = await client.training.artifactDownloadUrl(
completed.artifacts[0]!.artifactId,
);
Evaluations
const request = {
kind: "benchmark",
modelType: "LLM",
models: [{
type: "service",
msp_api_key_id: "7",
service_id: "42",
}],
dataset: "evaluation-dataset",
maxSamples: 100,
clusterId: 1,
};
await client.evaluations.preview({ clusterId: 1 });
const job = await client.evaluations.create<{ jobId: string }>(request);
await client.waitForEvaluationJob(job.jobId);
const report = await client.evaluations.report(job.jobId);
Jobs
const jobs = await client.jobs.list({
type: "train",
status: "RUNNING",
page: 1,
pageSize: 20,
});
const job = await client.jobs.get("job-id");
const logs = await client.jobs.logs("job-id", { tail: 200 });
await client.waitForJob("job-id");
API Keys
const keys = await client.keys.list();
const created = await client.keys.create<{ id: number }>({
keyValue: "sk-created-by-caller",
description: "automation key",
isActive: true,
});
const keyId = String(created.id);
const plaintext = await client.keys.reveal(keyId);
await client.keys.update(keyId, {
description: "renamed key",
isActive: true,
});
Treat the value returned by reveal(...) as a secret and never log it.
Provider Keys
const providers = await client.providerKeys.providers();
const keys = await client.providerKeys.list();
const created = await client.providerKeys.create<{ id: number }>({
provider: "example-provider",
apiKey: "provider-api-key",
description: "integration credential",
});
const result = await client.providerKeys.test(created.id);
await client.providerKeys.changeStatus(created.id, { status: 1 });
Usage
const startSeconds = 1782864000;
const endSeconds = 1785542400;
const summary = await client.usage.summary({ startDate: startSeconds, endDate: endSeconds });
const records = await client.usage.list({
startDate: startSeconds,
endDate: endSeconds,
page: 1,
pageSize: 20,
});
const trend = await client.usage.trend({
startDate: startSeconds,
endDate: endSeconds,
granularity: "day",
});
Observability
const overview = await client.observability.clusterOverview(1);
const services = await client.observability.services({ clusterId: 1 });
const snapshot = await client.observability.serviceSnapshot(42);
const series = await client.observability.serviceTimeseries(42, {
rangeHours: 1,
maxPoints: 120,
});
Detailed API Reference
Use the expandable TypeScript 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.