attacklm 0.7.0__tar.gz → 0.7.1__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.
- {attacklm-0.7.0 → attacklm-0.7.1}/CHANGELOG.md +24 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/PKG-INFO +25 -3
- {attacklm-0.7.0 → attacklm-0.7.1}/README.md +24 -2
- attacklm-0.7.1/attacklm-gui/README.md +148 -0
- attacklm-0.7.1/attacklm-gui/pyproject.toml +19 -0
- attacklm-0.7.1/attacklm-gui/src/attacklm_gui/__init__.py +3 -0
- attacklm-0.7.1/attacklm-gui/src/attacklm_gui/app.py +58 -0
- attacklm-0.7.1/attacklm-gui/src/attacklm_gui/cli.py +17 -0
- attacklm-0.7.1/attacklm-gui/src/attacklm_gui/presets.py +170 -0
- attacklm-0.7.1/attacklm-gui/src/attacklm_gui/runner.py +223 -0
- attacklm-0.7.1/attacklm-gui/src/attacklm_gui/screens/__init__.py +1 -0
- attacklm-0.7.1/attacklm-gui/src/attacklm_gui/screens/command_forms.py +343 -0
- attacklm-0.7.1/attacklm-gui/src/attacklm_gui/screens/main_menu.py +101 -0
- attacklm-0.7.1/attacklm-gui/src/attacklm_gui/screens/train_form.py +451 -0
- attacklm-0.7.1/attacklm-gui/src/attacklm_gui/screens/train_live.py +328 -0
- attacklm-0.7.1/attacklm-gui/src/attacklm_gui/widgets/__init__.py +1 -0
- attacklm-0.7.1/attacklm-gui/tests/test_gui.py +208 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/hf/README.md +4 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/train_template.py +73 -3
- attacklm-0.7.1/src/attacklm/__version__.py +1 -0
- attacklm-0.7.0/src/attacklm/__version__.py +0 -1
- {attacklm-0.7.0 → attacklm-0.7.1}/.github/workflows/release.yml +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/.gitignore +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/ATTRIBUTION.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/CONTRIBUTING.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/EVALUATION.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/LICENSE +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/MANIFEST.in +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/Makefile +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/NOTICE +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/QA_BEFORE_RELEASES.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/REPLAY_PLAN.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/ATTRIBUTION.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/LEGAL.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/REMOVAL.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/bench/questions.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/bench/speed_context.txt +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/ATTRIBUTION.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/GEMINI_RESEARCH_2026-06-11.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/acquisition_report.json +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/manifest.json +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/README.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/_index.json +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/atomic-red-team/LICENSE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/atomic-red-team/SOURCE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/atomic-red-team/base/collection/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/atomic-red-team/base/discovery/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/atomic-red-team/base/execution/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/atomic-red-team/base/exfiltration/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/atomic-red-team/base/persistence/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/attacklm-synthetic/LICENSE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/attacklm-synthetic/SOURCE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/attacklm-synthetic/orchestrator/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/azure-pyrit/LICENSE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/azure-pyrit/SOURCE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/cyberark-fuzzyai/LICENSE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/cyberark-fuzzyai/SOURCE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/elastic-rules/LICENSE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/elastic-rules/SOURCE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/elastic-rules/defensive/detection_engineering/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/llm-generated/LICENSE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/llm-generated/SOURCE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/llm-generated/cloud/attacks/data_llm.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/llm-generated/ics/attacks/data_llm.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/llm-generated/social_engineering/phishing/data_llm.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/llm-generated/wireless/attacks/data_llm.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/metasploit-framework/LICENSE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/metasploit-framework/SOURCE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/metasploit-framework/base/collection/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/metasploit-framework/base/credential_access/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/metasploit-framework/base/defense_evasion/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/metasploit-framework/base/discovery/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/metasploit-framework/base/execution/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/metasploit-framework/base/lateral_movement/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/metasploit-framework/base/persistence/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/metasploit-framework/base/privilege_escalation/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/metasploit-framework/tools/metasploit/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/mitre-atlas-arsenal/LICENSE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/mitre-atlas-arsenal/SOURCE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/mitre-atlas-arsenal/base/discovery/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/mitre-stockpile/LICENSE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/mitre-stockpile/SOURCE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/mitre-stockpile/base/collection/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/mitre-stockpile/base/discovery/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/mitre-stockpile/base/execution/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/mitre-stockpile/base/exfiltration/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/mitre-stockpile/base/persistence/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/mordor/LICENSE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/mordor/SOURCE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/mordor/defensive/threat_hunting/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/nist-ir/LICENSE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/nist-ir/SOURCE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/nist-ir/defensive/incident_response/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/nvidia-garak/LICENSE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/nvidia-garak/SOURCE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/nvidia-garak/ai/jailbreaking/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/promptfoo/LICENSE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/promptfoo/SOURCE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/promptfoo/ai/prompt-injection/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/promptmap/LICENSE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/promptmap/SOURCE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/promptmap/ai/prompt-injection/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/replay-general/LICENSE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/replay-general/SOURCE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/replay-general/base/replay/data_code.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/replay-general/base/replay/data_conversation.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/replay-general/base/replay/data_factual.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/replay-general/base/replay/data_reasoning.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/sigma-hq/LICENSE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/sigma-hq/SOURCE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/sigma-hq/defensive/detection_engineering/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/splunk-content/LICENSE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/splunk-content/SOURCE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/splunk-content/defensive/detection_engineering/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/threathunter-playbook/LICENSE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/threathunter-playbook/SOURCE.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/datasets/buckets/sources/threathunter-playbook/defensive/threat_hunting/data.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/golden/prompts.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/reference/prompts.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/steering/prompts/opsec_aware.txt +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/steering/prompts/opsec_unaware.txt +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/steering/prompts/succinct.txt +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/data/steering/prompts/verbose.txt +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/experiments/README.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/experiments/synthetic_dataset_mutator_evol_instruct.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/hf/.gitattributes +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/hf/data/manifest.json +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/hf/dataset_infos.json +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/hf/scripts/build_hf_dataset.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/hf/scripts/push_to_hf.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/hpo_runs/.gitkeep +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/hpo_runs/hpo_state.json +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/hpo_runs/hpo_trial_dataset.jsonl +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/logs/.gitkeep +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/notes/BLUE_TEAM_DESIGN_v0.5.0.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/notes/EVAL_DESIGN_v0.4.0.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/notes/STEERING_REVIEW_v0.4.0.md +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/pipeline.example.yaml +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/presets/blue-team.json +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/presets/purple-team.json +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/presets/red-team.json +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/pyproject.toml +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/requirements.txt +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/_eval_loader.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/acquire_cloud_attack_dataset.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/acquire_ics_dataset.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/acquire_phishing_dataset.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/acquire_red_team_tactics.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/acquire_replay_general.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/acquire_supply_chain_dataset.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/acquire_web_attack_dataset.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/acquire_wireless_dataset.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/audit_dataset.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/balance_buckets.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/bucket_loader.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/build.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/clone_repos.sh +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/collect_reference.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/compare_scores.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/convert_to_gguf.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/demo.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/device_utils.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/domain_bench.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/eval_retention.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/extract_ai_tools_to_jsonl.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/extract_atomic_red_team_to_jsonl.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/extract_by_tactic.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/extract_caldera_plugins_to_jsonl.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/extract_elastic_rules.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/extract_infection_monkey_to_jsonl.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/extract_mordor.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/extract_nist_ir.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/extract_rta_to_jsonl.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/extract_sigma_defensive.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/extract_splunk_content.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/extract_threathunter_playbook.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/generate_synthetic_scarce.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/golden_vectors.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/hpo_runner.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/import_gguf.sh +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/import_to_lmstudio.sh +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/infer.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/init_pipeline.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/merge_adapter.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/mitre_tactic_lookup.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/parse_metasploit_to_jsonl.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/pipeline.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/rebuild_manifest.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/register_ollama.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/reorganize_buckets.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/replay_mixer.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/score_candidates.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/setup_buckets.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/speed_bench.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/steering.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/scripts/train_all.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/src/attacklm/__init__.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/src/attacklm/_project_root.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/src/attacklm/cli.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/tests/test_balance_buckets.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/tests/test_collect_reference.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/tests/test_compare_scores.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/tests/test_domain_bench.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/tests/test_eval_loader.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/tests/test_eval_retention.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/tests/test_golden_vectors.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/tests/test_init_pipeline.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/tests/test_replay_mixer.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/tests/test_score_candidates.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/tests/test_speed_bench.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/tests/test_steering.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/tests/test_thinking_models.py +0 -0
- {attacklm-0.7.0 → attacklm-0.7.1}/uv.lock +0 -0
|
@@ -4,6 +4,30 @@ All notable changes to AttackLM are documented in this file. Versions follow [Se
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## [0.7.1] — 2026-06-30 — GUI, VRAM fixes, dataset caching
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **attacklm-gui**: Terminal-based TUI wrapper (Textual) for all CLI commands
|
|
11
|
+
- Form-based training config with 40+ params in 5 tabs (Basic, LoRA, GaLore, Advanced, Hardware)
|
|
12
|
+
- Live training monitor with loss sparkline, VRAM gauge, progress bar, log viewer
|
|
13
|
+
- One-click screens for extract, balance, infer, merge, build, pipeline, init
|
|
14
|
+
- 5 built-in presets (3B Q-GaLore, 3B LoRA, 7B Q-GaLore, 7B QLoRA, etc.)
|
|
15
|
+
- Pause/resume/quit controls during training
|
|
16
|
+
- Works in terminal-only environments (no X11/WSLg required)
|
|
17
|
+
- **Dataset caching**: Tokenized datasets saved to disk after first run, reloaded in ~1s on subsequent runs (keyed by dataset + model + max_length + packing)
|
|
18
|
+
- **config.json fix**: Training runs now preserve HF model architecture fields (model_type, architectures, hidden_size, etc.) for GGUF conversion compatibility
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- **VRAM fragmentation**: Removed `device_map="auto"` for single-GPU training — was splitting model across GPU+CPU, creating meta tensors, and leaving VRAM fragmented. Model now loads directly to GPU.
|
|
22
|
+
- **Spectrum crash on 7B**: Skip meta-device tensors in SNR computation (caused by CPU-offloaded layers from device_map="auto")
|
|
23
|
+
- **VRAM parsing**: Added regex for compact VRAM format (`VRAM X/Y GB (A alloc + C cache)`) in training output parser
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
- Moved attacklm-gui into the main AttackLM repo under `attacklm-gui/`
|
|
27
|
+
- 17 GUI tests passing
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
7
31
|
## [0.7.0] — 2026-06-29 — Defensive data extraction, test fixes, script cleanup
|
|
8
32
|
|
|
9
33
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: attacklm
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.1
|
|
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
|
|
@@ -205,6 +205,27 @@ AttackLM — QLoRA fine-tuning pipeline for a MITRE ATT&CK-grounded security AI
|
|
|
205
205
|
|
|
206
206
|
---
|
|
207
207
|
|
|
208
|
+
## GUI (New in v0.7.1)
|
|
209
|
+
|
|
210
|
+
AttackLM now includes a terminal-based GUI for all commands. No more memorizing 40+ CLI flags.
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
pip install attacklm-gui
|
|
214
|
+
attacklm-gui
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Features:
|
|
218
|
+
- **Training form** with 40+ params in 5 tabs (Basic, LoRA, GaLore, Advanced, Hardware)
|
|
219
|
+
- **Live training monitor** with loss sparkline, VRAM gauge, progress bar, and log viewer
|
|
220
|
+
- **One-click screens** for extract, balance, infer, merge, build, pipeline, and init
|
|
221
|
+
- **5 built-in presets** (3B Q-GaLore, 3B LoRA, 7B Q-GaLore, 7B QLoRA, etc.)
|
|
222
|
+
- **Pause/resume/quit** controls during training
|
|
223
|
+
- Works in **terminal-only** environments — no X11, no browser, no GPU required. Works over SSH and WSL.
|
|
224
|
+
|
|
225
|
+
The CLI still works exactly as before. The GUI is a thin wrapper that constructs and runs CLI commands.
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
208
229
|
## Install
|
|
209
230
|
The recommended way to install the full CUDA training stack:
|
|
210
231
|
|
|
@@ -348,7 +369,8 @@ This layout allows the pipeline to deterministically extract data from upstream
|
|
|
348
369
|
## CLI Reference
|
|
349
370
|
| Command | Description |
|
|
350
371
|
|---------|-------------|
|
|
351
|
-
| `attacklm-
|
|
372
|
+
| `attacklm-gui` | Terminal GUI for all commands (new in v0.7.1) |
|
|
373
|
+
| `attacklm-init` | One-shot init: clone → extract → attribute → buckets |
|
|
352
374
|
| `attacklm-train` | Train a single QLoRA adapter |
|
|
353
375
|
| `attacklm-train-all` | Orchestrated training for all buckets / HPO |
|
|
354
376
|
| `attacklm-balance` | Build a balanced subset of buckets |
|
|
@@ -356,7 +378,7 @@ This layout allows the pipeline to deterministically extract data from upstream
|
|
|
356
378
|
| `attacklm-infer` | Smoke-test inference |
|
|
357
379
|
| `attacklm-merge` | Merge LoRA adapter into base model |
|
|
358
380
|
| `attacklm-gguf` | Convert merged model to GGUF (llama.cpp) |
|
|
359
|
-
| `attacklm-build` | One-shot merge
|
|
381
|
+
| `attacklm-build` | One-shot merge → GGUF → install |
|
|
360
382
|
| `attacklm-demo` | Multi-agent orchestrator demo |
|
|
361
383
|
| `attacklm-extract` | Extract data from cloned repositories |
|
|
362
384
|
| `attacklm-buckets` | Organize data into bucket structure |
|
|
@@ -7,6 +7,27 @@ AttackLM — QLoRA fine-tuning pipeline for a MITRE ATT&CK-grounded security AI
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## GUI (New in v0.7.1)
|
|
11
|
+
|
|
12
|
+
AttackLM now includes a terminal-based GUI for all commands. No more memorizing 40+ CLI flags.
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install attacklm-gui
|
|
16
|
+
attacklm-gui
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Features:
|
|
20
|
+
- **Training form** with 40+ params in 5 tabs (Basic, LoRA, GaLore, Advanced, Hardware)
|
|
21
|
+
- **Live training monitor** with loss sparkline, VRAM gauge, progress bar, and log viewer
|
|
22
|
+
- **One-click screens** for extract, balance, infer, merge, build, pipeline, and init
|
|
23
|
+
- **5 built-in presets** (3B Q-GaLore, 3B LoRA, 7B Q-GaLore, 7B QLoRA, etc.)
|
|
24
|
+
- **Pause/resume/quit** controls during training
|
|
25
|
+
- Works in **terminal-only** environments — no X11, no browser, no GPU required. Works over SSH and WSL.
|
|
26
|
+
|
|
27
|
+
The CLI still works exactly as before. The GUI is a thin wrapper that constructs and runs CLI commands.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
10
31
|
## Install
|
|
11
32
|
The recommended way to install the full CUDA training stack:
|
|
12
33
|
|
|
@@ -150,7 +171,8 @@ This layout allows the pipeline to deterministically extract data from upstream
|
|
|
150
171
|
## CLI Reference
|
|
151
172
|
| Command | Description |
|
|
152
173
|
|---------|-------------|
|
|
153
|
-
| `attacklm-
|
|
174
|
+
| `attacklm-gui` | Terminal GUI for all commands (new in v0.7.1) |
|
|
175
|
+
| `attacklm-init` | One-shot init: clone → extract → attribute → buckets |
|
|
154
176
|
| `attacklm-train` | Train a single QLoRA adapter |
|
|
155
177
|
| `attacklm-train-all` | Orchestrated training for all buckets / HPO |
|
|
156
178
|
| `attacklm-balance` | Build a balanced subset of buckets |
|
|
@@ -158,7 +180,7 @@ This layout allows the pipeline to deterministically extract data from upstream
|
|
|
158
180
|
| `attacklm-infer` | Smoke-test inference |
|
|
159
181
|
| `attacklm-merge` | Merge LoRA adapter into base model |
|
|
160
182
|
| `attacklm-gguf` | Convert merged model to GGUF (llama.cpp) |
|
|
161
|
-
| `attacklm-build` | One-shot merge
|
|
183
|
+
| `attacklm-build` | One-shot merge → GGUF → install |
|
|
162
184
|
| `attacklm-demo` | Multi-agent orchestrator demo |
|
|
163
185
|
| `attacklm-extract` | Extract data from cloned repositories |
|
|
164
186
|
| `attacklm-buckets` | Organize data into bucket structure |
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# AttackLM GUI
|
|
2
|
+
|
|
3
|
+
Terminal-based GUI wrapper for the [AttackLM](https://github.com/Veedubin/AttackLM) CLI training tools. Built with [Textual](https://textual.textualize.io/).
|
|
4
|
+
|
|
5
|
+
## Why?
|
|
6
|
+
|
|
7
|
+
AttackLM has 21 CLI commands and `attacklm-train` alone has 40+ parameters. Remembering all the flags is painful. The GUI provides:
|
|
8
|
+
|
|
9
|
+
- **Form-based parameter input** with tabs, presets, and validation
|
|
10
|
+
- **Live training monitoring** with loss charts, VRAM gauges, and log streaming
|
|
11
|
+
- **One-click commands** for extract, balance, infer, merge, build, and more
|
|
12
|
+
- **Works everywhere** — terminal-only, no X11 required (WSL, SSH, headless)
|
|
13
|
+
|
|
14
|
+
The CLI still works exactly as before. The GUI is a thin wrapper that constructs and runs CLI commands.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install attacklm-gui
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Or from source:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
git clone https://github.com/Veedubin/AttackLM.git
|
|
26
|
+
cd AttackLM-Models/attacklm-gui
|
|
27
|
+
pip install -e .
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Requires `attacklm` (the CLI package) and `textual>=2.0`.
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
attacklm-gui
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Main Menu
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
┌──────────────────────────────────────┐
|
|
42
|
+
│ AttackLM GUI v0.1.0 │
|
|
43
|
+
│ │
|
|
44
|
+
│ 🏋️ Train Model │
|
|
45
|
+
│ 📊 Extract Data │
|
|
46
|
+
│ ⚖️ Balance Dataset │
|
|
47
|
+
│ 🧠 Run Inference │
|
|
48
|
+
│ 🔗 Merge Adapter │
|
|
49
|
+
│ 📦 Build & Install │
|
|
50
|
+
│ 🔧 Pipeline │
|
|
51
|
+
│ 🚀 Init Dataset │
|
|
52
|
+
└──────────────────────────────────────┘
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Training Form
|
|
56
|
+
|
|
57
|
+
The training form organizes 40+ parameters into tabs:
|
|
58
|
+
|
|
59
|
+
| Tab | Parameters |
|
|
60
|
+
|-----|-----------|
|
|
61
|
+
| **Basic** | dataset, output, base model, epochs, batch size, max length |
|
|
62
|
+
| **LoRA** | rank, alpha, dropout, DoRA, RSLoRA, target modules, PiSSA |
|
|
63
|
+
| **GaLore** | Q-GaLore, rank, 32-bit, Spectrum layer freezing |
|
|
64
|
+
| **Advanced** | eval split, early stopping, save steps, gradient accum, optimizer, packing, live LR |
|
|
65
|
+
| **Hardware** | precision (BF16/FP16/FP32), multi-GPU, MoE safe, auto-tune |
|
|
66
|
+
|
|
67
|
+
**Presets** save and load complete configurations. Built-in presets:
|
|
68
|
+
- `3B Q-GaLore Spectrum` — full-parameter training on 16GB GPU
|
|
69
|
+
- `3B Q-GaLore Rank 128` — higher quality variant
|
|
70
|
+
- `3B LoRA Default` — standard QLoRA
|
|
71
|
+
- `7B Q-GaLore` — for 24GB GPUs
|
|
72
|
+
- `7B QLoRA Default` — standard QLoRA for 7B
|
|
73
|
+
|
|
74
|
+
### Live Training Monitor
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
┌─────────────────────────────────────────────────────┐
|
|
78
|
+
│ Training: attacklm-3b-qgalore-spectrum │
|
|
79
|
+
│ Epoch 12/30 | Step 1896 | Elapsed: 1h 23m │
|
|
80
|
+
├──────────────────────┬──────────────────────────────┤
|
|
81
|
+
│ Loss: 1.1143 ▂▃▅▆▇ │ VRAM: 5.9/15.6 GB (62%) │
|
|
82
|
+
│ Eval Loss: 1.176 │ alloc: 5.9 cache: 4.8 │
|
|
83
|
+
│ Trend: ↓ -0.0116 │ ████████░░░░ │
|
|
84
|
+
├──────────────────────┴──────────────────────────────┤
|
|
85
|
+
│ Log Output (scrolling) │
|
|
86
|
+
│ Epoch 12/20 58% | loss 1.1143 | 2,844 tok/s ... │
|
|
87
|
+
├─────────────────────────────────────────────────────┤
|
|
88
|
+
│ [P]ause [S]top at checkpoint [Q]uit │
|
|
89
|
+
└─────────────────────────────────────────────────────┘
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Controls:
|
|
93
|
+
- **Pause/Resume** — SIGSTOP/SIGCONT the training process
|
|
94
|
+
- **Stop at Checkpoint** — graceful stop after current eval
|
|
95
|
+
- **Quit** — kill training immediately
|
|
96
|
+
|
|
97
|
+
### Other Commands
|
|
98
|
+
|
|
99
|
+
| Screen | What it does |
|
|
100
|
+
|--------|-------------|
|
|
101
|
+
| **Extract** | Run data extractors on upstream sources |
|
|
102
|
+
| **Balance** | Balance a dataset with cap size |
|
|
103
|
+
| **Inference** | Run a trained model with a prompt |
|
|
104
|
+
| **Merge** | Merge LoRA adapter into base model |
|
|
105
|
+
| **Build** | Merge → GGUF → install to LM Studio |
|
|
106
|
+
| **Pipeline** | Run a YAML training pipeline |
|
|
107
|
+
| **Init** | One-click dataset initialization |
|
|
108
|
+
|
|
109
|
+
## Keyboard Shortcuts
|
|
110
|
+
|
|
111
|
+
| Key | Action |
|
|
112
|
+
|-----|--------|
|
|
113
|
+
| `q` | Quit |
|
|
114
|
+
| `m` | Return to main menu |
|
|
115
|
+
| `Tab` | Next field |
|
|
116
|
+
| `Enter` | Activate button |
|
|
117
|
+
| `Esc` | Go back |
|
|
118
|
+
|
|
119
|
+
## Architecture
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
attacklm-gui/
|
|
123
|
+
├── src/attacklm_gui/
|
|
124
|
+
│ ├── app.py # Textual App entry point
|
|
125
|
+
│ ├── cli.py # CLI entry point
|
|
126
|
+
│ ├── runner.py # Subprocess manager + output parser
|
|
127
|
+
│ ├── presets.py # Save/load training presets
|
|
128
|
+
│ ├── screens/
|
|
129
|
+
│ │ ├── main_menu.py # Command launcher
|
|
130
|
+
│ │ ├── train_form.py # Training parameter form
|
|
131
|
+
│ │ ├── train_live.py # Live training monitor
|
|
132
|
+
│ │ └── command_forms.py # Other command screens
|
|
133
|
+
│ └── widgets/
|
|
134
|
+
│ └── __init__.py
|
|
135
|
+
└── tests/
|
|
136
|
+
└── test_gui.py
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
The GUI never touches AttackLM's internal logic. It constructs CLI command strings and runs them via `asyncio.create_subprocess_exec`. All training, extraction, and inference logic stays in `scripts/*.py`.
|
|
140
|
+
|
|
141
|
+
## Requirements
|
|
142
|
+
|
|
143
|
+
- Python 3.10+
|
|
144
|
+
- Linux (any flavor) or WSL
|
|
145
|
+
- Terminal with 256-color support
|
|
146
|
+
- AttackLM installed (`pip install attacklm[all]`)
|
|
147
|
+
|
|
148
|
+
No X11, no GPU, no browser required. Works over SSH.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "attacklm-gui"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Terminal GUI wrapper for AttackLM CLI training tools"
|
|
5
|
+
requires-python = ">=3.10"
|
|
6
|
+
dependencies = [
|
|
7
|
+
"textual>=2.0",
|
|
8
|
+
"attacklm",
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
[project.scripts]
|
|
12
|
+
attacklm-gui = "attacklm_gui.cli:main"
|
|
13
|
+
|
|
14
|
+
[build-system]
|
|
15
|
+
requires = ["setuptools>=75"]
|
|
16
|
+
build-backend = "setuptools.build_meta"
|
|
17
|
+
|
|
18
|
+
[tool.setuptools.packages.find]
|
|
19
|
+
where = ["src"]
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Main Textual application for AttackLM GUI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from textual.app import App, ComposeResult
|
|
8
|
+
from textual.binding import Binding
|
|
9
|
+
from textual.widgets import Header, Footer
|
|
10
|
+
|
|
11
|
+
from attacklm_gui.presets import ensure_builtin_presets
|
|
12
|
+
from attacklm_gui.screens.main_menu import MainMenuScreen
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AttackLMApp(App):
|
|
16
|
+
"""Terminal GUI wrapper for AttackLM CLI tools."""
|
|
17
|
+
|
|
18
|
+
TITLE = "AttackLM GUI"
|
|
19
|
+
SUB_TITLE = "v0.1.0"
|
|
20
|
+
CSS_PATH = None # We'll use inline CSS for now
|
|
21
|
+
|
|
22
|
+
BINDINGS = [
|
|
23
|
+
Binding("q", "quit", "Quit", show=True),
|
|
24
|
+
Binding("m", "main_menu", "Main Menu", show=True),
|
|
25
|
+
Binding("?", "help", "Help", show=False),
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
def __init__(self) -> None:
|
|
29
|
+
super().__init__()
|
|
30
|
+
self.attacklm_dir = self._find_attacklm_dir()
|
|
31
|
+
|
|
32
|
+
def _find_attacklm_dir(self) -> Path:
|
|
33
|
+
"""Find the AttackLM project directory."""
|
|
34
|
+
# Try common locations
|
|
35
|
+
candidates = [
|
|
36
|
+
Path.cwd(),
|
|
37
|
+
Path.home() / "Projects" / "reverse_engineering" / "AttackLM",
|
|
38
|
+
Path.home() / "AttackLM",
|
|
39
|
+
]
|
|
40
|
+
for c in candidates:
|
|
41
|
+
if (c / "pyproject.toml").exists() and (
|
|
42
|
+
c / "scripts" / "train_template.py"
|
|
43
|
+
).exists():
|
|
44
|
+
return c
|
|
45
|
+
return Path.cwd()
|
|
46
|
+
|
|
47
|
+
def compose(self) -> ComposeResult:
|
|
48
|
+
yield Header()
|
|
49
|
+
yield Footer()
|
|
50
|
+
|
|
51
|
+
def on_mount(self) -> None:
|
|
52
|
+
ensure_builtin_presets()
|
|
53
|
+
self.push_screen(MainMenuScreen())
|
|
54
|
+
|
|
55
|
+
def action_main_menu(self) -> None:
|
|
56
|
+
"""Return to main menu."""
|
|
57
|
+
while len(self.screen_stack) > 1:
|
|
58
|
+
self.pop_screen()
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""CLI entry point for attacklm-gui."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def main() -> int:
|
|
8
|
+
"""Launch the AttackLM TUI."""
|
|
9
|
+
from attacklm_gui.app import AttackLMApp
|
|
10
|
+
|
|
11
|
+
app = AttackLMApp()
|
|
12
|
+
app.run()
|
|
13
|
+
return 0
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
if __name__ == "__main__":
|
|
17
|
+
sys.exit(main())
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"""Configuration preset management for AttackLM training parameters."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
PRESETS_DIR = Path.home() / ".config" / "attacklm" / "presets"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class Preset:
|
|
16
|
+
"""A saved training configuration."""
|
|
17
|
+
|
|
18
|
+
name: str
|
|
19
|
+
params: dict[str, Any] = field(default_factory=dict)
|
|
20
|
+
description: str = ""
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def filename(self) -> str:
|
|
24
|
+
return f"{self.name.lower().replace(' ', '_')}.json"
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def path(self) -> Path:
|
|
28
|
+
return PRESETS_DIR / self.filename
|
|
29
|
+
|
|
30
|
+
def save(self) -> None:
|
|
31
|
+
"""Save preset to disk."""
|
|
32
|
+
PRESETS_DIR.mkdir(parents=True, exist_ok=True)
|
|
33
|
+
data = {
|
|
34
|
+
"name": self.name,
|
|
35
|
+
"description": self.description,
|
|
36
|
+
"params": self.params,
|
|
37
|
+
}
|
|
38
|
+
with open(self.path, "w") as f:
|
|
39
|
+
json.dump(data, f, indent=2)
|
|
40
|
+
|
|
41
|
+
@classmethod
|
|
42
|
+
def load(cls, name: str) -> Preset | None:
|
|
43
|
+
"""Load a preset by name."""
|
|
44
|
+
filename = f"{name.lower().replace(' ', '_')}.json"
|
|
45
|
+
path = PRESETS_DIR / filename
|
|
46
|
+
if not path.exists():
|
|
47
|
+
return None
|
|
48
|
+
with open(path) as f:
|
|
49
|
+
data = json.load(f)
|
|
50
|
+
return cls(
|
|
51
|
+
name=data["name"],
|
|
52
|
+
params=data.get("params", {}),
|
|
53
|
+
description=data.get("description", ""),
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
@classmethod
|
|
57
|
+
def list_all(cls) -> list[str]:
|
|
58
|
+
"""List all saved preset names."""
|
|
59
|
+
PRESETS_DIR.mkdir(parents=True, exist_ok=True)
|
|
60
|
+
presets = []
|
|
61
|
+
for p in sorted(PRESETS_DIR.glob("*.json")):
|
|
62
|
+
try:
|
|
63
|
+
with open(p) as f:
|
|
64
|
+
data = json.load(f)
|
|
65
|
+
presets.append(data.get("name", p.stem))
|
|
66
|
+
except (json.JSONDecodeError, OSError):
|
|
67
|
+
pass
|
|
68
|
+
return presets
|
|
69
|
+
|
|
70
|
+
@classmethod
|
|
71
|
+
def delete(cls, name: str) -> bool:
|
|
72
|
+
"""Delete a preset by name. Returns True if deleted."""
|
|
73
|
+
filename = f"{name.lower().replace(' ', '_')}.json"
|
|
74
|
+
path = PRESETS_DIR / filename
|
|
75
|
+
if path.exists():
|
|
76
|
+
path.unlink()
|
|
77
|
+
return True
|
|
78
|
+
return False
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# --- Built-in presets ---
|
|
82
|
+
|
|
83
|
+
BUILTIN_PRESETS: list[Preset] = [
|
|
84
|
+
Preset(
|
|
85
|
+
name="3B Q-GaLore Spectrum",
|
|
86
|
+
description="Q-GaLore full-parameter training for 3B models on 16GB GPU",
|
|
87
|
+
params={
|
|
88
|
+
"use_qgalore": True,
|
|
89
|
+
"spectrum": 0.5,
|
|
90
|
+
"galore_rank": 64,
|
|
91
|
+
"epochs": 20,
|
|
92
|
+
"batch_size": 1,
|
|
93
|
+
"max_length": 12000,
|
|
94
|
+
"packing": True,
|
|
95
|
+
"early_stop_steps": 5,
|
|
96
|
+
"live_lr": True,
|
|
97
|
+
"optim": "paged_adamw_8bit",
|
|
98
|
+
},
|
|
99
|
+
),
|
|
100
|
+
Preset(
|
|
101
|
+
name="3B Q-GaLore Rank 128",
|
|
102
|
+
description="Higher-rank Q-GaLore for better quality on 3B models",
|
|
103
|
+
params={
|
|
104
|
+
"use_qgalore": True,
|
|
105
|
+
"spectrum": 0.5,
|
|
106
|
+
"galore_rank": 128,
|
|
107
|
+
"epochs": 30,
|
|
108
|
+
"batch_size": 1,
|
|
109
|
+
"max_length": 12000,
|
|
110
|
+
"packing": True,
|
|
111
|
+
"early_stop_steps": 5,
|
|
112
|
+
"live_lr": True,
|
|
113
|
+
"optim": "paged_adamw_8bit",
|
|
114
|
+
},
|
|
115
|
+
),
|
|
116
|
+
Preset(
|
|
117
|
+
name="3B LoRA Default",
|
|
118
|
+
description="Standard QLoRA training for 3B models",
|
|
119
|
+
params={
|
|
120
|
+
"lora_r": 16,
|
|
121
|
+
"lora_alpha": 32,
|
|
122
|
+
"lora_dropout": 0.05,
|
|
123
|
+
"epochs": 10,
|
|
124
|
+
"batch_size": 2,
|
|
125
|
+
"max_length": 2048,
|
|
126
|
+
"packing": False,
|
|
127
|
+
"early_stop_steps": 5,
|
|
128
|
+
"optim": "paged_adamw_8bit",
|
|
129
|
+
},
|
|
130
|
+
),
|
|
131
|
+
Preset(
|
|
132
|
+
name="7B Q-GaLore",
|
|
133
|
+
description="Q-GaLore for 7B models on 24GB GPU",
|
|
134
|
+
params={
|
|
135
|
+
"use_qgalore": True,
|
|
136
|
+
"spectrum": 0.5,
|
|
137
|
+
"galore_rank": 128,
|
|
138
|
+
"epochs": 20,
|
|
139
|
+
"batch_size": 1,
|
|
140
|
+
"max_length": 8192,
|
|
141
|
+
"packing": True,
|
|
142
|
+
"early_stop_steps": 5,
|
|
143
|
+
"live_lr": True,
|
|
144
|
+
"optim": "paged_adamw_8bit",
|
|
145
|
+
},
|
|
146
|
+
),
|
|
147
|
+
Preset(
|
|
148
|
+
name="7B QLoRA Default",
|
|
149
|
+
description="Standard QLoRA for 7B models",
|
|
150
|
+
params={
|
|
151
|
+
"lora_r": 16,
|
|
152
|
+
"lora_alpha": 32,
|
|
153
|
+
"lora_dropout": 0.05,
|
|
154
|
+
"epochs": 10,
|
|
155
|
+
"batch_size": 2,
|
|
156
|
+
"max_length": 2048,
|
|
157
|
+
"packing": False,
|
|
158
|
+
"early_stop_steps": 5,
|
|
159
|
+
"optim": "paged_adamw_8bit",
|
|
160
|
+
},
|
|
161
|
+
),
|
|
162
|
+
]
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def ensure_builtin_presets() -> None:
|
|
166
|
+
"""Create built-in presets if they don't exist."""
|
|
167
|
+
existing = set(Preset.list_all())
|
|
168
|
+
for preset in BUILTIN_PRESETS:
|
|
169
|
+
if preset.name not in existing:
|
|
170
|
+
preset.save()
|