# Python bootstrap shared by the Windows launcher and its isolated checks. # Save as UTF-8 with BOM so Windows PowerShell 5.1 reads Chinese messages correctly. function Get-BootstrapText { param([string]$English, [string]$Chinese) if ($env:ARTEMIS_LAUNCH_LANG -eq 'zh-CN') { return $Chinese } return $English } function Initialize-ArtemisPython { param([Parameter(Mandatory=$true)][string]$ProjectRoot) $ProjectRoot = (Resolve-Path -LiteralPath $ProjectRoot -ErrorAction Stop).Path if (-not (Test-Path -LiteralPath (Join-Path $ProjectRoot 'pyproject.toml'))) { throw (Get-BootstrapText 'pyproject.toml is missing. Extract the complete source archive first.' '缺少 pyproject.toml;请重新解压完整源码包。') } if (-not (Test-Path -LiteralPath (Join-Path $ProjectRoot 'uv.lock'))) { throw (Get-BootstrapText 'uv.lock is missing. Extract the complete source archive first.' '缺少 uv.lock;请重新解压完整源码包。') } if (-not (Test-CommandExists 'uv')) { if ($ArtemisDownloads -and $ArtemisDownloads.python_index) { Write-Host (Get-BootstrapText ' [INSTALL] Installing uv from the selected Python mirror...' ' [安装] 正在从选定的 Python 镜像安装 uv...') -ForegroundColor Cyan Install-ArtemisUvFromIndex -IndexUrl $ArtemisDownloads.python_index } else { Write-Host (Get-BootstrapText ' [INSTALL] uv was not found. Installing from the configured installer...' ' [安装] 未找到 uv,正在下载安装...') -ForegroundColor Cyan $installer = Join-Path ([IO.Path]::GetTempPath()) ('artemis-uv-' + [guid]::NewGuid().ToString('N') + '.ps1') try { $installerUrl = if ($ArtemisDownloads) { $ArtemisDownloads.uv_installer_url } else { 'https://astral.sh/uv/install.ps1' } if (-not (Invoke-DownloadFile -Uri $installerUrl -OutFile $installer)) { throw (Get-BootstrapText 'Could not download uv. Check your network/proxy and retry.' 'uv 下载失败;请检查网络、代理或镜像后重试。') } & powershell.exe -NoProfile -ExecutionPolicy Bypass -File $installer | Out-Host if ($LASTEXITCODE -ne 0) { throw (Get-BootstrapText "uv installation failed (exit $LASTEXITCODE)." "uv 安装失败(退出码 $LASTEXITCODE)。") } } finally { Remove-Item -LiteralPath $installer -Force -ErrorAction SilentlyContinue } } Update-EnvironmentPath } else { Write-Host (Get-BootstrapText ' [SKIP] uv is already installed.' ' [复用] uv 已安装,跳过下载。') -ForegroundColor Green } if (-not (Test-CommandExists 'uv')) { throw (Get-BootstrapText 'uv is still unavailable. Install it from https://astral.sh/uv/ and retry.' '仍无法运行 uv;请检查安装并重试。') } & uv --version | Out-Host if ($LASTEXITCODE -ne 0) { throw (Get-BootstrapText 'The installed uv could not run. Repair its installation and retry.' '已安装的 uv 无法运行;请修复安装后重试。') } $venvDir = Join-Path $ProjectRoot '.venv' $pythonExe = Join-Path $venvDir 'Scripts\python.exe' $previousEnvironment = $env:UV_PROJECT_ENVIRONMENT $env:UV_PROJECT_ENVIRONMENT = $venvDir Push-Location -LiteralPath $ProjectRoot try { if (-not (Test-Path -LiteralPath $venvDir)) { Write-Host (Get-BootstrapText ' [INSTALL] Creating .venv with Python 3.12 (downloaded automatically if needed)...' ' [安装] 正在创建 Python 3.12 虚拟环境 .venv;缺少 Python 时自动下载...') -ForegroundColor Cyan & uv venv --python 3.12 $venvDir | Out-Host if ($LASTEXITCODE -ne 0) { throw (Get-BootstrapText 'Could not create .venv. Check the download errors above and retry.' '.venv 创建失败;请查看上方下载错误并重试。') } } else { Write-Host (Get-BootstrapText ' [SKIP] Reusing the existing project .venv.' ' [复用] 已有项目 .venv,跳过创建。') -ForegroundColor Green } if (-not (Test-Path -LiteralPath $pythonExe)) { throw (Get-BootstrapText 'The existing .venv is incomplete. Rename it as a backup, then rerun start.bat.' '已有 .venv 不完整。先重命名备份,再运行 start-cn.bat。') } & $pythonExe -c 'import sys; sys.exit(0 if sys.version_info >= (3, 12) and sys.prefix != sys.base_prefix else 1)' | Out-Host if ($LASTEXITCODE -ne 0) { throw (Get-BootstrapText 'The existing .venv is unusable or older than Python 3.12. Rename it as a backup, then rerun start.bat.' '已有 .venv 无法使用或 Python 低于 3.12。先重命名备份,再运行 start-cn.bat。') } # A cheap check skips installation when the lock and installed packages agree. # --inexact preserves any optional development tools already installed. $lockOption = if ($ArtemisDownloads -and $ArtemisDownloads.python_index) { '--frozen' } else { '--locked' } & uv sync --check $lockOption --no-dev --inexact --quiet 2>$null | Out-Host if ($LASTEXITCODE -eq 0) { Write-Host (Get-BootstrapText ' [SKIP] Python dependencies are already ready.' ' [复用] Python 依赖已就绪。') -ForegroundColor Green } else { Write-Host (Get-BootstrapText ' [INSTALL] Synchronizing dependencies from uv.lock...' ' [安装] 正在按 uv.lock 同步 Python 依赖...') -ForegroundColor Cyan if ($ArtemisDownloads -and $ArtemisDownloads.python_index) { Install-ArtemisLockedDependencies -PythonExe $pythonExe -IndexUrl $ArtemisDownloads.python_index } else { & uv sync --locked --no-dev --inexact | Out-Host if ($LASTEXITCODE -ne 0) { throw (Get-BootstrapText 'Dependency installation failed. Fix the error above and rerun start.bat.' '依赖安装失败;请处理上方错误,再运行 start-cn.bat。') } } } & $pythonExe -c 'import artemis, uvicorn, fastapi, langchain_openai' | Out-Host if ($LASTEXITCODE -ne 0) { throw (Get-BootstrapText 'Python dependency verification failed. The UI will not be started.' 'Python 依赖验证失败,网页不会启动。') } Write-Host (Get-BootstrapText ' [OK] Python environment is ready.' ' [完成] Python 环境已就绪。') -ForegroundColor Green return $pythonExe } finally { Pop-Location $env:UV_PROJECT_ENVIRONMENT = $previousEnvironment } }