mtplx 2.0.0__tar.gz → 2.0.2__tar.gz

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 (332) hide show
  1. {mtplx-2.0.0 → mtplx-2.0.2}/CHANGELOG.md +136 -0
  2. {mtplx-2.0.0 → mtplx-2.0.2}/PKG-INFO +1 -1
  3. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/artifacts.py +4 -0
  4. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/attention_split.py +4 -0
  5. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/cli.py +5 -3
  6. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/commands/public.py +42 -10
  7. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/default_models.py +30 -9
  8. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/fast_sampling.py +29 -8
  9. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/gdn_capture.py +17 -8
  10. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/generation.py +141 -38
  11. mtplx-2.0.2/mtplx/kernel_selfcheck.py +504 -0
  12. mtplx-2.0.2/mtplx/loop_guard.py +424 -0
  13. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/model_catalog.py +20 -2
  14. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/nax_verify.py +90 -7
  15. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/profiles.py +2 -0
  16. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/reasoning_codecs.py +8 -1
  17. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/runtime.py +6 -0
  18. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/sampling.py +28 -7
  19. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/server/openai.py +198 -13
  20. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/ui/onboarding.py +37 -8
  21. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/verify_kernels.py +85 -9
  22. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/version.py +2 -2
  23. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx.egg-info/PKG-INFO +1 -1
  24. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx.egg-info/SOURCES.txt +7 -21
  25. {mtplx-2.0.0 → mtplx-2.0.2}/pyproject.toml +1 -1
  26. mtplx-2.0.2/scripts/make_fp16_precision_sibling.py +207 -0
  27. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_default_models.py +20 -0
  28. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_generation_sustained.py +189 -1
  29. mtplx-2.0.2/tests/test_kernel_selfcheck.py +151 -0
  30. mtplx-2.0.2/tests/test_loop_guard.py +588 -0
  31. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_model_catalog.py +15 -5
  32. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_nax_verify.py +66 -0
  33. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_onboarding.py +47 -0
  34. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_public_cli.py +51 -2
  35. mtplx-2.0.2/tests/test_reasoning_stream_split.py +37 -0
  36. mtplx-2.0.2/tests/test_scoped_reasoning_history.py +475 -0
  37. mtplx-2.0.0/scripts/kvcache_exactness_audit_20260703.py +0 -303
  38. mtplx-2.0.0/scripts/kvcache_soak_20260703.py +0 -227
  39. mtplx-2.0.0/scripts/kvcache_warm_probe_20260703.py +0 -357
  40. mtplx-2.0.0/scripts/ocspeed-20260703/abba_final.sh +0 -85
  41. mtplx-2.0.0/scripts/ocspeed-20260703/accept_depth_probe.py +0 -209
  42. mtplx-2.0.0/scripts/ocspeed-20260703/arm_runner.sh +0 -38
  43. mtplx-2.0.0/scripts/ocspeed-20260703/forward_depth_bisect.py +0 -132
  44. mtplx-2.0.0/scripts/ocspeed-20260703/longgen_probe.py +0 -85
  45. mtplx-2.0.0/scripts/ocspeed-20260703/loop_degradation_probe.py +0 -81
  46. mtplx-2.0.0/scripts/ocspeed-20260703/paired_longgen.sh +0 -61
  47. mtplx-2.0.0/scripts/ocspeed-20260703/sdpa_microbench.py +0 -66
  48. mtplx-2.0.0/scripts/ocspeed-20260703/snapshot_tail.py +0 -60
  49. mtplx-2.0.0/scripts/prodqa-20260703/candidate_truecold_20260703.py +0 -101
  50. mtplx-2.0.0/scripts/prodqa-20260703/cold_pair_recheck.py +0 -88
  51. mtplx-2.0.0/scripts/prodqa-20260703/competitors_20260703.sh +0 -63
  52. mtplx-2.0.0/scripts/prodqa-20260703/decode_gap_matrix.py +0 -110
  53. mtplx-2.0.0/scripts/prodqa-20260703/hermes_pty_driver.py +0 -87
  54. mtplx-2.0.0/scripts/prodqa-20260703/omlx_probe.py +0 -63
  55. mtplx-2.0.0/scripts/prodqa-20260703/pi_pty_driver.py +0 -77
  56. mtplx-2.0.0/scripts/prodqa-20260703/pillar_ab_20260703.py +0 -169
  57. mtplx-2.0.0/scripts/prodqa-20260703/tool_gauntlet_20260703.sh +0 -40
  58. {mtplx-2.0.0 → mtplx-2.0.2}/CITATION.cff +0 -0
  59. {mtplx-2.0.0 → mtplx-2.0.2}/LICENSE +0 -0
  60. {mtplx-2.0.0 → mtplx-2.0.2}/MANIFEST.in +0 -0
  61. {mtplx-2.0.0 → mtplx-2.0.2}/NOTICE +0 -0
  62. {mtplx-2.0.0 → mtplx-2.0.2}/README.md +0 -0
  63. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/__init__.py +0 -0
  64. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/adaptive.py +0 -0
  65. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/app_settings.py +0 -0
  66. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/attention_context.py +0 -0
  67. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/backends/__init__.py +0 -0
  68. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/backends/deepseek_mtp.py +0 -0
  69. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/backends/descriptors.py +0 -0
  70. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/backends/gemma4_assistant.py +0 -0
  71. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/backends/glm_mtp.py +0 -0
  72. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/backends/mimo_mtp.py +0 -0
  73. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/backends/nemotron_h_mtp.py +0 -0
  74. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/backends/qwen3_next.py +0 -0
  75. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/backends/registry.py +0 -0
  76. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/backends/step3p5_mtp.py +0 -0
  77. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/batching/__init__.py +0 -0
  78. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/batching/admission.py +0 -0
  79. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/batching/buckets.py +0 -0
  80. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/batching/scheduler.py +0 -0
  81. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/batching/state.py +0 -0
  82. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/__init__.py +0 -0
  83. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/prompts/aime_2026.jsonl +0 -0
  84. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/prompts/calibration_coding.jsonl +0 -0
  85. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/prompts/default.jsonl +0 -0
  86. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/prompts/flappy.jsonl +0 -0
  87. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/prompts/long_code.jsonl +0 -0
  88. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/prompts/long_code_uncapped.jsonl +0 -0
  89. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/prompts/python_modules_long.jsonl +0 -0
  90. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/runners/__init__.py +0 -0
  91. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/runners/aime.py +0 -0
  92. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/runners/batch_equivalence.py +0 -0
  93. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/runners/capture_commit_equivalence.py +0 -0
  94. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/runners/competitor_baselines.py +0 -0
  95. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/runners/contract_probe.py +0 -0
  96. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/runners/harness.py +0 -0
  97. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/runners/mtp1_gate.py +0 -0
  98. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/runners/mtp1_sampler_smoke.py +0 -0
  99. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/runners/mtp_adaptive.py +0 -0
  100. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/runners/mtp_chain_probe.py +0 -0
  101. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/runners/mtp_depth_grid.py +0 -0
  102. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/runners/mtp_depth_sweep.py +0 -0
  103. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/runners/mtp_tree_probe.py +0 -0
  104. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/runners/multi_qmv_probe.py +0 -0
  105. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/runners/preflight.py +0 -0
  106. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/runners/runtime_smoke.py +0 -0
  107. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/runners/session_bank.py +0 -0
  108. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/runners/truth.py +0 -0
  109. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/runners/verify_profile.py +0 -0
  110. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/runners/verify_qmm_probe.py +0 -0
  111. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/runners/verify_ratio.py +0 -0
  112. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/schema.py +0 -0
  113. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/validators/__init__.py +0 -0
  114. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/validators/aime.py +0 -0
  115. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/benchmarks/validators/basic.py +0 -0
  116. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/block_attention.py +0 -0
  117. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/cache_bank/__init__.py +0 -0
  118. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/cache_bank/codec.py +0 -0
  119. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/cache_bank/cold_tier.py +0 -0
  120. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/cache_state.py +0 -0
  121. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/chat_encoding.py +0 -0
  122. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/commands/__init__.py +0 -0
  123. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/commands/forge.py +0 -0
  124. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/compressed_tensors.py +0 -0
  125. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/config.py +0 -0
  126. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/constants.py +0 -0
  127. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/correctors/__init__.py +0 -0
  128. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/correctors/diagonal_affine.py +0 -0
  129. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/correctors/low_rank.py +0 -0
  130. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/daemon_client.py +0 -0
  131. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/dashboard/__init__.py +0 -0
  132. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/dashboard/_static/assets/index-BYd4MFty.css +0 -0
  133. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/dashboard/_static/assets/index-COqTDxL-.js +0 -0
  134. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/dashboard/_static/index.html +0 -0
  135. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/deepseek_mtp_patch.py +0 -0
  136. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/diagnostics.py +0 -0
  137. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/draft_lm_head.py +0 -0
  138. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/draft_sampling.py +0 -0
  139. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/engine_session.py +0 -0
  140. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/env.py +0 -0
  141. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/errors.py +0 -0
  142. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/expert_layout.py +0 -0
  143. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/fan_mode.py +0 -0
  144. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/gemma4_pair.py +0 -0
  145. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/glm_mtp_patch.py +0 -0
  146. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/graphbank.py +0 -0
  147. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/hardware.py +0 -0
  148. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/hf_loader.py +0 -0
  149. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/kernels/__init__.py +0 -0
  150. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/kernels/copy_leaf.py +0 -0
  151. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/kernels/fused_norm.py +0 -0
  152. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/kernels/lm_head_topk.py +0 -0
  153. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/kernels/logits_topk.py +0 -0
  154. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/kernels/native_gdn_tail.py +0 -0
  155. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/kernels/sdpa_2pass.py +0 -0
  156. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/kernels/sdpa_2pass_paged.py +0 -0
  157. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/kernels/sdpa_2pass_paged_q8.py +0 -0
  158. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/kernels/sdpa_gqa_packed.py +0 -0
  159. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/kernels/verify_mlp_fused.py +0 -0
  160. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/kpi/__init__.py +0 -0
  161. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/kpi/reference_vllm.py +0 -0
  162. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/kpi/runtime_kpis.py +0 -0
  163. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/kv_quant.py +0 -0
  164. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/mimo_mtp_patch.py +0 -0
  165. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/model_scheduler.py +0 -0
  166. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/mtp_activation_stats.py +0 -0
  167. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/mtp_adapters.py +0 -0
  168. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/mtp_patch.py +0 -0
  169. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/native_mlp.py +0 -0
  170. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/nemotron_h_mtp_patch.py +0 -0
  171. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/opencode.py +0 -0
  172. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/pi.py +0 -0
  173. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/prefill_bench.py +0 -0
  174. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/proposal_reranker.py +0 -0
  175. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/runtime_options.py +0 -0
  176. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/server/__init__.py +0 -0
  177. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/server/dashboard_state.py +0 -0
  178. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/server/omlx_bridge/__init__.py +0 -0
  179. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/server/omlx_bridge/adapter.py +0 -0
  180. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/server/omlx_bridge/thinking.py +0 -0
  181. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/server/omlx_bridge/tool_calling.py +0 -0
  182. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/server_urls.py +0 -0
  183. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/session_bank.py +0 -0
  184. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/speculative.py +0 -0
  185. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/step3p5_mtp_patch.py +0 -0
  186. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/swival.py +0 -0
  187. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/thermal.py +0 -0
  188. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/thermal_sidecar.py +0 -0
  189. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/trace_parity.py +0 -0
  190. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/turboquant.py +0 -0
  191. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/ui/__init__.py +0 -0
  192. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/ui/banner.py +0 -0
  193. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/ui/chat_printer.py +0 -0
  194. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/ui/download_progress.py +0 -0
  195. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/ui/panels.py +0 -0
  196. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/ui/progress.py +0 -0
  197. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/verify_qmv.py +0 -0
  198. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/vision/__init__.py +0 -0
  199. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/vision/processing.py +0 -0
  200. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/vision/qwen3_vl_tower.py +0 -0
  201. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx/vision/splice.py +0 -0
  202. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx.egg-info/dependency_links.txt +0 -0
  203. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx.egg-info/entry_points.txt +0 -0
  204. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx.egg-info/requires.txt +0 -0
  205. {mtplx-2.0.0 → mtplx-2.0.2}/mtplx.egg-info/top_level.txt +0 -0
  206. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/agent_user_path_qa.py +0 -0
  207. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/aime_serve_gate.py +0 -0
  208. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/aime_shape_memory_bench.py +0 -0
  209. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/collect_mtp_activation_stats.py +0 -0
  210. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/collect_mtp_hidden_calib.py +0 -0
  211. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/combine_mtp_adapters.py +0 -0
  212. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/compiled_verify_exactness.py +0 -0
  213. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/convert_step37_to_mtplx_step3p5.py +0 -0
  214. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/eval_mtp_corrector.py +0 -0
  215. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/filter_mtp_adapter.py +0 -0
  216. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/fresh_venv_smoke.sh +0 -0
  217. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/hygiene_scan.sh +0 -0
  218. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/install_macos.sh +0 -0
  219. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/install_preview_global.sh +0 -0
  220. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/midform_gate.py +0 -0
  221. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/opencode_concurrency_qa.py +0 -0
  222. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/phase0h_paged_verifier_exactness.py +0 -0
  223. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/probe_mlx_pr3026_qsdpa.py +0 -0
  224. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/probe_mx_compile_buckets.py +0 -0
  225. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/probe_paged_gqa_sdpa_routes.py +0 -0
  226. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/r1_chisquare_verifier_correctness.py +0 -0
  227. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/release_macos_v1.sh +0 -0
  228. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/run_context_degradation_diagnostics.py +0 -0
  229. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/serve_openai_mtplx.py +0 -0
  230. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/session_cache_followup_qa.py +0 -0
  231. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/sparkle_rehearsal_kit.sh +0 -0
  232. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/step_acceptance.py +0 -0
  233. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/step_smoke.py +0 -0
  234. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/train_mtp_adapter_c4.py +0 -0
  235. {mtplx-2.0.0 → mtplx-2.0.2}/scripts/validate_step_mtp_injector.py +0 -0
  236. {mtplx-2.0.0 → mtplx-2.0.2}/setup.cfg +0 -0
  237. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_adaptive.py +0 -0
  238. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_aime_serve_gate.py +0 -0
  239. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_artifacts.py +0 -0
  240. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_attention_split.py +0 -0
  241. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_background_warmup.py +0 -0
  242. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_batching_foundation.py +0 -0
  243. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_cache_bank.py +0 -0
  244. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_cache_state.py +0 -0
  245. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_cli_parity_tools.py +0 -0
  246. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_compressed_tensors.py +0 -0
  247. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_config.py +0 -0
  248. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_config_profile_precedence.py +0 -0
  249. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_context_degradation_profiles.py +0 -0
  250. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_correctors.py +0 -0
  251. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_daemon_client.py +0 -0
  252. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_dashboard_endpoints.py +0 -0
  253. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_diagnostics.py +0 -0
  254. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_download_progress.py +0 -0
  255. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_draft_lm_head.py +0 -0
  256. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_engine_session_concurrency.py +0 -0
  257. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_engine_session_env.py +0 -0
  258. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_fast_sampling.py +0 -0
  259. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_forge_cli.py +0 -0
  260. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_generation_store_on_prefill.py +0 -0
  261. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_graphbank_compiled_verify.py +0 -0
  262. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_hf_loader.py +0 -0
  263. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_hygiene_scan.py +0 -0
  264. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_idle_postcommit_subagent.py +0 -0
  265. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_lazy_snapshot_cow.py +0 -0
  266. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_max_idle_watchdog.py +0 -0
  267. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_max_lifecycle.py +0 -0
  268. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_metal_memory_caps.py +0 -0
  269. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_midform_gate.py +0 -0
  270. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_model_scheduler.py +0 -0
  271. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_mtp_activation_stats.py +0 -0
  272. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_mtp_adapters.py +0 -0
  273. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_mtp_depth_sweep.py +0 -0
  274. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_mtp_patch.py +0 -0
  275. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_no_mlx_imports.py +0 -0
  276. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_omlx_bridge.py +0 -0
  277. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_openai_bridge.py +0 -0
  278. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_opencode.py +0 -0
  279. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_penalties.py +0 -0
  280. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_penalty_request_wiring.py +0 -0
  281. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_persistent_replay.py +0 -0
  282. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_phase0h_paged_verifier_exactness.py +0 -0
  283. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_policy_fingerprint_stability.py +0 -0
  284. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_postcommit_prefix_reuse.py +0 -0
  285. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_postcommit_tools_plumbing.py +0 -0
  286. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_postcommit_wait.py +0 -0
  287. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_postcommit_wait_integration.py +0 -0
  288. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_prefill_bench.py +0 -0
  289. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_prefill_chunk_defaults.py +0 -0
  290. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_prefill_tps_regression.py +0 -0
  291. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_profiles.py +0 -0
  292. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_prompt_encoding.py +0 -0
  293. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_runtime_kpis.py +0 -0
  294. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_sampling.py +0 -0
  295. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_sdpa_gqa_packed.py +0 -0
  296. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_server_openai.py +0 -0
  297. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_session_bank.py +0 -0
  298. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_session_bank_env_caps.py +0 -0
  299. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_step3p5_mtp_patch.py +0 -0
  300. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_sustained_long_context_qa.py +0 -0
  301. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_thermal.py +0 -0
  302. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_thermal_sidecar.py +0 -0
  303. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_tool_aware_stream_translator.py +0 -0
  304. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_trace_parity.py +0 -0
  305. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_turboquant.py +0 -0
  306. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_turboquant_fallback.py +0 -0
  307. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_ui_progress.py +0 -0
  308. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_validators.py +0 -0
  309. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_vision_tower.py +0 -0
  310. {mtplx-2.0.0 → mtplx-2.0.2}/tests/test_vllm_reference.py +0 -0
  311. {mtplx-2.0.0 → mtplx-2.0.2}/vllm_metal/__init__.py +0 -0
  312. {mtplx-2.0.0 → mtplx-2.0.2}/vllm_metal/metal/README.md +0 -0
  313. {mtplx-2.0.0 → mtplx-2.0.2}/vllm_metal/metal/__init__.py +0 -0
  314. {mtplx-2.0.0 → mtplx-2.0.2}/vllm_metal/metal/build.py +0 -0
  315. {mtplx-2.0.0 → mtplx-2.0.2}/vllm_metal/metal/constants.py +0 -0
  316. {mtplx-2.0.0 → mtplx-2.0.2}/vllm_metal/metal/kernels_v1/copy_blocks.metal +0 -0
  317. {mtplx-2.0.0 → mtplx-2.0.2}/vllm_metal/metal/kernels_v1/float8.metal +0 -0
  318. {mtplx-2.0.0 → mtplx-2.0.2}/vllm_metal/metal/kernels_v1/gather_kv_cache.metal +0 -0
  319. {mtplx-2.0.0 → mtplx-2.0.2}/vllm_metal/metal/kernels_v1/kv_scale_update.metal +0 -0
  320. {mtplx-2.0.0 → mtplx-2.0.2}/vllm_metal/metal/kernels_v1/pagedattention.metal +0 -0
  321. {mtplx-2.0.0 → mtplx-2.0.2}/vllm_metal/metal/kernels_v1/reshape_and_cache.metal +0 -0
  322. {mtplx-2.0.0 → mtplx-2.0.2}/vllm_metal/metal/kernels_v1/utils.metal +0 -0
  323. {mtplx-2.0.0 → mtplx-2.0.2}/vllm_metal/metal/kernels_v2/copy_blocks.metal +0 -0
  324. {mtplx-2.0.0 → mtplx-2.0.2}/vllm_metal/metal/kernels_v2/float8.metal +0 -0
  325. {mtplx-2.0.0 → mtplx-2.0.2}/vllm_metal/metal/kernels_v2/gather_kv_cache.metal +0 -0
  326. {mtplx-2.0.0 → mtplx-2.0.2}/vllm_metal/metal/kernels_v2/gdn_linear_attention.metal +0 -0
  327. {mtplx-2.0.0 → mtplx-2.0.2}/vllm_metal/metal/kernels_v2/kv_scale_update.metal +0 -0
  328. {mtplx-2.0.0 → mtplx-2.0.2}/vllm_metal/metal/kernels_v2/pagedattention.metal +0 -0
  329. {mtplx-2.0.0 → mtplx-2.0.2}/vllm_metal/metal/kernels_v2/reshape_and_cache.metal +0 -0
  330. {mtplx-2.0.0 → mtplx-2.0.2}/vllm_metal/metal/kernels_v2/turboquant.metal +0 -0
  331. {mtplx-2.0.0 → mtplx-2.0.2}/vllm_metal/metal/kernels_v2/utils.metal +0 -0
  332. {mtplx-2.0.0 → mtplx-2.0.2}/vllm_metal/metal/paged_ops.cpp +0 -0
