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 without executing
Section titled “Validate without executing”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.
Collect native tables
Section titled “Collect native tables”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 a one-off case
Section titled “Prepare a one-off case”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 Pathfrom 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.
Run one experiment
Section titled “Run one experiment”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 Pathfrom 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.
Public types and actionable errors
Section titled “Public types and actionable errors”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.
Freeze and replay local inputs
Section titled “Freeze and replay local inputs”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:
uv run helx freeze -c case.yaml --model local/model.tflite -o local/case.plan.jsonuv run helx validate frozen local/case.plan.json --jsonuv run helx replay local/case.plan.jsonThe 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
estimatestill supports URLs and its cache. - Per-layer fenced execution with Vela requires effective
fvp.enabled: trueso 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_argsandfvp_argsare refused because they can introduce undeclared external files.build_argsmay only be["--parallel", "N"]with positiveN; ordinary build defaults remain bounded. - AOT overrides support built-in
platform.nameandmodule.name/prefixidentifiers. 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.
Retain a compiled build
Section titled “Retain a compiled build”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.uv run helx build local/case.plan.json --output local/case-builduv run helx validate build local/case-build --jsonThe 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.
Select runtime and kernels
Section titled “Select runtime and kernels”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.
Inspect static runner support
Section titled “Inspect static runner support”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)helx supporthelx support --json # One document; no host probe, model fetch, build or run.Read saved results
Section titled “Read saved results”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 Pathfrom 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.
Inspect retained run identity
Section titled “Inspect retained run identity”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.runtimeif 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.
Discover and copy saved visualizations
Section titled “Discover and copy saved visualizations”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.
helx results artifacts results/kws_bus_trace --format jsonhelx results export-artifact results/kws_bus_trace \ --artifact artifacts/bus_trace_perfetto.json --output saved-timeline.jsonartifacts 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.
Draft portable mapping
Section titled “Draft portable mapping”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.
Draft result commands
Section titled “Draft result commands”helx results inspect results/my_case --format jsonhelx results show results/my_case --format jsonhelx results export results/my_case --format json --output portable-draft.jsoninspect 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.
Run a retained build
Section titled “Run a retained build”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)helx run-build local/kws-build -o results/kws-from-buildhelx validate bundle results/kws-from-buildThis 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.
Control acquisition lifecycle
Section titled “Control acquisition lifecycle”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.
Simulation target configuration identity
Section titled “Simulation target configuration identity”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, Corstonesubsystem, positive whole-Hz configurednpu_clock_hz, andsimulator_sha256.build_settings: CPU/device and NPU family/architecture definitions, platform target directory, register/IRQ/UART/timing-adapter addresses, firmwareSYSTEM_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 isBUILD_KEYSinresults/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.