soup-cli 0.40.2__tar.gz → 0.40.4__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 (404) hide show
  1. {soup_cli-0.40.2 → soup_cli-0.40.4}/CONTRIBUTING.md +1 -1
  2. {soup_cli-0.40.2 → soup_cli-0.40.4}/PKG-INFO +57 -15
  3. {soup_cli-0.40.2 → soup_cli-0.40.4}/README.md +57 -15
  4. {soup_cli-0.40.2 → soup_cli-0.40.4}/SECURITY.md +3 -1
  5. {soup_cli-0.40.2 → soup_cli-0.40.4}/pyproject.toml +1 -1
  6. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/__init__.py +1 -1
  7. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/data.py +67 -0
  8. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/diff.py +37 -6
  9. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/export.py +75 -15
  10. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/generate.py +26 -9
  11. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/infer.py +35 -10
  12. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/merge.py +25 -2
  13. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/serve.py +78 -1
  14. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/train.py +4 -5
  15. soup_cli-0.40.4/soup_cli/data/traces/quality.py +114 -0
  16. soup_cli-0.40.4/soup_cli/monitoring/trace_logger.py +162 -0
  17. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/trainer/bco.py +20 -2
  18. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/trainer/dpo.py +23 -2
  19. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/trainer/embedding.py +20 -2
  20. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/trainer/grpo.py +20 -2
  21. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/trainer/ipo.py +20 -2
  22. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/trainer/kto.py +20 -2
  23. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/trainer/orpo.py +20 -2
  24. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/trainer/ppo.py +44 -8
  25. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/trainer/preference.py +4 -0
  26. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/trainer/pretrain.py +48 -3
  27. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/trainer/reward_model.py +22 -2
  28. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/trainer/sft.py +44 -4
  29. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/trainer/simpo.py +20 -2
  30. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/batch_probe.py +106 -0
  31. soup_cli-0.40.4/soup_cli/utils/multipack_trainer.py +254 -0
  32. soup_cli-0.40.4/tests/test_v0403_part_a.py +206 -0
  33. soup_cli-0.40.4/tests/test_v0403_part_b.py +306 -0
  34. soup_cli-0.40.4/tests/test_v0403_part_c.py +584 -0
  35. soup_cli-0.40.4/tests/test_v0404_part_a.py +373 -0
  36. soup_cli-0.40.4/tests/test_v0404_part_b.py +179 -0
  37. {soup_cli-0.40.2 → soup_cli-0.40.4}/.dockerignore +0 -0
  38. {soup_cli-0.40.2 → soup_cli-0.40.4}/.github/FUNDING.yml +0 -0
  39. {soup_cli-0.40.2 → soup_cli-0.40.4}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  40. {soup_cli-0.40.2 → soup_cli-0.40.4}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  41. {soup_cli-0.40.2 → soup_cli-0.40.4}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  42. {soup_cli-0.40.2 → soup_cli-0.40.4}/.github/pull_request_template.md +0 -0
  43. {soup_cli-0.40.2 → soup_cli-0.40.4}/.github/workflows/ci.yml +0 -0
  44. {soup_cli-0.40.2 → soup_cli-0.40.4}/.github/workflows/docker.yml +0 -0
  45. {soup_cli-0.40.2 → soup_cli-0.40.4}/.github/workflows/publish.yml +0 -0
  46. {soup_cli-0.40.2 → soup_cli-0.40.4}/.github/workflows/recipe-validation.yml +0 -0
  47. {soup_cli-0.40.2 → soup_cli-0.40.4}/.gitignore +0 -0
  48. {soup_cli-0.40.2 → soup_cli-0.40.4}/CODEOWNERS +0 -0
  49. {soup_cli-0.40.2 → soup_cli-0.40.4}/CODE_OF_CONDUCT.md +0 -0
  50. {soup_cli-0.40.2 → soup_cli-0.40.4}/Dockerfile +0 -0
  51. {soup_cli-0.40.2 → soup_cli-0.40.4}/LICENSE +0 -0
  52. {soup_cli-0.40.2 → soup_cli-0.40.4}/NOTICE +0 -0
  53. {soup_cli-0.40.2 → soup_cli-0.40.4}/docker-compose.yml +0 -0
  54. {soup_cli-0.40.2 → soup_cli-0.40.4}/examples/README.md +0 -0
  55. {soup_cli-0.40.2 → soup_cli-0.40.4}/examples/configs/dpo_chat.yaml +0 -0
  56. {soup_cli-0.40.2 → soup_cli-0.40.4}/examples/configs/dpo_example.yaml +0 -0
  57. {soup_cli-0.40.2 → soup_cli-0.40.4}/examples/configs/grpo_reasoning.yaml +0 -0
  58. {soup_cli-0.40.2 → soup_cli-0.40.4}/examples/configs/rlhf_step1_sft.yaml +0 -0
  59. {soup_cli-0.40.2 → soup_cli-0.40.4}/examples/configs/rlhf_step2_reward.yaml +0 -0
  60. {soup_cli-0.40.2 → soup_cli-0.40.4}/examples/configs/rlhf_step3_ppo.yaml +0 -0
  61. {soup_cli-0.40.2 → soup_cli-0.40.4}/examples/configs/sft_basic.yaml +0 -0
  62. {soup_cli-0.40.2 → soup_cli-0.40.4}/examples/configs/vision_llama.yaml +0 -0
  63. {soup_cli-0.40.2 → soup_cli-0.40.4}/examples/data/alpaca_tiny.jsonl +0 -0
  64. {soup_cli-0.40.2 → soup_cli-0.40.4}/examples/data/chat_preferences.jsonl +0 -0
  65. {soup_cli-0.40.2 → soup_cli-0.40.4}/examples/data/dpo_sample.jsonl +0 -0
  66. {soup_cli-0.40.2 → soup_cli-0.40.4}/examples/data/reasoning_math.jsonl +0 -0
  67. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup.png +0 -0
  68. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/__main__.py +0 -0
  69. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/autopilot/__init__.py +0 -0
  70. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/autopilot/analyzer.py +0 -0
  71. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/autopilot/decisions.py +0 -0
  72. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/autopilot/generate_config.py +0 -0
  73. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/cans/__init__.py +0 -0
  74. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/cans/pack.py +0 -0
  75. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/cans/publish.py +0 -0
  76. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/cans/run.py +0 -0
  77. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/cans/schema.py +0 -0
  78. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/cans/unpack.py +0 -0
  79. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/cans/verify.py +0 -0
  80. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/cli.py +0 -0
  81. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/__init__.py +0 -0
  82. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/adapters.py +0 -0
  83. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/autopilot.py +0 -0
  84. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/bench.py +0 -0
  85. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/can.py +0 -0
  86. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/chat.py +0 -0
  87. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/cost.py +0 -0
  88. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/deploy.py +0 -0
  89. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/doctor.py +0 -0
  90. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/eval.py +0 -0
  91. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/history.py +0 -0
  92. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/init.py +0 -0
  93. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/migrate.py +0 -0
  94. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/profile.py +0 -0
  95. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/push.py +0 -0
  96. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/quickstart.py +0 -0
  97. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/recipes.py +0 -0
  98. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/registry.py +0 -0
  99. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/runs.py +0 -0
  100. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/sweep.py +0 -0
  101. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/tui.py +0 -0
  102. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/ui.py +0 -0
  103. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/commands/why.py +0 -0
  104. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/config/__init__.py +0 -0
  105. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/config/loader.py +0 -0
  106. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/config/schema.py +0 -0
  107. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/data/__init__.py +0 -0
  108. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/data/augment.py +0 -0
  109. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/data/chat_templates.py +0 -0
  110. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/data/collators.py +0 -0
  111. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/data/formats.py +0 -0
  112. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/data/loader.py +0 -0
  113. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/data/loss_mask.py +0 -0
  114. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/data/providers/__init__.py +0 -0
  115. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/data/providers/_utils.py +0 -0
  116. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/data/providers/anthropic.py +0 -0
  117. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/data/providers/ollama.py +0 -0
  118. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/data/providers/vllm.py +0 -0
  119. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/data/sft_format.py +0 -0
  120. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/data/templates/__init__.py +0 -0
  121. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/data/templates/code.py +0 -0
  122. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/data/templates/conversation.py +0 -0
  123. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/data/templates/preference.py +0 -0
  124. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/data/templates/qa.py +0 -0
  125. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/data/templates/reasoning.py +0 -0
  126. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/data/templates/tool_calling.py +0 -0
  127. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/data/templates/verifiable.py +0 -0
  128. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/data/traces/__init__.py +0 -0
  129. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/data/traces/pair_builder.py +0 -0
  130. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/data/traces/parsers.py +0 -0
  131. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/data/validator.py +0 -0
  132. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/eval/__init__.py +0 -0
  133. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/eval/checkpoint_intelligence.py +0 -0
  134. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/eval/custom.py +0 -0
  135. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/eval/forgetting.py +0 -0
  136. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/eval/gate.py +0 -0
  137. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/eval/human.py +0 -0
  138. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/eval/judge.py +0 -0
  139. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/eval/leaderboard.py +0 -0
  140. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/eval/quant_check.py +0 -0
  141. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/experiment/__init__.py +0 -0
  142. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/experiment/tracker.py +0 -0
  143. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/migrate/__init__.py +0 -0
  144. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/migrate/axolotl.py +0 -0
  145. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/migrate/common.py +0 -0
  146. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/migrate/llamafactory.py +0 -0
  147. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/migrate/unsloth.py +0 -0
  148. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/monitoring/__init__.py +0 -0
  149. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/monitoring/callback.py +0 -0
  150. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/monitoring/display.py +0 -0
  151. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/monitoring/hf_push.py +0 -0
  152. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/recipes/__init__.py +0 -0
  153. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/recipes/catalog.py +0 -0
  154. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/registry/__init__.py +0 -0
  155. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/registry/attach.py +0 -0
  156. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/registry/diff.py +0 -0
  157. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/registry/hashing.py +0 -0
  158. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/registry/store.py +0 -0
  159. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/templates/__init__.py +0 -0
  160. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/templates/audio.yaml +0 -0
  161. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/templates/bco.yaml +0 -0
  162. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/templates/chat.yaml +0 -0
  163. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/templates/code.yaml +0 -0
  164. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/templates/embedding.yaml +0 -0
  165. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/templates/ipo.yaml +0 -0
  166. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/templates/kto.yaml +0 -0
  167. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/templates/longcontext.yaml +0 -0
  168. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/templates/manifest.json +0 -0
  169. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/templates/medical.yaml +0 -0
  170. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/templates/moe.yaml +0 -0
  171. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/templates/orpo.yaml +0 -0
  172. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/templates/pretrain.yaml +0 -0
  173. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/templates/reasoning.yaml +0 -0
  174. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/templates/rlhf.yaml +0 -0
  175. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/templates/simpo.yaml +0 -0
  176. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/templates/tool-calling.yaml +0 -0
  177. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/templates/vision.yaml +0 -0
  178. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/trainer/__init__.py +0 -0
  179. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/trainer/mlx_dpo.py +0 -0
  180. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/trainer/mlx_grpo.py +0 -0
  181. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/trainer/mlx_routing.py +0 -0
  182. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/trainer/mlx_sft.py +0 -0
  183. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/trainer/rewards.py +0 -0
  184. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/tui_app.py +0 -0
  185. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/ui/__init__.py +0 -0
  186. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/ui/app.py +0 -0
  187. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/ui/static/app.js +0 -0
  188. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/ui/static/index.html +0 -0
  189. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/ui/static/logo.png +0 -0
  190. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/ui/static/logo.svg +0 -0
  191. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/ui/static/style.css +0 -0
  192. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/__init__.py +0 -0
  193. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/activation_offload.py +0 -0
  194. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/auto_quant.py +0 -0
  195. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/constants.py +0 -0
  196. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/convergence.py +0 -0
  197. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/crash.py +0 -0
  198. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/cross_doc_attn.py +0 -0
  199. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/curriculum.py +0 -0
  200. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/cut_ce.py +0 -0
  201. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/deepspeed.py +0 -0
  202. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/dpo_variants.py +0 -0
  203. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/encoding.py +0 -0
  204. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/errors.py +0 -0
  205. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/flash_attn.py +0 -0
  206. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/fp8.py +0 -0
  207. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/freeze.py +0 -0
  208. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/fsdp.py +0 -0
  209. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/galore.py +0 -0
  210. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/gpu.py +0 -0
  211. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/grad_accum.py +0 -0
  212. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/gradient_ckpt.py +0 -0
  213. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/hf.py +0 -0
  214. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/hf_space.py +0 -0
  215. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/jinja_analyzer.py +0 -0
  216. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/kernel_picker.py +0 -0
  217. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/launcher.py +0 -0
  218. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/liger.py +0 -0
  219. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/log_level.py +0 -0
  220. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/long_context.py +0 -0
  221. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/lr_finder.py +0 -0
  222. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/metrics.py +0 -0
  223. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/mii.py +0 -0
  224. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/mixed_precision.py +0 -0
  225. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/mlx.py +0 -0
  226. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/moe.py +0 -0
  227. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/multipack.py +0 -0
  228. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/multipack_sampler.py +0 -0
  229. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/neat_packing.py +0 -0
  230. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/ollama.py +0 -0
  231. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/paths.py +0 -0
  232. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/peft_builder.py +0 -0
  233. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/peft_patches.py +0 -0
  234. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/pipeline.py +0 -0
  235. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/preference_combine.py +0 -0
  236. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/profiler.py +0 -0
  237. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/profiling.py +0 -0
  238. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/qat.py +0 -0
  239. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/quality.py +0 -0
  240. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/quant_menu.py +0 -0
  241. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/registry.py +0 -0
  242. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/relora.py +0 -0
  243. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/replay.py +0 -0
  244. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/ring_attention.py +0 -0
  245. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/run_cost.py +0 -0
  246. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/sglang.py +0 -0
  247. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/spec_pairing.py +0 -0
  248. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/spike_recovery.py +0 -0
  249. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/structured_output.py +0 -0
  250. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/topology.py +0 -0
  251. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/tracing.py +0 -0
  252. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/trust_remote.py +0 -0
  253. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/unsloth.py +0 -0
  254. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/v028_features.py +0 -0
  255. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/vllm.py +0 -0
  256. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/warmup.py +0 -0
  257. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_cli/utils/why.py +0 -0
  258. {soup_cli-0.40.2 → soup_cli-0.40.4}/soup_logo_svg.svg +0 -0
  259. {soup_cli-0.40.2 → soup_cli-0.40.4}/templates/chat.yaml +0 -0
  260. {soup_cli-0.40.2 → soup_cli-0.40.4}/templates/code.yaml +0 -0
  261. {soup_cli-0.40.2 → soup_cli-0.40.4}/templates/medical.yaml +0 -0
  262. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/__init__.py +0 -0
  263. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/conftest.py +0 -0
  264. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_adapters.py +0 -0
  265. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_advanced_peft.py +0 -0
  266. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_assistant_mask.py +0 -0
  267. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_audio.py +0 -0
  268. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_auto_tuning.py +0 -0
  269. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_autopilot.py +0 -0
  270. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_awq_gptq_export.py +0 -0
  271. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_batch_probe.py +0 -0
  272. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_bco.py +0 -0
  273. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_bench.py +0 -0
  274. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_bugfixes.py +0 -0
  275. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_callback.py +0 -0
  276. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_cans.py +0 -0
  277. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_chat.py +0 -0
  278. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_chat_template.py +0 -0
  279. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_cli.py +0 -0
  280. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_cli_subprocess.py +0 -0
  281. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_config.py +0 -0
  282. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_cost.py +0 -0
  283. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_crash_reporter.py +0 -0
  284. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_curriculum.py +0 -0
  285. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_data.py +0 -0
  286. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_data_augment.py +0 -0
  287. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_data_sample.py +0 -0
  288. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_data_split.py +0 -0
  289. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_data_tools.py +0 -0
  290. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_dataset_hub.py +0 -0
  291. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_dataset_registry.py +0 -0
  292. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_deepspeed.py +0 -0
  293. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_deploy_ollama.py +0 -0
  294. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_diff.py +0 -0
  295. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_display.py +0 -0
  296. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_doctor.py +0 -0
  297. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_dpo_example.py +0 -0
  298. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_dpo_variants.py +0 -0
  299. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_embedding.py +0 -0
  300. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_errors.py +0 -0
  301. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_eval.py +0 -0
  302. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_eval_gate.py +0 -0
  303. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_eval_platform.py +0 -0
  304. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_export.py +0 -0
  305. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_formats.py +0 -0
  306. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_fp8_recipe.py +0 -0
  307. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_freeze_training.py +0 -0
  308. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_generate.py +0 -0
  309. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_gpu.py +0 -0
  310. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_grpo.py +0 -0
  311. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_hf_integration.py +0 -0
  312. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_infer.py +0 -0
  313. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_inference_advanced.py +0 -0
  314. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_init.py +0 -0
  315. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_ipo.py +0 -0
  316. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_jinja_analyzer.py +0 -0
  317. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_kto.py +0 -0
  318. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_loader.py +0 -0
  319. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_log_level.py +0 -0
  320. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_loss_watchdog.py +0 -0
  321. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_merge.py +0 -0
  322. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_migrate.py +0 -0
  323. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_mlx_backend.py +0 -0
  324. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_moe.py +0 -0
  325. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_multi_adapter.py +0 -0
  326. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_multi_gpu.py +0 -0
  327. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_multipack_config.py +0 -0
  328. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_multipack_invariants.py +0 -0
  329. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_multipack_sampler.py +0 -0
  330. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_neat_packing.py +0 -0
  331. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_neftune_rslora.py +0 -0
  332. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_onnx_tensorrt_export.py +0 -0
  333. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_orpo.py +0 -0
  334. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_packing.py +0 -0
  335. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_part_a_wave1.py +0 -0
  336. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_part_a_wave2.py +0 -0
  337. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_part_b.py +0 -0
  338. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_part_c.py +0 -0
  339. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_part_d.py +0 -0
  340. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_part_e.py +0 -0
  341. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_part_f_hardening.py +0 -0
  342. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_peft_methods.py +0 -0
  343. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_peft_patches.py +0 -0
  344. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_performance.py +0 -0
  345. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_pissa_init.py +0 -0
  346. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_ppo.py +0 -0
  347. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_preference_dispatcher.py +0 -0
  348. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_preference_multi.py +0 -0
  349. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_preference_multi_runtime.py +0 -0
  350. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_pretrain.py +0 -0
  351. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_profile.py +0 -0
  352. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_profiling.py +0 -0
  353. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_progress.py +0 -0
  354. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_push.py +0 -0
  355. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_qat.py +0 -0
  356. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_quality_filter.py +0 -0
  357. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_quant_check.py +0 -0
  358. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_quant_menu.py +0 -0
  359. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_quickstart.py +0 -0
  360. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_rank_pattern.py +0 -0
  361. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_recipes.py +0 -0
  362. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_recipes_v031.py +0 -0
  363. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_registry.py +0 -0
  364. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_relora.py +0 -0
  365. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_replay.py +0 -0
  366. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_resume.py +0 -0
  367. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_rlvr.py +0 -0
  368. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_run_cost.py +0 -0
  369. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_runs.py +0 -0
  370. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_serve.py +0 -0
  371. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_server_generate.py +0 -0
  372. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_sglang_serve.py +0 -0
  373. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_simpo.py +0 -0
  374. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_smoke_train.py +0 -0
  375. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_speculative_decoding.py +0 -0
  376. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_sweep.py +0 -0
  377. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_synth_data_pro.py +0 -0
  378. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_templates_yaml.py +0 -0
  379. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_tensorboard.py +0 -0
  380. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_tool_calling.py +0 -0
  381. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_trace_to_pref.py +0 -0
  382. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_tracker.py +0 -0
  383. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_trainer_coverage_v035.py +0 -0
  384. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_trainer_init.py +0 -0
  385. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_training_intelligence.py +0 -0
  386. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_training_speed.py +0 -0
  387. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_trust_remote_code.py +0 -0
  388. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_tui.py +0 -0
  389. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_ui.py +0 -0
  390. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_ui_chat.py +0 -0
  391. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_ui_config_builder.py +0 -0
  392. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_ui_live_monitor.py +0 -0
  393. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_ui_metrics.py +0 -0
  394. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_unsloth.py +0 -0
  395. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_v0401_part_c.py +0 -0
  396. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_v0401_part_d.py +0 -0
  397. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_v0401_part_e.py +0 -0
  398. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_v0402_part_a.py +0 -0
  399. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_v0402_part_b.py +0 -0
  400. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_validator.py +0 -0
  401. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_vision.py +0 -0
  402. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_vllm_serve.py +0 -0
  403. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_why.py +0 -0
  404. {soup_cli-0.40.2 → soup_cli-0.40.4}/tests/test_windows_encoding.py +0 -0
