網頁

2026年1月2日 星期五

vscode 的 vibe coding

點選左側 continue icon
拉寬 continue 視窗
點選 continue 視窗右上側齒輪
點選左側 Models
點選 Chat 右側齒輪
自動出現 config.yaml 檔案, 或手動開啟 %USERPROFILE%\.continue\config.yaml

name: Local Config
version: 1.0.0
schema: v1
models:
  - name: gpt-oss-120b
    provider: vllm
    model: gpt-oss-120b
    apiBase: http://192.168.0.108:8000/v1
    title: gpt-oss-120b
    roles:
      - chat
      - edit
      - autocomplete
      - apply
  - name: Llama-3.1-8B-Instruct
    provider: vllm
    model: Llama-3.1-8B-Instruct
    apiBase: https://www.sunhousetechnology.com.tw/trtllm-Llama-3.1-8B-Instruct/v1
    title: Llama-3.1-8B-Instruct
    roles:
      - chat
      - edit
      - autocomplete
      - apply
      
點選 continue 視窗右上 "Local Config v" 選擇 Reload

=========================
安裝 Cline 完成後 選 Login to Cline, 開啟網頁, 同意授權
回 vscode, 有對話框 Allow 'Cline' extension to open this URL
按 Open
點選左側 cline icon
點選 cline 視窗右上側齒輪
API Provider: OpenAI Compatible
Base URL: http://192.168.0.108:8000
OpenAI Compatible API Key: token-abc123
Model ID: gpt-oss-120b
點選 cline 視窗右上 Done

=========================
安裝 Remote-SSH
點選左側 Remote Explorer
在 REMOTE/SSH 右側按齒輪
中間視窗上面選擇 C:/Users/user_name/.ssh/config
# Read more about SSH config files: https://linux.die.net/man/5/ssh_config
Host hostA
    HostName user_ip
    User user_name
    Port 2201
Host hostB
    HostName user_ip
    User user_name
    Port 2202
在 REMOTE/SSH/spark 右側可以選擇
Connect in Current Window... 或 Connect in New Window...
第一次登入時在中間視窗上面選擇 linux, 選擇 continue
中間視窗上面輸入密碼

DGX Spark 使用 LLM 經歷

同一模型使用 TRT LLM 比使用 vLLM 好, 不論有無轉成 nvfp4

10GB 以下 Qwen2.5-Coder-7B
10GB 以下 Qwen2.5-Coder-32B-Instruct
30GB 以下 gpt-oss-20b
30GB 以下 Llama-3.1-8B-Instruct
上述模型耗記憶體和反應速度都不錯,只是 Qwen2.5-Coder-7B 對話不行

gpt-oss-120b 耗記憶體,速度有些慢

Llama-3.3-70B-Instruct 耗記憶體,速度超慢
# 原本應該從 meta-llama/Llama-3.3-70B-Instruct 下載模型,再轉成 nvfp4
# 但是下載的模型不只無法轉成 nvfp4, 也無法執行
# 發現有 nvidia/Llama-3.3-70B-Instruct-NVFP4 直接下載使用

將 open webui 的 http 轉成 https

產生證書,需先執行一個 nginx 在 80 port 上,以便 cerbot 產生證書
$ sta_nginx_certbot.sh
$ docker run --rm -it \
  -v $(pwd)/nginx/certs:/etc/letsencrypt \
  -v $(pwd)/nginx/acme:/var/www/certbot \
  certbot/certbot certonly \
  --webroot \
  -w /var/www/certbot \
  -d www.sunhousetechnology.com.tw
證書產生在下列目錄
nginx/certs/live/www.sunhousetechnology.com.tw/fullchain.pem
nginx/certs/live/www.sunhousetechnology.com.tw/privkey.pem
停止 nginx_certbot

docker nginx 加入 nginx_certbot.conf, 以便在更新證書時,不用停止 nginx
server {
    listen 80 default_server;
    server_name _;

    root /var/www/html;
    index index.html index.htm index.nginx-debian.html;
    
    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        return 200 "nginx certbot ok\n";
    }
}

