Taimwe commited on
Commit
14d00c2
·
verified ·
1 Parent(s): 6de7d94

Declare torch (and transformers for GGUF) so uv run works

Browse files
Files changed (1) hide show
  1. quantise_securecoder.py +191 -189
quantise_securecoder.py CHANGED
@@ -1,189 +1,191 @@
1
- # /// script
2
- # requires-python = ">=3.10"
3
- # dependencies = [
4
- # "huggingface_hub",
5
- # "llama-cpp-python",
6
- # ]
7
- # ///
8
-
9
-
10
-
11
- """Download the merged SecureCoder safetensors repo, convert to GGUF via
12
- llama.cpp's convert_hf_to_gguf.py, and quantise to Q4_K_M. Runs on
13
- cpu-performance (no GPU needed for the conversion path).
14
-
15
- Inputs:
16
- --merged-repo Taimwe/securecoder-30b-pro-merged (default)
17
- --gguf-repo Taimwe/securecoder-30b-pro-GGUF (default)
18
- --quant Q4_K_M (default)
19
-
20
- This deliberately uses the standard llama.cpp converter (rather than Unsloth's
21
- quantise_gguf_model) so the GGUF can be loaded by llama.cpp, Ollama, LM Studio,
22
- Jan, and text-generation-webui without Unsloth's patched kernels.
23
- """
24
-
25
- from __future__ import annotations
26
-
27
- import argparse
28
- import json
29
- import logging
30
- import os
31
- import subprocess
32
- import sys
33
- import time
34
- from pathlib import Path
35
-
36
- logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
37
- log = logging.getLogger("quantise")
38
-
39
-
40
- def parse_args() -> argparse.Namespace:
41
- p = argparse.ArgumentParser(description="Convert merged safetensors -> GGUF")
42
- p.add_argument("--merged-repo", default="Taimwe/securecoder-30b-pro-merged")
43
- p.add_argument("--gguf-repo", default="Taimwe/securecoder-30b-pro-GGUF")
44
- p.add_argument("--quant", default="Q4_K_M")
45
- p.add_argument("--private", action="store_true")
46
- p.add_argument("--work-dir", default="/data/securecoder-gguf")
47
- return p.parse_args()
48
-
49
-
50
- def main() -> int:
51
- args = parse_args()
52
- token = os.environ.get("HF_TOKEN")
53
- if not token:
54
- log.error("HF_TOKEN not set")
55
- return 1
56
-
57
- from huggingface_hub import HfApi, snapshot_download
58
-
59
- work = Path(args.work_dir)
60
- work.mkdir(parents=True, exist_ok=True)
61
-
62
- log.info("downloading %s ...", args.merged_repo)
63
- started = time.time()
64
- src = snapshot_download(
65
- args.merged_repo,
66
- local_dir=work / "merged",
67
- token=token,
68
- allow_patterns=["*.json", "*.txt", "*.safetensors", "*.tiktoken", "*.jinja"],
69
- )
70
- log.info("downloaded %s in %.1f min", src, (time.time() - started) / 60)
71
-
72
- src_path = Path(src)
73
-
74
- # Pull llama.cpp's converter + quantiser. We need cmake + make to build
75
- # llama-quantize; the container does not ship them, so apt-install them
76
- # up front.
77
- log.info("ensuring cmake / make are present ...")
78
- try:
79
- subprocess.run(["cmake", "--version"], check=True, stdout=subprocess.DEVNULL)
80
- except Exception: # noqa: BLE001
81
- log.info("installing cmake via apt ...")
82
- subprocess.run(["apt-get", "update", "-qq"], check=True)
83
- subprocess.run(["apt-get", "install", "-y", "-qq", "cmake", "build-essential"], check=True)
84
-
85
- log.info("fetching llama.cpp ...")
86
- llama_dir = work / "llama.cpp"
87
- subprocess.run([
88
- "git", "clone", "--depth=1", "--branch", "master",
89
- "https://github.com/ggml-org/llama.cpp", str(llama_dir),
90
- ], check=True)
91
- subprocess.run(["pip", "install", "-q", "-r", str(llama_dir / "requirements" / "requirements-convert_hf_to_gguf.txt")],
92
- check=True)
93
-
94
- f16_dir = work / "gguf-f16"
95
- f16_dir.mkdir(exist_ok=True)
96
- log.info("converting safetensors -> GGUF F16 ...")
97
- subprocess.run([
98
- sys.executable, str(llama_dir / "convert_hf_to_gguf.py"),
99
- str(src_path),
100
- "--outfile", str(f16_dir / "model.gguf"),
101
- "--outtype", "f16",
102
- ], check=True)
103
-
104
- log.info("quantising F16 -> %s ...", args.quant)
105
- qbin = llama_dir / "llama-quantize" # build only if present
106
- if not qbin.exists() or not os.access(qbin, os.X_OK):
107
- log.info("building llama-quantize binary ...")
108
- subprocess.run([
109
- "cmake", "-S", str(llama_dir), "-B", str(llama_dir / "build"),
110
- "-DBUILD_SHARED_LIBS=OFF",
111
- ], check=True)
112
- subprocess.run([
113
- "cmake", "--build", str(llama_dir / "build"), "--target", "llama-quantize", "--config", "Release",
114
- ], check=True)
115
- # Find the produced binary
116
- candidates = [
117
- llama_dir / "build" / "bin" / "llama-quantize",
118
- llama_dir / "build" / "llama-quantize",
119
- llama_dir / "llama-quantize",
120
- ]
121
- qbin = next((p for p in candidates if p.exists()), None)
122
- if qbin is None:
123
- log.error("could not find llama-quantize after build")
124
- return 1
125
-
126
- out_dir = work / "gguf-out"
127
- out_dir.mkdir(exist_ok=True)
128
- subprocess.run([str(qbin), str(f16_dir / "model.gguf"),
129
- str(out_dir / f"model-{args.quant}.gguf"), args.quant], check=True)
130
-
131
- # Write a README so the GGUF repo isn't an empty shell
132
- readme = f"""---
133
- base_model: Taimwe/securecoder-30b-pro-merged
134
- license: apache-2.0
135
- pipeline_tag: text-generation
136
- tags:
137
- - gguf
138
- - llama.cpp
139
- - q4_k_m
140
- - qwen3
141
- - code
142
- - tool-calling
143
- - security
144
- ---
145
-
146
- # SecureCoder (GGUF Q4_K_M)
147
-
148
- GGUF export of [Taimwe/securecoder-30b-pro-merged](https://huggingface.co/Taimwe/securecoder-30b-pro-merged),
149
- a LoRA fine-tune of `unsloth/Qwen3-Coder-30B-A3B-Instruct` for code + tool
150
- calling + cybersecurity (offence and defence).
151
-
152
- ## Files
153
-
154
- | File | Notes |
155
- | --- | --- |
156
- | `model-{args.quant}.gguf` | {args.quant} (~7 GB) |
157
- | `README.md` | this card |
158
-
159
- ## Usage (llama.cpp / Ollama / LM Studio)
160
-
161
- ```bash
162
- # llama.cpp server
163
- llama-server -m model-{args.quant}.gguf --host 0.0.0.0 --port 8080 -ngl 99
164
-
165
- # Ollama
166
- ollama create securecoder -f Modelfile
167
- ollama run securecoder "Write a binary search in Rust."
168
- ```
169
-
170
- See the parent repo for full provenance, training data, and the limitations
171
- section.
172
- """
173
- (out_dir / "README.md").write_text(readme, encoding="utf-8")
174
-
175
- api = HfApi(token=token)
176
- api.create_repo(args.gguf_repo, repo_type="model", exist_ok=True, private=args.private)
177
- api.upload_folder(folder_path=str(out_dir), repo_id=args.gguf_repo, repo_type="model",
178
- commit_message=f"Add {args.quant} GGUF export")
179
- log.info("GGUF live: https://huggingface.co/%s", args.gguf_repo)
180
-
181
- print("=" * 78)
182
- print("QUANTISE COMPLETE")
183
- print(f" gguf: https://huggingface.co/{args.gguf_repo}")
184
- print("=" * 78)
185
- return 0
186
-
187
-
188
- if __name__ == "__main__":
189
- raise SystemExit(main())
 
 
 
