10
stars
1
commits
4
linked in READMEs
Aug 15, 2026
updated
imatrix-calibrated quantizations of Qwen/Qwen3.8-27B spanning 3.06 β 5.76 bits per weight, calibrated on real agentic-coding logs. Every rung bundles the model's own Multi-Token-Prediction draft head at Q8_0 (blk.64) β speculative decoding with no second file to download. IQ4_XS is the recommended default (KLD 0.010, 88% top-token agreement); Q5_K_M is the closest substitute for FP16. Plain GGUF β runs in vanilla llama.cpp / Ollama / LM Studio, no custom runtime.
Qwen3.8 is natively multimodal, so the vision tower ships alongside as mmproj-Qwen3.8-27B-Q8_0.gguf (629 MB, 27-layer encoder at Q8_0). Pair it with any of the four rungs via --mmproj and the model can see images β screenshots, diagrams, UI states. The text weights are unchanged; vision is purely additive, so drop the flag and you are back to the identical text-only model. Verified on the 2-bit rung β the hardest case: shown a test image it reported "a red circle on the left, a blue rectangle on the right, and a green triangle at the bottom center", correct in shape, colour and position for all three.
An imatrix is activation statistics β it records what the model actually does while reading the calibration text. Pack that corpus into 7,500-token windows and the model never sees a long tool-call chain while the statistics are collected, so the channels carrying late-trajectory behaviour are under-weighted. Repacking for 32,768 took calibration windows from an 11,390-token maximum to a median of 13,004 and a p90 of 31,869 β 67% of sessions now exceed the old cap entirely, so a whole agentic trajectory fits in one calibration context instead of being cut mid-chain.
eval-ctx 8192. KLD is median; top_p is top-token agreement with FP16.| Metric | FP16 (ref) | Q5_K_M | IQ4_XS | IQ3_M | IQ2_M |
|---|---|---|---|---|---|
| File | β | Q5_K_M.gguf | IQ4_XS.gguf | IQ3_M.gguf | IQ2_M.gguf |
| Method | β | imatrix | imatrix | imatrix | imatrix |
| Quality | β | ββββ | βββ | ββ | β |
| BPW | 16.000 | 5.763 | 4.549 | 3.816 | 3.062 |
| Size (GiB) | 50.90 | 18.33 | 14.47 | 12.14 | 9.74 |
| π€ SWE issue resolved | β | β | β | β | β |
| π€ Steps to solve | 10 | 13 | 14 | 26 | 20 |
| π€ Malformed commands | 0 | 0 | 0 | 0 | 0 |
| π€ Tool-selection acc | 0.494 | 0.474 | 0.503 | 0.454 | 0.494 |
| π€ Param accuracy | 0.256 | 0.265 | 0.274 | 0.239 | 0.260 |
| π PPL | 22.850 | 23.274 | 31.118 | 37.363 | 56.540 |
| π KLD (med) | 0.000 | 0.0036 | 0.0105 | 0.0354 | 0.1242 |
| π same_top_p | 100.0% | 91.8% | 88.2% | 82.4% | 72.0% |
β οΈ The π€ rows are a band, not a ranking. At nβ174 turns the standard error is Β±3.8pp, and the whole spread β FP16 included β is 0.454β0.503. That is a 4.9pp range inside one standard error. It supports "every rung, down to 3.06 bpw, tool-calls indistinguishably from FP16 here" and nothing finer. IQ2_M and FP16 both score 86/174 yet disagree on 24 turns (12 each way) β the tie is a coincidence, not equivalence.
π KLD and
top_pare the trustworthy columns, strictly monotone across all six evals. IQ2_M gives up ~28% of top-token decisions β a memory-constrained option, not a drop-in.
Sampling. Static: eval-ctx 8192. Tool-call replay: greedy (temperature=0), ctx=32768, --no-stop-on-fail so every model is scored on the identical 174 turns (the default stop-on-fail halts a weak model early and scores it on fewer, easier turns β which makes models incomparable). Agentic: temperature=0.25, top_p=0.95, max_tokens=8096, 2,048-token reasoning budget, step cap 60. Run on an RTX PRO 6000 Blackwell.
"Tool errors" are classified, not counted. A non-zero exit is not an agent error β a grep that legitimately matched nothing and a pytest correctly reporting a failure both exit non-zero. Errors are split into malformed (shell syntax error, command not found, failed cd β the intended work never ran), timeout, and nonzero (the program ran and reported a result). Under that split every rung issues zero malformed commands; the only genuine tool failure in the whole sweep is one timeout on IQ3_M.
The reasoning budget is a cost control, not a quality lever. Uncapped, IQ2_M was observed emitting a 12,566-token single completion β a repetition loop, not reasoning. An unbudgeted control sweep was run alongside: at n=1 the 2k budget shows no reliable effect on whether the issue gets solved (IQ4_XS and Q5_K_M improved, IQ3_M got worse, FP16 rose).
Six eval distributions, each with its own FP16 baseline β never concatenated:
| eval | what it is | FP16 PPL | IQ2_M | IQ3_M | IQ4_XS | Q5_K_M |
|---|---|---|---|---|---|---|
external | code + math + tools, disjoint from calibration β the headline | 22.850 | 0.1242 | 0.0354 | 0.0105 | 0.0036 |
general | combined_en_tiny, broad English (30k tok) | 92.797 | 0.2199 | 0.0569 | 0.0154 | 0.0062 |
tools | held-out CLI + agent log sessions (150k tok) | 21.652 | 0.0498 | 0.0115 | 0.0032 | 0.0014 |
agentic | held-out SWE trajectories (99k tok) | 43.473 | 0.0169 | 0.0048 | 0.0013 | 0.0006 |
broad | held-out broad-instruct (100k tok) | 88.193 | 0.2691 | 0.0702 | 0.0204 | 0.0076 |
cal8k | slice of the previous 8192-packed corpus β a fit probe, not a holdout | 17.400 | 0.0840 | 0.0261 | 0.0062 | 0.0025 |
β οΈ The four chat-templated evals (tools, agentic, broad, cal8k) are quant-vs-quant only: llama-perplexity has no --parse-special, so chat markers tokenize as ordinary BPE and absolute PPL is off-distribution. KLD and top_p remain valid β which is why PPL goes non-monotone there while KLD does not.
An imatrix is only as good as the text it watched. Here is exactly what this one saw.
| source | what it is | samples | windows | tokens | share | median window |
|---|---|---|---|---|---|---|
| logs | real Claude Code / opencode / qwen code sessions | 108 sessions | 125 | 2,000,139 | 47.0% | 13,004 |
| swe-trajectories | agent runs on real GitHub issues | 61 sessions | 62 | 681,012 | 16.0% | 9,650 |
| reasoning | <think> turns, re-cut so reasoning lands last | 65 sessions | 230 | 638,046 | 15.0% | 2,022 |
| broad-supplement | broad-instruct, non-coding | 2,704 convos | 2,704 | 537,851 | 12.6% | 182 |
| wiki | wiki.test.raw, raw prose | 10 chunks | 10 | 297,053 | 7.0% | 29,641 |
| redteam-refusals | attack prompts + generic refusals | 305 convos | 305 | 101,660 | 2.4% | 231 |
| 3,436 | 4,255,761 | 100% |
Three things worth reading off that table:
<tool_call> blocks and 5,827 <tool_response> blocks across 76 distinct tool schemas, and in the log windows 99.3% of tokens sit in tool-bearing turns.reasoning overlaps logs by design. It re-renders the same conversations with a reasoning turn placed last, because chat templates keep <think> only on a render's final assistant turn and scrub it from history. Without those extra windows the corpus would contain almost no reasoning at all.The red-team slice ships attack prompts with generic refusals substituted β the targets' original harmful completions never enter any corpus. Refusal behaviour is what low-bit quantization erodes first, so the attack distribution belongs in calibration; the responses do not.
E[aΒ²] blended with weight-column energy βW[:,c]βΒ²Β·E[aΒ²] per tensor, collected over the corpus above in 129 chunks of a full 32,768-token context (3h54m on an RTX PRO 6000 Blackwell). 496 tensors received statistics.E[aΒ²]; output-aware re-ranking is mathematically invalid for state-space tensors.mtp_num_hidden_layers=1) is remapped to blk.64 and pinned to Q8_0 in every rung: 8 Γ Q8_0 + 7 Γ F32, zero low-bit tensors, verified per file. This is checked rather than assumed because llama-quantize silently accepts a --tensor-type pattern that matches nothing β a stale pin would quantize the draft head with the trunk and surface only as poor acceptance. The head gets no imatrix statistics; it sits outside the forward pass, which is why it is pinned rather than calibrated.<tool_call><function=NAME><parameter=KEY>), not the JSON form earlier Qwen3.x templates used. Calibrating on the JSON form would have collected statistics on a syntax this model never produces.output.weight is calibrated β llama-imatrix only collects tensors named blk.* unless you pass --process-output, so the largest quantized tensor (and the one that most directly shapes the distribution KLD measures) is otherwise quantized blind. llama-quantize prints did not find weights for output.weight and proceeds anyway, so the only symptom is a line in a log.cal8k, explicitly labelled a fit probe.@ f3e1828. Calibration logs mined with LogMiner.git clone https://github.com/pearsonkyle/Quant-Tuner && cd Quant-Tuner
git submodule update --init --recursive
cmake -S vendor/llama.cpp -B vendor/llama.cpp/build -DGGML_CUDA=ON \
-DCMAKE_BUILD_TYPE=Release -DCMAKE_CUDA_ARCHITECTURES=120 -DLLAMA_CURL=ON
cmake --build vendor/llama.cpp/build -j 64
uv sync --extra dev
PYTHONPATH=src .venv/bin/python scripts/exp060_repack_cal_32k.py --ctx 32768 \
--wiki out/exp-001/wiki/wiki.test.raw
PYTHONPATH=src .venv/bin/python scripts/exp060_quants_qwen38.py \
--run exp-060-32k --ctx 32768 --eval-ctx 8192 \
--evals external general tools agentic broad cal8k
β οΈ
llama-imatrixneeds--no-pplabove ~17k ctx on this model. Its perplexity path computesall_logits + first*n_vocabwithfirst = n_ctx/2inintarithmetic (tools/imatrix/imatrix.cpp:911). With Qwen3.8's 248,320-token vocab that product overflowsINT_MAXfor anyn_ctx > 2Β³Β²/248320 β 17,296, and the process segfaults after the first pass.--no-pplskips only the perplexity bookkeeping β the forward pass, and therefore every activation statistic, is unchanged.
ollama run hf.co/pearsonkyle/Qwen3.8-27B-imatrix-MTP-GGUF:IQ4_XS
# also: :Q5_K_M Β· :IQ3_M Β· :IQ2_M
./llama-server \
--model Qwen3.8-27B-IQ4_XS.gguf \
--ctx-size 32768 --n-gpu-layers 999 \
--spec-type draft-mtp --spec-draft-n-max 1 \
--flash-attn on --cache-type-k q8_0 --cache-type-v q8_0 \
--host 0.0.0.0 --port 1234
The draft head is inside the GGUF β no --model-draft, nothing extra to fetch. Qwen3.8
exposes one nextn layer, so --spec-draft-n-max 1 is the right setting. Drop both
--spec-* flags to run without speculative decoding.
The vision tower ships separately as mmproj-Qwen3.8-27B-Q8_0.gguf (629 MB) so you only
download it if you need images. It pairs with any of the four rungs β the text weights are
identical; the mmproj just adds the encoder + projector.
# one-shot from the CLI
./llama-mtmd-cli \
-m Qwen3.8-27B-IQ4_XS.gguf \
--mmproj mmproj-Qwen3.8-27B-Q8_0.gguf \
--image screenshot.png \
--jinja -ngl 999 --temp 0.2 -n 512 \
-p "Describe this image."
# or serve it β /v1/chat/completions then accepts image_url content parts
./llama-server \
-m Qwen3.8-27B-IQ4_XS.gguf \
--mmproj mmproj-Qwen3.8-27B-Q8_0.gguf \
--jinja --ctx-size 32768 --n-gpu-layers 999 \
--host 0.0.0.0 --port 1234
--jinjais required β the chat template is Jinja-based and the multimodal path needs it. For grounding tasks (pointing at or locating things in an image) llama.cpp recommends--image-min-tokens 1024; the default tokenization is fine for description and Q&A.
import json, urllib.request
def ask(content, max_tokens=512):
body = {"messages": [{"role": "user", "content": content}], "max_tokens": max_tokens}
req = urllib.request.Request("http://127.0.0.1:1234/v1/chat/completions",
json.dumps(body).encode(),
{"Content-Type": "application/json"})
return json.loads(urllib.request.urlopen(req).read())["choices"][0]["message"]["content"]
print(ask("Write a Python function that reverses a linked list."))
Qwen/Qwen3.8-27B (Apache-2.0).@ f3e1828.1 commits
10
stars
1
commits
4
linked in READMEs
Aug 15, 2026
updated
imatrix-calibrated quantizations of Qwen/Qwen3.8-27B spanning 3.06 β 5.76 bits per weight, calibrated on real agentic-coding logs. Every rung bundles the model's own Multi-Token-Prediction draft head at Q8_0 (blk.64) β speculative decoding with no second file to download. IQ4_XS is the recommended default (KLD 0.010, 88% top-token agreement); Q5_K_M is the closest substitute for FP16. Plain GGUF β runs in vanilla llama.cpp / Ollama / LM Studio, no custom runtime.
Qwen3.8 is natively multimodal, so the vision tower ships alongside as mmproj-Qwen3.8-27B-Q8_0.gguf (629 MB, 27-layer encoder at Q8_0). Pair it with any of the four rungs via --mmproj and the model can see images β screenshots, diagrams, UI states. The text weights are unchanged; vision is purely additive, so drop the flag and you are back to the identical text-only model. Verified on the 2-bit rung β the hardest case: shown a test image it reported "a red circle on the left, a blue rectangle on the right, and a green triangle at the bottom center", correct in shape, colour and position for all three.
An imatrix is activation statistics β it records what the model actually does while reading the calibration text. Pack that corpus into 7,500-token windows and the model never sees a long tool-call chain while the statistics are collected, so the channels carrying late-trajectory behaviour are under-weighted. Repacking for 32,768 took calibration windows from an 11,390-token maximum to a median of 13,004 and a p90 of 31,869 β 67% of sessions now exceed the old cap entirely, so a whole agentic trajectory fits in one calibration context instead of being cut mid-chain.
eval-ctx 8192. KLD is median; top_p is top-token agreement with FP16.| Metric | FP16 (ref) | Q5_K_M | IQ4_XS | IQ3_M | IQ2_M |
|---|---|---|---|---|---|
| File | β | Q5_K_M.gguf | IQ4_XS.gguf | IQ3_M.gguf | IQ2_M.gguf |
| Method | β | imatrix | imatrix | imatrix | imatrix |
| Quality | β | ββββ | βββ | ββ | β |
| BPW | 16.000 | 5.763 | 4.549 | 3.816 | 3.062 |
| Size (GiB) | 50.90 | 18.33 | 14.47 | 12.14 | 9.74 |
| π€ SWE issue resolved | β | β | β | β | β |
| π€ Steps to solve | 10 | 13 | 14 | 26 | 20 |
| π€ Malformed commands | 0 | 0 | 0 | 0 | 0 |
| π€ Tool-selection acc | 0.494 | 0.474 | 0.503 | 0.454 | 0.494 |
| π€ Param accuracy | 0.256 | 0.265 | 0.274 | 0.239 | 0.260 |
| π PPL | 22.850 | 23.274 | 31.118 | 37.363 | 56.540 |
| π KLD (med) | 0.000 | 0.0036 | 0.0105 | 0.0354 | 0.1242 |
| π same_top_p | 100.0% | 91.8% | 88.2% | 82.4% | 72.0% |
β οΈ The π€ rows are a band, not a ranking. At nβ174 turns the standard error is Β±3.8pp, and the whole spread β FP16 included β is 0.454β0.503. That is a 4.9pp range inside one standard error. It supports "every rung, down to 3.06 bpw, tool-calls indistinguishably from FP16 here" and nothing finer. IQ2_M and FP16 both score 86/174 yet disagree on 24 turns (12 each way) β the tie is a coincidence, not equivalence.
π KLD and
top_pare the trustworthy columns, strictly monotone across all six evals. IQ2_M gives up ~28% of top-token decisions β a memory-constrained option, not a drop-in.
Sampling. Static: eval-ctx 8192. Tool-call replay: greedy (temperature=0), ctx=32768, --no-stop-on-fail so every model is scored on the identical 174 turns (the default stop-on-fail halts a weak model early and scores it on fewer, easier turns β which makes models incomparable). Agentic: temperature=0.25, top_p=0.95, max_tokens=8096, 2,048-token reasoning budget, step cap 60. Run on an RTX PRO 6000 Blackwell.
"Tool errors" are classified, not counted. A non-zero exit is not an agent error β a grep that legitimately matched nothing and a pytest correctly reporting a failure both exit non-zero. Errors are split into malformed (shell syntax error, command not found, failed cd β the intended work never ran), timeout, and nonzero (the program ran and reported a result). Under that split every rung issues zero malformed commands; the only genuine tool failure in the whole sweep is one timeout on IQ3_M.
The reasoning budget is a cost control, not a quality lever. Uncapped, IQ2_M was observed emitting a 12,566-token single completion β a repetition loop, not reasoning. An unbudgeted control sweep was run alongside: at n=1 the 2k budget shows no reliable effect on whether the issue gets solved (IQ4_XS and Q5_K_M improved, IQ3_M got worse, FP16 rose).
Six eval distributions, each with its own FP16 baseline β never concatenated:
| eval | what it is | FP16 PPL | IQ2_M | IQ3_M | IQ4_XS | Q5_K_M |
|---|---|---|---|---|---|---|
external | code + math + tools, disjoint from calibration β the headline | 22.850 | 0.1242 | 0.0354 | 0.0105 | 0.0036 |
general | combined_en_tiny, broad English (30k tok) | 92.797 | 0.2199 | 0.0569 | 0.0154 | 0.0062 |
tools | held-out CLI + agent log sessions (150k tok) | 21.652 | 0.0498 | 0.0115 | 0.0032 | 0.0014 |
agentic | held-out SWE trajectories (99k tok) | 43.473 | 0.0169 | 0.0048 | 0.0013 | 0.0006 |
broad | held-out broad-instruct (100k tok) | 88.193 | 0.2691 | 0.0702 | 0.0204 | 0.0076 |
cal8k | slice of the previous 8192-packed corpus β a fit probe, not a holdout | 17.400 | 0.0840 | 0.0261 | 0.0062 | 0.0025 |
β οΈ The four chat-templated evals (tools, agentic, broad, cal8k) are quant-vs-quant only: llama-perplexity has no --parse-special, so chat markers tokenize as ordinary BPE and absolute PPL is off-distribution. KLD and top_p remain valid β which is why PPL goes non-monotone there while KLD does not.
An imatrix is only as good as the text it watched. Here is exactly what this one saw.
| source | what it is | samples | windows | tokens | share | median window |
|---|---|---|---|---|---|---|
| logs | real Claude Code / opencode / qwen code sessions | 108 sessions | 125 | 2,000,139 | 47.0% | 13,004 |
| swe-trajectories | agent runs on real GitHub issues | 61 sessions | 62 | 681,012 | 16.0% | 9,650 |
| reasoning | <think> turns, re-cut so reasoning lands last | 65 sessions | 230 | 638,046 | 15.0% | 2,022 |
| broad-supplement | broad-instruct, non-coding | 2,704 convos | 2,704 | 537,851 | 12.6% | 182 |
| wiki | wiki.test.raw, raw prose | 10 chunks | 10 | 297,053 | 7.0% | 29,641 |
| redteam-refusals | attack prompts + generic refusals | 305 convos | 305 | 101,660 | 2.4% | 231 |
| 3,436 | 4,255,761 | 100% |
Three things worth reading off that table:
<tool_call> blocks and 5,827 <tool_response> blocks across 76 distinct tool schemas, and in the log windows 99.3% of tokens sit in tool-bearing turns.reasoning overlaps logs by design. It re-renders the same conversations with a reasoning turn placed last, because chat templates keep <think> only on a render's final assistant turn and scrub it from history. Without those extra windows the corpus would contain almost no reasoning at all.The red-team slice ships attack prompts with generic refusals substituted β the targets' original harmful completions never enter any corpus. Refusal behaviour is what low-bit quantization erodes first, so the attack distribution belongs in calibration; the responses do not.
E[aΒ²] blended with weight-column energy βW[:,c]βΒ²Β·E[aΒ²] per tensor, collected over the corpus above in 129 chunks of a full 32,768-token context (3h54m on an RTX PRO 6000 Blackwell). 496 tensors received statistics.E[aΒ²]; output-aware re-ranking is mathematically invalid for state-space tensors.mtp_num_hidden_layers=1) is remapped to blk.64 and pinned to Q8_0 in every rung: 8 Γ Q8_0 + 7 Γ F32, zero low-bit tensors, verified per file. This is checked rather than assumed because llama-quantize silently accepts a --tensor-type pattern that matches nothing β a stale pin would quantize the draft head with the trunk and surface only as poor acceptance. The head gets no imatrix statistics; it sits outside the forward pass, which is why it is pinned rather than calibrated.<tool_call><function=NAME><parameter=KEY>), not the JSON form earlier Qwen3.x templates used. Calibrating on the JSON form would have collected statistics on a syntax this model never produces.output.weight is calibrated β llama-imatrix only collects tensors named blk.* unless you pass --process-output, so the largest quantized tensor (and the one that most directly shapes the distribution KLD measures) is otherwise quantized blind. llama-quantize prints did not find weights for output.weight and proceeds anyway, so the only symptom is a line in a log.cal8k, explicitly labelled a fit probe.@ f3e1828. Calibration logs mined with LogMiner.git clone https://github.com/pearsonkyle/Quant-Tuner && cd Quant-Tuner
git submodule update --init --recursive
cmake -S vendor/llama.cpp -B vendor/llama.cpp/build -DGGML_CUDA=ON \
-DCMAKE_BUILD_TYPE=Release -DCMAKE_CUDA_ARCHITECTURES=120 -DLLAMA_CURL=ON
cmake --build vendor/llama.cpp/build -j 64
uv sync --extra dev
PYTHONPATH=src .venv/bin/python scripts/exp060_repack_cal_32k.py --ctx 32768 \
--wiki out/exp-001/wiki/wiki.test.raw
PYTHONPATH=src .venv/bin/python scripts/exp060_quants_qwen38.py \
--run exp-060-32k --ctx 32768 --eval-ctx 8192 \
--evals external general tools agentic broad cal8k
β οΈ
llama-imatrixneeds--no-pplabove ~17k ctx on this model. Its perplexity path computesall_logits + first*n_vocabwithfirst = n_ctx/2inintarithmetic (tools/imatrix/imatrix.cpp:911). With Qwen3.8's 248,320-token vocab that product overflowsINT_MAXfor anyn_ctx > 2Β³Β²/248320 β 17,296, and the process segfaults after the first pass.--no-pplskips only the perplexity bookkeeping β the forward pass, and therefore every activation statistic, is unchanged.
ollama run hf.co/pearsonkyle/Qwen3.8-27B-imatrix-MTP-GGUF:IQ4_XS
# also: :Q5_K_M Β· :IQ3_M Β· :IQ2_M
./llama-server \
--model Qwen3.8-27B-IQ4_XS.gguf \
--ctx-size 32768 --n-gpu-layers 999 \
--spec-type draft-mtp --spec-draft-n-max 1 \
--flash-attn on --cache-type-k q8_0 --cache-type-v q8_0 \
--host 0.0.0.0 --port 1234
The draft head is inside the GGUF β no --model-draft, nothing extra to fetch. Qwen3.8
exposes one nextn layer, so --spec-draft-n-max 1 is the right setting. Drop both
--spec-* flags to run without speculative decoding.
The vision tower ships separately as mmproj-Qwen3.8-27B-Q8_0.gguf (629 MB) so you only
download it if you need images. It pairs with any of the four rungs β the text weights are
identical; the mmproj just adds the encoder + projector.
# one-shot from the CLI
./llama-mtmd-cli \
-m Qwen3.8-27B-IQ4_XS.gguf \
--mmproj mmproj-Qwen3.8-27B-Q8_0.gguf \
--image screenshot.png \
--jinja -ngl 999 --temp 0.2 -n 512 \
-p "Describe this image."
# or serve it β /v1/chat/completions then accepts image_url content parts
./llama-server \
-m Qwen3.8-27B-IQ4_XS.gguf \
--mmproj mmproj-Qwen3.8-27B-Q8_0.gguf \
--jinja --ctx-size 32768 --n-gpu-layers 999 \
--host 0.0.0.0 --port 1234
--jinjais required β the chat template is Jinja-based and the multimodal path needs it. For grounding tasks (pointing at or locating things in an image) llama.cpp recommends--image-min-tokens 1024; the default tokenization is fine for description and Q&A.
import json, urllib.request
def ask(content, max_tokens=512):
body = {"messages": [{"role": "user", "content": content}], "max_tokens": max_tokens}
req = urllib.request.Request("http://127.0.0.1:1234/v1/chat/completions",
json.dumps(body).encode(),
{"Content-Type": "application/json"})
return json.loads(urllib.request.urlopen(req).read())["choices"][0]["message"]["content"]
print(ask("Write a Python function that reverses a linked list."))
Qwen/Qwen3.8-27B (Apache-2.0).@ f3e1828.1 commits