attacklm 0.6.4__tar.gz → 0.6.6__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.4 → attacklm-0.6.6}/PKG-INFO +1 -1
  2. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/train_template.py +294 -8
  3. attacklm-0.6.6/src/attacklm/__version__.py +1 -0
  4. attacklm-0.6.4/src/attacklm/__version__.py +0 -1
  5. {attacklm-0.6.4 → attacklm-0.6.6}/.github/workflows/release.yml +0 -0
  6. {attacklm-0.6.4 → attacklm-0.6.6}/.gitignore +0 -0
  7. {attacklm-0.6.4 → attacklm-0.6.6}/ATTRIBUTION.md +0 -0
  8. {attacklm-0.6.4 → attacklm-0.6.6}/CHANGELOG.md +0 -0
  9. {attacklm-0.6.4 → attacklm-0.6.6}/CONTRIBUTING.md +0 -0
  10. {attacklm-0.6.4 → attacklm-0.6.6}/EVALUATION.md +0 -0
  11. {attacklm-0.6.4 → attacklm-0.6.6}/LICENSE +0 -0
  12. {attacklm-0.6.4 → attacklm-0.6.6}/MANIFEST.in +0 -0
  13. {attacklm-0.6.4 → attacklm-0.6.6}/Makefile +0 -0
  14. {attacklm-0.6.4 → attacklm-0.6.6}/NOTICE +0 -0
  15. {attacklm-0.6.4 → attacklm-0.6.6}/QA_BEFORE_RELEASES.md +0 -0
  16. {attacklm-0.6.4 → attacklm-0.6.6}/README.md +0 -0
  17. {attacklm-0.6.4 → attacklm-0.6.6}/REPLAY_PLAN.md +0 -0
  18. {attacklm-0.6.4 → attacklm-0.6.6}/data/ATTRIBUTION.md +0 -0
  19. {attacklm-0.6.4 → attacklm-0.6.6}/data/LEGAL.md +0 -0
  20. {attacklm-0.6.4 → attacklm-0.6.6}/data/REMOVAL.md +0 -0
  21. {attacklm-0.6.4 → attacklm-0.6.6}/data/bench/questions.jsonl +0 -0
  22. {attacklm-0.6.4 → attacklm-0.6.6}/data/bench/speed_context.txt +0 -0
  23. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/ATTRIBUTION.md +0 -0
  24. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/GEMINI_RESEARCH_2026-06-11.md +0 -0
  25. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/acquisition_report.json +0 -0
  26. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/manifest.json +0 -0
  27. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/README.md +0 -0
  28. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/_index.json +0 -0
  29. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/atomic-red-team/LICENSE.md +0 -0
  30. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/atomic-red-team/SOURCE.md +0 -0
  31. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/atomic-red-team/base/collection/data.jsonl +0 -0
  32. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/atomic-red-team/base/discovery/data.jsonl +0 -0
  33. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/atomic-red-team/base/execution/data.jsonl +0 -0
  34. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/atomic-red-team/base/exfiltration/data.jsonl +0 -0
  35. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/atomic-red-team/base/persistence/data.jsonl +0 -0
  36. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/attacklm-synthetic/LICENSE.md +0 -0
  37. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/attacklm-synthetic/SOURCE.md +0 -0
  38. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/attacklm-synthetic/orchestrator/data.jsonl +0 -0
  39. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/azure-pyrit/LICENSE.md +0 -0
  40. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/azure-pyrit/SOURCE.md +0 -0
  41. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/cyberark-fuzzyai/LICENSE.md +0 -0
  42. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/cyberark-fuzzyai/SOURCE.md +0 -0
  43. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/elastic-rules/LICENSE.md +0 -0
  44. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/elastic-rules/SOURCE.md +0 -0
  45. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/llm-generated/LICENSE.md +0 -0
  46. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/llm-generated/SOURCE.md +0 -0
  47. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/llm-generated/cloud/attacks/data_llm.jsonl +0 -0
  48. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/llm-generated/ics/attacks/data_llm.jsonl +0 -0
  49. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/llm-generated/social_engineering/phishing/data_llm.jsonl +0 -0
  50. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/llm-generated/wireless/attacks/data_llm.jsonl +0 -0
  51. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/metasploit-framework/LICENSE.md +0 -0
  52. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/metasploit-framework/SOURCE.md +0 -0
  53. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/metasploit-framework/base/collection/data.jsonl +0 -0
  54. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/metasploit-framework/base/credential_access/data.jsonl +0 -0
  55. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/metasploit-framework/base/defense_evasion/data.jsonl +0 -0
  56. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/metasploit-framework/base/discovery/data.jsonl +0 -0
  57. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/metasploit-framework/base/execution/data.jsonl +0 -0
  58. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/metasploit-framework/base/lateral_movement/data.jsonl +0 -0
  59. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/metasploit-framework/base/persistence/data.jsonl +0 -0
  60. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/metasploit-framework/base/privilege_escalation/data.jsonl +0 -0
  61. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/metasploit-framework/tools/metasploit/data.jsonl +0 -0
  62. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/mitre-atlas-arsenal/LICENSE.md +0 -0
  63. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/mitre-atlas-arsenal/SOURCE.md +0 -0
  64. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/mitre-atlas-arsenal/base/discovery/data.jsonl +0 -0
  65. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/mitre-stockpile/LICENSE.md +0 -0
  66. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/mitre-stockpile/SOURCE.md +0 -0
  67. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/mitre-stockpile/base/collection/data.jsonl +0 -0
  68. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/mitre-stockpile/base/discovery/data.jsonl +0 -0
  69. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/mitre-stockpile/base/execution/data.jsonl +0 -0
  70. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/mitre-stockpile/base/exfiltration/data.jsonl +0 -0
  71. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/mitre-stockpile/base/persistence/data.jsonl +0 -0
  72. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/mordor/LICENSE.md +0 -0
  73. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/mordor/SOURCE.md +0 -0
  74. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/nist-ir/LICENSE.md +0 -0
  75. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/nist-ir/SOURCE.md +0 -0
  76. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/nvidia-garak/LICENSE.md +0 -0
  77. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/nvidia-garak/SOURCE.md +0 -0
  78. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/nvidia-garak/ai/jailbreaking/data.jsonl +0 -0
  79. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/promptfoo/LICENSE.md +0 -0
  80. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/promptfoo/SOURCE.md +0 -0
  81. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/promptfoo/ai/prompt-injection/data.jsonl +0 -0
  82. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/promptmap/LICENSE.md +0 -0
  83. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/promptmap/SOURCE.md +0 -0
  84. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/promptmap/ai/prompt-injection/data.jsonl +0 -0
  85. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/replay-general/LICENSE.md +0 -0
  86. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/replay-general/SOURCE.md +0 -0
  87. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/replay-general/base/replay/data_code.jsonl +0 -0
  88. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/replay-general/base/replay/data_conversation.jsonl +0 -0
  89. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/replay-general/base/replay/data_factual.jsonl +0 -0
  90. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/replay-general/base/replay/data_reasoning.jsonl +0 -0
  91. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/sigma-hq/LICENSE.md +0 -0
  92. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/sigma-hq/SOURCE.md +0 -0
  93. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/splunk-content/LICENSE.md +0 -0
  94. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/splunk-content/SOURCE.md +0 -0
  95. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/threathunter-playbook/LICENSE.md +0 -0
  96. {attacklm-0.6.4 → attacklm-0.6.6}/data/datasets/buckets/sources/threathunter-playbook/SOURCE.md +0 -0
  97. {attacklm-0.6.4 → attacklm-0.6.6}/data/golden/prompts.jsonl +0 -0
  98. {attacklm-0.6.4 → attacklm-0.6.6}/data/reference/prompts.jsonl +0 -0
  99. {attacklm-0.6.4 → attacklm-0.6.6}/data/steering/prompts/opsec_aware.txt +0 -0
  100. {attacklm-0.6.4 → attacklm-0.6.6}/data/steering/prompts/opsec_unaware.txt +0 -0
  101. {attacklm-0.6.4 → attacklm-0.6.6}/data/steering/prompts/succinct.txt +0 -0
  102. {attacklm-0.6.4 → attacklm-0.6.6}/data/steering/prompts/verbose.txt +0 -0
  103. {attacklm-0.6.4 → attacklm-0.6.6}/experiments/README.md +0 -0
  104. {attacklm-0.6.4 → attacklm-0.6.6}/experiments/synthetic_dataset_mutator_evol_instruct.py +0 -0
  105. {attacklm-0.6.4 → attacklm-0.6.6}/hf/.gitattributes +0 -0
  106. {attacklm-0.6.4 → attacklm-0.6.6}/hf/README.md +0 -0
  107. {attacklm-0.6.4 → attacklm-0.6.6}/hf/data/manifest.json +0 -0
  108. {attacklm-0.6.4 → attacklm-0.6.6}/hf/dataset_infos.json +0 -0
  109. {attacklm-0.6.4 → attacklm-0.6.6}/hf/scripts/build_hf_dataset.py +0 -0
  110. {attacklm-0.6.4 → attacklm-0.6.6}/hf/scripts/push_to_hf.py +0 -0
  111. {attacklm-0.6.4 → attacklm-0.6.6}/hpo_runs/.gitkeep +0 -0
  112. {attacklm-0.6.4 → attacklm-0.6.6}/hpo_runs/hpo_state.json +0 -0
  113. {attacklm-0.6.4 → attacklm-0.6.6}/hpo_runs/hpo_trial_dataset.jsonl +0 -0
  114. {attacklm-0.6.4 → attacklm-0.6.6}/logs/.gitkeep +0 -0
  115. {attacklm-0.6.4 → attacklm-0.6.6}/notes/BLUE_TEAM_DESIGN_v0.5.0.md +0 -0
  116. {attacklm-0.6.4 → attacklm-0.6.6}/notes/EVAL_DESIGN_v0.4.0.md +0 -0
  117. {attacklm-0.6.4 → attacklm-0.6.6}/notes/STEERING_REVIEW_v0.4.0.md +0 -0
  118. {attacklm-0.6.4 → attacklm-0.6.6}/pipeline.example.yaml +0 -0
  119. {attacklm-0.6.4 → attacklm-0.6.6}/presets/blue-team.json +0 -0
  120. {attacklm-0.6.4 → attacklm-0.6.6}/presets/purple-team.json +0 -0
  121. {attacklm-0.6.4 → attacklm-0.6.6}/presets/red-team.json +0 -0
  122. {attacklm-0.6.4 → attacklm-0.6.6}/pyproject.toml +0 -0
  123. {attacklm-0.6.4 → attacklm-0.6.6}/requirements.txt +0 -0
  124. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/_eval_loader.py +0 -0
  125. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/acquire_cloud_attack_dataset.py +0 -0
  126. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/acquire_ics_dataset.py +0 -0
  127. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/acquire_phishing_dataset.py +0 -0
  128. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/acquire_red_team_tactics.py +0 -0
  129. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/acquire_replay_general.py +0 -0
  130. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/acquire_supply_chain_dataset.py +0 -0
  131. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/acquire_web_attack_dataset.py +0 -0
  132. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/acquire_wireless_dataset.py +0 -0
  133. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/add_attribution.py +0 -0
  134. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/audit_dataset.py +0 -0
  135. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/augment_attribution.py +0 -0
  136. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/balance_buckets.py +0 -0
  137. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/bucket_loader.py +0 -0
  138. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/build.py +0 -0
  139. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/classify_unrouted_modules.py +0 -0
  140. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/clone_repos.sh +0 -0
  141. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/collect_reference.py +0 -0
  142. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/compare_scores.py +0 -0
  143. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/convert_to_gguf.py +0 -0
  144. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/dedupe_and_import_llm.py +0 -0
  145. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/demo.py +0 -0
  146. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/device_utils.py +0 -0
  147. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/domain_bench.py +0 -0
  148. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/eval_retention.py +0 -0
  149. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/extract_ai_tools_to_jsonl.py +0 -0
  150. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/extract_atomic_red_team_to_jsonl.py +0 -0
  151. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/extract_by_tactic.py +0 -0
  152. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/extract_caldera_plugins_to_jsonl.py +0 -0
  153. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/extract_elastic_rules.py +0 -0
  154. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/extract_infection_monkey_to_jsonl.py +0 -0
  155. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/extract_mordor.py +0 -0
  156. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/extract_nist_ir.py +0 -0
  157. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/extract_rta_to_jsonl.py +0 -0
  158. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/extract_sigma_defensive.py +0 -0
  159. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/extract_splunk_content.py +0 -0
  160. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/extract_threathunter_playbook.py +0 -0
  161. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/generate_dataset.py +0 -0
  162. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/generate_dataset_deterministic.py +0 -0
  163. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/generate_hybrid_dataset.py +0 -0
  164. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/generate_metasploit_dataset.py +0 -0
  165. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/generate_orchestrator.py +0 -0
  166. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/generate_prompt_injection.py +0 -0
  167. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/generate_source_layout.py +0 -0
  168. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/generate_synthetic_scarce.py +0 -0
  169. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/golden_vectors.py +0 -0
  170. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/hpo_runner.py +0 -0
  171. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/import_gguf.sh +0 -0
  172. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/import_to_lmstudio.sh +0 -0
  173. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/infer.py +0 -0
  174. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/init_pipeline.py +0 -0
  175. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/llm_generate_wrapper.py +0 -0
  176. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/merge_adapter.py +0 -0
  177. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/migrate_buckets_to_v021.py +0 -0
  178. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/migrate_v015_to_v020.py +0 -0
  179. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/mitre_tactic_lookup.py +0 -0
  180. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/parse_metasploit_to_jsonl.py +0 -0
  181. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/pipeline.py +0 -0
  182. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/rebuild_manifest.py +0 -0
  183. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/register_ollama.py +0 -0
  184. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/reorganize_buckets.py +0 -0
  185. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/replay_mixer.py +0 -0
  186. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/run_all_acquisitions.py +0 -0
  187. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/score_candidates.py +0 -0
  188. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/scrub_bpl_callback.py +0 -0
  189. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/setup_buckets.py +0 -0
  190. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/speed_bench.py +0 -0
  191. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/stamp_and_reorg.py +0 -0
  192. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/steering.py +0 -0
  193. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/train_all.py +0 -0
  194. {attacklm-0.6.4 → attacklm-0.6.6}/scripts/validate_per_category.py +0 -0
  195. {attacklm-0.6.4 → attacklm-0.6.6}/src/attacklm/__init__.py +0 -0
  196. {attacklm-0.6.4 → attacklm-0.6.6}/src/attacklm/_project_root.py +0 -0
  197. {attacklm-0.6.4 → attacklm-0.6.6}/src/attacklm/cli.py +0 -0
  198. {attacklm-0.6.4 → attacklm-0.6.6}/tests/test_balance_buckets.py +0 -0
  199. {attacklm-0.6.4 → attacklm-0.6.6}/tests/test_collect_reference.py +0 -0
  200. {attacklm-0.6.4 → attacklm-0.6.6}/tests/test_compare_scores.py +0 -0
  201. {attacklm-0.6.4 → attacklm-0.6.6}/tests/test_domain_bench.py +0 -0
  202. {attacklm-0.6.4 → attacklm-0.6.6}/tests/test_eval_loader.py +0 -0
  203. {attacklm-0.6.4 → attacklm-0.6.6}/tests/test_eval_retention.py +0 -0
  204. {attacklm-0.6.4 → attacklm-0.6.6}/tests/test_golden_vectors.py +0 -0
  205. {attacklm-0.6.4 → attacklm-0.6.6}/tests/test_init_pipeline.py +0 -0
  206. {attacklm-0.6.4 → attacklm-0.6.6}/tests/test_replay_mixer.py +0 -0
  207. {attacklm-0.6.4 → attacklm-0.6.6}/tests/test_score_candidates.py +0 -0
  208. {attacklm-0.6.4 → attacklm-0.6.6}/tests/test_speed_bench.py +0 -0
  209. {attacklm-0.6.4 → attacklm-0.6.6}/tests/test_steering.py +0 -0
  210. {attacklm-0.6.4 → attacklm-0.6.6}/tests/test_thinking_models.py +0 -0
  211. {attacklm-0.6.4 → attacklm-0.6.6}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: attacklm
