embedl_hub.tracking package#

Public API for the Embedl Hub tracking package.

Top-level re-exports#

  • Client — Tracking client for projects and runs.

  • RunStatus — The states a run moves through, for filtering listings.

  • RunType — Built-in run-type categories.

  • TrackingFailureWarning — Warning raised in place of an error when a tracking call fails inside a safe scope.

  • TrackingProjectChangedWarning — Warning raised when adopting a run also moved the client to that run’s project.

  • TrackingUsageError — Raised when a tracking call is itself malformed; never downgraded by safe mode.

class embedl_hub.tracking.Client(api_config: ApiConfig | None = None, *, log_remote_artifacts: bool = True)[source]#

Bases: object

Tracks projects and runs for the Embedl Hub web app.

property active_run: Run#
property api_config: ApiConfig#

Get or create the API config from the resolved settings.

Returns:

The credentials and Hub address this client uses.

Raises:

RuntimeError – If no API key is configured.

property api_key: str#

The API key this client authenticates with.

For handing to something else that has to authenticate. Do not print or log it: a key that reaches a terminal, a notebook output or a CI log has to be treated as compromised. api_key_source answers where the key comes from without exposing it.

Returns:

The key in use.

Raises:

RuntimeError – If no API key is configured.

property api_key_source: Literal['env', 'config', 'default'] | None#

Which layer supplied the API key this client uses.

Names where the key was read from — the environment or the config file — rather than the key itself, so it is safe to print. Use api_key for the value.

Returns:

"env" or "config" for a key this client resolved, or None when no layer supplied one: either nothing is configured, or the client was handed an explicit configuration, which comes from the caller rather than from anywhere on this machine.

property base_url: str#

The address of the Hub this client sends requests to.

Needs no API key: which Hub is configured is not an authentication question, so this answers before auth has been run.

Returns:

The address, always ending in /.

create_run(type: RunType | str, name: str | None = None, parent_run_id: str | None = None, custom_type: str | None = None, description: str | None = None, *, pending: bool = False, safe: bool = False) Run | None[source]#

Create a new run for the current project.

Parameters:
  • type – Built-in run type or a custom type label.

  • name – Optional run display name.

  • parent_run_id – Optional parent run ID for lineage.

  • custom_type – Label used when type is RunType.CUSTOM.

  • description – Optional run description. The Hub trims it, stores blank input as no description, and accepts at most 1,000 characters.

  • pending – When True, the run is created pending rather than running: it exists and can be logged to, and the Hub lists it as RunStatus.PENDING until something starts it. For work launched elsewhere, so the run can be annotated before the job that will run in it exists. Its started_at is provisional until then.

  • safe – When True, a failure to reach the Hub emits a TrackingFailureWarning and returns None instead of raising. An invalid run type is a caller mistake, not a Hub failure, so it still raises.

Returns:

The created run, or None if it could not be created and safe is True.

Raises:
  • ValueError – If a custom run type has no label, whatever safe is set to.

  • ApiError – If the Hub rejects the request and safe is False.

property current_run_log: RunLog | None#

Get the current in-progress RunLog, or None if no run is active.

get_devices() list[Device][source]#

Get the list of supported devices in the Embedl device cloud.

get_run_project(run_id: str) Project[source]#

Look up the project a run belongs to.

Needs no project selected first, so a parent run ID is enough to open the next run in the same project. Select the project with use_project() rather than by name: names are unique per owner only, so a project shared with you may share its name with one of yours:

project = client.get_run_project(parent_run_id)
client.use_project(project)
with client.start_run("train", parent_run_id=parent_run_id):
    ...

Like get_devices(), a failure raises rather than warns.

Parameters:

run_id – The run’s ID.

Returns:

The project the run belongs to.

Raises:
  • ValueError – If run_id is blank.

  • ApiError – If the hub does not know the run, or it is in a project this API key cannot read; either is a 404.

property latest_run_log: RunLog | None#

Get the most recent completed RunLog, or None if no runs have completed.

list_runs(*, params: Mapping[str, str] | None = None, statuses: RunStatus | Sequence[RunStatus] | None = None, types: RunType | str | Sequence[RunType | str] | None = None, limit: int | None = None, offset: int | None = None) RunListPage[source]#

List the current project’s runs, newest first.

Find the run a job logged its own ID on, to link the next job to it:

