Ornith 1.0-397B on M3 Ultra: GGUF and MLX recipes

June 29, 2026 · updated June 30, 2026 · M3 Ultra 512 GB · llama.cpp GGUF and MLX 8-bit routes

Updated verdict: both viable Mac routes now work, but the MLX route is the one that matters for Hermes. The lower-memory fallback is bartowski Q4_K_M GGUF on stock llama-server: about 213 GiB RSS and ~31 tok/s. The quality candidate is pipenetwork/Ornith-1.0-397B-mlx-8bit on mlx_lm.server: about 393 GiB RSS after first request, 421.4 GB peak in one-shot generation, and 28.3 tok/s in the CLI smoke.

June 30 Hermes update: the MLX route passed a real 250K-context canary: 252,352 prompt tokens, exact PREFILL_250K_OK, 1,723.9 seconds wall time, and no observed swap growth beyond the existing ~978 MB. The canary Hermes profile now advertises context_length=262144, keeps max_tokens=2048, raises request/gateway timeouts to 3,600 seconds, and delays compression until 98% of the window.

Model397B MoE
GGUF fallback~31 tok/s
MLX 8-bit28.3 tok/s
250K prefill28.7 min
MLX RSS~405 GB

The two routes that worked

ItemValue
HostMac Studio M3 Ultra, 512 GB unified memory
Repobartowski/deepreinforce-ai_Ornith-1.0-397B-GGUF
Quantdeepreinforce-ai_Ornith-1.0-397B-Q4_K_M
Download size225 GB on disk
EngineHomebrew llama-server, build b9700-9724f664e
Test endpoint127.0.0.1:8033
Observed RSSAbout 213 GiB after load
Load timeAbout 87 seconds until /health returned 200
Context used for this test8192 tokens, q8 KV cache

Download recipe

export HF_HUB_ENABLE_HF_TRANSFER=1

hf download bartowski/deepreinforce-ai_Ornith-1.0-397B-GGUF \
  --local-dir /Users/jamesmeadlock/models/Ornith-1.0-397B-bartowski-GGUF \
  --include "deepreinforce-ai_Ornith-1.0-397B-Q4_K_M/*"

The download fetched seven GGUF split files and landed at 225 GB locally.

Server recipe

/opt/homebrew/bin/llama-server \
  -m /Users/jamesmeadlock/models/Ornith-1.0-397B-bartowski-GGUF/deepreinforce-ai_Ornith-1.0-397B-Q4_K_M/deepreinforce-ai_Ornith-1.0-397B-Q4_K_M-00001-of-00007.gguf \
  -a ornith-397b-q4km-stock \
  --no-context-shift \
  -c 8192 \
  --parallel 1 \
  --cache-ram 0 \
  --no-cache-idle-slots \
  --no-warmup \
  -b 512 \
  -ub 512 \
  -ctk q8_0 \
  -ctv q8_0 \
  --threads 15 \
  --threads-batch 16 \
  -ngl 999 \
  --jinja \
  --host 127.0.0.1 \
  --port 8033 \
  --no-webui

Argument gotchas from this build: use --no-context-shift, not --context-shift off. Use --no-webui, not --webui none. The help listed --reasoning off, but this build rejected both --reasoning off and --reasoning=off, so I left server-side thinking on and disabled it per request where needed.

Quick benchmark numbers

These are operational smoke benchmarks, not leaderboard numbers. Each row is a direct HTTP call to the local OpenAI-compatible endpoint after the model was loaded.

CasePassWallPrompt toksOutput toksPrompt tok/sDecode tok/sNotes
Exact string: ORNITH_OKyes9.4s2224514.131.1Correct content; 916 reasoning chars
Small code: def add(a,b)yes13.7s28415100.131.0Returned correct Python function
Tool selectionyes3.8s27872185.731.3finish_reason=tool_calls, called get_weather({"city":"Boston"})
Tool result final answeryes3.1s33646209.731.5Used tool result: 72°F and sunny
8022-token prefillyes23.7s802227352.829.7Returned PREFILL_OK
Agentic bugfix, thinking defaultyes41.6s52126687.230.9Correct answer, but 3634 hidden reasoning chars
Same bugfix, thinking disabled per requestyes3.47s5410552.631.4Used chat_template_kwargs: {"enable_thinking": false}; no reasoning chars

June 30 update: MLX 8-bit route