更新證書
$ docker run --rm \
  -v $(pwd)/nginx/certs:/etc/letsencrypt \
  -v $(pwd)/nginx/acme:/var/www/certbot \
  certbot/certbot renew

測試證書
$ docker run --rm \
  -v $(pwd)/nginx/certs:/etc/letsencrypt \
  -v $(pwd)/nginx/acme:/var/www/certbot \
  certbot/certbot renew --dry-run


# 1. Nginx 是否在 webnet
docker network inspect webnet
# 2. open-webui 是否在 webnet
docker ps
# 3. Nginx 內部能否解析
docker exec -it nginx getent hosts open-webui
# 4. Nginx 內部直連測試
docker exec -it nginx curl http://open-webui:8080
# nginx.conf 修改後 nginx 重開
docker restart nginx
# 測試命令
curl -k -H "Cache-Control: no-cache" \
     -H "Pragma: no-cache" \
     https://www.sunhousetechnology.com.tw/

為了避免一些 docker 沒有啟動,造成 nginx 無法啟動,將
    location /sub-path/ {
        proxy_pass http://sub-path:8001;
    }
改成
    location /sub-path/ {
        set $p8001 http://sub-path:8001;
        rewrite ^/sub-path/(.*)$ /$1 break;
        proxy_pass $p8001;
    }