page = client.list_runs(params={"vertex_ai_job_id": job_id})
parent = page.items[0] if page.items else None

Like get_devices(), a failure raises rather than warns.

Parameters:
  • params – Parameters the runs must have logged, as name to value. Names and values are normalized the way log_param() stores them, so what was logged is found again.

  • statuses – Only runs with this status, or one of these.

  • types – Only runs of this type, or one of these. A string is read as start_run() reads it: a built-in type name selects that type, any other label selects the custom runs filed under it. Built-in types and labels cannot be combined in one call.

  • limit – Page size; the hub’s default and cap apply.

  • offset – Runs to skip, for paging.

Returns:

One page of run summaries with pagination info.

Raises:
  • ValueError – If a run type label is blank or contains a comma, built-in types are mixed with labels, a parameter name is blank, a parameter name or value contains a NUL character, or two parameter names are the same once trimmed.

  • RuntimeError – If no project is set.

  • ApiError – If the hub rejects the request.

log_artifact(file_path: Path | str | LoggedArtifact, name: str | None = None, file_name: str | None = None, run_id: str | None = None, *, ctx: HubContext | None = None, device_name: str | None = None, save_to_run_log: bool = True) LoggedArtifact | None[source]#

Log an artifact file for a run and upload it to artifact storage.

Accepts a local path, a remote path, or an existing LoggedArtifact. Remote artifacts are fetched from the device and uploaded to the Hub when log_remote_artifacts is True (the default). Set it to False to skip remote uploads and speed up execution.

If no name is given, the artifact is logged as unnamed. If no file_name is given, the name from the file path is used. If no run_id is given, the current active run is used.

Parameters:
  • file_path – Local file path, remote path, or a LoggedArtifact.

  • name – Optional artifact name.

  • file_name – Optional file name override.

  • run_id – Optional run ID (defaults to the active run).

  • ctx – Hub context, required when file_path is remote or a LoggedArtifact.

  • device_name – Name of the device that owns the remote artifact. Required for remote paths; ignored for LoggedArtifact (uses its own device info).

  • save_to_run_log – Whether to also record the artifact in the current RunLog.

Returns:

The LoggedArtifact that was recorded, or None if it failed inside a safe_log() scope.

Raises:
  • RuntimeError – If a remote artifact is received without a ctx, or a remote path is received without device_name.

  • TrackingUsageError – If file_name is given but blank — a broken caller rather than a Hub failure, so it is raised even inside a safe_log() scope.

  • FileTooLargeError – If the file exceeds the maximum allowed upload size.

  • StorageQuotaExceededError – If the storage quota has been exceeded.

  • ArtifactUploadError – If the file upload fails.

log_batch(*, metrics: list[tuple[str, float, int | None]] | None = None, params: list[tuple[str, str]] | None = None, external_links: list[tuple[str, str]] | None = None, save_to_run_log: bool = True) None[source]#

Log multiple metrics and params in a single API call.

This is significantly more efficient than calling log_metric() and log_param() in a loop, as it sends all data in one PATCH request instead of one POST per item.

Parameters:
  • metrics – List of (name, value, step) tuples. step may be None for metrics without a step.

  • params – List of (name, value) tuples.

  • external_links – List of (label, url) tuples.

  • save_to_run_log – Whether to also record entries in the current RunLog. Defaults to True.

log_dashboard(dashboard: Dashboard | Path | str, *, name: str | None = None, description: str | None = None, default: bool = False, opens_run: bool = False) RunDashboard | None[source]#

Attach a dashboard to the current run.

Accepts a Dashboard or the path of a file written by save().

Parameters:
  • dashboard – The dashboard, or the path of its JSON file.

  • name – The name to store it under on the run. Overrides the name the dashboard carries; required for a file that holds a bare config.

  • description – Overrides the dashboard’s description.

  • default – Makes it the run’s default dashboard, the one the run’s Dashboard tab opens on. A run has one.

  • opens_run – Opens the run itself on it rather than on the run’s details. Implies default, so there is no need to pass both.

Returns:

The RunDashboard the Hub stored, or None if it failed inside a safe_log() scope.

Raises:
  • DashboardSchemaError – If the file is not a dashboard this SDK reads, the dashboard ends up without a name or with one the Hub would not store, or its sections and panels do not form a valid layout. A broken input rather than a Hub failure, so it is raised even inside a safe_log() scope.

  • FileNotFoundError – If there is no file at the given path. Degrades to a warning inside a safe_log() scope.

