From bfda782c8627901f4b056722dedbfa496af62e91 Mon Sep 17 00:00:00 2001 From: 4pmtong Date: Sun, 17 May 2026 12:45:37 +0800 Subject: [PATCH] feat: defer optional Python deps from app bundle --- electron-builder.json | 10 +++++ electron/main/utils/process.ts | 75 +++++++++++++++++++++++++++------- 2 files changed, 71 insertions(+), 14 deletions(-) diff --git a/electron-builder.json b/electron-builder.json index d13b62ff..7be0bc20 100644 --- a/electron-builder.json +++ b/electron-builder.json @@ -39,8 +39,18 @@ "!venv/**/__pycache__/**", "!venv/**/*.pyc", + "!venv/lib/python*/site-packages/datasets/**", + "!venv/lib/python*/site-packages/datasets-*.dist-info/**", + "!venv/lib/python*/site-packages/pyarrow/**", + "!venv/lib/python*/site-packages/pyarrow-*.dist-info/**", "!venv/lib/python*/site-packages/yt_dlp/**", "!venv/lib/python*/site-packages/yt_dlp-*.dist-info/**", + "!venv/Lib/site-packages/datasets/**", + "!venv/Lib/site-packages/datasets-*.dist-info/**", + "!venv/Lib/site-packages/pyarrow/**", + "!venv/Lib/site-packages/pyarrow-*.dist-info/**", + "!venv/Lib/site-packages/yt_dlp/**", + "!venv/Lib/site-packages/yt_dlp-*.dist-info/**", "!uv_python/**/site-packages/setuptools/**", "!uv_python/**/site-packages/wheel/**" ] diff --git a/electron/main/utils/process.ts b/electron/main/utils/process.ts index b5e2489a..b7c9f80c 100644 --- a/electron/main/utils/process.ts +++ b/electron/main/utils/process.ts @@ -523,12 +523,65 @@ function findPythonForTerminalVenv(): string | null { const TERMINAL_VENV_VERSION_FILE = '.terminal_venv_version'; const BACKEND_VENV_VERSION_FILE = '.backend_venv_version'; +const OPTIONAL_PREBUILT_DEPS_MARKER = '.optional_prebuilt_deps_synced'; +// Marker-version component only; uv.lock/pyproject.toml still define what uv sync installs. +const OPTIONAL_PREBUILT_PYTHON_PACKAGES = ['datasets', 'pyarrow', 'yt-dlp']; let _optionalDepsSyncStarted = false; -/** Background uv sync to install deps excluded from bundle (yt_dlp, etc). Does not block startup. */ +function getOptionalDepsSyncVersion(): string { + return `${app.getVersion()}:${OPTIONAL_PREBUILT_PYTHON_PACKAGES.join(',')}`; +} + +function getOptionalDepsIndexArgs(): string[] { + const configuredIndex = process.env.EIGENT_OPTIONAL_DEPS_INDEX_URL?.trim(); + const configuredExtraIndex = + process.env.EIGENT_OPTIONAL_DEPS_EXTRA_INDEX_URL?.trim(); + if (configuredIndex) { + return [ + '--default-index', + configuredIndex, + ...(configuredExtraIndex ? ['--index', configuredExtraIndex] : []), + ]; + } + + const configuredUseChinaMirror = + process.env.EIGENT_OPTIONAL_DEPS_USE_CHINA_MIRROR?.trim().toLowerCase(); + let useChinaMirror: boolean; + if (configuredUseChinaMirror === 'true') { + useChinaMirror = true; + } else if (configuredUseChinaMirror === 'false') { + useChinaMirror = false; + } else { + useChinaMirror = + Intl.DateTimeFormat().resolvedOptions().timeZone === 'Asia/Shanghai'; + } + if (!useChinaMirror) { + return []; + } + + return [ + '--default-index', + 'https://mirrors.aliyun.com/pypi/simple/', + '--index', + 'https://pypi.org/simple/', + ]; +} + +/** Background uv sync to install deps excluded from bundle. Does not block startup. */ function runBackgroundUvSyncForOptionalDeps(userBackendVenv: string): void { if (!app.isPackaged) return; + + const markerPath = path.join(userBackendVenv, OPTIONAL_PREBUILT_DEPS_MARKER); + const markerVersion = getOptionalDepsSyncVersion(); + if ( + fs.existsSync(markerPath) && + fs.readFileSync(markerPath, 'utf-8').trim() === markerVersion + ) { + log.info('[VENV] Optional prebuilt deps already synced'); + return; + } + if (_optionalDepsSyncStarted) return; _optionalDepsSyncStarted = true; @@ -551,20 +604,9 @@ function runBackgroundUvSyncForOptionalDeps(userBackendVenv: string): void { UV_TOOL_DIR: getCachePath('uv_tool'), UV_HTTP_TIMEOUT: '300', } as NodeJS.ProcessEnv; - const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; - const syncArgs = - timezone === 'Asia/Shanghai' - ? [ - 'sync', - '--no-dev', - '--default-index', - 'https://mirrors.aliyun.com/pypi/simple/', - '--index', - 'https://pypi.org/simple/', - ] - : ['sync', '--no-dev']; + const syncArgs = ['sync', '--no-dev', ...getOptionalDepsIndexArgs()]; log.info( - '[VENV] Starting background uv sync to install optional deps (e.g. yt_dlp); app will not wait.' + `[VENV] Starting background uv sync for optional prebuilt deps (${OPTIONAL_PREBUILT_PYTHON_PACKAGES.join(', ')}); app will not wait.` ); const child = spawn(uvPath, syncArgs, { cwd: backendPath, @@ -579,6 +621,11 @@ function runBackgroundUvSyncForOptionalDeps(userBackendVenv: string): void { child.on('exit', (code) => { if (code === 0) { log.info('[VENV] Background uv sync completed'); + try { + fs.writeFileSync(markerPath, markerVersion, 'utf-8'); + } catch (error) { + log.warn(`[VENV] Failed to write optional deps marker: ${error}`); + } } else { log.warn( `[VENV] Background uv sync exited with code ${code} (optional deps may be missing)`