libthx 0.3.1__tar.gz → 0.4.0__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 (296) hide show
  1. {libthx-0.3.1 → libthx-0.4.0}/PKG-INFO +1 -1
  2. {libthx-0.3.1 → libthx-0.4.0}/libthx.egg-info/PKG-INFO +1 -1
  3. {libthx-0.3.1 → libthx-0.4.0}/libthx.egg-info/SOURCES.txt +2 -0
  4. {libthx-0.3.1 → libthx-0.4.0}/pyproject.toml +1 -1
  5. libthx-0.4.0/tests/test_cleanup.py +145 -0
  6. {libthx-0.3.1 → libthx-0.4.0}/tests/test_execute_bootstrap.py +32 -0
  7. {libthx-0.3.1 → libthx-0.4.0}/tests/test_store.py +7 -4
  8. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/app.py +3 -1
  9. libthx-0.4.0/theseus/cli/cleanup.py +58 -0
  10. {libthx-0.3.1 → libthx-0.4.0}/theseus/execute/bootstrap.sh +15 -1
  11. {libthx-0.3.1 → libthx-0.4.0}/theseus/store.py +35 -2
  12. {libthx-0.3.1 → libthx-0.4.0}/LICENSE +0 -0
  13. {libthx-0.3.1 → libthx-0.4.0}/README.md +0 -0
  14. {libthx-0.3.1 → libthx-0.4.0}/libthx.egg-info/dependency_links.txt +0 -0
  15. {libthx-0.3.1 → libthx-0.4.0}/libthx.egg-info/entry_points.txt +0 -0
  16. {libthx-0.3.1 → libthx-0.4.0}/libthx.egg-info/requires.txt +0 -0
  17. {libthx-0.3.1 → libthx-0.4.0}/libthx.egg-info/top_level.txt +0 -0
  18. {libthx-0.3.1 → libthx-0.4.0}/setup.cfg +0 -0
  19. {libthx-0.3.1 → libthx-0.4.0}/tests/test_addition_dataset.py +0 -0
  20. {libthx-0.3.1 → libthx-0.4.0}/tests/test_analysis.py +0 -0
  21. {libthx-0.3.1 → libthx-0.4.0}/tests/test_base.py +0 -0
  22. {libthx-0.3.1 → libthx-0.4.0}/tests/test_bundle.py +0 -0
  23. {libthx-0.3.1 → libthx-0.4.0}/tests/test_checkpoint.py +0 -0
  24. {libthx-0.3.1 → libthx-0.4.0}/tests/test_cli.py +0 -0
  25. {libthx-0.3.1 → libthx-0.4.0}/tests/test_cli_browser.py +0 -0
  26. {libthx-0.3.1 → libthx-0.4.0}/tests/test_cli_configure.py +0 -0
  27. {libthx-0.3.1 → libthx-0.4.0}/tests/test_cli_execute.py +0 -0
  28. {libthx-0.3.1 → libthx-0.4.0}/tests/test_cli_import.py +0 -0
  29. {libthx-0.3.1 → libthx-0.4.0}/tests/test_cli_logs.py +0 -0
  30. {libthx-0.3.1 → libthx-0.4.0}/tests/test_cli_run.py +0 -0
  31. {libthx-0.3.1 → libthx-0.4.0}/tests/test_cli_verbosity.py +0 -0
  32. {libthx-0.3.1 → libthx-0.4.0}/tests/test_combobulator.py +0 -0
  33. {libthx-0.3.1 → libthx-0.4.0}/tests/test_comet_logging.py +0 -0
  34. {libthx-0.3.1 → libthx-0.4.0}/tests/test_contrastive_roundtrip.py +0 -0
  35. {libthx-0.3.1 → libthx-0.4.0}/tests/test_data.py +0 -0
  36. {libthx-0.3.1 → libthx-0.4.0}/tests/test_datasets.py +0 -0
  37. {libthx-0.3.1 → libthx-0.4.0}/tests/test_eval_padding.py +0 -0
  38. {libthx-0.3.1 → libthx-0.4.0}/tests/test_execute_bootstrap_generate.py +0 -0
  39. {libthx-0.3.1 → libthx-0.4.0}/tests/test_execute_dispatch.py +0 -0
  40. {libthx-0.3.1 → libthx-0.4.0}/tests/test_execute_provider_utils.py +0 -0
  41. {libthx-0.3.1 → libthx-0.4.0}/tests/test_execute_run.py +0 -0
  42. {libthx-0.3.1 → libthx-0.4.0}/tests/test_execute_solve.py +0 -0
  43. {libthx-0.3.1 → libthx-0.4.0}/tests/test_execute_volcano.py +0 -0
  44. {libthx-0.3.1 → libthx-0.4.0}/tests/test_flywheel_determinism.py +0 -0
  45. {libthx-0.3.1 → libthx-0.4.0}/tests/test_flywheel_reads.py +0 -0
  46. {libthx-0.3.1 → libthx-0.4.0}/tests/test_gpu_availability.py +0 -0
  47. {libthx-0.3.1 → libthx-0.4.0}/tests/test_hardware_discovery.py +0 -0
  48. {libthx-0.3.1 → libthx-0.4.0}/tests/test_hardware_dispatch.py +0 -0
  49. {libthx-0.3.1 → libthx-0.4.0}/tests/test_inference.py +0 -0
  50. {libthx-0.3.1 → libthx-0.4.0}/tests/test_job.py +0 -0
  51. {libthx-0.3.1 → libthx-0.4.0}/tests/test_kv_cache.py +0 -0
  52. {libthx-0.3.1 → libthx-0.4.0}/tests/test_lora.py +0 -0
  53. {libthx-0.3.1 → libthx-0.4.0}/tests/test_mfu.py +0 -0
  54. {libthx-0.3.1 → libthx-0.4.0}/tests/test_model_flops.py +0 -0
  55. {libthx-0.3.1 → libthx-0.4.0}/tests/test_module_debug.py +0 -0
  56. {libthx-0.3.1 → libthx-0.4.0}/tests/test_optimizers.py +0 -0
  57. {libthx-0.3.1 → libthx-0.4.0}/tests/test_optional_dependencies.py +0 -0
  58. {libthx-0.3.1 → libthx-0.4.0}/tests/test_profiler.py +0 -0
  59. {libthx-0.3.1 → libthx-0.4.0}/tests/test_quick.py +0 -0
  60. {libthx-0.3.1 → libthx-0.4.0}/tests/test_registries.py +0 -0
  61. {libthx-0.3.1 → libthx-0.4.0}/tests/test_rollout_chat_tokenization.py +0 -0
  62. {libthx-0.3.1 → libthx-0.4.0}/tests/test_rollout_eos.py +0 -0
  63. {libthx-0.3.1 → libthx-0.4.0}/tests/test_scalar_metadata.py +0 -0
  64. {libthx-0.3.1 → libthx-0.4.0}/tests/test_schedules.py +0 -0
  65. {libthx-0.3.1 → libthx-0.4.0}/tests/test_sharding.py +0 -0
  66. {libthx-0.3.1 → libthx-0.4.0}/tests/test_stack_e2e.py +0 -0
  67. {libthx-0.3.1 → libthx-0.4.0}/tests/test_store_delta.py +0 -0
  68. {libthx-0.3.1 → libthx-0.4.0}/tests/test_trainer_lifecycle.py +0 -0
  69. {libthx-0.3.1 → libthx-0.4.0}/tests/test_unembedding_dtype.py +0 -0
  70. {libthx-0.3.1 → libthx-0.4.0}/theseus/__init__.py +0 -0
  71. {libthx-0.3.1 → libthx-0.4.0}/theseus/analysis/__init__.py +0 -0
  72. {libthx-0.3.1 → libthx-0.4.0}/theseus/analysis/attention.py +0 -0
  73. {libthx-0.3.1 → libthx-0.4.0}/theseus/analysis/base.py +0 -0
  74. {libthx-0.3.1 → libthx-0.4.0}/theseus/analysis/plots.py +0 -0
  75. {libthx-0.3.1 → libthx-0.4.0}/theseus/analysis/trace.py +0 -0
  76. {libthx-0.3.1 → libthx-0.4.0}/theseus/base/__init__.py +0 -0
  77. {libthx-0.3.1 → libthx-0.4.0}/theseus/base/axis.py +0 -0
  78. {libthx-0.3.1 → libthx-0.4.0}/theseus/base/chip.py +0 -0
  79. {libthx-0.3.1 → libthx-0.4.0}/theseus/base/dag.py +0 -0
  80. {libthx-0.3.1 → libthx-0.4.0}/theseus/base/hardware.py +0 -0
  81. {libthx-0.3.1 → libthx-0.4.0}/theseus/base/job.py +0 -0
  82. {libthx-0.3.1 → libthx-0.4.0}/theseus/base/topology.py +0 -0
  83. {libthx-0.3.1 → libthx-0.4.0}/theseus/checkpoint.py +0 -0
  84. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/__init__.py +0 -0
  85. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/configurator/__init__.py +0 -0
  86. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/configurator/fields.py +0 -0
  87. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/configurator/steps.py +0 -0
  88. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/configure.py +0 -0
  89. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/__init__.py +0 -0
  90. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/actions.py +0 -0
  91. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/age.py +0 -0
  92. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/cache.py +0 -0
  93. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/chart.js +0 -0
  94. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/chart.py +0 -0
  95. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/data.py +0 -0
  96. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/driver.py +0 -0
  97. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/fields.py +0 -0
  98. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/keyboard.js +0 -0
  99. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/location.py +0 -0
  100. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/log_data.py +0 -0
  101. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/log_list.py +0 -0
  102. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/log_view.py +0 -0
  103. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/navigation.py +0 -0
  104. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/plot.py +0 -0
  105. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/plot_controls.py +0 -0
  106. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/plot_math.py +0 -0
  107. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/plotly_options.py +0 -0
  108. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/run.py +0 -0
  109. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/state.py +0 -0
  110. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/status.py +0 -0
  111. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/terminal.css +0 -0
  112. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/timeline.py +0 -0
  113. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/tree.py +0 -0
  114. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/tree_row.py +0 -0
  115. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/view.py +0 -0
  116. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/views.py +0 -0
  117. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/interface/walk.py +0 -0
  118. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/jobs.py +0 -0
  119. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/run.py +0 -0
  120. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/submit.py +0 -0
  121. {libthx-0.3.1 → libthx-0.4.0}/theseus/cli/ui.py +0 -0
  122. {libthx-0.3.1 → libthx-0.4.0}/theseus/config.py +0 -0
  123. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/__init__.py +0 -0
  124. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/__init__.py +0 -0
  125. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/addition.py +0 -0
  126. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/alpaca.py +0 -0
  127. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/bbq.py +0 -0
  128. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/ccaligned.py +0 -0
  129. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/cfq.py +0 -0
  130. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/clutrr.py +0 -0
  131. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/dataset.py +0 -0
  132. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/fever.py +0 -0
  133. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/fineweb.py +0 -0
  134. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/fineweb_edu.py +0 -0
  135. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/flan.py +0 -0
  136. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/harmfulqa.py +0 -0
  137. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/longbench.py +0 -0
  138. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/longhealth.py +0 -0
  139. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/mmlu.py +0 -0
  140. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/mnli.py +0 -0
  141. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/mtob.py +0 -0
  142. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/openr1_math.py +0 -0
  143. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/pes2o.py +0 -0
  144. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/pg19.py +0 -0
  145. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/pile.py +0 -0
  146. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/pile_detoxify.py +0 -0
  147. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/pile_injected.py +0 -0
  148. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/qqp.py +0 -0
  149. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/siqa.py +0 -0
  150. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/squad.py +0 -0
  151. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/sst2.py +0 -0
  152. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/datasets/winogrande.py +0 -0
  153. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/tokenize.py +0 -0
  154. {libthx-0.3.1 → libthx-0.4.0}/theseus/data/tokenizer.py +0 -0
  155. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/__init__.py +0 -0
  156. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/base.py +0 -0
  157. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/__init__.py +0 -0
  158. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/addition.py +0 -0
  159. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/alpaca.py +0 -0
  160. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/arc_challenge.py +0 -0
  161. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/arithmetic.py +0 -0
  162. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/bbh.py +0 -0
  163. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/bbq.py +0 -0
  164. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/blimp.py +0 -0
  165. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/ccaligned.py +0 -0
  166. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/cfq.py +0 -0
  167. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/clutrr.py +0 -0
  168. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/fever.py +0 -0
  169. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/gsm8k.py +0 -0
  170. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/hellaswag.py +0 -0
  171. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/longbench.py +0 -0
  172. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/longhealth.py +0 -0
  173. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/math.py +0 -0
  174. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/mmlu.py +0 -0
  175. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/mnli.py +0 -0
  176. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/mtob.py +0 -0
  177. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/perplexity_evals.py +0 -0
  178. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/pes2o.py +0 -0
  179. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/pg19.py +0 -0
  180. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/pg19_lengthgen.py +0 -0
  181. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/pile.py +0 -0
  182. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/pile_injected.py +0 -0
  183. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/qqp.py +0 -0
  184. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/siqa.py +0 -0
  185. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/squad.py +0 -0
  186. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/sst2.py +0 -0
  187. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/tinystories.py +0 -0
  188. {libthx-0.3.1 → libthx-0.4.0}/theseus/evaluation/datasets/winogrande.py +0 -0
  189. {libthx-0.3.1 → libthx-0.4.0}/theseus/execute/__init__.py +0 -0
  190. {libthx-0.3.1 → libthx-0.4.0}/theseus/execute/bootstrap.py +0 -0
  191. {libthx-0.3.1 → libthx-0.4.0}/theseus/execute/bundle.py +0 -0
  192. {libthx-0.3.1 → libthx-0.4.0}/theseus/execute/combobulator.py +0 -0
  193. {libthx-0.3.1 → libthx-0.4.0}/theseus/execute/config.py +0 -0
  194. {libthx-0.3.1 → libthx-0.4.0}/theseus/execute/dispatch.py +0 -0
  195. {libthx-0.3.1 → libthx-0.4.0}/theseus/execute/provider/__init__.py +0 -0
  196. {libthx-0.3.1 → libthx-0.4.0}/theseus/execute/provider/base.py +0 -0
  197. {libthx-0.3.1 → libthx-0.4.0}/theseus/execute/provider/slurm/__init__.py +0 -0
  198. {libthx-0.3.1 → libthx-0.4.0}/theseus/execute/provider/slurm/availability.py +0 -0
  199. {libthx-0.3.1 → libthx-0.4.0}/theseus/execute/provider/slurm/provider.py +0 -0
  200. {libthx-0.3.1 → libthx-0.4.0}/theseus/execute/provider/ssh.py +0 -0
  201. {libthx-0.3.1 → libthx-0.4.0}/theseus/execute/provider/utils.py +0 -0
  202. {libthx-0.3.1 → libthx-0.4.0}/theseus/execute/provider/volcano/__init__.py +0 -0
  203. {libthx-0.3.1 → libthx-0.4.0}/theseus/execute/provider/volcano/config.py +0 -0
  204. {libthx-0.3.1 → libthx-0.4.0}/theseus/execute/provider/volcano/manifest.py +0 -0
  205. {libthx-0.3.1 → libthx-0.4.0}/theseus/execute/provider/volcano/transport.py +0 -0
  206. {libthx-0.3.1 → libthx-0.4.0}/theseus/execute/run/__init__.py +0 -0
  207. {libthx-0.3.1 → libthx-0.4.0}/theseus/execute/run/__main__.py +0 -0
  208. {libthx-0.3.1 → libthx-0.4.0}/theseus/execute/run/lineage.py +0 -0
  209. {libthx-0.3.1 → libthx-0.4.0}/theseus/execute/run/run.py +0 -0
  210. {libthx-0.3.1 → libthx-0.4.0}/theseus/execute/solve.py +0 -0
  211. {libthx-0.3.1 → libthx-0.4.0}/theseus/experiments/__init__.py +0 -0
  212. {libthx-0.3.1 → libthx-0.4.0}/theseus/experiments/benchmark.py +0 -0
  213. {libthx-0.3.1 → libthx-0.4.0}/theseus/experiments/data/__init__.py +0 -0
  214. {libthx-0.3.1 → libthx-0.4.0}/theseus/experiments/data/blockwise.py +0 -0
  215. {libthx-0.3.1 → libthx-0.4.0}/theseus/experiments/data/streaming.py +0 -0
  216. {libthx-0.3.1 → libthx-0.4.0}/theseus/experiments/forking/__init__.py +0 -0
  217. {libthx-0.3.1 → libthx-0.4.0}/theseus/experiments/forking/pretrain.py +0 -0
  218. {libthx-0.3.1 → libthx-0.4.0}/theseus/experiments/models/__init__.py +0 -0
  219. {libthx-0.3.1 → libthx-0.4.0}/theseus/experiments/models/gpt.py +0 -0
  220. {libthx-0.3.1 → libthx-0.4.0}/theseus/experiments/models/gpt_neox.py +0 -0
  221. {libthx-0.3.1 → libthx-0.4.0}/theseus/experiments/models/llama.py +0 -0
  222. {libthx-0.3.1 → libthx-0.4.0}/theseus/experiments/models/qwen.py +0 -0
  223. {libthx-0.3.1 → libthx-0.4.0}/theseus/experiments/models/qwen_3_5.py +0 -0
  224. {libthx-0.3.1 → libthx-0.4.0}/theseus/inference/__init__.py +0 -0
  225. {libthx-0.3.1 → libthx-0.4.0}/theseus/inference/base.py +0 -0
  226. {libthx-0.3.1 → libthx-0.4.0}/theseus/job.py +0 -0
  227. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/__init__.py +0 -0
  228. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/activations/__init__.py +0 -0
  229. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/activations/swiglu.py +0 -0
  230. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/attention/__init__.py +0 -0
  231. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/attention/base.py +0 -0
  232. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/attention/forking.py +0 -0
  233. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/attention/gated_delta.py +0 -0
  234. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/attention/grouped.py +0 -0
  235. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/attention/rope.py +0 -0
  236. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/axes.py +0 -0
  237. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/block/__init__.py +0 -0
  238. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/block/block.py +0 -0
  239. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/block/forking.py +0 -0
  240. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/block/gpt_neox.py +0 -0
  241. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/block/llama.py +0 -0
  242. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/block/qwen.py +0 -0
  243. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/block/qwen_3_5.py +0 -0
  244. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/debug.py +0 -0
  245. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/layers/__init__.py +0 -0
  246. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/layers/layernorm.py +0 -0
  247. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/layers/mlp.py +0 -0
  248. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/layers/mrope.py +0 -0
  249. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/layers/rmsnorm.py +0 -0
  250. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/layers/rope.py +0 -0
  251. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/masks.py +0 -0
  252. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/models/__init__.py +0 -0
  253. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/models/base.py +0 -0
  254. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/models/contrib/__init__.py +0 -0
  255. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/models/contrib/gpt_neox.py +0 -0
  256. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/models/contrib/llama.py +0 -0
  257. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/models/contrib/marin.py +0 -0
  258. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/models/contrib/qwen.py +0 -0
  259. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/models/contrib/qwen_3_5.py +0 -0
  260. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/models/contrib/qwen_3_5_moe.py +0 -0
  261. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/models/thoughtbubbles.py +0 -0
  262. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/module.py +0 -0
  263. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/moe/__init__.py +0 -0
  264. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/moe/base.py +0 -0
  265. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/moe/shared.py +0 -0
  266. {libthx-0.3.1 → libthx-0.4.0}/theseus/model/utils.py +0 -0
  267. {libthx-0.3.1 → libthx-0.4.0}/theseus/quick.py +0 -0
  268. {libthx-0.3.1 → libthx-0.4.0}/theseus/registry.py +0 -0
  269. {libthx-0.3.1 → libthx-0.4.0}/theseus/training/__init__.py +0 -0
  270. {libthx-0.3.1 → libthx-0.4.0}/theseus/training/backbone.py +0 -0
  271. {libthx-0.3.1 → libthx-0.4.0}/theseus/training/base.py +0 -0
  272. {libthx-0.3.1 → libthx-0.4.0}/theseus/training/contrastive.py +0 -0
  273. {libthx-0.3.1 → libthx-0.4.0}/theseus/training/flywheel/__init__.py +0 -0
  274. {libthx-0.3.1 → libthx-0.4.0}/theseus/training/flywheel/contrastive.py +0 -0
  275. {libthx-0.3.1 → libthx-0.4.0}/theseus/training/flywheel/dataset.py +0 -0
  276. {libthx-0.3.1 → libthx-0.4.0}/theseus/training/flywheel/io.py +0 -0
  277. {libthx-0.3.1 → libthx-0.4.0}/theseus/training/flywheel/padded.py +0 -0
  278. {libthx-0.3.1 → libthx-0.4.0}/theseus/training/flywheel/pmd.py +0 -0
  279. {libthx-0.3.1 → libthx-0.4.0}/theseus/training/flywheel/strategy.py +0 -0
  280. {libthx-0.3.1 → libthx-0.4.0}/theseus/training/flywheel/stream.py +0 -0
  281. {libthx-0.3.1 → libthx-0.4.0}/theseus/training/grpo.py +0 -0
  282. {libthx-0.3.1 → libthx-0.4.0}/theseus/training/kl_divergence.py +0 -0
  283. {libthx-0.3.1 → libthx-0.4.0}/theseus/training/lora.py +0 -0
  284. {libthx-0.3.1 → libthx-0.4.0}/theseus/training/optimizers/__init__.py +0 -0
  285. {libthx-0.3.1 → libthx-0.4.0}/theseus/training/optimizers/adamw.py +0 -0
  286. {libthx-0.3.1 → libthx-0.4.0}/theseus/training/optimizers/base.py +0 -0
  287. {libthx-0.3.1 → libthx-0.4.0}/theseus/training/optimizers/muon.py +0 -0
  288. {libthx-0.3.1 → libthx-0.4.0}/theseus/training/ppo.py +0 -0
  289. {libthx-0.3.1 → libthx-0.4.0}/theseus/training/profiler.html +0 -0
  290. {libthx-0.3.1 → libthx-0.4.0}/theseus/training/profiler.py +0 -0
  291. {libthx-0.3.1 → libthx-0.4.0}/theseus/training/schedules/__init__.py +0 -0
  292. {libthx-0.3.1 → libthx-0.4.0}/theseus/training/schedules/base.py +0 -0
  293. {libthx-0.3.1 → libthx-0.4.0}/theseus/training/schedules/cosine_rewarm.py +0 -0
  294. {libthx-0.3.1 → libthx-0.4.0}/theseus/training/schedules/wsd.py +0 -0
  295. {libthx-0.3.1 → libthx-0.4.0}/theseus/training/schedules/wsds.py +0 -0
  296. {libthx-0.3.1 → libthx-0.4.0}/theseus/training/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: libthx