1
+ # /// script
2
+ # requires-python = ">=3.10"
3
+ # dependencies = [
4
+ # "huggingface_hub",
5
+ # "llama-cpp-python",
6
+ # "torch",
7
+ # "transformers>=4.57",
8
+ # ]
9
+ # ///
10
+
11
+
12
+
13
+ """Download the merged SecureCoder safetensors repo, convert to GGUF via
14
+ llama.cpp's convert_hf_to_gguf.py, and quantise to Q4_K_M. Runs on
15
+ cpu-performance (no GPU needed for the conversion path).
16
+
17
+ Inputs:
18
+ --merged-repo Taimwe/securecoder-30b-pro-merged (default)
19
+ --gguf-repo Taimwe/securecoder-30b-pro-GGUF (default)
20
+ --quant Q4_K_M (default)
21
+
22
+ This deliberately uses the standard llama.cpp converter (rather than Unsloth's
23
+ quantise_gguf_model) so the GGUF can be loaded by llama.cpp, Ollama, LM Studio,
24
+ Jan, and text-generation-webui without Unsloth's patched kernels.
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ import argparse
30
+ import json
31
+ import logging
32
+ import os
33
+ import subprocess
34
+ import sys
35
+ import time
36
+ from pathlib import Path
37
+
38
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
39
+ log = logging.getLogger("quantise")
40
+
41
+
42
+ def parse_args() -> argparse.Namespace:
43
+ p = argparse.ArgumentParser(description="Convert merged safetensors -> GGUF")
44
+ p.add_argument("--merged-repo", default="Taimwe/securecoder-30b-pro-merged")
45
+ p.add_argument("--gguf-repo", default="Taimwe/securecoder-30b-pro-GGUF")
46
+ p.add_argument("--quant", default="Q4_K_M")
47
+ p.add_argument("--private", action="store_true")
48
+ p.add_argument("--work-dir", default="/data/securecoder-gguf")
49
+ return p.parse_args()
50
+
51
+
52
+ def main() -> int:
53
+ args = parse_args()
54
+ token = os.environ.get("HF_TOKEN")
55
+ if not token:
56
+ log.error("HF_TOKEN not set")
57
+ return 1
58
+
59
+ from huggingface_hub import HfApi, snapshot_download
60
+
61
+ work = Path(args.work_dir)
62
+ work.mkdir(parents=True, exist_ok=True)
63
+
64
+ log.info("downloading %s ...", args.merged_repo)
65
+ started = time.time()
66
+ src = snapshot_download(
67
+ args.merged_repo,
68
+ local_dir=work / "merged",
69
+ token=token,
70
+ allow_patterns=["*.json", "*.txt", "*.safetensors", "*.tiktoken", "*.jinja"],
71
+ )
72
+ log.info("downloaded %s in %.1f min", src, (time.time() - started) / 60)
73
+
74
+ src_path = Path(src)
75
+
76
+ # Pull llama.cpp's converter + quantiser. We need cmake + make to build
77
+ # llama-quantize; the container does not ship them, so apt-install them
78
+ # up front.
79
+ log.info("ensuring cmake / make are present ...")
80
+ try:
81
+ subprocess.run(["cmake", "--version"], check=True, stdout=subprocess.DEVNULL)
82
+ except Exception: # noqa: BLE001
83
+ log.info("installing cmake via apt ...")
84
+ subprocess.run(["apt-get", "update", "-qq"], check=True)
85
+ subprocess.run(["apt-get", "install", "-y", "-qq", "cmake", "build-essential"], check=True)
86
+
87
+ log.info("fetching llama.cpp ...")
88
+ llama_dir = work / "llama.cpp"
89
+ subprocess.run([
90
+ "git", "clone", "--depth=1", "--branch", "master",
91
+ "https://github.com/ggml-org/llama.cpp", str(llama_dir),
92
+ ], check=True)
93
+ subprocess.run(["pip", "install", "-q", "-r", str(llama_dir / "requirements" / "requirements-convert_hf_to_gguf.txt")],
94
+ check=True)
95
+
96
+ f16_dir = work / "gguf-f16"
97
+ f16_dir.mkdir(exist_ok=True)
98
+ log.info("converting safetensors -> GGUF F16 ...")
99
+ subprocess.run([
100
+ sys.executable, str(llama_dir / "convert_hf_to_gguf.py"),
101
+ str(src_path),
102
+ "--outfile", str(f16_dir / "model.gguf"),
103
+ "--outtype", "f16",
104
+ ], check=True)
105
+
106
+ log.info("quantising F16 -> %s ...", args.quant)
107
+ qbin = llama_dir / "llama-quantize" # build only if present
108
+ if not qbin.exists() or not os.access(qbin, os.X_OK):
109
+ log.info("building llama-quantize binary ...")
110
+ subprocess.run([
111
+ "cmake", "-S", str(llama_dir), "-B", str(llama_dir / "build"),
112
+ "-DBUILD_SHARED_LIBS=OFF",
113
+ ], check=True)
114
+ subprocess.run([
115
+ "cmake", "--build", str(llama_dir / "build"), "--target", "llama-quantize", "--config", "Release",
116
+ ], check=True)
117
+ # Find the produced binary
118
+ candidates = [
119
+ llama_dir / "build" / "bin" / "llama-quantize",
120
+ llama_dir / "build" / "llama-quantize",
121
+ llama_dir / "llama-quantize",
122
+ ]
123
+ qbin = next((p for p in candidates if p.exists()), None)
124
+ if qbin is None:
125
+ log.error("could not find llama-quantize after build")
126
+ return 1
127
+
128
+ out_dir = work / "gguf-out"
129
+ out_dir.mkdir(exist_ok=True)
130
+ subprocess.run([str(qbin), str(f16_dir / "model.gguf"),
131
+ str(out_dir / f"model-{args.quant}.gguf"), args.quant], check=True)
132
+
133
+ # Write a README so the GGUF repo isn't an empty shell
134
+ readme = f"""---
135
+ base_model: Taimwe/securecoder-30b-pro-merged
136
+ license: apache-2.0
137
+ pipeline_tag: text-generation
138
+ tags:
139
+ - gguf
140
+ - llama.cpp
141
+ - q4_k_m
142
+ - qwen3
143
+ - code
144
+ - tool-calling
145
+ - security
146
+ ---
147
+
148
+ # SecureCoder (GGUF Q4_K_M)
149
+
150
+ GGUF export of [Taimwe/securecoder-30b-pro-merged](https://huggingface.co/Taimwe/securecoder-30b-pro-merged),
151
+ a LoRA fine-tune of `unsloth/Qwen3-Coder-30B-A3B-Instruct` for code + tool
152
+ calling + cybersecurity (offence and defence).
153
+
154
+ ## Files
155
+
156
+ | File | Notes |
157
+ | --- | --- |
158
+ | `model-{args.quant}.gguf` | {args.quant} (~7 GB) |
159
+ | `README.md` | this card |
160
+
161
+ ## Usage (llama.cpp / Ollama / LM Studio)
162
+
163
+ ```bash
164
+ # llama.cpp server
165
+ llama-server -m model-{args.quant}.gguf --host 0.0.0.0 --port 8080 -ngl 99
166
+
167
+ # Ollama
168
+ ollama create securecoder -f Modelfile
169
+ ollama run securecoder "Write a binary search in Rust."
170
+ ```
171
+
172
+ See the parent repo for full provenance, training data, and the limitations
173
+ section.
174
+ """
175
+ (out_dir / "README.md").write_text(readme, encoding="utf-8")
176
+
177
+ api = HfApi(token=token)
178
+ api.create_repo(args.gguf_repo, repo_type="model", exist_ok=True, private=args.private)
179
+ api.upload_folder(folder_path=str(out_dir), repo_id=args.gguf_repo, repo_type="model",
180
+ commit_message=f"Add {args.quant} GGUF export")
181
+ log.info("GGUF live: https://huggingface.co/%s", args.gguf_repo)
182
+
183
+ print("=" * 78)
184
+ print("QUANTISE COMPLETE")
185
+ print(f" gguf: https://huggingface.co/{args.gguf_repo}")
186
+ print("=" * 78)
187
+ return 0
188
+
189
+
190
+ if __name__ == "__main__":
191
+ raise SystemExit(main())