attacklm 0.6.2__tar.gz → 0.6.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 (211) hide show
  1. {attacklm-0.6.2 → attacklm-0.6.4}/PKG-INFO +1 -1
  2. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/device_utils.py +57 -2
  3. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/train_template.py +134 -49
  4. attacklm-0.6.4/src/attacklm/__version__.py +1 -0
  5. attacklm-0.6.2/src/attacklm/__version__.py +0 -1
  6. {attacklm-0.6.2 → attacklm-0.6.4}/.github/workflows/release.yml +0 -0
  7. {attacklm-0.6.2 → attacklm-0.6.4}/.gitignore +0 -0
  8. {attacklm-0.6.2 → attacklm-0.6.4}/ATTRIBUTION.md +0 -0
  9. {attacklm-0.6.2 → attacklm-0.6.4}/CHANGELOG.md +0 -0
  10. {attacklm-0.6.2 → attacklm-0.6.4}/CONTRIBUTING.md +0 -0
  11. {attacklm-0.6.2 → attacklm-0.6.4}/EVALUATION.md +0 -0
  12. {attacklm-0.6.2 → attacklm-0.6.4}/LICENSE +0 -0
  13. {attacklm-0.6.2 → attacklm-0.6.4}/MANIFEST.in +0 -0
  14. {attacklm-0.6.2 → attacklm-0.6.4}/Makefile +0 -0
  15. {attacklm-0.6.2 → attacklm-0.6.4}/NOTICE +0 -0
  16. {attacklm-0.6.2 → attacklm-0.6.4}/QA_BEFORE_RELEASES.md +0 -0
  17. {attacklm-0.6.2 → attacklm-0.6.4}/README.md +0 -0
  18. {attacklm-0.6.2 → attacklm-0.6.4}/REPLAY_PLAN.md +0 -0
  19. {attacklm-0.6.2 → attacklm-0.6.4}/data/ATTRIBUTION.md +0 -0
  20. {attacklm-0.6.2 → attacklm-0.6.4}/data/LEGAL.md +0 -0
  21. {attacklm-0.6.2 → attacklm-0.6.4}/data/REMOVAL.md +0 -0
  22. {attacklm-0.6.2 → attacklm-0.6.4}/data/bench/questions.jsonl +0 -0
  23. {attacklm-0.6.2 → attacklm-0.6.4}/data/bench/speed_context.txt +0 -0
  24. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/ATTRIBUTION.md +0 -0
  25. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/GEMINI_RESEARCH_2026-06-11.md +0 -0
  26. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/acquisition_report.json +0 -0
  27. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/manifest.json +0 -0
  28. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/README.md +0 -0
  29. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/_index.json +0 -0
  30. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/atomic-red-team/LICENSE.md +0 -0
  31. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/atomic-red-team/SOURCE.md +0 -0
  32. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/atomic-red-team/base/collection/data.jsonl +0 -0
  33. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/atomic-red-team/base/discovery/data.jsonl +0 -0
  34. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/atomic-red-team/base/execution/data.jsonl +0 -0
  35. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/atomic-red-team/base/exfiltration/data.jsonl +0 -0
  36. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/atomic-red-team/base/persistence/data.jsonl +0 -0
  37. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/attacklm-synthetic/LICENSE.md +0 -0
  38. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/attacklm-synthetic/SOURCE.md +0 -0
  39. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/attacklm-synthetic/orchestrator/data.jsonl +0 -0
  40. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/azure-pyrit/LICENSE.md +0 -0
  41. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/azure-pyrit/SOURCE.md +0 -0
  42. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/cyberark-fuzzyai/LICENSE.md +0 -0
  43. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/cyberark-fuzzyai/SOURCE.md +0 -0
  44. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/elastic-rules/LICENSE.md +0 -0
  45. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/elastic-rules/SOURCE.md +0 -0
  46. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/llm-generated/LICENSE.md +0 -0
  47. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/llm-generated/SOURCE.md +0 -0
  48. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/llm-generated/cloud/attacks/data_llm.jsonl +0 -0
  49. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/llm-generated/ics/attacks/data_llm.jsonl +0 -0
  50. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/llm-generated/social_engineering/phishing/data_llm.jsonl +0 -0
  51. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/llm-generated/wireless/attacks/data_llm.jsonl +0 -0
  52. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/metasploit-framework/LICENSE.md +0 -0
  53. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/metasploit-framework/SOURCE.md +0 -0
  54. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/metasploit-framework/base/collection/data.jsonl +0 -0
  55. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/metasploit-framework/base/credential_access/data.jsonl +0 -0
  56. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/metasploit-framework/base/defense_evasion/data.jsonl +0 -0
  57. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/metasploit-framework/base/discovery/data.jsonl +0 -0
  58. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/metasploit-framework/base/execution/data.jsonl +0 -0
  59. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/metasploit-framework/base/lateral_movement/data.jsonl +0 -0
  60. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/metasploit-framework/base/persistence/data.jsonl +0 -0
  61. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/metasploit-framework/base/privilege_escalation/data.jsonl +0 -0
  62. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/metasploit-framework/tools/metasploit/data.jsonl +0 -0
  63. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/mitre-atlas-arsenal/LICENSE.md +0 -0
  64. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/mitre-atlas-arsenal/SOURCE.md +0 -0
  65. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/mitre-atlas-arsenal/base/discovery/data.jsonl +0 -0
  66. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/mitre-stockpile/LICENSE.md +0 -0
  67. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/mitre-stockpile/SOURCE.md +0 -0
  68. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/mitre-stockpile/base/collection/data.jsonl +0 -0
  69. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/mitre-stockpile/base/discovery/data.jsonl +0 -0
  70. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/mitre-stockpile/base/execution/data.jsonl +0 -0
  71. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/mitre-stockpile/base/exfiltration/data.jsonl +0 -0
  72. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/mitre-stockpile/base/persistence/data.jsonl +0 -0
  73. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/mordor/LICENSE.md +0 -0
  74. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/mordor/SOURCE.md +0 -0
  75. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/nist-ir/LICENSE.md +0 -0
  76. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/nist-ir/SOURCE.md +0 -0
  77. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/nvidia-garak/LICENSE.md +0 -0
  78. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/nvidia-garak/SOURCE.md +0 -0
  79. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/nvidia-garak/ai/jailbreaking/data.jsonl +0 -0
  80. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/promptfoo/LICENSE.md +0 -0
  81. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/promptfoo/SOURCE.md +0 -0
  82. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/promptfoo/ai/prompt-injection/data.jsonl +0 -0
  83. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/promptmap/LICENSE.md +0 -0
  84. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/promptmap/SOURCE.md +0 -0
  85. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/promptmap/ai/prompt-injection/data.jsonl +0 -0
  86. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/replay-general/LICENSE.md +0 -0
  87. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/replay-general/SOURCE.md +0 -0
  88. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/replay-general/base/replay/data_code.jsonl +0 -0
  89. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/replay-general/base/replay/data_conversation.jsonl +0 -0
  90. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/replay-general/base/replay/data_factual.jsonl +0 -0
  91. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/replay-general/base/replay/data_reasoning.jsonl +0 -0
  92. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/sigma-hq/LICENSE.md +0 -0
  93. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/sigma-hq/SOURCE.md +0 -0
  94. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/splunk-content/LICENSE.md +0 -0
  95. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/splunk-content/SOURCE.md +0 -0
  96. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/threathunter-playbook/LICENSE.md +0 -0
  97. {attacklm-0.6.2 → attacklm-0.6.4}/data/datasets/buckets/sources/threathunter-playbook/SOURCE.md +0 -0
  98. {attacklm-0.6.2 → attacklm-0.6.4}/data/golden/prompts.jsonl +0 -0
  99. {attacklm-0.6.2 → attacklm-0.6.4}/data/reference/prompts.jsonl +0 -0
  100. {attacklm-0.6.2 → attacklm-0.6.4}/data/steering/prompts/opsec_aware.txt +0 -0
  101. {attacklm-0.6.2 → attacklm-0.6.4}/data/steering/prompts/opsec_unaware.txt +0 -0
  102. {attacklm-0.6.2 → attacklm-0.6.4}/data/steering/prompts/succinct.txt +0 -0
  103. {attacklm-0.6.2 → attacklm-0.6.4}/data/steering/prompts/verbose.txt +0 -0
  104. {attacklm-0.6.2 → attacklm-0.6.4}/experiments/README.md +0 -0
  105. {attacklm-0.6.2 → attacklm-0.6.4}/experiments/synthetic_dataset_mutator_evol_instruct.py +0 -0
  106. {attacklm-0.6.2 → attacklm-0.6.4}/hf/.gitattributes +0 -0
  107. {attacklm-0.6.2 → attacklm-0.6.4}/hf/README.md +0 -0
  108. {attacklm-0.6.2 → attacklm-0.6.4}/hf/data/manifest.json +0 -0
  109. {attacklm-0.6.2 → attacklm-0.6.4}/hf/dataset_infos.json +0 -0
  110. {attacklm-0.6.2 → attacklm-0.6.4}/hf/scripts/build_hf_dataset.py +0 -0
  111. {attacklm-0.6.2 → attacklm-0.6.4}/hf/scripts/push_to_hf.py +0 -0
  112. {attacklm-0.6.2 → attacklm-0.6.4}/hpo_runs/.gitkeep +0 -0
  113. {attacklm-0.6.2 → attacklm-0.6.4}/hpo_runs/hpo_state.json +0 -0
  114. {attacklm-0.6.2 → attacklm-0.6.4}/hpo_runs/hpo_trial_dataset.jsonl +0 -0
  115. {attacklm-0.6.2 → attacklm-0.6.4}/logs/.gitkeep +0 -0
  116. {attacklm-0.6.2 → attacklm-0.6.4}/notes/BLUE_TEAM_DESIGN_v0.5.0.md +0 -0
  117. {attacklm-0.6.2 → attacklm-0.6.4}/notes/EVAL_DESIGN_v0.4.0.md +0 -0
  118. {attacklm-0.6.2 → attacklm-0.6.4}/notes/STEERING_REVIEW_v0.4.0.md +0 -0
  119. {attacklm-0.6.2 → attacklm-0.6.4}/pipeline.example.yaml +0 -0
  120. {attacklm-0.6.2 → attacklm-0.6.4}/presets/blue-team.json +0 -0
  121. {attacklm-0.6.2 → attacklm-0.6.4}/presets/purple-team.json +0 -0
  122. {attacklm-0.6.2 → attacklm-0.6.4}/presets/red-team.json +0 -0
  123. {attacklm-0.6.2 → attacklm-0.6.4}/pyproject.toml +0 -0
  124. {attacklm-0.6.2 → attacklm-0.6.4}/requirements.txt +0 -0
  125. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/_eval_loader.py +0 -0
  126. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/acquire_cloud_attack_dataset.py +0 -0
  127. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/acquire_ics_dataset.py +0 -0
  128. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/acquire_phishing_dataset.py +0 -0
  129. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/acquire_red_team_tactics.py +0 -0
  130. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/acquire_replay_general.py +0 -0
  131. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/acquire_supply_chain_dataset.py +0 -0
  132. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/acquire_web_attack_dataset.py +0 -0
  133. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/acquire_wireless_dataset.py +0 -0
  134. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/add_attribution.py +0 -0
  135. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/audit_dataset.py +0 -0
  136. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/augment_attribution.py +0 -0
  137. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/balance_buckets.py +0 -0
  138. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/bucket_loader.py +0 -0
  139. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/build.py +0 -0
  140. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/classify_unrouted_modules.py +0 -0
  141. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/clone_repos.sh +0 -0
  142. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/collect_reference.py +0 -0
  143. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/compare_scores.py +0 -0
  144. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/convert_to_gguf.py +0 -0
  145. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/dedupe_and_import_llm.py +0 -0
  146. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/demo.py +0 -0
  147. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/domain_bench.py +0 -0
  148. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/eval_retention.py +0 -0
  149. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/extract_ai_tools_to_jsonl.py +0 -0
  150. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/extract_atomic_red_team_to_jsonl.py +0 -0
  151. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/extract_by_tactic.py +0 -0
  152. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/extract_caldera_plugins_to_jsonl.py +0 -0
  153. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/extract_elastic_rules.py +0 -0
  154. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/extract_infection_monkey_to_jsonl.py +0 -0
  155. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/extract_mordor.py +0 -0
  156. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/extract_nist_ir.py +0 -0
  157. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/extract_rta_to_jsonl.py +0 -0
  158. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/extract_sigma_defensive.py +0 -0
  159. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/extract_splunk_content.py +0 -0
  160. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/extract_threathunter_playbook.py +0 -0
  161. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/generate_dataset.py +0 -0
  162. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/generate_dataset_deterministic.py +0 -0
  163. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/generate_hybrid_dataset.py +0 -0
  164. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/generate_metasploit_dataset.py +0 -0
  165. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/generate_orchestrator.py +0 -0
  166. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/generate_prompt_injection.py +0 -0
  167. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/generate_source_layout.py +0 -0
  168. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/generate_synthetic_scarce.py +0 -0
  169. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/golden_vectors.py +0 -0
  170. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/hpo_runner.py +0 -0
  171. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/import_gguf.sh +0 -0
  172. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/import_to_lmstudio.sh +0 -0
  173. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/infer.py +0 -0
  174. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/init_pipeline.py +0 -0
  175. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/llm_generate_wrapper.py +0 -0
  176. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/merge_adapter.py +0 -0
  177. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/migrate_buckets_to_v021.py +0 -0
  178. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/migrate_v015_to_v020.py +0 -0
  179. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/mitre_tactic_lookup.py +0 -0
  180. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/parse_metasploit_to_jsonl.py +0 -0
  181. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/pipeline.py +0 -0
  182. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/rebuild_manifest.py +0 -0
  183. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/register_ollama.py +0 -0
  184. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/reorganize_buckets.py +0 -0
  185. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/replay_mixer.py +0 -0
  186. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/run_all_acquisitions.py +0 -0
  187. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/score_candidates.py +0 -0
  188. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/scrub_bpl_callback.py +0 -0
  189. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/setup_buckets.py +0 -0
  190. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/speed_bench.py +0 -0
  191. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/stamp_and_reorg.py +0 -0
  192. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/steering.py +0 -0
  193. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/train_all.py +0 -0
  194. {attacklm-0.6.2 → attacklm-0.6.4}/scripts/validate_per_category.py +0 -0
  195. {attacklm-0.6.2 → attacklm-0.6.4}/src/attacklm/__init__.py +0 -0
  196. {attacklm-0.6.2 → attacklm-0.6.4}/src/attacklm/_project_root.py +0 -0
  197. {attacklm-0.6.2 → attacklm-0.6.4}/src/attacklm/cli.py +0 -0
  198. {attacklm-0.6.2 → attacklm-0.6.4}/tests/test_balance_buckets.py +0 -0
  199. {attacklm-0.6.2 → attacklm-0.6.4}/tests/test_collect_reference.py +0 -0
  200. {attacklm-0.6.2 → attacklm-0.6.4}/tests/test_compare_scores.py +0 -0
  201. {attacklm-0.6.2 → attacklm-0.6.4}/tests/test_domain_bench.py +0 -0
  202. {attacklm-0.6.2 → attacklm-0.6.4}/tests/test_eval_loader.py +0 -0
  203. {attacklm-0.6.2 → attacklm-0.6.4}/tests/test_eval_retention.py +0 -0
  204. {attacklm-0.6.2 → attacklm-0.6.4}/tests/test_golden_vectors.py +0 -0
  205. {attacklm-0.6.2 → attacklm-0.6.4}/tests/test_init_pipeline.py +0 -0
  206. {attacklm-0.6.2 → attacklm-0.6.4}/tests/test_replay_mixer.py +0 -0
  207. {attacklm-0.6.2 → attacklm-0.6.4}/tests/test_score_candidates.py +0 -0
  208. {attacklm-0.6.2 → attacklm-0.6.4}/tests/test_speed_bench.py +0 -0
  209. {attacklm-0.6.2 → attacklm-0.6.4}/tests/test_steering.py +0 -0
  210. {attacklm-0.6.2 → attacklm-0.6.4}/tests/test_thinking_models.py +0 -0
  211. {attacklm-0.6.2 → attacklm-0.6.4}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: attacklm