3
- Version: 0.6.4
3
+ Version: 0.6.6
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
@@ -194,8 +194,10 @@ Examples:
194
194
  "--gradient-accumulation-steps",
195
195
  type=int,
196
196
  default=1,
197
- help="Gradient accumulation steps — simulate larger batch without extra VRAM. "
198
- "effective_batch = batch_size × grad_accum. (default: 1)",
197
+ help="Gradient accumulation steps — smoother gradients without extra VRAM. "
198
+ "effective_batch = batch_size × grad_accum. "
199
+ "Does NOT help with OOM (can't go below batch_size=1). "
200
+ "(default: 1)",
199
201
  )
200
202
  parser.add_argument(
201
203
  "--optim",
@@ -242,6 +244,34 @@ Examples:
242
244
  "Used by HPO trials to cap run-length at a fixed budget. "
243
245
  "Default: -1 (use --epochs)",
244
246
  )
247
+ parser.add_argument(
248
+ "--auto-tune",
249
+ action="store_true",
250
+ default=False,
251
+ help=(
252
+ "Auto-tune batch_size and max_length for your GPU. Runs a "
253
+ "warmup epoch with increasing max_length to find the "
254
+ "compute-bound sweet spot (where tok/s stops improving). "
255
+ "Saves optimal config to <output>/auto_tune.json for reuse. "
256
+ "On restart, loads from JSON if it exists (skip with --no-auto-tune)."
257
+ ),
258
+ )
259
+ parser.add_argument(
260
+ "--no-auto-tune",
261
+ dest="auto_tune",
262
+ action="store_false",
263
+ help="Disable auto-tuning (default).",
264
+ )
265
+ parser.add_argument(
266
+ "--live-lr",
267
+ action="store_true",
268
+ default=False,
269
+ help=(
270
+ "Live learning rate adjustment: reduce LR by 50% when loss "
271
+ "plateaus (same detection as early stopping). Restarts from "
272
+ "best checkpoint on each reduction. Default: OFF."
273
+ ),
274
+ )
245
275
  parser.add_argument(
246
276
  "--hpo-metrics-csv",
247
277
  type=str,
@@ -1372,6 +1402,231 @@ def _stochastic_round_projections(optimizer, buffers=None):
1372
1402
  group["proj_int4"] = rounded.clamp(-7, 7).to(torch.int8)
1373
1403
 
1374
1404
 
1405
+ def auto_tune_vram(args, model_loading_fn) -> dict:
1406
+ """Find the optimal max_length for this GPU/model combination.
1407
+
1408
+ Strategy: load the model once, then run a few forward passes at
1409
+ increasing max_length values. Measure tok/s at each. Stop when
1410
+ tok/s plateaus (the GPU is compute-bound — longer sequences don't
1411
+ help). Save the result to <output>/auto_tune.json.
1412
+
1413
+ Returns a dict with optimal max_length, batch_size, and tok/s.
1414
+ """
1415
+ import json
1416
+ import time
1417
+ from pathlib import Path
1418
+
1419
+ tune_path = Path(args.output) / "auto_tune.json"
1420
+ if tune_path.exists():
1421
+ print(f" Auto-tune: loading cached config from {tune_path}")
1422
+ with open(tune_path) as f:
1423
+ return json.load(f)
1424
+
1425
+ print("\n [Auto-tune] Probing VRAM to find optimal max_length...")
1426
+ print(" (This runs a few forward passes at different lengths.)")
1427
+
1428
+ # Load model once
1429
+ model, tokenizer = model_loading_fn()
1430
+
1431
+ # Test lengths: start at 1024, double until tok/s stops improving
1432
+ test_lengths = [1024, 2048, 4096, 6144, 8192, 10240, 12288]
1433
+ results = []
1434
+
1435
+ # Create a tiny dummy batch
1436
+ dummy_text = "The quick brown fox jumps over the lazy dog. " * 200
1437
+ dummy_text = dummy_text[: max(test_lengths) * 4] # enough tokens
1438
+
1439
+ for ml in test_lengths:
1440
+ # Check VRAM headroom
1441
+ if is_cuda():
1442
+ free, total = gpu_mem_info_bytes()
1443
+ allocated = gpu_mem_allocated_bytes()
1444
+ # Rough estimate: each doubling of max_length adds ~2GB
1445
+ # for a 3B model. If we're within 3GB of the ceiling, skip.
1446
+ if allocated > 0 and (total - allocated) < 3 * (1024**3):
1447
+ print(f" max_length={ml}: SKIP (VRAM too tight)")
1448
+ continue
1449
+
1450
+ try:
1451
+ tokens = tokenizer(
1452
+ dummy_text,
1453
+ truncation=True,
1454
+ max_length=ml,
1455
+ return_tensors="pt",
1456
+ )
1457
+ if is_cuda():
1458
+ tokens = {k: v.cuda() for k, v in tokens.items()}
1459
+
1460
+ # Warmup
1461
+ for _ in range(3):
1462
+ with torch.no_grad():
1463
+ _ = model(**tokens)
1464
+
1465
+ # Benchmark
1466
+ torch.cuda.synchronize()
1467
+ t0 = time.perf_counter()
1468
+ n_runs = 10
1469
+ for _ in range(n_runs):
1470
+ with torch.no_grad():
1471
+ _ = model(**tokens)
1472
+ torch.cuda.synchronize()
1473
+ elapsed = time.perf_counter() - t0
1474
+
1475
+ tok_per_sec = (ml * n_runs) / elapsed
1476
+ results.append((ml, tok_per_sec))
1477
+ print(f" max_length={ml:5d}: {tok_per_sec:6.0f} tok/s")
1478
+
1479
+ except RuntimeError as e:
1480
+ if "out of memory" in str(e):
1481
+ print(f" max_length={ml}: OOM — stopping search")
1482
+ torch.cuda.empty_cache()
1483
+ break
1484
+ raise
1485
+
1486
+ if not results:
1487
+ print(" Auto-tune: all lengths OOM'd. Using --max-length 1024.")
1488
+ result = {"max_length": 1024, "batch_size": 1, "tok_per_sec": 0}
1489
+ else:
1490
+ # Find the knee: last length where tok/s improved by >5%
1491
+ best = results[0]
1492
+ for i in range(1, len(results)):
1493
+ improvement = (results[i][1] - best[1]) / best[1]
1494
+ if improvement > 0.05:
1495
+ best = results[i]
1496
+ else:
1497
+ break # plateaued
1498
+
1499
+ result = {
1500
+ "max_length": best[0],
1501
+ "batch_size": 1,
1502
+ "tok_per_sec": round(best[1]),
1503
+ }
1504
+ print(
1505
+ f" Auto-tune: optimal max_length={result['max_length']} "
1506
+ f"({result['tok_per_sec']} tok/s)"
1507
+ )
1508
+
1509
+ # Save for reuse
1510
+ tune_path.parent.mkdir(parents=True, exist_ok=True)
1511
+ with open(tune_path, "w") as f:
1512
+ json.dump(result, f, indent=2)
1513
+ print(f" Auto-tune: saved to {tune_path}")
1514
+
1515
+ # Clean up model to free VRAM for actual training
1516
+ del model
1517
+ if is_cuda():
1518
+ torch.cuda.empty_cache()
1519
+
1520
+ return result
1521
+
1522
+
1523
+ class LiveLRCallback(TrainerCallback):
1524
+ """Reduce learning rate when loss plateaus.
1525
+
1526
+ Uses the same detection logic as StepEarlyStoppingCallback:
1527
+ split a window of EMA-smoothed losses into first/second half,
1528
+ compare means. If the trend is flat or rising for N consecutive
1529
+ checks, halve the LR and reload the best checkpoint.
1530
+
1531
+ This is ReduceLROnPlateau but integrated with our existing
1532
+ plateau detection — no separate scheduler needed.
1533
+
1534
+ The ``trainer`` reference must be set after the Trainer is created
1535
+ (before training starts). The callback accesses the optimizer
1536
+ through ``self._trainer.optimizer``.
1537
+ """
1538
+
1539
+ def __init__(
1540
+ self,
1541
+ check_every: int = 5,
1542
+ window_size: int = 20,
1543
+ ema_decay: float = 0.5,
1544
+ min_delta: float = 0.005,
1545
+ max_reductions: int = 3,
1546
+ reduction_factor: float = 0.5,
1547
+ output_dir: str = "",
1548
+ ):
1549
+ from collections import deque
1550
+
1551
+ self.check_every = check_every
1552
+ self.window_size = window_size
1553
+ self.ema_decay = ema_decay
1554
+ self.min_delta = min_delta
1555
+ self.max_reductions = max_reductions
1556
+ self.reduction_factor = reduction_factor
1557
+ self.output_dir = output_dir
1558
+
1559
+ self._ema: float | None = None
1560
+ self._window: deque[float] = deque(maxlen=window_size)
1561
+ self._stale_checks = 0
1562
+ self._max_stale = 3
1563
+ self._log_count = 0
1564
+ self._reductions = 0
1565
+ self._trainer = None # set after Trainer creation
1566
+
1567
+ def on_log(self, args, state, control, model=None, **kwargs):
1568
+ if self._trainer is None:
1569
+ return control
1570
+ logs = kwargs.get("logs", {})
1571
+ loss = logs.get("loss")
1572
+ if loss is None:
1573
+ return control
1574
+
1575
+ # EMA smoothing
1576
+ if self._ema is None:
1577
+ self._ema = float(loss)
1578
+ else:
1579
+ self._ema = self.ema_decay * self._ema + (1 - self.ema_decay) * float(loss)
1580
+ self._window.append(self._ema)
1581
+
1582
+ self._log_count += 1
1583
+ if self._log_count % self.check_every != 0:
1584
+ return control
1585
+ if len(self._window) < self.window_size:
1586
+ return control
1587
+
1588
+ # Split window
1589
+ mid = self.window_size // 2
1590
+ window_list = list(self._window)
1591
+ first_mean = sum(window_list[:mid]) / mid
1592
+ second_mean = sum(window_list[mid:]) / mid
1593
+
1594
+ if second_mean < first_mean - self.min_delta:
1595
+ self._stale_checks = 0 # still improving
1596
+ else:
1597
+ self._stale_checks += 1 # flat or rising
1598
+
1599
+ if self._stale_checks >= self._max_stale:
1600
+ if self._reductions >= self.max_reductions:
1601
+ return control
1602
+
1603
+ self._reductions += 1
1604
+ self._stale_checks = 0
1605
+
1606
+ # Halve LR for all param groups
1607
+ optimizer = self._trainer.optimizer
1608
+ for group in optimizer.param_groups:
1609
+ old_lr = group["lr"]
1610
+ group["lr"] = old_lr * self.reduction_factor
1611
+
1612
+ print(
1613
+ f"\r\n [LiveLR] Loss plateaued "
1614
+ f"(EMA {first_mean:.4f} → {second_mean:.4f}). "
1615
+ f"LR: {old_lr:.2e} → {group['lr']:.2e} "
1616
+ f"(reduction {self._reductions}/{self.max_reductions})"
1617
+ )
1618
+
1619
+ # Reload best checkpoint if available
1620
+ best_dir = Path(self.output_dir) / "checkpoint-best"
1621
+ if best_dir.exists():
1622
+ print(f" [LiveLR] Reloading best checkpoint from {best_dir}")
1623
+ # HF Trainer's _load_from_checkpoint handles this
1624
+ # if we set state.best_model_checkpoint
1625
+ state.best_model_checkpoint = str(best_dir)
1626
+
1627
+ return control
1628
+
1629
+
1375
1630
  def main() -> None:
1376
1631
  console = Console(width=80)
1377
1632
  args = parse_args()
@@ -2296,10 +2551,21 @@ def main() -> None:
2296
2551
  if "CUDA out of memory" in error_msg or "OOM" in error_msg:
2297
2552
  print("\nERROR: GPU ran out of memory during model loading!")
2298
2553
  print(" Suggestions:")
2299
- print(" 1. Reduce batch size: --batch-size 1")
2300
- print(" 2. Reduce sequence length: --max-length 1024")
2554
+ if args.batch_size > 1:
2555
+ print(
2556
+ " 1. Reduce batch size: --batch-size 1"
2557
+ " (currently {})".format(args.batch_size)
2558
+ )
2559
+ print(
2560
+ " 2. Reduce sequence length: --max-length 1024"
2561
+ " (currently {})".format(args.max_length)
2562
+ )
2301
2563
  print(" 3. Use a smaller model (e.g., Qwen2.5-3B-Instruct)")
2302
- print(" 4. Use Google Colab T4 or RunPod A100")
2564
+ print(
2565
+ " 4. Use gradient_accumulation_steps to simulate"
2566
+ " larger batches without VRAM cost"
2567
+ )
2568
+ print(" 5. Try --use-qgalore for INT4 projections (fits 7B on 16GB)")
2303
2569
  else:
2304
2570
  print(f"\nERROR: Failed to load model: {e}")
2305
2571
  sys.exit(1)
@@ -3623,6 +3889,7 @@ def main() -> None:
3623
3889
  GCEpochCallback(),
3624
3890
  _live_progress,
3625
3891
  PauseResumeCallback(interactive_control),
3892
+ *([LiveLRCallback(output_dir=args.output)] if args.live_lr else []),
3626
3893
  *hpo_callbacks,
3627
3894
  ],
