Python SDK reference
All np shell commands have a corresponding Python command.
from netspresso.api import NetsPresso
client = NetsPresso()
client.projects.create(name="resnet50-rpi5", model="resnet50", device="raspberry_pi_5_16gb",
runtime="executorch", backend="xnnpack")
The CLI and SDK provide the same 31 operations independently, using the same underlying functionality. Automated tests make sure they stay in sync in both directions.
This section documents the Python side: signatures, parameter types, return values, and the instances where the Python call deliberately behaves differently from the shell command. For what each command means in terms of pipeline concepts, run.yaml semantics, and artifact levels, see the CLI reference.
Command summary
Auth and environment
| CLI | Python | Description |
|---|---|---|
np health ready | client.health.ready() | Check backend connectivity |
np auth login, logout, status | client.auth.login(), .logout(), .status() | Log in, log out, and check authentication |
np workspace init | client.workspace.init(path) | Initialize workspace |
Resource management
| CLI | Python | Description |
|---|---|---|
np projects create, ls, info | client.projects.create(), .list(), .get() | Create, list, or inspect projects |
np models ls, register | client.models.list(), .register() | List preset models or register a model |
np datasets ls, register | client.datasets.list(), .register() | List or register datasets |
np devices ls | client.devices.list() | List supported device, runtime, and backend combinations |
np experiments create, detail, ls, info | client.experiments.create(), .generate_detail_config(), .list(), .get() | Create, detail, list, or inspect experiments |
np jobs ls, info | client.jobs.list(), .get() | List or inspect jobs within an experiment |
Execution
| CLI | Python | Description |
|---|---|---|
np validate | client.validate() | Validate configuration before running |
np run | client.run() | Execute the optimization pipeline |
np cancel | client.cancel() | Cancel an experiment and any in-progress post-hoc measurements |
Viewing results
| CLI | Python | Description |
|---|---|---|
np status | client.status() | Check run status |
np report | client.report() | Collect the results report |
np export | client.export() | Export artifacts |
np evaluate | client.evaluate() | Post-hoc accuracy evaluation |
np profile | client.profile() | Measure latency and memory |
np analyze sensitivity | client.analyze.sensitivity() | Module-wise sensitivity lookup |
np visualize, auth | client.visualize.create_link(), .authorize() | Generate a visualization link or bootstrap browser session access |
np ls --post-hoc | client.list_post_hoc(id) | List post-hoc run history |
np experiments detail is client.experiments.generate_detail_config(), not .detail(). The command does not read anything. It asks the backend for a DETAIL run-config and writes run.detail.yaml. Naming it get would have pointed at the wrong verb. client.experiments.get() is the read.
Creating a client
from netspresso.api import NetsPresso
client = NetsPresso()
The constructor takes no required arguments. The address and token come from the same sources the CLI uses, that is, the environment variables in Quick Start and the credential stored by a previous np auth login.
You can override both:
staging = NetsPresso(endpoint="https://api.staging.example", token="<access-token>")
| Parameter | Type | Required | Description |
|---|---|---|---|
endpoint | str | None | No | Backend API address. Overrides NP_CLIENT_API_URL for calls made through this client |
token | str | None | No | Access token. Overrides the stored credential for calls made through this client |
Both are keyword-only. If a field is ever inserted before them, a positional call would silently bind to the wrong slot. Keyword-only makes that impossible.
The override is scoped to calls that go through that object, so two clients pointing at different environments can be used alternately in one process without leaking into each other.
The stored credential refreshes itself when it expires. A token you pass to the constructor does not. When it expires, the backend answers 401 and you get an AuthenticationError. Catch the error and build a new client with a fresh token.
What replaces the CLI global options
Nothing was dropped. 3 groups of options simply take a different shape in a library.
--json / --plain → the return value
Every call returns a typed object. There is no formatting flag because there is nothing to format. You get fields, and serialize them yourself if you need to.
report = client.report(experiment=experiment_id)
report.step_results # attribute access, with IDE completion and mypy
report.model_dump() # what --json produced
-v / --quiet / --debug → standard logging
A library asking for its own log level as a call argument is not a Python convention. Logs go to 2 channels, both already created and wired with a handler by the time netspresso finishes importing — no basicConfig() needed to make these lines work.
| Channel | Carries | Default |
|---|---|---|
np.product | Human-readable progress | INFO |
np.develop | Internal detail | off (CRITICAL+1) |
| CLI | Python |
|---|---|
-v | logging.getLogger("np.develop").setLevel(logging.DEBUG) |
--quiet / -q | logging.getLogger("np.product").setLevel(logging.ERROR) |
-vv / --debug | the above, plus logging.basicConfig(level=logging.DEBUG) for third-party logs |
--debug also makes the CLI print tracebacks. Python needs no switch for that: exceptions propagate to the caller already.
--version → importlib.metadata
from importlib.metadata import version
version("netspresso")
4 rules that differ from the shell
These are the differences that change how you write the call. They are consistent across all 31 functions.
- Nothing is ever prompted. The CLI fills missing options with a TTY prompt. A library must not touch stdin, so a missing required option raises
ValueErrorand says so:
client.projects.create(model="resnet50", device="raspberry_pi_5_16gb")
# ValueError: Missing required options: name.
# The Python surface never prompts — pass them explicitly.
-
Nothing is detected from the current directory.
np statuswalks up from the working directory to find an experiment.client.status()does not. In this case the answer would depend on where the interpreter happened to start. Pass the selector. -
IDs and paths are both accepted where the CLI accepts both. Parameters typed
str | Pathtake a reference ID (looked up throughrefs.json) or a local experiment directory used without modification. -
Confirmation prompts become an explicit flag.
np cancelasks before it stops anything.client.cancel()cannot ask, so it refuses unless you sayyes=True.
client.cancel(experiment_id) # ValueError — nothing was cancelled
client.cancel(experiment_id, yes=True) # cancels
Errors
Failures raise. The hierarchy is shaped so you can catch errors by what you should do about them, not by which command you called.
from netspresso.api import (
NetsPressoError, # base — catches everything below
AuthenticationError, # 401 — re-authenticate
PermissionDeniedError, # 403
NotFoundError, # 404
ConflictError, # 409 — state is wrong for this action
InvalidRequestError, # 400 / 422 — the request itself is wrong
TransientError, # 503 / 504 — the only family worth retrying
InternalError, # 500 and friends
)
Every exception carries code, message, and hint. code is the same NP-{svc}{mod}-{http} value the CLI prints, so an error report from either surface points at the same place.
try:
client.run(experiment_id)
except TransientError:
... # retry
except NetsPressoError as e:
print(e.code, e.message, e.hint)
raise
client.validate() returns a result whose is_valid might be False. Check result.is_valid and read result.violations.
ValueError is raised for argument combinations that cannot be sent at all (a missing required option, dry_run together with retry). It sits outside the NetsPressoError hierarchy on purpose: nothing reached the backend, so no error code exists.
Command details
Auth and environment
client.health.ready()
CLI equivalent: np health ready
Checks that the backend and the services it depends on are up. It checks reachability independently of authentication, so it works before client.auth.login(). Call this first when you are bringing up a new environment. The backend URL comes from the NP_CLIENT_API_URL or NP_ENTERPRISE_API_URL environment variable. The call reports an error if that variable is not set.
health = client.health.ready()
print(health.model_dump())
Takes no arguments.
Returns HealthCheckResult — the readiness of the backend and its dependent services.
client.auth.login()
CLI equivalent: np auth login
Stores the credential issued by the portal on the local machine, where every later call picks it up.
import os
client.auth.login(os.environ["NP_CREDENTIAL"])
| Parameter | Type | Required | Description |
|---|---|---|---|
credential | str | Yes | The credential string issued by the portal (a base64 bundle) |
Returns AuthLoginResult — the outcome of storing the credential.
The CLI takes the credential as an argument too, not from a prompt, so neither surface ever reads stdin. Load it from an environment variable or a secret store and pass it in explicitly. If a prompt ever appeared inside a library, the call would simply stop there and wait. A string that is not a valid credential raises InvalidRequestError: Invalid credential: invalid credential encoding.
client.auth.logout()
CLI equivalent: np auth logout
Removes the credential stored on this machine (the CLI keeps it at ~/.config/np/auth.json). A stored credential is required before every other command, so calls after this one fail until you log in again.
client.auth.logout()
Takes no arguments.
Returns AuthLogoutResult — the outcome of removing the stored credential.
client.auth.status()
CLI equivalent: np auth status
Reports the login state recorded on this machine, that is, the current login and its expiry.
status = client.auth.status()
print(status.model_dump())
Takes no arguments.
Returns AuthStatusResult — the login state recorded on this machine.
This call reads the stored credential and does not contact the server. It tells you what is on disk, not whether the backend will accept it. Use client.health.ready() for reachability. An expired access token reported here is also not a reason to log in again: auth.status() has been observed reporting access_status=EXPIRED while later calls still succeeded, because the stored refresh token renews the access token in the background.
client.workspace.init()
CLI equivalent: np workspace init
Creates a workspace under path and registers it with the backend. See np workspace init for what the workspace contains and why it is created only once.
from pathlib import Path
result = client.workspace.init(Path("/home/user/np"))
print(result.model_dump())
| Parameter | Type | Required | Description |
|---|---|---|---|
path | Path | Yes | The parent directory to create the workspace in |
Returns WorkspaceInitResult — the outcome of creating the workspace and registering it with the backend.
path is required here, unlike the CLIThe CLI defaults this to Path.cwd(). The Python surface has no default, and because path is a required positional parameter, omitting it raises TypeError. That is deliberate — a library that leans on the current working directory puts the workspace wherever the caller happened to be running, and every command afterwards follows that location.
Projects, models, datasets, and devices
client.projects.create()
CLI equivalent: np projects create
Creates a project that binds a model to a target device, in the current workspace.
project = client.projects.create(
name="resnet-rpi5",
model="resnet50_pt2_pytorch_static_fp32",
device="raspberry_pi_5_16gb",
runtime="executorch",
backend="xnnpack",
)
# reuse an existing project's settings, overriding only the device
client.projects.create(
name="resnet-m4",
copy_config="resnet-rpi5",
device="m4_macbook",
)
| Parameter | Type | Required | Description |
|---|---|---|---|
name | str | None | No | Project name, 3–50 characters, alphanumeric plus hyphens and underscores. Default: None |
model | str | None | No | A model name from client.models.list() (exact match) or a Hugging Face repo_id. Default: None |
device | str | None | No | Target device, for example raspberry_pi_5_16gb. Default: None |
runtime | str | None | No | Target runtime, for example executorch. Default: None |
backend | str | None | No | Target backend, for example xnnpack. Default: None |
gpu_device | str | None | No | Compute device the run executes on — cpu, cuda:N, or auto. Omitting it means cpu. Default: None |
calibration_dataset | str | None | No | Calibration dataset name (exact match). Default: None |
evaluation_dataset | str | None | No | Evaluation dataset name (exact match). Default: None |
copy_config | str | None | No | Name or reference ID of a source project whose settings fill in only the arguments you left out. Anything you pass explicitly always wins. Default: None |
Returns ProjectCreateResult — the project that was created.
The CLI resolves gpu_device="auto" for AQ-only feature profiles, but that resolver lives in the CLI layer. The Python surface passes what you give it through unchanged.
None default does not mean optionalEvery parameter here is keyword-only and defaults to None, but that only describes the signature. Calling client.projects.create() with no name raises:
ValueError: Missing required options: name. The Python surface never prompts — pass them explicitly.
np projects create lets you omit any option and fills the gap interactively, and it marks --model, --device, --runtime, and --backend required. Pass those explicitly here, or bring them in from an existing project with copy_config.
client.projects.get()
CLI equivalent: np projects info
Fetches the detail record for a single project.
project = client.projects.get("resnet-rpi5") # by name
project = client.projects.get(project_id) # by ref id (UUID)
| Parameter | Type | Required | Description |
|---|---|---|---|
project_id | str | Yes | Project reference ID (UUID) or name. A UUID is used without modification with no lookup; a name goes through a backend keyword search and is then matched exactly |
Returns ProjectDetailView — the detail record for one project.
Unlike the CLI, you cannot omit the selector. If the library searched parent directories to pick a target, the same call would silently resolve differently depending on where it ran.
client.projects.list()
CLI equivalent: np projects ls
Lists the projects in the current workspace.
projects = client.projects.list(
filters=["target-device=raspberry"],
sort=["created:desc", "name"],
)
| Parameter | Type | Required | Description |
|---|---|---|---|
sort | Sequence[str] | None | No | field[:asc|desc] entries. Earlier entries take priority. Fields: created, name. Omitting it orders by created:desc. Default: None |
filters | Sequence[str] | None | No | key=value entries, case-insensitive partial match. If you specify a key more than once, the last value you specify is used. Keys: base-model, name, status, target-device. Default: None |
Returns list[ProjectView] — one entry for each project in the workspace.
client.models.list()
CLI equivalent: np models ls
Lists the models registered on the server.
for model in client.models.list(filters=["domain=LLM"], sort=["name"]):
print(model.alias, model.upload_status)
| Parameter | Type | Required | Description |
|---|---|---|---|
sort | Sequence[str] | None | No | "name" or "name:desc" entries. The order in which you pass them is the sort priority. Fields: name, type, task, domain, framework, source, format. Default: None. |
filters | Sequence[str] | None | No | "type=PRESET" entries. If you specify a key more than once, the last value you specify is used. Keys: name, type, domain, framework. Default: None |
Returns list[Model] — one entry for each registered model.
You can sort on more fields than you can filter on: task, source, and format are sortable but not filterable.
client.models.register()
CLI equivalent: np models register
Registers a model from a model_register.yaml recipe and transfers its weights. See model_register.yaml for the recipe schema and Bring Your Own Model (BYOM) and Bring Your Own Data (BYOD) formats for the registration lifecycle.
from pathlib import Path
result = client.models.register(Path("recipes/qwen.yaml"), wait=True)
print(result.model_dump())
| Parameter | Type | Required | Description |
|---|---|---|---|
recipe | Path | Yes | Path to the recipe file |
force_update | bool | No | Re-register an existing card against the latest Hugging Face revision. Default: False |
wait | bool | No | Poll until server-side verification reaches a terminal state (UPLOADED or FAILED). Default: False |
Returns ModelUploadResult — the outcome of registering the card and uploading its weights.
The CLI picks up model_register.yaml from the current directory when you omit the path; the Python surface requires the path, so which model gets registered never depends on where the call ran. With wait=False the call returns as soon as the upload is confirmed and verification continues in the background — check upload_status on the entries from client.models.list() before using the model.
A recipe path that cannot be read is rejected:
InvalidRequestError: Failed to read <path>
force_update re-transfers the weightsIt issues a new reference ID, downloads the weights from Hugging Face again and re-uploads them — several GB of traffic for each call, not a metadata-only refresh. The previous version is retired from listings rather than overwritten in place.
client.datasets.list()
CLI equivalent: np datasets ls
Lists the datasets available in the workspace.
calib = client.datasets.list(filters=["split=CALIBRATION"], sort=["samples:desc"])
# same as filters=["raw-data=plain_text"], including rejected uploads
client.datasets.list(raw_data="plain_text", show_all=True)
| Parameter | Type | Required | Description |
|---|---|---|---|
sort | Sequence[str] | None | No | "samples:desc" entries; the order you pass them is the sort priority. Fields: name, type, task, split, raw-data, samples, seed, strategy, shuffle. Default: None |
filters | Sequence[str] | None | No | "type=PRESET" entries. Keys: name, type, split, raw-data. Default: None |
raw_data | str | None | No | Shorthand for filters=["raw-data=<fingerprint>"]. If both are given this one wins — the same precedence the CLI gives --raw-data. Default: None |
show_all | bool | No | Include uploads that were rejected in validation and aren't usable yet. The default list carries only the usable ones. Default: False |
Returns list[SampleDataset] — one entry for each dataset.
client.datasets.register()
CLI equivalent: np datasets register
Archives a local folder into a tar, uploads it, and registers it with the backend. This command runs synchronously. Upload, validation, and the pass or fail verdict all happen in the same call, with no job to poll for. See Bring Your Own Model (BYOM) and Bring Your Own Data (BYOD) formats for the folder layout that each raw_data format expects.
from pathlib import Path
result = client.datasets.register(
Path("./my_corpus_calib_30"),
"plain_text",
calibration=True,
task=["TEXT_GENERATION"],
name="doc-capture-calib-30",
)
| Parameter | Type | Required | Description |
|---|---|---|---|
source | Path | Yes | The local folder to register |
raw_data | str | Yes | Name of the raw-data format the folder follows, for example "imagenet_folder", "coco2017" |
calibration | bool | No | Register the data as calibration data. Default: False. |
evaluation | bool | No | Register the data as evaluation data. Default: False. |
task | Sequence[str] | None | No | Task codes the data will be used for — at least one, several allowed. All of them must pass validation for the registration to succeed. Default: None. |
name | str | None | No | Display name. Derived from the source folder name if omitted. Default: None. |
Returns DatasetRegisterResult — the outcome of the registration.
calibration and evaluation are mutually exclusive purpose flags in the CLI, and it requires one of them. For object detection and instance or semantic segmentation, calibration data is checked more leniently (images only) than evaluation data, which needs matching ground truth. The client converts task codes to uppercase with underscores. For example, "text-generation" and "TEXT_GENERATION" are treated as the same value. The server checks the format name and its compatibility with each task before anything uploads.
task defaults to None but is not optionalRegistering without one raises:
ValueError: At least one task is required. Pass the task the data is for, and pass several for a dataset that serves more than one.
client.devices.list()
CLI equivalent: np devices ls
Lists the device, runtime, and backend combinations the backend supports.
for entry in client.devices.list(filters=["backend=xnnpack"], sort="device"):
print(entry.model_dump())
| Parameter | Type | Required | Description |
|---|---|---|---|
sort | str | None | No | A single sort field — "device", "runtime", or "backend". Like the CLI it takes no direction suffix; ordering is always ascending. Omitting it gives the same device → runtime → backend order the listing displays. Default: None |
filters | Sequence[str] | None | No | "device=raspberry" entries, case-insensitive partial match. Same 3 keys as sort. If you specify the same key more than once, the last value you specify is used. Default: None |
Returns list[Device] — one entry for each supported device, runtime, and backend combination.
Note that sort here is a single string, not a Sequence[str] like the other list calls on this page.
Experiments and jobs
client.experiments.create()
CLI equivalent: np experiments create
Creates an experiment directory under a project and downloads its run.yaml.
from pathlib import Path
exp = client.experiments.create(
project_id,
name="resnet-rpi5-gq",
steps="go,gq",
)
print(exp.model_dump())
# a local project directory works as the selector too
exp = client.experiments.create(Path("workspace/resnet-rpi5"), steps=["aq", "go", "gq"])
| Parameter | Type | Required | Description |
|---|---|---|---|
project | str | Path | Yes | Target project. A str is a reference ID. A Path is a local project directory. Unlike in the CLI, you cannot omit this parameter. |
name | str | None | No | Experiment name. Left out, the backend assigns experiment_NN (experiment_00, experiment_01, ...). Default: None. |
input_job | str | None | No | Create a derivation experiment that takes this job's output as its input. Default: None. |
steps | Sequence[str] | str | None | No | Pipeline steps. Accepts both the shorthands (aq = Advanced Quantize, go = Graph Optimize, gq = Graph Quantize) and the canonical names, either as a sequence or as a comma-separated string such as "go,gq" — the same notation as the CLI's -s. Left out, the backend picks the steps from the model's modality. Default: None. |
copy_config | str | None | No | Name or reference ID of another experiment in the same project whose run.yaml (and run.detail.yaml, if present) recipe should be copied. Default: None. |
Returns ExperimentCreateResult — the created experiment and where its run.yaml was written.
Passing copy_config alone copies the recipe without modification. Passing it together with steps regenerates a structurally correct recipe for the new step set and then overlays the matching tuning values by name.
When you combine copy_config and steps, anything from the source recipe that does not carry over is dropped and reported as a logging WARNING. Nothing is raised, so the call still succeeds with less tuning than the source had.
client.experiments.generate_detail_config()
CLI equivalent: np experiments detail
Generates a run.detail.yaml and downloads it into the experiment directory.
result = client.experiments.generate_detail_config(experiment_id)
print(result.model_dump())
| Parameter | Type | Required | Description |
|---|---|---|---|
experiment | str | Path | Yes | Target experiment. A str is a reference ID. A Path is a local experiment directory. Unlike in the CLI, you cannot omit this parameter. |
Returns RunConfigDownloadResult — the outcome of generating the DETAIL configuration.
The template is generated from the experiment's current run.yaml. See run.detail.yaml for the file's own reference.
client.experiments.get()
CLI equivalent: np experiments info
Fetches a single experiment.
exp = client.experiments.get("4d5e6f70-8192-4b3c-8d4e-5f6071829314")
# by name — scoped to a project
exp = client.experiments.get("experiment_00", project=project_id)
print(exp.model_dump())
| Parameter | Type | Required | Description |
|---|---|---|---|
experiment_id | str | Path | Yes | A reference ID (UUID), an experiment name, or a local experiment directory (Path). Unlike in the CLI, you cannot omit this parameter. |
project | str | Path | None | No | Scope for name lookup. A str is a reference ID. A Path is a local project directory. Default: None. |
Returns ExperimentView — the experiment's record.
Experiment names are unique only within a project. The CLI resolved that by searching parent directories to find the enclosing project, but in the SDK, project takes that job. It is ignored when experiment_id is a reference ID or a Path, but a bare name without it has nothing to resolve against.
client.experiments.list()
CLI equivalent: np experiments ls
Lists the experiments under a project.
experiments = client.experiments.list(
project_id,
sort=["status", "created:desc"],
filters={"status": "success"},
)
for exp in experiments:
print(exp.model_dump())
| Parameter | Type | Required | Description |
|---|---|---|---|
project | str | Path | Yes | Target project. A str is a reference ID; a Path is a local project directory. Unlike in the CLI, you cannot omit this parameter. |
sort | Sequence[str] | str | None | No | "field" or "field:asc|desc", ascending by default. Several entries sort by the order you pass them. Fields: name, status, created, updated. Default: None. |
filters | Mapping[str, str] | None | No | {key: value} partial match, case-insensitive. Keys: name, status. Default: None. |
Returns list[ExperimentView] — one entry for each experiment.
Listing never touches local folders, so the project ref does not have to be bound to this machine. filters is the dict form of the CLI's repeatable --filter key=value. A dict already expresses "the last value for a key wins" in its own notation. There is no page-size argument — page on the client side if you need to.
client.jobs.list()
CLI equivalent: np jobs ls
Lists the jobs inside an experiment.
jobs = client.jobs.list(
experiment_id,
filters=["step-type=graph_quantization"],
sort=["started:desc"],
)
for job in jobs:
print(job.model_dump())
| Parameter | Type | Required | Description |
|---|---|---|---|
experiment | str | Path | Yes | Target experiment. A str is a reference ID, and a Path is a local experiment directory. Unlike in the CLI, you cannot omit this parameter. A library that falls back to the current directory would silently change targets depending on where it is called from. |
sort | Sequence[str] | None | No | field[:asc|desc] entries, ascending by default. Earlier entries take priority. Fields: name, started, status, step-index. Omitted, it sorts by ["step-index:asc"]. Default: None. |
filters | Sequence[str] | None | No | key=value entries, case-insensitive partial match. A key specified more than once keeps the last value. Keys: name, status, step-type. Default: None. |
Returns list[JobView] — one entry for each job.
An experiment that has not produced any jobs yet returns []. This is expected behavior. The listing carries no metric values. See the CLI reference for more details.
filters is not a dict herejobs.list() takes key=value strings, but experiments.list() takes a Mapping. The 2 are not interchangeable.
client.jobs.get()
CLI equivalent: np jobs info
Fetches a single job.
job = client.jobs.get("f1a2b3c4-5d6e-4f70-8912-a3b4c5d6e7f8")
print(job.model_dump())
| Parameter | Type | Required | Description |
|---|---|---|---|
job_id | str | Yes | Job reference ID (UUID, full form), as returned by jobs.list() or experiments.get() |
Returns JobView — the job's record.
Only a full UUID resolves here. Name resolution is not supported, because job names are unique only within an experiment. This is also the only way to see the metrics of a post-hoc evaluate or profile job, because report() does not cover post-hoc results. See np jobs info for the contents of that metrics table.
Execution
client.validate()
CLI equivalent: np validate
Validates an experiment's run.yaml (and optionally run.detail.yaml) against the backend before you submit a pipeline.
result = client.validate("experiment_00")
if not result.is_valid:
for violation in result.violations:
print(violation)
| Parameter | Type | Required | Description |
|---|---|---|---|
experiment | str | Path | Yes | The target. A str is an experiment reference ID, a Path is a local experiment directory. Unlike in the CLI, you cannot omit this parameter. A library that falls back to the current working directory would silently validate a different experiment depending on where it was called from. |
config | str | Path | Sequence[str | Path] | None | No | YAML file or files to validate in place of the ones in the experiment directory, for example "run.yaml" or ["run.yaml", "run.detail.yaml"]. Default: None. |
Returns ValidateResult — the backend's verdict on the experiment's configuration.
result = client.validate(
"experiment_00",
config=["run.yaml", "run.detail.yaml"],
)
The CLI takes these as 1 comma-separated string (--config a.yaml,b.yaml) only because a shell cannot pass a list. In Python it is a sequence, and a single path needs no wrapping.
Passing a str reference ID does not make this a purely remote call. The files being validated are read locally, and the reference ID resolves back to a directory only for experiments registered in refs.json. Passing config does not change this. Backend validation is experiment-scoped, so the experiment context is required regardless of which YAML files you point at.
client.run()
CLI equivalent: np run
Submits an experiment's optimization pipeline to the backend, where it executes asynchronously on a GPU worker.
result = client.run("experiment_00")
print(result.model_dump())
# preview the plan without creating any job
plan = client.run("experiment_00", dry_run=True)
| Parameter | Type | Required | Description |
|---|---|---|---|
experiment | str | Path | Yes | The target. A str is an experiment reference ID, and a Path is a local experiment directory. Unlike in the CLI, you cannot omit this parameter. A library that falls back to the current working directory would silently run a different experiment depending on where it was called from. |
dry_run | bool | No | Compute the execution plan without executing anything. Default: False. |
wait | bool | No | Poll until the pipeline finishes. Default: False — submit and return immediately (detached). |
retry | bool | No | Roll back a failed or cancelled execution and resubmit it, reusing the steps that already succeeded. Default: False. |
profile_method | ProfileMethod | str | None | No | Measurement method for the inline profile. Default: None. |
Returns RunExecuteResult — the submitted execution.
result = client.run("experiment_00", wait=True)
dry_run=True creates nothingIt computes the execution plan only. No job is created, so a client.jobs.list() right after it returns the same empty list as before.
profile_method unset unless you mean itNone sends nothing at all, rather than sending a default of the client's choosing. The backend then picks the method appropriate to its own deployment type. Pass a value only when you specifically need to override that.
client.cancel()
CLI equivalent: np cancel
Cancels a running or queued experiment, along with any in-progress post-hoc evaluate or profile measurements under it.
result = client.cancel("experiment_00", yes=True)
| Parameter | Type | Required | Description |
|---|---|---|---|
experiment | str | Path | Yes | The target. A str is an experiment reference ID, and a Path is a local experiment directory. Unlike in the CLI, you cannot omit this parameter. An irreversible operation should not take its target from wherever the call was made. |
yes | bool | No | Confirm the cancellation. Default: False. |
Returns CancelResult — the outcome of the cancellation request.
yes=True is not optional in practiceyes occupies the same slot as the CLI's --yes, but the Python surface has no confirmation prompt to skip, so this argument is the confirmation. With yes=False (the default), nothing is stopped and the call raises ValueError. See np cancel for what cancellation does and does not interrupt on the backend.
Status and retrieval
client.status()
CLI equivalent: np status
Queries execution state on either axis: the run axis (experiment, project, job), or the measurement axis (a single evaluate, profile, or analyze round, or an experiment's rounds of 1 kind).
run_status = client.status(experiment=experiment_id)
job_status = client.status(job=job_id)
# one measurement round, with its execution log
eval_status = client.status(evaluate=measurement_ref, logs=True)
# every evaluate round recorded under an experiment
rounds = client.status(experiment=experiment_id, kind="evaluate")
| Parameter | Type | Required | Description |
|---|---|---|---|
experiment | str | Path | None | No | Experiment reference ID, or a local experiment directory. Paired with kind, it lists that experiment's measurement rounds instead. Default: None. |
project | str | Path | None | No | Project reference ID, or a local project directory. Default: None. |
job | str | None | No | Job reference ID. Jobs have no local directory counterpart, so only str is accepted. Default: None. |
evaluate | str | None | No | Reference ID of one evaluate measurement round. Returns its status and metrics. Default: None. |
profile | str | None | No | Reference ID of one profile measurement round. Default: None. |
analyze | str | None | No | Reference ID of one analyze measurement round (evaluation + analyze). Default: None. |
kind | MeasurementKind | str | None | No | Used with experiment to list that experiment's rounds of 1 kind ("evaluate" | "profile" | "analyze"). Default: None. |
logs | bool | No | Include the execution log (execution.log) in the result. Default: False. |
Returns StatusQueryResult | MeasurementStatusResult — a run-axis query returns the former, a measurement query the latter.
logs has nothing to attach to in a list querylogs=True applies to the experiment scope and to a single measurement round addressed by evaluate, profile, or analyze. In a list query (experiment + kind) there is no single round to read a log from, so it stays unfilled.
client.report()
CLI equivalent: np report
Collects optimization results for 1 experiment or 1 project. sort and filter shape the pipeline table, and detail and output control how far the result is expanded. Anything measured afterwards with np evaluate or np profile is reported per execution rather than merged into the pipeline table.
report = client.report(experiment=experiment_id)
ranked = client.report(
project=project_id,
sort="latency:asc,snr:desc",
filter="snr >= 30, latency <= 100",
detail=True,
)
| Parameter | Type | Required | Description |
|---|---|---|---|
project | str | Path | None | No | Project reference ID, or a local project directory. Default: None. |
experiment | str | Path | None | No | Experiment reference ID, or a local experiment directory. Mutually exclusive with project. Default: None. |
sort | str | None | No | Sort key or keys for the pipeline table — "latency", "snr:desc", "latency:asc,snr:desc". Post-hoc rounds keep submission order regardless. Default: None. |
filter | str | None | No | Filter expression for the pipeline table — "snr > 30", "snr >= 30, latency <= 100". Default: None. |
detail | bool | No | Expand 1 level further: step rows under each experiment at project level, component rows under each step at experiment level. Default: False. |
output | bool | No | Request output-level detail (per-output-tensor SNR, metric sub-scores). Experiment level only. Default: False. |
Returns ReportCollectResult — the optimization results collected for the requested scope.
project or experiment is requiredThe 2 selectors are mutually exclusive, and one of them must be given.
output=True only asserts that you are at experiment level. Per-output values are included in the returned object regardless, so Python callers who are not rendering a table rarely need it. The CLI, which does render tables, raises a usage error for --output at project level.
client.export()
CLI equivalent: np export
Downloads an experiment's artifacts from the backend and unpacks them into the local experiment directory. See np export for what each level contains.
result = client.export(experiment_id)
full = client.export(experiment_id, level="full")
etrec = client.export(experiment_id, level="archive", generate_etrecord=True)
| Parameter | Type | Required | Description |
|---|---|---|---|
experiment | str | Path | Yes | Target experiment: a reference ID (str) or a local experiment directory (Path) |
level | ExportLevel | str | No | How much to include (deploy ⊂ config ⊂ result ⊂ full ⊂ archive). Plain strings are accepted. Default: ExportLevel.DEPLOY. |
generate_etrecord | bool | No | Also generate an ExecuTorch ETRecord. Default: False |
ethosu_system_config | str | None | No | Ethos-U system configuration (Ethos-U targets only). Default: None. |
ethosu_memory_mode | str | None | No | Ethos-U memory mode (Ethos-U targets only). Default: None. |
ethosu_target | str | None | No | Ethos-U target (Ethos-U targets only). Default: None. |
Returns ExportSubmitResult — the outcome of the export request.
experiment is positional and required hereUnlike in the CLI, you cannot omit this parameter. A library call that fell back to the current working directory would silently export a different experiment depending on where the process happened to start.
Exporting an experiment that is not exportable raises ConflictError: Backend error (HTTP 409, code=CONFLICT): Experiment is not exportable.
Post-hoc measurement
client.evaluate()
CLI equivalent: np evaluate
Runs post-hoc accuracy evaluation on the backend for one or more completed models, jobs, or experiments.
result = client.evaluate(
job_id,
metrics="ACCURACY",
dataset="imagenet1k_eval_seed42_n5000",
wait=True,
)
# fan out over several targets, with a parameterized metric
result = client.evaluate(
[job_id, "02_graph_quantize"],
metrics=[MetricSpec(name="MAP", params={"iou_threshold": 0.5}), "SNR"],
dataset=detection_dataset,
source="input_model",
force=True,
)
| Parameter | Type | Required | Description |
|---|---|---|---|
ids | str | Sequence[str] | Yes | The target or targets to evaluate — job_id, model_id, model_name, or experiment_id. A single string evaluates just that target. A sequence fans out into 1 request for each target. |
metrics | str | Sequence[str | MetricSpec] | Yes | Metrics to compute. A plain string is a metric that takes no parameters ("ACCURACY"). Parameterized ones are given as MetricSpec(name="MAP", params={"iou_threshold": 0.5}). At least 1 is required, the same rule the CLI enforces. As with ids, a single metric does not have to be wrapped in a sequence. |
dataset | str | None | No | Dataset ref_id or name — what the accuracy-family metrics are scored against |
source | str | None | No | Source model ref_id or name used as the baseline for the comparison metrics (SNR, MSE, MAE). The CLI uses input_model when it is not given. |
force | bool | No | Ignore the server-side cache and evaluate again. Default: False. |
all_candidates | bool | No | For an experiment_id target, evaluate every candidate instead of only the best one. Default: False. |
as_type | str | None | No | Force how the ID is interpreted — "model", "job", or "experiment" |
gpu | str | None | No | GPU to measure on, for example "cuda:0" |
num_samples | int | None | No | Number of samples to evaluate on. Omit to use the whole dataset. Default: None. |
wait | bool | No | Poll until the evaluation reaches a terminal state. Default: False — the call returns as soon as the work is submitted (detached). |
project | str | Path | None | No | Project that should hold base or uploaded models, that is, targets with no owning experiment. A str is a project reference ID, and a Path is a local project directory. Ignored when no target needs it. |
Returns EvaluateResult — the evaluation submitted for each requested target.
Cache behaviour is unchanged from the CLI: the same (artifact, dataset, metric, source) combination returns the cached result without re-running unless you pass force=True. See np evaluate.
An ID that matches nothing raises NotFoundError: Id not found as job, model, or experiment: <id>.
client.profile()
CLI equivalent: np profile
Measures latency and memory of one or more targets on the backend.
result = client.profile(job_id, method="mlia", device="raspberry_pi_5_16gb", wait=True)
# every target of a local experiment directory
result = client.profile(Path("./experiment_00"))
# every candidate of an experiment, not just the best
result = client.profile(experiment_id, all_candidates=True, wait=True)
| Parameter | Type | Required | Description |
|---|---|---|---|
ids | str | Path | Sequence[str] | Yes | Targets to measure. A str (or a sequence of them) is a job_id, model_id, model_name, or experiment_id. A Path measures every target of that experiment directory. |
method | ProfileMethod | str | None | No | Measurement method — "dummy", "on-device", or "mlia". Omit to let the backend choose. |
device | str | None | No | Device alias to measure on, for example "raspberry_pi_5_16gb". Inherits the project's configured device when omitted. |
runtime | str | None | No | Runtime that narrows device down when the same alias exists under several combinations |
backend | str | None | No | Runtime backend that narrows device down, together with runtime |
component | str | None | No | Measure only this component of a multi-component model (prefill, decode, …). Omit to measure all of them. |
profile_opt | Mapping[str, int] | None | No | Measurement parameter overrides, for example {"num_executions": 5}. Only meaningful with method="on-device". The engine decides which keys are valid. |
force | bool | No | Measure again instead of reusing a past measurement. Default: False. |
all_candidates | bool | No | For an experiment_id target, profile every candidate instead of only the best one. Default: False. |
as_type | str | None | No | Force how the ID is interpreted — "model", "job", or "experiment" |
wait | bool | No | Poll until the measurement reaches a terminal state. Default: False — the call returns as soon as the work is submitted (detached). |
project | str | Path | None | No | Project that should hold base or uploaded models, that is, targets with no owning experiment. A str is a project reference ID, a Path is a local project directory. The CLI fills this in from the current directory, and the library takes it as an argument. |
Returns ProfileRunResult — the latency and memory measurement submitted for each target.
ids cannot be omittedThe CLI lets you drop the positional IDs and profiles every target of the experiment in the current directory. The library does not: a current working directory scan would make what got measured depend on where the process happened to be started. Pass a Path to an experiment directory to get the same behavior explicitly.
method unset is a real choiceWhen method is omitted nothing is sent for it, rather than a client-side default being filled in. The backend, which knows the target device, decides how to measure. Pass a method only when you specifically want to pin one.
An ID that matches nothing raises NotFoundError: Id not found as job, model, or experiment: <id>.
client.analyze.sensitivity()
CLI equivalent: np analyze sensitivity
Fetches module-wise sensitivity (SNR, KLD, kurtosis, Pctl-Max, MSE) for a job. Values come from the backend layerwise cache; nothing is computed locally.
result = client.analyze.sensitivity(job_id)
# a different axis, on one component of a multi-component model
result = client.analyze.sensitivity(
job_id,
metric="kld",
component="prefill",
threshold=20.0,
wait=True,
)
| Parameter | Type | Required | Description |
|---|---|---|---|
job_id | str | Yes | UUID of the job to analyze |
metric | AnalyzeMetric | str | No | Ranking and summary axis — "snr", "kld", "kurtosis", "pctl_max", or "mse". Unlike in the CLI, "mse" is accepted here, because no table is being rendered. Default: AnalyzeMetric.SNR |
dataset | str | None | No | ref_id of the evaluation dataset the metric was computed against, used to filter the cache lookup |
component | str | None | No | Component of a multi-component model (prefill, decode, …) |
threshold | float | None | No | Cutoff for the WARN/PASS verdict. Opt-in — no verdict is produced without it |
wait | bool | No | Poll until an on-demand recomputation finishes. Default: False — submitted work returns immediately (detached). A cache hit returns right away regardless of this value |
output_format | str | No | "table" or "json". Default: "table" |
Returns SensitivityAnalyzeResult — the module-wise sensitivity for the requested metric.
If the job has layerwise data for more than one component and you did not pass component, the call does not raise. It returns normally — pass component explicitly to pick one.
output_format is a presentation axis inherited from the CLI. The returned object is the same either way — the value is only validated to be one the CLI would accept.
A job ID that does not exist raises NotFoundError: Job not found: <id>.
client.list_post_hoc()
CLI equivalent: np ls --post-hoc
Lists the post-hoc (EVALUATE or PROFILE) run history recorded for a target.
history = client.list_post_hoc(job_id)
if not history.runs:
print("no post-hoc measurements yet")
| Parameter | Type | Required | Description |
|---|---|---|---|
post_hoc | str | Yes | ID of the target to look up — a job_id, model_id, model_name, or experiment_id |
Returns PostHocQueryResult — runs holds the post-hoc executions recorded for the target.
Despite the name, the argument is the thing you measured, not the kind of measurement. Passing "evaluate" raises NotFoundError: Not found as job, model, or experiment: evaluate. Verify the id. A target that simply has no history is not an error: runs comes back as [].
Unlike in the CLI, you cannot omit the ID: the CLI prints its help text when it is missing, while a library call with no target has nothing to do.
Visualization
client.visualize.create_link()
CLI equivalent: np visualize
Issues a model visualization link. Like the CLI, it hands back a URL rather than opening a browser, and the link only renders once a browser session exists — see client.visualize.authorize() below.
result = client.visualize.create_link(experiment_id)
# A specific job's output, one component of a multi-component model
result = client.visualize.create_link(job="job_9f2c1a", component="prefill")
# Baseline vs. target, side by side
result = client.visualize.create_link(compare=("job_9f2c1a", "job_be40d7"))
| Parameter | Type | Required | Description |
|---|---|---|---|
experiment | str | Path | None | No | Target experiment — a reference ID as str, or a local experiment directory as Path. Default: None |
job | str | None | No | Target job reference ID; visualizes the model that job produced. Default: None |
compare | tuple[str, str] | None | No | Compare 2 targets as (baseline, target). Each item is an experiment path or reference ID or a job reference ID. Default: None |
component | str | None | No | Component to render, for multi-component models. Omit to get 1 link for each component. Default: None |
tool | VisualizeTool | str | None | No | Visualization tool, as the enum or its string value. Default: None |
Returns VisualizeResult — the issued visualization link, or 1 link for each component when component was omitted.
One of experiment, job, or compare must be given. The CLI falls back to detecting workspace/project/experiment from the current directory when you pass nothing; the Python surface does not, because a library call whose target depends on the caller's working directory silently visualizes something else depending on where it runs.
There is no single "representative" component for a multi-component model — the right choice differs by model family — so omitting component returns every component's link and leaves the pick to you. Get component names from np jobs info.
Leaving tool unset selects probe; unlike the CLI, no advisory message is emitted about the choice. The per-tool constraints are the same as the CLI's: probe handles all formats and is the only tool that supports compare, while model-explorer is VGF-only, renders a single-graph view, and is unsupported for LLM/AUDIO domain models. See np visualize.
The link is built from the deployment's frontend base URL. With neither NP_ENTERPRISE_FE_URL nor the legacy FE_BASE_URL set in the environment, the call raises InternalError: FE base URL not configured. Set NP_ENTERPRISE_FE_URL (or legacy FE_BASE_URL).
client.visualize.authorize()
CLI equivalent: np visualize auth
Bootstraps browser access to the visualization pages. Those pages need a session cookie; this exchanges your stored credential for a short-lived bootstrap link and token that establishes it.
auth = client.visualize.authorize()
print(auth.model_dump())
Takes no arguments.
Returns VisualizeAuthResult — the short-lived bootstrap link and token for establishing the browser session.
The link and the token are the same value; the token is just the link's ?bootstrap= query parameter, so use whichever is convenient. Their lifetimes and the resulting session's lifetime are described under np visualize auth.
Without one, the call raises AuthenticationError: Auth server error (HTTP 401): License invalid or expired (code NP-CAU-401).