implement modular frontend components, backend services, and multi-platform configuration scripts for the local AI image generator
This commit is contained in:
1 parent
c280683a9d
commit
4a840679d6
14 files changed
+1114
-2089
No files matched your search
@@ -11,6 +11,37 @@ ROOT_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")"
|
||||
VENV_DIR="$ROOT_DIR/app/backend/mac/coreml_venv"
|
||||
PYTHON_BIN="$VENV_DIR/bin/python"
|
||||
|
||||
is_coreml_python_supported() {
|
||||
"$1" - <<'PY' >/dev/null 2>&1
|
||||
import sys
|
||||
raise SystemExit(0 if (3, 9) <= sys.version_info[:2] < (3, 12) else 1)
|
||||
PY
|
||||
}
|
||||
|
||||
find_coreml_python() {
|
||||
local candidate
|
||||
if [[ -n "${COREML_SETUP_PYTHON:-}" ]]; then
|
||||
if is_coreml_python_supported "$COREML_SETUP_PYTHON"; then
|
||||
echo "$COREML_SETUP_PYTHON"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
fi
|
||||
|
||||
for candidate in python3.11 python3.10 python3.9 python3; do
|
||||
if command -v "$candidate" >/dev/null 2>&1 && is_coreml_python_supported "$candidate"; then
|
||||
command -v "$candidate"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
coreml_venv_ready() {
|
||||
[[ -x "$PYTHON_BIN" ]] && "$PYTHON_BIN" -c "import sys; assert (3, 9) <= sys.version_info[:2] < (3, 12); import python_coreml_stable_diffusion, diffusers, transformers" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
echo ""
|
||||
echo " ============================================================"
|
||||
echo " Local AI Image Generator - Apple Silicon CoreML NPU Setup"
|
||||
@@ -27,14 +58,22 @@ if [[ "$(uname -m)" != "arm64" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v python3 >/dev/null 2>&1; then
|
||||
echo " [ERROR] python3 is required. Install Python 3 first (via Homebrew or Xcode)." >&2
|
||||
SETUP_PYTHON="$(find_coreml_python || true)"
|
||||
if [[ -z "$SETUP_PYTHON" ]]; then
|
||||
echo " [ERROR] CoreML setup requires Python 3.9, 3.10, or 3.11." >&2
|
||||
echo " Apple's ml-stable-diffusion dependency stack is not compatible with Python 3.12+." >&2
|
||||
echo " Install python@3.11 with Homebrew, or set COREML_SETUP_PYTHON to a supported Python." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -x "$PYTHON_BIN" ]] && ! coreml_venv_ready; then
|
||||
echo " Existing CoreML environment is incomplete or uses Python 3.12+. Recreating it..."
|
||||
rm -rf "$VENV_DIR"
|
||||
fi
|
||||
|
||||
if [[ ! -x "$PYTHON_BIN" ]]; then
|
||||
echo " Creating Python environment: $VENV_DIR"
|
||||
if ! python3 -m venv "$VENV_DIR"; then
|
||||
if ! "$SETUP_PYTHON" -m venv "$VENV_DIR"; then
|
||||
echo " [ERROR] Could not create the virtual environment."
|
||||
exit 1
|
||||
fi
|
||||
@@ -42,7 +81,7 @@ fi
|
||||
|
||||
echo " Installing CoreML Stable Diffusion dependencies (numpy, coremltools, diffusers)..."
|
||||
"$PYTHON_BIN" -m pip install --upgrade pip
|
||||
"$PYTHON_BIN" -m pip install numpy coremltools diffusers transformers huggingface-hub pillow
|
||||
"$PYTHON_BIN" -m pip install "numpy<1.24" coremltools diffusers transformers huggingface-hub pillow
|
||||
|
||||
echo " Installing Apple's python-coreml-stable-diffusion package..."
|
||||
if ! "$PYTHON_BIN" -m pip install "git+https://github.com/apple/ml-stable-diffusion.git"; then
|
||||
|
||||
@@ -1,16 +1,105 @@
|
||||
# Optional OpenVINO NPU setup for Intel AI Boost systems.
|
||||
# This does not modify the existing stable-diffusion.cpp backends.
|
||||
# Installs a portable Python runtime under app/tools/python-win.
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$rootDir = Split-Path -Parent (Split-Path -Parent $scriptDir)
|
||||
$venvDir = Join-Path $rootDir "app\tools\openvino-venv-win"
|
||||
$pythonExe = Join-Path $venvDir "Scripts\python.exe"
|
||||
$appDir = Join-Path $rootDir "app"
|
||||
$toolsDir = Join-Path $appDir "tools"
|
||||
$pythonDir = Join-Path $toolsDir "python-win"
|
||||
$pythonExe = Join-Path $pythonDir "python.exe"
|
||||
$pythonZip = Join-Path $toolsDir "python-win.zip"
|
||||
$getPip = Join-Path $toolsDir "get-pip.py"
|
||||
|
||||
$pythonVersion = "3.11.9"
|
||||
$pythonZipUrl = "https://www.python.org/ftp/python/$pythonVersion/python-$pythonVersion-embed-amd64.zip"
|
||||
$getPipUrl = "https://bootstrap.pypa.io/get-pip.py"
|
||||
|
||||
function Enable-Tls12 {
|
||||
try {
|
||||
$tls12 = [Enum]::ToObject([Net.SecurityProtocolType], 3072)
|
||||
[Net.ServicePointManager]::SecurityProtocol =
|
||||
[Net.ServicePointManager]::SecurityProtocol -bor $tls12
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function Download-File {
|
||||
param([string]$Url, [string]$Dest, [string]$Label)
|
||||
Write-Host " Downloading $Label..."
|
||||
Enable-Tls12
|
||||
New-Item -ItemType Directory -Force -Path (Split-Path -Parent $Dest) | Out-Null
|
||||
$client = New-Object System.Net.WebClient
|
||||
$client.Headers.Add("User-Agent", "Local-AI-Image-Generator-Setup")
|
||||
$client.DownloadFile($Url, $Dest)
|
||||
}
|
||||
|
||||
function Expand-Zip {
|
||||
param([string]$ZipPath, [string]$Destination)
|
||||
if (Test-Path $Destination) {
|
||||
Remove-Item $Destination -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
New-Item -ItemType Directory -Force -Path $Destination | Out-Null
|
||||
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||
[System.IO.Compression.ZipFile]::ExtractToDirectory($ZipPath, $Destination)
|
||||
}
|
||||
|
||||
function Enable-EmbeddedPythonSitePackages {
|
||||
$pth = Get-ChildItem $pythonDir -Filter "python*._pth" | Select-Object -First 1
|
||||
if (-not $pth) { throw "Embedded Python ._pth file not found in $pythonDir" }
|
||||
|
||||
$content = Get-Content $pth.FullName
|
||||
$hasSitePackages = $content | Where-Object { $_.Trim() -eq "Lib\site-packages" } | Select-Object -First 1
|
||||
$addedSitePackages = $false
|
||||
$next = foreach ($line in $content) {
|
||||
if (-not $hasSitePackages -and -not $addedSitePackages -and $line.Trim() -eq "import site") {
|
||||
"Lib\site-packages"
|
||||
$addedSitePackages = $true
|
||||
}
|
||||
if ($line.Trim() -eq "#import site") {
|
||||
if (-not $hasSitePackages -and -not $addedSitePackages) {
|
||||
"Lib\site-packages"
|
||||
$addedSitePackages = $true
|
||||
}
|
||||
"import site"
|
||||
} else {
|
||||
$line
|
||||
}
|
||||
}
|
||||
Set-Content -Path $pth.FullName -Value $next -Encoding ASCII
|
||||
}
|
||||
|
||||
function Ensure-PortablePython {
|
||||
if (Test-Path $pythonExe) {
|
||||
try {
|
||||
& $pythonExe -c "import sys; raise SystemExit(0 if sys.version_info[:2] == (3, 11) else 1)"
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Host " Portable Python already ready: $pythonExe"
|
||||
return
|
||||
}
|
||||
} catch {}
|
||||
|
||||
Write-Host " Existing portable Python is invalid. Reinstalling..."
|
||||
Remove-Item $pythonDir -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
Download-File -Url $pythonZipUrl -Dest $pythonZip -Label "Portable Python $pythonVersion"
|
||||
Expand-Zip -ZipPath $pythonZip -Destination $pythonDir
|
||||
Remove-Item $pythonZip -Force -ErrorAction SilentlyContinue
|
||||
Enable-EmbeddedPythonSitePackages
|
||||
|
||||
Download-File -Url $getPipUrl -Dest $getPip -Label "pip bootstrap"
|
||||
& $pythonExe $getPip --no-warn-script-location
|
||||
if ($LASTEXITCODE -ne 0) { throw "pip bootstrap failed for portable Python." }
|
||||
Remove-Item $getPip -Force -ErrorAction SilentlyContinue
|
||||
|
||||
& $pythonExe -m pip --version | Out-Host
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Write-Host " ============================================================"
|
||||
Write-Host " Local AI Image Generator - OpenVINO NPU Setup"
|
||||
Write-Host " Portable Python runtime: app/tools/python-win"
|
||||
Write-Host " ============================================================"
|
||||
Write-Host ""
|
||||
|
||||
@@ -25,22 +114,23 @@ if (-not $npu) {
|
||||
|
||||
Write-Host " Detected: $($npu.Name)"
|
||||
|
||||
if (-not (Test-Path $pythonExe)) {
|
||||
Write-Host " Creating Python environment: $venvDir"
|
||||
python -m venv $venvDir
|
||||
}
|
||||
Ensure-PortablePython
|
||||
|
||||
Write-Host " Installing OpenVINO GenAI runtime..."
|
||||
& $pythonExe -m pip install --upgrade pip
|
||||
& $pythonExe -m pip install openvino openvino-genai pillow huggingface-hub
|
||||
Write-Host " Installing OpenVINO GenAI runtime into portable Python..."
|
||||
& $pythonExe -m pip install --upgrade pip --no-warn-script-location --progress-bar off
|
||||
if ($LASTEXITCODE -ne 0) { throw "pip upgrade failed." }
|
||||
& $pythonExe -m pip install openvino openvino-genai pillow huggingface-hub --no-warn-script-location --progress-bar off
|
||||
if ($LASTEXITCODE -ne 0) { throw "OpenVINO package install failed." }
|
||||
|
||||
Write-Host ""
|
||||
Write-Host " Verifying OpenVINO NPU device..."
|
||||
& $pythonExe -c "import openvino as ov; core=ov.Core(); print('Available devices:', core.available_devices); raise SystemExit(0 if 'NPU' in core.available_devices else 2)"
|
||||
if ($LASTEXITCODE -ne 0) { throw "OpenVINO installed, but NPU was not available to the portable runtime." }
|
||||
|
||||
Write-Host ""
|
||||
Write-Host " ============================================================"
|
||||
Write-Host " OpenVINO NPU setup complete."
|
||||
Write-Host " Portable Python: $pythonExe"
|
||||
Write-Host " Restart windows.bat, then download an OpenVINO NPU model."
|
||||
Write-Host " ============================================================"
|
||||
Write-Host ""
|
||||
@@ -39,7 +39,7 @@ $oldPath = $env:PATH
|
||||
try {
|
||||
$env:PATH = "$nodeDir;$env:PATH"
|
||||
Print-Info "Installing kokoro-js into app/tts-runtime..."
|
||||
& $npmCmd install --prefer-offline 2>&1
|
||||
& $npmCmd install --prefer-offline --loglevel=error
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Print-Fail "kokoro-js install failed."
|
||||
exit 1
|
||||
|
||||
+70
-5
@@ -158,7 +158,7 @@ function Expand-WithProgress {
|
||||
# ══════════════════════════════════════════════════════════════════════════════
|
||||
Print-Header
|
||||
|
||||
$steps = 7
|
||||
$steps = 8
|
||||
|
||||
# ── Step 1: Portable Node.js ──────────────────────────────────────────────────
|
||||
Print-Step 1 $steps "Setting up portable Node.js (app/tools/node-win/)"
|
||||
@@ -242,6 +242,56 @@ if (-not $hasNvidia) {
|
||||
} catch {}
|
||||
}
|
||||
|
||||
Print-Step 2 $steps "Setting up stable-diffusion.cpp CPU backend (app/backend/win/cpu/)"
|
||||
$cpuBackendDest = Join-Path $appDir "backend\win\cpu"
|
||||
$cpuBackendExe = Join-Path $cpuBackendDest "sd-cpu.exe"
|
||||
$cpuBackendDll = Join-Path $cpuBackendDest "stable-diffusion.dll"
|
||||
|
||||
if ((Test-Path $cpuBackendExe) -and (Test-Path $cpuBackendDll)) {
|
||||
Print-OK "CPU backend binaries already ready."
|
||||
} else {
|
||||
$cpuBackendZip = Join-Path $toolsDir "sd-cpu.zip"
|
||||
New-Item -ItemType Directory -Force -Path $toolsDir | Out-Null
|
||||
New-Item -ItemType Directory -Force -Path $cpuBackendDest | Out-Null
|
||||
|
||||
$ok = Invoke-RichDownload `
|
||||
-Url "https://github.com/leejet/stable-diffusion.cpp/releases/download/master-669-2d40a8b/sd-master-2d40a8b-bin-win-avx2-x64.zip" `
|
||||
-Dest $cpuBackendZip `
|
||||
-Label "stable-diffusion.cpp CPU Backend (Windows x64 AVX2)"
|
||||
|
||||
if (-not $ok) { Print-Fail "Cannot download CPU backend binaries."; Read-Host; exit 1 }
|
||||
|
||||
$tempExt = Join-Path $toolsDir "sd-cpu-temp"
|
||||
Expand-WithProgress -ZipPath $cpuBackendZip -Destination $tempExt -Label "CPU Backend"
|
||||
Remove-Item $cpuBackendZip -Force
|
||||
|
||||
if (Test-Path $tempExt) {
|
||||
$extractedExe = Join-Path $tempExt "bin\sd-server.exe"
|
||||
if (-not (Test-Path $extractedExe)) { $extractedExe = Join-Path $tempExt "sd-server.exe" }
|
||||
if (-not (Test-Path $extractedExe)) { $extractedExe = Join-Path $tempExt "bin\sd.exe" }
|
||||
if (-not (Test-Path $extractedExe)) { $extractedExe = Join-Path $tempExt "sd.exe" }
|
||||
|
||||
$extractedDll = Join-Path $tempExt "bin\stable-diffusion.dll"
|
||||
if (-not (Test-Path $extractedDll)) { $extractedDll = Join-Path $tempExt "stable-diffusion.dll" }
|
||||
|
||||
if (Test-Path $extractedExe) { Copy-Item $extractedExe $cpuBackendExe -Force }
|
||||
if (Test-Path $extractedDll) { Copy-Item $extractedDll $cpuBackendDll -Force }
|
||||
|
||||
Get-ChildItem $tempExt -Filter "*.dll" -Recurse | ForEach-Object { Copy-Item $_.FullName $cpuBackendDest -Force }
|
||||
Get-ChildItem $tempExt -Filter "*.exe" -Recurse | ForEach-Object {
|
||||
if ($_.FullName -ne $extractedExe) { Copy-Item $_.FullName $cpuBackendDest -Force }
|
||||
}
|
||||
Remove-Item $tempExt -Recurse -Force
|
||||
}
|
||||
|
||||
if ((Test-Path $cpuBackendExe) -and (Test-Path $cpuBackendDll)) {
|
||||
Print-OK "CPU backend binaries installed successfully!"
|
||||
} else {
|
||||
Print-Fail "Failed to copy backend binaries to app/backend/win/cpu/."
|
||||
Read-Host; exit 1
|
||||
}
|
||||
}
|
||||
|
||||
if ($hasNvidia) {
|
||||
Print-Step 2 $steps "Setting up stable-diffusion.cpp CUDA GPU backend (app/backend/win/cuda/)"
|
||||
$backendDest = Join-Path $appDir "backend\win\cuda"
|
||||
@@ -441,7 +491,22 @@ if (-not $?) {
|
||||
Read-Host; exit 1
|
||||
}
|
||||
|
||||
Print-Step 6 $steps "Installing frontend dependencies (app/frontend/)"
|
||||
$npu = Get-CimInstance Win32_PnPEntity -ErrorAction SilentlyContinue |
|
||||
Where-Object { $_.Name -like "*Intel(R) AI Boost*" -or ($_.Name -match "NPU" -and $_.PNPClass -eq "ComputeAccelerator") } |
|
||||
Select-Object -First 1
|
||||
|
||||
Print-Step 6 $steps "Setting up portable OpenVINO NPU runtime"
|
||||
if ($npu) {
|
||||
& (Join-Path $scriptDir "setup-openvino-npu.ps1")
|
||||
if (-not $?) {
|
||||
Print-Fail "OpenVINO NPU setup failed."
|
||||
Read-Host; exit 1
|
||||
}
|
||||
} else {
|
||||
Print-Info "Intel AI Boost NPU not detected. Skipping OpenVINO NPU runtime."
|
||||
}
|
||||
|
||||
Print-Step 7 $steps "Installing frontend dependencies (app/frontend/)"
|
||||
Write-Host ""
|
||||
|
||||
if (-not (Test-Path $npmCmd)) {
|
||||
@@ -486,7 +551,7 @@ Push-Location $frontendDir
|
||||
$oldPath = $env:PATH
|
||||
try {
|
||||
$env:PATH = "$nodeDir;$env:PATH"
|
||||
& $npmCmd install --prefer-offline 2>&1
|
||||
& $npmCmd install --prefer-offline --loglevel=error
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Print-Fail "npm install failed."
|
||||
Read-Host; exit 1
|
||||
@@ -495,10 +560,10 @@ try {
|
||||
Print-OK "Dependencies installed!"
|
||||
|
||||
# ── Step 4: Build frontend ────────────────────────────────────────────────
|
||||
Print-Step 7 $steps "Building frontend -> app/dist/"
|
||||
Print-Step 8 $steps "Building frontend -> app/dist/"
|
||||
Write-Host ""
|
||||
|
||||
& $npmCmd run build 2>&1
|
||||
& $npmCmd run build
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Print-Fail "Frontend build failed."
|
||||
Read-Host; exit 1
|
||||
|
||||
Reference in new issue
Block a user