3628
3895
  )
@@ -3656,6 +3923,7 @@ def main() -> None:
3656
3923
  GCEpochCallback(),
3657
3924
  _live_progress,
3658
3925
  PauseResumeCallback(interactive_control),
3926
+ *([LiveLRCallback(output_dir=args.output)] if args.live_lr else []),
3659
3927
  *hpo_callbacks,
3660
3928
  ],
3661
3929
  )
@@ -3667,6 +3935,13 @@ def main() -> None:
3667
3935
 
3668
3936
  trainer.remove_callback(PrinterCallback)
3669
3937
 
3938
+ # Wire LiveLRCallback to the trainer so it can access the optimizer
3939
+ if args.live_lr:
3940
+ for cb in trainer.callback_handler.callbacks:
3941
+ if isinstance(cb, LiveLRCallback):
3942
+ cb._trainer = trainer
3943
+ break
3944
+
3670
3945
  # --- OOM fix #3: Clear CUDA cache + Python GC right before training ---
3671
3946
  # Even with expandable_segments, the model+LoRA+optimizer load leaves some
3672
3947
  # fragmentation behind. Forcing a GC + empty_cache here gives the trainer
@@ -3738,10 +4013,21 @@ def main() -> None:
3738
4013
  if "CUDA out of memory" in error_msg or "OOM" in error_msg:
