acprof 0.3.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.
Files changed (704) hide show
  1. acprof/__init__.py +3 -0
  2. acprof/__main__.py +5 -0
  3. acprof/_bundle/.dockerignore +20 -0
  4. acprof/_bundle/LICENSE +202 -0
  5. acprof/_bundle/NOTICE +43 -0
  6. acprof/_bundle/acprof/__init__.py +3 -0
  7. acprof/_bundle/acprof/__main__.py +5 -0
  8. acprof/_bundle/acprof/analysis/__init__.py +1 -0
  9. acprof/_bundle/acprof/analysis/audit.py +276 -0
  10. acprof/_bundle/acprof/analysis/comparison.py +364 -0
  11. acprof/_bundle/acprof/analysis/compatibility.py +223 -0
  12. acprof/_bundle/acprof/analysis/conditions.py +185 -0
  13. acprof/_bundle/acprof/analysis/independent_comparison.py +191 -0
  14. acprof/_bundle/acprof/analysis/latency_model.py +1106 -0
  15. acprof/_bundle/acprof/analysis/latency_report.py +577 -0
  16. acprof/_bundle/acprof/analysis/model.py +276 -0
  17. acprof/_bundle/acprof/analysis/precision.py +51 -0
  18. acprof/_bundle/acprof/analysis/uncertainty.py +144 -0
  19. acprof/_bundle/acprof/application/__init__.py +1 -0
  20. acprof/_bundle/acprof/application/download.py +14 -0
  21. acprof/_bundle/acprof/application/errors.py +5 -0
  22. acprof/_bundle/acprof/application/notifications.py +302 -0
  23. acprof/_bundle/acprof/application/preparation.py +333 -0
  24. acprof/_bundle/acprof/application/resources.py +66 -0
  25. acprof/_bundle/acprof/application/run.py +462 -0
  26. acprof/_bundle/acprof/application/terminal_log.py +136 -0
  27. acprof/_bundle/acprof/artifact_layout.py +261 -0
  28. acprof/_bundle/acprof/artifacts.py +161 -0
  29. acprof/_bundle/acprof/capabilities.py +404 -0
  30. acprof/_bundle/acprof/cli/__init__.py +1 -0
  31. acprof/_bundle/acprof/cli/audit.py +74 -0
  32. acprof/_bundle/acprof/cli/auto.py +41 -0
  33. acprof/_bundle/acprof/cli/backfill_compute.py +498 -0
  34. acprof/_bundle/acprof/cli/compare.py +34 -0
  35. acprof/_bundle/acprof/cli/coverage.py +70 -0
  36. acprof/_bundle/acprof/cli/doctor.py +47 -0
  37. acprof/_bundle/acprof/cli/inspect.py +101 -0
  38. acprof/_bundle/acprof/cli/load.py +190 -0
  39. acprof/_bundle/acprof/cli/main.py +64 -0
  40. acprof/_bundle/acprof/cli/model_store.py +22 -0
  41. acprof/_bundle/acprof/cli/plot.py +211 -0
  42. acprof/_bundle/acprof/cli/posthoc.py +113 -0
  43. acprof/_bundle/acprof/cli/probe.py +186 -0
  44. acprof/_bundle/acprof/cli/report.py +35 -0
  45. acprof/_bundle/acprof/cli/results.py +37 -0
  46. acprof/_bundle/acprof/cli/run.py +54 -0
  47. acprof/_bundle/acprof/cli/stats.py +94 -0
  48. acprof/_bundle/acprof/cli/tui.py +65 -0
  49. acprof/_bundle/acprof/config.py +177 -0
  50. acprof/_bundle/acprof/container/__init__.py +1 -0
  51. acprof/_bundle/acprof/container/compat/__init__.py +1 -0
  52. acprof/_bundle/acprof/container/compat/transformers_dynamic.py +54 -0
  53. acprof/_bundle/acprof/container/compat/transformers_model_types.json +1091 -0
  54. acprof/_bundle/acprof/container/compute_profile_runner.py +392 -0
  55. acprof/_bundle/acprof/container/download_model.py +165 -0
  56. acprof/_bundle/acprof/container/execution.py +73 -0
  57. acprof/_bundle/acprof/container/handlers/__init__.py +298 -0
  58. acprof/_bundle/acprof/container/handlers/audio.py +743 -0
  59. acprof/_bundle/acprof/container/handlers/custom_pipeline.py +74 -0
  60. acprof/_bundle/acprof/container/handlers/cv.py +321 -0
  61. acprof/_bundle/acprof/container/handlers/diffusion.py +728 -0
  62. acprof/_bundle/acprof/container/handlers/moss.py +64 -0
  63. acprof/_bundle/acprof/container/handlers/multimodal.py +609 -0
  64. acprof/_bundle/acprof/container/handlers/nlp.py +747 -0
  65. acprof/_bundle/acprof/container/handlers/onnx_tasks.py +231 -0
  66. acprof/_bundle/acprof/container/handlers/onnxruntime.py +90 -0
  67. acprof/_bundle/acprof/container/handlers/structured.py +185 -0
  68. acprof/_bundle/acprof/container/handlers/timeseries.py +178 -0
  69. acprof/_bundle/acprof/container/load_policy.py +81 -0
  70. acprof/_bundle/acprof/container/loading_quality.py +48 -0
  71. acprof/_bundle/acprof/container/local_pipeline.py +31 -0
  72. acprof/_bundle/acprof/container/model_files.py +280 -0
  73. acprof/_bundle/acprof/container/model_probe.py +80 -0
  74. acprof/_bundle/acprof/container/onnx_session.py +150 -0
  75. acprof/_bundle/acprof/container/runtime_manifest.py +150 -0
  76. acprof/_bundle/acprof/container/runtime_validate.py +156 -0
  77. acprof/_bundle/acprof/container/server.py +209 -0
  78. acprof/_bundle/acprof/container/torch_execution.py +55 -0
  79. acprof/_bundle/acprof/container/validation.py +172 -0
  80. acprof/_bundle/acprof/cpu_affinity.py +35 -0
  81. acprof/_bundle/acprof/dependency_locks.py +129 -0
  82. acprof/_bundle/acprof/experiment.py +356 -0
  83. acprof/_bundle/acprof/extensions/__init__.py +294 -0
  84. acprof/_bundle/acprof/extensions/builtin/manifest.json +3591 -0
  85. acprof/_bundle/acprof/extensions/onnxruntime/manifest.json +191 -0
  86. acprof/_bundle/acprof/extensions/schema.py +238 -0
  87. acprof/_bundle/acprof/extensions/transformers/4.57.6.json +1639 -0
  88. acprof/_bundle/acprof/extensions/transformers/5.10.4.json +1812 -0
  89. acprof/_bundle/acprof/failures.py +115 -0
  90. acprof/_bundle/acprof/hardware_conditions.py +18 -0
  91. acprof/_bundle/acprof/hf_download.py +143 -0
  92. acprof/_bundle/acprof/hf_endpoints.py +62 -0
  93. acprof/_bundle/acprof/hf_transport.py +238 -0
  94. acprof/_bundle/acprof/host/__init__.py +1 -0
  95. acprof/_bundle/acprof/host/automation.py +161 -0
  96. acprof/_bundle/acprof/host/client.py +520 -0
  97. acprof/_bundle/acprof/host/collection_history.py +77 -0
  98. acprof/_bundle/acprof/host/collection_workflow.py +285 -0
  99. acprof/_bundle/acprof/host/command.py +119 -0
  100. acprof/_bundle/acprof/host/container_lifecycle.py +225 -0
  101. acprof/_bundle/acprof/host/container_state.py +128 -0
  102. acprof/_bundle/acprof/host/coverage_state.py +259 -0
  103. acprof/_bundle/acprof/host/detect.py +669 -0
  104. acprof/_bundle/acprof/host/docker_runtime.py +359 -0
  105. acprof/_bundle/acprof/host/doctor.py +158 -0
  106. acprof/_bundle/acprof/host/env_utils.py +233 -0
  107. acprof/_bundle/acprof/host/execution_conditions.py +87 -0
  108. acprof/_bundle/acprof/host/gpu_device.py +66 -0
  109. acprof/_bundle/acprof/host/hardware_conditions.py +130 -0
  110. acprof/_bundle/acprof/host/images/__init__.py +1 -0
  111. acprof/_bundle/acprof/host/images/dependencies.py +169 -0
  112. acprof/_bundle/acprof/host/images/environment.py +313 -0
  113. acprof/_bundle/acprof/host/images/graph.py +143 -0
  114. acprof/_bundle/acprof/host/images/identity.py +6 -0
  115. acprof/_bundle/acprof/host/images/inventory.py +445 -0
  116. acprof/_bundle/acprof/host/images/runtime.py +457 -0
  117. acprof/_bundle/acprof/host/images/scan.py +154 -0
  118. acprof/_bundle/acprof/host/input_plan.py +1056 -0
  119. acprof/_bundle/acprof/host/interface_probe.py +141 -0
  120. acprof/_bundle/acprof/host/largest_scale_probe.py +566 -0
  121. acprof/_bundle/acprof/host/matrix_plan.py +98 -0
  122. acprof/_bundle/acprof/host/measurement/__init__.py +1 -0
  123. acprof/_bundle/acprof/host/measurement/boundary.py +162 -0
  124. acprof/_bundle/acprof/host/measurement/capture.py +139 -0
  125. acprof/_bundle/acprof/host/measurement/checkpoint.py +275 -0
  126. acprof/_bundle/acprof/host/measurement/config.py +212 -0
  127. acprof/_bundle/acprof/host/measurement/diagnostics.py +247 -0
  128. acprof/_bundle/acprof/host/measurement/energy_validation.py +172 -0
  129. acprof/_bundle/acprof/host/measurement/errors.py +25 -0
  130. acprof/_bundle/acprof/host/measurement/execution.py +396 -0
  131. acprof/_bundle/acprof/host/measurement/load.py +237 -0
  132. acprof/_bundle/acprof/host/measurement/metrics.py +975 -0
  133. acprof/_bundle/acprof/host/measurement/monitors.py +143 -0
  134. acprof/_bundle/acprof/host/measurement/publication.py +171 -0
  135. acprof/_bundle/acprof/host/measurement/requests.py +97 -0
  136. acprof/_bundle/acprof/host/measurement/types.py +82 -0
  137. acprof/_bundle/acprof/host/model_coverage.py +337 -0
  138. acprof/_bundle/acprof/host/model_errors.py +116 -0
  139. acprof/_bundle/acprof/host/model_inspection.py +107 -0
  140. acprof/_bundle/acprof/host/model_schema.py +35 -0
  141. acprof/_bundle/acprof/host/model_store.py +733 -0
  142. acprof/_bundle/acprof/host/network_preflight.py +229 -0
  143. acprof/_bundle/acprof/host/orchestrator.py +1133 -0
  144. acprof/_bundle/acprof/host/permissions.py +117 -0
  145. acprof/_bundle/acprof/host/platform_metadata.py +63 -0
  146. acprof/_bundle/acprof/host/posthoc/__init__.py +12 -0
  147. acprof/_bundle/acprof/host/posthoc/backfill.py +415 -0
  148. acprof/_bundle/acprof/host/posthoc/context.py +400 -0
  149. acprof/_bundle/acprof/host/posthoc/plans.py +597 -0
  150. acprof/_bundle/acprof/host/posthoc/service.py +303 -0
  151. acprof/_bundle/acprof/host/posthoc/storage.py +491 -0
  152. acprof/_bundle/acprof/host/preflight.py +370 -0
  153. acprof/_bundle/acprof/host/profiling/__init__.py +1 -0
  154. acprof/_bundle/acprof/host/profiling/compute.py +436 -0
  155. acprof/_bundle/acprof/host/profiling/compute_plan.py +261 -0
  156. acprof/_bundle/acprof/host/profiling/execution.py +547 -0
  157. acprof/_bundle/acprof/host/profiling/execution_plan.py +576 -0
  158. acprof/_bundle/acprof/host/profiling/progress.py +83 -0
  159. acprof/_bundle/acprof/host/profiling/support.py +252 -0
  160. acprof/_bundle/acprof/host/profiling/tools/__init__.py +1 -0
  161. acprof/_bundle/acprof/host/profiling/tools/advisor.py +164 -0
  162. acprof/_bundle/acprof/host/profiling/tools/compute_parsers.py +409 -0
  163. acprof/_bundle/acprof/host/profiling/tools/execution_environment.py +135 -0
  164. acprof/_bundle/acprof/host/profiling/tools/execution_parsers.py +364 -0
  165. acprof/_bundle/acprof/host/profiling/tools/massif.py +418 -0
  166. acprof/_bundle/acprof/host/profiling/tools/ncu.py +696 -0
  167. acprof/_bundle/acprof/host/profiling/tools/nsys.py +326 -0
  168. acprof/_bundle/acprof/host/profiling/tools/tool_discovery.py +229 -0
  169. acprof/_bundle/acprof/host/profiling/tools/torch.py +158 -0
  170. acprof/_bundle/acprof/host/result_cleanup.py +65 -0
  171. acprof/_bundle/acprof/host/run_state.py +639 -0
  172. acprof/_bundle/acprof/host/runtime_settings.py +10 -0
  173. acprof/_bundle/acprof/host/runtime_validation.py +302 -0
  174. acprof/_bundle/acprof/host/source_bundle.py +96 -0
  175. acprof/_bundle/acprof/host/startup_probe.py +95 -0
  176. acprof/_bundle/acprof/host/static_metadata.py +936 -0
  177. acprof/_bundle/acprof/host/task_support.py +121 -0
  178. acprof/_bundle/acprof/installation.py +26 -0
  179. acprof/_bundle/acprof/latency_slo.py +62 -0
  180. acprof/_bundle/acprof/messages.py +26 -0
  181. acprof/_bundle/acprof/metric_registry.py +315 -0
  182. acprof/_bundle/acprof/model_candidates.py +297 -0
  183. acprof/_bundle/acprof/model_contract.py +234 -0
  184. acprof/_bundle/acprof/model_dependencies.py +93 -0
  185. acprof/_bundle/acprof/model_dependency_domain.py +45 -0
  186. acprof/_bundle/acprof/model_dependency_eval.py +438 -0
  187. acprof/_bundle/acprof/model_dependency_flow.py +200 -0
  188. acprof/_bundle/acprof/model_evidence.py +115 -0
  189. acprof/_bundle/acprof/model_metadata_analysis.py +156 -0
  190. acprof/_bundle/acprof/model_repository.py +149 -0
  191. acprof/_bundle/acprof/model_resolution.py +129 -0
  192. acprof/_bundle/acprof/model_review.py +155 -0
  193. acprof/_bundle/acprof/model_source_analysis.py +299 -0
  194. acprof/_bundle/acprof/model_spec.py +204 -0
  195. acprof/_bundle/acprof/model_support.py +105 -0
  196. acprof/_bundle/acprof/model_transforms.py +81 -0
  197. acprof/_bundle/acprof/monitors/__init__.py +1 -0
  198. acprof/_bundle/acprof/monitors/common.py +63 -0
  199. acprof/_bundle/acprof/monitors/energy_cpu.py +651 -0
  200. acprof/_bundle/acprof/monitors/energy_nvml.py +381 -0
  201. acprof/_bundle/acprof/monitors/gpu_metrics.py +123 -0
  202. acprof/_bundle/acprof/monitors/interfaces.py +74 -0
  203. acprof/_bundle/acprof/monitors/perf_mips.py +617 -0
  204. acprof/_bundle/acprof/monitors/rapl_topology.py +100 -0
  205. acprof/_bundle/acprof/monitors/resource_metrics.py +472 -0
  206. acprof/_bundle/acprof/monitors/resource_readers.py +435 -0
  207. acprof/_bundle/acprof/monitors/resource_usage.py +450 -0
  208. acprof/_bundle/acprof/monitors/sampling.py +79 -0
  209. acprof/_bundle/acprof/network_policy.py +182 -0
  210. acprof/_bundle/acprof/notifications.py +279 -0
  211. acprof/_bundle/acprof/packet/__init__.py +1 -0
  212. acprof/_bundle/acprof/packet/merge_packet_latency.py +369 -0
  213. acprof/_bundle/acprof/packet/sniff_parse_pcap.py +240 -0
  214. acprof/_bundle/acprof/pixel_metrics.py +103 -0
  215. acprof/_bundle/acprof/platform.py +180 -0
  216. acprof/_bundle/acprof/plotting/__init__.py +1 -0
  217. acprof/_bundle/acprof/plotting/config.py +487 -0
  218. acprof/_bundle/acprof/plotting/data.py +236 -0
  219. acprof/_bundle/acprof/plotting/diagnostics.py +747 -0
  220. acprof/_bundle/acprof/plotting/latency.py +638 -0
  221. acprof/_bundle/acprof/plotting/metrics.py +648 -0
  222. acprof/_bundle/acprof/plotting/plotly-LICENSE.txt +21 -0
  223. acprof/_bundle/acprof/plotting/report.py +66 -0
  224. acprof/_bundle/acprof/plotting/styles.py +111 -0
  225. acprof/_bundle/acprof/precision.py +42 -0
  226. acprof/_bundle/acprof/preparation_events.py +78 -0
  227. acprof/_bundle/acprof/progress_events.py +105 -0
  228. acprof/_bundle/acprof/quality.py +153 -0
  229. acprof/_bundle/acprof/resource_budget.py +29 -0
  230. acprof/_bundle/acprof/result_csv.py +263 -0
  231. acprof/_bundle/acprof/result_layers.py +365 -0
  232. acprof/_bundle/acprof/run_args.py +223 -0
  233. acprof/_bundle/acprof/run_options.py +225 -0
  234. acprof/_bundle/acprof/runtime_dependencies.py +195 -0
  235. acprof/_bundle/acprof/runtime_profiles.py +258 -0
  236. acprof/_bundle/acprof/runtime_settings.py +72 -0
  237. acprof/_bundle/acprof/source_identity.py +68 -0
  238. acprof/_bundle/acprof/telemetry.py +225 -0
  239. acprof/_bundle/acprof/testing/__init__.py +1 -0
  240. acprof/_bundle/acprof/testing/evidence.py +117 -0
  241. acprof/_bundle/acprof/testing/plugin.py +126 -0
  242. acprof/_bundle/acprof/testing/provenance.py +53 -0
  243. acprof/_bundle/acprof/testing/sharding.py +14 -0
  244. acprof/_bundle/acprof/tui/__init__.py +1 -0
  245. acprof/_bundle/acprof/tui/app.py +934 -0
  246. acprof/_bundle/acprof/tui/app_contracts.py +43 -0
  247. acprof/_bundle/acprof/tui/catalog_controller.py +120 -0
  248. acprof/_bundle/acprof/tui/commands.py +209 -0
  249. acprof/_bundle/acprof/tui/configuration_controller.py +436 -0
  250. acprof/_bundle/acprof/tui/diagnostics.py +340 -0
  251. acprof/_bundle/acprof/tui/downloads.py +56 -0
  252. acprof/_bundle/acprof/tui/environment.py +248 -0
  253. acprof/_bundle/acprof/tui/experiment_catalog.py +279 -0
  254. acprof/_bundle/acprof/tui/experiment_controller.py +132 -0
  255. acprof/_bundle/acprof/tui/experiment_picker.py +273 -0
  256. acprof/_bundle/acprof/tui/field_validation.py +62 -0
  257. acprof/_bundle/acprof/tui/i18n.py +57 -0
  258. acprof/_bundle/acprof/tui/image_controller.py +525 -0
  259. acprof/_bundle/acprof/tui/images.py +737 -0
  260. acprof/_bundle/acprof/tui/input.py +191 -0
  261. acprof/_bundle/acprof/tui/localization_controller.py +110 -0
  262. acprof/_bundle/acprof/tui/log.py +310 -0
  263. acprof/_bundle/acprof/tui/model_candidates.py +278 -0
  264. acprof/_bundle/acprof/tui/model_controller.py +111 -0
  265. acprof/_bundle/acprof/tui/model_input.py +12 -0
  266. acprof/_bundle/acprof/tui/model_store.py +166 -0
  267. acprof/_bundle/acprof/tui/preflight_controller.py +271 -0
  268. acprof/_bundle/acprof/tui/preparation.py +269 -0
  269. acprof/_bundle/acprof/tui/presentation.py +46 -0
  270. acprof/_bundle/acprof/tui/process.py +111 -0
  271. acprof/_bundle/acprof/tui/process_controller.py +218 -0
  272. acprof/_bundle/acprof/tui/profile_controller.py +109 -0
  273. acprof/_bundle/acprof/tui/progress.py +195 -0
  274. acprof/_bundle/acprof/tui/recovery.py +114 -0
  275. acprof/_bundle/acprof/tui/recovery_controller.py +169 -0
  276. acprof/_bundle/acprof/tui/rendering.py +75 -0
  277. acprof/_bundle/acprof/tui/reports.py +303 -0
  278. acprof/_bundle/acprof/tui/result_controller.py +279 -0
  279. acprof/_bundle/acprof/tui/review_inputs.py +54 -0
  280. acprof/_bundle/acprof/tui/run_form.py +125 -0
  281. acprof/_bundle/acprof/tui/run_planning.py +97 -0
  282. acprof/_bundle/acprof/tui/run_results.py +175 -0
  283. acprof/_bundle/acprof/tui/scrollbar.py +65 -0
  284. acprof/_bundle/acprof/tui/settings.py +203 -0
  285. acprof/_bundle/acprof/tui/slash_controller.py +95 -0
  286. acprof/_bundle/acprof/tui/stages.py +97 -0
  287. acprof/_bundle/acprof/tui/storage.py +150 -0
  288. acprof/_bundle/acprof/tui/table.py +213 -0
  289. acprof/_bundle/acprof/tui/themes.py +112 -0
  290. acprof/_bundle/acprof/tui/translation_catalog.py +1861 -0
  291. acprof/_bundle/acprof/tui/tui.tcss +373 -0
  292. acprof/_bundle/acprof/tui/views.py +706 -0
  293. acprof/_bundle/acprof/window_records.py +165 -0
  294. acprof/_bundle/acprof/workloads/__init__.py +222 -0
  295. acprof/_bundle/acprof/workloads/audio.py +621 -0
  296. acprof/_bundle/acprof/workloads/contract.py +116 -0
  297. acprof/_bundle/acprof/workloads/cv.py +240 -0
  298. acprof/_bundle/acprof/workloads/diffusion.py +410 -0
  299. acprof/_bundle/acprof/workloads/multimodal.py +329 -0
  300. acprof/_bundle/acprof/workloads/nlp.py +151 -0
  301. acprof/_bundle/acprof/workloads/structured.py +151 -0
  302. acprof/_bundle/acprof/workloads/timeseries.py +69 -0
  303. acprof/_bundle/assets/audio/librispeech-clean-test-en-30s/audio.wav +0 -0
  304. acprof/_bundle/assets/audio/librispeech-clean-test-en-30s/source.json +81 -0
  305. acprof/_bundle/dockerfiles/environment_tools.py +314 -0
  306. acprof/_bundle/dockerfiles/locks/audio-cpu.artifacts.json +503 -0
  307. acprof/_bundle/dockerfiles/locks/audio-cpu.txt +143 -0
  308. acprof/_bundle/dockerfiles/locks/audio-cu124.artifacts.json +600 -0
  309. acprof/_bundle/dockerfiles/locks/audio-cu124.txt +171 -0
  310. acprof/_bundle/dockerfiles/locks/audio-cu128.artifacts.json +544 -0
  311. acprof/_bundle/dockerfiles/locks/audio-cu128.txt +155 -0
  312. acprof/_bundle/dockerfiles/locks/custom-multimodal-cpu.artifacts.json +510 -0
  313. acprof/_bundle/dockerfiles/locks/custom-multimodal-cpu.txt +145 -0
  314. acprof/_bundle/dockerfiles/locks/custom-multimodal-cu124.artifacts.json +607 -0
  315. acprof/_bundle/dockerfiles/locks/custom-multimodal-cu124.txt +173 -0
  316. acprof/_bundle/dockerfiles/locks/custom-multimodal-cu128.artifacts.json +642 -0
  317. acprof/_bundle/dockerfiles/locks/custom-multimodal-cu128.txt +183 -0
  318. acprof/_bundle/dockerfiles/locks/cv-cpu.artifacts.json +279 -0
  319. acprof/_bundle/dockerfiles/locks/cv-cpu.txt +79 -0
  320. acprof/_bundle/dockerfiles/locks/cv-cu124.artifacts.json +376 -0
  321. acprof/_bundle/dockerfiles/locks/cv-cu124.txt +107 -0
  322. acprof/_bundle/dockerfiles/locks/cv-cu128.artifacts.json +411 -0
  323. acprof/_bundle/dockerfiles/locks/cv-cu128.txt +117 -0
  324. acprof/_bundle/dockerfiles/locks/diffusion-cpu.artifacts.json +335 -0
  325. acprof/_bundle/dockerfiles/locks/diffusion-cpu.txt +95 -0
  326. acprof/_bundle/dockerfiles/locks/diffusion-cu124.artifacts.json +432 -0
  327. acprof/_bundle/dockerfiles/locks/diffusion-cu124.txt +123 -0
  328. acprof/_bundle/dockerfiles/locks/diffusion-cu128.artifacts.json +467 -0
  329. acprof/_bundle/dockerfiles/locks/diffusion-cu128.txt +133 -0
  330. acprof/_bundle/dockerfiles/locks/moss-transformers5104.artifacts.json +621 -0
  331. acprof/_bundle/dockerfiles/locks/moss-transformers5104.txt +177 -0
  332. acprof/_bundle/dockerfiles/locks/multimodal-transformers4576.artifacts.json +635 -0
  333. acprof/_bundle/dockerfiles/locks/multimodal-transformers4576.txt +181 -0
  334. acprof/_bundle/dockerfiles/locks/nlp-cpu.artifacts.json +363 -0
  335. acprof/_bundle/dockerfiles/locks/nlp-cpu.txt +103 -0
  336. acprof/_bundle/dockerfiles/locks/nlp-cu124.artifacts.json +460 -0
  337. acprof/_bundle/dockerfiles/locks/nlp-cu124.txt +131 -0
  338. acprof/_bundle/dockerfiles/locks/nlp-cu128.artifacts.json +495 -0
  339. acprof/_bundle/dockerfiles/locks/nlp-cu128.txt +141 -0
  340. acprof/_bundle/dockerfiles/locks/onnxruntime-cpu.artifacts.json +261 -0
  341. acprof/_bundle/dockerfiles/locks/onnxruntime-cpu.txt +73 -0
  342. acprof/_bundle/dockerfiles/locks/platform-cpu.artifacts.json +99 -0
  343. acprof/_bundle/dockerfiles/locks/platform-cpu.txt +27 -0
  344. acprof/_bundle/dockerfiles/locks/platform-cu124.artifacts.json +196 -0
  345. acprof/_bundle/dockerfiles/locks/platform-cu124.txt +55 -0
  346. acprof/_bundle/dockerfiles/locks/platform-cu128.artifacts.json +231 -0
  347. acprof/_bundle/dockerfiles/locks/platform-cu128.txt +65 -0
  348. acprof/_bundle/dockerfiles/locks/platform-python-cpu.artifacts.json +37 -0
  349. acprof/_bundle/dockerfiles/locks/platform-python-cpu.txt +9 -0
  350. acprof/_bundle/dockerfiles/locks/structured-cpu.artifacts.json +279 -0
  351. acprof/_bundle/dockerfiles/locks/structured-cpu.txt +79 -0
  352. acprof/_bundle/dockerfiles/locks/structured-cu124.artifacts.json +376 -0
  353. acprof/_bundle/dockerfiles/locks/structured-cu124.txt +107 -0
  354. acprof/_bundle/dockerfiles/locks/structured-cu128.artifacts.json +411 -0
  355. acprof/_bundle/dockerfiles/locks/structured-cu128.txt +117 -0
  356. acprof/_bundle/dockerfiles/locks/system-trixie-amd64.json +620 -0
  357. acprof/_bundle/dockerfiles/locks/timeseries-cpu.artifacts.json +314 -0
  358. acprof/_bundle/dockerfiles/locks/timeseries-cpu.txt +89 -0
  359. acprof/_bundle/dockerfiles/locks/timeseries-cu124.artifacts.json +411 -0
  360. acprof/_bundle/dockerfiles/locks/timeseries-cu124.txt +117 -0
  361. acprof/_bundle/dockerfiles/locks/timeseries-cu128.artifacts.json +446 -0
  362. acprof/_bundle/dockerfiles/locks/timeseries-cu128.txt +127 -0
  363. acprof/_bundle/dockerfiles/locks/transformers5104-cpu.artifacts.json +594 -0
  364. acprof/_bundle/dockerfiles/locks/transformers5104-cpu.txt +169 -0
  365. acprof/_bundle/dockerfiles/locks/transformers5104-cu124.artifacts.json +691 -0
  366. acprof/_bundle/dockerfiles/locks/transformers5104-cu124.txt +197 -0
  367. acprof/_bundle/dockerfiles/locks/transformers5104-cu128.artifacts.json +726 -0
  368. acprof/_bundle/dockerfiles/locks/transformers5104-cu128.txt +207 -0
  369. acprof/_bundle/dockerfiles/platform.Dockerfile +22 -0
  370. acprof/_bundle/dockerfiles/requirements/audio.in +5 -0
  371. acprof/_bundle/dockerfiles/requirements/custom-multimodal.in +2 -0
  372. acprof/_bundle/dockerfiles/requirements/cv.in +4 -0
  373. acprof/_bundle/dockerfiles/requirements/diffusion.in +6 -0
  374. acprof/_bundle/dockerfiles/requirements/moss-transformers5104.in +9 -0
  375. acprof/_bundle/dockerfiles/requirements/multimodal-transformers4576.in +3 -0
  376. acprof/_bundle/dockerfiles/requirements/nlp.in +7 -0
  377. acprof/_bundle/dockerfiles/requirements/onnxruntime.in +9 -0
  378. acprof/_bundle/dockerfiles/requirements/service.in +8 -0
  379. acprof/_bundle/dockerfiles/requirements/structured.in +4 -0
  380. acprof/_bundle/dockerfiles/requirements/timeseries.in +2 -0
  381. acprof/_bundle/dockerfiles/requirements/timm.in +1 -0
  382. acprof/_bundle/dockerfiles/requirements/transformers5104.in +4 -0
  383. acprof/_bundle/dockerfiles/runtime-final.Dockerfile +34 -0
  384. acprof/_bundle/dockerfiles/runtime-model.Dockerfile +19 -0
  385. acprof/_bundle/dockerfiles/runtime.Dockerfile +23 -0
  386. acprof/_bundle/examples/coverage/regression.json +17 -0
  387. acprof/_bundle/examples/hardware-validation.json +19 -0
  388. acprof/_bundle/examples/multimodal/ultravox.model.json +29 -0
  389. acprof/_bundle/examples/nlp/smoke.py +111 -0
  390. acprof/_bundle/examples/onnxruntime/fixtures.py +164 -0
  391. acprof/_bundle/examples/onnxruntime/iris.json +16 -0
  392. acprof/_bundle/examples/onnxruntime/iris.model.json +7 -0
  393. acprof/_bundle/examples/onnxruntime/real_models.py +155 -0
  394. acprof/_bundle/examples/onnxruntime/smoke.py +68 -0
  395. acprof/_bundle/examples/structured/chronos_smoke.py +56 -0
  396. acprof/_bundle/examples/structured/export_models.py +71 -0
  397. acprof/_bundle/examples/structured/smoke.py +71 -0
  398. acprof/_bundle/licenses/CC-BY-4.0.txt +156 -0
  399. acprof/analysis/__init__.py +1 -0
  400. acprof/analysis/audit.py +276 -0
  401. acprof/analysis/comparison.py +364 -0
  402. acprof/analysis/compatibility.py +223 -0
  403. acprof/analysis/conditions.py +185 -0
  404. acprof/analysis/independent_comparison.py +191 -0
  405. acprof/analysis/latency_model.py +1106 -0
  406. acprof/analysis/latency_report.py +577 -0
  407. acprof/analysis/model.py +276 -0
  408. acprof/analysis/precision.py +51 -0
  409. acprof/analysis/uncertainty.py +144 -0
  410. acprof/application/__init__.py +1 -0
  411. acprof/application/download.py +14 -0
  412. acprof/application/errors.py +5 -0
  413. acprof/application/notifications.py +302 -0
  414. acprof/application/preparation.py +333 -0
  415. acprof/application/resources.py +66 -0
  416. acprof/application/run.py +462 -0
  417. acprof/application/terminal_log.py +136 -0
  418. acprof/artifact_layout.py +261 -0
  419. acprof/artifacts.py +161 -0
  420. acprof/capabilities.py +404 -0
  421. acprof/cli/__init__.py +1 -0
  422. acprof/cli/audit.py +74 -0
  423. acprof/cli/auto.py +41 -0
  424. acprof/cli/backfill_compute.py +498 -0
  425. acprof/cli/compare.py +34 -0
  426. acprof/cli/coverage.py +70 -0
  427. acprof/cli/doctor.py +47 -0
  428. acprof/cli/inspect.py +101 -0
  429. acprof/cli/load.py +190 -0
  430. acprof/cli/main.py +64 -0
  431. acprof/cli/model_store.py +22 -0
  432. acprof/cli/plot.py +211 -0
  433. acprof/cli/posthoc.py +113 -0
  434. acprof/cli/probe.py +186 -0
  435. acprof/cli/report.py +35 -0
  436. acprof/cli/results.py +37 -0
  437. acprof/cli/run.py +54 -0
  438. acprof/cli/stats.py +94 -0
  439. acprof/cli/tui.py +65 -0
  440. acprof/config.py +177 -0
  441. acprof/container/__init__.py +1 -0
  442. acprof/container/compat/__init__.py +1 -0
  443. acprof/container/compat/transformers_dynamic.py +54 -0
  444. acprof/container/compat/transformers_model_types.json +1091 -0
  445. acprof/container/compute_profile_runner.py +392 -0
  446. acprof/container/download_model.py +165 -0
  447. acprof/container/execution.py +73 -0
  448. acprof/container/handlers/__init__.py +298 -0
  449. acprof/container/handlers/audio.py +743 -0
  450. acprof/container/handlers/custom_pipeline.py +74 -0
  451. acprof/container/handlers/cv.py +321 -0
  452. acprof/container/handlers/diffusion.py +728 -0
  453. acprof/container/handlers/moss.py +64 -0
  454. acprof/container/handlers/multimodal.py +609 -0
  455. acprof/container/handlers/nlp.py +747 -0
  456. acprof/container/handlers/onnx_tasks.py +231 -0
  457. acprof/container/handlers/onnxruntime.py +90 -0
  458. acprof/container/handlers/structured.py +185 -0
  459. acprof/container/handlers/timeseries.py +178 -0
  460. acprof/container/load_policy.py +81 -0
  461. acprof/container/loading_quality.py +48 -0
  462. acprof/container/local_pipeline.py +31 -0
  463. acprof/container/model_files.py +280 -0
  464. acprof/container/model_probe.py +80 -0
  465. acprof/container/onnx_session.py +150 -0
  466. acprof/container/runtime_manifest.py +150 -0
  467. acprof/container/runtime_validate.py +156 -0
  468. acprof/container/server.py +209 -0
  469. acprof/container/torch_execution.py +55 -0
  470. acprof/container/validation.py +172 -0
  471. acprof/cpu_affinity.py +35 -0
  472. acprof/dependency_locks.py +129 -0
  473. acprof/experiment.py +356 -0
  474. acprof/extensions/__init__.py +294 -0
  475. acprof/extensions/builtin/manifest.json +3591 -0
  476. acprof/extensions/onnxruntime/manifest.json +191 -0
  477. acprof/extensions/schema.py +238 -0
  478. acprof/extensions/transformers/4.57.6.json +1639 -0
  479. acprof/extensions/transformers/5.10.4.json +1812 -0
  480. acprof/failures.py +115 -0
  481. acprof/hardware_conditions.py +18 -0
  482. acprof/hf_download.py +143 -0
  483. acprof/hf_endpoints.py +62 -0
  484. acprof/hf_transport.py +238 -0
  485. acprof/host/__init__.py +1 -0
  486. acprof/host/automation.py +161 -0
  487. acprof/host/client.py +520 -0
  488. acprof/host/collection_history.py +77 -0
  489. acprof/host/collection_workflow.py +285 -0
  490. acprof/host/command.py +119 -0
  491. acprof/host/container_lifecycle.py +225 -0
  492. acprof/host/container_state.py +128 -0
  493. acprof/host/coverage_state.py +259 -0
  494. acprof/host/detect.py +669 -0
  495. acprof/host/docker_runtime.py +359 -0
  496. acprof/host/doctor.py +158 -0
  497. acprof/host/env_utils.py +233 -0
  498. acprof/host/execution_conditions.py +87 -0
  499. acprof/host/gpu_device.py +66 -0
  500. acprof/host/hardware_conditions.py +130 -0
  501. acprof/host/images/__init__.py +1 -0
  502. acprof/host/images/dependencies.py +169 -0
  503. acprof/host/images/environment.py +313 -0
  504. acprof/host/images/graph.py +143 -0
  505. acprof/host/images/identity.py +6 -0
  506. acprof/host/images/inventory.py +445 -0
  507. acprof/host/images/runtime.py +457 -0
  508. acprof/host/images/scan.py +154 -0
  509. acprof/host/input_plan.py +1056 -0
  510. acprof/host/interface_probe.py +141 -0
  511. acprof/host/largest_scale_probe.py +566 -0
  512. acprof/host/matrix_plan.py +98 -0
  513. acprof/host/measurement/__init__.py +1 -0
  514. acprof/host/measurement/boundary.py +162 -0
  515. acprof/host/measurement/capture.py +139 -0
  516. acprof/host/measurement/checkpoint.py +275 -0
  517. acprof/host/measurement/config.py +212 -0
  518. acprof/host/measurement/diagnostics.py +247 -0
  519. acprof/host/measurement/energy_validation.py +172 -0
  520. acprof/host/measurement/errors.py +25 -0
  521. acprof/host/measurement/execution.py +396 -0
  522. acprof/host/measurement/load.py +237 -0
  523. acprof/host/measurement/metrics.py +975 -0
  524. acprof/host/measurement/monitors.py +143 -0
  525. acprof/host/measurement/publication.py +171 -0
  526. acprof/host/measurement/requests.py +97 -0
  527. acprof/host/measurement/types.py +82 -0
  528. acprof/host/model_coverage.py +337 -0
  529. acprof/host/model_errors.py +116 -0
  530. acprof/host/model_inspection.py +107 -0
  531. acprof/host/model_schema.py +35 -0
  532. acprof/host/model_store.py +733 -0
  533. acprof/host/network_preflight.py +229 -0
  534. acprof/host/orchestrator.py +1133 -0
  535. acprof/host/permissions.py +117 -0
  536. acprof/host/platform_metadata.py +63 -0
  537. acprof/host/posthoc/__init__.py +12 -0
  538. acprof/host/posthoc/backfill.py +415 -0
  539. acprof/host/posthoc/context.py +400 -0
  540. acprof/host/posthoc/plans.py +597 -0
  541. acprof/host/posthoc/service.py +303 -0
  542. acprof/host/posthoc/storage.py +491 -0
  543. acprof/host/preflight.py +370 -0
  544. acprof/host/profiling/__init__.py +1 -0
  545. acprof/host/profiling/compute.py +436 -0
  546. acprof/host/profiling/compute_plan.py +261 -0
  547. acprof/host/profiling/execution.py +547 -0
  548. acprof/host/profiling/execution_plan.py +576 -0
  549. acprof/host/profiling/progress.py +83 -0
  550. acprof/host/profiling/support.py +252 -0
  551. acprof/host/profiling/tools/__init__.py +1 -0
  552. acprof/host/profiling/tools/advisor.py +164 -0
  553. acprof/host/profiling/tools/compute_parsers.py +409 -0
  554. acprof/host/profiling/tools/execution_environment.py +135 -0
  555. acprof/host/profiling/tools/execution_parsers.py +364 -0
  556. acprof/host/profiling/tools/massif.py +418 -0
  557. acprof/host/profiling/tools/ncu.py +696 -0
  558. acprof/host/profiling/tools/nsys.py +326 -0
  559. acprof/host/profiling/tools/tool_discovery.py +229 -0
  560. acprof/host/profiling/tools/torch.py +158 -0
  561. acprof/host/result_cleanup.py +65 -0
  562. acprof/host/run_state.py +639 -0
  563. acprof/host/runtime_settings.py +10 -0
  564. acprof/host/runtime_validation.py +302 -0
  565. acprof/host/source_bundle.py +96 -0
  566. acprof/host/startup_probe.py +95 -0
  567. acprof/host/static_metadata.py +936 -0
  568. acprof/host/task_support.py +121 -0
  569. acprof/installation.py +26 -0
  570. acprof/latency_slo.py +62 -0
  571. acprof/messages.py +26 -0
  572. acprof/metric_registry.py +315 -0
  573. acprof/model_candidates.py +297 -0
  574. acprof/model_contract.py +234 -0
  575. acprof/model_dependencies.py +93 -0
  576. acprof/model_dependency_domain.py +45 -0
  577. acprof/model_dependency_eval.py +438 -0
  578. acprof/model_dependency_flow.py +200 -0
  579. acprof/model_evidence.py +115 -0
  580. acprof/model_metadata_analysis.py +156 -0
  581. acprof/model_repository.py +149 -0
  582. acprof/model_resolution.py +129 -0
  583. acprof/model_review.py +155 -0
  584. acprof/model_source_analysis.py +299 -0
  585. acprof/model_spec.py +204 -0
  586. acprof/model_support.py +105 -0
  587. acprof/model_transforms.py +81 -0
  588. acprof/monitors/__init__.py +1 -0
  589. acprof/monitors/common.py +63 -0
  590. acprof/monitors/energy_cpu.py +651 -0
  591. acprof/monitors/energy_nvml.py +381 -0
  592. acprof/monitors/gpu_metrics.py +123 -0
  593. acprof/monitors/interfaces.py +74 -0
  594. acprof/monitors/perf_mips.py +617 -0
  595. acprof/monitors/rapl_topology.py +100 -0
  596. acprof/monitors/resource_metrics.py +472 -0
  597. acprof/monitors/resource_readers.py +435 -0
  598. acprof/monitors/resource_usage.py +450 -0
  599. acprof/monitors/sampling.py +79 -0
  600. acprof/network_policy.py +182 -0
  601. acprof/notifications.py +279 -0
  602. acprof/packet/__init__.py +1 -0
  603. acprof/packet/merge_packet_latency.py +369 -0
  604. acprof/packet/sniff_parse_pcap.py +240 -0
  605. acprof/pixel_metrics.py +103 -0
  606. acprof/platform.py +180 -0
  607. acprof/plotting/__init__.py +1 -0
  608. acprof/plotting/config.py +487 -0
  609. acprof/plotting/data.py +236 -0
  610. acprof/plotting/diagnostics.py +747 -0
  611. acprof/plotting/latency.py +638 -0
  612. acprof/plotting/metrics.py +648 -0
  613. acprof/plotting/plotly-LICENSE.txt +21 -0
  614. acprof/plotting/report.css +61 -0
  615. acprof/plotting/report.html +68 -0
  616. acprof/plotting/report.js +295 -0
  617. acprof/plotting/report.py +66 -0
  618. acprof/plotting/styles.py +111 -0
  619. acprof/plotting/view_model.js +145 -0
  620. acprof/precision.py +42 -0
  621. acprof/preparation_events.py +78 -0
  622. acprof/progress_events.py +105 -0
  623. acprof/quality.py +153 -0
  624. acprof/resource_budget.py +29 -0
  625. acprof/result_csv.py +263 -0
  626. acprof/result_layers.py +365 -0
  627. acprof/run_args.py +223 -0
  628. acprof/run_options.py +225 -0
  629. acprof/runtime_dependencies.py +195 -0
  630. acprof/runtime_profiles.py +258 -0
  631. acprof/runtime_settings.py +72 -0
  632. acprof/source_identity.py +68 -0
  633. acprof/telemetry.py +225 -0
  634. acprof/testing/__init__.py +1 -0
  635. acprof/testing/evidence.py +117 -0
  636. acprof/testing/plugin.py +126 -0
  637. acprof/testing/provenance.py +53 -0
  638. acprof/testing/sharding.py +14 -0
  639. acprof/tui/__init__.py +1 -0
  640. acprof/tui/app.py +934 -0
  641. acprof/tui/app_contracts.py +43 -0
  642. acprof/tui/catalog_controller.py +120 -0
  643. acprof/tui/commands.py +209 -0
  644. acprof/tui/configuration_controller.py +436 -0
  645. acprof/tui/diagnostics.py +340 -0
  646. acprof/tui/downloads.py +56 -0
  647. acprof/tui/environment.py +248 -0
  648. acprof/tui/experiment_catalog.py +279 -0
  649. acprof/tui/experiment_controller.py +132 -0
  650. acprof/tui/experiment_picker.py +273 -0
  651. acprof/tui/field_validation.py +62 -0
  652. acprof/tui/i18n.py +57 -0
  653. acprof/tui/image_controller.py +525 -0
  654. acprof/tui/images.py +737 -0
  655. acprof/tui/input.py +191 -0
  656. acprof/tui/localization_controller.py +110 -0
  657. acprof/tui/log.py +310 -0
  658. acprof/tui/model_candidates.py +278 -0
  659. acprof/tui/model_controller.py +111 -0
  660. acprof/tui/model_input.py +12 -0
  661. acprof/tui/model_store.py +166 -0
  662. acprof/tui/preflight_controller.py +271 -0
  663. acprof/tui/preparation.py +269 -0
  664. acprof/tui/presentation.py +46 -0
  665. acprof/tui/process.py +111 -0
  666. acprof/tui/process_controller.py +218 -0
  667. acprof/tui/profile_controller.py +109 -0
  668. acprof/tui/progress.py +195 -0
  669. acprof/tui/recovery.py +114 -0
  670. acprof/tui/recovery_controller.py +169 -0
  671. acprof/tui/rendering.py +75 -0
  672. acprof/tui/reports.py +303 -0
  673. acprof/tui/result_controller.py +279 -0
  674. acprof/tui/review_inputs.py +54 -0
  675. acprof/tui/run_form.py +125 -0
  676. acprof/tui/run_planning.py +97 -0
  677. acprof/tui/run_results.py +175 -0
  678. acprof/tui/scrollbar.py +65 -0
  679. acprof/tui/settings.py +203 -0
  680. acprof/tui/slash_controller.py +95 -0
  681. acprof/tui/stages.py +97 -0
  682. acprof/tui/storage.py +150 -0
  683. acprof/tui/table.py +213 -0
  684. acprof/tui/themes.py +112 -0
  685. acprof/tui/translation_catalog.py +1861 -0
  686. acprof/tui/tui.tcss +373 -0
  687. acprof/tui/views.py +706 -0
  688. acprof/window_records.py +165 -0
  689. acprof/workloads/__init__.py +222 -0
  690. acprof/workloads/audio.py +621 -0
  691. acprof/workloads/contract.py +116 -0
  692. acprof/workloads/cv.py +240 -0
  693. acprof/workloads/diffusion.py +410 -0
  694. acprof/workloads/multimodal.py +329 -0
  695. acprof/workloads/nlp.py +151 -0
  696. acprof/workloads/structured.py +151 -0
  697. acprof/workloads/timeseries.py +69 -0
  698. acprof-0.3.0.dist-info/METADATA +117 -0
  699. acprof-0.3.0.dist-info/RECORD +704 -0
  700. acprof-0.3.0.dist-info/WHEEL +4 -0
  701. acprof-0.3.0.dist-info/entry_points.txt +2 -0
  702. acprof-0.3.0.dist-info/licenses/LICENSE +202 -0
  703. acprof-0.3.0.dist-info/licenses/NOTICE +43 -0
  704. acprof-0.3.0.dist-info/licenses/licenses/CC-BY-4.0.txt +156 -0
