agilerl 2.8.0.dev0__tar.gz → 2.8.0.dev2__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 (151) hide show
  1. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/.gitignore +4 -4
  2. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/PKG-INFO +17 -7
  3. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/README.md +16 -6
  4. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/cispo.py +3 -6
  5. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/core/base.py +85 -138
  6. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/dpo.py +6 -21
  7. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/grpo.py +6 -35
  8. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/gspo.py +3 -6
  9. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/matd3.py +1 -10
  10. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/neural_ts_bandit.py +15 -4
  11. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/neural_ucb_bandit.py +15 -4
  12. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/ppo.py +5 -1
  13. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/ppo_llm.py +6 -17
  14. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/reinforce_llm.py +6 -17
  15. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/sft.py +7 -11
  16. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/hpo/mutation.py +13 -2
  17. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/models/manifest.py +102 -2
  18. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/models/networks.py +73 -14
  19. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/modules/cnn.py +1 -1
  20. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/modules/dummy.py +5 -1
  21. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/train.py +2 -2
  22. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/training/trainer.py +169 -2
  23. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/utils/algo_utils.py +68 -6
  24. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/utils/evolvable_networks.py +4 -4
  25. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/utils/llm_utils.py +4 -21
  26. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/utils/probe_envs_ma.py +0 -134
  27. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/utils/utils.py +3 -4
  28. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/pyproject.toml +5 -11
  29. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/LICENSE +0 -0
  30. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/__init__.py +0 -0
  31. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/__init__.py +0 -0
  32. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/bc_lm.py +0 -0
  33. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/core/__init__.py +0 -0
  34. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/core/llm_ops/__init__.py +0 -0
  35. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/core/llm_ops/fused_logprobs.py +0 -0
  36. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/core/llm_ops/fused_lora.py +0 -0
  37. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/core/llm_ops/fused_loss.py +0 -0
  38. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/core/llm_ops/vllm_colocate.py +0 -0
  39. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/core/optimizer_wrapper.py +0 -0
  40. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/core/registry.py +0 -0
  41. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/cqn.py +0 -0
  42. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/ddpg.py +0 -0
  43. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/dqn.py +0 -0
  44. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/dqn_rainbow.py +0 -0
  45. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/ilql.py +0 -0
  46. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/ippo.py +0 -0
  47. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/maddpg.py +0 -0
  48. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/algorithms/td3.py +0 -0
  49. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/components/__init__.py +0 -0
  50. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/components/data.py +0 -0
  51. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/components/replay_buffer.py +0 -0
  52. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/components/rollout_buffer.py +0 -0
  53. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/components/sampler.py +0 -0
  54. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/components/segment_tree.py +0 -0
  55. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/data/__init__.py +0 -0
  56. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/data/language_environment.py +0 -0
  57. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/data/rl_data.py +0 -0
  58. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/data/tokenizer.py +0 -0
  59. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/data/torch_datasets.py +0 -0
  60. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/hpo/__init__.py +0 -0
  61. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/hpo/tournament.py +0 -0
  62. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/llm_envs/__init__.py +0 -0
  63. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/llm_envs/base.py +0 -0
  64. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/llm_envs/preference.py +0 -0
  65. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/llm_envs/reasoning.py +0 -0
  66. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/llm_envs/search.py +0 -0
  67. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/llm_envs/sft.py +0 -0
  68. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/llm_envs/sync_vec_env.py +0 -0
  69. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/llm_envs/token_observation.py +0 -0
  70. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/logger.py +0 -0
  71. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/metrics.py +0 -0
  72. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/models/__init__.py +0 -0
  73. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/models/algo.py +0 -0
  74. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/models/algorithms/__init__.py +0 -0
  75. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/models/algorithms/cispo.py +0 -0
  76. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/models/algorithms/cqn.py +0 -0
  77. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/models/algorithms/ddpg.py +0 -0
  78. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/models/algorithms/dpo.py +0 -0
  79. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/models/algorithms/dqn.py +0 -0
  80. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/models/algorithms/grpo.py +0 -0
  81. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/models/algorithms/gspo.py +0 -0
  82. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/models/algorithms/ippo.py +0 -0
  83. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/models/algorithms/llmppo.py +0 -0
  84. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/models/algorithms/llmreinforce.py +0 -0
  85. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/models/algorithms/maddpg.py +0 -0
  86. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/models/algorithms/matd3.py +0 -0
  87. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/models/algorithms/neural_ts.py +0 -0
  88. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/models/algorithms/neural_ucb.py +0 -0
  89. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/models/algorithms/ppo.py +0 -0
  90. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/models/algorithms/rainbow_dqn.py +0 -0
  91. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/models/algorithms/sft.py +0 -0
  92. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/models/algorithms/td3.py +0 -0
  93. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/models/env.py +0 -0
  94. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/models/hpo.py +0 -0
  95. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/models/training.py +0 -0
  96. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/modules/__init__.py +0 -0
  97. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/modules/base.py +0 -0
  98. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/modules/bert.py +0 -0
  99. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/modules/configs.py +0 -0
  100. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/modules/custom_components.py +0 -0
  101. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/modules/gpt.py +0 -0
  102. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/modules/lstm.py +0 -0
  103. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/modules/mlp.py +0 -0
  104. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/modules/multi_input.py +0 -0
  105. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/modules/resnet.py +0 -0
  106. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/modules/simba.py +0 -0
  107. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/networks/__init__.py +0 -0
  108. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/networks/actors.py +0 -0
  109. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/networks/base.py +0 -0
  110. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/networks/custom_modules.py +0 -0
  111. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/networks/distributions.py +0 -0
  112. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/networks/q_networks.py +0 -0
  113. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/networks/value_networks.py +0 -0
  114. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/population.py +0 -0
  115. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/protocols.py +0 -0
  116. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/rollouts/__init__.py +0 -0
  117. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/rollouts/on_policy.py +0 -0
  118. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/training/__init__.py +0 -0
  119. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/training/train_bandits.py +0 -0
  120. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/training/train_llm.py +0 -0
  121. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/training/train_multi_agent_off_policy.py +0 -0
  122. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/training/train_multi_agent_on_policy.py +0 -0
  123. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/training/train_off_policy.py +0 -0
  124. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/training/train_offline.py +0 -0
  125. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/training/train_on_policy.py +0 -0
  126. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/typing.py +0 -0
  127. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/utils/__init__.py +0 -0
  128. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/utils/cache.py +0 -0
  129. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/utils/env_utils.py +0 -0
  130. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/utils/ilql_utils.py +0 -0
  131. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/utils/llm_packing.py +0 -0
  132. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/utils/log_utils.py +0 -0
  133. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/utils/minari_utils.py +0 -0
  134. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/utils/population_utils.py +0 -0
  135. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/utils/ppo_value_head.py +0 -0
  136. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/utils/probe_envs.py +0 -0
  137. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/utils/probe_envs_llm.py +0 -0
  138. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/utils/sampling_utils.py +0 -0
  139. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/utils/torch_utils.py +0 -0
  140. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/utils/trainer_utils.py +0 -0
  141. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/vector/__init__.py +0 -0
  142. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/vector/dummy_vec_env.py +0 -0
  143. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/vector/pz_async_vec_env.py +0 -0
  144. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/vector/pz_vec_env.py +0 -0
  145. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/wrappers/__init__.py +0 -0
  146. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/wrappers/agent.py +0 -0
  147. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/wrappers/learning.py +0 -0
  148. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/wrappers/llm_envs.py +0 -0
  149. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/wrappers/make_evolvable.py +0 -0
  150. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/wrappers/pettingzoo_wrappers.py +0 -0
  151. {agilerl-2.8.0.dev0 → agilerl-2.8.0.dev2}/agilerl/wrappers/utils.py +0 -0