3
- Version: 0.3.1
3
+ Version: 0.4.0
4
4
  Summary: Architecture experimentation and training infrastructure.
5
5
  Requires-Python: <3.14,>=3.11
6
6
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: libthx
3
- Version: 0.3.1
3
+ Version: 0.4.0
4
4
  Summary: Architecture experimentation and training infrastructure.
5
5
  Requires-Python: <3.14,>=3.11
6
6
  Description-Content-Type: text/markdown
@@ -12,6 +12,7 @@ tests/test_analysis.py
12
12
  tests/test_base.py
13
13
  tests/test_bundle.py
14
14
  tests/test_checkpoint.py
15
+ tests/test_cleanup.py
15
16
  tests/test_cli.py
16
17
  tests/test_cli_browser.py
17
18
  tests/test_cli_configure.py
@@ -81,6 +82,7 @@ theseus/base/job.py
81
82
  theseus/base/topology.py
82
83
  theseus/cli/__init__.py
83
84
  theseus/cli/app.py
85
+ theseus/cli/cleanup.py
84
86
  theseus/cli/configure.py
85
87
  theseus/cli/jobs.py
86
88
  theseus/cli/run.py
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "libthx"
3
- version = "0.3.1"
3
+ version = "0.4.0"
4
4
  description = "Architecture experimentation and training infrastructure."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11,<3.14"