Log an external link for the current run.

Parameters:
  • label – The link label.

  • url – The link target.

  • save_to_run_log – Whether to also record this entry in the current RunLog. Defaults to True.

Returns:

The LoggedExternalLink that was recorded, or None if it failed inside a safe_log() scope.

log_metric(name: str, value: float, step: int | None = None, *, save_to_run_log: bool = True) LoggedMetric | None[source]#

Log a metric for the current run.

Parameters:
  • name – The metric name.

  • value – The metric value.

  • step – Optional step number.

  • save_to_run_log – Whether to also record this entry in the current RunLog. Defaults to True.

Returns:

The LoggedMetric that was recorded, or None if it failed inside a safe_log() scope.

log_param(name: str, value: str, *, save_to_run_log: bool = True) LoggedParam | None[source]#

Log a parameter for the current run.

Parameters:
  • name – The parameter name.

  • value – The parameter value.

  • save_to_run_log – Whether to also record this entry in the current RunLog. Defaults to True.

Returns:

The LoggedParam that was recorded, or None if it failed inside a safe_log() scope.

property log_remote_artifacts: bool#

Whether remote artifacts are fetched and uploaded to the Hub.

When True (the default), calling log_artifact() with a remote path or a remote LoggedArtifact will download the file from the device and upload it to the Embedl Hub.

Set to False to skip the download-and-upload step and speed up execution. The artifact is still recorded in the run log, but its contents are not transferred.

log_tag(name: str, value: str) Tag | None[source]#

Log a tag for the current run.

Parameters:
  • name – The tag name.

  • value – The tag value.

Returns:

The Tag that was recorded, or None if it failed inside a safe_log() scope.

property project: Project#
property run_history: RunLogHistory#

Get the run history (read-only).

Returns:

A RunLogHistory containing all completed RunLog entries. The history cannot be modified.

safe_log() Iterator[Client][source]#

Downgrade logging failures to warnings inside this scope.

Within the scope, log_param(), log_metric(), log_link(), log_batch(), log_tag(), log_artifact() and log_dashboard() emit a TrackingFailureWarning and return None instead of raising, so a Hub outage cannot abort the surrounding script:

with client.safe_log():
    client.log_metric("accuracy", 0.92)

Scopes nest; leaving one restores the previous state. Only the client’s own logging calls are affected — exceptions raised by your code inside the scope propagate untouched, and calls made outside it raise as usual.

This does not cover starting or ending a run. Pass safe=True to start_run() for that.

Returns:

A context manager yielding this client.

property safe_logging: bool#

Whether logging calls currently warn instead of raising.

True while execution is inside a safe_log() scope. Read-only — safe mode is entered by scope, never by assignment, so a client handed to library code cannot be permanently disarmed.

set_project(name: str, *, safe: bool = False) Project | None[source]#

Set or create the current project by name.

Parameters:
  • name – The project name. Created on the Hub when it does not already exist.

  • safe – When True, a failure emits a TrackingFailureWarning and returns None instead of raising.

Returns:

The project, or None if it could not be set and safe is True. Either way, a failure leaves the client with no project selected rather than the one selected before, so a later run cannot be filed against the wrong project.

Raises:
  • TrackingUsageError – If a run is active. Moving the project out from under it would leave the client addressing that run inside a project it is not in, which every log call composes independently and so cannot detect: the requests go to a run that is not there. Raised whatever safe is set to, since this is a caller mistake rather than a Hub failure.

  • ApiError – If the Hub rejects the request and safe is False.

start_run(type: RunType | str, name: str | None = None, parent_run_id: str | None = None, custom_type: str | None = None, description: str | None = None, *, safe: bool = False) Iterator[Run | None][source]#

Start a run and finish it when the context exits.

Parameters:
  • type – Built-in run type or a custom type label.

  • name – Optional run display name.

  • parent_run_id – Optional parent run ID for lineage.

  • custom_type – Label used when type is RunType.CUSTOM.

  • description – Optional run description. The Hub trims it, stores blank input as no description, and accepts at most 1,000 characters.

  • safe – When True, a failure to create or complete the run emits a TrackingFailureWarning instead of raising. If the run cannot be created the body still runs as though tracking had never been added, with nothing active, and the context manager yields None. Guard the body with safe_log() as well if it logs — safe covers only the run lifecycle. Exceptions from the body are never suppressed.