@@ -111,7 +111,7 @@ soup_cli/
111
111
  templates/ - 17 built-in soup.yaml templates (YAML + manifest.json) with load_template loader (v0.39.0, +bco v0.40.0)
112
112
  ui/ - Web UI (FastAPI + HTML/JS SPA)
113
113
 
114
- tests/ - Test suite (143 files, 4756 tests)
114
+ tests/ - Test suite (148 files, 4930 tests)
115
115
  examples/ - Real-world config examples and datasets
116
116
  ```
117
117
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: soup-cli
3
- Version: 0.40.2
3
+ Version: 0.40.4
4
4
  Summary: Fine-tune LLMs in one command. No SSH, no config hell.
5
5
  Project-URL: Homepage, https://github.com/MakazhanAlpamys/Soup
6
6
  Project-URL: Repository, https://github.com/MakazhanAlpamys/Soup
@@ -134,17 +134,11 @@ soup train
134
134
 
135
135
  Latest highlights only. Full history: [GitHub Releases](https://github.com/MakazhanAlpamys/Soup/releases).
136
136
 
137
- **v0.40.2Quick polish + carry-overs**: closes 3 originally-scheduled GitHub issues (#36 eval-gate dashboard row, #50 smarter `--hf-resume`, #51 custom HF Space templates) plus 7 v0.40.1 long-tail UX papercuts.
137
+ **v0.40.4trust_remote_code multi-trainer + multipack live wiring**: closes two carry-over gaps from earlier releases the v0.36.0 known gap (only SFT honoured the opt-in) and the v0.40.3-deferred multipack sampler.
138
138
 
139
- - **`--hf-resume` prefers local newer** — `prepare_hf_resume` checks the highest local `checkpoint-N` against the highest remote branch and skips the snapshot download when the local copy is newer or equal. Saves bandwidth and never overwrites a fresher local checkpoint with stale Hub state.
140
- - **Eval-gate dashboard row** — pure formatter `format_gate_row(state)` lives in `soup_cli/monitoring/display.py` and renders a one-liner like `Gate: helpfulness 7.8 | math 0.82 (-0.06) | STOP` for the live training panel. Hidden when eval-gate is disabled.
141
- - **`soup deploy hf-space --template-dir <path>`** supply your own `app.py` + `README.md` (+ optional `requirements.txt`) instead of the built-in `gradio-chat` / `streamlit-chat`. Containment-checked, repo-id substitution validated *before* render, 256 KB cap per file, symlinks rejected.
142
- - **`soup quickstart --output DIR`** — route data, config, and run dir under any directory you choose (default keeps backwards-compat in cwd).
143
- - **`soup runs --cwd-only`** — restrict the listing to runs whose `output_dir` is under the current directory; the global `~/.soup/experiments.db` view is still default.
144
- - **`soup infer` / `soup bench` accept HF ids** — when the local model path is missing AND the value isn't path-like (no `./`, `/`, `~`, `C:\`), it falls through to a HuggingFace download via `transformers.from_pretrained`. Path-like-but-missing surfaces a friendly `FileNotFoundError`.
145
- - **CLI flag aliases** — `data filter --min-coherence` (alias for `--coherence`); `data split --train` accepted (informational, train is the implicit remainder); `data register / unregister` accept positional `<name>` and `<path>` alongside the `--name` / `--path` options, with conflict detection.
146
- - **`--log-level` plumbing complete** — `apply_logging_level` now sets the root logger so third-party libraries (transformers / peft / trl) actually respect QUIET and DEBUG. The four tiers no longer produce byte-identical output.
147
- - **+36 net new tests** across the new helpers, security-fix follow-ups (path-containment in `register_data`, `bench.py`, `infer.py --output`, symlink-reject in custom Space templates), and Windows cross-drive edge cases.
139
+ - **`--trust-remote-code` everywhere** — every non-SFT trainer (DPO / GRPO / KTO / ORPO / SimPO / IPO / PPO / RewardModel / Pretrain / Embedding / BCO + the unified Preference dispatcher) now defaults to `trust_remote_code=False` and only enables custom-code execution when the user explicitly opts in. Same for `soup diff`, `soup export`, `soup merge`, `soup infer`, and `soup data generate`. The v0.36.0 `KNOWN_SAFE_PREFIXES` allowlist still suppresses the warning panel for first-party orgs; unknown-org local checkpoints with `auto_map` raise a friendly `ValueError` at construction time instead of silently exec'ing on `from_pretrained`.
140
+ - **Multipack sampler — live in HF Trainer** — `make_multipack_trainer_class` adds a `get_train_dataloader` override that installs a `MultipackBatchSampler(real_batches=False)` as the DataLoader's `batch_sampler=`. The shape mismatch that blocked v0.40.3 (`Sampler[int]` vs `list[list[int]]`) is gone the sampler now yields flat `list[int]` per packed sequence, which is what `DataLoader.batch_sampler` actually expects. SFT and Pretrain wrappers instantiate the multipack subclass when `multipack: true`; the v0.40.3 yellow advisory is gone.
141
+ - **+75 net new tests** across the trainer×trust_remote_code matrix (parametrize over 12 wrappers × {default-off / opt-in / unknown-org rejection}), source-level invariants (no remaining `trust_remote_code=True` literal in any trainer file), the new DataLoader override (state-missing fallback + flat-pack-yield contract), and the real `transformers.Trainer` MRO mix-in.
148
142
 
149
143
  ## Why Soup?
150
144
 
@@ -661,7 +655,7 @@ training:
661
655
 
662
656
  **Architecture allowlist** — 18 supported (Llama 3.x, Qwen 2/3, Mistral, Gemma 2/3, Phi 3/4, DeepSeek V2/V3, Mixtral, Falcon, StableLM, SmolLM2). Unknown architectures **fail loudly at config-load** instead of silently no-opping (critical fix vs Axolotl's silent-miss footgun).
663
657
 
664
- **v0.37.0 scope:** schema gate + helper builder ship now. Live wiring of the sampler into HF Trainer's `_get_train_sampler` lands in v0.37.1 (mirrors v0.27.0 MII stub-then-live pattern). Multipack is **sft / pretrain only** on the `transformers` backend; preference / RLHF trainers and MLX backend get distinct error messages naming the actual reason.
658
+ **Live wiring** — landed. SFT and Pretrain trainer wrappers actually instantiate the multipack subclass when `multipack: true` is set. The factory's `get_train_dataloader` override installs `MultipackBatchSampler(real_batches=False)` (yields a flat `list[int]` per packed sequence — DataLoader-compatible) as the DataLoader's `batch_sampler=`, forwarding `dataloader_drop_last`/`num_workers`/`pin_memory` from `TrainingArguments`. The `_get_train_sampler` override stays as a defensive no-op fallback that always delegates to super, so any HF eval / prediction loop bypassing `get_train_dataloader` still gets the correct `Sampler[int]` shape (no nested-list shape mismatch). Multipack is **sft / pretrain only** on the `transformers` backend; preference / RLHF trainers and MLX backend get distinct error messages naming the actual reason. Datasets must expose `input_ids` (preferred) or `length` per row; raw text triggers an all-zeros warning.
665
659
 
666
660
  **DoS hardening** — the FFD packer caps at 1M items (algorithm is O(N²) worst-case); the 4D mask builder caps allocations at 2³¹ cells; the chat-template Jinja analyzer caps at 128KB. Every numeric input rejects `bool` explicitly (matches v0.30.0+ project policy).
667
661
 
@@ -698,12 +692,19 @@ data:
698
692
 
699
693
  When the tokenizer ships a chat template with `{% generation %}` markers, the mask is exact. Without those markers, Soup falls back to an incremental tokenize-delta walk and documents the looseness.
700
694
 
701
- ### `--trust-remote-code` opt-in
695
+ ### `--trust-remote-code` opt-in (every command, every trainer)
702
696
 
703
- `soup train`, `chat`, `serve`, `data download`, `eval auto` now require `--trust-remote-code` to load any HF model that ships custom Python (`auto_map` in `config.json`). First-party orgs (Meta, Mistral, Qwen, Google, etc.) suppress the warning panel; everything else prints a `REMOTE CODE WARNING` panel before loading.
697
+ Every command that loads a model now requires `--trust-remote-code` to execute custom Python from a model repo (`auto_map` in `config.json`). First-party orgs (Meta, Mistral, Qwen, Google, etc.) suppress the warning panel; everything else prints a `REMOTE CODE WARNING` panel before loading. Unknown-org local checkpoints with `auto_map` raise a friendly `ValueError` at construction time instead of silently exec'ing inside `from_pretrained`.
698
+
699
+ Coverage:
700
+ - `soup train` (every task — SFT, DPO, GRPO, KTO, ORPO, SimPO, IPO, PPO, Reward Model, Pretrain, Embedding, BCO, and the unified Preference dispatcher)
701
+ - `soup chat`, `soup serve`, `soup data download`, `soup eval auto`
702
+ - `soup diff`, `soup export`, `soup merge`, `soup infer`, `soup data generate`
704
703
 
705
704
  ```bash