open-webui 無法掛到 https://www.sunhousetechnology.com.tw/open-webui 必須使用 /
    location / {
        set $webui http://open-webui:8080;
        # 無法使用 rewrite, 只能用 location /
        proxy_pass $webui;
        # 避免下列 WebSocket 錯誤
        # "GET /ws/socket.io/?EIO=4&transport=websocket HTTP/1.1" 400 46
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

原先也要加入 dashboard, 但兩者同時都使用 WebSocket, 而且是 GET /ws 開頭,所以只好放棄
    location /dashboard/ {
        set $dashboard http://dashboard:8080;
        rewrite ^/dashboard/(.*)$ /$1 break;
        proxy_pass $dashboard;
        # 避免下列 WebSocket 錯誤
        # "GET /ws HTTP/1.1" 500 21
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

2025年12月30日 星期二

DGX Spark 使用 TRT LLM

參考 https://build.nvidia.com/spark 之下的
TRT LLM for Inference
NVFP4 Quantization
參考 https://nvidia.github.io/TensorRT-LLM/1.0.0rc2/commands/trtllm-serve.html

# Configure Docker permissions
$ sudo usermod -aG docker $USER
$ newgrp docker
$ id
uid=1000(spark) gid=988(docker) groups=988(docker),4(adm),27(sudo),29(audio),30(dip),46(plugdev),100(users),122(lpadmin),1000(spark)
$ ps
    PID TTY          TIME CMD
   6123 pts/1    00:00:00 bash
  24590 pts/1    00:00:00 bash
  24597 pts/1    00:00:00 ps

# Verify environment prerequisites
$ nvidia-smi
$ docker run --rm --gpus all nvcr.io/nvidia/tensorrt-llm/release:spark-single-gpu-dev nvidia-smi

$ export HF_TOKEN=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
$ docker run --rm -it --gpus all \
  nvcr.io/nvidia/tensorrt-llm/release:spark-single-gpu-dev \
  python -c "import tensorrt_llm; print(f'TensorRT-LLM version: {tensorrt_llm.__version__}')"
倒數兩行的輸出
[TensorRT-LLM] TensorRT-LLM version: 1.1.0rc3
TensorRT-LLM version: 1.1.0rc3

# Create Hugging Face cache directory
$ mkdir -p $HOME/.cache/huggingface/
## 若有需要改變目錄位置
$ export HF_HOME=/mnt/Data/huggingface

$ export MODEL_HANDLE="openai/gpt-oss-20b"
$ docker run \
  -e MODEL_HANDLE=$MODEL_HANDLE \
  -e HF_TOKEN=$HF_TOKEN \
  -v $HOME/.cache/huggingface/:/root/.cache/huggingface/ \
  --rm -it --ulimit memlock=-1 --ulimit stack=67108864 \
  --gpus=all --ipc=host --network host \
  nvcr.io/nvidia/tensorrt-llm/release:spark-single-gpu-dev \
  bash -c '
    export TIKTOKEN_ENCODINGS_BASE="/tmp/harmony-reqs" && \
    mkdir -p $TIKTOKEN_ENCODINGS_BASE && \
    wget -P $TIKTOKEN_ENCODINGS_BASE https://openaipublic.blob.core.windows.net/encodings/o200k_base.tiktoken && \
    wget -P $TIKTOKEN_ENCODINGS_BASE https://openaipublic.blob.core.windows.net/encodings/cl100k_base.tiktoken && \
    hf download $MODEL_HANDLE && \
    python examples/llm-api/quickstart_advanced.py \
      --model_dir $MODEL_HANDLE \
      --prompt "Paris is great because" \
      --max_tokens 64
    '

==================
# Serve LLM with OpenAI-compatible API
$ export MODEL_HANDLE="openai/gpt-oss-20b"
$ export MODEL_HANDLE="openai/gpt-oss-120b"
$ export MODEL_HANDLE="meta-llama/Llama-3.3-70B-Instruct"
$ export MODEL_HANDLE="Qwen/Qwen3-4B-Instruct-2507"
$ export MODEL_HANDLE="deepseek-ai/DeepSeek-R1-Distill-Llama-8B'"

$ docker run --name trtllm_llm_server --rm -it --gpus all --ipc host --network host \
  -e HF_TOKEN=$HF_TOKEN \
  -e MODEL_HANDLE="$MODEL_HANDLE" \
  -v $HOME/.cache/huggingface/:/root/.cache/huggingface/ \
  nvcr.io/nvidia/tensorrt-llm/release:spark-single-gpu-dev \
  bash -c '
    export TIKTOKEN_ENCODINGS_BASE="/tmp/harmony-reqs" && \
    mkdir -p $TIKTOKEN_ENCODINGS_BASE && \
    wget -P $TIKTOKEN_ENCODINGS_BASE https://openaipublic.blob.core.windows.net/encodings/o200k_base.tiktoken && \
    wget -P $TIKTOKEN_ENCODINGS_BASE https://openaipublic.blob.core.windows.net/encodings/cl100k_base.tiktoken && \
    hf download $MODEL_HANDLE && \
    cat > /tmp/extra-llm-api-config.yml <<EOF
print_iter_log: false
kv_cache_config:
  dtype: "auto"
  free_gpu_memory_fraction: 0.4
cuda_graph_config:
  enable_padding: true
disable_overlap_scheduler: true
EOF
    trtllm-serve "$MODEL_HANDLE" \
      --max_batch_size 8 \
      --trust_remote_code \
      --host 0.0.0.0 \
      --port 8000 \
      --extra_llm_api_options /tmp/extra-llm-api-config.yml
  '
$ curl -s http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "'"$MODEL_HANDLE"'",
    "messages": [{"role": "user", "content": "請你自我介紹"}],
    "max_tokens": 64
  }'

# Cleanup and rollback
sudo chown -R "$USER:$USER" "$HOME/.cache/huggingface"
rm -rf $HOME/.cache/huggingface/
docker image prune -f
docker rmi nvcr.io/nvidia/tensorrt-llm/release:spark-single-gpu-dev


==================
# NVFP4 Quantization
$ mkdir -p ./output_models
$ chmod 755 ./output_models
# 使用 huggingface 的模型, 轉換模型
$ docker run --rm -it --gpus all --ipc=host --ulimit memlock=-1 --ulimit stack=67108864 \
  -v "./output_models:/workspace/output_models" \
  -v "$HOME/.cache/huggingface:/root/.cache/huggingface" \
  -e HF_TOKEN=$HF_TOKEN \
  nvcr.io/nvidia/tensorrt-llm/release:spark-single-gpu-dev \
  bash -c "
    git clone -b 0.35.0 --single-branch https://github.com/NVIDIA/Model-Optimizer.git /app/TensorRT-Model-Optimizer && \
    cd /app/TensorRT-Model-Optimizer && pip install -e '.[dev]' && \
    export ROOT_SAVE_PATH='/workspace/output_models' && \
    /app/TensorRT-Model-Optimizer/examples/llm_ptq/scripts/huggingface_example.sh \
    --model $MODEL_HANDLE \
    --quant nvfp4 \
    --tp 1 \
    --export_fmt hf
  "
# 出現 pynvml.NVMLError_NotSupported: Not Supported 錯誤,不用怕
# 使用本地端的模型,轉換模型
$ docker run --rm -it --gpus all --ipc=host --ulimit memlock=-1 --ulimit stack=67108864 \
  -v "./output_models:/workspace/output_models" \
  -v /mnt/models:/mnt/models \
  -v "$HOME/.cache/huggingface:/root/.cache/huggingface" \
  -e HF_TOKEN=$HF_TOKEN \
  nvcr.io/nvidia/tensorrt-llm/release:spark-single-gpu-dev \
  bash -c "
    git clone -b 0.35.0 --single-branch https://github.com/NVIDIA/Model-Optimizer.git /app/TensorRT-Model-Optimizer && \
    cd /app/TensorRT-Model-Optimizer && pip install -e '.[dev]' && \
    export ROOT_SAVE_PATH='/workspace/output_models' && \
    /app/TensorRT-Model-Optimizer/examples/llm_ptq/scripts/huggingface_example.sh \
    --model /mnt/models/Qwen2.5-Coder-7B \
    --quant nvfp4 \
    --tp 1 \
    --export_fmt hf
  "

$ ls -la ./output_models/
$ find ./output_models/ -name "*.bin" -o -name "*.safetensors" -o -name "config.json"
$ export MODEL_PATH="./output_models/saved_models_DeepSeek-R1-Distill-Llama-8B_nvfp4_hf/"
$ export MODEL_PATH="./output_models/saved_models_Qwen3-4B-Instruct-2507_nvfp4_hf/"
# 使用轉換完成的模型
$ docker run \
  -e HF_TOKEN=$HF_TOKEN \
  -v $HOME/.cache/huggingface/:/root/.cache/huggingface/ \
  -v "$MODEL_PATH:/workspace/model" \
  --rm -it --ulimit memlock=-1 --ulimit stack=67108864 \
  --gpus=all --ipc=host --network host \
  nvcr.io/nvidia/tensorrt-llm/release:spark-single-gpu-dev \
  bash -c '
    python examples/llm-api/quickstart_advanced.py \
      --model_dir /workspace/model/ \
      --prompt "Paris is great because" \
      --max_tokens 64
    '
# Serve the model with OpenAI-compatible API
$ docker run \
  -e HF_TOKEN=$HF_TOKEN \
  -v "$MODEL_PATH:/workspace/model" \
  --rm -it --ulimit memlock=-1 --ulimit stack=67108864 \
  --gpus=all --ipc=host --network host \
  nvcr.io/nvidia/tensorrt-llm/release:spark-single-gpu-dev \
  trtllm-serve /workspace/model \
    --backend pytorch \
    --max_batch_size 4 \
    --host 0.0.0.0 \
    --port 8000
$ curl -X POST http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-oss-20b",
    "messages": [{"role": "user", "content": "What is artificial intelligence?"}],
    "max_tokens": 100,
    "temperature": 0.7,
    "stream": false
  }'

