Skip to content
heliaEstimator
User guide
HELIA HUB

Python experiments and results

Pre-v1 breaking cutover (#136): this API reads native schema 4 and saved plan/build format 2 only. Pre-change APIs/results are unsupported; recreate plans/builds/captures. No v1 product release or archival reader is implied.

For upgrade steps, see API migration.

For a worked example and visual tools, see Parse and visualize results.

validate_input(reference, ValidationKind) uses the same checks as helx validate. It returns a ValidationOutcome with kind, path, valid, summary, error a tuple of ValidationIssue values, and advisory diagnostics. Issues carry a code, path, message and optional field; bundle failures retain the native reader’s ReadIssueCode. Other actionable input failures use ValidationIssueCode.INVALID_INPUT. Diagnostics are typed Diagnostic values (DiagnosticLevel.INFO or WARNING); they do not by themselves invalidate a config. The CLI logs these messages using its existing logging configuration; Python callers decide how to present them. Programming errors and interruptions still propagate.

from helia_estimator.api import ValidationKind, validate_input
check = validate_input("examples/at110/kws_vela_only.yaml", ValidationKind.CONFIG)
if check.valid:
print(check.summary)
else:
for issue in check.issues:
print(issue.code.value, issue.path, issue.message)

Kinds are CONFIG, PLATFORM (a shipped name or YAML path), BUNDLE, FROZEN and BUILD. Config validation applies pre-compilation checks; bundle validation verifies the retained manifest and files. These checks run no tools, download nothing and write nothing. They do not establish host readiness or silicon accuracy. The library prints nothing; the caller owns presentation.

native_table(roots, fields=ExportFields.SUMMARY, strict=False) supplies the same rows as helx export. Pass a sequence of bundle directories, summary files or parent directories. Discovery deduplicates overlapping paths, sorts candidates within each root, and retains root order.

from helia_estimator.api import ExportFields, native_table
collected = native_table(["results/"], ExportFields.SUMMARY)
for skipped in collected.skipped:
print(skipped.path, skipped.message) # Optional presentation by your application.
rows = list(collected.rows) # JSON-ready mappings; no files written.

NativeTable.rows contains every indexed sample for SUMMARY or ALL fields; STATISTICS gives one aggregate row per bundle, preserving exact counter ratios. The dynamic flattened columns keep their existing native names. Missing values remain None, distinct from measured zero. Each call owns its row mappings.

NativeTable.skipped contains SkippedBundle records with the rejected path, structured reader issues and a diagnostic message. Incomplete attempts are included even if they have no summary yet. strict=True raises ResultReadError on the first rejected bundle. No matches or all inputs skipped returns empty rows; the library leaves that decision to the caller. The CLI reports skipped inputs on stderr and exits 1 when no rows remain, preserving its existing behavior.

These native tables are separate from the portable export_result contract. Neither operation decides whether different runs or producers are comparable.

prepare_case shares the preparation used by helx run-case. It resolves model, platform and output paths against an explicit base directory without writing files, downloading anything or starting tools. RunCaseOptions represents the case options; PreparedCase contains the resolved specification and selected paths.

from pathlib import Path
from helia_estimator.api import RunCaseOptions, prepare_case, estimate
case = prepare_case(
RunCaseOptions(model="models/model.tflite", run_root=Path("results/one-off"), vela_only=True),
base_dir=Path("/work/project"),
)
paths = case.materialize()
outcome = estimate(paths.config_path)

Materialization explicitly creates directories and writes config.yaml, marking any previous result at that location incomplete. Full configuration admission still occurs when estimate loads the config. Invalid preparation leaves prior files untouched; execution and materialization failures retain the existing incomplete-attempt behavior. Neither preparation nor materialization renders terminal output. Setup/download status uses logging; library callers control its handlers and verbosity. Interactive Arm licence presentation belongs to helx setup; ordinary execution never asks for consent.

Use load_request(path) for an existing version-2 YAML config, or construct the same validated configuration in Python. ExperimentConfig and its nested types are public names for the existing strict config models; all YAML options retain their meaning. The request captures a snapshot, so later edits to the input objects or YAML file do not change that request.

from pathlib import Path
from helia_estimator.api import (
EstimateRequest, ExperimentConfig, ExperimentDefaults,
ExperimentFvp, ExperimentModel, ExperimentRun, estimate, load_request,
)
# Existing YAML; equivalent to helx run -c examples/at110/kws_vela_only.yaml.
request = load_request("examples/at110/kws_vela_only.yaml")
# Or supply typed values, with an explicit base for local relative paths.
request = EstimateRequest(
ExperimentConfig(
version=2,
run=ExperimentRun(
results_dir="results/my_case", platform="at110", platform_mode="ULP",
),
defaults=ExperimentDefaults(fvp=ExperimentFvp(enabled=False)),
models=[ExperimentModel(source="local/my_model.tflite", memory_modes=["Sram_Only"])],
),
base_dir=Path.cwd(),
)
resolved = request.resolve() # Validate platform and resolve paths; no run.
outcome = estimate(request) # Compile, optionally build/run, write and verify.
print(outcome.results_dir, outcome.stage)
print(outcome.result.summary.run.model)

load_request checks the document. request.resolve() additionally reads local platform definitions and resolves references against the YAML directory or the explicit base_dir. Neither creates outputs, downloads a model, probes the host or invokes tools. The snapshot covers configuration, not the contents of referenced models or platform files; those are read during resolution/execution.

estimate deliberately starts a new attempt and invalidates any older bundle at that destination before platform resolution. It uses the existing pipeline and workspace safeguards. Actionable failures retain their existing exception types and causes. Use the public ConfigError for config failures or actionable_errors() for the specific failures also handled by the CLI; interruption and programming errors propagate. An incomplete attempt cannot return an EstimateOutcome. An outcome contains a VerifiedResult, its bundle directory, summary CSV path and native FVP stage. Verification establishes evidence integrity; inspect stage, sanity and energy availability before interpreting the numbers. Vela-only is disabled, not an executed FVP measurement. Library execution does not render CLI result tables.

The run’s helx.log captures DEBUG-and-higher records admitted by your logging configuration that reach the root logger. Library execution does not change logger levels or existing handler filters. To include INFO diagnostics, enable INFO in your application’s logging setup before calling estimate; use handler levels to control what appears on your console. Records suppressed by logger levels or filters, or routed through non-propagating loggers, are not recovered. The CLI configures INFO logging by default and DEBUG with -vv.

File requests retain their exact captured YAML bytes and original path. request.source_bytes exposes that immutable byte snapshot for an admitted config hash check; it is None for programmatic requests. Programmatic requests retain a generated input_config.yaml in the bundle, with HELX-owned paths resolved so it can be reloaded there. Engine-owned options keep their existing path conventions. No caller-managed temporary YAML is needed.

The CLI and Python share estimate. Passing a YAML path directly, as in estimate("case.yaml"), keeps the CLI’s early invalidation behavior even when the document has a schema error. load_request("case.yaml") alone remains read-only and captures a valid document for later execution.

Execution-only CLI overrides have a typed equivalent:

from helia_estimator.api import EstimateOptions, ModelCacheMode
outcome = estimate(
"case.yaml",
options=EstimateOptions(
model="local/alternate.tflite", # Relative to cwd, like CLI --model.
model_cache_mode=ModelCacheMode.EPHEMERAL,
),
progress_sink=lambda event: print(event.stage.value, event.phase.value),
)

ProgressEvent is frozen and names a PipelineStage and ProgressPhase (started, completed, failed or skipped). Its elapsed_seconds is host stage duration, not model latency. Events come from existing pipeline boundaries, independently of console rendering; they do not claim whole-run success, and resolution before the first pipeline stage may fail without an event. Observers are synchronous and scoped to this call. Observer exceptions normally propagate; if the pipeline is already failing, a failed notification adds a note without replacing the original error. These notifications do not make concurrent execution safe.

Stage timing and delivery live in the Rich-free progress layer. CLI commands pass a console observer through the same progress_sink argument available to Python. The FVP event measures simulator launch/execution; acquisition parsing and evidence validation follow it, so FVP completed alone does not establish a valid result. Run-file logging remains separate and preserves the application’s logging setup.

The supported import boundary is helia_estimator.api. It includes FvpStage for EstimateOutcome.stage, PmuProfile for ExperimentFvp.pmu_profile, and ProgressSink for callbacks. These are the same objects used internally, not parallel enum or exception definitions. dir(api) lists lazy public symbols without importing their implementation modules.

from helia_estimator.api import (
ConfigError, FvpStage, PmuProfile, ProgressSink,
actionable_errors, estimate, load_request,
)
try:
request = load_request("case.yaml")
outcome = estimate(request)
except ConfigError as exc:
print(f"Check the configuration: {exc}")
except actionable_errors() as exc:
print(f"Experiment could not complete: {exc}")
else:
if outcome.stage is FvpStage.RAN:
print("FVP measurements are available")

actionable_errors() returns a tuple of specific exception classes suitable for an except clause. It shares the CLI’s classification and does not wrap errors, change their causes, or treat every ValueError/OSError as actionable. Individual operations can still raise their documented argument and filesystem errors; for example, file export can raise OSError. Caller bugs and KeyboardInterrupt are not swallowed.

Importing the function does not load execution modules. Calling it lazily imports error definitions, but performs no readiness checks, downloads, builds or runs. This distinction also applies to discovering ConfigError with dir(api) versus explicitly importing it, which loads the config implementation.

EstimateRequest captures configuration. freeze additionally binds the local model, platform and its transitive SoC/energy-profile files, any target and legacy energy override. For FVP runs it also binds the shipped dependency manifest and required patch bytes. It checks the existing static platform/FVP constraints without creating outputs, preparing dependencies or downloading.

from helia_estimator.api import freeze, load_frozen, estimate
# A remote model must first be downloaded explicitly. A local override is also
# useful when the original config names a remote model.
plan = freeze("case.yaml", model="local/model.tflite")
plan.save("local/case.plan.json") # New file; refuses overwrite.
restored = load_frozen("local/case.plan.json") # Verifies now.
restored.verify() # Read-only recheck.
for item in restored.inputs:
print(item.role.value, item.path, item.sha256)
outcome = estimate(restored) # Or restored.run().

The equivalent CLI commands are:

Terminal window
uv run helx freeze -c case.yaml --model local/model.tflite -o local/case.plan.json
uv run helx validate frozen local/case.plan.json --json
uv run helx replay local/case.plan.json

The destination’s parent must exist. A plan stores exact captured YAML for file requests, independent validated settings, the original absolute reference base, resolved settings, file sizes/SHA-256 values and software requirements. Moving just the plan file or editing the original YAML does not change those settings. The input files must remain at their recorded locations. No model or coefficient bytes are embedded in the JSON. The plan can contain private local paths and captured inline configuration; share it accordingly.

FrozenRunError is a ConfigError with a typed issue (code, field, optional path, and message). A missing or changed input, changed resolved input set, software version or relevant HELX/NPU/Vela/AOT implementation change refuses replay before invalidating an existing result bundle. Other Python dependency versions are checked as requirements. Replay checks again after execution; if that check fails, the produced bundle is marked incomplete and no outcome is returned. Keep the inputs and producer installation unchanged throughout a run; these checks do not lock files against concurrent writes or modifications that are subsequently restored.

This is a local replay plan with schema_id: "helx.frozen-run" and integer schema_version: 2. Unknown versions/fields, duplicate JSON keys and mismatched checksums are rejected. Its snapshot_sha256 checks the v2 document excluding that checksum field; it is not a signature or an intent/build/observation hash. Removing a required input binding is also rejected against fresh resolution. Treat the document as a producer-owned versioned format and use load_frozen, not private dataclass fields, pickle or hand-edited plans.

The v2 boundary intentionally has these limits:

  • Models must be local; ordinary estimate still supports URLs and its cache.
  • Per-layer fenced execution with Vela requires effective fvp.enabled: true so the same FVP input and option checks cover both normal and fenced runs. Ordinary runs retain the separate main-stage/fenced-stage behavior.
  • Arbitrary cmake_args and fvp_args are refused because they can introduce undeclared external files. build_args may only be ["--parallel", "N"] with positive N; ordinary build defaults remain bounded.
  • AOT overrides support built-in platform.name and module.name/prefix identifiers. Other upstream options need an adapter-owned input contract before they can be frozen; ordinary AOT runs retain their existing options.
  • The manifest binds declared source/toolchain/FVP requirements, not the actual installed binary/source closure or ambient compiler environment. Normal managed-dependency readiness and licence checks still run during execution.
  • Replay uses the existing compile/build/run pipeline. It can prepare missing managed dependencies and rebuilds as normal; it does not reuse a frozen executable. Use the compiled-build APIs below to compile once and execute retained bytes.

The current native result is schema 4 and its mapping is helx-schema4-draft4. Retain the plan alongside the outcome if a caller needs that association; the native manifest does not claim to contain the saved plan or a semantic build identity.

A frozen plan captures local inputs. build compiles the model and builds the selected runner through the existing pipeline, then retains a separate verified compiled-build record. It does not invoke FVP or produce inference PMU results.

from helia_estimator.api import build, freeze, load_build
plan = freeze("case.yaml", model="local/model.tflite")
compiled = build(plan, "local/case-build") # New directory; parent must exist.
print(compiled.runtime, compiled.kernel_library, compiled.image)
for tool in compiled.tools:
print(tool.name, tool.version, tool.sha256)
compiled.save("local/copied-build")
retained = load_build("local/copied-build")
retained.verify() # Offline; original model and provider paths are not needed.
Terminal window
uv run helx build local/case.plan.json --output local/case-build
uv run helx validate build local/case-build --json

The v2 directory has build_record.json (schema_id: helx.compiled-build, integer schema_version: 2), the original plan.json, captured original inputs, CMake file-API metadata, and an evidence/ native schema-4 build-only bundle. It retains the actual executable and runtime model. The record’s file catalog has relative paths, roles, sizes and SHA-256 values. Every retained file is checked; missing, changed, additional, escaping or symlinked assets are refused. Loading also verifies the native evidence and its runtime, kernel and artifact references.

CompiledBuild, BuildArtifact, BuildInput, BuildTool, BuildSource and LinkedArchive expose immutable typed values. Original compiler/source/archive paths are provenance, not paths that offline verification opens. load_build and verify do not run Git, compilers or simulators, resolve a frozen plan, prepare dependencies, import Vela, or contact the network. A record can be moved and verified independently of the original installation.

During building, HELX checks the actual compiler drivers against CMake’s selected paths and records driver hashes/version text, managed Git revisions/tree and patch identities, and hashes of explicitly named static archives in the link recipe. It checks the compiler/source observations before and after the build. These are retained bytes and observed build inputs, with scope retained-bytes-and-observed-build-inputs-v1; they do not establish a complete hermetic toolchain closure, all implicit system libraries, or physical silicon qualification. record_sha256 is the v2 document checksum and is separate from the future semantic intent/build/observation hash trio.

Building may use normal managed dependency setup and licence checks. The caller’s cache options and progress sink are preserved. It writes only a new record location and leaves the frozen plan’s configured result/workspace destinations untouched. A failed build retains an incomplete marker and available diagnostics; load_build refuses it. Existing destinations are never overwritten. Record contents include the captured model/platform/energy inputs and local path provenance.

Retained builds support the LiteRT/CMSIS-NN, heliaRT/heliaCORE and heliaAOT/heliaCORE runner builds with effective FVP enabled and per-layer fencing off. Ordinary estimation continues to support Vela-only and fenced workflows. Execution from the retained record uses run_build or record.run, described in Run a retained build.

RuntimeEngine and KernelLibrary are public enums. Their existing aliases are accepted by select_runtime, which returns a frozen, validated RuntimeSelection. The default is LiteRT/CMSIS-NN. heliaRT and heliaAOT use heliaCORE. These CPU kernel choices are separate from the platform’s NPU/FVP accelerator route.

from helia_estimator.api import KernelLibrary, RuntimeEngine, select_runtime
selection = select_runtime(RuntimeEngine.HELIA_RT, KernelLibrary.HELIA_CORE)
fvp = ExperimentFvp(enabled=True, run=True, runtime_engine=selection.runtime)
print(selection.runtime.canonical_name, selection.kernel_library.value)

Selection checks have no operational effects. A SelectionError is a ConfigError with a SelectionIssueCode and allowed_values, so consumers can distinguish unknown names from unsupported pairs. Normal config validation and the legacy helia_rt_backend field keep their existing behavior. The selector does not enable new linkage combinations. RuntimeEngine.value retains native spellings (tflite_micro, helia_rt, helia_aot); canonical_name is litert, helia-rt or helia-aot for public naming. Historical results are unchanged.

runtime_support() returns frozen RuntimeSupport and FvpSupport records, derived from registered adapters and helia-npu FVP profiles. It lists native runtime identities, their effective kernel libraries, compatible subsystems/NPUs, registered memory modes and bus-trace availability. Its explicit basis is registered_runner_contract. This is neither local readiness nor qualification of arbitrary models or silicon. Use helx doctor for explicit host checks. Runtime/kernel restrictions still apply; heliaRT and heliaAOT use heliaCORE, LiteRT uses Arm CMSIS-NN.

from helia_estimator.api import runtime_support
for runtime in runtime_support():
print(runtime.runtime, runtime.targets, runtime.basis)
Terminal window
helx support
helx support --json # One document; no host probe, model fetch, build or run.

The additive helia_estimator.api reader inspects native result bundles without compiling, running FVP, resolving models or contacting download hosts. It uses HELX’s existing Python 3.12 or 3.13 environment; import isolation does not remove the package’s declared installation dependencies.

from pathlib import Path
from helia_estimator.api import inspect_result, load_result, export_native_summary
inspection = inspect_result(Path("results/my_case"))
if inspection.result is not None:
result = inspection.result
summary = result.summary
print(summary.run.case_name, summary.fvp.stage)
export_native_summary(result, Path("native-summary-copy.json"))
else:
print(inspection.status, inspection.issues)
# Strict callers may use load_result(path), which raises ResultReadError
# containing the inspection if the native evidence cannot be verified.

inspect_result and load_result accept a bundle directory or its run_summary.json. Verification requires the native version-1 manifest and version-4 summary, a completed latest attempt, matching retained sizes/hashes, safe relative paths, correctly bound summary/config/artifact references and consistent result stages. Portable Windows backslash-relative paths can be read on POSIX hosts; original manifest bytes and paths are retained. Duplicate portable paths and escaping symlinks are rejected. Unlisted workspace/ scratch and the root helx.log are excluded, as with native bundle verification; they are not verified evidence. The copied config must have an input artifact role. Runtime arena usage is accepted only for an executed stage.

Paths with Windows-reserved components, trailing dots/spaces or forbidden characters are rejected even on POSIX, so host-specific aliases cannot become distinct portable artifact identities.

Inspection statuses are VERIFIED, INCOMPLETE, UNVERIFIED, INVALID and UNSUPPORTED. Only VERIFIED supplies a result. An incomplete marker overrides an older summary and does not establish whether the attempt failed, was interrupted or is still running. A missing manifest is UNVERIFIED; malformed or corrupted evidence is not. These are native Python types, not a separately owned portable JSON format.

A completed Vela-only or build-only result is valid. A completed result can retain failed PMU sanity or unavailable energy; integrity does not establish numerical correctness or silicon qualification. Simulator counter sources and modeled energy keep their native meanings and provenance.

VerifiedResult captures exact summary/manifest content identities and retained artifact references. Each summary access returns an independent typed view. result.read_artifact(reference) rechecks containment, size/hash and completion before returning a retained artifact. Verification is a snapshot, not a promise that files on disk will never change. Native hashes establish content integrity, not source authenticity.

export_native_summary writes the exact captured native JSON to a new file outside the source bundle and refuses to overwrite an existing destination. The output is a summary copy, not a new verified bundle; it does not copy the manifest or artifacts. parse_summary(path) is an explicitly schema-only reader that does not establish completion or retained artifact integrity.

run_identity(result) returns a frozen RunIdentity over the captured native summary and manifest. EstimateOutcome.run_identity provides the same view after execution. Both paths work with the existing verified bundle; no tool probe, dependency lookup, external model read or simulation is performed.

from helia_estimator.api import load_result, run_identity
identity = run_identity(load_result("results/my_case"))
engine = identity.runtime.runtime
if engine.value is not None:
print(engine.value.canonical_name)
print(identity.runtime.kernel_library.value)
print(identity.model.runner_model_sha256.value)
print(identity.acquisition.warmup_invokes.value)
print(identity.runtime.runtime_commit.unavailable_reason)

Each IdentityFact[T] has a typed value, native sources and an unavailable_reason. A present value has no unavailable reason; a missing value always has one. Sources identify native JSON pointers, for example run_summary.json#/run/runtime or manifest.json#/files/2/sha256. NOT_RECORDED means the mapped structured native fields supply no value, UNRECOGNIZED_VALUE means a recorded value cannot be mapped, and NOT_ESTABLISHED means the semantic identity has not been defined and bound. The native snapshot remains available for inspecting unknown or future producer values.

Identity component Meaning
evidence Existing ResultIdentity: exact manifest/summary hashes and native schema version
configuration_sha256 Exact retained YAML bytes, including path-specific settings; not a semantic intent hash
runtime Recorded runtime and CPU kernel library; runtime/kernel versions and commits stay unknown when unrecorded
model Separate original-model, Vela-container, runner-input, raw-command-stream and input-set identities
runner_image_sha256 Manifest-bound executable image bytes, not a complete build/dependency identity
target Recorded SoC/platform revision, simulator target, accelerator, placement, operating mode, configured NPU clock and energy-profile hash
acquisition Reported warmups, measured count, state policy and NPU callback scope when retained; non-executed stages have no acquisition facts
hashes Future intent/build/observation semantic identities; schema-4 projection currently returns NOT_ESTABLISHED for all three

New native builds retain artifacts/build_provenance.json in the verified manifest. build_provenance(result) returns a frozen BuildProvenance, or None for compile-only bundles without build facts. Its versioned facts include configured runtime/kernel checkout commits, trees, tracked-change digests and exact tags, plus the selected compiler path/version/digest and runner/model digests. run_identity projects these recorded facts; it never probes current providers.

These are configured-source observations, not a complete linked-source closure. Untracked files and external dependencies are outside this basis. Modified tracked sources keep their base commit and change digest but have no release version. A CMake source override that cannot be bound to the observed provider stays unknown. Each missing BuildFact carries an unavailable_reason. For AOT, generator_version names the package that generated the selected module; it is separate from a linked runtime version, which remains unknown. Retained replay preserves the original build facts. Contradictory or malformed provenance is rejected rather than mixed with the native identity.

from helia_estimator.api import build_provenance, load_result
facts = build_provenance(load_result("results/my_case"))
if facts is not None:
print(facts.kernels.commit.value)
print(facts.generator_version.value)

Current LiteRT uses CMSIS-NN; heliaRT and heliaAOT use heliaCORE. Unsupported runtime/backend combinations are rejected. Pre-change native formats and saved records are not read by this API.

A Vela/runner TFLite file contains more than a raw NPU command stream. Its digest cannot identify the raw stream, original model or input tensors. Those identities remain unknown unless separately recorded. The projector does not follow source URIs, hash a current cache entry or infer linked runtime commits from today’s package pins. The configured clock describes the native run setting, not a measured silicon clock. An FVP target represents the recorded simulation configuration; it does not establish full-SoC emulation.

This is a context projection, not numerical output verification, hardware qualification or a replay guarantee. Like VerifiedResult, it describes the captured snapshot: reload the bundle to verify its current files. This projection does not modify the retained native record. See API migration for the schema-4 and saved-format-2 cutover.

viewer_artifacts(result) returns frozen ViewerArtifact records over existing manifest-bound files. ViewerKind distinguishes Model Explorer from Perfetto; ViewerBasis distinguishes layer attribution, simulated bus timelines and concatenated fenced costs. Model Explorer records also name the retained overlay metric and require the original model graph. original_model_uri is context, not a graph digest or proof that the model is still available. The catalog does not assert complete operator coverage or cross-producer graph correspondence.

from helia_estimator.api import load_result, viewer_artifacts, export_viewer_artifact
result = load_result("results/kws_bus_trace")
for item in viewer_artifacts(result):
print(item.viewer, item.basis, item.artifact.relative_path)
print(item.requires_original_model, item.metric)
# Select one catalogued file; create a new destination outside the bundle.
catalog = viewer_artifacts(result)
if catalog:
export_viewer_artifact(result, catalog[0], "saved-viewer.json")

Discovery reads the captured native summary/manifest metadata. It recognizes summary-named outputs and retained Model Explorer siblings from the existing writer convention; arbitrary JSON is not a viewer artifact. Missing exports remain absent. summary_source anchors the family in native JSON; the individual ArtifactRef supplies its retained identity. Source files can change after a snapshot: copying rechecks containment, completion, size and hash. It refuses foreign metadata, an existing destination or a destination inside the bundle. The parent destination directory must already exist.

These operations copy retained bytes unchanged; they do not compile, simulate, capture, regenerate graphs or launch viewers. Existing units and missing-data semantics stay in the files. Compiler overlay values are not new PMU counters; fenced cumulative costs are not an observed unfenced timeline. A copied JSON is a visualization file, not a new native verified bundle. See the viewer guide for loading it with the appropriate graph.

Terminal window
helx results artifacts results/kws_bus_trace --format json
helx results export-artifact results/kws_bus_trace \
--artifact artifacts/bus_trace_perfetto.json --output saved-timeline.json

artifacts emits one HELX catalog document with native identity and typed viewer metadata. It returns an empty viewers list when no supported files were retained. export-artifact writes the selected file and reports its path on stderr. Rejected native evidence or export errors return exit 1; usage errors return 2. This catalog is separate from the portable result draft below.

observations(result) projects verified retained fields into the draft portable view; render_result(result) emits its JSON, and export_result(result, path) writes it to a new file outside the bundle. The authority is BENCH’s versioned JSON Schema, copied with source hashes under results/contracts/v1/. This draft is not a released wire API. Generated candidate types are authoring aids only; they do not parse or establish trust in arbitrary common JSON.

from helia_estimator.api import observations, render_result, export_result
result = load_result(Path("results/my_case"))
view = observations(result)
print(render_result(result))
export_result(result, Path("portable-draft.json"))

The mapping retains NPU counters/traffic, compiler estimates, every recorded energy mode and retained memory facts. It preserves integer counter precision above 2**53 (including retained integer clock and compiler tokens), explicitly converts total energy from mJ to uJ, and keeps the native value/unit/basis alongside each energy observation. Modeled energy is never relabeled rail measurement. CPU runner diagnostics remain native detail. Recorded idle cycles retain a derived basis (total - active), simulator origin, and pointers to retained operands; they are not labeled an independent counter. If conversion overflows the supported finite numeric representation, the observation is unavailable with a reason; its finite native value/unit/basis remain in extensions. Empty native labels map to nullable draft fields while their original values remain in native detail. New namespaced metric IDs are not automatic cross-producer comparison contracts.

Unknown identities, engine versions, input/state protocol, acquisition windows and sample counts are not inferred. Missing metrics have unavailable reasons. Native JSON source pointers and manifest-bound artifact references remain available. Configured NPU clock fields map to the explicit npu clock domain; positive fractional Hz and exact integer clock tokens are retained without rounding. Missing domains stay unknown for consumers of other producers; rate or list position never establishes a domain. Nonpositive native clock facts remain explicitly unrepresented in extensions with a limitation. Native integrity, sanity/overflow, energy availability and correctness remain separate. A retained nonzero FVP exit remains a failed outcome; an incomplete marker cannot establish one.

Terminal window
helx results inspect results/my_case --format json
helx results show results/my_case --format json
helx results export results/my_case --format json --output portable-draft.json

inspect emits one draft inspection document. Verified evidence contains the result view and returns exit 0, even when a retained native outcome is failed. Incomplete, unverified, invalid or unsupported evidence contains issues and a null result, returning exit 1. The Python equivalent is render_inspection(inspect_result(path)).

show emits the common view only after native verification. export writes the same JSON to a new file outside the bundle and reports the destination on stderr. Both share the Python reader and mapping. They refuse rejected evidence with exit 1; unknown CLI options/formats are usage errors, exit 2. JSON is the format defined by this initial draft binding. Existing helx show --json remains native schema 4, and existing helx export retains its table/CSV behavior.

Artifact IDs are relative and stable within the result; artifact locations are explicit file URIs for the bundle read by the producer, independent of the export directory. Locations are resolution context, not content identity or permission to fetch files. Native digests remain unchanged if the bundle is moved and reread.

The exact adopted BENCH source is commit aa3ab6c791e36d4086f67dc663971e24ada2c8a5, result-schema SHA f919b64c823d1c44df75b186cb3be890fcbf5cc6b3dbfae5b35f8e3d686acf41; ORIGIN.json records both result/inspection schema digests. The mapping version is helx-schema4-draft4. The existing 1.0-draft wire label does not identify every draft revision; consumers should pin schema and mapping identities. Older integer-Hz/unknown-domain draft documents remain valid under the additive schema. The new domain property requires that updated consumer schema; an older closed draft schema will reject it. Consumers should retain their current HELX pin until they adopt this schema/mapping pair. Native JSON/CSV and verified-reader changes in 0.12.0 are documented in API migration.

The public DraftResultView, DraftObservation, DraftClock and DraftClockDomain names refer to the existing typed authoring/output values. observations(result) returns a new view each time; editing it does not edit verified native bytes. These dataclasses/enums are not validators for arbitrary common JSON and do not establish native trust or a released stable wire API.

The coordinated work was delivered through #136, against the contract coordinated in BENCH #111. The portable mapping is independent of the execution API above; no shared package or accepted generator is introduced.

HELX owns simulation and retained evidence; BENCH owns orchestration and comparison eligibility.

from helia_estimator.api import load_build, run_build
runner = load_build("local/kws-build")
outcome = run_build(runner, "results/kws-from-build")
# Equivalent: runner.run("results/another-run")
print(outcome.stage, outcome.results_dir)
Terminal window
helx run-build local/kws-build -o results/kws-from-build
helx validate bundle results/kws-from-build

This executes the retained image with the installed FVP. Vela, runtime code preparation and native compilation are skipped; missing simulator/licence setup is an actionable error, never an implicit rebuild. Install the appropriate simulator with helx setup first. The original build environment, model/config files and source checkouts are not required. Captured platform, SoC, energy and target inputs determine the result; current installed platform definitions are not substituted.

The destination must be new and outside the source record. Each invocation writes an ordinary schema-4 result bundle, including PMU sanity, analysis, modeled energy and debug evidence. A manifest-bound artifacts/compiled_build_reference.json identifies the record checksum and executed image/model hashes and original build software. The retained Vela INI and compiler version describe the actual compilation, even on a newer host. These are retained byte identities, not the future semantic intent/build/observation hashes. The source record is verified before and after execution; interrupted or failed outputs remain incomplete.

Retained-build execution uses the captured protocol, tensor inputs and comparison policy. It retains numerical comparisons when references were supplied at build time; it does not change the compiled model, input samples, warmup/repetition counts or comparison policy at replay time. To compare captured outputs against another reference without running again, use verify_outputs. A retained build is an executable artifact: only run records from a trusted producer.

LiteRT, heliaRT and heliaAOT support indexed measured invocations with typed state and warmup controls. See Acquisition lifecycle for YAML, replay behavior, unsupported routes and counter scope.

from helia_estimator.api import AcquisitionPolicy, ExperimentFvp, StatePolicy
fvp = ExperimentFvp(
enabled=True,
runtime_engine="litert",
acquisition=AcquisitionPolicy(state=StatePolicy.CARRY, warmup_invokes=2, measured_invokes=3),
)

acquisition_facts(load_result(path)) returns verified AcquisitionFacts, or None for non-executed stages. The frozen facts expose state, warmup_invokes, measured_invokes, call_once_present, initialization, and timing_scope. All runtimes use the same acquisition protocol. AOT records initialization before measurement and null CALL_ONCE; interpreters record allocation/first-invoke initialization with their observed CALL_ONCE boolean.

See Tensor inputs and numerical verification for fixed input bytes, per-sample output artifacts and explicit reference/tolerance policies.

Portable result identity and measurement meaning

Section titled “Portable result identity and measurement meaning”

observations(result) and render_result(result) project a verified native bundle without probing installed runtimes or reopening its original model path. The portable mapping version is helx-schema4-draft4.

Portable field Retained meaning
workload.model_identity SHA-256 of the source-model bytes observed before compilation and checked again before publication; not the Vela/runner model or a semantic graph hash
workload.state_policy Observed carry or single model-instance lifecycle; unavailable before execution
engine.version for heliaAOT The provider release that generated the module, from generator_version; native linked-runtime version remains unavailable
engine.version for LiteRT/heliaRT Captured linked-runtime release, when established by build evidence
memory.arena_capacity.v1 Configured capacity in bytes, scope arena_capacity, statistic size; distinct from allocator-reported use and model storage

The helia-estimator extension retains identity_facts with native source pointers, explicit version bases and reasons for unavailable identities. requested_acquisition keeps the resolved build request separately from the observed acquisition: an automatic warmup request remains null, not zero. A conflicting explicit request and observed lifecycle/count makes reading fail. Each indexed counter observation covers one measured inference; exact batch statistics remain a separately labeled native extension. Energy observations retain their modeled basis and mode, independently of simulated counters.

A model digest identifies bytes, not equivalent precision, placement, timing scope or target behavior. The source digest alone does not authenticate the model, and runner-model/command-stream identities are distinct. Unknown target identities stay unavailable. Retained tensor inputs have a separate storage identity, described in tensor verification. BENCH owns comparison eligibility; matching names or hashes do not automatically qualify a comparison.

from helia_estimator.api import load_result, simulation_target_identity
identity = simulation_target_identity(load_result("results/my_run"))
print(identity.value or identity.unavailable_reason)

The helx.simulation-target-config/v1:sha256: namespace identifies retained configured simulation facts, never a physical target or HPX equivalent. The portable draft4 mapping uses this value as target.identity and retains the preimage, source pointers and unavailable reason in its namespaced extension.

artifacts/simulation_target_build.json captures explicit target settings from the configured build and binds the runner image. Retained-build replay carries that evidence forward. At launch, artifacts/simulation_target.json binds those settings to the actual simulator executable digest and configured launch facts. The reader only reads manifest-bound artifacts; it performs no host discovery.

The canonical hash object includes:

  • schema, Corstone subsystem, positive whole-Hz configured npu_clock_hz, and simulator_sha256.
  • build_settings: CPU/device and NPU family/architecture definitions, platform target directory, register/IRQ/UART/timing-adapter addresses, firmware SYSTEM_CORE_CLOCK, MAC count, effective memory mode and cache capacity, external-region routing, SRAM/EXT burst controls, and all 13 timing-adapter register settings for each of two ports. The exact closed key set is BUILD_KEYS in results/simulation_target.py.
  • launch_parameters: configured NPU MAC count, explicit U85 SRAM hash, and the target profile’s additional hardware parameters (U55/U65 TCM sizes).

The SHA-256 preimage is UTF-8 JSON, recursively sorted keys, compact separators, ensure_ascii=False, no nonfinite numbers. It is available as canonical_json. Build-setting strings preserve their recorded CMake spelling; different spellings may conservatively differ. Paths, model/runtime/kernel identities, acquisition, reference policy, output measurements and energy coefficients are excluded. The image digest binds the receipt to evidence but is not in the target hash.

Effective C/C++/assembler flags from the CMake cache are retained and checked, including global and selected-build-type flags. Only ordinary optimization/debug flags and -DNDEBUG are admitted; unclassified flags (including target macro overrides or response files) leave identity unavailable. Missing compiler-flag evidence also leaves identity unavailable, after checking all available facts for contradictions. Build arguments admit only no arguments or --parallel N with positive N; persisted CMake project-include and make-rule override hooks also leave identity unavailable. Receipts are read and written as UTF-8. Launch MAC/TCM/mask settings must be nonnegative integers, independent of optional summary cross-checks.

Missing receipts, implicit build defaults, unclassified custom CMake/FVP arguments, missing image bindings and non-whole-Hz NPU rates leave identity unavailable. Malformed or contradictory evidence used to establish an identity invalidates the result. When launch evidence is absent or explicitly unavailable, no target identity is asserted; the identity reader does not certify unused build receipts. Older captures are not hydrated from current defaults. Separate simulator binaries conservatively produce separate identities, including across hosts.

SYSTEM_CORE_CLOCK is firmware configuration, not a measured CPU clock. Portable NPU clocks retain configured origin/domain. This identity is not full executable or environment equivalence (including simulator shared libraries), timing qualification, silicon identity, numerical correctness or energy calibration. BENCH owns comparison admission and any future cross-product target equivalence.

The identity is a description of HELX’s supported build/launch configuration, with safeguards against accidental misconfiguration. Cached toolchains that differ from the active runtime environment leave identity unavailable. This is not an attestation of arbitrary toolchains, modified receipts, or executable semantics. HELX supplies facts and unknown reasons; BENCH owns comparison policy and whether those facts suffice for a particular benchmark.