@@ -0,0 +1,364 @@
1
+ """只读比较已有实验条件;不替代严格的续跑身份或模型质量验收。"""
2
+ from __future__ import annotations
3
+
4
+ import hashlib
5
+ import json
6
+ from collections import Counter
7
+ from fractions import Fraction
8
+ from pathlib import Path
9
+
10
+ from acprof.analysis.audit import audit_result, number
11
+ from acprof.analysis.conditions import (
12
+ ALLOWED_RESOURCE_DIMENSIONS,
13
+ PURPOSES,
14
+ compare_profiles,
15
+ compare_workload_profiles,
16
+ comparison_profile,
17
+ workload_case_profile,
18
+ )
19
+ from acprof.artifacts import read_json_object
20
+ from acprof.hardware_conditions import HARDWARE_FIELDS, conditions_path
21
+ from acprof.metric_registry import METRICS
22
+ from acprof.platform import recorded_identity
23
+ from acprof.quality import QUALITY_FIELDS
24
+ from acprof.result_csv import measurement_key, read_result_csv
25
+ from acprof.runtime_settings import RUNTIME_ENV_NAMES
26
+
27
+ MEASUREMENT_OPTIONS = (
28
+ "profiling_mode", "warmup", "repeat", "repeat_in_window", "repeat_window_seconds",
29
+ "request_timeout_seconds", "sample_hz", "sampling_policy", "raw_sample_limit",
30
+ "idle_seconds", "idle_cooldown_seconds",
31
+ "compute_profile_tool", "execution_profile_tool", "prune_startup_oom",
32
+ "matrix_order", "matrix_seed", "dram_energy",
33
+ )
34
+ RUNTIME_ENVIRONMENT = (set(RUNTIME_ENV_NAMES) - {"ACPROF_REQUEST_TIMEOUT_S"}) | {"OMP_NUM_THREADS", "MKL_NUM_THREADS"}
35
+
36
+
37
+ def _canonical(value) -> str:
38
+ return json.dumps(value, sort_keys=True, ensure_ascii=False, allow_nan=False, separators=(",", ":"))
39
+
40
+
41
+ def _digest(value) -> str:
42
+ return hashlib.sha256(_canonical(value).encode()).hexdigest()
43
+
44
+
45
+ def _unknown(value) -> bool:
46
+ if value is None or value == "unknown":
47
+ return True
48
+ if isinstance(value, dict):
49
+ return not value or any(_unknown(item) for item in value.values())
50
+ if isinstance(value, list):
51
+ return not value or any(_unknown(item) for item in value)
52
+ return False
53
+
54
+
55
+ def _condition(left, right) -> dict:
56
+ if isinstance(left, dict) and isinstance(right, dict) and left and right:
57
+ statuses = {_condition(left.get(key), right.get(key))["status"] for key in left.keys() | right.keys()}
58
+ status = ("incompatible" if "incompatible" in statuses
59
+ else "unknown" if "unknown" in statuses else "compatible")
60
+ elif _unknown(left) or _unknown(right):
61
+ status = "unknown"
62
+ else:
63
+ status = "compatible" if left == right else "incompatible"
64
+ return {"status": status, "left": left, "right": right}
65
+
66
+
67
+ def _actual_workload(rows: list[dict]) -> dict | None:
68
+ """Pool observed request counts per case without losing the mixture weights."""
69
+ facts: dict[str, Counter] = {}
70
+ for row in rows:
71
+ row_key = measurement_key(row)
72
+ if row.get("status") not in {"ok", "warn"} or row_key[4] != "0":
73
+ continue
74
+ raw = row.get("workload_contract")
75
+ if not raw or raw.lower() in {"nan", "null", "none"}:
76
+ return None
77
+ summary = json.loads(raw)
78
+ if not isinstance(summary, dict) or summary.get("schema_version") != 1:
79
+ return None
80
+ variants, total = summary.get("variants"), summary.get("request_count")
81
+ if not isinstance(variants, list) or not variants or type(total) is not int or total <= 0:
82
+ return None
83
+ counts: list[int] = []
84
+ for variant in variants:
85
+ count = variant.get("count") if isinstance(variant, dict) else None
86
+ if type(count) is not int or count <= 0:
87
+ return None
88
+ counts.append(count)
89
+ if sum(counts) != total:
90
+ return None
91
+ key = _canonical(row_key[:4])
92
+ for variant in variants:
93
+ contract = variant.get("contract")
94
+ if not isinstance(contract, dict) or contract.get("schema_version") != 1:
95
+ return None
96
+ # Backend parameters are identity evidence, not workload semantics.
97
+ contract = {name: item for name, item in contract.items() if name != "runtime"}
98
+ facts.setdefault(key, Counter())[_canonical(contract)] += variant["count"]
99
+ return {key: {"request_count": sum(counts.values()), "variants": [
100
+ {"contract": json.loads(contract), "count": count,
101
+ "proportion": str(Fraction(count, sum(counts.values())))}
102
+ for contract, count in sorted(counts.items())]}
103
+ for key, counts in sorted(facts.items())} or None
104
+
105
+
106
+ def _distribution(case, projection=lambda contract: contract):
107
+ counts: Counter[str] = Counter()
108
+ for variant in case["variants"]:
109
+ counts[_canonical(projection(variant["contract"]))] += variant["count"]
110
+ total = case["request_count"]
111
+ return {value: str(Fraction(count, total)) for value, count in sorted(counts.items())}
112
+
113
+
114
+ def _dimensions(contract, prefix=""):
115
+ result = {}
116
+ for key, value in contract.items():
117
+ name = f"{prefix}.{key}" if prefix else key
118
+ if isinstance(value, dict):
119
+ result.update(_dimensions(value, name))
120
+ else:
121
+ result[name] = value
122
+ return result
123
+
124
+
125
+ def _workload_condition(left, right):
126
+ result = {"status": "unknown", "left": left, "right": right,
127
+ "request_counts_changed": None, "changed_dimensions": {},
128
+ "reason": "actual_workload_evidence_missing"}
129
+ if left is None or right is None:
130
+ return result
131
+ if left.keys() != right.keys():
132
+ return {**result, "status": "incompatible", "reason": "workload_cases_changed"}
133
+ result["request_counts_changed"] = any(left[key]["request_count"] != right[key]["request_count"] for key in left)
134
+ incomplete = any(_unknown(v["contract"]) for cases in (left, right) for case in cases.values() for v in case["variants"])
135
+ changed: dict[str, dict[str, dict[str, dict[str, str]]]] = {}
136
+ differing_cases = [key for key in left if _distribution(left[key]) != _distribution(right[key])]
137
+ if not differing_cases:
138
+ return {**result, "status": "unknown" if incomplete else "compatible",
139
+ "reason": "actual_workload_evidence_incomplete" if incomplete else "same_per_request_distribution"}
140
+ profile_status = compare_workload_profiles(*({key: workload_case_profile(case) for key, case in cases.items()}
141
+ for cases in (left, right)))
142
+ for key in differing_cases:
143
+ a, b = left[key], right[key]
144
+ names = set().union(*(_dimensions(v["contract"]).keys() for c in (a, b) for v in c["variants"]))
145
+ for name in sorted(names):
146
+ lhs = _distribution(a, lambda c, name=name: _dimensions(c).get(name))
147
+ rhs = _distribution(b, lambda c, name=name: _dimensions(c).get(name))
148
+ if lhs != rhs:
149
+ changed.setdefault(name, {})[key] = {"left": lhs, "right": rhs}
150
+ # Equal marginal distributions may still hide different joint mixtures.
151
+ if not changed:
152
+ changed["joint_distribution"] = {key: {"left": _distribution(left[key]), "right": _distribution(right[key])}
153
+ for key in differing_cases}
154
+ return {**result, "status": profile_status, "changed_dimensions": changed,
155
+ "reason": ("per_request_distribution_changed" if profile_status == "incompatible" else
156
+ "actual_workload_evidence_incomplete" if incomplete else "output_distribution_equivalence_unverified")}
157
+
158
+
159
+ def load_comparison_snapshot(source: str | Path) -> dict:
160
+ source = Path(source)
161
+ directory = source if source.is_dir() else source.parent
162
+ audit = audit_result(source)
163
+ issues = []
164
+
165
+ def read_json(name):
166
+ try:
167
+ from acprof.artifact_layout import ArtifactLayout
168
+ layout = ArtifactLayout.discover(source) if source.is_dir() else ArtifactLayout.from_csv(source)
169
+ path = layout.path(name)
170
+ if not path.exists():
171
+ return {}
172
+ return read_json_object(path, label=name)
173
+ except (OSError, ValueError) as error:
174
+ issues.append(f"{name}: {error}")
175
+ return {}
176
+
177
+ state = read_json("run_state.json")
178
+ metadata = read_json("static_meta.json")
179
+ plan = read_json("input_scale_plan.json")
180
+ from acprof.artifact_layout import ArtifactLayout
181
+ layout = ArtifactLayout.discover(source) if source.is_dir() else ArtifactLayout.from_csv(source)
182
+ hardware_payload = read_json(str(conditions_path(layout).relative_to(layout.root)))
183
+ hardware_cases = hardware_payload.get("cases", {})
184
+ if hardware_payload and (hardware_payload.get("schema_version") != 1 or not isinstance(hardware_cases, dict)):
185
+ issues.append("hardware_conditions.json: unsupported schema")
186
+ hardware_cases = {}
187
+ for case_id, record in list(hardware_cases.items()):
188
+ if not isinstance(record, dict):
189
+ issues.append(f"hardware_conditions.json: invalid case {case_id}")
190
+ hardware_cases[case_id] = {}
191
+
192
+ def object_field(parent, name):
193
+ value = parent.get(name, {})
194
+ if not isinstance(value, dict):
195
+ issues.append(f"{name}: 应为 JSON 对象")
196
+ return {}
197
+ return value
198
+
199
+ options = object_field(state, "options")
200
+ environment = object_field(options, "measurement_environment") if "measurement_environment" in options else None
201
+ runtime_environment = {key: value for key, value in (environment or {}).items() if key in RUNTIME_ENVIRONMENT}
202
+ measurement_environment = (_digest({key: value for key, value in environment.items()
203
+ if key not in RUNTIME_ENVIRONMENT})
204
+ if isinstance(environment, dict) else None)
205
+ entries = plan.get("entries")
206
+ inputs = None
207
+ if plan.get("schema_version") == 2 and isinstance(entries, list) and entries:
208
+ if all(isinstance(entry, dict) and isinstance(entry.get("payload"), dict)
209
+ and entry.get("input_scale") is not None for entry in entries):
210
+ try:
211
+ inputs = _digest([{key: entry[key] for key in ("input_scale", "payload")} for entry in entries])
212
+ except (ValueError, TypeError) as error:
213
+ issues.append(f"input_scale_plan.json: {error}")
214
+ threads = {}
215
+ validation = object_field(metadata, "runtime_validation")
216
+ devices = object_field(validation, "devices")
217
+ # The independent probe injects quota-derived TORCH_NUM_THREADS, while the
218
+ # ordinary server preserves runtime defaults. Its effective count only
219
+ # establishes a shared setting when this run explicitly requested threads.
220
+ thread_names: tuple[str, ...] = (("ACPROF_ONNX_INTRA_OP_THREADS",) if metadata.get("runtime_backend") == "onnxruntime" else ())
221
+ thread_names += ("ACPROF_RUNTIME_THREADS", "TORCH_NUM_THREADS")
222
+ thread_request = next(((environment or {})[name] for name in thread_names if name in (environment or {})), None)
223
+ try:
224
+ explicit_threads = thread_request is not None and not isinstance(thread_request, bool) and int(thread_request) > 0
225
+ except (TypeError, ValueError):
226
+ explicit_threads = False
227
+ for device in devices:
228
+ result = object_field(devices, device)
229
+ parameters = object_field(result, "runtime_parameters")
230
+ effective = object_field(parameters, "effective")
231
+ observed = effective.get("threads")
232
+ threads[device] = (observed if explicit_threads and result.get("status") == "ok"
233
+ and type(observed) is int and observed > 0 else None)
234
+ case_ids = set()
235
+ rows: list[dict[str, str]] = []
236
+ try:
237
+ _, rows = read_result_csv(directory / "result_layers.json" if source.is_dir() else source)
238
+ actual = _actual_workload(rows)
239
+ case_ids = {f"{int(float(row['cpu_cores']))}c_{int(float(row['mem_cap_gb']))}g_{row['gpu_mode']}"
240
+ for row in rows if row.get("status") in {"ok", "warn"}}
241
+
242
+ except (OSError, ValueError, TypeError, AttributeError) as error:
243
+ actual = None
244
+ issues.append(f"actual workload: {error}")
245
+ # Only the input payload and semantic conditions are compared. Model/export,
246
+ # packages and backend hashes continue to belong to the existing run identity.
247
+ host = object_field(state, "host")
248
+ measured_rows = [row for row in rows if row.get("status") == "ok" and measurement_key(row)[4] == "0"]
249
+ return {
250
+ **recorded_identity(metadata),
251
+ "metric_availability": {name: any(number(row.get(name)) is not None
252
+ for row in measured_rows)
253
+ for name, metric in METRICS.items() if metric.kind == "number"},
254
+ "run_id": state.get("run_id"), "result_csv": audit["result_csv"],
255
+ "run_status": audit["run_status"], "measurement_status": audit["measurement_status"],
256
+ "missing_metrics": audit["missing_metrics"],
257
+ **{key: audit[key] for key in QUALITY_FIELDS},
258
+ "valid": audit["valid"] and not issues, "issues": issues,
259
+ "planned_input_entries": ([{key: entry[key] for key in ("input_scale", "payload")} for entry in entries]
260
+ if inputs is not None else None),
261
+ "conditions": {
262
+ "comparability_class": recorded_identity(metadata)["comparability_class"],
263
+ "task_semantics": {key: plan.get(key) for key in ("task_family", "pipeline_tag", "scenario")},
264
+ "planned_inputs": inputs,
265
+ "resources": {**{key: options.get(key) for key in ("cpus", "mems", "gpus", "batch_size")},
266
+ **{key: metadata.get(key) for key in ("cgroup_version", "cgroup_collection_mode")}},
267
+ "runtime_threads": threads or None,
268
+ "measurement_protocol": {**{key: options.get(key) for key in MEASUREMENT_OPTIONS},
269
+ "measurement_environment": measurement_environment,
270
+ "static_schema_version": metadata.get("schema_version")},
271
+ "quality_constraints": plan.get("quality_constraints"),
272
+ "actual_workload": actual,
273
+ },
274
+ "hardware": {
275
+ name: {case_id: hardware_cases.get(case_id, {}).get(name) for case_id in sorted(case_ids)} or None
276
+ for name in HARDWARE_FIELDS
277
+ },
278
+ "identity": {**{key: metadata.get(key) for key in (
279
+ "model_name", "model_revision", "runtime_backend", "image_id", "runtime_environment")},
280
+ "model_source": metadata.get("model_source", "huggingface"),
281
+ "runtime_requested_environment": runtime_environment,
282
+ **{key: host.get(key) for key in ("source_sha256", "packages_sha256")}},
283
+ }
284
+
285
+
286
+ def compare_results(left: str | Path, right: str | Path, *, purpose: str = "same-hardware") -> dict:
287
+ """Return compatible/incompatible/unknown for recorded comparison conditions.
288
+
289
+ Quality constraints describe a shared target, not proof that either model
290
+ meets it. Missing legacy evidence is unknown and never reconstructed.
291
+ """
292
+ if purpose not in PURPOSES:
293
+ raise ValueError(f"comparison purpose must be one of {PURPOSES}")
294
+ snapshots = {"left": load_comparison_snapshot(left), "right": load_comparison_snapshot(right)}
295
+ lhs, rhs = snapshots.values()
296
+ conditions = {name: _condition(value, rhs["conditions"][name])
297
+ for name, value in lhs["conditions"].items()}
298
+ conditions["actual_workload"] = _workload_condition(lhs["conditions"]["actual_workload"],
299
+ rhs["conditions"]["actual_workload"])
300
+ if purpose == "resource-scaling":
301
+ def resource_workload(cases):
302
+ if cases is None:
303
+ return None
304
+ normalized: dict[str, dict] = {}
305
+ for key, value in cases.items():
306
+ projected = _canonical(json.loads(key)[2:])
307
+ if projected in normalized and workload_case_profile(normalized[projected]) != workload_case_profile(value):
308
+ return None
309
+ normalized[projected] = value
310
+ return normalized
311
+ conditions["actual_workload"] = _workload_condition(resource_workload(lhs["conditions"]["actual_workload"]),
312
+ resource_workload(rhs["conditions"]["actual_workload"]))
313
+ for name in HARDWARE_FIELDS:
314
+ left_value, right_value = lhs["hardware"].get(name), rhs["hardware"].get(name)
315
+ item = _condition(left_value, right_value)
316
+ if purpose == "cross-hardware" and name != "runtime_threads":
317
+ # A known model difference cannot turn an unknown power policy into
318
+ # verified evidence. Complete but different CPU-ID maps are expected.
319
+ item["status"] = ("unknown" if _unknown(left_value) or _unknown(right_value)
320
+ else "compatible" if left_value == right_value else "expected_difference")
321
+ conditions[f"hardware_{name}"] = item
322
+ # Observed formal-server threads supersede independent-probe approximations.
323
+ if not _unknown(lhs["hardware"].get("runtime_threads")) and not _unknown(rhs["hardware"].get("runtime_threads")):
324
+ conditions["runtime_threads"] = conditions["hardware_runtime_threads"]
325
+ profiles = {side: comparison_profile(snapshot, purpose=purpose) for side, snapshot in snapshots.items()}
326
+ qualification = compare_profiles(profiles["left"], profiles["right"])
327
+ for name, item in conditions.items():
328
+ statuses = {value for key, value in qualification["checks"].items() if key == name or key.startswith(name + ".")}
329
+ item["status"] = ("incompatible" if "incompatible" in statuses else "unknown" if "unknown" in statuses else
330
+ "expected_difference" if "expected_difference" in statuses else "compatible")
331
+ valid = lhs["valid"] and rhs["valid"]
332
+ status = qualification["status"]
333
+ differences = {name: {"left": value, "right": rhs["identity"][name]}
334
+ for name, value in lhs["identity"].items() if value != rhs["identity"][name]}
335
+ environment_condition = conditions["comparability_class"]
336
+ warnings = []
337
+ if environment_condition["status"] != "compatible":
338
+ warnings.append(f"Environment comparison blocked: {lhs['comparability_class']} vs "
339
+ f"{rhs['comparability_class']}. WSL measurements must not be treated as "
340
+ "native Linux measurements; unknown provenance cannot enter a Native baseline.")
341
+ metric_comparability = {}
342
+ for name in lhs["metric_availability"]:
343
+ available = lhs["metric_availability"][name] and rhs["metric_availability"][name]
344
+ metric_comparability[name] = {
345
+ "status": "comparable" if status == "compatible" and available else "not comparable",
346
+ "reason": ("missing_measurement" if not available else
347
+ "environment_" + environment_condition["status"]
348
+ if environment_condition["status"] != "compatible" else
349
+ "conditions_" + status if status != "compatible" else ""),
350
+ }
351
+ return {"schema_version": 2, "purpose": purpose, "status": status, "valid": valid,
352
+ "allowed_resource_dimensions": ALLOWED_RESOURCE_DIMENSIONS if purpose == "resource-scaling" else [],
353
+ "comparison_reasons": qualification["reasons"],
354
+ "warnings": warnings, "metric_comparability": metric_comparability,
355
+ "native_baseline_eligible": status == "compatible" and lhs["comparability_class"] == "native_linux",
356
+ "scope": "recorded_comparison_conditions_not_model_quality_or_resume_identity",
357
+ "limitations": ["hardware conditions are boundary observations, not proof of continuous isolation or identical thermal state",
358
+ "resource-scaling allows CPU and memory quotas; changing both cannot isolate either causal effect",
359
+ "missing hardware or effective thread evidence remains unknown; affinity does not prove exclusive CPU use",
360
+ "matching quality constraints do not prove either model meets them"],
361
+ "conditions": conditions, "expected_differences": differences,
362
+ "experiments": {side: {key: snapshot[key] for key in ("run_id", "result_csv", "valid", "issues", "environment_class",
363
+ "run_status", "measurement_status", "missing_metrics", *QUALITY_FIELDS)}
364
+ for side, snapshot in snapshots.items()}}
@@ -0,0 +1,223 @@
1
+ """Render compatibility evidence without reinterpreting exception messages."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import csv
6
+ import io
7
+ import json
8
+ import math
9
+ from pathlib import Path
10
+ from typing import TextIO
11
+
12
+ from acprof.artifacts import atomic_write
13
+ from acprof.failures import Failure, compatibility_status
14
+ from acprof.model_repository import MODEL_SOURCES
15
+ from acprof.quality import combine_quality, read_quality, summarize_quality
16
+
17
+ MAX_COMPATIBILITY_ARTIFACT_BYTES = 4 * 1024 * 1024
18
+
19
+
20
+ def _finite_json_number(raw: str) -> float:
21
+ value = float(raw)
22
+ if not math.isfinite(value):
23
+ raise ValueError("non-finite number")
24
+ return value
25
+
26
+
27
+ def _read_recorded_object(path: Path) -> dict:
28
+ with path.open("rb") as stream:
29
+ content = stream.read(MAX_COMPATIBILITY_ARTIFACT_BYTES + 1)
30
+ if len(content) > MAX_COMPATIBILITY_ARTIFACT_BYTES:
31
+ raise ValueError("artifact exceeds the 4 MiB read limit")
32
+ payload = json.loads(content, parse_float=_finite_json_number,
33
+ parse_constant=_finite_json_number)
34
+ if not isinstance(payload, dict):
35
+ raise ValueError("top-level JSON value must be an object")
36
+ return payload
37
+
38
+
39
+ def _recorded_evidence_failure(path: Path, error: Exception) -> dict:
40
+ return Failure(
41
+ "compatibility_report", "recorded_evidence_invalid", f"{path.name}: {error}",
42
+ evidence={"artifact": str(path.resolve()), "artifact_name": path.name},
43
+ exception_type=type(error).__name__,
44
+ ).to_dict()
45
+
46
+
47
+ class _RecordedSourceError(ValueError):
48
+ def __init__(self, artifact: str, detail: str):
49
+ super().__init__(detail)
50
+ self.artifact = artifact
51
+
52
+
53
+ def _recorded_model_source(metadata: dict, resolution: dict) -> str:
54
+ provenance = resolution.get("provenance", {})
55
+ if not isinstance(provenance, dict):
56
+ raise _RecordedSourceError("model_resolution.json", "model provenance must be an object")
57
+ sources = provenance.get("sources", {})
58
+ if not isinstance(sources, dict):
59
+ raise _RecordedSourceError("model_resolution.json", "model provenance sources must be an object")
60
+ snapshot = sources.get("repository_snapshot", {})
61
+ if not isinstance(snapshot, dict):
62
+ raise _RecordedSourceError("model_resolution.json", "repository snapshot provenance must be an object")
63
+ source = metadata.get("model_source", snapshot.get("source", "huggingface"))
64
+ if not isinstance(source, str) or source not in MODEL_SOURCES:
65
+ raise _RecordedSourceError("static_meta.json" if "model_source" in metadata else "model_resolution.json",
66
+ "recorded model source must be huggingface/modelscope")
67
+ if "source" in snapshot and snapshot["source"] != source:
68
+ raise _RecordedSourceError("model_resolution.json", "recorded source conflicts with static_meta.json")
69
+ return source
70
+
71
+
72
+ def result_status(row):
73
+ if row.get("failure"):
74
+ return compatibility_status(row["failure"])
75
+ if row.get("full_profile_complete"):
76
+ if row.get("quality_status") == "unknown":
77
+ return "full_success_quality_unknown"
78
+ return "full_success_with_warnings" if any(check["severity"] == "warning" for check in row.get("quality_checks", [])) else "full_success"
79
+ return row.get("runtime_status", "unverified")
80
+
81
+
82
+ def write_compatibility_report(root: Path, rows: list[dict]):
83
+ fields = ("model_id", "revision", "status", "stage", "reason_code", "detail", "device",
84
+ "runtime_profile", "retryability", "evidence", "full_profile_complete", "quality_status", "quality_checks",
85
+ "quality_reasons", "auto_selection_eligible", "attempt_id", "attempt_path", "configuration_sha256", "source")
86
+ stream = io.StringIO(newline="")
87
+ writer = csv.DictWriter(stream, fieldnames=fields)
88
+ writer.writeheader()
89
+ lines = ["# Compatibility report", "", "状态仅适用于所记录的 source、revision、设备、环境和验证范围。", "",
90
+ "| Source | Model | Revision | Status | reason_code | Detail |", "| --- | --- | --- | --- | --- | --- |"]
91
+ for row in rows:
92
+ failure = row.get("failure") or {}
93
+ quality = summarize_quality(row.get("quality_checks"))
94
+ if row.get("quality_status") == "unknown":
95
+ quality = combine_quality([quality, summarize_quality(None)])
96
+ if "quality_reasons" in row:
97
+ quality.update({key: row[key] for key in quality if key in row})
98
+ row = {"source": "huggingface", **row, **quality}
99
+ checks = row["quality_checks"]
100
+ values = {key: failure.get(key, row.get(key, "")) for key in fields}
101
+ values.update(status=result_status(row), evidence=json.dumps(failure.get("evidence", row.get("evidence", {})), ensure_ascii=False),
102
+ quality_checks=json.dumps(checks, ensure_ascii=False),
103
+ quality_reasons=json.dumps(row["quality_reasons"], ensure_ascii=False))
104
+ writer.writerow(values)
105
+ def cell(value):
106
+ return str(value).replace("|", "\\|").replace("\n", " ")
107
+ detail = failure.get("detail", "") or ", ".join(check["code"] for check in checks)
108
+ lines.append("| " + " | ".join(cell(value) for value in (
109
+ row["source"], row["model_id"], values["revision"], values["status"], values["reason_code"], detail)) + " |")
110
+ csv_text = stream.getvalue()
111
+ report_text = "\n".join(lines) + "\n"
112
+ def write_text(stream: TextIO, content: str) -> None:
113
+ stream.write(content)
114
+
115
+ atomic_write(root / "models.csv", lambda output: write_text(output, csv_text))
116
+ atomic_write(root / "REPORT.md", lambda output: write_text(output, report_text))
117
+
118
+
119
+ def report_results(sources: list[Path], output: Path) -> dict:
120
+ """Read recorded results, including failed preflight directories; never rerun."""
121
+ from acprof.artifact_layout import ArtifactLayout
122
+ from acprof.artifacts import atomic_write_json
123
+ rows = []
124
+ for source in sources:
125
+ layout = ArtifactLayout.discover(source)
126
+ invalid = []
127
+
128
+ def read(name):
129
+ path = layout.path(name)
130
+ if not path.is_file():
131
+ return {}
132
+ try:
133
+ return _read_recorded_object(path)
134
+ except (OSError, UnicodeError, ValueError, RecursionError) as error:
135
+ invalid.append(_recorded_evidence_failure(path, error))
136
+ return {}
137
+
138
+ def invalid_shape(name, detail):
139
+ invalid.append(_recorded_evidence_failure(layout.path(name), ValueError(detail)))
140
+
141
+ def validated_failure(name, value, location):
142
+ try:
143
+ if not isinstance(value, dict):
144
+ raise ValueError("must be an object")
145
+ failure = Failure(**value)
146
+ if (not isinstance(failure.stage, str) or not failure.stage
147
+ or not isinstance(failure.detail, str)
148
+ or not isinstance(failure.evidence, dict)):
149
+ raise ValueError("has invalid typed failure fields")
150
+ return failure.to_dict()
151
+ except (TypeError, ValueError) as error:
152
+ invalid_shape(name, f"{location} must be a valid typed failure: {error}")
153
+ return None
154
+
155
+ metadata, resolution = read("static_meta.json"), read("model_resolution.json")
156
+ capability = read("capability_report.json")
157
+ validation = read("runtime_validation.json")
158
+ failure_report = read("runtime_failures.json")
159
+ if not any((metadata, resolution, capability, validation, failure_report, invalid)):
160
+ raise ValueError(f"No recorded compatibility evidence: {source}")
161
+
162
+ invalid_identity = any(problem["evidence"]["artifact_name"] in {
163
+ "static_meta.json", "model_resolution.json"} for problem in invalid)
164
+ try:
165
+ # Corrupt identity evidence is not a historical document with missing fields.
166
+ model_source = "unknown" if invalid_identity else _recorded_model_source(metadata, resolution)
167
+ except _RecordedSourceError as error:
168
+ invalid_shape(error.artifact, str(error))
169
+ model_source = "unknown"
170
+
171
+ recorded_failures = failure_report.get("failures", [])
172
+ if not isinstance(recorded_failures, list):
173
+ invalid_shape("runtime_failures.json", "failures must be a list")
174
+ recorded_failures = []
175
+ else:
176
+ recorded_failures = [
177
+ failure for index, value in enumerate(recorded_failures)
178
+ if (failure := validated_failure(
179
+ "runtime_failures.json", value, f"failures[{index}]",
180
+ )) is not None
181
+ ]
182
+
183
+ devices = validation.get("devices", {})
184
+ if not isinstance(devices, dict):
185
+ invalid_shape("runtime_validation.json", "devices must be an object")
186
+ devices = {}
187
+ device_failures = []
188
+ for device, value in devices.items():
189
+ if not isinstance(value, dict):
190
+ invalid_shape("runtime_validation.json", f"device {device!r} must be an object")
191
+ continue
192
+ failure = value.get("failure")
193
+ if failure is None:
194
+ continue
195
+ failure = validated_failure(
196
+ "runtime_validation.json", failure, f"device {device!r} failure",
197
+ )
198
+ if failure is not None:
199
+ device_failures.append(failure)
200
+
201
+ resolution_failure = resolution.get("failure")
202
+ if resolution_failure is not None:
203
+ resolution_failure = validated_failure(
204
+ "model_resolution.json", resolution_failure, "failure",
205
+ )
206
+ failures = [*recorded_failures, *device_failures]
207
+ if resolution_failure:
208
+ failures.append(resolution_failure)
209
+ failures.extend(invalid)
210
+
211
+ quality = read_quality(source)
212
+ rows.append({"source": model_source, "model_id": metadata.get("model_id", resolution.get("model_id", source.name)),
213
+ "revision": metadata.get("model_revision", resolution.get("model_revision", "unknown")),
214
+ "runtime_profile": metadata.get("runtime_profile_id", resolution.get("runtime_profile", "")),
215
+ "full_profile_complete": capability.get("full_profile_complete") is True,
216
+ "runtime_status": validation.get("status", "unverified"),
217
+ **quality, "failure": failures[0] if failures else None,
218
+ "failures": failures, "evidence": {"result_directory": str(source.resolve())}})
219
+ output.mkdir(parents=True, exist_ok=False)
220
+ report = {"schema_version": 1, "scope": "recorded_results; no_reexecution", "rows": rows}
221
+ atomic_write_json(output / "coverage.json", report)
222
+ write_compatibility_report(output, rows)
223
+ return report