tan-cli 0.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tan/__init__.py +0 -0
- tan/__main__.py +120 -0
- tan/cli.py +939 -0
- tan/command_table.py +265 -0
- tan/commands/__init__.py +8 -0
- tan/commands/bootstrap_cmd.py +4856 -0
- tan/commands/bootstrap_patches.py +164 -0
- tan/commands/bootstrap_toolchain_fetch.py +155 -0
- tan/commands/bootstrap_toolchain_pin.py +36 -0
- tan/commands/build/__init__.py +1 -0
- tan/commands/build/configure_inputs.py +277 -0
- tan/commands/build/execute.py +1852 -0
- tan/commands/build/host_python.py +59 -0
- tan/commands/build/link_stale.py +78 -0
- tan/commands/build/manifest.py +428 -0
- tan/commands/build/materialise.py +81 -0
- tan/commands/build/token_substitution.py +381 -0
- tan/commands/build/toolchain.py +482 -0
- tan/commands/build/user_defines_wipe.py +115 -0
- tan/commands/build/workspace_patches.py +65 -0
- tan/commands/build_cmd.py +2236 -0
- tan/commands/build_output.py +331 -0
- tan/commands/clean_cmd.py +1183 -0
- tan/commands/completion_cmd.py +786 -0
- tan/commands/debug_config_cmd.py +2425 -0
- tan/commands/diff_cmd.py +940 -0
- tan/commands/doctor_cmd.py +4767 -0
- tan/commands/examples_cmd.py +644 -0
- tan/commands/explain_cmd.py +1319 -0
- tan/commands/faultdecode_cmd.py +931 -0
- tan/commands/flash_atoc_guard.py +240 -0
- tan/commands/flash_cmd.py +5731 -0
- tan/commands/flash_mram_guard.py +150 -0
- tan/commands/flash_ram.py +578 -0
- tan/commands/flash_raw.py +583 -0
- tan/commands/generate_cmd.py +1456 -0
- tan/commands/image_cmd.py +753 -0
- tan/commands/init_cmd.py +1740 -0
- tan/commands/inspect_cmd.py +377 -0
- tan/commands/kconfig_cmd.py +734 -0
- tan/commands/model_cmd.py +1514 -0
- tan/commands/model_device_cmd.py +351 -0
- tan/commands/model_device_live.py +234 -0
- tan/commands/model_host_cmd.py +430 -0
- tan/commands/model_zoo_cmd.py +363 -0
- tan/commands/monitor_actions.py +125 -0
- tan/commands/monitor_cmd.py +626 -0
- tan/commands/monitor_session.py +505 -0
- tan/commands/new_som_cmd.py +1522 -0
- tan/commands/pinmux_cmd.py +568 -0
- tan/commands/presets_cmd.py +1123 -0
- tan/commands/probe_cmd.py +421 -0
- tan/commands/reset_cmd.py +354 -0
- tan/commands/reset_confirm.py +168 -0
- tan/commands/run_cmd.py +637 -0
- tan/commands/scaffold_cmd.py +490 -0
- tan/commands/sdk_cmd.py +1723 -0
- tan/commands/size_cmd.py +1020 -0
- tan/commands/support_bundle_cmd.py +1073 -0
- tan/commands/trace_cmd.py +341 -0
- tan/commands/validate_cmd.py +2009 -0
- tan/commands/west_forward_cmd.py +736 -0
- tan/commands/workspace_patch_check.py +296 -0
- tan/core/__init__.py +1 -0
- tan/core/alp_conf_pregen.py +100 -0
- tan/core/artifact_provenance.py +240 -0
- tan/core/atoc_guard.py +276 -0
- tan/core/atoc_guard_messages.py +232 -0
- tan/core/atoc_replacement.py +118 -0
- tan/core/atomic_write.py +178 -0
- tan/core/board_context.py +351 -0
- tan/core/board_diagnostic.py +111 -0
- tan/core/board_files.py +221 -0
- tan/core/board_validator.py +542 -0
- tan/core/board_validator_compat.py +196 -0
- tan/core/board_validator_run.py +129 -0
- tan/core/board_validator_schema.py +186 -0
- tan/core/board_validator_skew.py +46 -0
- tan/core/board_yaml_edit.py +295 -0
- tan/core/board_yaml_pos.py +118 -0
- tan/core/bootstrap.py +2572 -0
- tan/core/build_plan.py +625 -0
- tan/core/consent.py +104 -0
- tan/core/console_filter.py +245 -0
- tan/core/debug_launch.py +1803 -0
- tan/core/devicetree_lint.py +168 -0
- tan/core/dir_removal.py +229 -0
- tan/core/doctor_git.py +172 -0
- tan/core/doctor_libraries.py +253 -0
- tan/core/doctor_render.py +185 -0
- tan/core/doctor_scope.py +66 -0
- tan/core/doctor_setools.py +237 -0
- tan/core/doctor_stale.py +180 -0
- tan/core/document_guards.py +723 -0
- tan/core/dp_id.py +97 -0
- tan/core/elf_load.py +63 -0
- tan/core/error_catalog.py +171 -0
- tan/core/example_catalog.py +499 -0
- tan/core/example_facets.py +151 -0
- tan/core/faultdecode.py +521 -0
- tan/core/flash_plan.py +2501 -0
- tan/core/flow_d_report.py +381 -0
- tan/core/flow_d_uptime.py +164 -0
- tan/core/fs_confine.py +130 -0
- tan/core/global_flags.py +230 -0
- tan/core/host_python.py +210 -0
- tan/core/image_bundle.py +247 -0
- tan/core/inert.py +147 -0
- tan/core/jlink_binary.py +128 -0
- tan/core/jlink_probe.py +481 -0
- tan/core/jsonc_splice.py +404 -0
- tan/core/launch_provenance.py +257 -0
- tan/core/link_refusal.py +76 -0
- tan/core/metadata_schema.py +376 -0
- tan/core/model_check.py +227 -0
- tan/core/model_device.py +449 -0
- tan/core/model_doctor.py +198 -0
- tan/core/model_host.py +48 -0
- tan/core/model_list.py +112 -0
- tan/core/model_zoo.py +353 -0
- tan/core/module_template.py +340 -0
- tan/core/mram_link.py +87 -0
- tan/core/new_som_ospi.py +94 -0
- tan/core/os_class.py +159 -0
- tan/core/patch_paths.py +87 -0
- tan/core/pending.py +40 -0
- tan/core/plain_zephyr_plan.py +138 -0
- tan/core/plan_exec.py +373 -0
- tan/core/plan_tokens.py +600 -0
- tan/core/probe.py +155 -0
- tan/core/probe_plan.py +237 -0
- tan/core/proxy.py +168 -0
- tan/core/publish.py +31 -0
- tan/core/python_floor.py +178 -0
- tan/core/ram_run.py +694 -0
- tan/core/ram_watch.py +231 -0
- tan/core/raw_write.py +160 -0
- tan/core/reset_plan.py +56 -0
- tan/core/run.py +161 -0
- tan/core/scaffold.py +2203 -0
- tan/core/scaffold_selftest_identity.py +225 -0
- tan/core/sdk_default_registry.py +565 -0
- tan/core/sdk_discovery.py +996 -0
- tan/core/sdk_removal.py +465 -0
- tan/core/serial_actions.py +175 -0
- tan/core/serial_capture.py +185 -0
- tan/core/serial_url.py +45 -0
- tan/core/setools.py +608 -0
- tan/core/setools_scratch.py +243 -0
- tan/core/shapes.py +194 -0
- tan/core/size.py +610 -0
- tan/core/som_buildability.py +204 -0
- tan/core/som_schema_version.py +62 -0
- tan/core/subprocess_env.py +201 -0
- tan/core/sysbuild_image.py +94 -0
- tan/core/system_manifest.py +827 -0
- tan/core/text_layout.py +73 -0
- tan/core/timestamp.py +182 -0
- tan/core/tool_lookup.py +383 -0
- tan/core/toolchain_pin.py +117 -0
- tan/core/toolchain_provision.py +1224 -0
- tan/core/trusted_path.py +134 -0
- tan/core/uboot_breakin.py +154 -0
- tan/core/uri_reference.py +415 -0
- tan/core/user_defines.py +152 -0
- tan/core/venv.py +332 -0
- tan/core/west_patches.py +143 -0
- tan/core/west_workspace_refusal.py +159 -0
- tan/core/zephyr_env.py +124 -0
- tan/env.py +185 -0
- tan/envelope.py +647 -0
- tan/exit_codes.py +12 -0
- tan/lazy_typer.py +185 -0
- tan/model/__init__.py +2 -0
- tan/model/_gen_fixture.py +132 -0
- tan/model/adapters/__init__.py +116 -0
- tan/model/adapters/cpu.py +26 -0
- tan/model/adapters/deepx.py +94 -0
- tan/model/adapters/drpai.py +242 -0
- tan/model/adapters/ethos_u.py +951 -0
- tan/model/adapters/executorch.py +55 -0
- tan/model/analyze.py +694 -0
- tan/model/build.py +304 -0
- tan/model/check.py +682 -0
- tan/model/manifest.py +625 -0
- tan/model/measure.py +216 -0
- tan/model/npu_ops_validate.py +130 -0
- tan/model/package.py +125 -0
- tan/model/perf.py +635 -0
- tan/model/perf_apply.py +721 -0
- tan/model/prep.py +193 -0
- tan/model/sram_fit.py +502 -0
- tan/model/targets.py +374 -0
- tan/model/tensorio.py +287 -0
- tan/net.py +73 -0
- tan/output_format.py +213 -0
- tan/planner/__init__.py +219 -0
- tan/planner/aperture.py +202 -0
- tan/planner/buildplan.py +1092 -0
- tan/planner/camera_owner.py +192 -0
- tan/planner/cameras.py +132 -0
- tan/planner/carveout.py +755 -0
- tan/planner/cli.py +274 -0
- tan/planner/headers.py +315 -0
- tan/planner/kconfig.py +2435 -0
- tan/planner/kconfig_symbols.py +473 -0
- tan/planner/libraries.py +677 -0
- tan/planner/link_target.py +237 -0
- tan/planner/linux_ownership.py +216 -0
- tan/planner/loader.py +1525 -0
- tan/planner/manifest.py +116 -0
- tan/planner/memory.py +161 -0
- tan/planner/memregion.py +56 -0
- tan/planner/models.py +533 -0
- tan/planner/orchestrator.py +874 -0
- tan/planner/ownership.py +203 -0
- tan/planner/partition.py +1160 -0
- tan/planner/paths.py +32 -0
- tan/planner/plain_slice.py +139 -0
- tan/planner/project_emit/__init__.py +60 -0
- tan/planner/project_emit/bom_netlist.py +375 -0
- tan/planner/project_emit/dts.py +580 -0
- tan/planner/project_emit/hw_info.py +221 -0
- tan/planner/project_emit/native_sim.py +136 -0
- tan/planner/project_emit/west_libs.py +213 -0
- tan/planner/project_loader.py +264 -0
- tan/planner/sdk_capability.py +203 -0
- tan/planner/sdk_compat.py +395 -0
- tan/planner/secure.py +350 -0
- tan/planner/slugs.py +394 -0
- tan/planner/som_metadata.py +325 -0
- tan/planner/strict_loaders.py +131 -0
- tan/planner/template.py +1037 -0
- tan/planner/template_pins.py +535 -0
- tan/planner/template_rewrite.py +714 -0
- tan/planner/topology.py +208 -0
- tan/planner/validate.py +365 -0
- tan/planner/whole_device_alias.py +67 -0
- tan/planner/zephyr_board.py +3510 -0
- tan/planner_cli.py +97 -0
- tan/planner_emit.py +617 -0
- tan/planner_root.py +119 -0
- tan/soc_ref.py +58 -0
- tan/templates/__init__.py +34 -0
- tan/templates/vendored/MANIFEST.md +1862 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/CMakeLists.txt +8 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/README.md +112 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/board.yaml +38 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/boards/native_sim_native_64.conf +11 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/boards/native_sim_native_64.overlay +29 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/prj.conf +5 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/src/main.c +266 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/testcase.yaml +53 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/CMakeLists.txt +8 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/README.md +112 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/board.yaml +38 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/boards/native_sim_native_64.conf +11 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/boards/native_sim_native_64.overlay +29 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/prj.conf +5 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/src/main.c +266 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/testcase.yaml +53 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/CMakeLists.txt +10 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/README.md +75 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/board.yaml +38 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/prj.conf +8 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/src/cold_chain.c +371 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/src/cold_chain.h +178 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/src/main.c +373 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/testcase.yaml +62 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/CMakeLists.txt +10 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/README.md +75 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/board.yaml +37 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/prj.conf +8 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/src/cold_chain.c +371 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/src/cold_chain.h +178 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/src/main.c +373 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/testcase.yaml +62 -0
- tan/templates/vendored/iot/E1M-AEN801/CMakeLists.txt +14 -0
- tan/templates/vendored/iot/E1M-AEN801/README.md +103 -0
- tan/templates/vendored/iot/E1M-AEN801/board.yaml +69 -0
- tan/templates/vendored/iot/E1M-AEN801/prj.conf +15 -0
- tan/templates/vendored/iot/E1M-AEN801/src/cc3501e_bridge.c +196 -0
- tan/templates/vendored/iot/E1M-AEN801/src/cc3501e_bridge.h +173 -0
- tan/templates/vendored/iot/E1M-AEN801/src/main.c +262 -0
- tan/templates/vendored/iot/E1M-AEN801/testcase.yaml +68 -0
- tan/templates/vendored/minimal/E1M-AEN801/CMakeLists.txt +8 -0
- tan/templates/vendored/minimal/E1M-AEN801/README.md +108 -0
- tan/templates/vendored/minimal/E1M-AEN801/board.yaml +32 -0
- tan/templates/vendored/minimal/E1M-AEN801/prj.conf +8 -0
- tan/templates/vendored/minimal/E1M-AEN801/src/main.c +111 -0
- tan/templates/vendored/minimal/E1M-AEN801/testcase.yaml +49 -0
- tan/templates/vendored/minimal/E1M-V2N101/CMakeLists.txt +8 -0
- tan/templates/vendored/minimal/E1M-V2N101/README.md +108 -0
- tan/templates/vendored/minimal/E1M-V2N101/board.yaml +32 -0
- tan/templates/vendored/minimal/E1M-V2N101/prj.conf +8 -0
- tan/templates/vendored/minimal/E1M-V2N101/src/main.c +111 -0
- tan/templates/vendored/minimal/E1M-V2N101/testcase.yaml +49 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/CMakeLists.txt +8 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/README.md +120 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/board.yaml +57 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/boards/native_sim_native_64.overlay +40 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/CMakeLists.txt +13 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/main.c +171 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/prj.conf +17 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/testcase.yaml +23 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/prj.conf +22 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/src/main.c +185 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/testcase.yaml +26 -0
- tan/templates/vendored/sensor/E1M-AEN801/CMakeLists.txt +8 -0
- tan/templates/vendored/sensor/E1M-AEN801/README.md +112 -0
- tan/templates/vendored/sensor/E1M-AEN801/board.yaml +57 -0
- tan/templates/vendored/sensor/E1M-AEN801/boards/native_sim_native_64.conf +10 -0
- tan/templates/vendored/sensor/E1M-AEN801/boards/native_sim_native_64.overlay +28 -0
- tan/templates/vendored/sensor/E1M-AEN801/prj.conf +5 -0
- tan/templates/vendored/sensor/E1M-AEN801/src/main.c +186 -0
- tan/templates/vendored/sensor/E1M-AEN801/testcase.yaml +52 -0
- tan/templates/vendored/sensor/E1M-V2N101/CMakeLists.txt +8 -0
- tan/templates/vendored/sensor/E1M-V2N101/README.md +112 -0
- tan/templates/vendored/sensor/E1M-V2N101/board.yaml +57 -0
- tan/templates/vendored/sensor/E1M-V2N101/boards/native_sim_native_64.conf +10 -0
- tan/templates/vendored/sensor/E1M-V2N101/boards/native_sim_native_64.overlay +28 -0
- tan/templates/vendored/sensor/E1M-V2N101/prj.conf +5 -0
- tan/templates/vendored/sensor/E1M-V2N101/src/main.c +186 -0
- tan/templates/vendored/sensor/E1M-V2N101/testcase.yaml +52 -0
- tan/version.py +102 -0
- tan_cli-0.7.0.dist-info/METADATA +125 -0
- tan_cli-0.7.0.dist-info/RECORD +330 -0
- tan_cli-0.7.0.dist-info/WHEEL +5 -0
- tan_cli-0.7.0.dist-info/entry_points.txt +2 -0
- tan_cli-0.7.0.dist-info/top_level.txt +1 -0
tan/core/model_doctor.py
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Pure logic for `tan model doctor`: one row per compiler-backend toolchain.
|
|
3
|
+
|
|
4
|
+
Under alp-sdk ADR-0028 the model engine (`tan.model.build`, the adapters
|
|
5
|
+
under `tan.model.adapters`) moved OUT of alp-sdk and into tan, so tan is now
|
|
6
|
+
the customer's only diagnostic surface for the NPU compiler toolchains
|
|
7
|
+
`tan model build` shells out to. When a compile silently produces nothing,
|
|
8
|
+
this is what turns "nothing happened" into "`dxcom` is not on PATH; it is
|
|
9
|
+
license-gated and Linux-only" -- that is the whole job.
|
|
10
|
+
|
|
11
|
+
**This module never spawns anything and never touches `metadata/`.** Every
|
|
12
|
+
fact it renders comes from an already-computed `available`/`version` pair the
|
|
13
|
+
caller (`tan.commands.model_cmd`) obtained by calling each registered
|
|
14
|
+
adapter's own `is_available()` -- `shutil.which(...)`/an env-var directory
|
|
15
|
+
check in every adapter today (`tan/model/adapters/{cpu,ethos_u,drpai,
|
|
16
|
+
deepx,executorch}.py`) -- and, for the one backend with a non-spawning
|
|
17
|
+
version probe (`ethos_u`'s `importlib.metadata` read), a version string. No
|
|
18
|
+
function here calls `subprocess.run`/`Popen`, imports an adapter, or reads a
|
|
19
|
+
SoM/SoC file: `doctor` must be safe to run on a completely broken host, and
|
|
20
|
+
"safe" here specifically means it cannot invoke a compiler as a side effect
|
|
21
|
+
of asking whether one is installed.
|
|
22
|
+
|
|
23
|
+
`data.optional[]` (`optional_row`) is the one row shape that is NOT a
|
|
24
|
+
"can tan compile for this backend" verdict: an unavailable OPTIONAL
|
|
25
|
+
prerequisite -- today the vendor vela `.ini` a licensed customer points
|
|
26
|
+
`ALP_VELA_CONFIG` at -- is not a fault and must not read as one. It is kept out
|
|
27
|
+
of `backends[]` for that reason; see `optional_row`.
|
|
28
|
+
|
|
29
|
+
`registry_backends` still reads the *shape* of `tan.model.build._ADAPTERS`
|
|
30
|
+
(which backends the registry actually declares, in what order) rather than
|
|
31
|
+
hard-coding the four-entry list a snapshot of that registry happens to be
|
|
32
|
+
today -- so a fifth adapter (or a genuinely distinct `"executorch"` backend,
|
|
33
|
+
should one ever replace `ExecutorchAdapter`'s current `backend == "cpu"`)
|
|
34
|
+
shows up here automatically instead of silently going unprobed.
|
|
35
|
+
"""
|
|
36
|
+
from __future__ import annotations
|
|
37
|
+
|
|
38
|
+
from dataclasses import dataclass
|
|
39
|
+
|
|
40
|
+
from tan.model.adapters import CompilerAdapter
|
|
41
|
+
|
|
42
|
+
#: The external binary/script each backend's adapter actually spawns to
|
|
43
|
+
#: compile a model -- `None` for a backend with no external tool at all.
|
|
44
|
+
#: `cpu`'s two adapters (`CpuAdapter`, `ExecutorchAdapter`) are both pure
|
|
45
|
+
#: byte passthroughs (`tan/model/adapters/cpu.py`,
|
|
46
|
+
#: `tan/model/adapters/executorch.py`) -- neither spawns anything, so `cpu`
|
|
47
|
+
#: reports no tool.
|
|
48
|
+
#:
|
|
49
|
+
#: `drpai`'s entry names the vendor TUTORIAL SCRIPT the adapter invokes
|
|
50
|
+
#: (`compile_onnx_model_quant.py`, `tan/model/adapters/drpai.py`'s own
|
|
51
|
+
#: `cmd = ["python3", str(script), ...]`), not the `python3` interpreter --
|
|
52
|
+
#: `python3` is present on essentially every host and naming it would tell a
|
|
53
|
+
#: customer nothing they can act on; the toolchain checkout the script lives
|
|
54
|
+
#: inside (`$ALP_DRPAI_TVM_HOME/tutorials/...`) is the thing that is
|
|
55
|
+
#: actually absent.
|
|
56
|
+
BACKEND_TOOLS: dict[str, str | None] = {
|
|
57
|
+
"cpu": None,
|
|
58
|
+
"ethos_u": "vela",
|
|
59
|
+
"drpai": "compile_onnx_model_quant.py",
|
|
60
|
+
"deepx_dxm1": "dxcom",
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
#: Actionable unavailability guidance, keyed by backend -- surfaced ONLY when
|
|
64
|
+
#: that backend's `available` is False (`backend_row` below). Each string is
|
|
65
|
+
#: derived from what that backend's adapter actually checks in
|
|
66
|
+
#: `is_available()`; never an install instruction this repo cannot support.
|
|
67
|
+
_UNAVAILABLE_REASONS: dict[str, str] = {
|
|
68
|
+
# VelaAdapter.is_available(): shutil.which("vela") is not None
|
|
69
|
+
# (tan/model/adapters/ethos_u.py). This repo's own pinned path is the
|
|
70
|
+
# `model-compile` extra (`pyproject.toml`: `ethos-u-vela>=3.9`), which
|
|
71
|
+
# carries the floor -- name it instead of a bare `pip install
|
|
72
|
+
# ethos-u-vela` that could drift from that pin.
|
|
73
|
+
"ethos_u": "vela not on PATH; pip install tan-cli[model-compile]",
|
|
74
|
+
# deepx_dxm1's row is NOT `DeepxAdapter.is_available()` (see
|
|
75
|
+
# `_deepx_dxm1_status` in `tan.commands.model_cmd`): that adapter method
|
|
76
|
+
# ORs in a second arm -- `ALP_DEEPX_SDK_HOME` pointing at a directory --
|
|
77
|
+
# that `DeepxAdapter.compile()` never reads (it always shells the bare
|
|
78
|
+
# `dxcom` off PATH), so gating on it reported this row green on hosts
|
|
79
|
+
# where the next `model build` immediately raised `FileNotFoundError:
|
|
80
|
+
# 'dxcom'`. This default reason covers the case neither PATH nor the env
|
|
81
|
+
# var is set; the ALP_DEEPX_SDK_HOME-set-but-no-dxcom case gets its own
|
|
82
|
+
# caveat reason from `_deepx_dxm1_status`, passed through `backend_row`'s
|
|
83
|
+
# `reason` override below. Its module doc: the dx-com wheel "is not
|
|
84
|
+
# redistributable" and is "verified -- Linux x86_64, CPython 3.12" only,
|
|
85
|
+
# so "license-gated, Linux-only" is what a customer needs to know before
|
|
86
|
+
# chasing a `pip install` that does not exist for them.
|
|
87
|
+
"deepx_dxm1": "dxcom not on PATH; license-gated, Linux-only",
|
|
88
|
+
# drpai's row is likewise NOT the bare `DrpaiAdapter.is_available()`
|
|
89
|
+
# (see `_drpai_status` in `tan.commands.model_cmd`): that adapter method
|
|
90
|
+
# only checks `ALP_DRPAI_TVM_HOME` names a directory, never that the
|
|
91
|
+
# vendor tutorial script `DrpaiAdapter.compile()` actually spawns
|
|
92
|
+
# (`tutorials/compile_onnx_model_quant.py`) exists under it, so an
|
|
93
|
+
# unpacked-but-unbuilt toolchain tree reported green here too. This
|
|
94
|
+
# default reason covers the case the env var isn't set at all; the
|
|
95
|
+
# var-set-but-script-missing case gets its own caveat reason from
|
|
96
|
+
# `_drpai_status`. Its module doc: the toolchain is "large and
|
|
97
|
+
# account-/source-gated ... so it is NOT bundled" -- there is no
|
|
98
|
+
# pip/apt install to name here either.
|
|
99
|
+
"drpai": (
|
|
100
|
+
"ALP_DRPAI_TVM_HOME not set to a built rzv_drp-ai_tvm install; "
|
|
101
|
+
"account-gated toolchain, not bundled"
|
|
102
|
+
),
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@dataclass(frozen=True)
|
|
107
|
+
class BackendRow:
|
|
108
|
+
"""One backend's toolchain verdict -- the wire shape `tan model doctor`
|
|
109
|
+
reports per backend under `data.backends[]`."""
|
|
110
|
+
|
|
111
|
+
backend: str
|
|
112
|
+
tool: str | None
|
|
113
|
+
available: bool
|
|
114
|
+
version: str | None
|
|
115
|
+
reason: str | None
|
|
116
|
+
|
|
117
|
+
def as_dict(self) -> dict[str, object]:
|
|
118
|
+
return {
|
|
119
|
+
"backend": self.backend,
|
|
120
|
+
"tool": self.tool,
|
|
121
|
+
"available": self.available,
|
|
122
|
+
"version": self.version,
|
|
123
|
+
"reason": self.reason,
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def backend_row(
|
|
128
|
+
backend: str, *, available: bool, version: str | None, reason: str | None = None
|
|
129
|
+
) -> BackendRow:
|
|
130
|
+
"""Assemble one row from an ALREADY-PROBED `available`/`version` pair.
|
|
131
|
+
|
|
132
|
+
Pure -- no IO here at all, real or otherwise; the caller did the probing.
|
|
133
|
+
`reason` is populated only when `available` is False (never a "why it
|
|
134
|
+
works" note on a green row); `version` is discarded when `available` is
|
|
135
|
+
False even if the caller passed one in, since an unavailable backend has
|
|
136
|
+
no running toolchain for a version to describe.
|
|
137
|
+
|
|
138
|
+
`reason` is an explicit override for `_UNAVAILABLE_REASONS[backend]`'s
|
|
139
|
+
default, used when the caller's own probe (`_deepx_dxm1_status`/
|
|
140
|
+
`_drpai_status` in `tan.commands.model_cmd`) found a more specific
|
|
141
|
+
situation than "nothing at all is set" -- e.g. the toolchain env var IS
|
|
142
|
+
set but the thing `compile()` actually needs under/via it is still
|
|
143
|
+
absent. `None` (the default) falls back to the generic per-backend
|
|
144
|
+
reason, same as before this parameter existed.
|
|
145
|
+
"""
|
|
146
|
+
fallback_reason = reason if reason is not None else _UNAVAILABLE_REASONS.get(backend)
|
|
147
|
+
return BackendRow(
|
|
148
|
+
backend=backend,
|
|
149
|
+
tool=BACKEND_TOOLS.get(backend),
|
|
150
|
+
available=available,
|
|
151
|
+
version=version if available else None,
|
|
152
|
+
reason=None if available else fallback_reason,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def optional_row(
|
|
157
|
+
backend: str, *, tool: str, available: bool, reason: str | None
|
|
158
|
+
) -> BackendRow:
|
|
159
|
+
"""One OPTIONAL prerequisite's row -- the same five keys as `backend_row`,
|
|
160
|
+
reported under `data.optional[]` rather than `data.backends[]`.
|
|
161
|
+
|
|
162
|
+
A SEPARATE LIST, not a fifth backend row, because the two answer different
|
|
163
|
+
questions and a consumer keyed on `backends[]` reads one row per backend.
|
|
164
|
+
`available: false` there means "tan cannot compile for this backend at
|
|
165
|
+
all"; here it means "this backend works, and a vendor-tuned enhancement is
|
|
166
|
+
not installed". Rendering the second as the first would tell an unlicensed
|
|
167
|
+
customer their toolchain is broken when it is complete and correct: without
|
|
168
|
+
a vendor `.ini` vela uses Arm's own built-in system config, which is what
|
|
169
|
+
the arena/SRAM figures tan reports already describe.
|
|
170
|
+
|
|
171
|
+
`version` is always None: this is a data file, not a toolchain, and there
|
|
172
|
+
is no version of it to probe without reading a customer's proprietary file.
|
|
173
|
+
`reason` is dropped when available, exactly as `backend_row` drops it --
|
|
174
|
+
and it is REQUIRED from the caller when it is not, because
|
|
175
|
+
`_UNAVAILABLE_REASONS[backend]` answers the other question (`ethos_u`'s
|
|
176
|
+
entry is about `vela` not being on PATH) and must never fall through to
|
|
177
|
+
this row."""
|
|
178
|
+
return BackendRow(
|
|
179
|
+
backend=backend,
|
|
180
|
+
tool=tool,
|
|
181
|
+
available=available,
|
|
182
|
+
version=None,
|
|
183
|
+
reason=None if available else reason,
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def registry_backends(adapters: list[CompilerAdapter]) -> list[str]:
|
|
188
|
+
"""Distinct `backend` values across `adapters`, in first-seen registry
|
|
189
|
+
order -- mirrors `build_model`'s own `by_backend` grouping
|
|
190
|
+
(`tan/model/build.py`): a backend with MORE THAN ONE adapter (`cpu`
|
|
191
|
+
today: `CpuAdapter` + `ExecutorchAdapter`, both `backend == "cpu"`)
|
|
192
|
+
reports as one row, not two -- `doctor` answers "can tan compile for
|
|
193
|
+
cpu", not "how many adapter classes register under cpu"."""
|
|
194
|
+
seen: list[str] = []
|
|
195
|
+
for adapter in adapters:
|
|
196
|
+
if adapter.backend not in seen:
|
|
197
|
+
seen.append(adapter.backend)
|
|
198
|
+
return seen
|
tan/core/model_host.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""What the host-tier `tan model prep` / `run` / `ab` need installed
|
|
3
|
+
(tan-cli#1287).
|
|
4
|
+
|
|
5
|
+
Their numeric dependencies (`numpy`, `onnxruntime`, `onnx`, `sympy`) live in the
|
|
6
|
+
optional `model` extra, never in the base install and never in the frozen
|
|
7
|
+
release assets (`.[monitor]` only): onnxruntime ships no macOS x86_64 wheel and
|
|
8
|
+
adds tens of MB for a verb most users never run. A missing one is a coded
|
|
9
|
+
refusal naming `pip install "tan-cli[model]"`, the `monitor.pyserial-missing`
|
|
10
|
+
pattern -- found with `importlib.util.find_spec`, so the check itself imports
|
|
11
|
+
nothing heavy.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import importlib.util
|
|
17
|
+
|
|
18
|
+
#: Modules each verb imports (lazily) from the `model` extra.
|
|
19
|
+
REQUIRED_MODULES = {
|
|
20
|
+
"prep": ("numpy", "onnxruntime", "onnx", "sympy"),
|
|
21
|
+
"run": ("numpy", "onnxruntime"),
|
|
22
|
+
"ab": ("numpy", "onnxruntime"),
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
#: The one install hint every refusal quotes.
|
|
26
|
+
INSTALL_HINT = 'pip install "tan-cli[model]"'
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def missing_extra_modules(verb: str) -> list[str]:
|
|
30
|
+
"""The `verb`'s extra modules that are not importable, in declared order."""
|
|
31
|
+
return [m for m in REQUIRED_MODULES[verb] if importlib.util.find_spec(m) is None]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def extra_missing_message(verb: str, missing: list[str]) -> str:
|
|
35
|
+
return (
|
|
36
|
+
f"`tan model {verb}` needs the optional `model` extra ({', '.join(missing)} not "
|
|
37
|
+
f"installed). Install it with `{INSTALL_HINT}`. A frozen `tan` binary does not "
|
|
38
|
+
"bundle it; use a pip-installed tan for this command."
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def broken_extra_message(verb: str, detail: str) -> str:
|
|
43
|
+
"""The same refusal when the modules are findable but fail to import (a
|
|
44
|
+
damaged install): the import error is quoted, not mistaken for a module name."""
|
|
45
|
+
return (
|
|
46
|
+
f"`tan model {verb}` could not import the optional `model` extra's dependencies "
|
|
47
|
+
f"({detail}). Reinstall it with `{INSTALL_HINT}`."
|
|
48
|
+
)
|
tan/core/model_list.py
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Pure engine for `tan model list` (tan-cli#674): what `board.yaml` declares
|
|
3
|
+
under `models:`, next to what `--out` already holds for each one -- read-only,
|
|
4
|
+
no SDK metadata consulted at all. Unlike `build`/`check`, nothing this reports
|
|
5
|
+
comes out of `metadata/**`: there is no compile target to resolve and no NPU
|
|
6
|
+
support table to read, only a board.yaml already in hand and a directory to
|
|
7
|
+
`stat()`. `tan.commands.model_cmd` does the board.yaml IO/validation shared
|
|
8
|
+
with `build`/`check` (`_load_board`/`_require_models_list`/
|
|
9
|
+
`_require_model_entry`); this module is the per-model shaping, mirroring the
|
|
10
|
+
`tan.core.model_check`/`tan.core.model_doctor` split (the engine computes,
|
|
11
|
+
this renders/serialises).
|
|
12
|
+
|
|
13
|
+
The one piece of real IO here -- reading a package's own manifest back off
|
|
14
|
+
disk (`tan.model.package.read_manifest_file`) to report its size and whether
|
|
15
|
+
it is stale -- is itself read-only and already the pattern `model_cmd.
|
|
16
|
+
_shipped_caveat_issues` uses for the same reason: describe the ARTIFACT, not
|
|
17
|
+
what a build call happened to hold in memory.
|
|
18
|
+
|
|
19
|
+
A readback failure there (a corrupt/truncated package, or a `source` that no
|
|
20
|
+
longer resolves) is reported the SAME way `_shipped_caveat_issues` reports its
|
|
21
|
+
own readback failure: a `model.artifact-stale-unknown` WARNING `Issue`, never
|
|
22
|
+
silent. Silence would be indistinguishable from "not stale" -- a `board.yaml`
|
|
23
|
+
naming a `source` that no longer exists, with a perfectly good package already
|
|
24
|
+
on disk, used to answer `{"exists": true, "bytes": N}` with no sign the
|
|
25
|
+
staleness check never ran at all (tan-cli#674 review MAJOR 1). The row itself
|
|
26
|
+
still degrades to the bare `exists: True` shape (see `_artifact_status`): the
|
|
27
|
+
package IS there, and downgrading that to a failure over a diagnostic
|
|
28
|
+
comparison would be the wrong trade -- exactly `_shipped_caveat_issues`'s own
|
|
29
|
+
reasoning, applied to a second readback.
|
|
30
|
+
"""
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
|
|
33
|
+
import hashlib
|
|
34
|
+
from pathlib import Path
|
|
35
|
+
from typing import Any
|
|
36
|
+
|
|
37
|
+
from tan.envelope import Issue
|
|
38
|
+
from tan.model.package import read_manifest_file
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def declared_sku(board_doc: dict) -> str | None:
|
|
42
|
+
"""`board.yaml`'s `som.sku`, or `None`. Unlike `build`/`check`'s
|
|
43
|
+
`_require_sku`, a missing or malformed SKU does not refuse `list` --
|
|
44
|
+
naming what is declared and what is built needs no real SoM at all, so
|
|
45
|
+
this degrades instead of raising."""
|
|
46
|
+
som = board_doc.get("som")
|
|
47
|
+
sku = som.get("sku") if isinstance(som, dict) else None
|
|
48
|
+
return sku if isinstance(sku, str) and sku else None
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _artifact_status(
|
|
52
|
+
name: str, out_path: Path, source: Path
|
|
53
|
+
) -> tuple[dict[str, Any], Issue | None]:
|
|
54
|
+
"""`({"exists": False}, None)` for a package `tan model build` has not
|
|
55
|
+
written (or no longer holds); `({"exists": True, "bytes", "stale"}, None)`
|
|
56
|
+
for one that is on disk and whose staleness could be determined. `stale`
|
|
57
|
+
is `True` when the package's own recorded `src_sha` (read back from the
|
|
58
|
+
FILE, not a cached value) no longer matches the CURRENT `source` file's
|
|
59
|
+
hash -- i.e. the model was edited since the last `tan model build`.
|
|
60
|
+
`bytes`/`stale` are both best-effort: a corrupt/truncated package or a
|
|
61
|
+
`source` that no longer resolves degrades to the bare `exists: True` row
|
|
62
|
+
rather than refusing the whole list -- staleness is a bonus fact about a
|
|
63
|
+
build that DID succeed, not a reason to hide that it exists.
|
|
64
|
+
|
|
65
|
+
That degradation is never silent, though (tan-cli#674 review MAJOR 1): the
|
|
66
|
+
second element is a `model.artifact-stale-unknown` WARNING `Issue` whenever
|
|
67
|
+
the readback failed, `None` when it did not need to fall back at all --
|
|
68
|
+
the caller (`list_entry`) collects it into the envelope's `issues` rather
|
|
69
|
+
than reporting a clean row with no sign anything failed."""
|
|
70
|
+
if not out_path.is_file():
|
|
71
|
+
return {"exists": False}, None
|
|
72
|
+
status: dict[str, Any] = {"exists": True, "bytes": out_path.stat().st_size}
|
|
73
|
+
try:
|
|
74
|
+
manifest = read_manifest_file(out_path)
|
|
75
|
+
current_sha = hashlib.sha256(source.read_bytes()).digest()
|
|
76
|
+
except (OSError, ValueError) as err:
|
|
77
|
+
return status, Issue(
|
|
78
|
+
"model.artifact-stale-unknown",
|
|
79
|
+
"warning",
|
|
80
|
+
f"model '{name}': found {out_path} but could not confirm whether "
|
|
81
|
+
f"it is stale against {source}: {type(err).__name__}: {err}",
|
|
82
|
+
)
|
|
83
|
+
status["stale"] = manifest.src_sha != current_sha
|
|
84
|
+
return status, None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def list_entry(name: str, source: Path, out_dir: Path) -> tuple[dict[str, Any], Issue | None]:
|
|
88
|
+
"""One declared model's `list` row: its name, resolved source path, and
|
|
89
|
+
the artifact `tan model build` would write for it under `out_dir` -- the
|
|
90
|
+
SAME `{name}.alpmodel` naming `build_model` uses (`tan/model/build.py`),
|
|
91
|
+
read here, never compiled. The second element is `_artifact_status`'s own
|
|
92
|
+
readback-failure `Issue`, or `None`."""
|
|
93
|
+
artifact, issue = _artifact_status(name, out_dir / f"{name}.alpmodel", source)
|
|
94
|
+
return {"name": name, "source": str(source), "artifact": artifact}, issue
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def render_list_text(data: dict[str, Any]) -> list[str]:
|
|
98
|
+
"""Every declared model's line, in board.yaml order -- `[]` for no
|
|
99
|
+
declared models, the same split `build`/`check`'s own text branches make
|
|
100
|
+
between "no lines" and "needs a filler saying so", decided by the caller
|
|
101
|
+
(`model_cmd.finish`)."""
|
|
102
|
+
lines: list[str] = []
|
|
103
|
+
for m in data.get("models") or []:
|
|
104
|
+
artifact = m.get("artifact") or {}
|
|
105
|
+
if not artifact.get("exists"):
|
|
106
|
+
lines.append(f"{m['name']}: not built ({m['source']})")
|
|
107
|
+
continue
|
|
108
|
+
size = artifact.get("bytes")
|
|
109
|
+
size_note = f", {size} bytes" if size is not None else ""
|
|
110
|
+
stale_note = ", STALE (source changed since last build)" if artifact.get("stale") else ""
|
|
111
|
+
lines.append(f"{m['name']}: built{size_note}{stale_note}")
|
|
112
|
+
return lines
|