3
- Version: 0.6.2
3
+ Version: 0.6.4
4
4
  Summary: QLoRA-fine-tuned Qwen2.5-Coder for offensive/defensive security and AI red-teaming
5
5
  Project-URL: Homepage, https://github.com/Veedubin/AttackLM
6
6
  Project-URL: Repository, https://github.com/Veedubin/AttackLM
@@ -103,14 +103,16 @@ def setup_allocator_env() -> None:
103
103
  return
104
104
  os.environ.setdefault(
105
105
  "PYTORCH_CUDA_ALLOC_CONF",
106
- "expandable_segments:True,max_split_size_mb:128",
106
+ "expandable_segments:True,max_split_size_mb:512,"
107
+ "garbage_collection_threshold:0.8",
107
108
  )
108
109
  if is_rocm():
109
110
  # Older ROCm builds read PYTORCH_HIP_ALLOC_CONF instead.
110
111
  # Setting it is harmless on newer builds that ignore it.
111
112
  os.environ.setdefault(
112
113
  "PYTORCH_HIP_ALLOC_CONF",
113
- "expandable_segments:True,max_split_size_mb:128",
114
+ "expandable_segments:True,max_split_size_mb:512,"
115
+ "garbage_collection_threshold:0.8",
114
116
  )
115
117
 
