fix(local-agent): 修 vendor wheels 多版本累積導致 SDK 沒裝進 bundled runtime

根因: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/<plat>/*.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) <noreply@anthropic.com>
This commit is contained in:
jim800121chen 2026-07-16 14:21:08 +08:00
parent 51dfd0f2d8
commit fc7e1e0bc1
2 changed files with 58 additions and 3 deletions

11
.gitignore vendored
View File

@ -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

View File

@ -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
@# ⚠️ 先清空舊 wheelpip 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 "==> 同步內部 wheelsKneronPLUS 等)..."
@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-wheelsmacOS的說明先清空避免多版本累積否則 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-wheelsmacOS的說明先清空避免多版本累積否則 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 wheelLinux 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