==================
# trtllm-serve 使用本地端模型
$ export MODEL_HANDLE="/mnt/models/gpt-oss-20b"    # 0.8:42.6GB 48W 16s | 0.4:26.1GB 48W 21s | 0.2:26.6GB 46W 17s
$ export MODEL_HANDLE="/mnt/models/gpt-oss-120b"   # 0.8:  72GB 50W 35s | 0.5:70.4GB 49W 40s | 0.4:68.7GB 49W 39s 
$ docker run --name trtllm_llm_server --rm -it --gpus all --ipc host --network host \
  -e MODEL_HANDLE="$MODEL_HANDLE" \
  -v /mnt/models:/mnt/models \
  -v $HOME/.cache/huggingface/:/root/.cache/huggingface/ \
  nvcr.io/nvidia/tensorrt-llm/release:spark-single-gpu-dev \
  bash -c '
    export TIKTOKEN_ENCODINGS_BASE="/tmp/harmony-reqs" && \
    mkdir -p $TIKTOKEN_ENCODINGS_BASE && \
    wget -P $TIKTOKEN_ENCODINGS_BASE https://openaipublic.blob.core.windows.net/encodings/o200k_base.tiktoken && \
    wget -P $TIKTOKEN_ENCODINGS_BASE https://openaipublic.blob.core.windows.net/encodings/cl100k_base.tiktoken && \
    cat > /tmp/extra-llm-api-config.yml <<EOF