116
118
 
@@ -149,6 +151,59 @@ def gpu_mem_info_bytes() -> Tuple[int, int]:
149
151
  return 0, 0
150
152
 
151
153
 
154
+ def gpu_mem_allocated_bytes() -> int:
155
+ """Bytes currently allocated to tensors (excludes cached pool).
156
+
157
+ This is the number that matters for OOM risk — it counts only
158
+ tensors that are actually in use, not blocks the caching allocator
159
+ is holding for reuse. Compare with gpu_mem_reserved_bytes() to
160
+ see how much is cached vs. active.
161
+ """
162
+ if is_cuda():
163
+ return torch.cuda.memory_allocated()
164
+ return 0
165
+
166
+
167
+ def gpu_mem_reserved_bytes() -> int:
168
+ """Bytes reserved from the CUDA driver (allocated + cached pool)."""
169
+ if is_cuda():
170
+ return torch.cuda.memory_reserved()
171
+ return 0
172
+
173
+
174
+ def gpu_mem_cached_bytes() -> int:
175
+ """Bytes in the allocator cache (reserved - allocated).
176
+
177
+ This is what empty_cache() can release back to the driver.
178
+ """
179
+ if is_cuda():
180
+ return torch.cuda.memory_reserved() - torch.cuda.memory_allocated()
181
+ return 0
182
+
183
+
184
+ def gpu_fragmentation_report() -> dict:
185
+ """Return detailed allocator stats for diagnosing fragmentation.
186
+
187
+ Key metrics:
188
+ inactive_split_bytes — memory that is free but can't be used
189
+ because it's split across segment boundaries (fragmentation).
190
+ num_alloc_retries — how many times the allocator had to flush
191
+ the cache and retry. >0 means fragmentation is hurting.
192
+ """
193
+ if not is_cuda():
194
+ return {}
195
+ stats = torch.cuda.memory_stats()
196
+ return {
197
+ "allocated_mb": stats.get("allocated_bytes.all.current", 0) / (1024**2),
198
+ "reserved_mb": stats.get("reserved_bytes.all.current", 0) / (1024**2),
199
+ "active_mb": stats.get("active_bytes.all.current", 0) / (1024**2),
200
+ "inactive_split_mb": stats.get("inactive_split_bytes.all.current", 0)
201
+ / (1024**2),
202
+ "num_alloc_retries": stats.get("num_alloc_retries", 0),
203
+ "num_ooms": stats.get("num_ooms", 0),
204
+ }
205
+
206
+
152
207
  # ---------------------------------------------------------------------------
