Text Generation
GGUF
qwen36
Mixture of Experts
conversational
multimodal
agent
ollama
heretic
uncensored
reasoning
distillation
Instructions to use FoolDev/Janus-35B-HERETIC with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- llama.cpp
How to use FoolDev/Janus-35B-HERETIC with llama.cpp:
Install (macOS, Linux)
curl -LsSf https://llama.app/install.sh | sh # Start a local OpenAI-compatible server with a web UI: llama serve -hf FoolDev/Janus-35B-HERETIC:Q4_K_M # Run inference directly in the terminal: llama cli -hf FoolDev/Janus-35B-HERETIC:Q4_K_M
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf FoolDev/Janus-35B-HERETIC:Q4_K_M # Run inference directly in the terminal: llama cli -hf FoolDev/Janus-35B-HERETIC:Q4_K_M
Use pre-built binary
# Download pre-built binary from: # https://github.com/ggerganov/llama.cpp/releases # Start a local OpenAI-compatible server with a web UI: ./llama-server -hf FoolDev/Janus-35B-HERETIC:Q4_K_M # Run inference directly in the terminal: ./llama-cli -hf FoolDev/Janus-35B-HERETIC:Q4_K_M
Build from source code
git clone https://github.com/ggerganov/llama.cpp.git cd llama.cpp cmake -B build cmake --build build -j --target llama-server llama-cli # Start a local OpenAI-compatible server with a web UI: ./build/bin/llama-server -hf FoolDev/Janus-35B-HERETIC:Q4_K_M # Run inference directly in the terminal: ./build/bin/llama-cli -hf FoolDev/Janus-35B-HERETIC:Q4_K_M
Use Docker
docker model run hf.co/FoolDev/Janus-35B-HERETIC:Q4_K_M
- Llama new
- LM Studio
- Jan
- vLLM
How to use FoolDev/Janus-35B-HERETIC with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "FoolDev/Janus-35B-HERETIC" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "FoolDev/Janus-35B-HERETIC", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/FoolDev/Janus-35B-HERETIC:Q4_K_M
- Ollama
How to use FoolDev/Janus-35B-HERETIC with Ollama:
ollama run hf.co/FoolDev/Janus-35B-HERETIC:Q4_K_M
- Unsloth Desktop
- Pi
How to use FoolDev/Janus-35B-HERETIC with Pi:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf FoolDev/Janus-35B-HERETIC:Q4_K_M
Configure the model in Pi
# Install Pi: npm install -g @earendil-works/pi-coding-agent # Add to ~/.pi/agent/models.json: { "providers": { "llama-cpp": { "baseUrl": "http://localhost:8080/v1", "api": "openai-completions", "apiKey": "none", "models": [ { "id": "FoolDev/Janus-35B-HERETIC:Q4_K_M" } ] } } }Run Pi
# Start Pi in your project directory: pi
- Docker Model Runner
How to use FoolDev/Janus-35B-HERETIC with Docker Model Runner:
docker model run hf.co/FoolDev/Janus-35B-HERETIC:Q4_K_M
- Lemonade
How to use FoolDev/Janus-35B-HERETIC with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull FoolDev/Janus-35B-HERETIC:Q4_K_M
Run and chat with the model
lemonade run user.Janus-35B-HERETIC-Q4_K_M
List all available models
lemonade list
- Hermes Agent
How to use FoolDev/Janus-35B-HERETIC with Hermes Agent:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf FoolDev/Janus-35B-HERETIC:Q4_K_M
Configure Hermes
# Install Hermes: curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash hermes setup # Point Hermes at the local server: hermes config set model.provider custom hermes config set model.base_url http://127.0.0.1:8080/v1 hermes config set model.default FoolDev/Janus-35B-HERETIC:Q4_K_M
Run Hermes
hermes
- Atomic Chat
- OpenClaw
How to use FoolDev/Janus-35B-HERETIC with OpenClaw:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf FoolDev/Janus-35B-HERETIC:Q4_K_M
Configure OpenClaw
# Install OpenClaw: npm install -g openclaw@latest # Register the local server and set it as the default model: openclaw onboard --non-interactive --mode local \ --auth-choice custom-api-key \ --custom-base-url http://127.0.0.1:8080/v1 \ --custom-model-id "FoolDev/Janus-35B-HERETIC:Q4_K_M" \ --custom-provider-id llama-cpp \ --custom-compatibility openai \ --custom-text-input \ --accept-risk \ --skip-health
Run OpenClaw
openclaw agent --local --agent main --message "Hello from Hugging Face"
File size: 9,136 Bytes
98074bd f48fa07 c0d1a26 98074bd c0d1a26 f48fa07 c0d1a26 f48fa07 c0d1a26 98074bd f48fa07 98074bd 2de4ce0 f48fa07 2de4ce0 98074bd 2de4ce0 98074bd 2de4ce0 98074bd 2de4ce0 98074bd e023532 98074bd 2de4ce0 98074bd 26f1c69 2de4ce0 26f1c69 e023532 2de4ce0 98074bd 2de4ce0 98074bd 2de4ce0 98074bd 2de4ce0 98074bd | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | #!/usr/bin/env python3
"""
Strip the MTP / NextN layer (the extra last block) out of a qwen35moe GGUF so it
loads on stock llama.cpp / Ollama.
MTP-preserving GGUF conversions keep the MTP tensors as one extra NextN block,
so block_count = <transformer layers> + 1 MTP layer — here 40 + 1 = 41, with the
NextN block at index 40. Stock loaders treat that last block as a normal hybrid
layer and abort with `missing tensor 'blk.<last>.ssm_conv1d.weight'`. This drops
all `blk.<last>.*` tensors, sets block_count down by one (back to 40), and
removes nextn_predict_layers.
For the blob this repo bundles, that is a no-op passthrough (hardlink, no extra
disk), not a required build step. Janus-35B-A3B.Q4_K_M.gguf comes from
llmfan46/Qwen3.6-35B-A3B-uncensored-heretic-GGUF and is already MTP-clean:
block_count 40, blk.0…blk.39 only, no nextn_predict_layers. The head exists
upstream (the base's config.json declares mtp_num_hidden_layers 1) and llmfan46
publishes it as a separate *-Native-MTP-Preserved-GGUF whose Q4_K_M reports
block_count 41 — that variant is what this script is for, and this repo
deliberately does not ship it. So the strip is kept purely as a safety net, the
way the dense sibling FoolDev/Thanatos-27B-HERETIC needs it for its
MTP-preserved heretic-ara base: it guards against a future llmfan46 re-quant
shipping the MTP layer. Wanting the MTP head means running the upstream
safetensors under vLLM / SGLang, or taking the unstripped GGUF straight from
that variant repo.
Every KEPT tensor is copied byte-for-byte (raw quantized data) — no
re-quantization, so no quality change to the model itself.
Optionally also restamp the embedded chat template in the same pass:
--chat-template FILE replace tokenizer.chat_template with FILE's contents
That matters because llama.cpp, LM Studio, Jan and KoboldCpp all read the
template baked into the GGUF, so replacing it is the only way to change what
those loaders see. This repo no longer carries a chat_template.jinja — the Qwen
3.6 base's own embedded template governs the llama.cpp path — so nothing here
passes the flag today: build.sh calls this script without it, and the bundled
blob keeps the template it shipped with.
With --chat-template and no MTP block to strip, the file is still rewritten (the
usual hardlink passthrough would not carry the new template).
Usage:
python3 scripts/strip_mtp.py IN.gguf OUT.gguf
python3 scripts/strip_mtp.py IN.gguf OUT.gguf --chat-template chat_template.jinja
"""
from __future__ import annotations
import argparse
import sys
import numpy as np
from gguf import GGUFReader, GGUFWriter, GGUFValueType
def decode_field(field):
if not field or not field.types:
return None
t = field.types[0]
if t == GGUFValueType.ARRAY:
sub = field.types[-1]
if sub == GGUFValueType.STRING:
return [str(bytes(field.parts[i]), encoding="utf-8") for i in field.data]
return [pv for i in field.data for pv in field.parts[i].tolist()]
if t == GGUFValueType.STRING:
return str(bytes(field.parts[-1]), encoding="utf-8")
return field.parts[-1].tolist()[0]
def main() -> int:
ap = argparse.ArgumentParser(
description="Strip the MTP/NextN block from a GGUF; optionally restamp its chat template.")
ap.add_argument("src", metavar="IN.gguf")
ap.add_argument("dst", metavar="OUT.gguf")
ap.add_argument("--chat-template", metavar="FILE", default=None,
help="replace tokenizer.chat_template with the contents of FILE")
args = ap.parse_args()
src, dst = args.src, args.dst
new_template = None
if args.chat_template:
try:
with open(args.chat_template, encoding="utf-8") as fh:
new_template = fh.read()
except OSError as exc:
print(f"[!] cannot read {args.chat_template}: {exc}", file=sys.stderr)
return 1
if not new_template.strip():
print(f"[!] {args.chat_template} is empty; refusing to stamp a blank template",
file=sys.stderr)
return 1
reader = GGUFReader(src, "r")
arch = decode_field(reader.get_field("general.architecture"))
if arch is None:
print("[!] no general.architecture", file=sys.stderr)
return 1
bc_field = reader.get_field(f"{arch}.block_count")
block_count = decode_field(bc_field)
# Same guard the general.architecture lookup above has: decode_field returns
# None for a missing key, and `block_count - 1` on None raises a TypeError
# traceback from inside a build.sh run instead of this diagnostic.
if block_count is None:
print(f"[!] no {arch}.block_count", file=sys.stderr)
return 1
last = block_count - 1
prefix = f"blk.{last}."
# Only strip when the last block is actually an MTP/NextN layer. On an
# already-clean GGUF this is a no-op: hardlink (or copy) src -> dst so the
# caller always gets a valid output without touching real transformer layers.
has_nextn = decode_field(reader.get_field(f"{arch}.nextn_predict_layers")) or 0
last_is_mtp = any(t.name.startswith(prefix) and "nextn" in t.name.lower() for t in reader.tensors)
strip = bool(has_nextn or last_is_mtp)
# The hardlink passthrough cannot carry a new chat template, so it only
# applies when there is nothing at all to rewrite.
if not strip and new_template is None:
print(f"[=] no MTP/NextN layer (block_count={block_count}); copying through unchanged")
if src != dst:
import os
if os.path.exists(dst):
os.remove(dst)
try:
os.link(src, dst)
except OSError:
import shutil
shutil.copyfile(src, dst)
return 0
# We only ever drop the last block, so it MUST be the MTP/NextN block. If
# nextn_predict_layers is set but blk.<last>.* holds no NextN tensor, the MTP
# block isn't where we assume — refuse rather than drop a real transformer layer.
if strip and not last_is_mtp:
print(f"[!] {arch}.nextn_predict_layers is set but blk.{last}.* has no NextN tensor; "
"refusing to strip (would drop a real transformer layer).", file=sys.stderr)
return 1
# This script drops exactly ONE block and decrements block_count by one, so a
# model declaring more than one MTP head would be silently half-stripped and
# reported as a success. Refuse instead: there is no artifact here to test a
# multi-block path against, and the sibling refusal above already treats a
# broken assumption as fatal rather than guessing.
if strip and has_nextn and has_nextn != 1:
print(f"[!] {arch}.nextn_predict_layers={has_nextn}; this script only removes a single "
"MTP block. Refusing rather than half-stripping.", file=sys.stderr)
return 1
if strip:
print(f"[*] arch={arch} block_count={block_count} -> stripping MTP block {last}, new block_count={block_count-1}")
else:
print(f"[=] arch={arch} block_count={block_count}; no MTP block, rewriting to restamp the chat template")
# Writer manages general.architecture itself; block_count/nextn we override/drop.
skip_keys = {
"general.architecture",
f"{arch}.block_count",
f"{arch}.nextn_predict_layers",
}
if new_template is not None:
skip_keys.add("tokenizer.chat_template")
writer = GGUFWriter(dst, arch=arch)
writer.add_block_count(block_count - 1 if strip else block_count)
n_kv = 0
for name, field in reader.fields.items():
# GGUF.version / GGUF.tensor_count / GGUF.kv_count are header pseudo-fields
# the writer emits itself — re-adding them yields a duplicate key.
if name in skip_keys or name.startswith("GGUF."):
continue
vtype = field.types[0]
val = decode_field(field)
if vtype == GGUFValueType.ARRAY:
writer.add_array(name, val)
elif vtype == GGUFValueType.STRING:
writer.add_string(name, val)
else:
writer.add_key_value(name, val, vtype)
n_kv += 1
if new_template is not None:
writer.add_string("tokenizer.chat_template", new_template)
n_kv += 1
print(f"[*] chat template restamped from {args.chat_template} "
f"({len(new_template)} chars)")
kept = [t for t in reader.tensors if not (strip and t.name.startswith(prefix))]
dropped = [t.name for t in reader.tensors if strip and t.name.startswith(prefix)]
print(f"[*] copied {n_kv} KV keys; keeping {len(kept)} tensors, dropping {len(dropped)} (block {last})")
for t in kept:
writer.add_tensor_info(t.name, t.data.shape, t.data.dtype, t.data.nbytes, t.tensor_type)
writer.write_header_to_file()
writer.write_kv_data_to_file()
writer.write_ti_data_to_file()
for t in kept:
writer.write_tensor_data(np.ascontiguousarray(t.data))
writer.close()
print(f"[+] wrote {dst}")
return 0
if __name__ == "__main__":
sys.exit(main())
|