convertor/scripts/download-models.sh
Your Name 55b0f519ab refactor(docker): 完整重構 Dockerfile v0.1.16
BREAKING CHANGE: Dockerfile 結構重新設計

## 重構內容

### Multi-Stage Build 結構
- Stage 1 [base]: Bun runtime 基礎
- Stage 2 [install]: Node 依賴安裝
- Stage 3 [prerelease]: 應用程式建構
- Stage 4 [system-tools]: APT 系統工具(拆分為 14 個獨立 RUN)
- Stage 5 [fonts]: 字型安裝
- Stage 6 [python-tools]: Python CLI 工具(拆分為 9 個獨立 RUN)
- Stage 7 [models]: 模型下載
- Stage 8 [release]: 最終 Image

### 新增 Scripts
- install-system-tools.sh: 系統工具安裝
- install-fonts.sh: 字型安裝
- install-python-tools.sh: Python 工具安裝
- download-models.sh: 模型下載
- verify-installation.sh: 完整安裝驗證

### 刪除舊檔案
- Dockerfile.backup
- Dockerfile.offline
- Dockerfile.old

### 路徑更新
- MinerU 模型: /opt/convertx/models/mineru/PDF-Extract-Kit-1.0
- BabelDOC 快取: /root/.cache/babeldoc

### Multi-Arch 支援
- linux/amd64: 功能完整
- linux/arm64: 安全降級(MinerU/resvg 跳過)

Closes #docker-refactor
2026-01-25 13:15:31 +08:00