153
208
  # Attention implementation picker
154
209
  # ---------------------------------------------------------------------------
@@ -67,6 +67,10 @@ from device_utils import ( # noqa: E402 (import after sys.path tweak)
67
67
  empty_cache_and_sync,
68
68
  gpu_mem_info,
69
69
  gpu_mem_info_bytes,
70
+ gpu_mem_allocated_bytes,
71
+ gpu_mem_reserved_bytes,
72
+ gpu_mem_cached_bytes,
73
+ gpu_fragmentation_report,
70
74
  suggest_attn_implementation,
71
75
  print_hardware_banner,
72
76
  )
@@ -1317,7 +1321,7 @@ def detect_assistant_loss_support(tokenizer, dataset_sample: dict) -> dict:
1317
1321
  return result
1318
1322
 
1319
1323
 
1320
- def _stochastic_round_projections(optimizer):
1324
+ def _stochastic_round_projections(optimizer, buffers=None):
1321
1325
  """Re-quantize projection matrices with stochastic rounding.
1322
1326
 
1323
1327
  Stochastic rounding preserves gradient information that would be
@@ -1327,6 +1331,10 @@ def _stochastic_round_projections(optimizer):
1327
1331
 
1328
1332
  This is called after every optimizer step to maintain INT4 precision
1329
1333
  while preventing quantization drift.
1334
+
1335
+ If ``buffers`` is provided (a dict of pre-allocated tensors), reuses
1336
+ those instead of allocating new temporaries. This eliminates ~168
1337
+ temporary allocations per step for a 24-layer model.
1330
1338
  """
