From fc7e1e0bc17d9aef2c206ee5c783791c1dda48b0 Mon Sep 17 00:00:00 2001 From: jim800121chen Date: Thu, 16 Jul 2026 14:21:08 +0800 Subject: [PATCH] =?UTF-8?q?fix(local-agent):=20=E4=BF=AE=20vendor=20wheels?= =?UTF-8?q?=20=E5=A4=9A=E7=89=88=E6=9C=AC=E7=B4=AF=E7=A9=8D=E5=B0=8E?= =?UTF-8?q?=E8=87=B4=20SDK=20=E6=B2=92=E8=A3=9D=E9=80=B2=20bundled=20runti?= =?UTF-8?q?me?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根因:Makefile vendor-wheels 用 pip download --dest 只加不清空,跨多次 vendor-sync 累積出同套件多版本(certifi 三版/numpy 二版等共 17 wheel)→ agent bootstrap 的 ensureBundledPython 把目錄所有 .whl 當獨立參數丟 pip install → 版本衝突 ResolutionImpossible → pip 失敗 → venv 建了但空 → 無 Kneron PLUS SDK → bridge scan 走 pyusb fallback 回假序號 0x00000000 → 裝置「尚未回報序號」+ 載入模型 device not connected。 修法(3 個 vendor-wheels target): - 開頭 rm -f vendor/wheels//*.whl 清空,確保每次 sync 單版本 - 結尾 dedup 驗證(sort|uniq -d 偵測多版本 → exit 1 fail-fast) - linux 補「無專屬 KneronPLUS wheel 時退回複製 macos py3-none-any」 - .gitignore 補 local-agent/vendor|payload|dist 對稱規則(保留 ffmpeg binary + .gitkeep 例外),防 vendor-sync 後 git add -A 撈進 build 產物 決定性 evidence(agent 實際 bundled runtime):清空 runtime → 起剛 build 的 .app → agent 自己 bootstrap 裝 9 個乾淨單版 wheel(修前 17 含重複必失敗) → venv 有 kp → GET /api/devices 回真序號 0xB906162C(非 0x00000000)。 Reviewer 通過(0C/0M/4Mi/3Sug)。win/linux 同邏輯已補、未在對應 runner 驗證(已知限制)。follow-up:ensureBundledPython 列舉 wheel 脆弱設計建議 改 find-links 自解析(backend 另案)。 Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitignore | 11 ++++++++++ local-agent/Makefile | 50 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index d7e9e6f..7b7c33c 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,17 @@ local-tool/payload/ !local-tool/vendor/ffmpeg/macos/ !local-tool/vendor/ffmpeg/macos/** +# local-agent 對稱規則(build 產物 wheel/python/ffmpeg 不進 git,避免 vendor-sync +# 後 git add -A 撈進大量 build 產物;同 local-tool 保留 macOS LGPL ffmpeg binary 例外) +local-agent/vendor/** +local-agent/dist/ +local-agent/payload/** +!local-agent/payload/.gitkeep +!local-agent/vendor/.gitkeep +!local-agent/vendor/ffmpeg/ +!local-agent/vendor/ffmpeg/macos/ +!local-agent/vendor/ffmpeg/macos/** + # Go server 的 embed 與 build 產物 local-tool/server/web/out/ local-tool/server/visiona-local-server diff --git a/local-agent/Makefile b/local-agent/Makefile index 11eb131..9f08fca 100644 --- a/local-agent/Makefile +++ b/local-agent/Makefile @@ -91,6 +91,14 @@ vendor-python: ## 下載 python-build-standalone tarball → vendor/python/darwi vendor-wheels: ## 同步 wheels → vendor/wheels/darwin/(內部 wheel 從 visiona-agent/wheels 複製,公開相依用 pip download) @mkdir -p vendor/wheels/darwin + @# ⚠️ 先清空舊 wheel:pip download --dest 不會覆蓋/清理舊版本,跨多次 vendor-sync + @# 會累積同一套件的多個版本(如 certifi 2026.2.25 + 2026.4.22 + 2026.6.17)。 + @# 下游 ensureBundledPython 會列舉目錄下「所有」.whl 丟給 pip install,多版本 + @# 並存時 pip resolver 直接 ResolutionImpossible → 整批安裝失敗 → runtime venv + @# 沒有 kp/numpy/cv2 → Kneron scan fallback 回假序號 0x00000000。 + @# 每次都清空重建,確保每個套件恰好單一版本(與 POC 乾淨 vendor 目錄一致)。 + @echo "==> 清空舊 wheels(避免多版本累積導致 pip ResolutionImpossible)..." + @rm -f vendor/wheels/darwin/*.whl @echo "==> 同步內部 wheels(KneronPLUS 等)..." @if [ -d visiona-agent/wheels/macos ]; then \ cp visiona-agent/wheels/macos/*.whl vendor/wheels/darwin/ 2>/dev/null || true; \ @@ -105,7 +113,14 @@ vendor-wheels: ## 同步 wheels → vendor/wheels/darwin/(內部 wheel 從 vis --implementation cp \ --dest vendor/wheels/darwin \ numpy opencv-python-headless pyusb requests || echo "WARN: pip download 部分失敗(詳見上方訊息)" - @echo "==> wheels 總覽:" + @echo "==> 驗證每個套件恰好單一版本(多版本會讓 runtime pip install 整批失敗)..." + @dup=$$(ls -1 vendor/wheels/darwin/*.whl 2>/dev/null | sed -E 's|.*/||; s/-[0-9].*//' | sort | uniq -d); \ + if [ -n "$$dup" ]; then \ + echo "❌ 偵測到多版本 wheel(清空邏輯失效):$$dup"; \ + ls -1 vendor/wheels/darwin/*.whl | sed 's|.*/| |'; \ + exit 1; \ + fi + @echo "==> wheels 總覽(每套件單版):" @ls -1 vendor/wheels/darwin/*.whl 2>/dev/null | wc -l | xargs -I{} echo " 共 {} 個 wheel" @du -sh vendor/wheels/darwin @@ -288,6 +303,10 @@ vendor-python-windows: ## 下載 python-build-standalone Windows x86_64 → vend vendor-wheels-windows: ## 同步 Windows wheels → vendor/wheels/windows/ @mkdir -p vendor/wheels/windows + @# ⚠️ 見 vendor-wheels(macOS)的說明:先清空避免多版本累積,否則 runtime + @# ensureBundledPython 的 pip install 會 ResolutionImpossible → venv 缺 kp。 + @echo "==> 清空舊 wheels (Windows,避免多版本累積)..." + @rm -f vendor/wheels/windows/*.whl @echo "==> 同步內部 wheels (Windows, KneronPLUS 等)..." @if [ -d visiona-agent/wheels/windows ]; then \ cp visiona-agent/wheels/windows/*.whl vendor/wheels/windows/ 2>/dev/null || true; \ @@ -312,7 +331,14 @@ vendor-wheels-windows: ## 同步 Windows wheels → vendor/wheels/windows/ --dest vendor/wheels/windows \ numpy opencv-python-headless pyusb requests || echo "WARN: pip download 部分失敗(詳見上方訊息)"; \ fi - @echo "==> Windows wheels 總覽:" + @echo "==> 驗證每個套件恰好單一版本(多版本會讓 runtime pip install 整批失敗)..." + @dup=$$(ls -1 vendor/wheels/windows/*.whl 2>/dev/null | sed -E 's|.*/||; s/-[0-9].*//' | sort | uniq -d); \ + if [ -n "$$dup" ]; then \ + echo "❌ 偵測到多版本 wheel(清空邏輯失效):$$dup"; \ + ls -1 vendor/wheels/windows/*.whl | sed 's|.*/| |'; \ + exit 1; \ + fi + @echo "==> Windows wheels 總覽(每套件單版):" @ls -1 vendor/wheels/windows/*.whl 2>/dev/null | wc -l | xargs -I{} echo " 共 {} 個 wheel" @du -sh vendor/wheels/windows 2>/dev/null || true @@ -398,10 +424,21 @@ vendor-python-linux: ## 下載 python-build-standalone Linux x86_64 → vendor/p vendor-wheels-linux: ## 同步 Linux wheels → vendor/wheels/linux/ @mkdir -p vendor/wheels/linux + @# ⚠️ 見 vendor-wheels(macOS)的說明:先清空避免多版本累積,否則 runtime + @# ensureBundledPython 的 pip install 會 ResolutionImpossible → venv 缺 kp。 + @echo "==> 清空舊 wheels (Linux,避免多版本累積)..." + @rm -f vendor/wheels/linux/*.whl @echo "==> 同步內部 wheels (Linux, KneronPLUS 等)..." @if [ -d visiona-agent/wheels/linux ]; then \ cp visiona-agent/wheels/linux/*.whl vendor/wheels/linux/ 2>/dev/null || true; \ fi + @# KneronPLUS-*.whl 是 py3-none-any(平台無關)。若上一步沒從 Linux 專屬目錄 + @# 複製到 KneronPLUS wheel,退回複製 macOS 目錄下的,確保 Linux payload 也含 kp。 + @if ! ls vendor/wheels/linux/KneronPLUS-*.whl >/dev/null 2>&1; then \ + cp visiona-agent/wheels/macos/KneronPLUS-*.whl vendor/wheels/linux/ 2>/dev/null && \ + echo "==> (Linux 無專屬 KneronPLUS wheel,改用 macos 目錄的 py3-none-any wheel)" || \ + echo "WARN: 找不到任何 KneronPLUS wheel,Linux payload 將缺 kp"; \ + fi @echo "==> 從 PyPI 下載公開相依 wheels (cp312, manylinux2014_x86_64)..." @pip3 download \ --only-binary=:all: \ @@ -410,7 +447,14 @@ vendor-wheels-linux: ## 同步 Linux wheels → vendor/wheels/linux/ --implementation cp \ --dest vendor/wheels/linux \ numpy opencv-python-headless pyusb requests || echo "WARN: pip download 部分失敗(詳見上方訊息)" - @echo "==> Linux wheels 總覽:" + @echo "==> 驗證每個套件恰好單一版本(多版本會讓 runtime pip install 整批失敗)..." + @dup=$$(ls -1 vendor/wheels/linux/*.whl 2>/dev/null | sed -E 's|.*/||; s/-[0-9].*//' | sort | uniq -d); \ + if [ -n "$$dup" ]; then \ + echo "❌ 偵測到多版本 wheel(清空邏輯失效):$$dup"; \ + ls -1 vendor/wheels/linux/*.whl | sed 's|.*/| |'; \ + exit 1; \ + fi + @echo "==> Linux wheels 總覽(每套件單版):" @ls -1 vendor/wheels/linux/*.whl 2>/dev/null | wc -l | xargs -I{} echo " 共 {} 個 wheel" @du -sh vendor/wheels/linux 2>/dev/null || true