Text Generation
MLX
Safetensors
qwen3_5_moe
quantized
vq
aqlm
codebook
mixed-bit
Mixture of Experts
custom-code
conversational
4-bit precision
Instructions to use aquaman164/Qwen3.6-35B-A3B-MLX-VQ-2.6bpw with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- MLX
How to use aquaman164/Qwen3.6-35B-A3B-MLX-VQ-2.6bpw with MLX:
# Make sure mlx-lm is installed # pip install --upgrade mlx-lm # Generate text with mlx-lm from mlx_lm import load, generate model, tokenizer = load("aquaman164/Qwen3.6-35B-A3B-MLX-VQ-2.6bpw") prompt = "Write a story about Einstein" messages = [{"role": "user", "content": prompt}] prompt = tokenizer.apply_chat_template( messages, add_generation_prompt=True ) text = generate(model, tokenizer, prompt=prompt, verbose=True) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- LM Studio
- Pi
How to use aquaman164/Qwen3.6-35B-A3B-MLX-VQ-2.6bpw with Pi:
Start the MLX server
# Install MLX LM: uv tool install mlx-lm # Start a local OpenAI-compatible server: mlx_lm.server --model "aquaman164/Qwen3.6-35B-A3B-MLX-VQ-2.6bpw"
Configure the model in Pi
# Install Pi: npm install -g @earendil-works/pi-coding-agent # Add to ~/.pi/agent/models.json: { "providers": { "mlx-lm": { "baseUrl": "http://localhost:8080/v1", "api": "openai-completions", "apiKey": "none", "models": [ { "id": "aquaman164/Qwen3.6-35B-A3B-MLX-VQ-2.6bpw" } ] } } }Run Pi
# Start Pi in your project directory: pi
- MLX LM
How to use aquaman164/Qwen3.6-35B-A3B-MLX-VQ-2.6bpw with MLX LM:
Generate or start a chat session
# Install MLX LM uv tool install mlx-lm # Interactive chat REPL mlx_lm.chat --model "aquaman164/Qwen3.6-35B-A3B-MLX-VQ-2.6bpw"
Run an OpenAI-compatible server
# Install MLX LM uv tool install mlx-lm # Start the server mlx_lm.server --model "aquaman164/Qwen3.6-35B-A3B-MLX-VQ-2.6bpw" # Calling the OpenAI-compatible server with curl curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "aquaman164/Qwen3.6-35B-A3B-MLX-VQ-2.6bpw", "messages": [ {"role": "user", "content": "Hello"} ] }' - Hermes Agent
How to use aquaman164/Qwen3.6-35B-A3B-MLX-VQ-2.6bpw with Hermes Agent:
Start the MLX server
# Install MLX LM: uv tool install mlx-lm # Start a local OpenAI-compatible server: mlx_lm.server --model "aquaman164/Qwen3.6-35B-A3B-MLX-VQ-2.6bpw"
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 aquaman164/Qwen3.6-35B-A3B-MLX-VQ-2.6bpw
Run Hermes
hermes
- Atomic Chat
- OpenClaw
How to use aquaman164/Qwen3.6-35B-A3B-MLX-VQ-2.6bpw with OpenClaw:
Start the MLX server
# Install MLX LM: uv tool install mlx-lm # Start a local OpenAI-compatible server: mlx_lm.server --model "aquaman164/Qwen3.6-35B-A3B-MLX-VQ-2.6bpw"
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 "aquaman164/Qwen3.6-35B-A3B-MLX-VQ-2.6bpw" \ --custom-provider-id mlx-lm \ --custom-compatibility openai \ --custom-text-input \ --accept-risk \ --skip-health
Run OpenClaw
openclaw agent --local --agent main --message "Hello from Hugging Face"
Upload code/vq_proxy.py with huggingface_hub
Browse files- code/vq_proxy.py +38 -3
code/vq_proxy.py
CHANGED
|
@@ -30,6 +30,7 @@ Notes:
|
|
| 30 |
are folded into user turns — Qwen chat templates only allow system first.
|
| 31 |
"""
|
| 32 |
import argparse
|
|
|
|
| 33 |
import json
|
| 34 |
import os
|
| 35 |
import sys
|
|
@@ -66,15 +67,37 @@ if _timeout_env in ("", "0", "none", "None"):
|
|
| 66 |
else:
|
| 67 |
_READ_TIMEOUT = float(_timeout_env)
|
| 68 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
@asynccontextmanager
|
| 70 |
async def lifespan(app):
|
| 71 |
app.state.client = httpx.AsyncClient(timeout=httpx.Timeout(_READ_TIMEOUT, connect=10.0))
|
|
|
|
| 72 |
try:
|
| 73 |
yield
|
| 74 |
finally:
|
| 75 |
await app.state.client.aclose()
|
| 76 |
|
| 77 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
app = FastAPI(title="vq_proxy", lifespan=lifespan)
|
| 79 |
|
| 80 |
|
|
@@ -317,7 +340,7 @@ def openai_to_anthropic_response(oai, model_name):
|
|
| 317 |
FORWARD_THINKING = os.environ.get("VQ_PROXY_THINKING", "1") != "0"
|
| 318 |
|
| 319 |
|
| 320 |
-
async def anthropic_stream(oai_body, model_name, est_input):
|
| 321 |
"""Consume the upstream OpenAI SSE stream, yield Anthropic SSE events."""
|
| 322 |
msg_id = _new_id("msg")
|
| 323 |
open_kind = None # "think" | "text" | "tool" | None
|
|
@@ -364,6 +387,17 @@ async def anthropic_stream(oai_body, model_name, est_input):
|
|
| 364 |
yield _sse("ping", {"type": "ping"})
|
| 365 |
|
| 366 |
async for line in resp.aiter_lines():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 367 |
if not line or not line.startswith("data:"):
|
| 368 |
continue
|
| 369 |
payload = line[len("data:"):].strip()
|
|
@@ -485,12 +519,13 @@ async def messages(request: Request):
|
|
| 485 |
if stream:
|
| 486 |
est_input = max(1, len(json.dumps(oai_body.get("messages", []), ensure_ascii=False)) // 4)
|
| 487 |
return StreamingResponse(
|
| 488 |
-
anthropic_stream(oai_body, model_name, est_input),
|
| 489 |
media_type="text/event-stream",
|
| 490 |
)
|
| 491 |
|
| 492 |
try:
|
| 493 |
-
|
|
|
|
| 494 |
except httpx.HTTPError as exc:
|
| 495 |
return JSONResponse(status_code=502, content={
|
| 496 |
"type": "error",
|
|
|
|
| 30 |
are folded into user turns — Qwen chat templates only allow system first.
|
| 31 |
"""
|
| 32 |
import argparse
|
| 33 |
+
import asyncio
|
| 34 |
import json
|
| 35 |
import os
|
| 36 |
import sys
|
|
|
|
| 67 |
else:
|
| 68 |
_READ_TIMEOUT = float(_timeout_env)
|
| 69 |
|
| 70 |
+
# The single GPU can only do one big prefill at a time without OOMing the Metal
|
| 71 |
+
# working set. mlx_lm.server batches concurrent HTTP requests, so if Claude Code
|
| 72 |
+
# retries/resends (or fires a background request) while a large prefill is in
|
| 73 |
+
# flight, two prefills stack on the GPU. Serialize upstream generations here so
|
| 74 |
+
# vq_serve never sees more than VQ_MAX_CONCURRENCY at once. Raise it only on a
|
| 75 |
+
# machine with unified memory to spare.
|
| 76 |
+
_MAX_CONCURRENCY = max(1, int(os.environ.get("VQ_MAX_CONCURRENCY", "1")))
|
| 77 |
+
|
| 78 |
+
|
| 79 |
@asynccontextmanager
|
| 80 |
async def lifespan(app):
|
| 81 |
app.state.client = httpx.AsyncClient(timeout=httpx.Timeout(_READ_TIMEOUT, connect=10.0))
|
| 82 |
+
app.state.sem = asyncio.Semaphore(_MAX_CONCURRENCY)
|
| 83 |
try:
|
| 84 |
yield
|
| 85 |
finally:
|
| 86 |
await app.state.client.aclose()
|
| 87 |
|
| 88 |
|
| 89 |
+
async def _with_sem(gen, sem):
|
| 90 |
+
"""Hold `sem` for the lifetime of async generator `gen`, releasing on normal
|
| 91 |
+
completion OR on cancellation (client disconnect). This is what frees the slot
|
| 92 |
+
so a queued retry can proceed instead of stacking a second upstream prefill."""
|
| 93 |
+
await sem.acquire()
|
| 94 |
+
try:
|
| 95 |
+
async for item in gen:
|
| 96 |
+
yield item
|
| 97 |
+
finally:
|
| 98 |
+
sem.release()
|
| 99 |
+
|
| 100 |
+
|
| 101 |
app = FastAPI(title="vq_proxy", lifespan=lifespan)
|
| 102 |
|
| 103 |
|
|
|
|
| 340 |
FORWARD_THINKING = os.environ.get("VQ_PROXY_THINKING", "1") != "0"
|
| 341 |
|
| 342 |
|
| 343 |
+
async def anthropic_stream(oai_body, model_name, est_input, request=None):
|
| 344 |
"""Consume the upstream OpenAI SSE stream, yield Anthropic SSE events."""
|
| 345 |
msg_id = _new_id("msg")
|
| 346 |
open_kind = None # "think" | "text" | "tool" | None
|
|
|
|
| 387 |
yield _sse("ping", {"type": "ping"})
|
| 388 |
|
| 389 |
async for line in resp.aiter_lines():
|
| 390 |
+
# During a long prefill this generator yields nothing (keepalive lines
|
| 391 |
+
# below are skipped), so Starlette can't detect a client disconnect on
|
| 392 |
+
# a send. Poll it explicitly here — mlx_lm emits keepalive lines every
|
| 393 |
+
# chunk, so this loop keeps ticking — and bail out on disconnect so the
|
| 394 |
+
# async-with closes the upstream connection and vq_serve aborts the run.
|
| 395 |
+
if request is not None:
|
| 396 |
+
try:
|
| 397 |
+
if await request.is_disconnected():
|
| 398 |
+
break
|
| 399 |
+
except Exception:
|
| 400 |
+
pass
|
| 401 |
if not line or not line.startswith("data:"):
|
| 402 |
continue
|
| 403 |
payload = line[len("data:"):].strip()
|
|
|
|
| 519 |
if stream:
|
| 520 |
est_input = max(1, len(json.dumps(oai_body.get("messages", []), ensure_ascii=False)) // 4)
|
| 521 |
return StreamingResponse(
|
| 522 |
+
_with_sem(anthropic_stream(oai_body, model_name, est_input, request), app.state.sem),
|
| 523 |
media_type="text/event-stream",
|
| 524 |
)
|
| 525 |
|
| 526 |
try:
|
| 527 |
+
async with app.state.sem:
|
| 528 |
+
resp = await app.state.client.post(f"{UPSTREAM_URL}/v1/chat/completions", json=oai_body)
|
| 529 |
except httpx.HTTPError as exc:
|
| 530 |
return JSONResponse(status_code=502, content={
|
| 531 |
"type": "error",
|