@@ -0,0 +1,145 @@
1
+ """Value-table maintenance preserves committed rows and reader snapshots."""
2
+
3
+ import os
4
+ import importlib
5
+ import shutil
6
+ from time import time
7
+ from unittest.mock import Mock
8
+
9
+ import pyarrow as pa
10
+ import pytest
11
+ from deltalake import DeltaTable, write_deltalake
12
+ from deltalake.exceptions import CommitFailedError
13
+ from deltalake.table import TableOptimizer
14
+ from typer.testing import CliRunner
15
+
16
+ from theseus.cli.app import app
17
+ from theseus.store import ObjectStore
18
+ import theseus.store as store_module
19
+
20
+
21
+ def seed(values):
22
+ for value in range(3):
23
+ write_deltalake(values, pa.table({"value": [value]}), mode="append")
24
+
25
+
26
+ @pytest.mark.parametrize("expire", [False, True])
27
+ def test_cleanup_compacts_and_vacuums_with_selected_retention(tmp_path, monkeypatch, expire):
28
+ values = tmp_path / "objects" / "values"
29
+ seed(values)
30
+ snapshot = DeltaTable(values)
31
+ old_files = snapshot.file_uris()
32
+ expired = values / "orphan.parquet"
33
+ shutil.copyfile(old_files[0], expired)
34
+ old_time = time() - 8 * 24 * 3600
35
+ os.utime(expired, (old_time, old_time))
36
+ recent = values / "recent-orphan.parquet"
37
+ shutil.copyfile(old_files[0], recent)
38
+ cleanup_module = importlib.import_module("theseus.cli.cleanup")
39
+ delay = Mock()
40
+ monkeypatch.setattr(cleanup_module, "sleep", delay)
41
+
42
+ result = CliRunner().invoke(app, ["cleanup", str(tmp_path), *(["--expire"] if expire else [])])
43
+
44
+ assert result.exit_code == 0, result.output
45
+ assert "Value table cleanup" in result.output
46
+ assert "CLEANUP | compacting" in result.output
47
+ assert "CLEANUP | vacuuming" in result.output
48
+ assert not expired.exists()
49
+ assert recent.exists() is not expire
50
+ assert len(DeltaTable(values).file_uris()) == 1
51
+ if expire:
52
+ delay.assert_called_once_with(10)
53
+ assert "ALL ACTIVE WRITERS WILL CRASH IN 10 SECONDS, C-c ONCE TO CANCEL" in result.output
54
+ assert all(not os.path.exists(path) for path in old_files)
55
+ else:
56
+ delay.assert_not_called()
57
+ # Default retention preserves readers bound before compaction.
58
+ assert sorted(snapshot.to_pyarrow_table()["value"].to_pylist()) == [0, 1, 2]
59
+ assert sorted(DeltaTable(values).to_pyarrow_table()["value"].to_pylist()) == [0, 1, 2]
60
+
61
+
62
+ def test_cleanup_expire_can_be_cancelled_before_mutation(tmp_path, monkeypatch):
63
+ cleanup_module = importlib.import_module("theseus.cli.cleanup")
64
+ delay = Mock(side_effect=KeyboardInterrupt)
65
+ compact = Mock()
66
+ vacuum = Mock()
67
+ monkeypatch.setattr(cleanup_module, "sleep", delay)
68
+ monkeypatch.setattr(ObjectStore, "compact", compact)
69
+ monkeypatch.setattr(DeltaTable, "vacuum", vacuum)
70
+
71
+ result = CliRunner().invoke(app, ["cleanup", str(tmp_path), "--expire"])
72
+
73
+ assert result.exit_code != 0
74
+ assert "ALL ACTIVE WRITERS WILL CRASH IN 10 SECONDS, C-c ONCE TO CANCEL" in result.output
75
+ delay.assert_called_once_with(10)
76
+ compact.assert_not_called()
77
+ vacuum.assert_not_called()
78
+
79
+
80
+ def test_cleanup_missing_table_is_noop(tmp_path):
81
+ result = CliRunner().invoke(app, ["cleanup", str(tmp_path)])
82
+ assert result.exit_code == 0, result.output
83
+ assert "no Delta value table" in result.output
84
+ assert not (tmp_path / "objects").exists()
85
+
86
+
87
+ @pytest.mark.parametrize("schema_change", [False, True])
88
+ def test_compaction_preserves_concurrent_append_and_retries_schema_change(
89
+ tmp_path, monkeypatch, schema_change,
90
+ ):
91
+ values = tmp_path / "objects" / "values"
92
+ seed(values)
93
+ original = TableOptimizer.compact
94
+ calls = 0
95
+
96
+ def racing_compact(optimizer):
97
+ nonlocal calls
98
+ calls += 1
99
+ if calls == 1:
100
+ data = {"value": [3]}
101
+ if schema_change:
102
+ data["new_column"] = [4]
103
+ write_deltalake(values, pa.table(data), mode="append", schema_mode="merge")
104
+ return original(optimizer)
105
+
106
+ monkeypatch.setattr(TableOptimizer, "compact", racing_compact)
107
+ monkeypatch.setattr(store_module, "_DELTA_IO_RETRY_SECONDS", 0)
108
+ ObjectStore.compact(tmp_path)
109
+
110
+ assert calls == (2 if schema_change else 1)
111
+ rows = DeltaTable(values).to_pyarrow_table()
112
+ assert sorted(rows["value"].to_pylist()) == [0, 1, 2, 3]
113
+ if schema_change:
114
+ assert rows.filter(pa.compute.equal(rows["value"], 3))["new_column"].to_pylist() == [4]
115
+
116
+
117
+ def test_close_logs_compaction_failure_after_flushing(tmp_path, monkeypatch):
118
+ from theseus.base import Node
119
+
120
+ store = ObjectStore.local(tmp_path)
121
+ store.value(Node(name="test"), {"value": 1})
122
+ warning = Mock()
123
+ monkeypatch.setattr(store_module.logger, "warning", warning)
124
+ monkeypatch.setattr(
125
+ ObjectStore, "compact", Mock(side_effect=CommitFailedError("conflict")),
126
+ )
127
+ store.close()
128
+ warning.assert_called_once()
129
+ assert store.query().select() == [{"value": 1}]
130
+
131
+
132
+ def test_cleanup_stops_before_vacuum_after_repeated_conflicts(tmp_path, monkeypatch):
133
+ seed(tmp_path / "objects" / "values")
134
+ compact = Mock(side_effect=CommitFailedError("concurrent schema change"))
135
+ vacuum = Mock()
136
+ monkeypatch.setattr(TableOptimizer, "compact", compact)
137
+ monkeypatch.setattr(DeltaTable, "vacuum", vacuum)
138
+ monkeypatch.setattr(store_module, "_DELTA_IO_RETRY_SECONDS", 0)
139
+
140
+ result = CliRunner().invoke(app, ["cleanup", str(tmp_path)])
141
+
142
+ assert result.exit_code == 1
143
+ assert "concurrent schema change" in result.output
144
+ assert compact.call_count == store_module._DELTA_IO_ATTEMPTS
145
+ vacuum.assert_not_called()
@@ -603,6 +603,38 @@ def test_bootstrap_autobatch_isolates_probes_and_overrides_real_run(
603
603
  assert "probing training.per_device_batch_size=512" in contents
604
604
 
605
605
 
606
+ def test_bootstrap_autobatch_waits_for_delayed_log_writer(
607
+ render_bootstrap: Callable[[int], tuple[Path, Path, Path, Path]],
608
+ monkeypatch: pytest.MonkeyPatch,
609
+ tmp_path: Path,
610
+ ) -> None:
611
+ import shlex
612
+
613
+ script, dispatch, calls, logs = render_bootstrap(-1)
614
+ monkeypatch.setenv("THESEUS_DISPATCH_PROBE_COOLDOWN_SECONDS", "0")
615
+ real_tee = shutil.which("tee")
616
+ assert real_tee is not None
617
+ delayed_tee = tmp_path / "bin" / "tee"
618
+ delayed_tee.write_text(
619
+ '#!/usr/bin/env bash\n'
620
+ 'if [[ "$1" == */autobatch-*.log ]]; then sleep 2; fi\n'
621
+ f'exec {shlex.quote(real_tee)} "$@"\n'
622
+ )
623
+ delayed_tee.chmod(0o755)
624
+
625
+ result = subprocess.run(
626
+ ["bash", script, dispatch], text=True, capture_output=True, timeout=30,
627
+ )
628
+
629
+ assert result.returncode == 0, result.stdout + result.stderr
630
+ output = result.stdout + result.stderr
631
+ assert "No such file or directory" not in output
632
+ run_calls = [line for line in calls.read_text().splitlines() if line.startswith("run")]
633
+ assert len(run_calls) == 3
634
+ assert "dispatch.json training.per_device_batch_size=512" in run_calls[-1]
635
+ assert "RESOURCE_EXHAUSTED" in (logs / "project-group-name-abc123.0.log").read_text()
636
+
637
+
606
638
  def test_bootstrap_autobatch_skips_clamped_duplicate_candidates(
607
639
  render_bootstrap: Callable[[int], tuple[Path, Path, Path, Path]],
608
640
  monkeypatch: pytest.MonkeyPatch,
@@ -203,7 +203,7 @@ def test_value_batch_flush_and_lifecycle(
203
203
  assert store.thread is not None and store.thread is not first_thread
204
204
  store.value(node, {"restarted": 1})
205
205
  store.close()
206
- assert len(DeltaTable(store.values()).file_uris()) == 2
206
+ assert len(DeltaTable(store.values()).file_uris()) == 1
207
207
  assert store.query().node(node).select() == [{"loss": 2.0, "restarted": 1}]
208
208
 
209
209
 
@@ -222,7 +222,7 @@ def test_value_time_flush(
222
222
  assert store.query().node(node).select() == [{"value": 1}]
223
223
 
224
224
 
225
- def test_close_does_not_rewrite_or_remove_committed_parts(
225
+ def test_close_compacts_without_vacuuming_committed_parts(
226
226
  hardware: HardwareResult, monkeypatch: pytest.MonkeyPatch
227
227
  ) -> None:
228
228
  monkeypatch.setattr(store_module, "_VALUE_BATCH_SIZE", 1)
@@ -235,8 +235,11 @@ def test_close_does_not_rewrite_or_remove_committed_parts(
235
235
  before = {part: part.read_bytes() for part in parts}
236
236
  version = DeltaTable(store.values()).version()
237
237
  store.close()
238
+ compacted = DeltaTable(store.values())
239
+ assert len(compacted.file_uris()) == 1
240
+ assert compacted.version() > version
238
241
  store.close()
239
- assert DeltaTable(store.values()).version() == version
242
+ assert DeltaTable(store.values()).version() == compacted.version()
240
243
  assert {part: part.read_bytes() for part in parts} == before
241
244
  assert store.query().all() == [
242
245
  Node(name="train", nonce="abcdef", seq=0),
@@ -304,7 +307,7 @@ def test_parallel_processes_publish_without_collisions(
304
307
 
305
308
  second.close()
306
309
  parts = DeltaTable(first.values()).file_uris()
307
- assert len(parts) == 2
310
+ assert len(parts) == 1
308
311
  assert {row["_x_prc_idx"] for row in read_rows(first)} == {0, 1}
309
312
 
310
313
  process_index = 0
@@ -14,6 +14,7 @@ from theseus.cli.configure import configure
14
14
  from theseus.cli.submit import submit
15
15
  from theseus.cli.run import run
16
16
  from theseus.cli.jobs import jobs
17
+ from theseus.cli.cleanup import cleanup
17
18
 
18
19
 
19
20
  app = typer.Typer(name="theseus", no_args_is_help=True)
@@ -22,6 +23,7 @@ app.command()(configure)
22
23
  app.command()(submit)
23
24
  app.command()(run)
24
25
  app.command()(jobs)
26
+ app.command()(cleanup)
25
27
 
26
28
 
27
29
  @app.callback()
@@ -42,7 +44,7 @@ def main(
42
44
  """Run and inspect Theseus workloads."""
43
45
  logger.remove()
44
46
  logger.add(
45
- lambda message: sys.stderr.write(message),
47
+ sys.stderr,
46
48
  level="DEBUG" if verbose else "INFO",
47
49
  format="<green>{time:HH:mm:ss}</green> | <level>{level: <8}</level> | <level>{message}</level>",
48
50
  backtrace=False,
@@ -0,0 +1,58 @@
1
+ """Compact and vacuum a Theseus root's Delta value table."""
2
+
3
+ from pathlib import Path
4
+ from time import sleep
5
+
6
+ from deltalake import DeltaTable
7
+ from loguru import logger
8
+ from rich.console import Console
9
+ from rich.table import Table
10
+ import typer
11
+
12
+ from theseus.store import ObjectStore
13
+
14
+
15
+ def cleanup(
16
+ root: Path = typer.Argument(..., exists=True, file_okay=False),
17
+ expire: bool = typer.Option(
18
+ False,
19
+ "--expire",
20
+ help="Immediately delete obsolete files. Can break active readers and writers; waits 10 seconds before starting.",
21
+ ),
22
+ ) -> None:
23
+ """Compact ROOT/objects/values and vacuum; --expire bypasses retention."""
24
+ values = root / "objects" / "values"
25
+ if expire:
26
+ logger.warning(
27
+ "ALL ACTIVE WRITERS WILL CRASH IN 10 SECONDS, C-c ONCE TO CANCEL"
28
+ )
29
+ logger.info(
30
+ "--expire deletes obsolete and uncommitted files without retention; "
31
+ "active readers and writers may fail."
32
+ )
33
+ sleep(10)
34
+ try:
35
+ logger.info("CLEANUP | compacting {}", values)
36
+ metrics = ObjectStore.compact(root)
37
+ if metrics is None:
38
+ logger.info("CLEANUP | no Delta value table at {}", values)
39
+ return
40
+ logger.info(
41
+ "CLEANUP | vacuuming {} with {}",
42
+ values,
43
+ "zero retention" if expire else "table retention",
44
+ )
45
+ removed = DeltaTable(values).vacuum(
46
+ dry_run=False,
47
+ full=True,
48
+ retention_hours=0 if expire else None,
49
+ enforce_retention_duration=not expire,
50
+ )
51
+ summary = Table("Statistic", "Files", title="Value table cleanup")
52
+ summary.add_row("Compacted inputs", str(metrics["numFilesRemoved"]))
53
+ summary.add_row("Compacted outputs", str(metrics["numFilesAdded"]))
54
+ summary.add_row("Expired files deleted", str(len(removed)))
55
+ Console().print(summary)
56
+ except Exception as error:
57
+ logger.error("CLEANUP | {}", error)
58
+ raise typer.Exit(1) from error
@@ -552,6 +552,13 @@ if grep -Eq \
552
552
  "${NONCE}-${probe_id}-autobatch-${candidate}"
553
553
 
554
554
  echo "[bootstrap] probing training.per_device_batch_size=$candidate"
555
+ # Create the buffer before polling, and keep the writer PID so a
556
+ # fast-failing probe cannot be classified before its output drains.
557
+ : > "$probe_log"
558
+ probe_pipe="$BOOTSTRAP_STAGING/autobatch-${candidate}.fifo"
559
+ mkfifo "$probe_pipe"
560
+ tee "$probe_log" < "$probe_pipe" &
561
+ probe_log_sink_pid=$!
555
562
  set +e
556
563
  # The probe tee keeps a classification buffer; its stdout still
557
564
  # flows through the outer tee into the durable cluster log.
@@ -560,7 +567,7 @@ if grep -Eq \
560
567
  uv run --no-sync python -m theseus.execute.run "$probe_spec" \
561
568
  "training.per_device_batch_size=$candidate" \
562
569
  "${probe_overrides[@]}" \
563
- > >(tee "$probe_log") 2>&1 &
570
+ > "$probe_pipe" 2>&1 &
564
571
  MAIN_CHILD_PID=$!
565
572
  probe_completed=0
566
573
  while kill -0 "$MAIN_CHILD_PID" 2>/dev/null; do
@@ -574,8 +581,15 @@ if grep -Eq \
574
581
  wait "$MAIN_CHILD_PID"
575
582
  probe_exit_code=$?
576
583
  MAIN_CHILD_PID=""
584
+ wait "$probe_log_sink_pid"
585
+ probe_log_exit_code=$?
577
586
  set -e
587
+ rm -f "$probe_pipe"
578
588
 
589
+ if [[ "$probe_log_exit_code" -ne 0 ]]; then
590
+ echo "[bootstrap] ERROR: autobatch log sink failed with exit $probe_log_exit_code"
591
+ exit "$probe_log_exit_code"
592
+ fi
579
593
  if [[ "$probe_exit_code" -eq 0 || "$probe_completed" -eq 1 ]]; then
580
594
  batch_size_override="training.per_device_batch_size=$candidate"
581
595
  break
@@ -25,6 +25,7 @@ from typing import (
25
25
  Mapping,
26
26
  Self,
27
27
  Sequence,
28
+ TypeVar,
28
29
  cast,
29
30
  overload,
30
31
  )
@@ -37,6 +38,7 @@ import pyarrow as pa
37
38
  from deltalake import DeltaTable, write_deltalake
38
39
  from deltalake.exceptions import CommitFailedError, TableNotFoundError
39
40
  from flax import serialization
41
+ from loguru import logger
40
42
 
41
43
  from theseus.base import JobSpec, Node, PyTree
42
44
  from theseus.base.hardware import HardwareResult, local as local_hardware
@@ -46,6 +48,7 @@ _VALUE_BATCH_SIZE = 4096
46
48
  _VALUE_FLUSH_SECONDS = 30.0
47
49
  _DELTA_IO_ATTEMPTS = 3
48
50
  _DELTA_IO_RETRY_SECONDS = 0.25
51
+ _IOResult = TypeVar("_IOResult")
49
52
  _chdb_query = cast(Callable[..., pa.Table], chdb.query)
50
53
  _METADATA_TYPES: dict[str, pa.DataType] = {
51
54
  "_x_blob": pa.string(),
@@ -153,7 +156,7 @@ class ObjectReader:
153
156
  )
154
157
 
155
158
  @staticmethod
156
- def _retry_io(operation: Callable[[], Any]) -> Any:
159
+ def _retry_io(operation: Callable[[], _IOResult]) -> _IOResult:
157
160
  for attempt in range(_DELTA_IO_ATTEMPTS):
158
161
  try:
159
162
  return operation()
@@ -248,10 +251,34 @@ class ObjectStore(ObjectReader):
248
251
  """
249
252
  return cls(local_hardware(str(root_dir), "-"))
250
253
 
254
+ @classmethod
255
+ def compact(cls, root_dir: str | Path) -> dict[str, Any] | None:
256
+ """Compact ROOT/objects/values without starting a writer or vacuuming.
257
+
258
+ Reload the table after concurrent commit conflicts. An absent table
259
+ is a no-op; obsolete files remain available to existing readers.
260
+ """
261
+ values = Path(root_dir) / "objects" / "values"
262
+ for attempt in range(_DELTA_IO_ATTEMPTS):
263
+ try:
264
+ return cast(
265
+ dict[str, Any],
266
+ cls._retry_io(lambda: DeltaTable(values).optimize.compact()),
267
+ )
268
+ except TableNotFoundError:
269
+ return None
270
+ except CommitFailedError:
271
+ if attempt + 1 == _DELTA_IO_ATTEMPTS:
272
+ raise
273
+ sleep(_DELTA_IO_RETRY_SECONDS * 2**attempt)
274
+ raise AssertionError("unreachable")
275
+
251
276
  def close(self) -> None:
252
- """Commit queued values and stop the writer without rewriting files.
277
+ """Commit queued values, stop the writer, and compact the value table.
253
278
 
254
279
  A successfully closed store may be restarted with :meth:`start`.
280
+ Compaction failures are logged without failing committed writes.
281
+ Closing never vacuums files needed by existing readers.
255
282
 
256
283
  Raises:
257
284
  RuntimeError: If the background writer failed while producing a
@@ -265,6 +292,12 @@ class ObjectStore(ObjectReader):
265
292
  self.thread = None
266
293
 
267
294
  self._raise_writer_error()
295
+ try:
296
+ self.compact(self.root().parent)
297
+ except Exception as error:
298
+ logger.warning(
299
+ "STORE | value compaction failed for {}: {}", self.values(), error
300
+ )
268
301
 
269
302
  #### public writes ####
270
303
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes