Instructions to use FoolDev/Janus-35B-HERETIC with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use FoolDev/Janus-35B-HERETIC with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-text-to-text", model="FoolDev/Janus-35B-HERETIC") messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] pipe(text=messages)# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("FoolDev/Janus-35B-HERETIC", device_map="auto") - llama-cpp-python
How to use FoolDev/Janus-35B-HERETIC with llama-cpp-python:
# !pip install llama-cpp-python from llama_cpp import Llama llm = Llama.from_pretrained( repo_id="FoolDev/Janus-35B-HERETIC", filename="Janus-35B-A3B.Q4_K_M.gguf", )
llm.create_chat_completion( messages = [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] ) - 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
- 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": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker
docker model run hf.co/FoolDev/Janus-35B-HERETIC:Q4_K_M
- SGLang
How to use FoolDev/Janus-35B-HERETIC with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "FoolDev/Janus-35B-HERETIC" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "FoolDev/Janus-35B-HERETIC", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "FoolDev/Janus-35B-HERETIC" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "FoolDev/Janus-35B-HERETIC", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }' - Ollama
How to use FoolDev/Janus-35B-HERETIC with Ollama:
ollama run hf.co/FoolDev/Janus-35B-HERETIC:Q4_K_M
- Unsloth Studio
How to use FoolDev/Janus-35B-HERETIC with Unsloth Studio:
Install Unsloth Studio (macOS, Linux, WSL)
curl -fsSL https://unsloth.ai/install.sh | sh # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for FoolDev/Janus-35B-HERETIC to start chatting
Install Unsloth Studio (Windows)
irm https://unsloth.ai/install.ps1 | iex # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for FoolDev/Janus-35B-HERETIC to start chatting
Using HuggingFace Spaces for Unsloth
# No setup required # Open https://huggingface.co/spaces/unsloth/studio in your browser # Search for FoolDev/Janus-35B-HERETIC to start chatting
- 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 @mariozechner/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
- Hermes Agent new
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 new
- OpenClaw new
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"
- 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
Complete examples/ suite parity with Thanatos-27B (adapted to Janus)
Browse filesPorts the sibling's remaining runnable clients, adapted to Janus:
- examples/ollama_chat.py — chat/stream/tools/OpenAI-compat, MODEL=janus
- examples/transformers_quickstart.py — Heretic safetensors, 4-bit ~20 GB /
bf16 ~70 GB, MoE all-experts-resident note
- examples/llama_cpp_quickstart.py — GGUF via llama-cpp-python, no daemon
- examples/README.md — index rewritten for Janus's direct-script
flow (no Makefile/heal-hf; ./scripts/*.sh,
tag janus, Q4_K_M ~19 GB, mmproj ~903 MB)
README 'What's here' row consolidated to examples/; inline 'Inference
examples' now points at examples/. Supersedes the earlier
'text examples not duplicated' CHANGELOG note.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- CHANGELOG.md +19 -12
- README.md +2 -2
- examples/README.md +91 -0
- examples/llama_cpp_quickstart.py +84 -0
- examples/ollama_chat.py +214 -0
- examples/transformers_quickstart.py +127 -0
|
@@ -55,19 +55,26 @@ track the **tooling and documentation**, not the underlying base model.
|
|
| 55 |
dense sibling's exact wording and drops the ambiguous `262k` rounding
|
| 56 |
(262 144 = 256 × 1024, not 262 000).
|
| 57 |
|
| 58 |
-
### Added (vision tooling — parity with Thanatos-27B)
|
| 59 |
-
- **`
|
| 60 |
-
README section.**
|
| 61 |
-
bundle `Janus-35B-A3B.Q4_K_M.gguf`, arch `qwen35moe`,
|
| 62 |
-
`Qwen3.6-35B-A3B-uncensored-heretic-mmproj-BF16.gguf` (~903 MB,
|
| 63 |
-
from `llmfan46/Qwen3.6-35B-A3B-uncensored-heretic-GGUF`, with the
|
| 64 |
`unsloth/Qwen3.6-35B-A3B-GGUF` `mmproj-F16.gguf` as the F16/F32 fallback.
|
| 65 |
-
`
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
|
| 72 |
### Changed (HF repo rename → FoolDev/Janus-35B-HERETIC)
|
| 73 |
- **HF repository slug renamed `FoolDev/Janus-35B` → `FoolDev/Janus-35B-HERETIC`.**
|
|
|
|
| 55 |
dense sibling's exact wording and drops the ambiguous `262k` rounding
|
| 56 |
(262 144 = 256 × 1024, not 262 000).
|
| 57 |
|
| 58 |
+
### Added (examples/ suite + vision tooling — parity with Thanatos-27B)
|
| 59 |
+
- **`examples/` directory, `scripts/fetch_vision.sh`, and a `## Vision`
|
| 60 |
+
README section.** Ports the dense sibling's runnable clients and vision path,
|
| 61 |
+
adapted to Janus: bundle `Janus-35B-A3B.Q4_K_M.gguf`, arch `qwen35moe`,
|
| 62 |
+
projector `Qwen3.6-35B-A3B-uncensored-heretic-mmproj-BF16.gguf` (~903 MB,
|
| 63 |
+
BF16 only) from `llmfan46/Qwen3.6-35B-A3B-uncensored-heretic-GGUF`, with the
|
| 64 |
`unsloth/Qwen3.6-35B-A3B-GGUF` `mmproj-F16.gguf` as the F16/F32 fallback.
|
| 65 |
+
- `examples/ollama_chat.py` (chat / streaming / tool round-trip /
|
| 66 |
+
OpenAI-compat, default `MODEL=janus`), `examples/transformers_quickstart.py`
|
| 67 |
+
(Heretic safetensors, 4-bit ~20 GB / bf16 ~70 GB, MoE keeps all experts
|
| 68 |
+
resident), `examples/llama_cpp_quickstart.py` (GGUF, no daemon),
|
| 69 |
+
`examples/llama_cpp_vision.py` (image-text-to-text via llama-cpp-python),
|
| 70 |
+
and `examples/README.md`. The index is adapted to Janus's direct-script
|
| 71 |
+
flow (`./scripts/build.sh`, `./scripts/load_bundle.sh`,
|
| 72 |
+
`./scripts/fetch_vision.sh`) — Janus has no Makefile and, being already
|
| 73 |
+
`qwen35moe`-stamped, no heal/rebadge step.
|
| 74 |
+
- `fetch_vision.sh` pulls the projector; the Vision section documents the
|
| 75 |
+
llama.cpp loader path and the Ollama-vision-broken caveat (`unknown model
|
| 76 |
+
architecture: 'qwen35moe'`, ollama/ollama#15898).
|
| 77 |
+
- "What's here" table + "Inference examples" pointer updated.
|
| 78 |
|
| 79 |
### Changed (HF repo rename → FoolDev/Janus-35B-HERETIC)
|
| 80 |
- **HF repository slug renamed `FoolDev/Janus-35B` → `FoolDev/Janus-35B-HERETIC`.**
|
|
@@ -103,7 +103,7 @@ and `thinking` under Capabilities. Hardware: ~38 GB RAM at default
|
|
| 103 |
| `scripts/bench.sh` | Measures tok/s from Ollama's `eval_count` / `eval_duration` over a short/medium/long prompt mix (with a discarded warmup). Defaults to `MODEL=janus`. |
|
| 104 |
| `scripts/load_bundle.sh` | Loads the bundled `Janus-35B-A3B.Q4_K_M.gguf` into Ollama as a local `janus` tag without an upstream pull (smudges the LFS pointer via `hf download` if needed, checks the arch is `qwen35moe`). |
|
| 105 |
| `scripts/fetch_vision.sh` | Downloads the vision projector (`Qwen3.6-35B-A3B-uncensored-heretic-mmproj-BF16.gguf`) from the Heretic GGUF repo for llama.cpp image input (Ollama vision is broken upstream — see [Vision](#vision)). |
|
| 106 |
-
| `examples/
|
| 107 |
|
| 108 |
GGUF-only release. Pull the Heretic safetensors from [`llmfan46/Qwen3.6-35B-A3B-uncensored-heretic`](https://huggingface.co/llmfan46/Qwen3.6-35B-A3B-uncensored-heretic) if you need the `transformers` tree (or the vanilla pre-Heretic base from [`Qwen/Qwen3.6-35B-A3B`](https://huggingface.co/Qwen/Qwen3.6-35B-A3B)).
|
| 109 |
|
|
@@ -150,7 +150,7 @@ After either path, `ollama show janus` should list `completion`, `tools`, and `t
|
|
| 150 |
|
| 151 |
### Inference examples
|
| 152 |
|
| 153 |
-
Once the model is loaded (via `ollama run janus`, `lms server`, or `llama-server`), all the standard OpenAI-compatible clients work. Examples assume the loader is listening on `http://localhost:11434` (Ollama default) — adjust the port for LM Studio (`:1234`) or llama.cpp (`:8080`).
|
| 154 |
|
| 155 |
#### curl
|
| 156 |
|
|
|
|
| 103 |
| `scripts/bench.sh` | Measures tok/s from Ollama's `eval_count` / `eval_duration` over a short/medium/long prompt mix (with a discarded warmup). Defaults to `MODEL=janus`. |
|
| 104 |
| `scripts/load_bundle.sh` | Loads the bundled `Janus-35B-A3B.Q4_K_M.gguf` into Ollama as a local `janus` tag without an upstream pull (smudges the LFS pointer via `hf download` if needed, checks the arch is `qwen35moe`). |
|
| 105 |
| `scripts/fetch_vision.sh` | Downloads the vision projector (`Qwen3.6-35B-A3B-uncensored-heretic-mmproj-BF16.gguf`) from the Heretic GGUF repo for llama.cpp image input (Ollama vision is broken upstream — see [Vision](#vision)). |
|
| 106 |
+
| `examples/` | Ready-to-run Python clients for Ollama, Transformers, and llama-cpp-python (text, tools, and vision — see `examples/README.md`) |
|
| 107 |
|
| 108 |
GGUF-only release. Pull the Heretic safetensors from [`llmfan46/Qwen3.6-35B-A3B-uncensored-heretic`](https://huggingface.co/llmfan46/Qwen3.6-35B-A3B-uncensored-heretic) if you need the `transformers` tree (or the vanilla pre-Heretic base from [`Qwen/Qwen3.6-35B-A3B`](https://huggingface.co/Qwen/Qwen3.6-35B-A3B)).
|
| 109 |
|
|
|
|
| 150 |
|
| 151 |
### Inference examples
|
| 152 |
|
| 153 |
+
Once the model is loaded (via `ollama run janus`, `lms server`, or `llama-server`), all the standard OpenAI-compatible clients work. Examples assume the loader is listening on `http://localhost:11434` (Ollama default) — adjust the port for LM Studio (`:1234`) or llama.cpp (`:8080`). Runnable versions of everything below live in [`examples/`](examples/README.md).
|
| 154 |
|
| 155 |
#### curl
|
| 156 |
|
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Janus-35B examples
|
| 2 |
+
|
| 3 |
+
Four minimal entry points. Pick the one that matches how you run models.
|
| 4 |
+
|
| 5 |
+
| File | Backend | When to use |
|
| 6 |
+
|---|---|---|
|
| 7 |
+
| `ollama_chat.py` | Ollama HTTP API | You already have `ollama serve` running and the `janus` model created from the project `Modelfile`. **Text + tool calling** — vision via Ollama is broken upstream for this arch. |
|
| 8 |
+
| `transformers_quickstart.py` | Hugging Face Transformers | You want to run the Heretic safetensors (`llmfan46/Qwen3.6-35B-A3B-uncensored-heretic`) on GPU, optionally in 4-bit via bitsandbytes. |
|
| 9 |
+
| `llama_cpp_quickstart.py` | llama-cpp-python | You want to invoke a local GGUF directly without a daemon (CI, batch jobs, scripts). Text only. |
|
| 10 |
+
| `llama_cpp_vision.py` | llama-cpp-python + mmproj | **Image input.** Loads a text GGUF + `Qwen3.6-35B-A3B-uncensored-heretic-mmproj-BF16.gguf` and answers questions about an image. The only working vision path right now. |
|
| 11 |
+
|
| 12 |
+
All four apply the same Janus system prompt and sampling defaults
|
| 13 |
+
(`temp=0.6, top_p=0.95, top_k=20, repeat_penalty=1.05`) so behavior should
|
| 14 |
+
be consistent across backends modulo quantization noise. The three
|
| 15 |
+
non-Ollama scripts set them explicitly; `ollama_chat.py` inherits them
|
| 16 |
+
from the `Modelfile` / bridge files.
|
| 17 |
+
|
| 18 |
+
## Setup
|
| 19 |
+
|
| 20 |
+
### Ollama
|
| 21 |
+
|
| 22 |
+
Pull straight from HF (gets the bundled Q4_K_M GGUF + this repo's
|
| 23 |
+
root-level `template` / `system` / `params` files via HF's Ollama
|
| 24 |
+
bridge):
|
| 25 |
+
|
| 26 |
+
```bash
|
| 27 |
+
ollama pull hf.co/FoolDev/Janus-35B-HERETIC # ~19 GB Q4_K_M (only bundled quant)
|
| 28 |
+
pip install requests
|
| 29 |
+
MODEL=hf.co/FoolDev/Janus-35B-HERETIC python ollama_chat.py
|
| 30 |
+
```
|
| 31 |
+
|
| 32 |
+
Or build a local `janus` tag from this repo's bundled GGUF without going
|
| 33 |
+
through the HF pull:
|
| 34 |
+
|
| 35 |
+
```bash
|
| 36 |
+
cd .. && ./scripts/load_bundle.sh && cd examples
|
| 37 |
+
python ollama_chat.py # MODEL defaults to `janus`
|
| 38 |
+
```
|
| 39 |
+
|
| 40 |
+
For a non-bundled quant (e.g. Q3_K_M ~14 GB, Q5_K_M ~24 GB),
|
| 41 |
+
`./scripts/build.sh` downloads it from
|
| 42 |
+
`llmfan46/Qwen3.6-35B-A3B-uncensored-heretic-GGUF`, patches the `Modelfile`
|
| 43 |
+
`FROM` line into a temp copy, and creates the local `janus` tag:
|
| 44 |
+
|
| 45 |
+
```bash
|
| 46 |
+
cd .. && QUANT=Q5_K_M ./scripts/build.sh && cd examples
|
| 47 |
+
python ollama_chat.py
|
| 48 |
+
```
|
| 49 |
+
|
| 50 |
+
### Transformers (safetensors)
|
| 51 |
+
|
| 52 |
+
```bash
|
| 53 |
+
pip install --upgrade "transformers>=4.45" accelerate sentencepiece bitsandbytes
|
| 54 |
+
python transformers_quickstart.py # 4-bit, ~20 GB VRAM
|
| 55 |
+
python transformers_quickstart.py --no-4bit # bf16, ~70 GB VRAM
|
| 56 |
+
```
|
| 57 |
+
|
| 58 |
+
### llama-cpp-python (GGUF, no daemon)
|
| 59 |
+
|
| 60 |
+
```bash
|
| 61 |
+
pip install llama-cpp-python # CPU-only build
|
| 62 |
+
python llama_cpp_quickstart.py /path/to/Janus-35B-A3B.Q4_K_M.gguf --gpu-layers 99
|
| 63 |
+
```
|
| 64 |
+
|
| 65 |
+
For GPU offload, rebuild llama-cpp-python with the matching backend — see
|
| 66 |
+
the script header for `CMAKE_ARGS` recipes (CUDA, Metal, ROCm/HIP).
|
| 67 |
+
|
| 68 |
+
### Vision (image input)
|
| 69 |
+
|
| 70 |
+
```bash
|
| 71 |
+
# Pull the projector once (~903 MB) into the repo root:
|
| 72 |
+
cd .. && ./scripts/fetch_vision.sh && cd examples
|
| 73 |
+
|
| 74 |
+
pip install llama-cpp-python pillow
|
| 75 |
+
python llama_cpp_vision.py \
|
| 76 |
+
--gguf ../Janus-35B-A3B.Q4_K_M.gguf \
|
| 77 |
+
--mmproj ../Qwen3.6-35B-A3B-uncensored-heretic-mmproj-BF16.gguf \
|
| 78 |
+
--image /path/to/photo.jpg \
|
| 79 |
+
--prompt "Describe this image."
|
| 80 |
+
```
|
| 81 |
+
|
| 82 |
+
Why not Ollama? Ollama's Go engine has the `qwen35` / `qwen35moe`
|
| 83 |
+
arch entries (text inference works in 0.24+), but the C++ llama.cpp
|
| 84 |
+
fallback that Ollama switches to when an mmproj is attached still
|
| 85 |
+
lacks them. `ollama create` accepts the dual-`FROM` and `ollama show`
|
| 86 |
+
reports `vision` capability, but the first inference call fails with
|
| 87 |
+
`error loading model architecture: unknown model architecture:
|
| 88 |
+
'qwen35moe'`. Tracked in
|
| 89 |
+
[ollama/ollama#15898](https://github.com/ollama/ollama/issues/15898).
|
| 90 |
+
Until that's fixed, llama.cpp / llama-cpp-python is the working path
|
| 91 |
+
for vision.
|
|
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Janus-35B — llama-cpp-python quickstart.
|
| 4 |
+
|
| 5 |
+
Skip Ollama entirely and call the GGUF directly through llama-cpp-python.
|
| 6 |
+
Useful for batch jobs, CI, or environments where you don't want a daemon.
|
| 7 |
+
|
| 8 |
+
Install:
|
| 9 |
+
pip install llama-cpp-python
|
| 10 |
+
|
| 11 |
+
For GPU offload (CUDA / Metal / ROCm), install with the matching extras:
|
| 12 |
+
CMAKE_ARGS="-DGGML_CUDA=on" pip install llama-cpp-python --no-binary :all:
|
| 13 |
+
CMAKE_ARGS="-DGGML_METAL=on" pip install llama-cpp-python --no-binary :all:
|
| 14 |
+
CMAKE_ARGS="-DGGML_HIPBLAS=on" pip install llama-cpp-python --no-binary :all:
|
| 15 |
+
|
| 16 |
+
Usage:
|
| 17 |
+
python llama_cpp_quickstart.py /path/to/Janus-35B-A3B.Q4_K_M.gguf
|
| 18 |
+
python llama_cpp_quickstart.py /path/to/file.gguf --gpu-layers 99
|
| 19 |
+
python llama_cpp_quickstart.py /path/to/file.gguf --prompt "..."
|
| 20 |
+
"""
|
| 21 |
+
from __future__ import annotations
|
| 22 |
+
|
| 23 |
+
import argparse
|
| 24 |
+
import sys
|
| 25 |
+
|
| 26 |
+
try:
|
| 27 |
+
from llama_cpp import Llama
|
| 28 |
+
except ImportError: # pragma: no cover
|
| 29 |
+
sys.exit("Missing llama-cpp-python. Install with: pip install llama-cpp-python")
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
JANUS_SYSTEM = (
|
| 33 |
+
"You are Janus, a precise and capable assistant for reasoning, writing, "
|
| 34 |
+
"coding, and long-form dialogue.\n\n"
|
| 35 |
+
"Behavior rules:\n"
|
| 36 |
+
"- Answer the user's actual request directly.\n"
|
| 37 |
+
"- Be accurate, complete, and structured.\n"
|
| 38 |
+
"- Think before answering, but do not get stuck in repetitive loops.\n"
|
| 39 |
+
"- If the request is ambiguous, state what is missing and make the smallest "
|
| 40 |
+
"reasonable assumption needed to continue.\n"
|
| 41 |
+
"- Finish with a usable answer, not just planning."
|
| 42 |
+
)
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def main() -> None:
|
| 46 |
+
ap = argparse.ArgumentParser()
|
| 47 |
+
ap.add_argument("gguf", help="Path to Janus-35B-A3B GGUF (e.g. Q4_K_M).")
|
| 48 |
+
ap.add_argument(
|
| 49 |
+
"--prompt",
|
| 50 |
+
default="Explain the Burrows-Wheeler transform in 200 words.",
|
| 51 |
+
)
|
| 52 |
+
ap.add_argument("--ctx", type=int, default=16384, help="Context window.")
|
| 53 |
+
ap.add_argument(
|
| 54 |
+
"--gpu-layers",
|
| 55 |
+
type=int,
|
| 56 |
+
default=0,
|
| 57 |
+
help="Layers to offload to GPU (-1 or 99 = all).",
|
| 58 |
+
)
|
| 59 |
+
ap.add_argument("--max-tokens", type=int, default=512)
|
| 60 |
+
args = ap.parse_args()
|
| 61 |
+
|
| 62 |
+
llm = Llama(
|
| 63 |
+
model_path=args.gguf,
|
| 64 |
+
n_ctx=args.ctx,
|
| 65 |
+
n_gpu_layers=args.gpu_layers,
|
| 66 |
+
verbose=False,
|
| 67 |
+
)
|
| 68 |
+
|
| 69 |
+
out = llm.create_chat_completion(
|
| 70 |
+
messages=[
|
| 71 |
+
{"role": "system", "content": JANUS_SYSTEM},
|
| 72 |
+
{"role": "user", "content": args.prompt},
|
| 73 |
+
],
|
| 74 |
+
temperature=0.6,
|
| 75 |
+
top_p=0.95,
|
| 76 |
+
top_k=20,
|
| 77 |
+
repeat_penalty=1.05,
|
| 78 |
+
max_tokens=args.max_tokens,
|
| 79 |
+
)
|
| 80 |
+
print(out["choices"][0]["message"]["content"])
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
if __name__ == "__main__":
|
| 84 |
+
main()
|
|
@@ -0,0 +1,214 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Janus-35B — Ollama chat examples.
|
| 4 |
+
|
| 5 |
+
Prerequisites (pick one):
|
| 6 |
+
|
| 7 |
+
A. From the bundled GGUF (default flow):
|
| 8 |
+
$ ../scripts/load_bundle.sh # local `janus` tag from the bundled Q4_K_M
|
| 9 |
+
# or build another quant:
|
| 10 |
+
$ ../scripts/build.sh # default Q4_K_M -> `ollama create janus`
|
| 11 |
+
# or straight from the Modelfile:
|
| 12 |
+
$ ollama create janus -f ../Modelfile
|
| 13 |
+
|
| 14 |
+
B. Pull straight from HF (Q4_K_M is the only bundled quant):
|
| 15 |
+
$ ollama run hf.co/FoolDev/Janus-35B-HERETIC
|
| 16 |
+
# then set MODEL=hf.co/FoolDev/Janus-35B-HERETIC below
|
| 17 |
+
|
| 18 |
+
Then:
|
| 19 |
+
$ ollama serve # usually already running
|
| 20 |
+
$ python ollama_chat.py
|
| 21 |
+
|
| 22 |
+
The model emits <think>...</think> reasoning blocks before its answer.
|
| 23 |
+
Current Ollama (0.24, especially with `OLLAMA_NEW_ENGINE=1`) returns the
|
| 24 |
+
reasoning in a separate `message.thinking` field and keeps `content`
|
| 25 |
+
clean. Older builds put the whole `<think>...</think>` block inside
|
| 26 |
+
`content`. The demo below reads `message.thinking` first and falls
|
| 27 |
+
back to parsing `<think>` tags out of `content` so it works against
|
| 28 |
+
either path.
|
| 29 |
+
|
| 30 |
+
Endpoints used:
|
| 31 |
+
- Native Ollama: http://localhost:11434/api/chat
|
| 32 |
+
- OpenAI-compat: http://localhost:11434/v1/chat/completions
|
| 33 |
+
"""
|
| 34 |
+
from __future__ import annotations
|
| 35 |
+
|
| 36 |
+
import json
|
| 37 |
+
import os
|
| 38 |
+
import re
|
| 39 |
+
import sys
|
| 40 |
+
from typing import Any, Iterator
|
| 41 |
+
|
| 42 |
+
import requests
|
| 43 |
+
|
| 44 |
+
MODEL = os.environ.get("MODEL", "janus")
|
| 45 |
+
HOST = os.environ.get("HOST", "http://localhost:11434")
|
| 46 |
+
|
| 47 |
+
_THINK_RE = re.compile(r"<think>.*?</think>\s*", re.DOTALL)
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def split_thinking(content: str) -> tuple[str, str]:
|
| 51 |
+
"""Return (thinking, final_answer) from a content string."""
|
| 52 |
+
parts = re.findall(r"<think>(.*?)</think>", content, re.DOTALL)
|
| 53 |
+
thinking = "\n".join(p.strip() for p in parts).strip()
|
| 54 |
+
answer = _THINK_RE.sub("", content).strip()
|
| 55 |
+
return thinking, answer
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
# ---------- 1. Simple chat ----------
|
| 59 |
+
|
| 60 |
+
def chat(prompt: str, system: str | None = None) -> dict[str, Any]:
|
| 61 |
+
msgs: list[dict[str, Any]] = []
|
| 62 |
+
if system:
|
| 63 |
+
msgs.append({"role": "system", "content": system})
|
| 64 |
+
msgs.append({"role": "user", "content": prompt})
|
| 65 |
+
r = requests.post(
|
| 66 |
+
f"{HOST}/api/chat",
|
| 67 |
+
json={"model": MODEL, "messages": msgs, "stream": False},
|
| 68 |
+
timeout=600,
|
| 69 |
+
)
|
| 70 |
+
r.raise_for_status()
|
| 71 |
+
return r.json()
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
# ---------- 2. Streaming ----------
|
| 75 |
+
|
| 76 |
+
def chat_stream(prompt: str) -> Iterator[str]:
|
| 77 |
+
"""Yield content tokens as they arrive."""
|
| 78 |
+
with requests.post(
|
| 79 |
+
f"{HOST}/api/chat",
|
| 80 |
+
json={
|
| 81 |
+
"model": MODEL,
|
| 82 |
+
"messages": [{"role": "user", "content": prompt}],
|
| 83 |
+
"stream": True,
|
| 84 |
+
},
|
| 85 |
+
stream=True,
|
| 86 |
+
timeout=600,
|
| 87 |
+
) as r:
|
| 88 |
+
r.raise_for_status()
|
| 89 |
+
for line in r.iter_lines():
|
| 90 |
+
if not line:
|
| 91 |
+
continue
|
| 92 |
+
chunk = json.loads(line)
|
| 93 |
+
if "message" in chunk and "content" in chunk["message"]:
|
| 94 |
+
yield chunk["message"]["content"]
|
| 95 |
+
if chunk.get("done"):
|
| 96 |
+
break
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
# ---------- 3. Tool calling ----------
|
| 100 |
+
|
| 101 |
+
WEATHER_TOOL = {
|
| 102 |
+
"type": "function",
|
| 103 |
+
"function": {
|
| 104 |
+
"name": "get_current_weather",
|
| 105 |
+
"description": "Get the current weather in a given city",
|
| 106 |
+
"parameters": {
|
| 107 |
+
"type": "object",
|
| 108 |
+
"properties": {
|
| 109 |
+
"city": {"type": "string", "description": "City name"},
|
| 110 |
+
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
|
| 111 |
+
},
|
| 112 |
+
"required": ["city", "unit"],
|
| 113 |
+
},
|
| 114 |
+
},
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
def fake_weather(city: str, unit: str) -> str:
|
| 119 |
+
"""Stand-in tool implementation."""
|
| 120 |
+
return json.dumps(
|
| 121 |
+
{"city": city, "temperature": 14, "unit": unit, "conditions": "light rain"}
|
| 122 |
+
)
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
def tool_round_trip(prompt: str) -> str:
|
| 126 |
+
"""Single-shot tool call: model -> tool -> model -> final answer."""
|
| 127 |
+
history: list[dict[str, Any]] = [{"role": "user", "content": prompt}]
|
| 128 |
+
r = requests.post(
|
| 129 |
+
f"{HOST}/api/chat",
|
| 130 |
+
json={
|
| 131 |
+
"model": MODEL,
|
| 132 |
+
"messages": history,
|
| 133 |
+
"tools": [WEATHER_TOOL],
|
| 134 |
+
"stream": False,
|
| 135 |
+
},
|
| 136 |
+
timeout=600,
|
| 137 |
+
)
|
| 138 |
+
r.raise_for_status()
|
| 139 |
+
msg = r.json()["message"]
|
| 140 |
+
|
| 141 |
+
if not msg.get("tool_calls"):
|
| 142 |
+
return msg["content"]
|
| 143 |
+
|
| 144 |
+
history.append({"role": "assistant", "tool_calls": msg["tool_calls"]})
|
| 145 |
+
for tc in msg["tool_calls"]:
|
| 146 |
+
fn = tc["function"]
|
| 147 |
+
if fn["name"] == "get_current_weather":
|
| 148 |
+
result = fake_weather(**fn["arguments"])
|
| 149 |
+
else:
|
| 150 |
+
result = json.dumps({"error": f"unknown tool {fn['name']}"})
|
| 151 |
+
history.append({"role": "tool", "tool_name": fn["name"], "content": result})
|
| 152 |
+
|
| 153 |
+
r = requests.post(
|
| 154 |
+
f"{HOST}/api/chat",
|
| 155 |
+
json={
|
| 156 |
+
"model": MODEL,
|
| 157 |
+
"messages": history,
|
| 158 |
+
"tools": [WEATHER_TOOL],
|
| 159 |
+
"stream": False,
|
| 160 |
+
},
|
| 161 |
+
timeout=600,
|
| 162 |
+
)
|
| 163 |
+
r.raise_for_status()
|
| 164 |
+
return r.json()["message"]["content"]
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
# ---------- 4. OpenAI-compatible endpoint ----------
|
| 168 |
+
|
| 169 |
+
def openai_chat(prompt: str) -> str:
|
| 170 |
+
r = requests.post(
|
| 171 |
+
f"{HOST}/v1/chat/completions",
|
| 172 |
+
json={
|
| 173 |
+
"model": MODEL,
|
| 174 |
+
"messages": [{"role": "user", "content": prompt}],
|
| 175 |
+
"temperature": 0.6,
|
| 176 |
+
},
|
| 177 |
+
timeout=600,
|
| 178 |
+
)
|
| 179 |
+
r.raise_for_status()
|
| 180 |
+
return r.json()["choices"][0]["message"]["content"]
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
# ---------- demo ----------
|
| 184 |
+
|
| 185 |
+
def _demo() -> None:
|
| 186 |
+
print("=== 1. simple chat ===")
|
| 187 |
+
resp = chat("What is 84 * 3 / 2?")
|
| 188 |
+
msg = resp["message"]
|
| 189 |
+
# Prefer the dedicated `thinking` field (Ollama 0.24+ / new engine);
|
| 190 |
+
# fall back to extracting <think>...</think> from `content` for
|
| 191 |
+
# older builds that inline the reasoning.
|
| 192 |
+
thinking = (msg.get("thinking") or "").strip()
|
| 193 |
+
answer = msg.get("content", "")
|
| 194 |
+
if not thinking:
|
| 195 |
+
thinking, answer = split_thinking(answer)
|
| 196 |
+
if thinking:
|
| 197 |
+
print(f"[thinking] {thinking[:200]}...")
|
| 198 |
+
print(f"[answer] {answer}")
|
| 199 |
+
|
| 200 |
+
print("\n=== 2. streaming ===")
|
| 201 |
+
for tok in chat_stream("Count from 1 to 5 in one line."):
|
| 202 |
+
sys.stdout.write(tok)
|
| 203 |
+
sys.stdout.flush()
|
| 204 |
+
print()
|
| 205 |
+
|
| 206 |
+
print("\n=== 3. tool round-trip ===")
|
| 207 |
+
print(tool_round_trip("What is the weather in Paris in celsius?"))
|
| 208 |
+
|
| 209 |
+
print("\n=== 4. OpenAI-compat ===")
|
| 210 |
+
print(openai_chat("Say 'OpenAI endpoint OK' and nothing else."))
|
| 211 |
+
|
| 212 |
+
|
| 213 |
+
if __name__ == "__main__":
|
| 214 |
+
_demo()
|
|
@@ -0,0 +1,127 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""
|
| 3 |
+
Janus-35B — Hugging Face Transformers quickstart.
|
| 4 |
+
|
| 5 |
+
Loads the Heretic Qwen 3.6 35B-A3B safetensors directly and runs a single
|
| 6 |
+
chat turn using its embedded chat template. Janus-35B is a *wrapper*
|
| 7 |
+
around that base, so for the transformers route there is nothing to
|
| 8 |
+
download from this repo — point at llmfan46/Qwen3.6-35B-A3B-uncensored-heretic
|
| 9 |
+
and apply the same system prompt the Modelfile uses.
|
| 10 |
+
|
| 11 |
+
Set MODEL_ID = "Qwen/Qwen3.6-35B-A3B" to bypass the Heretic abliteration and
|
| 12 |
+
load the vanilla upstream base instead.
|
| 13 |
+
|
| 14 |
+
Note: this is a mixture-of-experts model — 35B total / 3B active per token.
|
| 15 |
+
Transformers keeps all 256 experts resident, so memory tracks the 35B total,
|
| 16 |
+
not the 3B active count.
|
| 17 |
+
|
| 18 |
+
Requirements:
|
| 19 |
+
pip install --upgrade "transformers>=4.45" accelerate sentencepiece bitsandbytes
|
| 20 |
+
|
| 21 |
+
Memory:
|
| 22 |
+
- bf16 full precision: ~70 GB VRAM (multi-GPU or CPU offload).
|
| 23 |
+
- 4-bit (bitsandbytes nf4): ~20 GB VRAM, fits a 24 GB card (3090/4090)
|
| 24 |
+
at modest context.
|
| 25 |
+
- Fall back to device_map="auto" + bnb_4bit on consumer GPUs.
|
| 26 |
+
|
| 27 |
+
Usage:
|
| 28 |
+
python transformers_quickstart.py
|
| 29 |
+
python transformers_quickstart.py --no-4bit # bf16, needs ~70 GB VRAM
|
| 30 |
+
python transformers_quickstart.py --prompt "..." # custom prompt
|
| 31 |
+
"""
|
| 32 |
+
from __future__ import annotations
|
| 33 |
+
|
| 34 |
+
import argparse
|
| 35 |
+
import sys
|
| 36 |
+
|
| 37 |
+
try:
|
| 38 |
+
import torch
|
| 39 |
+
from transformers import AutoModelForCausalLM, AutoTokenizer
|
| 40 |
+
except ImportError as e: # pragma: no cover
|
| 41 |
+
sys.exit(
|
| 42 |
+
f"Missing dependency: {e.name}. Install with:\n"
|
| 43 |
+
" pip install --upgrade 'transformers>=4.45' accelerate sentencepiece bitsandbytes"
|
| 44 |
+
)
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
MODEL_ID = "llmfan46/Qwen3.6-35B-A3B-uncensored-heretic"
|
| 48 |
+
|
| 49 |
+
JANUS_SYSTEM = (
|
| 50 |
+
"You are Janus, a precise and capable assistant for reasoning, writing, "
|
| 51 |
+
"coding, and long-form dialogue.\n\n"
|
| 52 |
+
"Behavior rules:\n"
|
| 53 |
+
"- Answer the user's actual request directly.\n"
|
| 54 |
+
"- Be accurate, complete, and structured.\n"
|
| 55 |
+
"- Think before answering, but do not get stuck in repetitive loops or "
|
| 56 |
+
"meta-commentary.\n"
|
| 57 |
+
"- If the request is ambiguous or incomplete, state what is missing and "
|
| 58 |
+
"make the smallest reasonable assumption needed to continue.\n"
|
| 59 |
+
"- If the user wants creative writing, preserve tone, continuity, and "
|
| 60 |
+
"character consistency.\n"
|
| 61 |
+
"- If the user wants analysis or technical help, prefer concrete steps, "
|
| 62 |
+
"examples, and decisions over fluff.\n"
|
| 63 |
+
"- Finish with a usable answer, not just planning."
|
| 64 |
+
)
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def load(use_4bit: bool):
|
| 68 |
+
kwargs: dict = {"device_map": "auto", "torch_dtype": torch.bfloat16}
|
| 69 |
+
if use_4bit:
|
| 70 |
+
from transformers import BitsAndBytesConfig
|
| 71 |
+
kwargs["quantization_config"] = BitsAndBytesConfig(
|
| 72 |
+
load_in_4bit=True,
|
| 73 |
+
bnb_4bit_quant_type="nf4",
|
| 74 |
+
bnb_4bit_compute_dtype=torch.bfloat16,
|
| 75 |
+
bnb_4bit_use_double_quant=True,
|
| 76 |
+
)
|
| 77 |
+
kwargs.pop("torch_dtype", None)
|
| 78 |
+
|
| 79 |
+
tok = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True)
|
| 80 |
+
model = AutoModelForCausalLM.from_pretrained(MODEL_ID, trust_remote_code=True, **kwargs)
|
| 81 |
+
return tok, model
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def generate(tok, model, prompt: str, max_new_tokens: int = 512) -> str:
|
| 85 |
+
messages = [
|
| 86 |
+
{"role": "system", "content": JANUS_SYSTEM},
|
| 87 |
+
{"role": "user", "content": prompt},
|
| 88 |
+
]
|
| 89 |
+
inputs = tok.apply_chat_template(
|
| 90 |
+
messages,
|
| 91 |
+
add_generation_prompt=True,
|
| 92 |
+
return_tensors="pt",
|
| 93 |
+
).to(model.device)
|
| 94 |
+
|
| 95 |
+
out = model.generate(
|
| 96 |
+
inputs,
|
| 97 |
+
max_new_tokens=max_new_tokens,
|
| 98 |
+
do_sample=True,
|
| 99 |
+
temperature=0.6,
|
| 100 |
+
top_p=0.95,
|
| 101 |
+
top_k=20,
|
| 102 |
+
repetition_penalty=1.05,
|
| 103 |
+
)
|
| 104 |
+
return tok.decode(out[0][inputs.shape[-1]:], skip_special_tokens=True)
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def main() -> None:
|
| 108 |
+
ap = argparse.ArgumentParser()
|
| 109 |
+
ap.add_argument("--prompt", default="Explain the Burrows-Wheeler transform in 200 words.")
|
| 110 |
+
ap.add_argument(
|
| 111 |
+
"--no-4bit",
|
| 112 |
+
action="store_true",
|
| 113 |
+
help="Disable 4-bit quantization (requires ~70 GB VRAM in bf16).",
|
| 114 |
+
)
|
| 115 |
+
ap.add_argument("--max-new-tokens", type=int, default=512)
|
| 116 |
+
args = ap.parse_args()
|
| 117 |
+
|
| 118 |
+
print(f"[load] {MODEL_ID} (4bit={'no' if args.no_4bit else 'yes'})")
|
| 119 |
+
tok, model = load(use_4bit=not args.no_4bit)
|
| 120 |
+
|
| 121 |
+
print(f"[gen] prompt: {args.prompt!r}")
|
| 122 |
+
print()
|
| 123 |
+
print(generate(tok, model, args.prompt, args.max_new_tokens))
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
if __name__ == "__main__":
|
| 127 |
+
main()
|