1331
1339
  import torch
1332
1340
 
@@ -1338,16 +1346,30 @@ def _stochastic_round_projections(optimizer):
1338
1346
  continue
1339
1347
  scale = group["proj_scale"]
1340
1348
  with torch.no_grad():
1341
- # Dequantize current INT4 for the forward pass
1342
- proj.copy_(group["proj_int4"].to(proj.dtype) * scale)
1343
- # Stochastic rounding: re-quantize with probability-based rounding
1344
- fp = proj / scale
1345
- floor_val = fp.floor()
1346
- frac = fp - floor_val
1347
- # Random rounding: round up with probability = fractional part
1348
- rand = torch.rand_like(frac)
1349
- rounded = floor_val + (rand < frac).to(fp.dtype)
1350
- group["proj_int4"] = rounded.clamp(-7, 7).to(torch.int8)
1349
+ if buffers is not None:
1350
+ # --- Pre-allocated path: zero temporary allocations ---
1351
+ b = buffers
1352
+ b["dequant"].copy_(group["proj_int4"].to(proj.dtype) * scale)
1353
+ proj.copy_(b["dequant"])
1354
+ torch.div(proj, scale, out=b["fp"])
1355
+ torch.floor(b["fp"], out=b["floor"])
1356
+ torch.sub(b["fp"], b["floor"], out=b["frac"])
1357
+ b["rand"].uniform_() # in-place random, no allocation
1358
+ # lt returns bool; convert to float for addition
1359
+ torch.lt(b["rand"], b["frac"], out=b["rounded_bool"])
1360
+ b["rounded"].copy_(b["rounded_bool"].to(proj.dtype))
1361
+ b["rounded"].add_(b["floor"])
1362
+ b["rounded"].clamp_(-7, 7)
1363
+ group["proj_int4"] = b["rounded"].to(torch.int8)
1364
+ else:
1365
+ # --- Fallback: allocates temporaries (legacy path) ---
1366
+ proj.copy_(group["proj_int4"].to(proj.dtype) * scale)
1367
+ fp = proj / scale
1368
+ floor_val = fp.floor()
1369
+ frac = fp - floor_val
1370
+ rand = torch.rand_like(frac)
1371
+ rounded = floor_val + (rand < frac).to(fp.dtype)
1372
+ group["proj_int4"] = rounded.clamp(-7, 7).to(torch.int8)
1351
1373
 