3739
4014
  print("\nERROR: GPU ran out of memory during training!")
3740
4015
  print(" Suggestions:")
3741
- print(" 1. Reduce batch size: --batch-size 1")
3742
- print(" 2. Reduce sequence length: --max-length 1024")
4016
+ if args.batch_size > 1:
4017
+ print(
4018
+ " 1. Reduce batch size: --batch-size 1"
4019
+ " (currently {})".format(args.batch_size)
4020
+ )
4021
+ print(
4022
+ " 2. Reduce sequence length: --max-length 1024"
4023
+ " (currently {})".format(args.max_length)
4024
+ )
3743
4025
  print(" 3. Use a smaller model (e.g., Qwen2.5-3B-Instruct)")
3744
- print(" 4. Use gradient_accumulation_steps to simulate larger batches")
4026
+ print(
4027
+ " 4. Use gradient_accumulation_steps to simulate"
4028
+ " larger batches without VRAM cost"
4029
+ )
4030
+ print(" 5. Try --use-qgalore for INT4 projections (fits 7B on 16GB)")
3745
4031
  else:
3746
4032
  print(f"\nERROR: Training failed: {e}")
3747
4033
  sys.exit(1)
@@ -0,0 +1 @@
1
+ __version__ = "0.6.6"
@@ -1 +0,0 @@
1
- __version__ = "0.6.4"
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