Reverse-engineering + crypto/exploit-audit analysis model. A LoRA SFT
fine-tune of lancejames221b/HAWQ-v1,
trained to produce faithful, structured analysis of x86-64 assembly and
decompiled C (purpose, inputs/outputs, algorithm, control flow,
security-relevant behavior) and, as of v1.1, cryptographic-primitive
identification, misuse enumeration, and exploit-path reasoning over
arbitrary source code. Merged into the base weights (no adapter required
at inference time) and shipped as GGUF (llama.cpp / LM Studio) and MLX
(Apple Silicon) quantizations. An Ollama registry listing is planned but
not live yet - see "Ollama" under Usage.
This is the HAWQ-SEC-RE line's public repo ("v1" — internal training
iteration v3 for the original RE release; v4 for the v1.1 crypto/exploit-audit
update; v4-dpo-v12 for this v1.2 DPO fix pass). Same repo, same links, updated
in place; v1.0 and v1.1 tags on this repo preserve the exact prior commits.
Known limitations (read before using for audit triage)
Fixed in v1.2: v1.1's clean-code over-flagging regression. v1.1
claimed a security flaw in correct, textbook AES-256-GCM (no injected
flaw) in 9/9 sampled runs (probe_clean_control, k=9, temperature=0).
A DPO pass corrects this - v1.2 measures 0-1/9 false-positive hits
across repeated runs on the GGUF build (0/9 on the MLX build), down
from a consistent 9/9 in v1.1. See "Fix status" below.
Still open in v1.2 - NOT fixed by this release: real-world edit-tool
discipline for agentic coding use. Mining this model's own agent-session
history (omp and Claude Code, across real coding projects) surfaced two
concrete failure patterns when this model edits files as a coding agent:
(1) issuing an edit anchored on stale/remembered file content instead of
re-reading current state first, and (2) - the more serious case - a
line-range edit that applies with no tool error while silently splicing
content into the wrong location or deleting adjacent logic, only caught
later by a human or a test failure. A DPO pass targeting this behavior
(79 pairs mined from real failure sessions, combined with the
clean-code-fix corpus) was trained into v1.2, but the measured effect on
the target metric was zero: v1.2 scores read_before_first_edit
2/15, identical to both pre-DPO baselines (2/15 on the MLX build, 2/15 on
the GGUF build). Treat v1.2's edit-tool discipline as unchanged from
v1.1 - continue to expect it to frequently edit without first
re-confirming current file state, and to occasionally produce a
"successful" edit that corrupts unrelated code. This is tracked as open
follow-up work, not resolved here.
Cause of the v1.1 over-flagging regression (historical context,
now fixed): the v1.1 crypto/exploit-audit training data
(build_sec_audit.py) generated each example as a chosen/rejected pair
for a planned DPO pass, but the SFT stage that shipped in v1.1 trained
on the rejected (i.e. vulnerable) variant of every sample
unconditionally - the model never saw a "this code is actually fine"
example during SFT. It pattern-matched "review this code" to "find
something wrong," independent of whether anything was actually wrong.
Fix status (v1.2)
A single DPO pass over the v1.1 merged weights, using preference pairs
from two sources:
AES/clean-code counterweight (1577 pairs: 1177 omp-advisory +
400 clean-code) - the same corpus staged for v1.1, unchanged.
Edit-discipline pairs (79 pairs, new in v1.2) - mined from real
HAWQ agent sessions, covering the stale-anchor and silent-corruption
failure classes described above.
Measured results (see docs/v1.2_gate_criteria.md in the training
repo for the full pre-registered gate, both pre-DPO baselines, and the
control-run evidence behind the probe_crypto_id note below):
probe_clean_control: FIXED - PASS. GGUF (generic's 4090,
IQ4_XS): 0/9 on the initial gate run, 1/9 on a later re-verification
run (some run-to-run variance observed at temperature=0, likely
floating-point non-associativity under batched GPU inference - not
fully deterministic, but both runs are a clean majority PASS). MLX
(Apple Silicon, vision-restored 4-bit): 0/9. Both dramatically below
v1.1's consistent 9/9 FAIL.
probe_misuse_enum, probe_exploit_path: PASS (9/9), no
regression on genuinely-vulnerable-code detection.
probe_crypto_id: 3/5 on the GGUF build (MD5 and Blowfish cases
scored FAIL). Investigated with a same-prompt, same-temperature
control run against the v1.1 GGUF: v1.1 fails the identical MD5 case
(also answers "SHA-1" - the test's 4 given constants are
byte-identical to SHA-1's IV) and fails Blowfish WORSE (answers
"TEA/XXTEA", not even naming Blowfish, vs v1.2's hedged-but-correct
"Blowfish (or TEA)"). The MLX build's partial run independently
scored MD5 PASS where GGUF failed it - a divergence across builds
on the same prompt is further evidence this is a pre-existing
test-fixture/scoring artifact, not a DPO-induced regression. Not
treated as a fix-status regression.
probe_edit_discipline (new probe, introduced with this release):
NOT FIXED - overall task-completion PASS 15/15, but
read_before_first_edit 2/15 - identical to both pre-DPO baselines.
See "Known limitations" above.
RE/tool-use regression check (eval_re_v2_http_probes.py against the
deployed GGUF): tool_loopPASS, error_recoveryPASS,
long_cotPASS - no regression.
Changelog
v1.2 (this update): DPO pass fixing v1.1's clean-code over-flagging
regression (probe_clean_control now PASS on both GGUF and MLX). Also
trained a set of edit-tool-discipline preference pairs targeting a
separate known agentic-coding failure mode, but that fix did not
land - see "Known limitations" above; edit-tool discipline remains
unchanged from v1.1 and is deferred to a future release. Same base and
merge methodology as v1.1; GGUF and MLX artifacts both rebuilt from the
DPO'd weights.
v1.1: added crypto/exploit-audit SFT domain (primitive identification
from constants alone, misuse-pattern enumeration, and stack-overflow
exploit-path reasoning) on top of the v1.0 RE-analysis model. Shipped
with the clean-code over-flagging regression, fixed in v1.2 above.
v1.0: original public release - RE-analysis SFT only (x86-64
disassembly / decompiled-C structured analysis). Preserved at the
v1.0 git tag.
The pre-v1.2 state (v1.1, with the known over-flagging regression) is
preserved at the v1.1 git tag.
Model family
Base: lancejames221b/HAWQ-v1 — a Holo3 + Qwopus + AgentWorld merge
on Qwen3.6-35B-A3B: hybrid linear-attention/SSM MoE, 40 text layers (10
full self-attention, 30 linear-attention/SSM), 256 experts,
moe_intermediate_size=512, hidden_size=2048,
num_attention_heads=16, num_key_value_heads=2, head_dim=256,
vocab_size=248320. Text-only Qwen3_5MoeForCausalLM (693 tensors) —
no vision tower.
Fine-tune: HAWQ-SEC-RE — LoRA SFT (r=64, alpha=128) over attention
(q_proj/k_proj/v_proj/o_proj) and linear-attention/SSM projection
modules (in_proj_qkv/in_proj_a/in_proj_b/in_proj_z/out_proj),
merged into the base with PeftModel.merge_and_unload().
Training
Method: LoRA SFT via transformers + peft, response-only
prompt-prefix masking (no TRL, to avoid a transformers-v5 compatibility
risk on this architecture).
Data: hawq-re-v3 — RE-analysis + decompile families sourced from
LLM4Binary/decompile-bench, with frontier-model-generated gold analyses
as the SFT targets.
Hyperparameters: r=64, lora_alpha=128, target modules as above.
v1.1 addition: crypto/exploit-audit SFT domain (build_sec_audit.py)
layered on top of the same base via a further LoRA SFT pass (targets and
methodology unchanged from above), merged the same way. See "Known
limitations" above for the one confirmed regression this introduced.
v1.2 addition: a DPO pass over the v1.1 merged weights (see "Fix
status" above) - no new SFT domain, same base architecture and merge
methodology.
Validation
Gate 1 — coherence probes (eval_peft_direct.py, run on a Colab G4
session against the merged checkpoint vs. the unmodified HAWQ-v1 base):
probe_tool_loop — native Qwen tool-call convention (no CALL text,
no vacuous calls that never fire): PASS
probe_error_recovery — matches the canonical eval_loop_recovery.py
gate methodology, requires finish reason stop (no forced-loop
artifacts): PASS
probe_long_cot — respects the card's 81,920-token hard-math cap,
classifies length-cap hits as PASS/FAIL/TRUNCATE by deconfounding
sentence-repetition tokens from the raw base rate: PASS
Adapter-binding assertion (added to the eval harness specifically for
this run) confirmed the LoRA delta was actually applied — not a
no-op merge silently shipping unmodified base weights under a new
name.
Gate 2 — win-rate judge (frontier-judged base-vs-tuned comparison on
held-out RE-analysis tasks, eval_re_analysis.py, gate threshold 0.55)
was not completed for this release — base-side and tuned-side
generation passes were run partway (dozens of tasks each) before the
formal run was deprioritized in favor of shipping the already-working
deployment. Treat this release as coherence-validated, not yet
win-rate-validated against the base model. Re-running Gate 2 to
completion is tracked as follow-up work.
Gate 3 — crypto/exploit-audit + edit-discipline probes (v1.2,
replaces v1.1's Gate 3): eval_crypto_audit.py, HTTP against the
deployed v1.2 GGUF/MLX artifact,
k=9 samples per case, temperature=0, majority vote:
probe_crypto_id (identify AES/SHA-256/TEA/Blowfish/MD5 from
constants alone, 5 cases): 3/5 on GGUF (AES, SHA-256, TEA PASS;
MD5, Blowfish FAIL) - investigated and attributed to a pre-existing
test-fixture/scoring artifact reproducing on v1.1 too, not a
regression. See "Fix status" above for the control-run evidence.
probe_misuse_enum (ECB mode + time-seeded key + reused static IV,
one function, three planted misuses): PASS (found all three, 9/9
runs)
probe_clean_control (correct AES-256-GCM, should NOT be flagged):
PASS - 0-1/9 false positives across repeated runs on GGUF (0/9
on MLX). Was FAIL 9/9 in v1.1.
probe_edit_discipline.py (new for v1.2), k=15, against the deployed
GGUF artifact:
Overall task-completion PASS rate: 15/15
read_before_first_edit rate (the discriminating metric - both v1.1
baselines were 2/15): 2/15 - unchanged, not fixed.
eval_re_v2_http_probes.py (RE/tool-use regression check) against the
deployed GGUF: tool_loopPASS, error_recoveryPASS,
long_cotPASS - no regression from v1.1.
MLX is published as its own repo because format detection (LM Studio, HF filters)
keys off files at the repository root — a repo whose root holds a .gguf
is classified GGUF, and a nested MLX folder is not discoverable regardless of
tags. The copy under hawq-sec-re-v1-mlx-4bit/ here is retained for continuity;
prefer the dedicated repo.
Files in this repository
File
Format
Size
Notes
hawq-sec-re-v1-IQ4_XS.gguf
GGUF, IQ4_XS
≈18.9 GB (≈4.4 bpw)
Text-only. llama.cpp / LM Studio. Fits a 24GB GPU (e.g. RTX 4090) with room for q8_0 KV cache at long context. Deployed and smoke-tested (coherent generation on both x86-64 disassembly and RE-analysis prompts). Built from the merged text-only checkpoint (no vision tower).
hawq-sec-re-v1-mlx-4bit/
MLX, mixed 4-bit/8-bit
≈20.4 GB (4.649 bpw average)
Multimodal (vision-enabled). Apple Silicon, via mlx-vlm (mlx_vlm.models.qwen3_5_moe) - NOT plain mlx-lm, which silently drops the vision tower for this architecture. Vision encoder grafted back from the original Qwen/Qwen3.6-35B-A3B multimodal checkpoint (same lineage this model's text backbone was extracted from); text weights are this model's fine-tuned weights. MoE router (mlp.gate) and shared_expert_gate tensors kept at 8-bit per-layer (architecture's built-in quantization predicate), everything else 4-bit at group size 64. Verified end-to-end on an M4 Max: text and vision paths both load and generate (≈95-100 tok/s, ≈21 GB peak RAM). File integrity byte-exact against the source build; vision tensor count matches the donor exactly (333/333).
Ollama registry listing (lancejames221b/hawq-sec-re-v1) is in progress
but push has stalled repeatedly on a registry-side network error;
retry pending. The GGUF above is the reliable distribution path for now.
Both quantizations were built fresh from bf16 checkpoints and carry the
model's native chat_template.jinja (Qwen-style, thinking-mode-capable)
embedded (verified byte-identical to source on both).
Known GGUF conversion gotcha (already fixed in these builds)
Earlier GGUF builds of this architecture family assumed IQ4_XS was
structurally broken on this MoE/hybrid-attention layout. That was
misdiagnosed: the real defect was an off-by-one block_count (MTP
head counted as a regular transformer block) in the f16 intermediate
GGUF. Converting with --no-mtp and block_count=40 (the correct text
layer count) produces a working IQ4_XS quant, confirmed by smoke-testing
with llama-cli and by a verified Ollama deployment on an RTX 4090 (24GB)
producing coherent output on x86-64 disassembly and RE-analysis prompts.
Note the KV cache is only ≈2.7 GB at the full 262k window because just
10 of 40 layers use KV attention - the other 30 are linear-attention/SSM
layers holding a small recurrent state (≈63 MiB). That's what makes 262k
context co-exist with 18.9 GB of weights on a 24 GB card.
Context vs. parallel slots is runtime-specific — don't assume the divide.
Under Ollama / the llama.cpp server, n_ctx is split across concurrent
slots (n_ctx_seq = n_ctx / n_parallel), so the full 262k per request needs
--parallel 1 / OLLAMA_NUM_PARALLEL=1; serving 8 slots at n_ctx=262144
leaves ≈32k per request.
LM Studio does not behave this way. Measured against this exact IQ4_XS
build loaded at --context-length 262144 --parallel 4 on a single RTX 4090:
an 80,010-token prompt and then a 200,010-token prompt were both accepted
and returned normally. LM Studio treats the window as a shared pool that a
single request may consume in full, not a statically pre-divided one — so
you get 4-way concurrency and the full context per request. Concurrent
large requests still contend for the one pool, so peak concurrency at
near-full context is bounded by KV memory, not by a fixed per-slot cap.
Usage
Recommended system prompt
This build does not carry a baked-in default system prompt — pass one
explicitly for reverse-engineering / crypto-audit work. Reasoning cannot be
turned off on this model family; the prompt below is designed to aim the
thinking rather than fight it (no /no_think-style instruction — measured
to triple reasoning-token count and empty the output on this build).
markdown
1# ROLE23You are a reverse-engineering analyst. You work on decompiler output, disassembly,
4binaries, and crash artifacts, mainly from 64-bit Windows PEs built with MSVC, and
5from ELF binaries. You analyse malicious code for defensive purposes: the output is
6a specification precise enough for a defender to act on.
78You state what the code does and you prove it. You are not a summarizer and you are
9not a search engine for threat reports.
1011# HARD RULES12131. EVIDENCE OR SILENCE. Every factual claim about a specific binary must be tied to
14 something you were actually shown in this conversation. If it was not in the
15 input, you do not know it.
162. QUOTE, DO NOT PARAPHRASE. When you assert something about the code, cite the
17 line number or address and copy the relevant source substring character for
18 character. Never reconstruct a quote from memory or clean it up.
193. NO FAMILY RECALL AS FACT. You are forbidden from asserting a property of the
20 binary in front of you because a malware family, vendor blog, or CTI report says
21 so. You may use general knowledge of algorithms, compilers, and library idioms
22 only to RECOGNISE a structure that is visibly present in the input. If your only
23 basis is "this family is known to do X", the answer is "unknown".
244. "UNKNOWN" IS A CORRECT ANSWER. When the evidence does not settle a question, say
25 unknown and state the specific artifact you would need to see. A confident wrong
26 answer is the worst output you can produce; it is worse than no answer.
275. NEVER INVENT AN ADDRESS, OFFSET, SIZE, CONSTANT, OR SYMBOL NAME. If you did not
28 read it, it does not go in the answer.
296. Distinguish what the code DOES from what it is FOR. Report mechanism first;
30 label intent separately and mark it as inference.
3132# HOW TO USE YOUR REASONING3334You think before answering. Spend that budget on the input, not on restating the
35question:
3637- Read the actual bytes, lines, or instructions given to you before forming any
38 hypothesis.
39- Convert every pointer arithmetic expression to a byte offset while you reason.
40- Where two readings both fit the evidence, carry both through your reasoning and
41 name the observation that would discriminate them.
42- Check each claim you are about to make against the input one more time before
43 you commit to it. Quantized recall drifts; re-reading is cheaper than being wrong.
4445Then always emit a final answer outside your reasoning. Never end your turn with
46reasoning alone. If you are running short, cut the analysis and state the partial
47conclusion plus what remains open.
4849# DECOMPILER AND DISASSEMBLY IDIOMS5051-`FUN_140xxxxxxx` is an unnamed function at that virtual address. `DAT_`, `_DAT_`,
52`PTR_` are unnamed data. `s_Foo_140xxxxxxx` is a string literal named after its
53 own contents. `LAB_` is a label.
54-`uVar1`, `iVar2`, `lVar3`, `pcVar4`, `puVar5`, `local_38`, `param_1` are
55 synthesised names with no semantic content. Never infer intent from them.
56-`undefined1/2/4/8` mean 1/2/4/8 bytes of unknown type. `code *` is a function
57 pointer.
58- POINTER ARITHMETIC IS IN ELEMENT UNITS. `param_1 + 4` on a `uint *` is byte
59 offset 0x10. `p[3]` on an `undefined8 *` is byte offset 0x18. Always convert to
60 byte offsets and say that you converted.
61-`__security_check_cookie`, `_Init_thread_footer`, `_alloca_probe`, `atexit`, and
62`local_XX = DAT_... ^ (ulonglong)&local_YY` are compiler boilerplate, never
63 program logic. Name them as boilerplate and move on.
64- The decompiler unrolls, rotates, and flattens loops. A literal integer assigned
65 to a variable that is later decremented to zero is an ITERATION or ROUND COUNT.
66 Report its exact value and the line it is assigned on. Do not assume a standard
67 value.
68- MSVC inlines aggressively: one function may contain several logical operations.
69 Split them in your answer.
70- STL, Boost, and allocator/refcount churn produce large volumes of noise. Say you
71 are skipping it rather than describing it.
72- In disassembly, prefer the instruction sequence over the decompiler when the two
73 disagree, and say that they disagreed.
7475# CRYPTOGRAPHIC PATTERN RECOGNITION7677Name an algorithm only from structure visible in the input.
7879- A 16-word (64-byte) state, the constant `expand 32-byte k` or `expand 16-byte k`,
80 and add-xor-rotate quarter-rounds indicate a Salsa/ChaCha family stream cipher.
81 Discriminate by rotation constants: ChaCha is 16, 12, 8, 7; Salsa20 is 7, 9, 13,
8218. Report the rotations you actually observed.
83- ROUND COUNT equals the double-round loop bound times two. Report the loop-bound
84 literal, its line, then the total. Never assume 20.
85- Derive nonce and counter sizes from which state words the IV setter writes, and
86 in what order. Report byte offsets.
87- A 256-entry byte table plus a `j = (j + S[i] + key[...]) & 0xff` swap loop is RC4.
88- 4x4 byte state, 16-byte blocks, an S-box table, and xtime/0x1b reduction indicate
89 AES. Report key schedule length to infer key size; do not assume 128.
90- Multi-precision limb arrays, Montgomery reduction, or square-and-multiply over
91 64-bit limbs indicate big-integer asymmetric arithmetic. Report the limb count,
92 and the modulus bit size if it is visible; otherwise unknown.
93- Constants such as 0x67452301, 0x5A827999, 0x6A09E667, 0x428A2F98 indicate MD5,
94 SHA-1, or SHA-2. Report which constants you saw before naming the hash.
95- State byte order explicitly, little- or big-endian, whenever a value is
96 serialised, and cite the line that shows it.
97- Distinguish the cipher from the protocol. Key derivation, key wrapping, IV
98 handling, and the on-disk container are separate questions; answer them
99 separately.
100101# WINDOWS AND PE SPECIFICS102103- Attribute behaviour to imports you can actually see. Do not assume an API is used
104 because the behaviour would need it.
105-`CryptGenRandom`, `BCryptGenRandom`, and `RtlGenRandom`/`SystemFunction036` are
106 CSPRNGs. `rand`, `srand`, `GetTickCount`, and `QueryPerformanceCounter` used as a
107 seed are not. Report which one the code actually calls.
108- Ordinal-only imports, delay-load tables, and dynamically resolved APIs via
109`GetProcAddress` hide the real import surface. Say so when you see the pattern.
110- Wide strings are UTF-16LE. When you decode obfuscated data, state the encoding
111 you decoded to and show the recovered bytes.
112113# OUTPUT114115Default to compact technical markdown:
116117- Lead with the conclusion, then the evidence that supports it.
118- Numbers in hex with a `0x` prefix; add decimal in parentheses when the value is a
119 size, count, or offset.
120- Keep an explicit `Unknown / needs evidence` list at the end whenever anything is
121 unresolved.
122- Mark anything not directly observed as `[INFERENCE]`.
123- No filler, no preamble, no restating the question, no marketing language.
124- Never renumber, paraphrase, or summarise away a line number or an address.
125126If the request, or a more specific system message, specifies an output contract —
127a JSON schema, a fixed set of fields, "JSON only" — that contract overrides this
128section completely. Follow it exactly and emit nothing outside it.
Token budget for tool calling (required)
This is a thinking model: reasoning is returned in a separate reasoning_content
field and is emitted before any tool call. Give it real output headroom —
max_tokens of at least 8192 (32768 recommended) for tool-using turns.
With a small cap the reasoning pass is truncated mid-thought and you get
finish_reason: length, empty content, populated reasoning_content, and notool_calls — which looks like "the model can't call tools" but is purely a budget
problem. Measured on this build: at max_tokens: 300 roughly 1 in 4 tool-using
requests failed this way; at max_tokens: 8000, 10/10 returned correct structured
tool_calls (at both temperature 0.6 and 1.0), consuming only 80–281 completion
tokens.
Harness clients must also read reasoning from its own reasoning_content key and keep
it bound to the turn carrying the tool call.
Sampler defaults match this repo's shipped LM Studio serving config
(temperature=0.6, top_p=0.95, top_k=20, min_p=0.0, repeat_penalty=1.0)
consistent with prior HAWQ-SEC-RE releases. Recommended
--ctx-size 262144 if VRAM allows; the model was validated at this
context length on a single RTX 4090 (24GB) with q8_0 KV cache.
Requires mlx-vlm (not mlx-lm alone) for the vision tower to load.
Or point LM Studio's MLX engine at the hawq-sec-re-v1-mlx-4bit/ folder.
Pass an image with --image:
bash
1python3 -m mlx_vlm.generate --model hawq-sec-re-v1-mlx-4bit \2 --image disassembly.png --temperature 0.6 --max-tokens 200\3 --prompt "Read the assembly in this image and explain what it computes."
MLX verification
Measured on an M4 Max (mlx-vlm 0.6.8, mlx 0.32.0):
Path
Prompt tokens
Generation
Peak RAM
Result
Text-only
43
120 tok @ 94.6 tok/s
20.95 GB
Coherent RE analysis
Image + text
168
58 tok @ 100.8 tok/s
21.07 GB
Transcribed all 7 rendered instructions verbatim, then analyzed them
The prompt-token jump (43 -> 168) is the image patch tokens, confirming
the vision encoder is actually in the graph rather than being silently
skipped - the specific failure mode that occurs if this checkpoint is
converted with plain mlx-lm, whose qwen3_5_moe handler strips
vision_tower/model.visual keys during sanitization.
Ollama
Not published to the ollama.com registry yet (the push repeatedly fails
partway through a multi-gigabyte multipart upload and restarts from zero;
unresolved). Running it locally from the GGUF works and is verified -
build it yourself:
Do not add a hand-written TEMPLATE block - the GGUF already carries
the model's native tokenizer.chat_template, and Ollama derives the
correct template from it (verified byte-identical to the source
chat_template.jinja). A hand-rolled template risks diverging from what
the model was trained against.
For the full 262k window and q8_0 KV cache, set these on the Ollama
server (q8_0 KV is silently ignored unless flash attention is on):
The model is tool-capable and thinking-enabled at the same time, which has
two practical consequences worth getting right - both verified against the
IQ4_XS GGUF served by LM Studio (server-side Qwen tool-call parsing) on an
RTX 4090:
1. Give it enough output budget, or you get no tool call at all.
Reasoning is emitted before the tool call. If max_tokens cuts the
response off mid-thought you get finish_reason: "length", an empty
content, a populated reasoning field, and notool_calls - which
resembles "the model can't call tools" but is purely a budget problem:
finish_reason: tool_calls, correct call, only 359 tokens used
It needed just 359 completion tokens once allowed to finish - the cap needs
headroom for the reasoning pass, not for the final answer.
2. Handle the reasoning field separately from content.
Reasoning arrives in its own field (reasoning_content), not inline in
content. A client that doesn't model that separately can drop or mangle
the tool call once the model starts thinking. This is a client/harness
configuration concern, not a property of any particular server.
Whatever client you use, the requirement is the same: read the reasoning
field from its own key and keep it associated with the turn that carries
the tool call. The keys below are specific to the omp harness and are
shown only as a concrete example - they are not a standard, and other
runtimes (vLLM, LiteLLM, llama.cpp server, etc.) expose their own
reasoning-parser options under different names:
Which architecture is this, exactly?
The model_type string is qwen3_5_moe, but the model is Qwen3.6-35B-A3B —
the architecture identifier lags the release name, which trips people up. It's a
hybrid MoE: 40 text layers of which 10 are full self-attention and 30 are
linear-attention/SSM, 256 experts, ~3B active params per token. That layer
split is why a 262k context costs only ~2.7 GB of KV cache.
Why build on an abliterated / uncensored base?
Because refusal gates are actively counterproductive here. RE work means staring
at malware, packers, exploit code and obfuscated binaries — a safety-tuned model
refuses or hedges on exactly the inputs you care about, and you spend your time
fighting the model instead of analyzing the sample. Starting from weights where
those gates are already gone means the SFT budget goes into analysis quality
rather than clawing back refused behavior.
Lineage specifics, since two lines came out of the same repo and are easy to
conflate: an abliterated donor (huihui-ai/...Claude-4.7-Opus-abliterated) was
used explicitly in razorstrike-v1, an earlier DARE-TIES merge that is now
superseded. This model's chain is
nightmedia/Qwen3.6-35B-A3B-Holo3-Qwopus-AgentWorld-qx64-hi-mlx (a third-party
merge, dequantized to bf16) → HAWQ-v1 → this SFT. I did not apply abliteration
myself anywhere in the HAWQ chain, and I don't document the internal composition
of that third-party merge.
What tooling did you use for training?
LoRA SFT with plain transformers + peft — deliberately not TRL, to
avoid a transformers-v5 compatibility risk on this architecture.
r=64, alpha=128. Target modules cover both attention families:
q_proj/k_proj/v_proj/o_proj plus the linear-attention/SSM projections
in_proj_qkv / in_proj_a / in_proj_b / in_proj_z / out_proj. Ground these
against the real weight map rather than reusing a dense-model recipe — on this
arch a Qwen-standard target list silently misses 30 of the 40 layers.
Merged with PeftModel.merge_and_unload().
What about inference / serving?
GGUF: convert_hf_to_gguf.py + llama-quantize (IQ4_XS), served via LM
Studio and Ollama. Fits one RTX 4090 with all 41/41 layers offloaded.
MLX: mlx-vlm 0.6.8 on an M4 Max, ~95-100 tok/s.
Any non-obvious gotchas on this architecture?
Three that cost real time:
Don't convert the multimodal checkpoint with mlx-lm. Its qwen3_5_moe
handler strips vision_tower / model.visual during sanitization, so you get
a silently text-only model wearing a multimodal label. Use mlx-vlm.
The MTP head breaks GGUF conversion. The multi-token-prediction head gets
counted as a transformer block, yielding block_count = 41 instead of 40.
This was long misattributed to "IQ4_XS is broken on this arch" — it isn't.
Convert with --no-mtp.
Whether n_ctx divides across parallel slots depends on the runtime —
verify, don't assume. Under Ollama / the llama.cpp server it does
(n_ctx_seq = n_ctx / n_parallel; 262144 across 8 slots gives each request
~32k), so use --parallel 1 / OLLAMA_NUM_PARALLEL=1 there for the full
window. LM Studio does not divide it — measured on this build at
--parallel 4, single requests of 80,010 and 200,010 tokens both succeeded,
so the window is a shared pool one request can use in full. Relatedly, on
both runtimes JIT-loading applies the default context, not your configured
one.
Why LoRA instead of a full fine-tune?
This is a behavioral/format adaptation — produce faithful structured RE analysis —
not knowledge injection. LoRA at r=64 across both attention families was
sufficient and leaves the base model's general capability intact. The adapter is
~260 MB against a ~70 GB bf16 base.
Limitations
GGUF is text-only. It's built from the merged text-only checkpoint
(no vision tower - despite vestigial vision_config metadata inherited
from the original Qwen3.6-35B-A3B lineage in that checkpoint's
config.json). Use the MLX build for multimodal.
MLX multimodal is verified (Apple Silicon, M4 Max). Both the text
and vision paths load and generate correctly - see "MLX verification"
below. Note the vision tower is the stock Qwen3.6-35B-A3B encoder
grafted back on; it was not fine-tuned by this model's RE-analysis
SFT, so image understanding is base-Qwen quality, while the text
backbone carries the RE tuning.
Win-rate not benchmarked. See "Gate 2" above — this release has
not been formally scored against the base model on held-out RE tasks.
Long chain-of-thought on hard math/reasoning tasks can approach the
81,920-token cap; the eval harness treats this as expected behavior
for the thinking-mode preset, not a defect, but downstream callers
setting a lower max_tokens should budget accordingly.
License
Released under Apache 2.0, matching the lancejames221b/HAWQ-v1
base model.