Instructions to use patdev/k3-a40-bootstrap 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 patdev/k3-a40-bootstrap 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 patdev/k3-a40-bootstrap:BF16 # Run inference directly in the terminal: llama cli -hf patdev/k3-a40-bootstrap:BF16
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf patdev/k3-a40-bootstrap:BF16 # Run inference directly in the terminal: llama cli -hf patdev/k3-a40-bootstrap:BF16
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 patdev/k3-a40-bootstrap:BF16 # Run inference directly in the terminal: ./llama-cli -hf patdev/k3-a40-bootstrap:BF16
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 patdev/k3-a40-bootstrap:BF16 # Run inference directly in the terminal: ./build/bin/llama-cli -hf patdev/k3-a40-bootstrap:BF16
Use Docker
docker model run hf.co/patdev/k3-a40-bootstrap:BF16
- LM Studio
- Jan
- Ollama
How to use patdev/k3-a40-bootstrap with Ollama:
ollama run hf.co/patdev/k3-a40-bootstrap:BF16
- Unsloth Desktop
- Docker Model Runner
How to use patdev/k3-a40-bootstrap with Docker Model Runner:
docker model run hf.co/patdev/k3-a40-bootstrap:BF16
- Lemonade
How to use patdev/k3-a40-bootstrap with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull patdev/k3-a40-bootstrap:BF16
Run and chat with the model
lemonade run user.k3-a40-bootstrap-BF16
List all available models
lemonade list
- Atomic Chat
Download banc_flashnext.py from patdev/k3-a40-bootstrap: direct link, hf CLI and curl.
- Browser
- Download file 8.51 kB
-
https://huggingface.co/patdev/k3-a40-bootstrap/resolve/main/banc_flashnext.py
- Command line
-
hf download hf://patdev/k3-a40-bootstrap/banc_flashnext.py
-
curl -L -o banc_flashnext.py https://huggingface.co/patdev/k3-a40-bootstrap/resolve/main/banc_flashnext.py
8.51 kB
| """Qwen3.8-Flash-Next sur une seule carte, avec la table n-gramme en RAM. | |
| CE QU'ON CHERCHE A SAVOIR. Ce modele bat Claude Opus 4.6 sur SWE-bench Pro | |
| (62,5 contre 53,4) et Multilingual (81,0 contre 77,5), mesures avec le harnais | |
| de Claude Code. Il est donc le bon modele pour cet usage. La seule question est | |
| s'il tourne sur du materiel accessible. | |
| 125 B de parametres dont 6 B actifs, PLUS une table d'embeddings n-grammes de | |
| 51 B et 4 B de MTP -- 180 B au total. En NVFP4 : 125,9 Go, dont 47,7 pour la | |
| seule table n-gramme. | |
| `VLLM_PLE_CPU_OFFLOAD=1` place cette table en RAM hote. Il reste alors 78,2 Go | |
| sur GPU, ce qui tient sur une RTX PRO 6000 de 96 Go SANS parallelisme de | |
| tenseurs. C'est le point : toute la campagne a montre que les penalites | |
| multi-cartes sont severes, et une carte unique les supprime toutes. | |
| CE QUI PEUT ECHOUER, dans l'ordre de vraisemblance : | |
| 1. la RAM hote est insuffisante -- il faut 51 Go plus une marge ; | |
| 2. le dechargement fait traverser le PCIe a chaque consultation de n-gramme, | |
| et le debit s'effondre ; | |
| 3. l'architecture `Qwen4Exp` n'a jamais ete servie sur sm_120 -- la recette | |
| officielle vise GB300 et H200 ; | |
| 4. les parseurs different de Nemotron : `qwen3` et non `nemotron_v3`. | |
| Reperes sur cette meme carte, meme banc, contexte 131 072 : | |
| Nemotron NVFP4 (3,70 B actifs) : 275,3 solo | 910 agrege@8 | |
| Socle actif calcule pour Flash-Next : 4,02 Go/jeton -> plafond ~446 tok/s ici. | |
| """ | |
| import json | |
| import os | |
| import re | |
| import statistics | |
| import subprocess | |
| import threading | |
| import time | |
| import urllib.request | |
| MODEL = os.environ.get("BANC_MODEL", "RadixArk/Qwen3.8-Flash-Next-NVFP4") | |
| PORT = 8000 | |
| URL = "http://127.0.0.1:%d" % PORT | |
| CTX = os.environ.get("BANC_CTX", "131072") | |
| SEQS = os.environ.get("BANC_SEQS", "32") | |
| def dire(*a): | |
| print(*a, flush=True) | |
| dire("=" * 76) | |
| subprocess.run(["nvidia-smi", "--query-gpu=name,memory.total,compute_cap", | |
| "--format=csv,noheader"], check=False) | |
| subprocess.run(["bash", "-lc", | |
| "echo -n 'RAM hote : '; free -g | awk '/^Mem:/{print $2\" Go total, \"$7\" Go dispo\"}'"], | |
| check=False) | |
| subprocess.run(["python3", "-c", | |
| "import vllm;print('vllm',vllm.__version__)"], check=False) | |
| dire("PLE_CPU_OFFLOAD =", os.environ.get("VLLM_PLE_CPU_OFFLOAD", "(non pose)")) | |
| dire("=" * 76) | |
| BASE = ["vllm", "serve", MODEL, | |
| "--served-model-name", "m", | |
| "--host", "127.0.0.1", "--port", str(PORT), | |
| "--trust-remote-code", | |
| "--max-model-len", CTX, | |
| "--max-num-seqs", SEQS, | |
| "--enable-prefix-caching", | |
| "--gpu-memory-utilization", "0.90", | |
| # Parseurs de CE modele, differents de Nemotron. | |
| "--reasoning-parser", "qwen3", | |
| "--tool-call-parser", "qwen3_coder", | |
| "--enable-auto-tool-choice", | |
| # La recette officielle les recommande explicitement. | |
| "--no-enable-flashinfer-autotune", | |
| # Pas de vision : on sert du texte, et la tour visuelle coute de la | |
| # memoire pour rien dans cet usage. | |
| "--limit-mm-per-prompt", '{"image":0,"video":0}'] | |
| SUJETS = ["un cache LRU avec dict et liste doublement chainee", | |
| "un pool de connexions avec expiration et sante des sockets", | |
| "un analyseur d'expressions arithmetiques par descente recursive", | |
| "une file de priorite par tas binaire avec decrease-key", | |
| "un limiteur de debit par seau a jetons", | |
| "un index inverse pour recherche plein texte", | |
| "un ordonnanceur de taches avec dependances et cycles detectes", | |
| "un serialiseur binaire avec versionnement de schema"] | |
| journal = "/tmp/vllm.log" | |
| with open(journal, "w") as f: | |
| proc = subprocess.Popen(BASE, stdout=f, stderr=subprocess.STDOUT) | |
| JALONS = ("Starting to load model", "Loading weights took", "Using ", "PLE", | |
| "offload", "GPU KV cache size", "Capturing CUDA graphs", | |
| "init engine", "Application startup complete", "Error", "Traceback") | |
| pret = None | |
| vus = set() | |
| # Le telechargement fait 125,9 Go : compter large, et publier l'avancement | |
| # sinon on reste aveugle une demi-heure. | |
| for i in range(400): | |
| try: | |
| urllib.request.urlopen(URL + "/v1/models", timeout=5).read() | |
| pret = i * 10 | |
| break | |
| except Exception: | |
| pass | |
| if proc.poll() is not None: | |
| break | |
| if i and i % 6 == 0: | |
| t = open(journal, errors="replace").read().splitlines() | |
| recent = [l for l in t[-80:] if any(j in l for j in JALONS)] | |
| msg = recent[-1].split("] ")[-1][:120] if recent else "(rien de neuf, %d lignes)" % len(t) | |
| if msg not in vus: | |
| vus.add(msg) | |
| dire(" [%4d s] %s" % (i * 10, msg)) | |
| time.sleep(10) | |
| texte = open(journal, errors="replace").read() | |
| for motif in ("attention backend", "MoE backend", "GEMM", "GPU KV cache size", | |
| "Maximum concurrency", "PLE", "offload"): | |
| for ligne in texte.splitlines(): | |
| if motif in ligne and "ERROR" not in ligne: | |
| dire(" " + ligne.split("] ")[-1][:150]) | |
| break | |
| if pret is None: | |
| dire("NE DEMARRE PAS") | |
| vu = [] | |
| for ligne in texte.splitlines(): | |
| m = re.search(r"((?:Value|Runtime|NotImplemented|Assertion|Type|Memory|OS)Error" | |
| r"|out of memory|Unsupported|is not supported|CUDA error)[:\s].{0,170}", ligne) | |
| if m and m.group(0) not in vu: | |
| vu.append(m.group(0)) | |
| for t in vu[:6]: | |
| dire(" > " + t) | |
| if not vu: | |
| for ligne in texte.splitlines()[-15:]: | |
| dire(" | " + ligne[:150]) | |
| raise SystemExit(1) | |
| dire(" PRET en %d s (contexte %s, %s sequences)" % (pret, CTX, SEQS)) | |
| subprocess.run(["nvidia-smi", "--query-gpu=memory.used", "--format=csv,noheader"], check=False) | |
| def appel(contenu, sortie=400): | |
| corps = json.dumps({"model": "m", | |
| "messages": [{"role": "user", "content": contenu}], | |
| "max_tokens": sortie, "temperature": 1.0, "top_p": 0.95, | |
| "stream": True, | |
| "stream_options": {"include_usage": True}}).encode() | |
| r = urllib.request.Request(URL + "/v1/chat/completions", data=corps, | |
| headers={"Content-Type": "application/json"}) | |
| t0 = time.time() | |
| t1 = None | |
| n = 0 | |
| bouts = [] | |
| with urllib.request.urlopen(r, timeout=900) as rep: | |
| for l in rep: | |
| l = l.strip() | |
| if not l.startswith(b"data: ") or l[6:] == b"[DONE]": | |
| continue | |
| d = json.loads(l[6:]) | |
| ch = d.get("choices") or [] | |
| if not ch: | |
| continue | |
| de = ch[0].get("delta", {}) or {} | |
| x = de.get("content") or de.get("reasoning_content") or de.get("reasoning") | |
| if x: | |
| if t1 is None: | |
| t1 = time.time() | |
| n += 1 | |
| bouts.append(x) | |
| return {"ttft": (t1 - t0) if t1 else None, "n": n, "t1": t1, | |
| "t2": time.time(), "txt": "".join(bouts)} | |
| def div4(t): | |
| m = t.split() | |
| if len(m) < 40: | |
| return 1.0 | |
| g = [tuple(m[i:i + 4]) for i in range(len(m) - 3)] | |
| return len(set(g)) / len(g) | |
| dire("conc | agrege | par flux | ttft med | 4-gr | etat") | |
| for conc in (1, 4, 8): | |
| res = [None] * conc | |
| def un(i): | |
| try: | |
| res[i] = appel("Ecris en Python %s, avec trois tests unittest." | |
| % SUJETS[i % len(SUJETS)]) | |
| except Exception as e: | |
| res[i] = {"err": "%s: %s" % (type(e).__name__, str(e)[:70])} | |
| d0 = time.time() | |
| fils = [threading.Thread(target=un, args=(i,)) for i in range(conc)] | |
| for f in fils: | |
| f.start() | |
| for f in fils: | |
| f.join() | |
| d1 = time.time() | |
| bons = [r for r in res if r and not r.get("err") and r.get("t1")] | |
| if not bons: | |
| dire("%4d | ECHEC %s" % (conc, (res[0] or {}).get("err"))) | |
| continue | |
| par = statistics.median([(r["n"] - 1) / (r["t2"] - r["t1"]) | |
| for r in bons if r["t2"] > r["t1"]]) | |
| ttft = statistics.median([r["t1"] - d0 for r in bons]) | |
| dv = statistics.median([div4(r["txt"]) for r in bons]) | |
| dire("%4d | %8.1f | %8.1f | %7.2fs | %.3f | %s" | |
| % (conc, sum(r["n"] for r in bons) / (d1 - d0), par, ttft, dv, | |
| "SAIN" if dv > 0.6 else "DEGENERE")) | |
| dire("") | |
| dire("repere meme carte, meme banc : Nemotron NVFP4 275,3 solo | 910 agrege@8") | |
| dire("plafond memoire theorique de Flash-Next ici : ~446 tok/s (socle 4,02 Go)") | |
| proc.terminate() | |