Returns:

An iterator yielding the created run, or None when safe is True and the run could not be created.

Raises:
  • RuntimeError – If a run is already active, whatever safe is set to. Runs do not nest; use parent_run_id to record that one run followed another.

  • ValueError – If a custom run type has no label, whatever safe is set to — that is a mistake in the call, not a Hub failure.

  • ApiError – If the Hub rejects a create or completion request and safe is False.

update_active_run(status: Literal[RunStatus.FINISHED, RunStatus.KILLED, RunStatus.FAILED] | None = None, ended_at: datetime | None = None, metrics: list[Metric] | None = None, params: list[Parameter] | None = None, external_links: list[ExternalLink] | None = None, parent_run_id: str | _RemoveParent | None = None, description: str | None = None) None[source]#

Update the active run.

Writes metrics, params, links and the description, so it counts as logging: inside a safe_log() scope a failure warns instead of raising.

Parameters:
  • status – Optional terminal run status.

  • ended_at – Optional completion time.

  • metrics – Metrics to append.

  • params – Parameters to append.

  • external_links – External links to append.

  • parent_run_id – Parent run ID, or REMOVE_PARENT to clear the parent.

  • description – New run description. Pass a blank or whitespace-only string to clear it. The Hub accepts at most 1,000 characters.

Returns:

None.

Raises:

ApiError – If the Hub rejects the request and the call is not inside a safe_log() scope.

use_inherited_run(*, safe: bool | None = None) Run | None[source]#

Log to the run this process was handed, if it was handed one.

Reads EMBEDL_HUB_RUN and EMBEDL_HUB_PROJECT, which embedl-hub exec exports for the command it wraps, so a script can record into the run the wrapper already created:

embedl-hub exec --type training ./train.py
client = Client()
client.use_inherited_run()
client.log_metric("loss", 0.1)

A missing EMBEDL_HUB_RUN is not an error. It is what exec --safe leaves behind when the Hub could not be reached: the project is set, no run exists, and the command is meant to carry on untracked. So this warns and returns None rather than raising, and a script that treats the return value as optional works under both.

Parameters:

safe – As for use_run(), including that the default defers to EMBEDL_HUB_SAFE, which is set for exactly this case by exec --safe.

Returns:

The adopted run, or None if the environment named none, or if it could not be adopted and safe is True.

Raises:
  • TrackingUsageError – If a run is already active.

  • ApiError – If the Hub rejects the request and safe is False.

use_pending_run(run_id: str, project: str | None = None, *, safe: bool | None = None) Iterator[Run | None][source]#

Start a pending run and finish it when the context exits.

For the job a pending run was created for: something else made the run with create_run() and pending=True, annotated it, and handed over its ID. This starts it as the work begins and, unlike use_run(), takes responsibility for it — the run is completed from how the block ends, exactly as start_run() completes a run it created:

with client.use_pending_run(run_id):
    client.log_metric("loss", 0.1)
Parameters:
  • run_id – The pending run to start.

  • project – As for use_run(): None means the selected project, or, with none selected, that the run is looked up by ID alone and its own project becomes the selection; a name switches to that project first.

  • safe – As for use_run(), including that None defers to EMBEDL_HUB_SAFE. When tolerated, a failure to find or start the run warns, leaves nothing selected, and yields None; the body still runs.

Returns:

An iterator yielding the run, now running, or None if it could not be started and safe is True.

Raises:
  • TrackingUsageError – If run_id is blank, if a run is already active, or if the run is not pending — it is running or finished, or something else started it while it was being adopted here — since whoever did that owns its outcome; use use_run() to log to a run something else is running. Raised whatever safe is set to.

  • ApiError – If the Hub rejects a request and safe is False.

use_project(project: Project) Project[source]#

Select a project already looked up, without a Hub call.

For a Project in hand, such as the one get_run_project() returns. It selects exactly that project, where set_project() looks one up by name and cannot tell apart two projects that share one.

Parameters:

project – The project to select.

Returns:

project, now the current project.

Raises:

TrackingUsageError – If a run is active; see set_project().

use_run(run_id: str, project: str | None = None, *, safe: bool | None = None) Run | None[source]#