1352
1374
 
1353
1375
  def main() -> None:
@@ -2701,7 +2723,8 @@ def main() -> None:
2701
2723
  self,
2702
2724
  check_every: int = 5,
2703
2725
  window_size: int = 20,
2704
- ema_decay: float = 0.9,
2726
+ ema_decay: float = 0.5,
2727
+ min_delta: float = 0.005,
2705
2728
  output_dir: str = "",
2706
2729
  ):
2707
2730
  from collections import deque
@@ -2709,6 +2732,7 @@ def main() -> None:
2709
2732
  self.check_every = check_every
2710
2733
  self.window_size = window_size
2711
2734
  self.ema_decay = ema_decay
2735
+ self.min_delta = min_delta
2712
2736
  self.output_dir = output_dir
2713
2737
 
2714
2738
  self._ema: float | None = None
@@ -2772,11 +2796,14 @@ def main() -> None:
2772
2796
  second_mean = sum(window_list[mid:]) / mid
2773
2797
  self.trend_delta = second_mean - first_mean
2774
2798
 
2775
- if second_mean < first_mean:
2799
+ if second_mean < first_mean - self.min_delta:
2776
2800
  self.trend_direction = "↓"
2777
2801
  self._stale_checks = 0
2802
+ elif second_mean > first_mean + self.min_delta:
2803
+ self.trend_direction = "↑"
2804
+ self._stale_checks += 1
2778
2805
  else:
2779
- self.trend_direction = "↑" if second_mean > first_mean else "→"
2806
+ self.trend_direction = "→"
2780
2807
  self._stale_checks += 1
2781
2808
 
2782
2809
  if self._stale_checks >= self._max_stale:
@@ -2908,22 +2935,30 @@ def main() -> None:
2908
2935
  return control
2909
2936
 
2910
2937
  class GCEpochCallback(TrainerCallback):
2911
- """Run gc.collect() + empty_cache() after every eval to defragment VRAM.
2938
+ """Aggressive VRAM management: periodic clears + emergency threshold.
2939
+
2940
+ PyTorch's CUDA caching allocator holds freed blocks in a pool for
2941
+ reuse. ``mem_get_info()`` (the driver-level query) counts those
2942
+ cached blocks as "used", so the old 256MB threshold fired on cache
2943
+ bloat, not genuine OOM risk.
2944
+
2945
+ This callback now uses ``memory_allocated()`` (tensors actually in
2946
+ use) for the emergency threshold, and clears the cache *periodically*
2947
+ (every 50 steps) to prevent the pool from growing large enough to
2948
+ trigger spurious emergencies.
2912
2949
 