706
705
  soup train --config soup.yaml --trust-remote-code
706
+ soup infer --model my-org/custom-arch-model --input prompts.jsonl --trust-remote-code
707
+ soup export --model ./adapter --format gguf --trust-remote-code
707
708
  ```
708
709
 
709
710
  ### Chat-template hardening
@@ -2579,6 +2580,7 @@ soup serve --model <m> --structured-output json --json-schema s.json Constraine
2579
2580
  soup serve --model <m> --structured-output regex --regex-pattern '...' Regex-constrained output
2580
2581
  soup serve --model <m> --dashboard Live dashboard + /metrics endpoint
2581
2582
  soup serve --model <m> --trace --trace-endpoint http://localhost:4317 OpenTelemetry tracing
2583
+ soup serve --model <m> --trace-log ./serve.jsonl Per-request JSONL log + rotation + secret redaction
2582
2584
  POST /v1/adapters/activate/<name> Hot-swap active LoRA adapter
2583
2585
  soup sweep --config soup.yaml --param lr=... Hyperparameter search
2584
2586
  soup diff --model-a ./a --model-b ./b Compare two models
@@ -2594,7 +2596,9 @@ soup data generate ... --provider anthropic Use Claude API
2594
2596
  soup data generate ... --provider vllm Use local vLLM server
2595
2597
  soup data generate ... --template code Domain templates (code/conversation/qa/preference/reasoning)
2596
2598
  soup data generate ... --quality-pipeline Auto validate + filter + dedup
2597
- soup data augment <path> --strategy rephrase|translate|style LLM-driven augmentationsoup data from-traces --logs l.jsonl --format langchain --signal thumbs_up --output p.jsonl Preference pairs from tracessoup data review prefs.jsonl --sample 10 Preview preference pairssoup data filter <path> --coherence 0.3 Quality filter (perplexity/coherence)
2599
+ soup data augment <path> --strategy rephrase|translate|style LLM-driven augmentationsoup data from-traces --logs l.jsonl --format langchain --signal thumbs_up --output p.jsonl Preference pairs from traces
2600
+ soup data from-traces ... --judge --min-confidence 0.7 LLM-judge confidence filter
2601
+ soup data review prefs.jsonl --sample 10 Preview preference pairssoup data filter <path> --coherence 0.3 Quality filter (perplexity/coherence)
2598
2602
  soup data sample <path> --n 1000 Random sample subset
2599
2603
  soup data sample <path> --n 1000 --strategy diverse Cluster-based diverse sampling
2600
2604
  soup data sample <path> --n 1000 --strategy hard Sample hardest examples
@@ -2791,6 +2795,44 @@ pytest tests/ -v
2791
2795
  pytest tests/ -m smoke -v
2792
2796
  ```