Log to a run that already exists, started somewhere else.

For picking up work another process began — most often the run embedl-hub exec created for the command it is wrapping. Use use_inherited_run() for that case, which reads the run and project out of the environment rather than being told them.

Adopting a run does not take responsibility for it. Nothing here completes it, and leaving the process does not either: whoever started the run still decides its outcome. Under exec that is the wrapper, which completes the run from the command’s exit status.

Parameters:
  • run_id – The run to log to.

  • project – The project the run is in. None means the project already selected, which the run must then be in; with no project selected, the run is looked up by ID alone and its own project becomes the selection. A name switches to that project first and warns with TrackingProjectChangedWarning, because a run belongs to one project and adopting it has to move the client with it.

  • safe – When True, a failure to reach the Hub emits a TrackingFailureWarning and returns None instead of raising, leaving nothing adopted. As elsewhere, this covers an unreliable Hub and not a malformed call. None, the default, takes the answer from the environment: embedl-hub exec --safe sets EMBEDL_HUB_SAFE for the command it wraps, so a script adopting the run that wrapper handed it does not raise where the wrapper itself warned. Pass False to insist on raising even there.

Returns:

The adopted run, or None if it could not be adopted and safe is True.

Raises:
  • TrackingUsageError – If run_id is blank, if a run is already active, or if the run has already finished. Raised whatever safe is set to.

  • ApiError – If the Hub rejects the request and safe is False.

validate_device(device: str) Device[source]#

Check if the specified device is supported in the Embedl device cloud.

Parameters:

device – The device name to check.

Returns:

The Device if supported.

Raises:

UnsupportedDeviceError – If device is not in the supported device list.

class embedl_hub.tracking.RunStatus(value)[source]#

Bases: Enum

FAILED = 'FAILED'#
FINISHED = 'FINISHED'#
KILLED = 'KILLED'#
PENDING = 'PENDING'#
RUNNING = 'RUNNING'#
class embedl_hub.tracking.RunType(value)[source]#

Bases: Enum

COMPILE = 'COMPILE'#
CUSTOM = 'CUSTOM'#
EVAL = 'EVAL'#
GRAPH = 'GRAPH'#
INFERENCE = 'INFERENCE'#
PROFILE = 'PROFILE'#
exception embedl_hub.tracking.TrackingFailureWarning[source]#

Bases: UserWarning

Warns that a tracking operation failed and was skipped.

Emitted instead of raising when the failing call is covered by a safe scope — a run started with safe=True, or a logging call made inside safe_log(). The script continues, and whether the affected data reached the Hub is unknown: a request that failed while the response was being read may already have been committed. Treat it as uncertain rather than lost, since retrying on the assumption it was lost can duplicate it.

This is its own category so it can be filtered independently of other warnings. Escalating it to an error is not applied to a failed run completion while an exception is already leaving the with body: that exception is the one the caller needs, so it is never replaced:

# Silence them
warnings.filterwarnings("ignore", category=TrackingFailureWarning)

# Or turn them back into errors
warnings.filterwarnings("error", category=TrackingFailureWarning)
exception embedl_hub.tracking.TrackingProjectChangedWarning[source]#

Bases: UserWarning

Warns that selecting a run also changed the active project.

A run belongs to exactly one project, so adopting one from another project has to move the client with it. That is not a failure, and it is not what the caller literally asked for either, so it is said out loud rather than done quietly — a later log_metric would otherwise land somewhere the caller did not choose.

Its own category, so it can be silenced by a script that switches deliberately without also silencing TrackingFailureWarning:

warnings.filterwarnings(
    "ignore", category=TrackingProjectChangedWarning
)
exception embedl_hub.tracking.TrackingUsageError[source]#

Bases: RuntimeError

Raised when a tracking call itself is malformed.

Safe mode (safe=True, safe_log()) tolerates an unreliable Hub, not a malformed call, so this is raised even inside a safe scope: a warning the caller cannot act on would just hide the mistake.

It is marked with its own class rather than inferred from the exception type, because the type does not carry the distinction — a missing project or absent active run raises RuntimeError too, and those are degradable, being the downstream consequence of a failure the caller already chose to tolerate.

Subclasses RuntimeError so that existing handlers and the :raises RuntimeError: contracts keep working.

Submodules#