@@ -1,5 +1,5 @@
1
- # Symlink for Pylance/Pyright type checking of namespace package
2
- agilerl/arena
1
+ # Symlink for Pylance/Pyright type checking of namespace package (repo root only)
2
+ /agilerl/arena
3
3
 
4
4
  # Byte-compiled / optimized / DLL files
5
5
  __pycache__/
@@ -162,5 +162,5 @@ wandb/
162
162
  CLAUDE.md
163
163
  AGENTS.md
164
164
 
165
- # Simlink for type-checking
166
- agilerl/arena/
165
+ # Symlink for type-checking (repo root only)
166
+ /agilerl/arena/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agilerl
3
- Version: 2.8.0.dev0
3
+ Version: 2.8.0.dev2
4
4
  Summary: AgileRL is a deep reinforcement learning library focused on improving RL development through RLOps.
5
5
  Author-email: Nick Ustaran-Anderegg <dev@agilerl.com>
6
6
  License-Expression: Apache-2.0
@@ -87,7 +87,7 @@ AgileRL is a Deep Reinforcement Learning library focused on improving developmen
87
87
 
88
88
  This library is initially focused on reducing the time taken for training models and hyperparameter optimization (HPO) by pioneering [evolutionary HPO techniques](https://docs.agilerl.com/en/latest/evo_hyperparam_opt/index.html) for reinforcement learning.<br>
89
89
  Evolutionary HPO has been shown to drastically reduce overall training times by automatically converging on optimal hyperparameters, without requiring numerous training runs.<br>
90
- We are constantly adding more algorithms and features. AgileRL already includes state-of-the-art evolvable [on-policy](https://docs.agilerl.com/en/latest/on_policy/index.html), [off-policy](https://docs.agilerl.com/en/latest/off_policy/index.html), [offline](https://docs.agilerl.com/en/latest/offline_training/index.html), [multi-agent](https://docs.agilerl.com/en/latest/multi_agent_training/index.html) and [contextual multi-armed bandit](https://docs.agilerl.com/en/latest/bandits/index.html) reinforcement learning algorithms with [distributed training](https://docs.agilerl.com/en/latest/distributed_training/index.html).
90
+ We are constantly adding more algorithms and features. AgileRL already includes state-of-the-art evolvable [LLM fine-tuning](https://docs.agilerl.com/en/latest/llm_finetuning/index.html), [on-policy](https://docs.agilerl.com/en/latest/on_policy/index.html), [off-policy](https://docs.agilerl.com/en/latest/off_policy/index.html), [offline](https://docs.agilerl.com/en/latest/offline_training/index.html), [multi-agent](https://docs.agilerl.com/en/latest/multi_agent_training/index.html) and [contextual multi-armed bandit](https://docs.agilerl.com/en/latest/bandits/index.html) reinforcement learning algorithms with [distributed training](https://docs.agilerl.com/en/latest/distributed_training/index.html).
91
91
 
92
92
  <p align="center">
93
93
  <img src=https://user-images.githubusercontent.com/47857277/236407686-21363eb3-ffcf-419f-b019-0be4ddf1ed4a.gif width="100%" max-width="900">
@@ -105,6 +105,16 @@ We are constantly adding more algorithms and features. AgileRL already includes
105
105
 
106
106
  ## Benchmarks
107
107
 
108
+ ### LLM Fine-tuning
109
+
110
+ AgileRL's multi-turn LLM training enables state-of-the-art performance on long-horizon tasks with small models. In the following example, AgileRL's CISPO was benchmarked against ART and TRL on the <a href="https://github.com/axon-rl/gem">GEM</a> Sudoku Hard task. This is a difficult multi-turn problem, which requires a context length of 32k tokens and up to 50 turns per rollout. The sync AgileRL run is a single agent using the AgileRL framework. The async and HPO runs were performed on <a href="https://arena.agilerl.com">Arena</a>, AgileRL's RLOps platform. All runs used the same starting hyperparameters. AgileRL runs were run on A100 40GB nodes, whereas ART and TRL required A100 80GB nodes due to a lack of optimizations. AgileRL runs significantly outperformed those using the ART and TRL frameworks.
111
+
112
+ <p align="center">
113
+ <img src="https://raw.githubusercontent.com/AgileRL/AgileRL/main/docs/_static/multi_turn_llm_benchmarks.png" min-width="100%" width="700">
114
+ </p>
115
+
116
+ ### Classic RL
117
+
108
118
  Reinforcement learning algorithms and libraries are usually benchmarked once the optimal hyperparameters for training are known, but it often takes hundreds or thousands of experiments to discover these. This is unrealistic and does not reflect the true, total time taken for training. What if we could remove the need to conduct all these prior experiments?
109
119
 
110
120
  In the charts below, a single AgileRL run, which automatically tunes hyperparameters, is benchmarked against Optuna's multiple training runs traditionally required for hyperparameter optimization, demonstrating the real time savings possible. Global steps is the sum of every step taken by any agent in the environment, including across an entire population.
@@ -402,10 +412,10 @@ Use the `ArenaClient` to interact with Arena programmatically from scripts or no
402
412
  from agilerl.arena import ArenaClient
403
413
 
404
414
  client = ArenaClient()
405
- client.login()
415
+ client.login() # OAuth2 device-flow authentication
406
416
 
407
- # Register and validate a custom environment
408
- client.validate_environment(source="path/to/my_env.py")
417
+ # Upload and validate a custom environment
418
+ client.validate_environment(name="my-custom-env", source="path/to/my_env.py")
409
419
 
410
420
  # Train on validated custom environment
411
421
  client.submit_experiment(
@@ -423,13 +433,13 @@ The same operations are available from the command line:
423
433
  arena login
424
434
 
425
435
  # Upload and validate
426
- arena env validate --source path/to/my_env.py
436
+ arena env validate my-custom-env --source path/to/my_env.py
427
437
 
428
438
  # Train on validated custom environment
429
439
  arena experiments submit path/to/manifest.yaml --project my-project
430
440
  ```
431
441
 
432
- For the full CLI and Python SDK referenceincluding authentication, environment validation, experiments, and deploymentsee the [Arena Client](https://docs.agilerl.com/en/latest/arena/index.html) documentation.
442
+ For the full CLI and Python SDK reference, including authentication, environment validation, experiments, and deployment, see the [Arena Client](https://docs.agilerl.com/en/latest/arena/index.html) documentation.
433
443
 
434
444
  ## Tutorials
435
445
 
@@ -23,7 +23,7 @@ AgileRL is a Deep Reinforcement Learning library focused on improving developmen
23
23
 
24
24
  This library is initially focused on reducing the time taken for training models and hyperparameter optimization (HPO) by pioneering [evolutionary HPO techniques](https://docs.agilerl.com/en/latest/evo_hyperparam_opt/index.html) for reinforcement learning.<br>
25
25
  Evolutionary HPO has been shown to drastically reduce overall training times by automatically converging on optimal hyperparameters, without requiring numerous training runs.<br>
26
- We are constantly adding more algorithms and features. AgileRL already includes state-of-the-art evolvable [on-policy](https://docs.agilerl.com/en/latest/on_policy/index.html), [off-policy](https://docs.agilerl.com/en/latest/off_policy/index.html), [offline](https://docs.agilerl.com/en/latest/offline_training/index.html), [multi-agent](https://docs.agilerl.com/en/latest/multi_agent_training/index.html) and [contextual multi-armed bandit](https://docs.agilerl.com/en/latest/bandits/index.html) reinforcement learning algorithms with [distributed training](https://docs.agilerl.com/en/latest/distributed_training/index.html).
26
+ We are constantly adding more algorithms and features. AgileRL already includes state-of-the-art evolvable [LLM fine-tuning](https://docs.agilerl.com/en/latest/llm_finetuning/index.html), [on-policy](https://docs.agilerl.com/en/latest/on_policy/index.html), [off-policy](https://docs.agilerl.com/en/latest/off_policy/index.html), [offline](https://docs.agilerl.com/en/latest/offline_training/index.html), [multi-agent](https://docs.agilerl.com/en/latest/multi_agent_training/index.html) and [contextual multi-armed bandit](https://docs.agilerl.com/en/latest/bandits/index.html) reinforcement learning algorithms with [distributed training](https://docs.agilerl.com/en/latest/distributed_training/index.html).
27
27
 
28
28
  <p align="center">
29
29
  <img src=https://user-images.githubusercontent.com/47857277/236407686-21363eb3-ffcf-419f-b019-0be4ddf1ed4a.gif width="100%" max-width="900">
@@ -41,6 +41,16 @@ We are constantly adding more algorithms and features. AgileRL already includes
41
41
 
42
42
  ## Benchmarks
43
43
 
44
+ ### LLM Fine-tuning
45
+
46
+ AgileRL's multi-turn LLM training enables state-of-the-art performance on long-horizon tasks with small models. In the following example, AgileRL's CISPO was benchmarked against ART and TRL on the <a href="https://github.com/axon-rl/gem">GEM</a> Sudoku Hard task. This is a difficult multi-turn problem, which requires a context length of 32k tokens and up to 50 turns per rollout. The sync AgileRL run is a single agent using the AgileRL framework. The async and HPO runs were performed on <a href="https://arena.agilerl.com">Arena</a>, AgileRL's RLOps platform. All runs used the same starting hyperparameters. AgileRL runs were run on A100 40GB nodes, whereas ART and TRL required A100 80GB nodes due to a lack of optimizations. AgileRL runs significantly outperformed those using the ART and TRL frameworks.
47
+
48
+ <p align="center">
49
+ <img src="https://raw.githubusercontent.com/AgileRL/AgileRL/main/docs/_static/multi_turn_llm_benchmarks.png" min-width="100%" width="700">
50
+ </p>
51
+
52
+ ### Classic RL
53
+
44
54
  Reinforcement learning algorithms and libraries are usually benchmarked once the optimal hyperparameters for training are known, but it often takes hundreds or thousands of experiments to discover these. This is unrealistic and does not reflect the true, total time taken for training. What if we could remove the need to conduct all these prior experiments?
45
55
 
46
56
  In the charts below, a single AgileRL run, which automatically tunes hyperparameters, is benchmarked against Optuna's multiple training runs traditionally required for hyperparameter optimization, demonstrating the real time savings possible. Global steps is the sum of every step taken by any agent in the environment, including across an entire population.
@@ -338,10 +348,10 @@ Use the `ArenaClient` to interact with Arena programmatically from scripts or no
338
348
  from agilerl.arena import ArenaClient
339
349
 
340
350
  client = ArenaClient()
341
- client.login()
351
+ client.login() # OAuth2 device-flow authentication
342
352
 
343
- # Register and validate a custom environment
344
- client.validate_environment(source="path/to/my_env.py")
353
+ # Upload and validate a custom environment
354
+ client.validate_environment(name="my-custom-env", source="path/to/my_env.py")
345
355
 
346
356
  # Train on validated custom environment
347
357
  client.submit_experiment(
@@ -359,13 +369,13 @@ The same operations are available from the command line:
359
369
  arena login
360
370
 
361
371
  # Upload and validate
362
- arena env validate --source path/to/my_env.py
372
+ arena env validate my-custom-env --source path/to/my_env.py
363
373
 
364
374
  # Train on validated custom environment
365
375
  arena experiments submit path/to/manifest.yaml --project my-project
366
376
  ```
367
377
 
368
- For the full CLI and Python SDK referenceincluding authentication, environment validation, experiments, and deploymentsee the [Arena Client](https://docs.agilerl.com/en/latest/arena/index.html) documentation.
378
+ For the full CLI and Python SDK reference, including authentication, environment validation, experiments, and deployment, see the [Arena Client](https://docs.agilerl.com/en/latest/arena/index.html) documentation.
369
379
 
370
380
  ## Tutorials
371
381
 
@@ -4,9 +4,11 @@ from __future__ import annotations
4
4
 
5
5
  from typing import Any
6
6
 
7
- from agilerl.algorithms.grpo import GRPO, _signatures_without_loss_type
7
+ from agilerl.algorithms.grpo import GRPO
8
+ from agilerl.utils.algo_utils import inherit_init_signature
8
9
 
9
10
 
11
+ @inherit_init_signature(GRPO, fixed={"loss_type"})
10
12
  class CISPO(GRPO):
11
13
  """CISPO loss variant of :class:`agilerl.algorithms.grpo.GRPO`
12
14
 
@@ -16,8 +18,3 @@ class CISPO(GRPO):
16
18
  def __init__(self, *args: Any, **kwargs: Any) -> None:
17
19
  """Initialize a CISPO agent with fixed ``loss_type``."""
18
20
  super().__init__(*args, loss_type="cispo", **kwargs)
19
-
20
-
21
- _CISPO_CLASS_SIG, _CISPO_INIT_SIG = _signatures_without_loss_type()
22
- CISPO.__signature__ = _CISPO_CLASS_SIG
23
- CISPO.__init__.__signature__ = _CISPO_INIT_SIG
@@ -149,7 +149,6 @@ if TYPE_CHECKING or HAS_LLM_DEPENDENCIES:
149
149
  build_vllm_llm_init_kwargs,
150
150
  build_vllm_rollout_lora_request,
151
151
  create_model_from_name_or_path,
152
- format_colocated_vllm_oom_hint,
153
152
  gather_if_zero3,
154
153
  get_model_name_or_path,
155
154
  get_state_dict,
@@ -164,8 +163,12 @@ if TYPE_CHECKING or HAS_LLM_DEPENDENCIES:
164
163
  if TYPE_CHECKING or HAS_DEEPSPEED:
165
164
  from deepspeed.checkpoint.utils import clone_tensors_for_torch_save
166
165
 
167
- if TYPE_CHECKING or HAS_VLLM:
166
+ if TYPE_CHECKING:
167
+ from vllm import LLM, CompletionOutput, SamplingParams
168
+ elif HAS_VLLM:
168
169
  from vllm import LLM, CompletionOutput, SamplingParams
170
+ else:
171
+ LLM = CompletionOutput = SamplingParams = None
169
172
 
170
173
  __all__ = ["ActionResult", "EvolvableAlgorithm", "MultiAgentRLAlgorithm", "RLAlgorithm"]
171
174
 
@@ -173,8 +176,6 @@ logger = logging.getLogger(__name__)
173
176
 
174
177
  SelfAgentWrapper = TypeVar("SelfAgentWrapper", bound=AgentWrapperProtocol)
175
178
 
176
- logger = logging.getLogger(__name__)
177
-
178
179
 
179
180
  class _RegistryMeta(type):
180
181
  """Metaclass to wrap registry information after algorithm is done
@@ -2239,18 +2240,10 @@ class LLMAlgorithm(EvolvableAlgorithm, ABC):
2239
2240
  :param lora_target_scope: Optional PEFT LoRA path scope for multimodal models
2240
2241
  (e.g. ``"language_model"``). Passed to :func:`adapt_lora_config_for_model`.
2241
2242
  :type lora_target_scope: str | None, optional
2242
- :param fused_logprobs_chunk_rows: Standard (non-Liger) path only. Rows
2243
- (tokens) per ``(chunk_rows, vocab)`` logit tile when computing per-token
2244
- log-probs via the fused-linear-logprob path. Peak logits memory is
2245
- ``O(chunk_rows * vocab)`` regardless of batch/sequence length. ``None``
2246
- (default) auto-tunes to a ~256 MB fp32 tile.
2247
- :type fused_logprobs_chunk_rows: int | None, optional
2248
- :param fused_loss_chunk_rows: Rows per ``(chunk_rows, vocab)`` logit tile in
2249
- the token-level Liger fused policy loss. ``None`` (default) auto-tunes to
2250
- a ~256 MB fp32 logit workspace — the same heuristic as
2251
- ``fused_logprobs_chunk_rows`` on the standard path; pass an int to
2252
- override.
2253
- :type fused_loss_chunk_rows: int | None, optional
2243
+ :param chunk_rows: Primary chunk-size knob for fused logit tiles used by
2244
+ both the standard fused-logprob path and the Liger fused-loss path.
2245
+ ``None`` (default) preserves each path's auto-tuned behavior.
2246
+ :type chunk_rows: int | None, optional
2254
2247
  :param vllm_importance_sampling_correction: When ``True`` (default) and
2255
2248
  ``use_vllm=True``, correct the rollout/trainer log-prob mismatch by
2256
2249
  weighting each training token by ``clamp(exp(trainer - sampling),
@@ -2339,8 +2332,7 @@ class LLMAlgorithm(EvolvableAlgorithm, ABC):
2339
2332
  activation_offload: bool = False,
2340
2333
  use_sequence_packing: bool = False,
2341
2334
  lora_target_scope: str | None = None,
2342
- fused_logprobs_chunk_rows: int | None = None,
2343
- fused_loss_chunk_rows: int | None = None,
2335
+ chunk_rows: int | None = None,
2344
2336
  vllm_importance_sampling_correction: bool = True,
2345
2337
  vllm_importance_sampling_cap: float = 2.0,
2346
2338
  ) -> None:
@@ -2503,10 +2495,10 @@ class LLMAlgorithm(EvolvableAlgorithm, ABC):
2503
2495
  self.wrap = wrap
2504
2496
  self.use_separate_reference_adapter = use_separate_reference_adapter
2505
2497
  self.cast_logprobs_to_fp32 = cast_logprobs_to_fp32
2506
- # Per-chunk row count for the fused-linear-logprob workspace. ``None``
2507
- # uses a vocab-aware ~256 MB-workspace heuristic;
2508
- # set explicitly to trade kernel-launch count vs ``rows * vocab`` peak.
2509
- self.fused_logprobs_chunk_rows = fused_logprobs_chunk_rows
2498
+ if chunk_rows is not None and chunk_rows <= 0:
2499
+ msg = f"chunk_rows must be a positive int or None, got {chunk_rows}."
2500
+ raise ValueError(msg)
2501
+ self.chunk_rows = chunk_rows
2510
2502
  # vLLM sampling-mismatch correction (truncated importance sampling).
2511
2503
  # The rollout is drawn from vLLM but the loss treats the trainer's
2512
2504
  # recomputed ``old_log_probs`` as the behaviour policy; the two differ
@@ -2522,13 +2514,6 @@ class LLMAlgorithm(EvolvableAlgorithm, ABC):
2522
2514
  # Kept on even when use_vllm=False: decoupled rollouts still sample
2523
2515
  # from a separate vLLM engine.
2524
2516
  self._is_correction_liger_warned = False
2525
- if fused_loss_chunk_rows is not None and fused_loss_chunk_rows <= 0:
2526
- msg = (
2527
- f"fused_loss_chunk_rows must be a positive int or None, "
2528
- f"got {fused_loss_chunk_rows}."
2529
- )
2530
- raise ValueError(msg)
2531
- self.fused_loss_chunk_rows = fused_loss_chunk_rows
2532
2517
  # Warn-once flag for the canonical Liger + non-token importance-sampling
2533
2518
  # "not memory-bounded" warning (see :meth:`_warn_liger_non_token_is`).
2534
2519
  self._liger_non_token_warned = False
@@ -2553,6 +2538,7 @@ class LLMAlgorithm(EvolvableAlgorithm, ABC):
2553
2538
  self._vllm_moved = False
2554
2539
  self._vllm_lora_loaded = False
2555
2540
  self._vllm_lora_staging_dir: Path | None = None
2541
+ self._vllm_lora_staging_dir_is_temp = True
2556
2542
  self._vllm_rollout_lora_request: Any | None = None
2557
2543
  # Colocated vLLM (use_vllm=True) runs the rollout engine and the HF
2558
2544
  # trainer in one process. Each holds its OWN base: vLLM cycles its base
@@ -4201,7 +4187,7 @@ class LLMAlgorithm(EvolvableAlgorithm, ABC):
4201
4187
  fused_ids[start:end, 1:],
4202
4188
  temperature=self.temperature,
4203
4189
  cast_to_fp32=self.cast_logprobs_to_fp32,
4204
- _chunk_rows=self.fused_logprobs_chunk_rows,
4190
+ chunk_rows=self.chunk_rows,
4205
4191
  )
4206
4192
  del first
4207
4193
 
@@ -4376,7 +4362,7 @@ class LLMAlgorithm(EvolvableAlgorithm, ABC):
4376
4362
  packed.input_ids[:, 1:],
4377
4363
  temperature=self.temperature,
4378
4364
  cast_to_fp32=self.cast_logprobs_to_fp32,
4379
- _chunk_rows=self.fused_logprobs_chunk_rows,
4365
+ chunk_rows=self.chunk_rows,
4380
4366
  )
4381
4367
  log_probs = unpack_logprobs(packed_lp, packed)
4382
4368
 
@@ -4600,7 +4586,7 @@ class LLMAlgorithm(EvolvableAlgorithm, ABC):
4600
4586
  packed.input_ids[:, 1:],
4601
4587
  temperature=self.temperature,
4602
4588
  cast_to_fp32=self.cast_logprobs_to_fp32,
4603
- _chunk_rows=self.fused_logprobs_chunk_rows,
4589
+ chunk_rows=self.chunk_rows,
4604
4590
  )
4605
4591
  # Map back to the dense (mb, T-1) frame so the loss path is
4606
4592
  # unchanged; cross-segment boundary predictions are dropped.
@@ -4614,7 +4600,7 @@ class LLMAlgorithm(EvolvableAlgorithm, ABC):
4614
4600
  batch_ids[:, 1:],
4615
4601
  temperature=self.temperature,
4616
4602
  cast_to_fp32=self.cast_logprobs_to_fp32,
4617
- _chunk_rows=self.fused_logprobs_chunk_rows,
4603
+ chunk_rows=self.chunk_rows,
4618
4604
  )
4619
4605
 
4620
4606
  first = None
@@ -4696,13 +4682,17 @@ class LLMAlgorithm(EvolvableAlgorithm, ABC):
4696
4682
  def _ensure_vllm_lora_staging_dir(self) -> Path:
4697
4683
  """Resolve (once) the dir the rollout LoRA adapter is exported to.
4698
4684
 
4699
- Honours ``VLLMConfig.lora_staging_dir`` when set e.g. a shared/NFS
4700
- path that a colocated Ray rollout worker reads the adapter from by
4701
- creating it (parents included) and marking it non-temporary so
4702
- ``clean_up`` never deletes it. Otherwise falls back to a process-private
4703
- ``mkdtemp`` that ``clean_up`` removes. Idempotent: both the colocated
4704
- init (``_configure_vllm``) and every adapter sync (``_move_lora_to_vllm``)
4705
- call this, so the same directory is used throughout the agent's life.
4685
+ The staging dir is always process-private: each rank exports its own
4686
+ adapter copy and reads it back locally, so ranks never race on shared
4687
+ files. Honours ``VLLMConfig.lora_staging_dir`` when set e.g. a known
4688
+ path that orchestrated deployments expect the adapter under staging
4689
+ in a ``rank_<process_index>`` subdirectory of that root when
4690
+ distributed. The dir is created (parents included) and marked
4691
+ non-temporary so ``clean_up`` never deletes it. Otherwise falls back
4692
+ to a process-private ``mkdtemp`` that ``clean_up`` removes.
4693
+ Idempotent: both the colocated init (``_configure_vllm``) and every
4694
+ adapter sync (``_move_lora_to_vllm``) call this, so the same directory
4695
+ is used throughout the agent's life.
4706
4696
 
4707
4697
  :return: The resolved staging directory.
4708
4698
  :rtype: pathlib.Path
@@ -4710,8 +4700,11 @@ class LLMAlgorithm(EvolvableAlgorithm, ABC):
4710
4700
  if self._vllm_lora_staging_dir is None:
4711
4701
  configured = getattr(self.vllm_config, "lora_staging_dir", None)
4712
4702
  if configured is not None:
4713
- self._vllm_lora_staging_dir = Path(configured)
4714
- self._vllm_lora_staging_dir.mkdir(parents=True, exist_ok=True)
4703
+ staging_dir = Path(configured)
4704
+ if self.accelerator is not None and self.accelerator.num_processes > 1:
4705
+ staging_dir = staging_dir / f"rank_{self.accelerator.process_index}"
4706
+ staging_dir.mkdir(parents=True, exist_ok=True)
4707
+ self._vllm_lora_staging_dir = staging_dir
4715
4708
  self._vllm_lora_staging_dir_is_temp = False
4716
4709
  else:
4717
4710
  self._vllm_lora_staging_dir = Path(
@@ -4734,12 +4727,7 @@ class LLMAlgorithm(EvolvableAlgorithm, ABC):
4734
4727
  peft_ref = self._get_peft_model_for_vllm_sync()
4735
4728
  peft_ref.set_adapter(self._vllm_rollout_adapter)
4736
4729
 
4737
- # Export to a fixed staging dir + id and refresh the resident rollout
4738
- # slot in place: ``load_inplace`` (2nd sync onward) re-reads the updated
4739
- # weights from disk, and the fixed id avoids per-sync CUDA-graph
4740
- # accumulation that would grow GPU memory across iterations.
4741
4730
  staging_dir = self._ensure_vllm_lora_staging_dir()
4742
- is_main_process = self.accelerator is None or self.accelerator.is_main_process
4743
4731
  with gather_if_zero3(self.zero_stage, list(peft_ref.parameters())):
4744
4732
  if self.lora_config is None:
4745
4733
  msg = "lora_config is required for vLLM LoRA adapter export."
@@ -4749,33 +4737,9 @@ class LLMAlgorithm(EvolvableAlgorithm, ABC):
4749
4737
  staging_dir,
4750
4738
  self._vllm_rollout_adapter,
4751
4739
  target_modules=self.lora_config.target_modules,
4752
- is_main_process=is_main_process,
4753
4740
  )
4754
4741
  if self.accelerator is not None:
4755
4742
  self.accelerator.wait_for_everyone()
4756
- if not adapter_path.is_dir():
4757
- msg = (
4758
- f"PEFT adapter export for {self._vllm_rollout_adapter!r} not found under "
4759
- f"{staging_dir}. Expected {adapter_path} or adapter_config.json in "
4760
- f"{staging_dir}."
4761
- )
4762
- raise FileNotFoundError(msg)
4763
-
4764
- if is_main_process and logger.isEnabledFor(logging.DEBUG):
4765
- # Sum of L2 norms of the trained-from-zero LoRA-B weights; a value
4766
- # that changes across syncs confirms the trainer is exporting
4767
- # updated weights into the rollout adapter. Gated on the standard
4768
- # logging level (the norm reduction costs a GPU sync).
4769
- lora_b_sq = sum(
4770
- float(p.detach().float().pow(2).sum().item())
4771
- for n, p in peft_ref.named_parameters()
4772
- if "lora_B" in n and self._vllm_rollout_adapter in n
4773
- )
4774
- logger.debug(
4775
- "lora-sync: actor lora_B L2=%.6f path=%s",
4776
- lora_b_sq**0.5,
4777
- adapter_path,
4778
- )
4779
4743
 
4780
4744
  # One-shot refresh of the resident slot. ``load_inplace`` forces vLLM to
4781
4745
  # re-read the (updated) adapter weights from disk; required from the second
@@ -4784,11 +4748,19 @@ class LLMAlgorithm(EvolvableAlgorithm, ABC):
4784
4748
  adapter_path,
4785
4749
  load_inplace=self._vllm_lora_loaded,
4786
4750
  )
4787
- loaded = self.llm.llm_engine.add_lora(refresh_request)
4751
+ lora_device = torch.device(self.device)
4752
+ if lora_device.type == "cuda":
4753
+ # Pin the CUDA context to this agent's device: vLLM's LoRA copy
4754
+ # kernels otherwise launch on the process-default device.
4755
+ with torch.cuda.device(lora_device):
4756
+ loaded = self.llm.llm_engine.add_lora(refresh_request)
4757
+ else:
4758
+ loaded = self.llm.llm_engine.add_lora(refresh_request)
4788
4759
  if not loaded:
4789
4760
  msg = (
4790
- f"vLLM failed to load LoRA adapter from {adapter_path}. "
4791
- "Check max_lora_rank / target module names match the trainer."
4761
+ "vLLM failed to load LoRA adapter from "
4762
+ f"{adapter_path}. Check max_lora_rank / target module "
4763
+ "names match the trainer."
4792
4764
  )
4793
4765
  raise RuntimeError(msg)
4794
4766
 
@@ -5077,13 +5049,13 @@ class LLMAlgorithm(EvolvableAlgorithm, ABC):
5077
5049
  logits: torch.Tensor,
5078
5050
  index: torch.Tensor,
5079
5051
  cast_to_fp32: bool = True,
5080
- _chunk_rows: int = 1,
5052
+ chunk_rows: int = 1,
5081
5053
  ) -> torch.Tensor:
5082
5054
  """Calculate log probabilities for previously generated token ids.
5083
5055
 
5084
- Processes ``_chunk_rows`` rows at a time so peak memory stays bounded to
5085
- ``(_chunk_rows, seq_len, vocab_size)`` rather than the full batch, avoiding
5086
- OOM on large-vocabulary models. Default ``_chunk_rows=1`` minimizes the
5056
+ Processes ``chunk_rows`` rows at a time so peak memory stays bounded to
5057
+ ``(chunk_rows, seq_len, vocab_size)`` rather than the full batch, avoiding
5058
+ OOM on large-vocabulary models. Default ``chunk_rows=1`` minimizes the
5087
5059
  fp32 workspace at the cost of more kernel launches; raise to amortize
5088
5060
  launch overhead when memory headroom allows.
5089
5061
 
@@ -5120,12 +5092,12 @@ class LLMAlgorithm(EvolvableAlgorithm, ABC):
5120
5092
  result = target - log_z
5121
5093
  return result.to(orig_dtype) if cast_to_fp32 else result
5122
5094
 
5123
- if B <= _chunk_rows:
5095
+ if B <= chunk_rows:
5124
5096
  return _logprobs_chunk(logits, index)
5125
5097
 
5126
5098
  per_token_logps = []
5127
- for start in range(0, B, _chunk_rows):
5128
- end = min(start + _chunk_rows, B)
5099
+ for start in range(0, B, chunk_rows):
5100
+ end = min(start + chunk_rows, B)
5129
5101
  per_token_logps.append(
5130
5102
  _logprobs_chunk(logits[start:end], index[start:end]),
5131
5103
  )
@@ -5135,10 +5107,9 @@ class LLMAlgorithm(EvolvableAlgorithm, ABC):
5135
5107
  def _resolve_fused_chunk_rows(vocab_size: int, explicit: int | None = None) -> int:
5136
5108
  """Rows per fused ``(chunk_rows, vocab)`` logit tile.
5137
5109
 
5138
- Shared by the fused-linear-logprob (standard) path
5139
- (``fused_logprobs_chunk_rows``) and the Liger fused-loss path
5140
- (``fused_loss_chunk_rows``) so both bound their per-chunk logit
5141
- workspace identically. A positive ``explicit`` overrides; ``None``
5110
+ Shared by the fused-linear-logprob (standard) path and the Liger
5111
+ fused-loss path so both bound their per-chunk logit workspace
5112
+ identically. A positive ``explicit`` overrides; ``None``
5142
5113
  auto-tunes to a ~256 MB fp32 logit workspace (fewer rows at larger
5143
5114
  vocab), clamped to ``[128, 4096]``.
5144
5115
 
@@ -5162,12 +5133,12 @@ class LLMAlgorithm(EvolvableAlgorithm, ABC):
5162
5133
  target_ids: torch.Tensor,
5163
5134
  temperature: float = 1.0,
5164
5135
  cast_to_fp32: bool = True,
5165
- _chunk_rows: int | None = None,
5136
+ chunk_rows: int | None = None,
5166
5137
  ) -> torch.Tensor:
5167
5138
  """Per-token target logprobs without materializing the full ``(B, T, V)``
5168
5139
  logits tensor.
5169
5140
 
5170
- Tiles flat over ``(B*T)`` with workspace bounded to ``(_chunk_rows, V)``
5141
+ Tiles flat over ``(B*T)`` with workspace bounded to ``(chunk_rows, V)``
5171
5142
  per iteration. Counterpart of :meth:`_logprobs_from_logits` for
5172
5143
  callers that hold hidden states and the lm_head separately. **No-grad
5173
5144
  only** — gradients won't flow to ``lm_head_weight`` from this fn. The
@@ -5195,16 +5166,16 @@ class LLMAlgorithm(EvolvableAlgorithm, ABC):
5195
5166
  in fp32 then cast back. Same semantics as
5196
5167
  :meth:`_logprobs_from_logits`.
5197
5168
  :type cast_to_fp32: bool, optional
5198
- :param _chunk_rows: rows of the flattened ``(B*T)`` workspace per
5199
- iteration; trades launch count vs ``_chunk_rows * V`` peak. When
5169
+ :param chunk_rows: rows of the flattened ``(B*T)`` workspace per
5170
+ iteration; trades launch count vs ``chunk_rows * V`` peak. When
5200
5171
  ``None`` (default) it is resolved from the vocab size via
5201
5172
  a ~256 MB fp32 workspace heuristic.
5202
- :type _chunk_rows: int | None, optional
5173
+ :type chunk_rows: int | None, optional
5203
5174
  :return: ``(B, T)`` per-token logprobs in ``hidden.dtype``.
5204
5175
  :rtype: torch.Tensor
5205
5176
  """
5206
- _chunk_rows = LLMAlgorithm._resolve_fused_chunk_rows(
5207
- lm_head_weight.shape[0], _chunk_rows
5177
+ chunk_rows = LLMAlgorithm._resolve_fused_chunk_rows(
5178
+ lm_head_weight.shape[0], chunk_rows
5208
5179
  )
5209
5180
  return fused_linear_logprobs_chunked(
5210
5181
  hidden,
@@ -5213,7 +5184,7 @@ class LLMAlgorithm(EvolvableAlgorithm, ABC):
5213
5184
  target_ids,
5214
5185
  temperature=temperature,
5215
5186
  cast_to_fp32=cast_to_fp32,
5216
- chunk_rows=_chunk_rows,
5187
+ chunk_rows=chunk_rows,
5217
5188
  )
5218
5189
 
5219
5190
  @staticmethod
@@ -5224,7 +5195,7 @@ class LLMAlgorithm(EvolvableAlgorithm, ABC):
5224
5195
  target_ids: torch.Tensor,
5225
5196
  temperature: float = 1.0,
5226
5197
  cast_to_fp32: bool = True,
5227
- _chunk_rows: int | None = None,
5198
+ chunk_rows: int | None = None,
5228
5199
  ) -> torch.Tensor:
5229
5200
  """Gradient-aware version of :meth:`_logprobs_from_hidden_fused`.
5230
5201
 
@@ -5250,15 +5221,15 @@ class LLMAlgorithm(EvolvableAlgorithm, ABC):
5250
5221
  :type temperature: float, optional
5251
5222
  :param cast_to_fp32: run the per-chunk reduction in fp32.
5252
5223
  :type cast_to_fp32: bool, optional
5253
- :param _chunk_rows: rows of the flattened ``(B*T)`` workspace per chunk.
5224
+ :param chunk_rows: rows of the flattened ``(B*T)`` workspace per chunk.
5254
5225
  When ``None`` (default) it is resolved from the vocab size via
5255
5226
  a ~256 MB fp32 workspace heuristic.
5256
- :type _chunk_rows: int | None, optional
5227
+ :type chunk_rows: int | None, optional
5257
5228
  :return: ``(B, T)`` per-token logprobs in ``hidden.dtype``.
5258
5229
  :rtype: torch.Tensor
5259
5230
  """
5260
- _chunk_rows = LLMAlgorithm._resolve_fused_chunk_rows(
5261
- lm_head_weight.shape[0], _chunk_rows
5231
+ chunk_rows = LLMAlgorithm._resolve_fused_chunk_rows(
5232
+ lm_head_weight.shape[0], chunk_rows
5262
5233
  )
5263
5234
  return FusedLinearLogProbsFunction.apply(
5264
5235
  hidden,
@@ -5267,7 +5238,7 @@ class LLMAlgorithm(EvolvableAlgorithm, ABC):
5267
5238
  target_ids,
5268
5239
  temperature,
5269
5240
  cast_to_fp32,
5270
- _chunk_rows,
5241
+ chunk_rows,
5271
5242
  )
5272
5243
 
5273
5244
  def _configure_batch_size_per_process(
@@ -5729,10 +5700,11 @@ class LLMAlgorithm(EvolvableAlgorithm, ABC):
5729
5700
  def _sleep_vllm_after_init(self) -> None:
5730
5701
  """Put the colocated engine to sleep once after construction.
5731
5702
 
5732
- Native ``sleep(level=1)``: vLLM backs its base up to host RAM and frees
5733
- the KV cache; ``wake_up()`` restores the base (dense or bnb 4-bit).
5703
+ Native ``sleep(level=sleep_mode_level)``: vLLM cycles its allocator
5704
+ state based on the configured sleep level; ``wake_up()`` restores the
5705
+ engine allocations.
5734
5706
  """
5735
- self.llm.sleep(level=1)
5707
+ self.llm.sleep(level=self.vllm_config.sleep_mode_level)
5736
5708
  self._vllm_awake = False
5737
5709
  if self.accelerator is None or self.accelerator.is_main_process:
5738
5710
  log_cuda_memory_snapshot("vLLM sleep complete")
@@ -5745,8 +5717,6 @@ class LLMAlgorithm(EvolvableAlgorithm, ABC):
5745
5717
  return
5746
5718
 
5747
5719
  ds_plugin = getattr(self.accelerator.state, "deepspeed_plugin", None)
5748
- if ds_plugin is None:
5749
- return
5750
5720
 
5751
5721
  ds_config = ds_plugin.deepspeed_config
5752
5722
  if "gradient_clipping" not in ds_config:
@@ -5860,15 +5830,16 @@ class LLMAlgorithm(EvolvableAlgorithm, ABC):
5860
5830
 
5861
5831
  def _prepare_vllm_for_training(self) -> None:
5862
5832
  """Prepare vLLM for learning."""
5863
- if self._vllm_awake and (
5864
- self.accelerator is None or self.accelerator.is_main_process
5865
- ):
5833
+ if not self.use_vllm:
5834
+ return
5835
+ # Every rank holds its own colocated engine (external_launcher), so
5836
+ # every rank must sleep it — not just the main process.
5837
+ if self.vllm_config.sleep_mode and self._vllm_awake:
5866
5838
  torch.cuda.empty_cache()
5867
- self.llm.sleep(level=1)
5839
+ self.llm.sleep(level=self.vllm_config.sleep_mode_level)
5868
5840
  self._vllm_awake = False
5869
5841
 
5870
- if self.use_vllm:
5871
- self._vllm_moved = False
5842
+ self._vllm_moved = False
5872
5843
 
5873
5844
  def _prepare_vllm_for_generation(self) -> None:
5874
5845
  if self.use_memory_efficient_params:
@@ -5881,35 +5852,11 @@ class LLMAlgorithm(EvolvableAlgorithm, ABC):
5881
5852
  log_cuda_memory_snapshot(
5882
5853
  "trainer base offloaded to CPU (before vLLM wake)"
5883
5854
  )
5884
- if not self._vllm_awake and (
5885
- self.accelerator is None or self.accelerator.is_main_process
5886
- ):
5855
+ # Every rank holds its own colocated engine, and _sleep_vllm_after_init
5856
+ # slept them all; we wake them all here.
5857
+ if self.vllm_config.sleep_mode and not self._vllm_awake:
5887
5858
  torch.cuda.empty_cache()
5888
- device_index = (
5889
- self.accelerator.local_process_index
5890
- if self.accelerator is not None
5891
- else 0
5892
- )
5893
- try:
5894
- self.llm.wake_up()
5895
- except RuntimeError as err: # pragma: no cover
5896
- err_text = str(err).lower()
5897
- if "out of memory" in err_text or "cuda error" in err_text:
5898
- vcfg = self.vllm_config
5899
- hint = format_colocated_vllm_oom_hint(
5900
- device_index,
5901
- kv_cache_memory_bytes=(
5902
- vcfg.kv_cache_memory_bytes if vcfg is not None else None
5903
- ),
5904
- gpu_memory_utilization=(
5905
- vcfg.gpu_memory_utilization if vcfg is not None else None
5906
- ),
5907
- max_model_len=getattr(self, "max_model_len", None),
5908
- trainer_on_gpu=not self.use_memory_efficient_params,
5909
- )
5910
- msg = f"vLLM wake_up failed (GPU OOM).\n{hint}"
5911
- raise RuntimeError(msg) from err
5912
- raise
5859
+ self.llm.wake_up()
5913
5860
  self._vllm_awake = True
5914
5861
  if self.accelerator is None or self.accelerator.is_main_process:
5915
5862
  log_cuda_memory_snapshot("vLLM base restored on GPU (after wake)")