CoolFace
Modelpublic

patdev/k3-a40-bootstrap

sourceHugging Faceotherupdated 18d agoView on Hugging Face
0likes1.3kdownloads
lancer_flashnext.sh359 linesDownload Raw Back to root
1#!/usr/bin/env bash2# Qwen3.8-Flash-Next NVFP4 sur UNE RTX PRO 6000 (96 Go), derriere le pont3# Anthropic, pret pour Claude Code. Recette complete, validee le 26/08/2026.4#5# Image : vllm/vllm-openai:qwen38-flash-next  (0.1.dev20073+g8e685d198)6# Pod   : 1 GPU, >=120 Go de RAM, >=200 Go de disque conteneur, port 8080/http.7#8# CE QUI EST INDISPENSABLE, et pourquoi :9#10#   VLLM_PLE_CPU_OFFLOAD=1        place la table n-gramme (51 B parametres) en11#                                 RAM hote. Sans elle, 125,9 Go de poids pour12#                                 96 Go de VRAM : impossible.13#14#   --distributed-executor-backend mp   OBLIGATOIRE MEME EN TP=1. En15#                                 UniProcExecutor (le defaut a TP=1),16#                                 `spawn_ple_offload()` n'est jamais appele :17#                                 aucun ouvrier n'existe, et le moteur attend18#                                 pour toujours apres la capture des graphes,19#                                 sans une seule ligne d'erreur.20#21#   patch_ple.py                  sans lui, le chargement echoue au 205e shard22#                                 sur 206 sur `ngram_embedding.weight_scale`.23#24#   --gpu-memory-utilization 0.93 NE PAS imposer --kv-cache-memory, et NE PAS25#                                 suivre la suggestion "fully utilize gpu26#                                 memory" de vLLM (17,92 GiB ici) : elle ne27#                                 garde aucune marge au-dessus du pic28#                                 d'activation mesure a vide, et sous charge29#                                 reelle ce pic est plus haut -- c'est cette30#                                 valeur-la qui a tue le serveur en plein31#                                 service. Mesure du 26/08, une seule variable32#                                 changee :33#                                     0.88 ->  7,52 GiB de KV, 309 594 jetons34#                                     0.93 -> 12,26 GiB de KV, 504 841 jetons35#                                 A 0.88 vLLM refusait d'admettre des requetes36#                                 (num_requests_waiting_by_reason reason=37#                                 "capacity" = 5 pour 2 en cours) ; a 0.93 la38#                                 file est vide.39#40#   PAS de --speculative-config.  La tete MTP existe pourtant (31 tenseurs41#                                 mtp.* dans le depot) et vLLM l'accepte, mais42#                                 elle prend 5,79 GiB de VRAM : le KV tombe a43#                                 6,47 GiB quand une seule requete a 262 144 en44#                                 reclame 7,25, et le moteur refuse de demarrer.45#                                 Mauvais marche de toute facon : au mieux46#                                 423 000 jetons contre 504 841. Voir DEPLOY.md.47#48#   PAS de PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True. Les segments49#                                 extensibles se partagent entre processus par50#                                 un chemin qui exige CAP_SYS_PTRACE, absent des51#                                 conteneurs RunPod : `pidfd_getfd: Operation52#                                 not permitted` dans l'ouvrier PLE.53#54# COUT MEMOIRE REEL : l'ouvrier construit l'embedding au dtype du modele (BF16),55# donc la table fp8 de 51,2 Go y occupe ~102 Go. Prevoir >110 Go de RAM.56#57# MESURE sur RTX PRO 6000, contexte 262144, 26/08/2026 :58#   solo 75,3 et 76,0 tok/s | agrege 186,2 a 4 sessions | 335,2 a 859#   prefill 8 535 jetons/s  | KV 26,2 Ko/jeton60#   repere meme carte, meme banc : Nemotron NVFP4 275,3 solo | 910 agrege@861#   RESERVE : le repere Nemotron a ete pris sous l'executeur par defaut.62set -u63DEPOT=patdev/k3-a40-bootstrap64MODELE="${VL_MODEL:-RadixArk/Qwen3.8-Flash-Next-NVFP4}"65CTX="${VL_CTX:-262144}"66SEQS="${VL_SEQS:-16}"67 68echo "[FLASHNEXT] $(date -u +%H:%M:%S) modele=$MODELE ctx=$CTX"69mkdir -p /travail && cd /travail70 71for f in patch_ple.py anthropic_proxy.py; do72  for i in 1 2 3 4 5; do73    curl -sL "https://huggingface.co/$DEPOT/resolve/main/$f" -o "/travail/$f" \74      && [ -s "/travail/$f" ] && break75    sleep 476  done77done78 79python3 /travail/patch_ple.py || { echo "[FLASHNEXT] correctif refuse, on arrete"; exit 1; }80find /usr/local/lib/python3.12/dist-packages/vllm/models/qwen3_8_flash_next \81     -name '__pycache__' -type d -exec rm -rf {} + 2>/dev/null82 83# v2 : journal distant. Sans lui, un echec de boot est invisible depuis le84# poste client (image custom = pas de sshd, API de logs peu fiable) et le pod85# facture dans le vide. Publie toutes les 30 s tant que vLLM ne repond pas.86if [ -n "${HF_TOKEN:-}" ]; then87  CLE_J="etat/${RUNPOD_POD_ID:-flashnext}.log"88  echo "[FLASHNEXT] journal : https://huggingface.co/$DEPOT/resolve/main/$CLE_J"89  (90    while true; do91      { echo "=== lancer_flashnext | $(date -u +%H:%M:%S) UTC ==="92        nvidia-smi --query-gpu=memory.used,memory.total --format=csv,noheader 2>/dev/null93        free -g 2>/dev/null | head -294        # v5 : la comptabilite VRAM est au DEBUT du log, donc invisible dans le95        # tail. "Model loading took X GiB" tranche a lui seul si les 4,856 Gio96        # de mtp.* sont residents (78,2 = oui / 73,4 = non), et les lignes de97        # profilage donnent la marge reelle pour dimensionner KVBYTES.98        echo "--- comptabilite VRAM ---"99        grep -aE "Model loading took|model weights take|non_torch_memory|activation peak|Available KV cache|memory profiling|Graph capturing|GPU KV cache size|Maximum concurrency" \100          /travail/vllm.log 2>/dev/null | head -20101        echo "--- vllm.log (fin) ---"; tail -c 40000 /travail/vllm.log 2>/dev/null102        echo "--- pont.log (fin) ---"; tail -c 4000 /travail/pont.log 2>/dev/null103      } > /travail/journal.txt 2>/dev/null104      python3 - "$CLE_J" >/dev/null 2>&1 <<'PYJ'105import os, sys106from huggingface_hub import HfApi107HfApi().upload_file(path_or_fileobj="/travail/journal.txt", path_in_repo=sys.argv[1],108                    repo_id="patdev/k3-a40-bootstrap", commit_message="journal flashnext",109                    token=os.environ["HF_TOKEN"])110PYJ111      curl -sf --max-time 4 http://127.0.0.1:8080/v1/models >/dev/null 2>&1 && sleep 300 || sleep 30112    done113  ) &114fi115 116# --- v5 : plafond explicite du lot de calcul.117# --kv-cache-memory-bytes fait SAUTER le profilage memoire : vLLM ne mesure118# donc jamais le pic d'activation, et ce qui reste apres les poids et119# l'epingle KV doit suffire. C'est pour ca qu'UN prefill plein passe et que120# DEUX tuent le moteur. Borner max-num-batched-tokens borne ce pic, et permet121# de remonter KVBYTES d'autant.122# ATTENTION : le defaut vLLM est DEJA 8192 ("Chunked prefill is enabled with123# max_num_batched_tokens=8192" au boot du 05/09). Il n'y a donc rien a124# recuperer en le baissant, et le monter COUTERAIT de la VRAM. Ce drapeau ne125# sert qu'a monter volontairement le lot pour du debit de prefill.126BATCHED_FLAG=""127if [ -n "${VL_BATCHED:-}" ]; then128  BATCHED_FLAG="--max-num-batched-tokens ${VL_BATCHED}"129  echo "[FLASHNEXT] lot de calcul plafonne a ${VL_BATCHED} jetons"130fi131 132# --- v4 : KV fp8 et YaRN officiel, chacun coupable identifiable.133# VL_KV=off PAR DEFAUT. Flash-Next REFUSE le KV fp8 :134#   NotImplementedError: Qwen3.8-Flash-Next QSA requires a BF16 main KV cache135# L'ancien defaut fp8 obligeait a poser VL_KV=off dans l'env de CHAQUE pod ;136# l'oublier faisait echouer le demarrage avec une trace qui ne nomme pas la137# cause (vu le 05/09). Un defaut qui ne peut jamais marcher est un piege.138# JAMAIS --calculate-kv-scales sur un hybride GDN (vLLM #37554).139KV_FLAG=""140if [ "${VL_KV:-off}" != "off" ]; then141  KV_FLAG="--kv-cache-dtype ${VL_KV:-off}"142  echo "[FLASHNEXT] KV quantifie : ${VL_KV} (sans calculate-kv-scales)"143fi144 145# --- v6 : les surcharges text_config sont assemblees en UN seul --hf-overrides.146# Mesure du 05/09 : le depot NVFP4 ne quantifie que les experts routes (63,32147# Gio) ; le socle reste en BF16 (14,91 Gio), dont 4,856 Gio de mtp.*.148# VL_NO_MTP=1 pose mtp_num_hidden_layers=0 pour ne pas construire la tete.149# ATTENTION : mesure faite, ca ne sert A RIEN ici. "Model loading took150# 74.47 GiB" alors que le socle SANS mtp vaut 73,4 Gio et AVEC 78,2 :151# vLLM ne charge deja pas la tete quand aucun --speculative-config n'est152# passe. Le drapeau ne reste que pour un moteur qui, lui, la chargerait.153# Ne pas se fier au nvidia-smi de 79 104 MiB pendant le chargement : il154# inclut contexte CUDA, cache de l'allocateur et tampons de transit PLE.155# Note : bash n'applique PAS l'expansion d'accolades au resultat d'une156# expansion de variable, donc ce JSON non quote passe intact.157YARN_FLAG=""158OVR_TC=""159if [ "$CTX" -gt 262144 ]; then160  export VLLM_ALLOW_LONG_MAX_MODEL_LEN=1161  OVR_TC='"rope_parameters":{"mrope_interleaved":true,"mrope_section":[11,11,10],"rope_type":"yarn","rope_theta":10000000,"partial_rotary_factor":0.25,"factor":'"${VL_YARN_FACTOR:-4.0}"',"original_max_position_embeddings":262144}'162  echo "[FLASHNEXT] YaRN facteur ${VL_YARN_FACTOR:-4.0} -> contexte $CTX"163fi164 165if [ "${VL_NO_MTP:-0}" = "1" ]; then166  [ -n "$OVR_TC" ] && OVR_TC="$OVR_TC,"167  OVR_TC="$OVR_TC"'"mtp_num_hidden_layers":0'168  echo "[FLASHNEXT] tete MTP non construite (4,856 Gio rendus au pool KV)"169fi170 171if [ -n "$OVR_TC" ]; then172  YARN_FLAG="--hf-overrides {\"text_config\":{$OVR_TC}}"173fi174 175# --- v7 : le disque conteneur (220 Go) ne tient pas DEUX checkpoints.176# Le depot fait ~124 Gio ; en garder deux en cache demande 249 Gio et le177# telechargement meurt disque plein apres vingt minutes. On purge donc les178# autres depots AVANT de tirer celui qu'on veut. Retelecharger coute ~4 min179# a 1 Go/s, un boot perdu en coute quinze.180CACHE=$(python3 -c "from huggingface_hub.constants import HF_HUB_CACHE; print(HF_HUB_CACHE)" 2>/dev/null)181if [ -n "$CACHE" ] && [ -d "$CACHE" ]; then182  # ATTENTION : le Hub nomme ses dossiers "models--<org>--<nom>", avec DEUX183  # tirets comme separateur. Un `tr '/' '-'` n'en met qu'un, la garde ne184  # correspond alors jamais et la purge efface le modele qu'on vient de185  # tirer -- boucle de retelechargement a chaque demarrage (vu le 05/09).186  GARDE="models--$(printf '%s' "$MODELE" | sed 's|/|--|g')"187  for d in "$CACHE"/models--*; do188    [ -d "$d" ] || continue189    if [ "$(basename "$d")" != "$GARDE" ]; then190      echo "[FLASHNEXT] purge du cache : $(basename "$d")"191      rm -rf "$d"192    fi193  done194  df -h "$CACHE" 2>/dev/null | tail -1195fi196 197# --- v9 : vLLM sert NATIVEMENT l'API Anthropic (/v1/messages,198# /v1/messages/count_tokens), enregistree sans drapeau depuis le PR #22627.199# Raison de basculer : l'endpoint natif SUPPRIME les blocs systeme commencant200# par x-anthropic-billing-header, dont le hash change a chaque requete de201# Claude Code et DETRUIT le cache de prefixe. Notre pont ne le faisait pas :202# chaque tour repartait donc d'un prefixe different et repreremplissait tout.203# Il gere aussi les signatures de blocs de reflexion, que le pont bricolait.204# VL_BRIDGE=off -> vLLM ecoute directement sur le seul port expose.205# v10 : VL_BRIDGE=keepalive. `off` reglait le cache de prefixe mais rouvrait la206# coupure du proxy RunPod : il tue toute connexion SILENCIEUSE a ~120 s, et vLLM207# n'emet rien avant le premier jeton, streame ou non. Mesure du 05/09 sur le208# dense : un prompt de 480 K tue a 126,5 s sans un seul jeton. Le petit pont ne209# fait QUE le battement SSE et relaie les octets bruts, donc il garde le cache.210if [ "${VL_BRIDGE:-on}" = "off" ]; then211  VL_HOTE=0.0.0.0; VL_PORT_M=8080212else213  VL_HOTE=127.0.0.1; VL_PORT_M=8000214fi215echo "[FLASHNEXT] vLLM sur $VL_HOTE:$VL_PORT_M (pont=${VL_BRIDGE:-on})"216VLLM_PLE_CPU_OFFLOAD=1 \217# Config retenue le 27/08 apres balayage de huit leviers et quatre phases.218#   --kv-cache-memory-bytes 15 Gio : KV 617 633 jetons, concurrence 2,36x219#     (+22,3 % sur gpu-memory-utilization 0.93 seul). Le KV est LINEAIRE en220#     octets, 26 078 octets/jeton. NE PAS monter a 17 Gio : un prefill plein221#     passe, DEUX simultanes tuent le moteur (EngineDeadError, 83 Mo de VRAM222#     libre). La reserve 'peak activation' de vLLM ne couvre qu'une requete.223#   : +4,6 % d'agrege, +14 % de prefill chaud, et224#     ZERO jeton de capacite -- mais seulement sous budget explicite en octets.225#     Exactitude verifiee en A/B : 5/5 aiguilles des deux cotes a 120 906 j.226#   --kv-sharing-fast-prefill : RETIRE le 27/08. Il rendait +4,6 % d'agrege227#     et +14 % de prefill chaud, exactitude verifiee 5/5. Mais les TROIS morts228#     du moteur (queue.Full dans ple_offload/connector.py:389) ont eu lieu avec229#     lui actif, et aucune sans, sur des dizaines de configurations du balayage.230#     L'invariante du connecteur PLE suppose des forwards SERIALISES ("each231#     forward consumes its output before the next launch") ; ce drapeau touche232#     justement a l'ordonnancement du prefill. Correlation non prouvee -- la233#     charge multi-agents a commence en meme temps -- mais 4,6 % ne paient pas234#     un moteur qui meurt toutes les quinze minutes sous 4 requetes.235#   --enable-flashinfer-autotune : +8,2 % en solo. Il etait coupe pour gagner236#     ~40 s au demarrage, mauvais echange sur un serveur qui tourne des heures.237#   --limit-mm-per-prompt {"image":4,...} : ce plafond etait a 0, et vLLM le238#     fait respecter par un 400 dont le texte est litteralement le plafond --239#     "At most 0 image(s) may be provided in one prompt. (parameter=image)".240#     Une capture collee dans Claude Code cassait donc le tour, et l'erreur241#     ressemblait a une panne du modele alors que c'etait notre reglage.242#     Le cout est nul ou presque : la tour de vision pese 0,90 Go sur les243#     135,16 du depot (0,449 B parametres BF16, 333 tenseurs model.visual.*)244#     et elle est DEJA residente -- le plafond ne gouvernait que la validation245#     des requetes et le profilage, pas le chargement des poids.246#     La video reste a 0 : temporal_patch_size 2 et de nombreuses images par247#     seconde feraient exploser le budget d'activation, et Claude Code n'en248#     envoie jamais.249#   KV 15,0 -> 14,5 GiB EN MEME TEMPS, et c'est lie. Mesure du 27/08 sur le pod250#     en service : 96 612 MiB utilises sur 97 887, soit 1,25 GiB libres. Or251#     --kv-cache-memory-bytes fait SAUTER le profilage memoire ("skipped memory252#     profiling" dans le log) : vLLM ne recalculera donc rien tout seul et253#     reservera ses 15 GiB quoi qu'il arrive. Encoder une image demande des254#     activations transitoires qu'on n'a jamais mesurees ici, et on a deja tue255#     ce moteur trois fois pour avoir vise trop haut. 0,5 GiB rendus coutent256#     ~3,3 % de cache (617 633 -> ~597 000 jetons) et achetent la marge.257setsid nohup vllm serve "$MODELE" \258  --served-model-name flashnext --host "$VL_HOTE" --port "$VL_PORT_M" --trust-remote-code \259  --max-model-len "$CTX" --max-num-seqs "$SEQS" --enable-prefix-caching \260  --gpu-memory-utilization 0.93 --distributed-executor-backend mp \261  --reasoning-parser qwen3 --tool-call-parser qwen3_coder --enable-auto-tool-choice \262  --enable-flashinfer-autotune --limit-mm-per-prompt '{"image":4,"video":0}' \263  --kv-cache-memory-bytes "${KVBYTES:-15569256448}" \264  $BATCHED_FLAG $KV_FLAG $YARN_FLAG \265  --enable-prompt-tokens-details \266  > /travail/vllm.log 2>&1 < /dev/null &267 268python3 -c "import fastapi, httpx, uvicorn" 2>/dev/null \269  || pip install -q fastapi uvicorn httpx270 271for i in $(seq 1 45); do272  sleep 20273  curl -sf --max-time 5 http://127.0.0.1:$VL_PORT_M/v1/models >/dev/null 2>&1 && break274  if grep -aq "Engine core initialization failed\|EngineDeadError" /travail/vllm.log; then275    echo "[FLASHNEXT] vLLM a plante :"276    # v8 : un grep "Error" en head -6 ne donne QUE le nom de l'exception, pas277    # le module ni le parametre fautif -- inutilisable pour diagnostiquer un278    # echec de chargement de poids. On publie la vraie fin du log, qui contient279    # la trace complete, et on la met de cote sur le Hub avant que la tentative280    # suivante n'ecrase le journal.281    echo "----- fin de vllm.log -----"282    tail -c 8000 /travail/vllm.log283    echo "----- fin de la trace -----"284    if [ -n "${HF_TOKEN:-}" ]; then285      tail -c 60000 /travail/vllm.log > /travail/plantage.txt 2>/dev/null286      python3 - >/dev/null 2>&1 <<'PYP'287import os288from huggingface_hub import HfApi289HfApi().upload_file(path_or_fileobj="/travail/plantage.txt",290                    path_in_repo="etat/%s-plantage.log" % os.environ.get("RUNPOD_POD_ID", "flashnext"),291                    repo_id="patdev/k3-a40-bootstrap", commit_message="trace de plantage",292                    token=os.environ["HF_TOKEN"])293PYP294      echo "[FLASHNEXT] trace complete : etat/${RUNPOD_POD_ID:-flashnext}-plantage.log"295    fi296    exit 1297  fi298done299grep -a "KV cache size\|Maximum concurrency\|Worker ready" /travail/vllm.log | tail -3300 301# Le pod n'expose qu'un port : le pont le prend, et relaie /v1/chat/completions302# vers vLLM pour que les outils OpenAI restent joignables de l'exterieur.303if [ "${VL_BRIDGE:-on}" = "off" ]; then304  echo "[FLASHNEXT] pont DESACTIVE : vLLM sert /v1/messages en direct"305elif [ "${VL_BRIDGE:-on}" = "keepalive" ]; then306  # Le pont minimal : relais brut + `: ping` toutes les 15 s. Il traduit aussi307  # `output_config.effort` (que vLLM refuse au niveau `max`) et borne la308  # reflexion `adaptive`, qui sans plafond laisse un tour partir en minutes.309  curl -sL "https://huggingface.co/patdev/k3-a40-bootstrap/resolve/main/keepalive_proxy.py" \310    -o /travail/keepalive_proxy.py 2>/dev/null311  if [ -s /travail/keepalive_proxy.py ]; then312    VL_UPSTREAM="http://127.0.0.1:$VL_PORT_M" \313    VL_THINK_BUDGET="${VL_THINK_BUDGET:-4096}" \314    setsid nohup python3 -m uvicorn keepalive_proxy:app --app-dir /travail \315      --host 0.0.0.0 --port 8080 --log-level warning \316      > /travail/pont.log 2>&1 < /dev/null &317    echo "[FLASHNEXT] pont keepalive sur 0.0.0.0:8080 (relais brut + ping 15 s)"318  else319    # Ne pas continuer en silence : sans pont, vLLM ecoute sur un port NON320    # expose et le service serait injoignable sans qu'aucune ligne ne le dise.321    echo "[FLASHNEXT] ERREUR pont keepalive non telecharge -- service injoignable"322  fi323else324echo "[FLASHNEXT] pont Anthropic sur 0.0.0.0:8080"325# VL_TIMEOUT : le defaut d'anthropic_proxy.py est 1800 s, et il est piegeux.326# Quand le client abandonne (classifieur de securite qui expire sous charge),327# le pont GARDE sa connexion amont ouverte une demi-heure et immobilise une328# place du bassin httpx. Assez d'abandons et le bassin sature : /v1/models329# continue de repondre 200 pendant que TOUT le reste rend 500 en 10 s330# (pool=10.0). Observe deux fois le 27/08, chaque fois en session multi-agents.331# 240 s borne la fuite sans gener le pire cas legitime : un prefill froid de332# 262 k a 11 200 j/s prend 23 s, plus la generation.333VL_UPSTREAM=http://127.0.0.1:8000 \334VL_SERVED_NAME=flashnext \335VL_REAL_MODEL="$MODELE" \336VL_MAX_OUTPUT=32768 \337VL_TIMEOUT=240 \338setsid nohup python3 -m uvicorn anthropic_proxy:app --host 0.0.0.0 --port 8080 \339  --log-level warning > /travail/pont.log 2>&1 < /dev/null &340fi341 342for i in $(seq 1 20); do343  sleep 3344  curl -sf --max-time 4 http://127.0.0.1:8080/v1/models >/dev/null 2>&1 \345    && { echo "[FLASHNEXT] PRET a $(date -u +%H:%M:%S)"; break; }346done347curl -s --max-time 8 http://127.0.0.1:8080/v1/models -H 'anthropic-version: 2023-06-01' \348 | head -c 200; echo349 350# Cote client, DEUX variables sont necessaires : la seconde est plafonnee par la351# premiere. Et effacer ANTHROPIC_API_KEY, qui prime sur ANTHROPIC_AUTH_TOKEN et352# renvoie vers l'API officielle -- facturee, sans le dire.353echo "[FLASHNEXT] cote client :"354echo "  ANTHROPIC_BASE_URL=https://<pod>-8080.proxy.runpod.net"355echo "  ANTHROPIC_AUTH_TOKEN=x   ANTHROPIC_MODEL=claude-flashnext"356echo "  CLAUDE_CODE_DISABLE_UNKNOWN_MODEL_WINDOW_ENFORCEMENT=1"357echo "  CLAUDE_CODE_AUTO_COMPACT_WINDOW=$CTX"358sleep infinity359