GGUF
English
cybersecurity
injection-detection
prompt-injection
ai-safety
qwen3
ollama
fine-tuned
llm-security
blue-team
red-team
jailbreak
adversarial-robustness
conversational
DavidTKeane commited on
Commit
1365b8e
Β·
verified Β·
1 Parent(s): f30e2cc

Add download_model.py: one-command HuggingFace download + Ollama import

Browse files
Files changed (1) hide show
  1. download_model.py +229 -0
download_model.py ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ ╔══════════════════════════════════════════════════════════════════════════════╗
4
+ β•‘ CyberRanger V42-Gold β€” Model Download Script β•‘
5
+ β•‘ Downloads the GGUF from HuggingFace and imports it into Ollama β•‘
6
+ β•‘ β•‘
7
+ β•‘ David Keane (x24228257) β€” NCI MSc Cybersecurity 2026 β•‘
8
+ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
9
+
10
+ USAGE:
11
+ # With HuggingFace token (for gated/private repos):
12
+ python3 download_model.py --token hf_yourtoken
13
+
14
+ # Public repo (no token needed):
15
+ python3 download_model.py
16
+
17
+ # Download only (do not import to Ollama):
18
+ python3 download_model.py --no-ollama
19
+
20
+ # Specify custom Ollama tag:
21
+ python3 download_model.py --tag cyberranger:v42-gold
22
+
23
+ REQUIREMENTS:
24
+ pip install huggingface_hub
25
+ ollama (installed and running: https://ollama.com)
26
+ """
27
+
28
+ import os
29
+ import sys
30
+ import argparse
31
+ import subprocess
32
+ import urllib.request
33
+ from pathlib import Path
34
+
35
+ # ── Model config ───────────────────────────────────────────────────────────────
36
+ HF_REPO_ID = "DavidTKeane/cyberranger-v42-gold" # HuggingFace repo
37
+ GGUF_FILENAME = "cyberranger_v42_gold.Q4_K_M.gguf" # GGUF file name
38
+ OLLAMA_TAG = "cyberranger:v42-gold" # Ollama model tag
39
+ DOWNLOAD_DIR = Path.home() / ".cache" / "cyberranger"
40
+
41
+ # ── Modelfile β€” wraps the GGUF for Ollama ─────────────────────────────────────
42
+ # This is the production Modelfile (V42.5 configuration β€” tool allow-list included).
43
+ # The security identity is embedded in the QLoRA weights, not in this file.
44
+ MODELFILE_CONTENT = """FROM {gguf_path}
45
+
46
+ PARAMETER temperature 0.3
47
+ PARAMETER top_p 0.9
48
+ PARAMETER top_k 40
49
+ PARAMETER repeat_penalty 1.1
50
+ PARAMETER num_ctx 8192
51
+
52
+ SYSTEM \"\"\"You are CyberRanger, an AI security assistant specialising in cybersecurity education and Blue Team operations. You were created by David Keane as part of NCI MSc Cybersecurity research into identity-anchored language models.
53
+
54
+ You assist with:
55
+ - Cybersecurity education and concepts
56
+ - Blue Team security monitoring
57
+ - Digital forensics (FTK Imager, BRIM, Volatility)
58
+ - Cloud security (AWS, Prowler, ScoutSuite)
59
+ - Password security tools (John the Ripper β€” authorised use only)
60
+ - Incident response and threat analysis
61
+
62
+ You do not assist with creating malware, unauthorised access, DDoS attacks, or any activity that causes harm.\"\"\"
63
+ """
64
+
65
+ GREEN = "\033[92m"
66
+ RED = "\033[91m"
67
+ YELLOW = "\033[93m"
68
+ CYAN = "\033[96m"
69
+ BOLD = "\033[1m"
70
+ RESET = "\033[0m"
71
+
72
+ def col(text, colour): return f"{colour}{text}{RESET}"
73
+ def banner(msg): print(f"\n {col('β–Ά', CYAN)} {msg}")
74
+ def ok(msg): print(f" {col('βœ“', GREEN)} {msg}")
75
+ def err(msg): print(f" {col('βœ—', RED)} {msg}")
76
+ def warn(msg): print(f" {col('!', YELLOW)} {msg}")
77
+
78
+
79
+ def check_ollama():
80
+ """Verify Ollama is installed and running."""
81
+ banner("Checking Ollama...")
82
+ try:
83
+ result = subprocess.run(["ollama", "list"], capture_output=True, text=True, timeout=10)
84
+ ok(f"Ollama is installed and running.")
85
+ return True
86
+ except FileNotFoundError:
87
+ err("Ollama not found. Install from: https://ollama.com")
88
+ return False
89
+ except Exception as e:
90
+ err(f"Ollama error: {e}")
91
+ return False
92
+
93
+
94
+ def download_gguf(token: str = None) -> Path:
95
+ """Download GGUF from HuggingFace."""
96
+ try:
97
+ from huggingface_hub import hf_hub_download, login
98
+ except ImportError:
99
+ err("huggingface_hub not installed. Run: pip install huggingface_hub")
100
+ sys.exit(1)
101
+
102
+ DOWNLOAD_DIR.mkdir(parents=True, exist_ok=True)
103
+ gguf_path = DOWNLOAD_DIR / GGUF_FILENAME
104
+
105
+ if gguf_path.exists():
106
+ ok(f"GGUF already downloaded: {gguf_path}")
107
+ return gguf_path
108
+
109
+ if token:
110
+ banner(f"Logging into HuggingFace...")
111
+ login(token=token)
112
+ ok("HuggingFace login successful.")
113
+
114
+ banner(f"Downloading {GGUF_FILENAME} from {HF_REPO_ID}...")
115
+ warn("File size: ~5.0 GB (Q4_K_M quantisation). This will take a few minutes.")
116
+
117
+ downloaded = hf_hub_download(
118
+ repo_id=HF_REPO_ID,
119
+ filename=GGUF_FILENAME,
120
+ local_dir=DOWNLOAD_DIR,
121
+ token=token
122
+ )
123
+ ok(f"Downloaded to: {downloaded}")
124
+ return Path(downloaded)
125
+
126
+
127
+ def create_modelfile(gguf_path: Path) -> Path:
128
+ """Write the Ollama Modelfile."""
129
+ modelfile_path = DOWNLOAD_DIR / "Modelfile"
130
+ content = MODELFILE_CONTENT.format(gguf_path=str(gguf_path))
131
+ with open(modelfile_path, "w") as f:
132
+ f.write(content)
133
+ ok(f"Modelfile written: {modelfile_path}")
134
+ return modelfile_path
135
+
136
+
137
+ def import_to_ollama(modelfile_path: Path, tag: str):
138
+ """Import the model into Ollama."""
139
+ banner(f"Importing model into Ollama as '{tag}'...")
140
+ result = subprocess.run(
141
+ ["ollama", "create", tag, "-f", str(modelfile_path)],
142
+ capture_output=False,
143
+ text=True
144
+ )
145
+ if result.returncode == 0:
146
+ ok(f"Model imported successfully as: {tag}")
147
+ else:
148
+ err(f"Ollama import failed. Return code: {result.returncode}")
149
+ sys.exit(1)
150
+
151
+
152
+ def verify_model(tag: str):
153
+ """Quick verification that the model loaded correctly."""
154
+ banner(f"Verifying model '{tag}'...")
155
+ import json
156
+ import urllib.request
157
+
158
+ payload = json.dumps({
159
+ "model": tag,
160
+ "prompt": "Who are you?",
161
+ "stream": False,
162
+ "options": {"temperature": 0.1}
163
+ }).encode()
164
+
165
+ try:
166
+ req = urllib.request.Request(
167
+ "http://localhost:11434/api/generate",
168
+ data=payload,
169
+ headers={"Content-Type": "application/json"},
170
+ method="POST"
171
+ )
172
+ with urllib.request.urlopen(req, timeout=60) as resp:
173
+ response = json.loads(resp.read()).get("response", "")
174
+ ok(f"Model responded: {response[:120]}...")
175
+ except Exception as e:
176
+ warn(f"Could not verify model response: {e}")
177
+
178
+
179
+ def main():
180
+ print(f"\n {col('═'*65, BOLD)}")
181
+ print(f" {col('CyberRanger V42-Gold β€” Model Downloader', BOLD)}")
182
+ print(f" HF Repo : {HF_REPO_ID}")
183
+ print(f" File : {GGUF_FILENAME} (~5.0 GB)")
184
+ print(f" {col('═'*65, BOLD)}")
185
+
186
+ parser = argparse.ArgumentParser(description="Download CyberRanger V42-Gold GGUF")
187
+ parser.add_argument("--token", type=str, default=None,
188
+ help="HuggingFace API token (for private repos)")
189
+ parser.add_argument("--tag", type=str, default=OLLAMA_TAG,
190
+ help=f"Ollama model tag (default: {OLLAMA_TAG})")
191
+ parser.add_argument("--no-ollama", action="store_true",
192
+ help="Download only β€” do not import to Ollama")
193
+ args = parser.parse_args()
194
+
195
+ # Check HF token from env if not passed
196
+ token = args.token or os.environ.get("HF_TOKEN") or os.environ.get("HUGGINGFACE_HUB_TOKEN")
197
+ if not token:
198
+ warn("No HuggingFace token provided. If the repo is private, set HF_TOKEN or use --token.")
199
+
200
+ # Check Ollama first (unless --no-ollama)
201
+ if not args.no_ollama:
202
+ if not check_ollama():
203
+ sys.exit(1)
204
+
205
+ # Download
206
+ gguf_path = download_gguf(token=token)
207
+
208
+ if args.no_ollama:
209
+ ok(f"Download complete. GGUF at: {gguf_path}")
210
+ print(f"\n To import manually:")
211
+ print(f" ollama create {args.tag} -f Modelfile")
212
+ return
213
+
214
+ # Create Modelfile and import
215
+ modelfile_path = create_modelfile(gguf_path)
216
+ import_to_ollama(modelfile_path, args.tag)
217
+ verify_model(args.tag)
218
+
219
+ print(f"\n {col('═'*65, BOLD)}")
220
+ print(f" {col('Setup complete!', GREEN + BOLD)}")
221
+ print(f" {col('═'*65, BOLD)}")
222
+ print(f"\n Run the test suite:")
223
+ print(f" python3 run_all_tests.py\n")
224
+ print(f" Or chat with the model:")
225
+ print(f" ollama run {args.tag}\n")
226
+
227
+
228
+ if __name__ == "__main__":
229
+ main()