2793
2797
 
2798
+ ## Live CUDA Batch-Size Probe
2799
+
2800
+ Set `auto_batch_size_strategy: probe` in `training:` and Soup will run a real OOM-probe before training:
2801
+
2802
+ ```yaml
2803
+ training:
2804
+ batch_size: auto
2805
+ auto_batch_size_strategy: probe
2806
+ ```
2807
+
2808
+ For each candidate size `B`, the probe runs ONE forward + backward + step on a synthetic batch of `B` sequences of length `max_length`. On `torch.cuda.OutOfMemoryError` it halves; otherwise it doubles up to `4 × static_estimate`. The picked size is cached per `(model, max_length, quantization, lora_r, gpu)` tuple in `~/.soup/batch_cache.json` so subsequent runs skip the probe.
2809
+
2810
+ CPU sessions and `auto_batch_size_strategy: static` skip the probe. Synthetic batch tensors are freed before the backward pass so peak VRAM reflects the realistic training step. SFT-only this release — non-SFT trainers fall back to the static estimate.
2811
+
2812
+ ## Trace-to-Preference: LLM-Judge Filter
2813
+
2814
+ `soup data from-traces --judge` filters harvested preference pairs through an LLM judge:
2815
+
2816
+ ```bash
2817
+ soup data from-traces \
2818
+ --logs ./prod-traces.jsonl --format langchain --signal thumbs_up \
2819
+ --output ./prefs.jsonl \
2820
+ --judge --judge-provider ollama --judge-model llama3 \
2821
+ --min-confidence 0.7
2822
+ ```
2823
+
2824
+ The judge scores `chosen` and `rejected` independently against its rubric (default helpfulness/accuracy/safety on a 1-5 scale). Pairs whose normalised `(chosen - rejected)` confidence falls below `--min-confidence` are dropped. Per-pair backend exceptions are counted (not crashed) and reported. Provider allowlist `{openai, server, ollama}` validated at the CLI boundary; SSRF protection on `--judge-api-base` carries over from `soup eval judge`.
2825
+
2826
+ ## Inference Server Trace Log
2827
+
2828
+ `soup serve --trace-log <path>` writes a passive append-only JSONL log per chat completion:
2829
+
2830
+ ```bash
2831
+ soup serve --model ./out --trace-log ./serve-trace.jsonl --trace-log-cap-mb 100
2832
+ ```
2833
+
2834
+ Each line: `{"ts": ..., "prompt": ..., "response": ..., "latency_ms": ..., "tokens": ...}`. Path-containment validated, hard rotation cap (default 100 MB, one backup retained), symlink-reject on the backup path (TOCTOU defence), and `hf_*` / `sk-*` / `Bearer …` token shapes redacted to `<redacted>` before write. Failures (disk full, serialisation errors) never crash the request handler.
2835
+
2794
2836
  ## Changelog
2795
2837
 