2913
- Also monitors VRAM after every optimizer step and triggers an
2914
- emergency cache clear if free memory drops below 2GB this
2915
- catches the case where peak transient allocations during a
2916
- forward pass push us close to the ceiling.
2950
+ Also monitors fragmentation (inactive_split_bytes) and allocation
2951
+ retries if the allocator is struggling to find contiguous blocks,
2952
+ we log a warning.
2917
2953
  """
2918
2954
 
2919
- # OOM fix #10: VRAM threshold for emergency cache clear
2920
- # (in bytes). If free VRAM drops below this after an optimizer
2921
- # step, force a gc.collect + empty_cache. 256MB is the "we're
2922
- # about to OOM" threshold — below this, PyTorch can't allocate
2923
- # a single large tensor. GaLore's per-layer hooks free gradients
2924
- # after each layer's backward pass, so peak memory is much lower
2925
- # than QLoRA and we rarely need to clear.
2926
- EMERGENCY_CLEAR_THRESHOLD_BYTES = 256 * (1024**2)
2955
+ # Fraction of total VRAM that triggers an emergency clear.
2956
+ # 0.92 = 14.3GB on a 15.6GB card, leaving ~1.3GB for transients.
2957
+ EMERGENCY_ALLOCATED_FRACTION = 0.92
2958
+
2959
+ # Clear the cache every N optimizer steps to prevent pool bloat.
2960
+ # Cost: ~1-2ms per clear, negligible vs. step time (~350ms).
2961
+ PERIODIC_CLEAR_EVERY_N_STEPS = 50
2927
2962
 
2928
2963
  def on_evaluate(self, args, state, control, **kwargs):
2929
2964
  import gc
@@ -2932,36 +2967,53 @@ def main() -> None:
2932
2967
  if is_cuda():
2933
2968
  empty_cache_and_sync()
2934
2969
  free_gb, total_gb = gpu_mem_info()
2970
+ allocated_gb = gpu_mem_allocated_bytes() / (1024**3)
2971
+ cached_gb = gpu_mem_cached_bytes() / (1024**3)
2935
2972
  print(
2936
2973
  f"\r\n [GCEpochCallback] Post-eval VRAM: "
2937
- f"{free_gb:.2f}GB free / {total_gb:.2f}GB total"
2974
+ f"{free_gb:.2f}GB free / {total_gb:.2f}GB total "
2975
+ f"(alloc {allocated_gb:.1f}GB, cached {cached_gb:.1f}GB)"
2938
2976
  )
2939
2977
  return control
2940
2978
 
2941
2979
  def on_optimizer_step(self, args, state, control, **kwargs):
2942
- """Emergency VRAM clear when free memory gets dangerously low.
2980
+ """Periodic cache clear + emergency threshold check.
2943
2981
 
2944
- Fires after every optimizer step (per `on_optimizer_step`
2945
- callback event). We DO NOT unconditionally clear cache here
2946
- (that would cost 5-10% throughput). Instead, we only clear
2947
- if free VRAM is below the emergency threshold.
2982
+ Periodic clears prevent the cached pool from growing large
2983
+ enough to trigger the emergency threshold. The emergency
2984
+ check uses ``memory_allocated()`` (tensors in use), not
2985
+ ``mem_get_info()`` (which includes the cached pool).
2948
2986
  """
2949
2987
  if not is_cuda():
2950
2988
  return control
2951
- free_bytes, _total_bytes = gpu_mem_info_bytes()
2952
- if free_bytes < self.EMERGENCY_CLEAR_THRESHOLD_BYTES:
2989
+
2990
+ # --- Periodic clear: prevent cache bloat ---
2991
+ if state.global_step % self.PERIODIC_CLEAR_EVERY_N_STEPS == 0:
2992
+ import gc
2993
+
2994
+ gc.collect()
2995
+ torch.cuda.empty_cache()
2996
+
2997
+ # --- Emergency check: actual OOM risk ---
2998
+ allocated = gpu_mem_allocated_bytes()
2999
+ total = torch.cuda.get_device_properties(0).total_memory
3000
+ if allocated / total > self.EMERGENCY_ALLOCATED_FRACTION:
2953
3001
  import gc
2954
3002
 
2955
3003
  gc.collect()
2956
3004
  torch.cuda.empty_cache()
2957
- # Don't sync here — that defeats the purpose of the
2958
- # threshold check. The next forward pass will sync
2959
- # implicitly if it needs to.
2960
- free_gb = free_bytes / (1024**3)
3005
+ cached_mb = gpu_mem_cached_bytes() / (1024**2)
3006
+ frag = gpu_fragmentation_report()
2961
3007
  print(
2962
- f"\r\n [GCEpochCallback] Step {state.global_step} emergency "
2963
- f"cache clear: {free_gb:.2f}GB free"
3008
+ f"\r\n [GCEpochCallback] Step {state.global_step} "
3009
+ f"EMERGENCY: alloc {allocated / (1024**3):.1f}GB "
3010
+ f"(>{self.EMERGENCY_ALLOCATED_FRACTION:.0%} of "
3011
+ f"{total / (1024**3):.1f}GB), "
3012
+ f"cached {cached_mb:.0f}MB freed, "
3013
+ f"frag {frag.get('inactive_split_mb', 0):.0f}MB, "
3014
+ f"retries {frag.get('num_alloc_retries', 0)}"
2964
3015
  )
