| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- #!/usr/bin/env bash
- # ============================================================
- # build_binary.sh — 用 Nuitka 把整个 agentpaas 打成单个原生可执行文件
- # (standalone + onefile)。用户拿到后【无需安装 Python】,直接 ./agentpaas serve。
- #
- # 与 --build-secure(.so wheel)的区别:
- # --build-secure : 仍是 wheel,用户要有匹配的 Python;保护的是源码
- # --build-binary : 单文件二进制,内嵌 Python + 依赖 + 前端 + 内置包;保护源码 + 免装环境
- #
- # 产物:dist-binary/agentpaas(绑定「平台 + 架构」,如 macOS arm64)。
- #
- # 用法: scripts/build_binary.sh [PYTHON]
- # PYTHON 默认 python3,须为原生架构、3.10+、带头文件(macOS 推荐 Homebrew python3.11)。
- # 环境:
- # PROVIDERS 要打进二进制的 LLM SDK,空格分隔,默认 "anthropic openai"
- # (dashscope 走 openai 兼容/HTTP、ollama 走 HTTP,均无需 SDK)
- # ONEFILE 1=单文件(默认,体验最好但打包耗资源);0=standalone 目录
- # (产出一个文件夹,更省内存、更稳,适合 CI/资源受限机器)
- #
- # 前置: C 编译器(macOS: xcode-select --install)
- # ============================================================
- set -e
- SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
- PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
- cd "$PROJECT_DIR"
- RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
- info() { echo -e "${BLUE}[INFO]${NC} $1"; }
- ok() { echo -e "${GREEN}[OK]${NC} $1"; }
- warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
- fail() { echo -e "${RED}[FAIL]${NC} $1"; exit 1; }
- PYTHON="${1:-python3}"
- PROVIDERS="${PROVIDERS:-anthropic openai}"
- ONEFILE="${ONEFILE:-1}"
- command -v "$PYTHON" >/dev/null 2>&1 || fail "未找到 Python: $PYTHON"
- # ── 架构/版本/头文件检查(与 build_secure 一致)──────────────
- read -r PYARCH HAVE_H <<EOF
- $("$PYTHON" - <<'PY'
- import platform, sysconfig, os
- inc = sysconfig.get_path('include')
- print(f"{platform.machine()} {int(os.path.exists(os.path.join(inc,'Python.h')))}")
- PY
- )
- EOF
- HOSTARCH="$(uname -m)"
- [ "$PYARCH" = "$HOSTARCH" ] || fail "Python 架构($PYARCH) ≠ 主机($HOSTARCH)。请用原生 $HOSTARCH 的 Python(如 Homebrew python3.11),不要用 Rosetta 下的 x86_64 Python。"
- [ "$HAVE_H" = "1" ] || fail "缺 Python.h 头文件。请装带头文件的 Python(如 Homebrew)。"
- # ── 构建专用 venv:装 nuitka + 应用 + provider SDK ─────────
- BUILD_VENV="$PROJECT_DIR/.nuitka-venv"
- [ -x "$BUILD_VENV/bin/python" ] || "$PYTHON" -m venv "$BUILD_VENV"
- BPY="$BUILD_VENV/bin/python"
- info "准备构建环境(装 nuitka + 应用 + provider)..."
- "$BPY" -m pip install -q --upgrade pip nuitka >/dev/null
- # onefile 是 standalone:依赖必须真实安装进构建环境,Nuitka 才能跟进打包
- "$BPY" -m pip install -q ./lambdagent ./agentpaas $PROVIDERS
- # 仅对真正装上的 provider 生成 --include-module
- PROVIDER_FLAGS=()
- for p in $PROVIDERS; do
- if "$BPY" -c "import $p" >/dev/null 2>&1; then
- PROVIDER_FLAGS+=("--include-module=$p")
- ok "provider 已纳入: $p"
- else
- warn "provider 未装上,跳过: $p"
- fi
- done
- # ── Nuitka standalone + onefile ───────────────────────────
- OUT="$PROJECT_DIR/build_binary_out"
- rm -rf "$OUT"; mkdir -p "$OUT"
- ENTRY="agentpaas/src/agentpaas/__main__.py"
- MODE_FLAGS=(--standalone)
- [ "$ONEFILE" = "1" ] && MODE_FLAGS+=(--onefile)
- info "Nuitka 打包二进制(mode: $([ "$ONEFILE" = 1 ] && echo onefile单文件 || echo standalone目录),首次可能 10+ 分钟)..."
- "$BPY" -m nuitka \
- "${MODE_FLAGS[@]}" \
- --output-filename=agentpaas \
- --output-dir="$OUT" \
- --include-package=uvicorn \
- --include-module=uvloop --include-module=httptools \
- --include-module=websockets --include-module=h11 \
- --include-package=agentpaas --include-package=lambdagent \
- "${PROVIDER_FLAGS[@]}" \
- --include-data-dir=webui-dist=webui-dist \
- --include-data-dir=agentexample/agentpacks=agentexample/agentpacks \
- --include-data-dir=agentexample/skills=agentexample/skills \
- --python-flag=no_docstrings \
- --assume-yes-for-downloads \
- "$ENTRY"
- # 平台标签 + 可执行后缀(Windows 走 git-bash,uname 形如 MINGW64_NT)
- case "$(uname -s)" in
- MINGW*|MSYS*|CYGWIN*) OSLABEL=windows; EXE=".exe" ;;
- Darwin) OSLABEL=darwin; EXE="" ;;
- *) OSLABEL=$(uname -s | tr 'A-Z' 'a-z'); EXE="" ;;
- esac
- PLATSUFFIX="$OSLABEL-$HOSTARCH"
- mkdir -p dist-binary
- if [ "$ONEFILE" = "1" ]; then
- BIN="$OUT/agentpaas$EXE"
- [ -x "$BIN" ] || fail "未生成二进制"
- cp "$BIN" "dist-binary/agentpaas-$PLATSUFFIX$EXE"
- cp "$BIN" "dist-binary/agentpaas$EXE"
- echo ""
- ok "单文件二进制已生成 → dist-binary/agentpaas$EXE ($(du -h "$BIN" | cut -f1))"
- echo " 用户用法(无需 Python): ./agentpaas$EXE serve --port 8000"
- else
- # standalone:产出一个目录,整目录即发行件
- DISTDIR="$OUT/__main__.dist"
- [ -x "$DISTDIR/agentpaas$EXE" ] || fail "未生成 standalone 目录二进制"
- rm -rf "dist-binary/agentpaas-$PLATSUFFIX.app"
- cp -R "$DISTDIR" "dist-binary/agentpaas-$PLATSUFFIX.app"
- echo ""
- ok "standalone 目录已生成 → dist-binary/agentpaas-$PLATSUFFIX.app/ ($(du -sh "$DISTDIR" | cut -f1))"
- echo " 用户用法(无需 Python): ./agentpaas-$PLATSUFFIX.app/agentpaas$EXE serve --port 8000"
- fi
- echo ""
- warn "提醒: 二进制绑定 $PLATSUFFIX —— 换平台/架构需在对应机器重新构建。"
- warn " 未打包: chromadb 向量库、PDF 外部工具(pandoc/xelatex)——用到这些功能仍需系统侧依赖。"
|