306 lines
9.6 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# ==============================================================================
# ConvertX-CN 模型下載腳本
# ==============================================================================
# 用途:下載所有 AI 模型MinerU, BabelDOC, PDFMathTranslate
# 執行環境Docker build stage (models-download)
#
# ⚠️ 重要:此腳本在 build time 執行,下載所有模型到固定目錄
# Runtime 完全離線,不得有任何下載行為
# ==============================================================================
set -euo pipefail
# 顏色輸出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
log_info() { echo -e "${BLUE}📥 [INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}✅ [SUCCESS]${NC} $1"; }
log_warn() { echo -e "${YELLOW}⚠️ [WARN]${NC} $1"; }
log_error() { echo -e "${RED}❌ [ERROR]${NC} $1"; }
# 架構檢測
ARCH=$(uname -m)
log_info "檢測架構: ${ARCH}"
# ==============================================================================
# 目錄定義
# ==============================================================================
MODELS_BASE_DIR="/opt/convertx/models"
MINERU_MODELS_DIR="${MODELS_BASE_DIR}/mineru"
BABELDOC_CACHE_DIR="/root/.cache/babeldoc"
BABELDOC_MODELS_DIR="${BABELDOC_CACHE_DIR}/models"
BABELDOC_FONTS_DIR="${BABELDOC_CACHE_DIR}/fonts"
BABELDOC_CMAP_DIR="${BABELDOC_CACHE_DIR}/cmap"
BABELDOC_TIKTOKEN_DIR="${BABELDOC_CACHE_DIR}/tiktoken"
# ==============================================================================
# 創建目錄結構
# ==============================================================================
create_directories() {
log_info "創建模型目錄結構..."
mkdir -p "${MINERU_MODELS_DIR}"
mkdir -p "${BABELDOC_MODELS_DIR}"
mkdir -p "${BABELDOC_FONTS_DIR}"
mkdir -p "${BABELDOC_CMAP_DIR}"
mkdir -p "${BABELDOC_TIKTOKEN_DIR}"
log_success "目錄結構創建完成"
}
# ==============================================================================
# 下載 MinerU Pipeline 模型
# ==============================================================================
download_mineru_models() {
log_info "下載 MinerU Pipeline 模型..."
if [ "${ARCH}" = "aarch64" ]; then
log_warn "ARM64跳過 MinerU 模型下載(不支援)"
return 0
fi
python3 << 'PYTHON'
from huggingface_hub import snapshot_download
import os
models_dir = os.environ.get('MINERU_MODELS_DIR', '/opt/convertx/models/mineru')
print(f'下載 PDF-Extract-Kit-1.0 到 {models_dir}...')
try:
snapshot_download(
repo_id='opendatalab/PDF-Extract-Kit-1.0',
local_dir=f'{models_dir}/PDF-Extract-Kit-1.0',
local_dir_use_symlinks=False,
resume_download=True
)
print('✅ PDF-Extract-Kit-1.0 下載完成')
except Exception as e:
print(f'❌ 下載失敗: {e}')
raise
PYTHON
# 顯示模型大小
log_info "MinerU 模型目錄內容:"
du -sh "${MINERU_MODELS_DIR}/PDF-Extract-Kit-1.0" 2>/dev/null || true
ls -la "${MINERU_MODELS_DIR}/" 2>/dev/null || true
log_success "MinerU 模型下載完成"
}
# ==============================================================================
# 產生 MinerU 配置檔
# ==============================================================================
generate_mineru_config() {
log_info "產生 MinerU 配置檔..."
if [ "${ARCH}" = "aarch64" ]; then
log_warn "ARM64跳過 MinerU 配置"
return 0
fi
python3 << 'PYTHON'
import json
import os
mineru_models_dir = os.environ.get('MINERU_MODELS_DIR', '/opt/convertx/models/mineru')
config = {
'models-dir': {
'pipeline': f'{mineru_models_dir}/PDF-Extract-Kit-1.0',
'vlm': ''
},
'model-source': 'local',
'latex-delimiter-config': {
'display': {'left': '@@', 'right': '@@'},
'inline': {'left': '@', 'right': '@'}
}
}
# 寫入多個位置
config_paths = [
'/opt/convertx/mineru.json',
'/root/mineru.json'
]
for path in config_paths:
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, 'w') as f:
json.dump(config, f, indent=2)
print(f'✅ 已產生: {path}')
print(json.dumps(config, indent=2))
PYTHON
log_success "MinerU 配置檔產生完成"
}
# ==============================================================================
# BabelDOC warmup下載所有資源
# ==============================================================================
babeldoc_warmup() {
log_info "執行 BabelDOC warmup..."
export BABELDOC_CACHE_PATH="${BABELDOC_CACHE_DIR}"
if command -v babeldoc >/dev/null 2>&1; then
babeldoc --warmup 2>&1 || {
log_warn "BabelDOC warmup 可能有警告,但繼續"
}
log_info "BabelDOC 快取內容:"
ls -la "${BABELDOC_CACHE_DIR}/" 2>/dev/null || true
du -sh "${BABELDOC_CACHE_DIR}/" 2>/dev/null || true
else
log_warn "babeldoc 不可用,跳過 warmup"
fi
log_success "BabelDOC warmup 完成"
}
# ==============================================================================
# 下載 tiktoken 編碼
# ==============================================================================
download_tiktoken() {
log_info "下載 tiktoken 編碼..."
python3 << 'PYTHON'
try:
import tiktoken
encodings = ['cl100k_base', 'p50k_base', 'r50k_base']
for enc_name in encodings:
try:
enc = tiktoken.get_encoding(enc_name)
print(f'✅ tiktoken {enc_name} 已下載')
except Exception as e:
print(f'⚠️ tiktoken {enc_name} 下載失敗: {e}')
except ImportError:
print('⚠️ tiktoken 未安裝,跳過')
PYTHON
log_success "tiktoken 編碼下載完成"
}
# ==============================================================================
# 複製預下載的 ONNX 模型(從 COPY 複製)
# ==============================================================================
setup_onnx_models() {
log_info "設定 ONNX 模型..."
# 如果有預下載的模型,複製到正確位置
local ONNX_MODEL="doclayout_yolo_docstructbench_imgsz1024.onnx"
local SRC_PATHS=(
"/app/models/${ONNX_MODEL}"
"/tmp/models/${ONNX_MODEL}"
)
for src in "${SRC_PATHS[@]}"; do
if [ -f "${src}" ]; then
cp "${src}" "${BABELDOC_MODELS_DIR}/"
log_success "已複製 ONNX 模型: ${src} -> ${BABELDOC_MODELS_DIR}/"
return 0
fi
done
log_warn "未找到預下載的 ONNX 模型,將依賴 babeldoc --warmup"
}
# ==============================================================================
# 驗證下載結果
# ==============================================================================
verify_downloads() {
log_info "驗證模型下載..."
echo ""
echo "模型目錄統計:"
echo "=============================================="
# MinerU
if [ "${ARCH}" != "aarch64" ]; then
if [ -d "${MINERU_MODELS_DIR}/PDF-Extract-Kit-1.0" ]; then
local size
size=$(du -sh "${MINERU_MODELS_DIR}/PDF-Extract-Kit-1.0" 2>/dev/null | awk '{print $1}')
echo " ✅ MinerU Pipeline: ${size}"
else
echo " ❌ MinerU Pipeline: 不存在"
fi
if [ -f "/root/mineru.json" ]; then
echo " ✅ MinerU 配置檔: 已產生"
else
echo " ❌ MinerU 配置檔: 不存在"
fi
else
echo " ⚠️ MinerU: ARM64 跳過"
fi
# BabelDOC
if [ -d "${BABELDOC_CACHE_DIR}" ]; then
local size
size=$(du -sh "${BABELDOC_CACHE_DIR}" 2>/dev/null | awk '{print $1}')
echo " ✅ BabelDOC 快取: ${size}"
else
echo " ⚠️ BabelDOC 快取: 不存在"
fi
# ONNX 模型
if [ -f "${BABELDOC_MODELS_DIR}/doclayout_yolo_docstructbench_imgsz1024.onnx" ]; then
local size
size=$(ls -lh "${BABELDOC_MODELS_DIR}/doclayout_yolo_docstructbench_imgsz1024.onnx" | awk '{print $5}')
echo " ✅ DocLayout-YOLO ONNX: ${size}"
else
echo " ⚠️ DocLayout-YOLO ONNX: 不存在"
fi
echo "=============================================="
log_success "驗證完成"
}
# ==============================================================================
# 清理下載快取
# ==============================================================================
cleanup_cache() {
log_info "清理下載快取..."
rm -rf /tmp/hf_download_cache 2>/dev/null || true
rm -rf /root/.cache/huggingface 2>/dev/null || true
rm -rf /root/.cache/pip 2>/dev/null || true
rm -rf /root/.cache/uv 2>/dev/null || true
# 清理 Python cache
find /usr -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
log_success "快取清理完成"
}
# ==============================================================================
# 主程式
# ==============================================================================
main() {
log_info "=========================================="
log_info "開始下載模型 (${ARCH})"
log_info "=========================================="
create_directories
download_mineru_models
generate_mineru_config
setup_onnx_models
babeldoc_warmup
download_tiktoken
verify_downloads
cleanup_cache
log_success "=========================================="
log_success "所有模型下載完成!"
log_success "=========================================="
}
# 執行
main "$@"