@@ -4,6 +4,142 @@ All notable user-facing changes to MTPLX. The format is based on
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow
5
5
  [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [2.0.2] - 2026-07-09
8
+
9
+ The agent-reliability release: multi-turn agent sessions now render
10
+ reasoning history on the model's trained contract (the fix for the
11
+ plan-execution repetition marathons), LAN serving works from the app,
12
+ and every agent client gets warm prefix reuse.
13
+
14
+ ### Changed
15
+
16
+ - Reasoning history is now scoped to the active agent round by default
17
+ on Qwen3.6/3.5 models, matching Qwen's trained multi-turn contract.
18
+ The Qwen chat template keeps `<think>` blocks only for assistant
19
+ messages after the last real user query (its built-in "rolling
20
+ checkpoint"); MTPLX used to override that with `preserve_thinking`,
21
+ which rendered an off-contract empty `<think>` scaffold on every
22
+ completed assistant turn and replayed stale inline reasoning across
23
+ turns, while silently dropping the structured `reasoning_content`
24
+ fields agent clients such as OpenCode send. Scoped mode lets the
25
+ template's own checkpoint govern: completed turns render with no
26
+ think scaffold, and the active round (assistant -> tool -> assistant
27
+ chains) keeps its reasoning, now including structured
28
+ `reasoning_content` - strictly better in-round continuity than
29
+ before. `--preserve-thinking on` restores the previous behavior
30
+ byte-for-byte (including cache identity), `off` still strips
31
+ everything, and the new explicit `scoped` value pins the scoped mode.
32
+ Templates without the rolling checkpoint (Gemma 4, custom templates)
33
+ keep the previous preserve-all behavior. The resolved policy is shown
34
+ at startup ("Reasoning history: scoped (active round only)") and as
35
+ `reasoning_history_mode` in `/v1/mtplx/settings` and the snapshot.
36
+
37
+ ### Added
38
+
39
+ - Loop Guard (opt-in): `MTPLX_LOOP_GUARD=1` enables a loop-armed
40
+ anti-repetition steering mode for models prone to verbatim repetition
41
+ marathons (for example repetition-damaged third-party quants). Unlike
42
+ a static presence penalty, the guard is completely inert until a real
43
+ loop is detected mid-response (bit-exact sampling otherwise, MTP
44
+ acceptance math unchanged), then penalizes only the tokens that would
45
+ extend a verbatim repeat and disarms once the loop is broken. Content
46
+ inside tool calls is never steered (code legitimately repeats short
47
+ token runs, so tool-call spans are masked token-exactly). The default
48
+ is OFF: no synthetic steering touches sampling unless you ask for it,
49
+ and the repetition fix that matters for MTPLX's own models is the
50
+ scoped reasoning history change above. Detector/steering knobs are
51
+ documented in `mtplx/loop_guard.py`; per-request guard activity is
52
+ visible under `loop_guard` in `/v1/mtplx/snapshot`.
53
+
54
+ ### Fixed
55
+
56
+ - Hidden reasoning no longer leaks into visible chat content when a
57
+ long reasoning tag (e.g. `</reasoning>`) splits across a streaming
58
+ chunk boundary; the splitter held back too few bytes for tags longer
59
+ than `</think>` (PR #149 by @Osamaali313).
60
+ - `mtplx` quickstart onboarding screens now display the requested
61
+ `--host`/`--port` in the Web UI and dashboard URLs instead of a
62
+ hardcoded `http://127.0.0.1:8000` (PR #148 by @hasegaw).
63
+ - Ctrl-C now returns control to the terminal within a bounded delay
64
+ even when a browser tab holds an open chat/dashboard stream. The
65
+ server previously waited forever for infinite SSE generators to
66
+ finish; shutdown now drains in-flight requests with a 5-second
67
+ deadline, and thermal/fan cleanup still runs (#124).
68
+ - Serving on any host and port now works from the app. Setting host
69
+ 0.0.0.0 (LAN serving) used to misreport free ports as occupied, bump
70
+ the port, and kill the healthy daemon after a health-wait timeout,
71
+ because the app probed the bind address verbatim; all app-side
72
+ connections now resolve wildcard binds to a connectable loopback
73
+ address (#109). The app also surfaces the "LAN serving requires an
74
+ API key" rule before launch instead of a generic Degraded state, an
75
+ API-key mismatch reads as a live-but-unauthorized daemon instead of
76
+ a lost one, and the port-in-use preflight tests the address family
77
+ the daemon will actually bind.
78
+ - The app now passes the SSD session cache setting to the daemon
79
+ explicitly, including "Off". Since 2.0.0 flipped the serve default to
80
+ on, an explicit Settings "Off" was silently re-enabled with default
81
+ limits on app-launched daemons, and `~/.mtplx/session-bank` kept
82
+ growing (#140; also the "session-bank came back after I deleted it"
83
+ half of #138). Generated `mtplx start` server commands carry the
84
+ explicit `--ssd-session-cache off` for the same reason.
85
+ - The app's runtime venv now self-heals after app updates. A venv whose
86
+ base-interpreter symlink pointed into a replaced app bundle made every
87
+ reinstall fail with "[Errno 2] No such file or directory:
88
+ .../runtime-venv/bin/python3" and no DMG reinstall could fix it; venv
89
+ creation now rebuilds with `--clear` when the existing venv python is
90
+ broken or creation fails (#139).
91
+ - Warm prefix reuse no longer freezes on the oldest short prefix for
92
+ agent harnesses outside OpenCode (Pi, little-coder, Hermes, custom
93
+ clients). The block-prefix restore lane was still gated to OpenCode's
94
+ compact tool contract even though kvcache-v2's boundary-true restores
95
+ made it exact for every client, so transcripts whose turns diverge
96
+ more than a few tokens before the stored end re-prefilled a growing
97
+ suffix every turn (#138). The lane now engages for all clients while
98
+ boundary-true restore is on; `MTPLX_SESSION_BLOCK_PREFIX_RESTORE=0`
99
+ still disables it.
100
+
101
+ ## [2.0.1] - 2026-07-07
102
+
103
+ Turbo for every Mac. The v2 turbo default now covers every dense catalog
104
+ model on every Apple Silicon generation, with a load-time kernel
105
+ self-validation safety net.
106
+
107
+ ### Added
108
+
109
+ - 6-bit affine verify kernels (split-K hexpack family): the 6-bit 9B tier
110
+ gains 33-62% decode and 43% 2k-prefill under turbo (M5 Max, verified
111
+ arms). Qwen 3.5 9B and 9B FP16 now default to turbo.
112
+ - New model: `Youssofal/Qwen3.6-27B-MTPLX-Optimized-Quality-FP16`, the
113
+ missing M1/M2 quality artifact. Wired into the app picker, CLI catalog,
114
+ and chip-aware routing (a Quality pick on M1/M2 resolves the FP16
115
+ sibling, mirroring the speed lane). Validated against the bf16 parent
116
+ and measured at 2.5x over true AR under turbo.
117
+ - Load-time kernel self-validation: every turbo lane checks itself against
118
+ stock MLX in the model's exact dtype/quantization at boot; a mismatching
119
+ lane falls back to the stock path for the session and the verdicts are
120
+ surfaced as `kernel_selfcheck` in `/health`. Worst case is v2.0.0 speed,
121
+ never wrong output.
122
+ - `MTPLX_FORCE_GPU_FAMILY_FALLBACK=1` rehearses the exact M1-M4 code path
123
+ on newer machines; `MTPLX_KERNEL_SELFCHECK=0` disables the probe.
124
+ - CI kernel matrix on real M1 runners: kernel exactness across
125
+ {bf16, fp16} x {4, 6, 8}-bit plus a live 4B turbo boot smoke.
126
+
127
+ ### Changed
128
+
129
+ - 27B Optimized-Speed-FP16 (the M1/M2 routing target) defaults to turbo:
130
+ 19-31% faster decode than the v2.0.0 sustained default across 0.5k-32k
131
+ context; true-AR multiplier 1.34x to ~2x. First-ever e2e measurement of
132
+ this artifact; exactness gated (30/30 hot-shape logit-diff cases on real
133
+ weights, greedy match turbo vs stock).
134
+ - The published Speed-FP16 model card now recommends the turbo profile.
135
+
136
+ ### Unchanged on purpose
137
+
138
+ - 35B A3B MoE and Gemma 4 keep sustained (expert layers / assistant-pair
139
+ architecture bypass these kernels; named 2.0.2 lanes). The 4B keeps
140
+ sustained (turbo measured slightly slower at matched depth). Compiled
141
+ verify stays off for 6-bit models.
142
+
7
143
  ## [2.0.0] - 2026-07-06
8
144
 
9
145
  MTPLX v2: the coding-agent release. Session-cache v2 (RAM + SSD), the
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mtplx
3
- Version: 2.0.0
3
+ Version: 2.0.2
4
4
  Summary: Native MTP speculative decoding for Qwen3-Next on Apple Silicon.
5
5
  Author-email: Youssof Altoukhi <business@youssofal.com>
6
6
  License-Expression: Apache-2.0
@@ -32,6 +32,8 @@ from .profiles import (
32
32
  DEFAULT_PUBLIC_MODEL_ID,
33
33
  LEGACY_OPTIMIZED_HF_MODEL_ID,
34
34
  LEGACY_OPTIMIZED_PUBLIC_MODEL_ID,
35
+ QUALITY_FP16_HF_MODEL_ID,
36
+ QUALITY_FP16_PUBLIC_MODEL_ID,
35
37
  QUALITY_HF_MODEL_ID,
36
38
  QUALITY_PUBLIC_MODEL_ID,
37
39
  QWEN35_9B_OPTIMIZED_SPEED_FP16_HF_MODEL_ID,
@@ -57,6 +59,7 @@ _KNOWN_PUBLIC_MODEL_ALIASES = {
57
59
  DEFAULT_PUBLIC_MODEL_ID: DEFAULT_HF_MODEL_ID,
58
60
  DEFAULT_FP16_PUBLIC_MODEL_ID: DEFAULT_FP16_HF_MODEL_ID,
59
61
  QUALITY_PUBLIC_MODEL_ID: QUALITY_HF_MODEL_ID,
62
+ QUALITY_FP16_PUBLIC_MODEL_ID: QUALITY_FP16_HF_MODEL_ID,
60
63
  LEGACY_OPTIMIZED_PUBLIC_MODEL_ID: LEGACY_OPTIMIZED_HF_MODEL_ID,
61
64
  QWEN35_9B_OPTIMIZED_SPEED_PUBLIC_MODEL_ID: QWEN35_9B_OPTIMIZED_SPEED_HF_MODEL_ID,
62
65
  QWEN35_9B_OPTIMIZED_SPEED_FP16_PUBLIC_MODEL_ID: QWEN35_9B_OPTIMIZED_SPEED_FP16_HF_MODEL_ID,
@@ -71,6 +74,7 @@ _KNOWN_PUBLIC_MODEL_ALIASES = {
71
74
  "qwen3.6-27b-mtplx-optimized": LEGACY_OPTIMIZED_HF_MODEL_ID,
72
75
  "qwen3.6-27b-mtplx-optimized-speed-fp16": DEFAULT_FP16_HF_MODEL_ID,
73
76
  "qwen3.6-27b-mtplx-optimized-quality": QUALITY_HF_MODEL_ID,
77
+ "qwen3.6-27b-mtplx-optimized-quality-fp16": QUALITY_FP16_HF_MODEL_ID,
74
78
  "qwen3.6-35b-a3b-mtplx-optimized-speed": QWEN36_35B_OPTIMIZED_SPEED_HF_MODEL_ID,
75
79
  "qwen3.6-35b-a3b-mtplx-optimized-speed-fp16": QWEN36_35B_OPTIMIZED_SPEED_FP16_HF_MODEL_ID,
76
80
  "qwen3.6-35b-a3b-mtplx-optimized-balance": QWEN36_35B_OPTIMIZED_BALANCE_HF_MODEL_ID,
@@ -234,6 +234,10 @@ def _install_split_attention_hook(attn: Any) -> bool:
234
234
  gqa_packed_threshold = int(
235
235
  getattr(self, "_mtplx_gqa_packed_sdpa_threshold", 8192)
236
236
  )
237
+ if gqa_packed_enabled:
238
+ from .kernel_selfcheck import lane_disabled
239
+
240
+ gqa_packed_enabled = not lane_disabled("gqa_packed_sdpa")
237
241
  should_use_gqa_packed = (
238
242
  gqa_packed_enabled
239
243
  and cache is not None
@@ -542,11 +542,13 @@ def _add_reasoning_effort_arg(parser: argparse.ArgumentParser) -> None:
542
542
  def _add_preserve_thinking_arg(parser: argparse.ArgumentParser) -> None:
543
543
  parser.add_argument(
544
544
  "--preserve-thinking",
545
- choices=["auto", "on", "off"],
545
+ choices=["auto", "on", "off", "scoped"],
546
546
  default="auto",
547
547
  help=(
548
- "Preserve prior assistant reasoning in Qwen chat-template history. "
549
- "Default auto preserves it for reasoning-capable templates; off is a speed/debug mode."
548
+ "Reasoning-history policy for Qwen chat-template history. scoped keeps "
549
+ "reasoning only inside the active agent round (Qwen's trained contract); "
550
+ "on preserves all; off strips all. Default auto resolves to scoped for "
551
+ "checkpoint-capable templates."
550
552
  ),
551
553
  )
552
554
  parser.add_argument(
@@ -83,6 +83,8 @@ from mtplx.profiles import (
83
83
  DEFAULT_PUBLIC_MODEL_ID,
84
84
  LEGACY_OPTIMIZED_HF_MODEL_ID,
85
85
  LEGACY_OPTIMIZED_PUBLIC_MODEL_ID,
86
+ QUALITY_FP16_HF_MODEL_ID,
87
+ QUALITY_FP16_PUBLIC_MODEL_ID,
86
88
  QUALITY_HF_MODEL_ID,
87
89
  QUALITY_PUBLIC_MODEL_ID,
88
90
  QWEN35_9B_OPTIMIZED_SPEED_FP16_HF_MODEL_ID,
@@ -823,19 +825,40 @@ def _apply_model_contract_depth_default(
823
825
 
824
826
  # Quantized 27B flagships whose measured default profile is turbo. This is
825
827
  # the same launch rule the macOS app applies in MTPLXCommandBuilder
826
- # (Speed/Quality -> turbo; FP16 siblings stay sustained): NAX verify kernels
827
- # +22-40% chat decode on q8 (ULP-exact) and vk_k on q4, plus compiled verify
828
- # behind its per-model quant-bits gate. Before 2026-07-05 the bare CLI
829
- # (`mtplx serve` / quickstart / start) silently stayed on sustained, so every
830
- # OpenAI-API consumer — including third-party benchmark harnesses — measured
831
- # the slow path while the app ran turbo. Keep this list measured-win only:
832
- # 6-bit small models, 35B, Gemma, FP16 and third-party artifacts keep the
833
- # sustained default.
828
+ # (Speed/Quality/Speed-FP16 -> turbo): NAX verify kernels +22-40% chat decode
829
+ # on q8 (ULP-exact) and vk_k on q4, plus compiled verify behind its per-model
830
+ # quant-bits gate. Before 2026-07-05 the bare CLI (`mtplx serve` / quickstart
831
+ # / start) silently stayed on sustained, so every OpenAI-API consumer —
832
+ # including third-party benchmark harnesses — measured the slow path while
833
+ # the app ran turbo. Keep this list measured-win only: 6-bit small models
834
+ # (vk covers 4/8-bit affine only), 35B MoE (experts bypass the NAX patch),
835
+ # Gemma, and third-party artifacts keep the sustained default.
834
836
  _TURBO_DEFAULT_PUBLIC_MODEL_IDS = frozenset(
835
837
  {
836
838
  DEFAULT_PUBLIC_MODEL_ID, # 27B Optimized-Speed (flat 4-bit)
837
839
  QUALITY_PUBLIC_MODEL_ID, # 27B Optimized-Quality (8-bit)
838
840
  LEGACY_OPTIMIZED_PUBLIC_MODEL_ID, # 27B Optimized (gdn8 hybrid, 8/4-bit)
841
+ # 27B Speed-FP16 (INT4/g64 weights, fp16 activations — the M1/M2
842
+ # routing target). Promoted 2026-07-07 after the first-ever e2e
843
+ # measurement: turbo 1.98-2.08x over true AR on M5 Max (55-60 tok/s
844
+ # D3 vs 27-29 AR), acceptance [86/66/51]%, peak 18.6 GB, vs only
845
+ # 1.34x on sustained (fp16 verify_hidden_eval tax ~8s/384-tok run).
846
+ # The 9B FP16 (6-bit) and 35B FP16 (MoE) siblings are NOT promoted.
847
+ DEFAULT_FP16_PUBLIC_MODEL_ID,
848
+ # 27B Quality-FP16 (q8/g64 weights, fp16 activations — the M1/M2
849
+ # quality pick, built 2026-07-07). Measured on its own artifact:
850
+ # turbo MTP D3 43.8/42.1 tok/s vs true AR 17.4 (2.5x), selfcheck
851
+ # all vk q8 lanes ok, parent logit-diff argmax 16/16.
852
+ QUALITY_FP16_PUBLIC_MODEL_ID,
853
+ # 9B (6-bit/g64) promoted 2026-07-07 after the 6-bit hexpack
854
+ # split-K kernels landed: live ABBA on the 9B measured turbo MTP
855
+ # D3 110/102 tok/s vs sustained 90/69 (+34%), true AR 62-65 flat
856
+ # both profiles -> multiplier 1.25x -> 1.68x. Exactness: 54
857
+ # synthetic {bf16,fp16}x{gs32,64,128} cases dmax <= 0.027 vs
858
+ # stock. The FP16 sibling shares the 6-bit packs and fp16 lanes
859
+ # are exactness-proven, so both promote together.
860
+ QWEN35_9B_OPTIMIZED_SPEED_PUBLIC_MODEL_ID,
861
+ QWEN35_9B_OPTIMIZED_SPEED_FP16_PUBLIC_MODEL_ID,
839
862
  }
840
863
  )
841
864
 
@@ -1162,7 +1185,7 @@ def _preserve_thinking_policy(args: Any) -> str:
1162
1185
  return "off"
1163
1186
  raw = getattr(args, "preserve_thinking", None)
1164
1187
  mode = str(raw or "auto").strip().lower()
1165
- return mode if mode in {"auto", "on", "off"} else "auto"
1188
+ return mode if mode in {"auto", "on", "off", "scoped"} else "auto"
1166
1189
 
1167
1190
 
1168
1191
  def _pi_preserve_thinking_policy(args: Any) -> str:
@@ -7628,6 +7651,9 @@ def _model_ref_from_public_model_id(model_id: str | None) -> str | None:
7628
7651
  QUALITY_PUBLIC_MODEL_ID.lower(): QUALITY_HF_MODEL_ID,
7629
7652
  QUALITY_HF_MODEL_ID.lower(): QUALITY_HF_MODEL_ID,
7630
7653
  Path(QUALITY_HF_MODEL_ID).name.lower(): QUALITY_HF_MODEL_ID,
7654
+ QUALITY_FP16_PUBLIC_MODEL_ID.lower(): QUALITY_FP16_HF_MODEL_ID,
7655
+ QUALITY_FP16_HF_MODEL_ID.lower(): QUALITY_FP16_HF_MODEL_ID,
7656
+ Path(QUALITY_FP16_HF_MODEL_ID).name.lower(): QUALITY_FP16_HF_MODEL_ID,
7631
7657
  QWEN35_9B_OPTIMIZED_SPEED_PUBLIC_MODEL_ID.lower(): QWEN35_9B_OPTIMIZED_SPEED_HF_MODEL_ID,
7632
7658
  QWEN35_9B_OPTIMIZED_SPEED_HF_MODEL_ID.lower(): QWEN35_9B_OPTIMIZED_SPEED_HF_MODEL_ID,
7633
7659
  Path(QWEN35_9B_OPTIMIZED_SPEED_HF_MODEL_ID).name.lower(): QWEN35_9B_OPTIMIZED_SPEED_HF_MODEL_ID,
@@ -9670,8 +9696,12 @@ def _batching_command_suffix(args: Any) -> str:
9670
9696
  if bool(getattr(args, "experimental_mtp_cohorts", False)):
9671
9697
  parts.append("--experimental-mtp-cohorts")
9672
9698
  ssd_session_cache = str(getattr(args, "ssd_session_cache", "on") or "on")
9699
+ # Emit the mode unconditionally, "off" included: these generated
9700
+ # commands are re-parsed by CLIs whose own default is "on"
9701
+ # (kvcache-v2), so omitting an explicit "off" silently re-enables
9702
+ # the cache (issue #140 class).
9703
+ parts.extend(["--ssd-session-cache", shlex.quote(ssd_session_cache)])
9673
9704
  if ssd_session_cache != "off":
9674
- parts.extend(["--ssd-session-cache", shlex.quote(ssd_session_cache)])
9675
9705
  ssd_dir = getattr(args, "ssd_session_cache_dir", None)
9676
9706
  if ssd_dir:
9677
9707
  parts.extend(["--ssd-session-cache-dir", shlex.quote(str(ssd_dir))])
@@ -11956,6 +11986,8 @@ def cmd_quickstart_public(args: Any) -> int:
11956
11986
  fresh=fresh,
11957
11987
  configured_model=configured_model,
11958
11988
  open_dashboard_override=explicit_open_dashboard,
11989
+ host=str(getattr(args, "host", "127.0.0.1")),
11990
+ port=int(getattr(args, "port", 8000)),
11959
11991
  )
11960
11992
  if choice is None:
11961
11993
  _quickstart_line("aborted")
@@ -18,6 +18,8 @@ from mtplx.profiles import (
18
18
  DEFAULT_MODEL_ID,
19
19
  DEFAULT_PUBLIC_MODEL_ID,
20
20
  LEGACY_OPTIMIZED_PUBLIC_MODEL_ID,
21
+ QUALITY_FP16_HF_MODEL_ID,
22
+ QUALITY_FP16_PUBLIC_MODEL_ID,
21
23
  QUALITY_HF_MODEL_ID,
22
24
  QUALITY_PUBLIC_MODEL_ID,
23
25
  QWEN35_9B_OPTIMIZED_SPEED_FP16_HF_MODEL_ID,
@@ -60,6 +62,10 @@ _OPTIMIZED_QUALITY_LOCAL_CANDIDATES = (
60
62
  "~/Documents/MTPLX/hf-staging/Qwen3.6-27B-MTPLX-Optimized-Quality",
61
63
  "~/.mtplx/models/Youssofal--Qwen3.6-27B-MTPLX-Optimized-Quality",
62
64
  )
65
+ _OPTIMIZED_QUALITY_FP16_LOCAL_CANDIDATES = (
66
+ "~/Documents/MTPLX/hf-staging/Qwen3.6-27B-MTPLX-Optimized-Quality-FP16",
67
+ "~/.mtplx/models/Youssofal--Qwen3.6-27B-MTPLX-Optimized-Quality-FP16",
68
+ )
63
69
  _OPTIMIZED_35B_SPEED_LOCAL_CANDIDATES = (
64
70
  "~/Documents/MTPLX/models/Qwen3.6-35B-A3B-MTPLX-Optimized-Speed",
65
71
  "~/Documents/MTPLX/models/Qwen3.6-35B-A3B-MTPLX-Official4-CyanKiwiMTP-CleanRecipe",
@@ -171,16 +177,29 @@ def optimized_speed_model_ref() -> str:
171
177
  return local or DEFAULT_HF_MODEL_ID
172
178
 
173
179
 
174
- def optimized_quality_model_ref() -> str:
180
+ def optimized_quality_model_ref(
181
+ *,
182
+ hardware: Mapping[str, Any] | None = None,
183
+ ) -> str:
175
184
  env_ref = str(os.environ.get(QUALITY_MODEL_ENV) or "").strip()
176
- if env_ref:
177
- if _env_ref_disabled(env_ref):
178
- candidates = ()
179
- else:
180
- candidates = (env_ref, *_OPTIMIZED_QUALITY_LOCAL_CANDIDATES)
181
- else:
182
- candidates = _OPTIMIZED_QUALITY_LOCAL_CANDIDATES
183
- local = _complete_local_model_ref(candidates)
185
+ env_disabled = _env_ref_disabled(env_ref) if env_ref else False
186
+ if env_ref and not env_disabled:
187
+ local = _complete_local_model_ref((env_ref,))
188
+ if local:
189
+ return local
190
+ # A quality pick on legacy (M1/M2) silicon resolves the FP16 sibling,
191
+ # mirroring the speed lane's precision routing (2.0.1, 2026-07-07).
192
+ hardware_info = dict(detect_apple_silicon() if hardware is None else hardware)
193
+ generation = _hardware_generation(hardware_info)
194
+ if generation in _LEGACY_APPLE_FP16_GENERATIONS:
195
+ if not env_disabled:
196
+ local = _complete_local_model_ref(_OPTIMIZED_QUALITY_FP16_LOCAL_CANDIDATES)
197
+ if local:
198
+ return local
199
+ return QUALITY_FP16_HF_MODEL_ID
200
+ if env_disabled:
201
+ return QUALITY_HF_MODEL_ID
202
+ local = _complete_local_model_ref(_OPTIMIZED_QUALITY_LOCAL_CANDIDATES)
184
203
  return local or QUALITY_HF_MODEL_ID
185
204
 
186
205
 
@@ -293,6 +312,8 @@ def _public_model_id_from_name(value: str) -> str | None:
293
312
  # First-party local research build of the released 35B speed
294
313
  # artifact (listed in _OPTIMIZED_35B_SPEED_LOCAL_CANDIDATES).
295
314
  return QWEN36_35B_OPTIMIZED_SPEED_PUBLIC_MODEL_ID
315
+ if "qwen3.6-27b-mtplx-optimized-quality-fp16" in lowered:
316
+ return QUALITY_FP16_PUBLIC_MODEL_ID
296
317
  if "qwen3.6-27b-mtplx-optimized-quality" in lowered:
297
318
  return QUALITY_PUBLIC_MODEL_ID
298
319
  if "qwen3.6-27b-mtplx-optimized-speed-fp16" in lowered:
@@ -15,6 +15,7 @@ def apply_penalties_mlx(
15
15
  token_counts: Mapping[int, int] | None,
16
16
  presence_penalty: float = 0.0,
17
17
  frequency_penalty: float = 0.0,
18
+ penalty_overlay: Mapping[int, float] | None = None,
18
19
  ) -> mx.array:
19
20
  """On-device MLX twin of ``sampling.apply_penalties`` for one logit row.
20
21
 
@@ -22,20 +23,33 @@ def apply_penalties_mlx(
22
23
  temperature/top-k/top-p — via a sparse scatter over only the seen tokens
23
24
  (``logits.at[ids].add(-deltas)``): O(unique_seen), no dense vocab-sized
24
25
  allocation and no host round-trip beyond the small (ids, deltas) transfer.
25
- Penalties clamp to [-2, 2]. Returns the same array unchanged (no-op) when
26
- both penalties are 0 or there are no counts, preserving exactness.
26
+ Penalties clamp to [-2, 2]. ``penalty_overlay`` is the Loop Guard's sparse
27
+ token->subtraction map (not clamped; the guard caps it). Returns the same
28
+ array unchanged (no-op) when nothing is active, preserving exactness.
27
29
 
28
30
  Expects a 1-D logit row (``logits.shape == (vocab,)``); batched callers apply
29
31
  it per row (the MTP draft block is a handful of positions, not a hot loop).
30
32
  """
31
33
  presence = min(max(float(presence_penalty), PENALTY_MIN), PENALTY_MAX)
32
34
  frequency = min(max(float(frequency_penalty), PENALTY_MIN), PENALTY_MAX)
33
- if (presence == 0.0 and frequency == 0.0) or not token_counts:
35
+ counts_active = bool(token_counts) and (presence != 0.0 or frequency != 0.0)
36
+ overlay_active = bool(penalty_overlay)
37
+ if not counts_active and not overlay_active:
34
38
  return logits
35
- ids = np.fromiter(token_counts.keys(), dtype=np.int64, count=len(token_counts))
36
- counts = np.fromiter(token_counts.values(), dtype=np.float64, count=len(token_counts))
37
- deltas = (frequency * counts + presence * (counts > 0.0)).astype(np.float32)
38
- return logits.at[mx.array(ids)].add(mx.array(-deltas))
39
+ if counts_active:
40
+ ids = np.fromiter(token_counts.keys(), dtype=np.int64, count=len(token_counts))
41
+ counts = np.fromiter(token_counts.values(), dtype=np.float64, count=len(token_counts))
42
+ deltas = (frequency * counts + presence * (counts > 0.0)).astype(np.float32)
43
+ logits = logits.at[mx.array(ids)].add(mx.array(-deltas))
44
+ if overlay_active:
45
+ overlay_ids = np.fromiter(
46
+ penalty_overlay.keys(), dtype=np.int64, count=len(penalty_overlay)
47
+ )
48
+ overlay_vals = np.fromiter(
49
+ penalty_overlay.values(), dtype=np.float64, count=len(penalty_overlay)
50
+ ).astype(np.float32)
51
+ logits = logits.at[mx.array(overlay_ids)].add(mx.array(-overlay_vals))
52
+ return logits
39
53
 
40
54
 
41
55
  class BatchedSparseDistributions:
@@ -85,6 +99,7 @@ def sparse_distribution_from_mlx_logits(
85
99
  config: SamplerConfig,
86
100
  *,
87
101
  token_counts: Mapping[int, int] | None = None,
102
+ penalty_overlay: Mapping[int, float] | None = None,
88
103
  ) -> SparseDistribution | None:
89
104
  """Return an exact sparse distribution for top-p then top-k sampling.
90
105
 
@@ -96,13 +111,19 @@ def sparse_distribution_from_mlx_logits(
96
111
  ``token_counts`` (completion tokens seen so far, scoped by the caller) applies
97
112
  the additive presence/frequency penalty to the raw logits BEFORE the
98
113
  temperature divide — a no-op (same array) when penalties are 0.
114
+ ``penalty_overlay`` is the Loop Guard's sparse steering map, applied at the
115
+ same raw-logit stage.
99
116
  """
100
117
 
101
118
  if config.temperature <= 0 or config.top_k <= 0:
102
119
  return None
103
120
 
104
121
  row = apply_penalties_mlx(
105
- logits.reshape(-1), token_counts, config.presence_penalty, config.frequency_penalty
122
+ logits.reshape(-1),
123
+ token_counts,
124
+ config.presence_penalty,
125
+ config.frequency_penalty,
126
+ penalty_overlay=penalty_overlay,
106
127
  )
107
128
  flat = row.astype(mx.float32) / float(config.temperature)
108
129
  vocab_size = int(flat.shape[-1])
@@ -1636,9 +1636,13 @@ def gdn_forward_with_capture(
1636
1636
  "yes",
1637
1637
  "on",
1638
1638
  }:
1639
+ from .kernel_selfcheck import lane_disabled
1639
1640
  from .kernels.fused_norm import fused_gdn_norm_gate
1640
1641
 
1641
- out = fused_gdn_norm_gate(out, z, gdn.norm.weight, gdn.norm.eps)
1642
+ if lane_disabled("fused_gdn_norm_gate"):
1643
+ out = gdn.norm(out, z)
1644
+ else:
1645
+ out = fused_gdn_norm_gate(out, z, gdn.norm.weight, gdn.norm.eps)
1642
1646
  else:
1643
1647
  out = gdn.norm(out, z)
1644
1648
  if not tail_projected:
@@ -1739,15 +1743,20 @@ def forward_with_gdn_capture(
1739
1743
  "yes",
1740
1744
  "on",
1741
1745
  }:
1746
+ from .kernel_selfcheck import lane_disabled
1742
1747
  from .kernels.fused_norm import fused_add_rmsnorm
1743
1748
 
1744
- h, mlp_input = fused_add_rmsnorm(
1745
- hidden_states,
1746
- r,
1747
- layer.post_attention_layernorm.weight,
1748
- layer.post_attention_layernorm.eps,
1749
- threadgroup_size=512,
1750
- )
1749
+ if lane_disabled("fused_add_rmsnorm"):
1750
+ h = hidden_states + r
1751
+ mlp_input = layer.post_attention_layernorm(h)
1752
+ else:
1753
+ h, mlp_input = fused_add_rmsnorm(
1754
+ hidden_states,
1755
+ r,
1756
+ layer.post_attention_layernorm.weight,
1757
+ layer.post_attention_layernorm.eps,
1758
+ threadgroup_size=512,
1759
+ )
1751
1760
  else:
1752
1761
  h = hidden_states + r
1753
1762
  mlp_input = layer.post_attention_layernorm(h)