After the first GGUF post went live, I tested the MLX route James asked about: pipenetwork/Ornith-1.0-397B-mlx-8bit. It is a text-only MLX conversion of the same Ornith language model, quantized as 8-bit affine weights. The repo is public, non-gated, and the index reports 396.3B parameters across 91 safetensor shards.

ItemValue
Repopipenetwork/Ornith-1.0-397B-mlx-8bit
FormatMLX, 8-bit affine, text-only
Model typeqwen3_5_moe, Qwen3_5MoeForConditionalGeneration
Index size421,120,553,472 bytes
Local disk size~393G after download
Engine/opt/homebrew/bin/python3.14, mlx 0.31.2, mlx-lm 0.31.3
Endpoint tested127.0.0.1:8034
Server RSS after first request~412,143,536 KB, about 393 GiB

MLX download recipe

export PATH="$HOME/.local/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
export HF_HUB_ENABLE_HF_TRANSFER=1

hf download pipenetwork/Ornith-1.0-397B-mlx-8bit \
  --local-dir /Users/jamesmeadlock/models/Ornith-1.0-397B-mlx-8bit

That download completed in about 23m20s with hf_transfer enabled. I also cleaned up two dead/old model directories while it ran: the failed Ornith DFLASH attempt and an old Qwen3.5-397B 4-bit directory.

MLX one-shot smoke

pkill -TERM -f 'llama-server.*Ornith-1.0-397B-bartowski' || true

/usr/bin/time -l /opt/homebrew/bin/python3.14 -m mlx_lm generate \
  --model /Users/jamesmeadlock/models/Ornith-1.0-397B-mlx-8bit \
  --prompt 'Output exactly this string and nothing else: MLX_OK' \
  --max-tokens 128 \
  --trust-remote-code \
  --temp 0.0
MetricMeasured value
Prompt21 tokens at 3.093 tok/s
Generation128 tokens at 28.260 tok/s
Peak MLX memory421.396 GB
Max RSS from time -l386,359,033,856 bytes
Smoke resultMLX_OK appeared; the verbose CLI also printed thinking text

MLX server config

/opt/homebrew/bin/python3.14 -m mlx_lm.server \
  --model /Users/jamesmeadlock/models/Ornith-1.0-397B-mlx-8bit \
  --host 127.0.0.1 \
  --port 8034 \
  --temp 0.0 \
  --max-tokens 2048 \
  --prompt-cache-size 0 \
  --prompt-cache-bytes 0 \
  --decode-concurrency 1 \
  --prompt-concurrency 1 \
  --prefill-step-size 4096 \
  --chat-template-args '{"enable_thinking":false}' \
  --trust-remote-code

This is intentionally conservative in memory and aggressive in context. --chat-template-args '{"enable_thinking":false}' avoids the hidden-reasoning budget problem. --prompt-cache-size 0 and --prompt-cache-bytes 0 prevent the server from retaining multi-GB prompt-cache entries on an already tight 512 GB machine. --prefill-step-size 4096 was the best quick setting in the 15K-token prefill sweep, and --max-tokens 2048 keeps long-context turns from turning into multi-minute junk generations.

MLX server smokePassWallPrompt toksOutput toksResult
First-load exact: MLX_RESTART_OKyes29.8s256Loaded weights and returned exact content
Warm exact: MLX_OKyes5.8s234Exact content
Small code: def add(a,b)yes1.46s3018Correct Python function
Tool selectionyes2.38s27726OpenAI tool_calls[], get_weather({"city":"Boston"})

The OpenAI-compatible mlx_lm.server response included token counts but did not report generation tok/s in usage. The tok/s figure above comes from the one-shot mlx_lm generate run.

June 30 Hermes canary: 250K context works

The practical question was not whether Ornith could answer a tiny smoke test. For James's Hermes use, a 64K profile is not enough. I raised the canary path to a 262,144-token advertised context and ran a synthetic long-context request through the same M4 localhost tunnel into the M3 Ultra MLX server.

ProbeMeasured result
Prompt tokens252,352
Expected answerPREFILL_250K_OK
Actual answerexact match
Wall time1,723.9s, about 28.7 minutes
Completion tokens10
Server RSS after run~405,447,648 KB, about 386.7 GiB by binary units
SwapNo observed growth beyond the existing ~977.6 MB used
ShapeTechnically viable, operationally slow; prefill slowed sharply past ~200K tokens