2796
2838
  See [GitHub Releases](https://github.com/MakazhanAlpamys/Soup/releases) for version history.
@@ -43,17 +43,11 @@ soup train
43
43
 
44
44
  Latest highlights only. Full history: [GitHub Releases](https://github.com/MakazhanAlpamys/Soup/releases).
45
45
 
46
- **v0.40.2Quick polish + carry-overs**: closes 3 originally-scheduled GitHub issues (#36 eval-gate dashboard row, #50 smarter `--hf-resume`, #51 custom HF Space templates) plus 7 v0.40.1 long-tail UX papercuts.
47
-
48
- - **`--hf-resume` prefers local newer** — `prepare_hf_resume` checks the highest local `checkpoint-N` against the highest remote branch and skips the snapshot download when the local copy is newer or equal. Saves bandwidth and never overwrites a fresher local checkpoint with stale Hub state.
49
- - **Eval-gate dashboard row** — pure formatter `format_gate_row(state)` lives in `soup_cli/monitoring/display.py` and renders a one-liner like `Gate: helpfulness 7.8 | math 0.82 (-0.06) | STOP` for the live training panel. Hidden when eval-gate is disabled.
50
- - **`soup deploy hf-space --template-dir <path>`** supply your own `app.py` + `README.md` (+ optional `requirements.txt`) instead of the built-in `gradio-chat` / `streamlit-chat`. Containment-checked, repo-id substitution validated *before* render, 256 KB cap per file, symlinks rejected.
51
- - **`soup quickstart --output DIR`** — route data, config, and run dir under any directory you choose (default keeps backwards-compat in cwd).
52
- - **`soup runs --cwd-only`** — restrict the listing to runs whose `output_dir` is under the current directory; the global `~/.soup/experiments.db` view is still default.
53
- - **`soup infer` / `soup bench` accept HF ids** — when the local model path is missing AND the value isn't path-like (no `./`, `/`, `~`, `C:\`), it falls through to a HuggingFace download via `transformers.from_pretrained`. Path-like-but-missing surfaces a friendly `FileNotFoundError`.
54
- - **CLI flag aliases** — `data filter --min-coherence` (alias for `--coherence`); `data split --train` accepted (informational, train is the implicit remainder); `data register / unregister` accept positional `<name>` and `<path>` alongside the `--name` / `--path` options, with conflict detection.
55
- - **`--log-level` plumbing complete** — `apply_logging_level` now sets the root logger so third-party libraries (transformers / peft / trl) actually respect QUIET and DEBUG. The four tiers no longer produce byte-identical output.
56
- - **+36 net new tests** across the new helpers, security-fix follow-ups (path-containment in `register_data`, `bench.py`, `infer.py --output`, symlink-reject in custom Space templates), and Windows cross-drive edge cases.
46
+ **v0.40.4trust_remote_code multi-trainer + multipack live wiring**: closes two carry-over gaps from earlier releases the v0.36.0 known gap (only SFT honoured the opt-in) and the v0.40.3-deferred multipack sampler.
47
+
48
+ - **`--trust-remote-code` everywhere** — every non-SFT trainer (DPO / GRPO / KTO / ORPO / SimPO / IPO / PPO / RewardModel / Pretrain / Embedding / BCO + the unified Preference dispatcher) now defaults to `trust_remote_code=False` and only enables custom-code execution when the user explicitly opts in. Same for `soup diff`, `soup export`, `soup merge`, `soup infer`, and `soup data generate`. The v0.36.0 `KNOWN_SAFE_PREFIXES` allowlist still suppresses the warning panel for first-party orgs; unknown-org local checkpoints with `auto_map` raise a friendly `ValueError` at construction time instead of silently exec'ing on `from_pretrained`.
49
+ - **Multipack sampler — live in HF Trainer** — `make_multipack_trainer_class` adds a `get_train_dataloader` override that installs a `MultipackBatchSampler(real_batches=False)` as the DataLoader's `batch_sampler=`. The shape mismatch that blocked v0.40.3 (`Sampler[int]` vs `list[list[int]]`) is gone the sampler now yields flat `list[int]` per packed sequence, which is what `DataLoader.batch_sampler` actually expects. SFT and Pretrain wrappers instantiate the multipack subclass when `multipack: true`; the v0.40.3 yellow advisory is gone.
50
+ - **+75 net new tests** across the trainer×trust_remote_code matrix (parametrize over 12 wrappers × {default-off / opt-in / unknown-org rejection}), source-level invariants (no remaining `trust_remote_code=True` literal in any trainer file), the new DataLoader override (state-missing fallback + flat-pack-yield contract), and the real `transformers.Trainer` MRO mix-in.
57
51
 
58
52
  ## Why Soup?
59
53
 
@@ -570,7 +564,7 @@ training:
570
564
 
571
565
  **Architecture allowlist** — 18 supported (Llama 3.x, Qwen 2/3, Mistral, Gemma 2/3, Phi 3/4, DeepSeek V2/V3, Mixtral, Falcon, StableLM, SmolLM2). Unknown architectures **fail loudly at config-load** instead of silently no-opping (critical fix vs Axolotl's silent-miss footgun).
572
566
 
573
- **v0.37.0 scope:** schema gate + helper builder ship now. Live wiring of the sampler into HF Trainer's `_get_train_sampler` lands in v0.37.1 (mirrors v0.27.0 MII stub-then-live pattern). Multipack is **sft / pretrain only** on the `transformers` backend; preference / RLHF trainers and MLX backend get distinct error messages naming the actual reason.
567
+ **Live wiring** — landed. SFT and Pretrain trainer wrappers actually instantiate the multipack subclass when `multipack: true` is set. The factory's `get_train_dataloader` override installs `MultipackBatchSampler(real_batches=False)` (yields a flat `list[int]` per packed sequence — DataLoader-compatible) as the DataLoader's `batch_sampler=`, forwarding `dataloader_drop_last`/`num_workers`/`pin_memory` from `TrainingArguments`. The `_get_train_sampler` override stays as a defensive no-op fallback that always delegates to super, so any HF eval / prediction loop bypassing `get_train_dataloader` still gets the correct `Sampler[int]` shape (no nested-list shape mismatch). Multipack is **sft / pretrain only** on the `transformers` backend; preference / RLHF trainers and MLX backend get distinct error messages naming the actual reason. Datasets must expose `input_ids` (preferred) or `length` per row; raw text triggers an all-zeros warning.
574
568
 
575
569
  **DoS hardening** — the FFD packer caps at 1M items (algorithm is O(N²) worst-case); the 4D mask builder caps allocations at 2³¹ cells; the chat-template Jinja analyzer caps at 128KB. Every numeric input rejects `bool` explicitly (matches v0.30.0+ project policy).
576
570
 
@@ -607,12 +601,19 @@ data:
607
601
 
608
602
  When the tokenizer ships a chat template with `{% generation %}` markers, the mask is exact. Without those markers, Soup falls back to an incremental tokenize-delta walk and documents the looseness.
609
603
 
610
- ### `--trust-remote-code` opt-in
604
+ ### `--trust-remote-code` opt-in (every command, every trainer)
605
+
606
+ Every command that loads a model now requires `--trust-remote-code` to execute custom Python from a model repo (`auto_map` in `config.json`). First-party orgs (Meta, Mistral, Qwen, Google, etc.) suppress the warning panel; everything else prints a `REMOTE CODE WARNING` panel before loading. Unknown-org local checkpoints with `auto_map` raise a friendly `ValueError` at construction time instead of silently exec'ing inside `from_pretrained`.
611
607
 
612
- `soup train`, `chat`, `serve`, `data download`, `eval auto` now require `--trust-remote-code` to load any HF model that ships custom Python (`auto_map` in `config.json`). First-party orgs (Meta, Mistral, Qwen, Google, etc.) suppress the warning panel; everything else prints a `REMOTE CODE WARNING` panel before loading.
608
+ Coverage:
609
+ - `soup train` (every task — SFT, DPO, GRPO, KTO, ORPO, SimPO, IPO, PPO, Reward Model, Pretrain, Embedding, BCO, and the unified Preference dispatcher)
610
+ - `soup chat`, `soup serve`, `soup data download`, `soup eval auto`
611
+ - `soup diff`, `soup export`, `soup merge`, `soup infer`, `soup data generate`
613
612
 
614
613
  ```bash
615
614
  soup train --config soup.yaml --trust-remote-code
615
+ soup infer --model my-org/custom-arch-model --input prompts.jsonl --trust-remote-code
616
+ soup export --model ./adapter --format gguf --trust-remote-code
616
617
  ```
617
618
 
618
619
  ### Chat-template hardening
@@ -2488,6 +2489,7 @@ soup serve --model <m> --structured-output json --json-schema s.json Constraine
2488
2489
  soup serve --model <m> --structured-output regex --regex-pattern '...' Regex-constrained output
2489
2490
  soup serve --model <m> --dashboard Live dashboard + /metrics endpoint
2490
2491
  soup serve --model <m> --trace --trace-endpoint http://localhost:4317 OpenTelemetry tracing
2492
+ soup serve --model <m> --trace-log ./serve.jsonl Per-request JSONL log + rotation + secret redaction
2491
2493
  POST /v1/adapters/activate/<name> Hot-swap active LoRA adapter
2492
2494
  soup sweep --config soup.yaml --param lr=... Hyperparameter search
2493
2495
  soup diff --model-a ./a --model-b ./b Compare two models
@@ -2503,7 +2505,9 @@ soup data generate ... --provider anthropic Use Claude API
2503
2505
  soup data generate ... --provider vllm Use local vLLM server
2504
2506
  soup data generate ... --template code Domain templates (code/conversation/qa/preference/reasoning)
2505
2507
  soup data generate ... --quality-pipeline Auto validate + filter + dedup
2506
- soup data augment <path> --strategy rephrase|translate|style LLM-driven augmentationsoup data from-traces --logs l.jsonl --format langchain --signal thumbs_up --output p.jsonl Preference pairs from tracessoup data review prefs.jsonl --sample 10 Preview preference pairssoup data filter <path> --coherence 0.3 Quality filter (perplexity/coherence)
2508
+ soup data augment <path> --strategy rephrase|translate|style LLM-driven augmentationsoup data from-traces --logs l.jsonl --format langchain --signal thumbs_up --output p.jsonl Preference pairs from traces
2509
+ soup data from-traces ... --judge --min-confidence 0.7 LLM-judge confidence filter
2510
+ soup data review prefs.jsonl --sample 10 Preview preference pairssoup data filter <path> --coherence 0.3 Quality filter (perplexity/coherence)
2507
2511
  soup data sample <path> --n 1000 Random sample subset
2508
2512
  soup data sample <path> --n 1000 --strategy diverse Cluster-based diverse sampling
2509
2513
  soup data sample <path> --n 1000 --strategy hard Sample hardest examples
@@ -2700,6 +2704,44 @@ pytest tests/ -v
2700
2704
  pytest tests/ -m smoke -v
2701
2705
  ```
2702
2706
 
2707
+ ## Live CUDA Batch-Size Probe
2708
+
2709
+ Set `auto_batch_size_strategy: probe` in `training:` and Soup will run a real OOM-probe before training:
2710
+
2711
+ ```yaml
2712
+ training:
2713
+ batch_size: auto
2714
+ auto_batch_size_strategy: probe
2715
+ ```
2716
+
2717
+ For each candidate size `B`, the probe runs ONE forward + backward + step on a synthetic batch of `B` sequences of length `max_length`. On `torch.cuda.OutOfMemoryError` it halves; otherwise it doubles up to `4 × static_estimate`. The picked size is cached per `(model, max_length, quantization, lora_r, gpu)` tuple in `~/.soup/batch_cache.json` so subsequent runs skip the probe.
2718
+
2719
+ CPU sessions and `auto_batch_size_strategy: static` skip the probe. Synthetic batch tensors are freed before the backward pass so peak VRAM reflects the realistic training step. SFT-only this release — non-SFT trainers fall back to the static estimate.
2720
+
2721
+ ## Trace-to-Preference: LLM-Judge Filter
2722
+
2723
+ `soup data from-traces --judge` filters harvested preference pairs through an LLM judge:
2724
+
2725
+ ```bash
2726
+ soup data from-traces \
2727
+ --logs ./prod-traces.jsonl --format langchain --signal thumbs_up \
2728
+ --output ./prefs.jsonl \
2729
+ --judge --judge-provider ollama --judge-model llama3 \
2730
+ --min-confidence 0.7
2731
+ ```
2732
+
2733
+ The judge scores `chosen` and `rejected` independently against its rubric (default helpfulness/accuracy/safety on a 1-5 scale). Pairs whose normalised `(chosen - rejected)` confidence falls below `--min-confidence` are dropped. Per-pair backend exceptions are counted (not crashed) and reported. Provider allowlist `{openai, server, ollama}` validated at the CLI boundary; SSRF protection on `--judge-api-base` carries over from `soup eval judge`.
2734
+
2735
+ ## Inference Server Trace Log
2736
+
2737
+ `soup serve --trace-log <path>` writes a passive append-only JSONL log per chat completion:
2738
+
2739
+ ```bash
2740
+ soup serve --model ./out --trace-log ./serve-trace.jsonl --trace-log-cap-mb 100
2741
+ ```
2742
+
2743
+ Each line: `{"ts": ..., "prompt": ..., "response": ..., "latency_ms": ..., "tokens": ...}`. Path-containment validated, hard rotation cap (default 100 MB, one backup retained), symlink-reject on the backup path (TOCTOU defence), and `hf_*` / `sk-*` / `Bearer …` token shapes redacted to `<redacted>` before write. Failures (disk full, serialisation errors) never crash the request handler.
2744
+
2703
2745
  ## Changelog
2704
2746
 
2705
2747
  See [GitHub Releases](https://github.com/MakazhanAlpamys/Soup/releases) for version history.
@@ -9,7 +9,7 @@ We provide security updates for the following versions:
9
9
  - **Versions older than 3 minor versions:** No support
10
10
 
11
11
  Example:
12
- - v0.40.2 -- Full support (latest)
12
+ - v0.40.4 -- Full support (latest)
13
13
  - v0.40.0-v0.40.x -- Full support
14
14
  - v0.39.0-0.39.x -- Bug-fix support only
15
15
  - v0.38.0-0.38.x -- Bug-fix support only
@@ -144,6 +144,8 @@ No known critical vulnerabilities in current releases.
144
144
  - **v0.32.0 — Training Stability & Auto-Tuning**: `--find-lr-output` containment via shared `utils/paths.is_under_cwd` (prevents writes outside cwd); `save_lr_finder_report` rejects NaN / Infinity floats in `lrs` / `losses` and serialises with `allow_nan=False` (keeps the report parser-safe); `compute_lr_schedule` rejects non-positive `start_lr`, inverted ranges, and `num_steps` outside `[2, 10_000]`; `pick_mixed_precision` rejects empty / null-byte / >200-char model names and resolves multi-version quirks (`qwen2.5` vs `qwen2`, `phi-3.5` vs `phi-3`) by longest-substring-first iteration so an added family can never accidentally make a more-specific entry dead code; `compute_warmup_steps` clamps to `[10, 1000]` with a `ratio==0.0` short-circuit matching HF Trainer's "no warmup" convention; `SpikeRecoveryStrategy` is `@dataclass(frozen=True)` (post-construction mutation cannot bypass validation), `max_attempts ∈ [1, 10]`, `lr_decay ∈ (0, 1)`, `min_lr > 0`; cross-validator `_validate_spike_recovery_requires_watchdog` rejects `loss_spike_recovery=true, loss_watchdog=false` at config-load (fails fast instead of never triggering); `convergence_window ∈ [5, 10_000]`, `convergence_rel_tol ∈ (0, 1]`, `recommend_action` reuses `detect_plateau` so plateau heuristic stays single-source-of-truth; `GradAccumMonitor.recommend()` caps doubled `accum` at `MAX_ACCUM=1024` so a runaway advisory loop cannot blow up DataLoader prefetch; `generate_config` validates BOTH the YAML output path AND the embedded `decisions["output"]` field via `is_under_cwd` (closes the gap where a crafted `decisions["output"]="../../etc"` would have silently propagated into the rendered YAML)
145
145
  - **v0.34.0 — Observability & Dev UX**: `.crash` bundle generator (`utils/crash.py`) recursively redacts `hf_*` / `sk-*` / `Bearer …` token-shaped strings in any captured `config` and metric tail before serialisation, so a `.crash` file shared on a public GitHub issue cannot leak credentials; `output_dir` is reduced to `os.path.basename` so `$HOME` doesn't leak; `write_crash_bundle` uses `os.path.realpath + commonpath` for cwd containment (Windows-safe; raises `ValueError` not `PermissionError` so callers cannot silently swallow with `except OSError`); filename appends `secrets.token_hex(4)` so two crashes in the same UTC second don't collide; bundle truncated to `MAX_BUNDLE_BYTES=1_000_000`. `train.py` crash-write surfaces failures to the user (no silent missing-bundle). `profiling.py` `resolve_trace_path` rejects empty / `.` / `..` / `/` / `\\` / null-byte `run_id` (closes the `output_dir/profiles/../trace.json` escape) and uses `os.path.realpath + is_under_cwd`; profiles dir is created only on successful torch import (no stale empty dirs on torch-less CI). `tracker.get_run` LIKE-prefix match escapes `%` / `_` / `\\` and uses `ESCAPE '\\'` so a crafted `run_id` cannot widen the match (mirrors v0.26.0 registry policy). Lazy schema migration (`_ensure_schema`) tolerates the "duplicate column" race when two CLI processes start simultaneously on a fresh DB (fork-based multi-GPU training, TUI auto-refresh). `runs.py show/replay/clean` switched user `run_id` rendering to `markup_escape` and switched `clean` containment from broken `Path.resolve() + relative_to()` to project-standard `os.path.realpath + is_under_cwd`. `tui_app.py` lazy-imports `ExperimentTracker` and `markup_escape`s every DB-sourced string before passing into Textual widgets so a crafted base_model / experiment_name cannot inject `[bold red]…[/]` markup. `run_cost.estimate_run_cost_usd` rejects `bool` in `num_gpus` (bool is a subclass of int — same defence as v0.30.0 `Candidate.__post_init__`); duration clamped to `[0, 1 year]`; unknown GPU returns `None` so callers render `—` instead of fabricating `$0.00`. `log_level.parse_log_level` rejects non-string + null-byte input.
146
146
  - **v0.33.0 — Live Wire**: RLVR `code_exec_reward` adds OS-level isolation (Linux best-effort `os.unshare(CLONE_NEWUSER|CLONE_NEWNET|CLONE_NEWPID)`, macOS `sandbox-exec` with default-deny `MACOS_SANDBOX_PROFILE` narrowed to a 3-name `mach-lookup` allowlist to prevent DNS / NSURLSession bypass of `(deny network*)`); `prune_checkpoints` switches to TOCTOU-safe `os.lstat + S_ISLNK` + `shutil.rmtree(onerror=_abort_on_symlink)` so a symlink encountered mid-walk aborts rather than escapes; `run_gate` wraps each task scorer in a typed `try/except` so backend failures produce `score=None, error=str(exc)` (never silent `score=1.0`); `_parse_judge_url` removes the bare `http://` catch-all (defence-in-depth after the Pydantic GateTask validator); `soup can run` requires `--yes` or explicit consent callback and raises `ValueError` (not `PermissionError`, which is an `OSError` subclass that broad `except` blocks would swallow); GGUF `rglob` result for ollama deploy is `realpath+commonpath` checked against extract_dir (prevents symlink escape from a crafted can); `DeployTarget.path` validator normalises mixed `\\`/`/` separators before splitting (closes a Windows `..` bypass); `CAN_FORMAT_VERSION` 1→2 (additive — v1 still loads); `soup can publish` validates `repo_id` via `utils/hf.validate_repo_id`, resolves token via `resolve_token`, sanitises commit messages (first-line, 200-char cap), uses HTTPS-only HfApi; `_write_spike_recovery_hint` adds `is_under_cwd` containment check on `args.output_dir` from raw HF `TrainingArguments`; `lookup_entry_by_output_dir` emits `ResourceWarning` when 1000-row scan limit is hit (no silent miss); `CrossDocCollator` no longer mutates input feature dicts (HF Dataset rows are cached and reused — mutation broke subsequent batches); `Candidate` rejects `bool` in `score`/`latency_ms` (was sneaking past `int` isinstance check); `evaluate_candidate` latency mean now divides by *completed* prompts (excludes crashed) so a broken candidate isn't artificially fast; `auto_quant.run_auto_quant_picker` soft-falls-back to highest-scored candidate when no candidate clears `min_score` (server still binds); `build_logits_processors` returns `[]` when neither `outlines` nor `lm-format-enforcer` is installed (server degrades to free-form rather than 500); MII server uses loopback-only CORS, max_tokens cap [1, 16384], stream rejection, generic 500 with no stack-trace leak; `os.execvp` auto-reexec uses list args (no shell), all forwarded flags pre-validated; `cleanup_extract_dir` uses `os.path.commonpath` (Windows-safe) instead of `startswith`; `_run_subprocess` catches `TimeoutExpired` and returns rc=124 (coreutils convention) instead of an unhandled traceback; new `eval_results` and `tensorrt` artifact kinds in `RegistryStore._VALID_KINDS`
147
+ - **v0.40.4 — trust_remote_code multi-trainer + multipack live**: closes the v0.36.0 #63 known gap by extending the `--trust-remote-code` opt-in across every non-SFT trainer wrapper (DPO / GRPO / KTO / ORPO / SimPO / IPO / PPO / RewardModel / Pretrain / Embedding / BCO + the unified `PreferenceTrainerWrapper` dispatcher) and the 5 standalone commands (`soup diff`, `soup export`, `soup merge`, `soup infer`, `soup data generate`). Pattern (15 sites): each `__init__` resolves once via `model_requires_trust_remote_code(config.base) or False` + `resolve_trust_remote_code(...)` and stores `self._trust_remote_code` — every `from_pretrained` call site now reads the resolved value (no remaining `trust_remote_code=True` literal in any trainer file; source-level invariant test in `tests/test_v0404_part_a.py`). `commands/train.py` no longer carries the v0.36.0 `sft_kwargs` split; `trust_remote_code` is part of the unified `trainer_kwargs` dict that flows to every trainer regardless of task. `_load_reward_model` (module-level helper in `ppo.py`) accepts a `trust_remote_code: bool` parameter and resolves internally — design intent is that the helper is independently safe to call from outside `PPOTrainerWrapper`. `PreferenceTrainerWrapper` dispatcher forwards the raw bool to the inner DPO/SimPO/ORPO/IPO/BCO wrapper kwargs at both `_build_inner` and `_build_multi_objective` sites; the resolver fires inside the inner wrapper at construction time. `_export_onnx` / `_export_tensorrt` / `_export_awq` / `_export_gptq` and `_merge_adapter` helpers all gain a `trust_remote_code: bool = False` parameter threaded from the Typer flag. Multipack live HF Trainer wiring (#65) lands via a new `get_train_dataloader` override on `make_multipack_trainer_class` that installs `MultipackBatchSampler(real_batches=False)` (yields flat `list[int]` per pack — DataLoader-compatible) as the DataLoader's `batch_sampler=`. The override forwards `args.dataloader_drop_last` / `dataloader_num_workers` / `dataloader_pin_memory` from `TrainingArguments`. `_get_train_sampler` override stays as a defensive no-op fallback that ALWAYS delegates to super (review-fix: a multipack `list[list[int]]` from this method would cause a shape mismatch if any HF eval / prediction loop bypasses `get_train_dataloader`). The state-presence guard switched from falsy (`not max_seq`) to explicit `is None` + `not lengths` (defensively rejects only-None and empty-list cases — non-positive ints already rejected upstream by `attach_multipack_state`). Falls back to `super().get_train_dataloader()` when state is missing OR when `train_dataset` is unset (defence-in-depth so the subclass remains safe to instantiate). Known limitations: (1) `multipack: true` requires the dataset to expose `input_ids` (preferred) or `length` per row — un-tokenized text-only datasets trigger the v0.40.3 all-zeros WARNING and the `MultipackBatchSampler` will reject the run. (2) The DataLoader override does NOT thread FSDP / DeepSpeed parallelism env hints from `super().get_train_dataloader()`, so distributed `multipack: true` runs are still untested under FSDP / ZeRO; tracked for v0.40.5+ paired with v0.42.0 multi-GPU work. (3) `_live_lr_sweep_from_config` in `commands/train.py` still hardcodes `trust_remote_code=False` for the LR sweep's internal model load — defensive but means `--find-lr` cannot consume custom-code models even with the user opt-in (defence-in-depth, not a bypass). (4) Each non-SFT trainer's `__init__` repeats the resolver block (10 sites) — code-quality refactor candidate (single shared helper) deferred to a future patch to keep the v0.40.4 diff focused on the gap closure.
148
+ - **v0.40.3 — Stub-to-live**: New `soup_cli/utils/batch_probe.py:make_cuda_probe_fn` builds a CUDA probe closure that runs ONE forward+backward+step on a synthetic batch per candidate; `model.zero_grad(set_to_none=True)` runs BEFORE forward (defends against the synthetic backward accumulating into the live training model's grad buffers — matches v0.35.0 #45 `benchmark_kernel_combos` policy); intermediate `ids/attn/labels/outputs` are `del`-ed before `loss.backward()` so peak VRAM reflects a realistic training step; `bool` rejected on `batch_size` and `max_length`; `max_length < 8` rejected; `torch.cuda.OutOfMemoryError` returns False, other exceptions propagate; returns `None` (no-op) on non-CUDA / no-torch / missing model or tokenizer. New `soup_cli/utils/multipack_trainer.py:make_multipack_trainer_class` is `lru_cache`d so two calls with the same `base_cls` return the same subclass (consistent `isinstance`, pickle-safe); `attach_multipack_state` rejects `bool` on `max_seq_len`/`batch_size`/`seed` and rejects empty `lengths`; `lengths_from_dataset` logs WARNING when every row produces 0 (loud-fail mirrors v0.37.0 multipack arch allowlist — prevents silent NaN-loss footgun); `_get_train_sampler` override accepts `*args, **kwargs` for HF >= 4.41 signature compat. **Live wiring of the sampler into SFT / Pretrain trainer wrappers is deferred to v0.40.4** — adversarial 5th-pass review surfaced a `Sampler[int]` vs `list[list[int]]` shape mismatch with HF Trainer's DataLoader; the wrappers currently print a yellow advisory and fall back to the standard sampler when `multipack: true`. New `soup_cli/data/traces/quality.py:judge_filter_pairs` reuses v0.19.0 `JudgeEvaluator` SSRF protections; threshold rejects `bool` / NaN / out-of-`[0,1]`; `_MAX_BATCH=100_000` cap applied via lazy `itertools.islice` (never fully materialises a malicious / pathological generator); per-pair backend exceptions caught and logged at DEBUG (matches v0.33.0 #47 `CrossDocCollator` policy — never silently crash the harvest); `judge_provider` validated against `VALID_PROVIDERS` allowlist at the CLI boundary BEFORE constructor, with a Rich-escape error on mismatch. New `soup_cli/monitoring/trace_logger.py:TraceLogWriter` is thread-safe (single `threading.Lock` — multi-worker `--workers 4` documented as a single-process limitation); path containment via shared `is_under_cwd`; null-byte / empty / non-string path rejected; `cap_mb` bounds `[1, 10000]` with explicit `bool` rejection; rotation: when `current + extra > cap_bytes`, rename to `<path>.1` (one backup retained); symlink at the backup path is rejected via `os.lstat + stat.S_ISLNK` (matches v0.33.0 #22 TOCTOU policy) — defends against pre-placed `<log>.1 -> /etc/cron.d/x` overwrite. Secret redaction: prompt + response strings passed through `_SECRET_RE` matching `hf_*` (≥8), `sk-*` (≥16), and `Bearer …` (≥8) — replaces matches with `<redacted>` before serialisation (mirrors v0.34.0 `crash.py` policy). `--trace-log` constructor error messages in `commands/serve.py` are `rich.markup.escape`d before printing so a crafted path name cannot inject Rich markup. Unserialisable entries dropped silently; disk-full / OSError on write never crashes the request handler (passive log). Known limitations: live CUDA probe is wired in SFT only; multipack live wiring covers SFT+Pretrain only; `TraceLogWriter` retains exactly ONE backup file (operators wanting longer retention should use external rotation); custom HF Space templates from v0.40.2 still always create the Space with `space_sdk="gradio"` (tracked for v0.40.4+).
147
149
  - **v0.40.2 — Quick polish + carry-overs**: New `soup_cli/utils/hf_space.py:render_custom_template_dir` enforces `is_under_cwd` containment on the template directory; `validate_repo_id` runs BEFORE `{MODEL_REPO}` substitution (matches v0.29.0 Part F policy); per-file 256 KB cap (matches v0.39.0 Part E template-size policy); only `app.py` / `README.md` / `requirements.txt` are read (closed allowlist — no path-from-user-data). Symlinks rejected via `os.lstat + stat.S_ISLNK` and non-regular files (FIFO / device) also rejected (matches v0.33.0 #22 prune_checkpoints TOCTOU policy) — defends against `<template_dir>/app.py -> /etc/passwd`. `_find_highest_local_checkpoint` reads `output_dir` after caller's `is_under_cwd` validation (in `prepare_hf_resume`) and silently drops non-directories + OSError. `prepare_hf_resume` skips the snapshot download when local `checkpoint-N >= remote checkpoint-N` (saves bandwidth and never overwrites a fresher local checkpoint). `commands/data.py:register_data` containment switched from `Path.resolve() + relative_to()` to shared `is_under_cwd` (Windows 8.3 short-name safety per CLAUDE.md project rule); same fix applied to `commands/bench.py` prompts-file containment. `commands/infer.py:--output` now containment-checked via `is_under_cwd` (late-evaluated after model+input validation so pre-existing `tmp_path` test contracts keep working). `commands/quickstart.py:--output` validates target dir via `is_under_cwd` before `mkdir(parents=True)`; rejects out-of-cwd targets with friendly message. `commands/runs.py:_filter_runs_by_cwd` uses `os.path.realpath + commonpath`, catches `(ValueError, OSError)` so cross-drive paths on Windows (`D:\runs` vs `C:\project`) drop silently rather than crash. `monitoring/display.py:format_gate_row` uses explicit `task.get("passed") is True` so a missing `"passed"` field renders neutrally instead of as a false-y red ✗. `commands/infer.py:_resolve_model_source` heuristic for HF-id-vs-local-path: only falls through to HF when value is NOT path-like (no `./`, `/`, `\\`, `~`, no Windows drive letter, non-empty); path-like-but-missing raises `FileNotFoundError` so users see actionable errors instead of confusing HF download attempts. Known limitation: custom HF Space templates always create the Space with `space_sdk="gradio"` regardless of the supplied `app.py` (no `--sdk` flag in this release; combine `--template streamlit-chat` with the inline registry for Streamlit Spaces). Tracked for v0.40.3+.
148
150
  - **v0.40.1 — QA Hardening**: `soup_cli/utils/encoding.force_utf8_stdio` reconfigures Windows stdout/stderr to UTF-8 before any Rich Console is constructed; `os.environ.setdefault("PYTHONIOENCODING", "utf-8")` preserves user override; `(OSError, ValueError, AttributeError)` swallowed on detached streams; POSIX no-op. `SoupConfig._remap_root_level_misplaced_keys` (model_validator, mode='before') migrates root-level `lora:` into `training.lora` so nested validators (including `lora.init_strategy: Literal["random","pissa","olora"]`) actually fire — closes a footgun where the misplaced key was silently dropped. Caller's dict is never mutated (shallow-copy policy mirroring v0.33.0 #47 / v0.40.0 Part B). `PreferenceTrainerWrapper._build_multi_objective` replaces the v0.40.0 `NotImplementedError` stub with a primary-loss approximation; `validate_weight_compat` rejects BCO mixed with paired losses at runtime (data-format incompatible). `combine_losses` rejects empty weights, propagates NaN loudly (no silent zeroing), and rejects `bool` weight values (matches v0.30.0 `Candidate` policy). `_probe_cache_param_count` rejects empty / null-byte model names before path construction (mirrors v0.26.0 registry / v0.39.0 ReLoRAPolicy policy). `commands/doctor` flags `transformers ≥ 5.0.0` as INCOMPATIBLE via `_MAX_EXCLUSIVE` table; `_version_ge` parses leading-int chunks so `5.0.0.dev0` correctly trips the cap. `_detect_gpu_hw_without_torch_cuda` calls `nvidia-smi` via argv list (no shell), 5s timeout, `OSError` / `TimeoutExpired` caught; GPU label from `nvidia-smi` stdout is `rich.markup.escape`d before embedding in Rich-markup string (a real GPU name like `NVIDIA Quadro [T4]` cannot break or inject markup). `_detect_dual_python_interpreters` uses `os.path.realpath` (not `Path.resolve()`) for Windows 8.3 short-name compat. `_pick_quickstart_model` swaps TinyLlama-1.1B → SmolLM2-135M when `total_memory ≤ 6 GB` (prevents step-0 OOM on RTX 3050 4 GB / similar). `_live_lr_sweep_from_config` switched broken `load_local` import to `load_raw_data` (previously always silently fell back to a static placeholder curve). `commands/migrate` rejects `.jsonl` input (with first-line `{` sniff) with exit-2 friendly error; `.jsonl`-only suffix gate prevents false-positives on `.ipynb` notebooks. `commands/eval custom -o` is now honored independently of `--attach-to-registry`; loop-shadow regression where `output = generate_fn(...)` overwrote the CLI option fixed (variable renamed to `response`). `_load_jsonl` switched from `utf-8` to `utf-8-sig` so PowerShell `Out-File -Encoding utf8`-produced JSONL no longer fails first-row parse. Known limitation: `--trust-remote-code` opt-in surface still excludes 10 non-SFT trainers + 5 commands (v0.36.0 #63 carry-over).
149
151
  - **v0.40.0 — Preference Variety**: New `task='bco'` (Binary Classifier Optimization) and `task='preference'` (unified dispatcher). New schema fields: `bco_beta` (gt=0), `preference_loss: Literal[dpo,simpo,orpo,ipo,bco]|None`, `preference_loss_weights: Optional[Dict[str,float]]`, `dpo_beta_schedule: Literal[linear,cosine,exponential]|None`, `dpo_beta_end: float, gt=0|None`, `dpo_ref_regen_epochs: int [1,1000]|None`. Cross-validators: `_validate_preference_dispatcher` rejects setting either `preference_loss` or `preference_loss_weights` outside `task='preference'` (closes ordering-dependency between Part B/D validators); `_validate_dpo_variants_supported_tasks` gates β-schedule + ref-regen to DPO-family tasks (`dpo`, `ipo`, or `preference` + `preference_loss in {dpo, ipo}`); rejected on mlx backend with distinct error message (matches v0.34.0 distinct-reason policy); `_validate_preference_loss_weights` enforces 2–5 entries (single-entry rejected with actionable message pointing at scalar `preference_loss`), key allowlist `{dpo, simpo, orpo, ipo, bco}`, explicit null-byte rejection on keys (matches v0.39.0 rank_pattern policy), per-value bounds `(0, 1]`, weights must sum to 1.0 (±1e-6), mutually exclusive with scalar `preference_loss`, rejected on mlx backend. `compute_beta_at_step` rejects `bool` on `step` and `total_steps` (project bool-as-int policy from v0.30.0). `BetaScheduleCallback` resolves `total_steps` lazily in `on_train_begin` so the schedule sees the real `state.max_steps` populated by HF Trainer (closes a first-cut silent-no-op bug where total_steps=0 emitted beta_end for every step). `RefModelRegenCallback._regenerate` uses `strict=True` on `load_state_dict` and logs at WARNING on mismatch (closes a first-cut silent partial-copy hazard where strict=False could produce a hybrid old-base + new-LoRA reference); epoch 0 regen suppressed (avoids copying untrained student); trainer `.beta` assignment swallow narrowed to `AttributeError` only. `PreferenceTrainerWrapper._make_inner_cfg` uses `model_copy` (not `model_dump`+`model_validate`) so re-validation never sees an inconsistent intermediate state and the caller's `cfg` is never mutated (mirrors v0.33.0 #47 immutability policy). `_split_dpo_rows_to_bco` skipped-row count emitted at DEBUG so production silent-degradation is inspectable (mirrors v0.33.0 #47 CrossDocCollator policy). Multi-objective live runtime weighted-loss combination is deferred to v0.40.1: `PreferenceTrainerWrapper.setup` raises `NotImplementedError` with a friendly message naming the deferred-version follow-up (mirrors v0.27.0 MII / v0.37.0 multipack / v0.38.0 quant menu / v0.39.0 ReLoRA stub-then-live pattern). Known limitation: `BCOTrainerWrapper._setup_transformers` still hardcodes `trust_remote_code=True` (v0.36.0 #63 known-gap family carry-over across non-SFT trainers).
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "soup-cli"
7
- version = "0.40.2"
7
+ version = "0.40.4"
8
8
  description = "Fine-tune LLMs in one command. No SSH, no config hell."
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -1,3 +1,3 @@
1
1
  """Soup CLI — Fine-tune LLMs in one command."""
2
2
 
3
- __version__ = "0.40.2"
3
+ __version__ = "0.40.4"
@@ -1586,6 +1586,26 @@ def from_traces_cmd(
1586
1586
  "prefs.jsonl", "--output", "-o",
1587
1587
  help="Output path for preference pairs (JSONL)",
1588
1588
  ),
1589
+ judge: bool = typer.Option(
1590
+ False, "--judge",
1591
+ help="Filter pairs via LLM-as-a-judge confidence (v0.40.3 #33).",
1592
+ ),
1593
+ judge_provider: str = typer.Option(
1594
+ "openai", "--judge-provider",
1595
+ help="Judge backend: openai | server | ollama. Used with --judge.",
1596
+ ),
1597
+ judge_model: str = typer.Option(
1598
+ "gpt-4o-mini", "--judge-model",
1599
+ help="Judge model id (e.g. 'gpt-4o-mini', 'llama3', 'qwen2.5'). Used with --judge.",
1600
+ ),
1601
+ judge_api_base: Optional[str] = typer.Option(
1602
+ None, "--judge-api-base",
1603
+ help="Judge API base URL. SSRF-protected. Used with --judge.",
1604
+ ),
1605
+ min_confidence: float = typer.Option(
1606
+ 0.7, "--min-confidence",
1607
+ help="Drop pairs with judge-confidence below this threshold (0.0 - 1.0).",
1608
+ ),
1589
1609
  ) -> None:
1590
1610
  """Harvest preference pairs from production traces (v0.26.0 Part C).
1591
1611
 
@@ -1666,6 +1686,53 @@ def from_traces_cmd(
1666
1686
  trace_iter = parse_openai(events)
1667
1687
 
1668
1688
  pairs = list(build_pairs(trace_iter, signal=signal))
1689
+
1690
+ if judge:
1691
+ # v0.40.3 (#33 (a)) — LLM-judge confidence filter.
1692
+ from soup_cli.data.traces.quality import judge_filter_pairs
1693
+ from soup_cli.eval.judge import VALID_PROVIDERS, JudgeEvaluator
1694
+
1695
+ # Friendly early validation matches the existing CLI conventions —
1696
+ # fall through to the constructor only after the obvious typo is caught.
1697
+ if judge_provider not in VALID_PROVIDERS:
1698
+ console.print(
1699
+ f"[red]--judge-provider '{_escape(judge_provider)}' is invalid. "
1700
+ f"Choose: {', '.join(sorted(VALID_PROVIDERS))}[/]"
1701
+ )
1702
+ raise typer.Exit(1)
1703
+
1704
+ try:
1705
+ judge_evaluator = JudgeEvaluator(
1706
+ provider=judge_provider,
1707
+ model=judge_model,
1708
+ api_base=judge_api_base,
1709
+ )
1710
+ except ValueError as exc:
1711
+ console.print(f"[red]--judge config error:[/] {_escape(str(exc))}")
1712
+ raise typer.Exit(1) from exc
1713
+
1714
+ # Cost-shock warning: each pair → TWO judge calls (chosen + rejected).
1715
+ projected = len(pairs) * 2
1716
+ console.print(
1717
+ f"[yellow]Judge filter will issue ~{projected} backend calls "
1718
+ f"({len(pairs)} pairs × 2). Cost depends on provider and model. "
1719
+ f"Use --min-confidence to tune throughput.[/]"
1720
+ )
1721
+
1722
+ try:
1723
+ filtered, report = judge_filter_pairs(
1724
+ pairs, judge=judge_evaluator, min_confidence=min_confidence,
1725
+ )
1726
+ except (TypeError, ValueError) as exc:
1727
+ console.print(f"[red]--judge runtime error:[/] {_escape(str(exc))}")
1728
+ raise typer.Exit(1) from exc
1729
+
1730
+ console.print(
1731
+ f"[green]Judge filter:[/] kept={report.kept} dropped={report.dropped} "
1732
+ f"errors={report.errors} (min_confidence={min_confidence:.2f})"
1733
+ )
1734
+ pairs = filtered
1735
+
1669
1736
  with output_path.open("w", encoding="utf-8") as fh:
1670
1737
  for pair in pairs:
1671
1738
  fh.write(_json.dumps(pair.to_jsonl_dict(), ensure_ascii=False) + "\n")
@@ -69,6 +69,14 @@ def diff(
69
69
  "-o",
70
70
  help="Save results to JSONL file",
71
71
  ),
72
+ trust_remote_code: bool = typer.Option(
73
+ False,
74
+ "--trust-remote-code",
75
+ help=(
76
+ "Allow loading models that ship custom Python via auto_map. "
77
+ "Default deny (v0.36.0). Only enable if you trust the source."
78
+ ),
79
+ ),
72
80
  ):
73
81
  """Compare outputs of two models side-by-side on the same prompts."""
74
82
  # Validate model paths
@@ -105,9 +113,13 @@ def diff(
105
113
 
106
114
  # Load models
107
115
  console.print("[dim]Loading Model A...[/]")
108
- model_obj_a, tokenizer_a = _load_model(str(path_a), base_a, device)
116
+ model_obj_a, tokenizer_a = _load_model(
117
+ str(path_a), base_a, device, trust_remote_code,
118
+ )
109
119
  console.print("[dim]Loading Model B...[/]")
110
- model_obj_b, tokenizer_b = _load_model(str(path_b), base_b, device)
120
+ model_obj_b, tokenizer_b = _load_model(
121
+ str(path_b), base_b, device, trust_remote_code,
122
+ )
111
123
  console.print("[green]Both models loaded.[/]\n")
112
124
 
113
125
  # Run comparison
@@ -199,11 +211,21 @@ def _collect_prompts(prompts_file: Optional[str], prompt_args: Optional[list[str
199
211
  return result
200
212
 
201
213
 
202
- def _load_model(model_path: str, base_model: Optional[str], device: str):
214
+ def _load_model(
215
+ model_path: str,
216
+ base_model: Optional[str],
217
+ device: str,
218
+ trust_remote_code: bool = False,
219
+ ):
203
220
  """Load a model and tokenizer."""
204
221
  import torch
205
222
  from transformers import AutoModelForCausalLM, AutoTokenizer
206
223
 
224
+ from soup_cli.utils.trust_remote import (
225
+ model_requires_trust_remote_code,
226
+ resolve_trust_remote_code,
227
+ )
228
+
207
229
  path = Path(model_path)
208
230
  adapter_config_path = path / "adapter_config.json"
209
231
  is_adapter = adapter_config_path.exists()
@@ -220,7 +242,16 @@ def _load_model(model_path: str, base_model: Optional[str], device: str):
220
242
  console.print(f"[red]Cannot detect base model for {path}. Use --base-a/--base-b.[/]")
221
243
  raise typer.Exit(1)
222
244
 
223
- tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=True)
245
+ probe_target = base_model or model_path
246
+ requires = model_requires_trust_remote_code(model_path) or False
247
+ trc = resolve_trust_remote_code(
248
+ probe_target,
249
+ requested=trust_remote_code,
250
+ console=console,
251
+ requires_remote_code=requires,
252
+ )
253
+
254
+ tokenizer = AutoTokenizer.from_pretrained(model_path, trust_remote_code=trc)
224
255
  if tokenizer.pad_token is None:
225
256
  tokenizer.pad_token = tokenizer.eos_token
226
257
 
@@ -229,7 +260,7 @@ def _load_model(model_path: str, base_model: Optional[str], device: str):
229
260
 
230
261
  base = AutoModelForCausalLM.from_pretrained(
231
262
  base_model,
232
- trust_remote_code=True,
263
+ trust_remote_code=trc,
233
264
  device_map="auto",
234
265
  dtype=torch.float16,
235
266
  )
@@ -237,7 +268,7 @@ def _load_model(model_path: str, base_model: Optional[str], device: str):
237
268
  else:
238
269
  model_obj = AutoModelForCausalLM.from_pretrained(
239
270
  model_path,
240
- trust_remote_code=True,
271
+ trust_remote_code=trc,
241
272
  device_map="auto",
242
273
  dtype=torch.float16,
243
274
  )