3016
+
2965
3017
  return control
2966
3018
 
2967
3019
  class LiveProgressCallback(TrainerCallback):
@@ -3049,14 +3101,20 @@ def main() -> None:
3049
3101
  except (TypeError, ValueError):
3050
3102
  loss_val = 0.0
3051
3103
 
3052
- # --- VRAM (used/total) ---
3104
+ # --- VRAM (allocated + cached / total) ---
3053
3105
  vram_str = ""
3054
3106
  if is_cuda():
3055
3107
  try:
3056
- free_b, total_b = gpu_mem_info_bytes()
3057
- used_gb = (total_b - free_b) / (1024**3)
3058
- total_gb = total_b / (1024**3)
3059
- vram_str = f"VRAM USED {used_gb:.1f}/{total_gb:.1f} GB"
3108
+ allocated_gb = gpu_mem_allocated_bytes() / (1024**3)
3109
+ cached_gb = gpu_mem_cached_bytes() / (1024**3)
3110
+ total_gb = torch.cuda.get_device_properties(0).total_memory / (
3111
+ 1024**3
3112
+ )
3113
+ vram_str = (
3114
+ f"VRAM alloc {allocated_gb:.1f} "
3115
+ f"cache {cached_gb:.1f} "
3116
+ f"/{total_gb:.1f} GB"
3117
+ )
3060
3118
  except Exception:
3061
3119
  pass
3062
3120
 
@@ -3446,9 +3504,33 @@ def main() -> None:
3446
3504
  # Q-GaLore: quantize projection matrices to INT4
3447
3505
  if _use_qgalore:
3448
3506
  self._quantize_projections()
3507
+ # Pre-allocate reusable buffers for stochastic
3508
+ # rounding. Without this, every optimizer step
3509
+ # creates 7 temporary tensors per layer (168
3510
+ # allocs/step for 24 layers), each of which
3511
+ # leaves a cached block in the allocator pool.
3512
+ self._sr_buffers: dict[str, dict] = {}
3513
+ for prefix, opt in self._galore_optimizers.items():
3514
+ for group in opt.param_groups:
3515
+ proj = group.get("projection_matrix")
3516
+ if proj is None or proj.numel() == 0:
3517
+ continue
3518
+ self._sr_buffers[prefix] = {
3519
+ "dequant": torch.empty_like(proj),
3520
+ "fp": torch.empty_like(proj),
3521
+ "floor": torch.empty_like(proj),
3522
+ "frac": torch.empty_like(proj),
3523
+ "rand": torch.empty_like(proj),
3524
+ "rounded_bool": torch.empty(
3525
+ proj.shape,
3526
+ dtype=torch.bool,
3527
+ device=proj.device,
3528
+ ),
3529
+ "rounded": torch.empty_like(proj),
3530
+ }
3449
3531
 
3450
3532
  # Register per-layer hooks
3451
- def _make_hook(opt):
3533
+ def _make_hook(opt, prefix=""):
3452
3534
  def hook(p):
3453
3535
  if p.grad is None:
3454
3536
  return
@@ -3456,14 +3538,17 @@ def main() -> None:
3456
3538
  opt.zero_grad()
3457
3539
  # Q-GaLore: stochastic rounding after step
3458
3540
  if _use_qgalore:
3459
- _stochastic_round_projections(opt)
3541
+ bufs = self._sr_buffers.get(prefix)
3542
+ _stochastic_round_projections(opt, bufs)
3460
3543
 
3461
3544
  return hook
3462
3545
 
3463
3546
  for prefix, opt in self._galore_optimizers.items():
3464
3547
  for group in opt.param_groups:
3465
3548
  for p in group["params"]:
3466
- p.register_post_accumulate_grad_hook(_make_hook(opt))
3549
+ p.register_post_accumulate_grad_hook(
3550
+ _make_hook(opt, prefix)
3551
+ )
3467
3552
 
3468
3553
  def _quantize_projections(self):
3469
3554
  """Quantize GaLore projection matrices to INT4.
@@ -0,0 +1 @@
1
+ __version__ = "0.6.4"
@@ -1 +0,0 @@
1
- __version__ = "0.6.2"
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