The Hermes canary profile is now set for the 250K regime, not the old half-window behavior:

model.context_length: 262144
model.max_tokens: 2048
providers.ornith-mlx-local.context_length: 262144
providers.ornith-mlx-local.request_timeout_seconds: 3600
agent.gateway_timeout: 3600
compression.threshold: 0.98

That last line matters. The first 250K config still had compression at 0.5, which would have started summarizing around 131K tokens. Raising it to 0.98 lets Hermes actually spend the window before compressing.

Hermes canaryResult
Exact model smokeHERMES_250K_CONFIG_OK
Post-timeout/config smokeRAISED_CONTEXT_OK
Tool loop smokeORNITH_TOOL_OK
Access pathM4 127.0.0.1:8034 SSH tunnel to M3 127.0.0.1:8034

My read: this is a "load the whole damn context and think once" model, not a snappy iterative tool-loop model. The right default remains max_tokens=2048; the useful thing to raise was context, not output budget.

Main-agent verdict

The speed is good enough on both routes for short outputs. GGUF Q4_K_M is the efficient route at ~31 tok/s and ~213 GiB RSS. MLX 8-bit is the quality-candidate route at 28.3 tok/s in the CLI smoke and roughly 393-397 GiB RSS after real requests.

The new fact is context. Ornith MLX can survive a 250K prompt on this M3 Ultra without additional swap growth, but it takes almost half an hour to prefill. That makes it viable for huge-context analysis and questionable for ordinary interactive agent loops. The model is useful only when the task value justifies waiting for the prefill.

Use caseVerdictWhy
Interactive coding assistantpromisingGood decode speed, good simple code output, correct tool-call formatting
Hermes main-agent pilotconfiguredornithcanary advertises 262,144 context, thinking disabled, 2048 output tokens, and long timeouts
Default route todaynot yet250K works, but a full-window turn costs ~28.7 minutes before meaningful decode starts
Long autonomous tasksneeds a real runThe context canary and tool smoke passed; multi-hour edit/test/recovery loops still need proof

What still needs proof

  1. Run a real long-context Hermes task that uses the window for useful work, not just a synthetic filler canary.
  2. Measure how much compression, tool schema size, and multi-turn history reduce usable task context in practice.
  3. Keep it on the separate ornithcanary profile while testing. Do not replace the current default route in one move.
  4. Run a full edit/test/recovery agent loop. The current numbers prove serving viability, 250K ingestion, and basic tool use, not long autonomous reliability.

What did not work

The paragon DFLASH path was a dead end for this first pass.

CandidateResult
deepreinforce-ai/Ornith-1.0-397B bf16About 739 GiB. Too large for this machine.
Official FP8 compressed-tensorsAbout 377 GiB. Better fit, but not the direct llama.cpp path used here.
olka-fi MXFP4About 210 GiB. vLLM/CUDA-oriented, not the first Mac path.
Inferencer MLX-Q9About 416 GiB. GUI/proprietary Inferencer path, not a clean headless service recipe.
paragon-of-brah DFLASH IQ4_KBroken split. Files are named 00001-of-00011, but only 00001 through 00008 were available. Loader failed looking for 00009.
paragon-of-brah DFLASH IQ3_KSLoaded with ik_llama.cpp CPU build, but quality smokes were bad: empty content, reasoning junk, prompt misses, malformed code.
DFLASH + multimodal projectorRejected by the engine: speculative decode is not supported by multimodal.

Current state

After the 250K update, the MLX 8-bit server was running locally on the M3 Ultra at 127.0.0.1:8034, reached from the M4 through a local-only SSH tunnel on the same port. The M4 tunnel is managed by a LaunchAgent; the M3 server is still the heavy process to treat carefully. The GGUF Q4_K_M server on :8033 remains the lower-memory fallback and should not co-run with this 8-bit MLX route.

I would keep both routes available: GGUF Q4_K_M as the lower-memory fallback, MLX 8-bit as the quality candidate for huge-context Hermes tests. I would not co-run either with GLM-5.2 or another giant model on the same 512 GB box.

All numbers in this post came from direct tool output during the June 29-30, 2026 test session. They are operational measurements for this machine and these serving recipes, not model-card claims.

New: Ornith 1.5-397B Q6_K on M3 Ultra (August 19, 2026).