print_iter_log: false
kv_cache_config:
  dtype: "auto"
  free_gpu_memory_fraction: 0.5
cuda_graph_config:
  enable_padding: true
disable_overlap_scheduler: true
EOF

    trtllm-serve "$MODEL_HANDLE" \
      --max_batch_size 8 \
      --max_seq_len 65536 \
      --max_num_tokens 131072 \
      --trust_remote_code \
      --host 0.0.0.0 \
      --port 8000 \
      --extra_llm_api_options /tmp/extra-llm-api-config.yml
  '

DGX Spark 使用 vLLM server

經測試發現 vLLM 在記憶體和功耗上都比 TRT LLM差

$ sudo mount -t nfs 192.168.0.107:/mnt/Data/LangGraph/HuggingFace/models /mnt/models

$ export MODEL_HANDLE="/mnt/models/gpt-oss-20b"    # 0.8:96.9GB 43W 148s | 0.4:49.4GB 39W 120s
$ export MODEL_HANDLE="/mnt/models/gpt-oss-120b"   # 0.8:97.9GB 41W  75s | 0.7:86.5GB 40W 104s

$ docker run --rm --name vllm_server -it --gpus all \
-p 8000:8000 \
-v /mnt/models:/models \
nvcr.io/nvidia/vllm:25.11-py3 \
vllm serve "/models/gpt-oss-20b" \
--trust_remote_code \
--max-num-seqs 2 \
--quantization mxfp4 \
--gpu-memory-utilization 0.3 \
--served-model-name llm_chat \
--api-key token-abc123

若執行失敗,可清除記憶體,再試一遍
$ sudo sh -c 'sync && echo 3 > /proc/sys/vm/drop_caches'

DGX Spark 之 Open WebUI 配合 vLLM 或 TRT LLM

# ollama 連接 vllm 或 trt_llm
$ docker run -d --rm \
  --name open-webui-vllm \
  -p 8501:8080 \
  -v open-webui:/app/backend/data \
  -e OPENAI_API_BASE_URL=http://192.168.0.108:8000/v1 \
  -e OPENAI_API_KEY=token-abc123 \
  ghcr.io/open-webui/open-webui:main
其中 8080 為固定,不需改變
開啟 Open WebUI(http://localhost:8501)
登入後點選左下 使用者 → Admin Panel → Settings → Connections → OpenAI
你會看到已自動配置的 OpenAI 連線(指向 vLLM)

上述命令只可使用單一 vLLM 模型
改用下述命令,由網頁手動輸入多組 vLLM 模型
$ docker run -d --rm --gpus=all \
  -p 8501:8080 \
  -v open-webui:/app/backend/data \
  -v open-webui-ollama:/root/.ollama \
  --name open-webui ghcr.io/open-webui/open-webui:ollama
若啟動失敗,應該是 open-webui volume 不一致,使用下列命令
$ docker volume rm open-webui

網頁左下使用者名稱/Admin Panel/Settings/Connections/ +
URL: http://192.168.0.108:8000/v1
按 Save

DGX Spark 使用 dashboard

參考 https://github.com/DanTup/dgx_dashboard
可以觀察 記憶體使用量,溫度,功耗,Docker Container

$ docker run -d --gpus all \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -p 8080:8080 \
    --pull=always \
    --restart=unless-stopped \
    --name dashboard \
    ghcr.io/dantup/dgx_dashboard:latest
    
$ docker stop dashboard && docker rm dashboard

firefox http://192.168.0.108:8080/