增加exe打包配置

This commit is contained in:
lasean.zhou
2026-04-14 17:07:08 +08:00
parent cc83fe963a
commit 026d3bf88c
3 changed files with 376 additions and 0 deletions
+203
View File
@@ -0,0 +1,203 @@
# G3SB Backman Windows Service Installation Script
# Run as Administrator
param(
[Parameter(Mandatory=$false)]
[string]$Action = "install"
)
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin) {
Write-Host "ERROR: Administrator privileges required" -ForegroundColor Red
Write-Host "Please right-click and select 'Run as administrator'" -ForegroundColor Yellow
Start-Sleep -Seconds 5
exit 1
}
$serviceName = "Backman_Text2sql"
$serviceDisplayName = "Backman Text2SQL Service"
$serviceDescription = "Backman Text2SQL AI Backend Service"
$scriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
# 单文件模式:exe 在 deploy 同级目录
$exePath = Join-Path $scriptPath "Backman_Text2SQL_Server.exe"
if (-not (Test-Path $exePath)) {
Write-Host "ERROR: Backman_Text2SQL_Server.exe not found" -ForegroundColor Red
Write-Host "Path: $exePath" -ForegroundColor Yellow
Write-Host "Please run 'pyinstaller --clean main.spec' in project root first" -ForegroundColor Yellow
Start-Sleep -Seconds 5
exit 1
}
$NSSM_URL = "https://nssm.cc/release/nssm-2.24.zip"
$nssmPath = Join-Path $scriptPath "nssm.exe"
function Get-NSSM {
if (-not (Test-Path $nssmPath)) {
Write-Host "Downloading NSSM..." -ForegroundColor Cyan
$zipPath = Join-Path $env:TEMP "nssm.zip"
try {
Invoke-WebRequest -Uri $NSSM_URL -OutFile $zipPath -UseBasicParsing
Add-Type -AssemblyName System.IO.Compression.FileSystem
[System.IO.Compression.ZipFile]::ExtractToDirectory($zipPath, $env:TEMP)
$nssmSource = Get-ChildItem -Path $env:TEMP -Recurse -Filter "nssm.exe" | Where-Object { $_.FullName -like "*win64*" } | Select-Object -First 1
if ($nssmSource) {
Copy-Item $nssmSource.FullName -Destination $nssmPath -Force
Write-Host "NSSM downloaded to: $nssmPath" -ForegroundColor Green
} else {
throw "nssm.exe not found"
}
Remove-Item $zipPath -Force
Remove-Item (Join-Path $env:TEMP "nssm-2.24") -Recurse -Force
}
catch {
Write-Host "Failed to download NSSM: $_" -ForegroundColor Red
Write-Host "Manual: Visit https://nssm.cc/download and place nssm.exe in this directory" -ForegroundColor Yellow
Start-Sleep -Seconds 5
exit 1
}
} else {
Write-Host "NSSM exists: $nssmPath" -ForegroundColor Green
}
}
function Install-Service {
Write-Host "`n=================================" -ForegroundColor Cyan
Write-Host "Installing G3SB Backman Service" -ForegroundColor Cyan
Write-Host "=================================`n" -ForegroundColor Cyan
Get-NSSM
$workDir = $scriptPath
Write-Host "Creating service: $serviceName" -ForegroundColor Cyan
& $nssmPath install $serviceName "$exePath"
if ($LASTEXITCODE -eq 0) {
& $nssmPath set $serviceName DisplayName $serviceDisplayName
& $nssmPath set $serviceName Description $serviceDescription
& $nssmPath set $serviceName Start SERVICE_AUTO_START
& $nssmPath set $serviceName AppDirectory $workDir
$logDir = Join-Path $workDir "logs"
if (-not (Test-Path $logDir)) { New-Item -ItemType Directory -Path $logDir -Force | Out-Null }
$stdoutLog = Join-Path $logDir "service_stdout.log"
$stderrLog = Join-Path $logDir "service_stderr.log"
& $nssmPath set $serviceName AppStdout $stdoutLog
& $nssmPath set $serviceName AppStderr $stderrLog
& $nssmPath set $serviceName AppRestartDelay 1000
& $nssmPath set $serviceName AppExit Default Restart
Write-Host "\n[OK] Service installed successfully!" -ForegroundColor Green
Write-Host " Service Name: $serviceName" -ForegroundColor White
Write-Host " Display Name: $serviceDisplayName" -ForegroundColor White
Write-Host " Executable: $exePath" -ForegroundColor White
$response = Read-Host "`nStart service now? (Y/n)"
if ([string]::IsNullOrWhiteSpace($response) -or $response -match '^[Yy]$') {
Start-ServiceAction
}
Write-Host "`nTips:" -ForegroundColor Yellow
Write-Host " Status: .\install_service.ps1 status" -ForegroundColor White
Write-Host " Stop: .\install_service.ps1 stop" -ForegroundColor White
Write-Host " Start: .\install_service.ps1 start" -ForegroundColor White
Write-Host " Uninstall: .\install_service.ps1 uninstall" -ForegroundColor White
} else {
Write-Host "`n[FAIL] Service installation failed!" -ForegroundColor Red
}
}
function Uninstall-Service {
Write-Host "`n=================================" -ForegroundColor Cyan
Write-Host "Uninstalling G3SB Backman Service" -ForegroundColor Cyan
Write-Host "=================================`n" -ForegroundColor Cyan
Stop-ServiceAction
Get-NSSM
$confirm = Read-Host "Confirm uninstall? (y/N)"
if ($confirm -notmatch '^[Yy]$') {
Write-Host "Cancelled" -ForegroundColor Yellow
return
}
& $nssmPath remove $serviceName confirm
if ($LASTEXITCODE -eq 0) {
Write-Host "`n[OK] Service uninstalled!" -ForegroundColor Green
} else {
Write-Host "`n[FAIL] Uninstall failed!" -ForegroundColor Red
}
}
function Start-ServiceAction {
Write-Host "`nStarting service $serviceName ..." -ForegroundColor Cyan
try {
Start-Service -Name $serviceName -ErrorAction Stop
Write-Host "[OK] Service started" -ForegroundColor Green
Start-Sleep -Seconds 3
$status = Get-Service -Name $serviceName -ErrorAction SilentlyContinue
if ($status.Status -eq 'Running') {
Write-Host "[OK] Service running" -ForegroundColor Green
} else {
Write-Host "[WARN] Service status: $($status.Status)" -ForegroundColor Yellow
}
}
catch {
Write-Host "[FAIL] Failed to start: $_" -ForegroundColor Red
}
}
function Stop-ServiceAction {
Write-Host "`nStopping service $serviceName ..." -ForegroundColor Cyan
try {
Stop-Service -Name $serviceName -Force -ErrorAction Stop
Write-Host "[OK] Service stopped" -ForegroundColor Green
}
catch {
Write-Host "[WARN] Service not running or failed to stop: $_" -ForegroundColor Yellow
}
}
function Get-ServiceStatus {
Write-Host "`n=================================" -ForegroundColor Cyan
Write-Host "Service Status" -ForegroundColor Cyan
Write-Host "=================================`n" -ForegroundColor Cyan
try {
$service = Get-Service -Name $serviceName -ErrorAction Stop
Write-Host "Service Name: $($service.Name)" -ForegroundColor White
Write-Host "Display Name: $($service.DisplayName)" -ForegroundColor White
Write-Host "Status: $($service.Status)" -ForegroundColor $(if ($service.Status -eq 'Running') { 'Green' } else { 'Yellow' })
Write-Host "Start Type: $($service.StartType)" -ForegroundColor White
if ($service.Status -eq 'Running') {
Write-Host "`n[OK] Service is running" -ForegroundColor Green
Write-Host "URL: http://localhost:8041" -ForegroundColor Cyan
} else {
Write-Host "`n[WARN] Service is not running" -ForegroundColor Yellow
}
}
catch {
Write-Host "[FAIL] Service not installed" -ForegroundColor Red
Write-Host "Install: .\install_service.ps1 install" -ForegroundColor Yellow
}
Write-Host ""
}
Write-Host "`nG3SB Backman Service Manager" -ForegroundColor Cyan
Write-Host "============================`n" -ForegroundColor Cyan
switch ($Action.ToLower()) {
"install" { Install-Service }
"uninstall" { Uninstall-Service }
"start" { Start-ServiceAction }
"stop" { Stop-ServiceAction }
"status" { Get-ServiceStatus }
"restart" { Stop-ServiceAction; Start-Sleep -Seconds 2; Start-ServiceAction }
default {
Write-Host "Usage: .\install_service.ps1 [action]" -ForegroundColor Yellow
Write-Host "Actions: install, uninstall, start, stop, restart, status" -ForegroundColor White
}
}
Write-Host ""
+21
View File
@@ -0,0 +1,21 @@
以**管理员身份**打开 PowerShell,然后执行:
```powershell
# 安装服务
powershell.exe -ExecutionPolicy Bypass -File .\install_service.ps1 install
# 查看服务状态
powershell.exe -ExecutionPolicy Bypass -File .\install_service.ps1 status
# 启动服务
powershell.exe -ExecutionPolicy Bypass -File .\install_service.ps1 start
# 停止服务
powershell.exe -ExecutionPolicy Bypass -File .\install_service.ps1 stop
# 重启服务
powershell.exe -ExecutionPolicy Bypass -File .\install_service.ps1 restart
# 卸载服务
powershell.exe -ExecutionPolicy Bypass -File .\install_service.ps1 uninstall
```
+152
View File
@@ -0,0 +1,152 @@
# -*- mode: python ; coding: utf-8 -*-
import os
from pathlib import Path
from PyInstaller.utils.hooks import collect_data_files, collect_submodules
# 获取项目根目录(spec 文件所在目录)
import sys
_REPO_DIR = Path(os.getcwd()).resolve()
# 根目录执行:pyinstaller --clean main.spec
# 收集配置文件夹和数据文件
datas = [
# 所有数据文件都不打包,从外部读取
# ('.env', '.'),
# ('data/schemas', 'data/schemas'),
# ('data/embeddings', 'data/embeddings'),
# ('data/experiences', 'data/experiences'),
]
# 收集所有需要的隐藏导入
hiddenimports = [
# Web 框架
'fastapi',
'fastapi.middleware.cors',
'fastapi.responses',
'uvicorn',
'uvicorn.lifespan.on',
'uvicorn.config',
'uvicorn.protocols.http.auto',
'uvicorn.protocols.websockets.auto',
'uvicorn.loops.auto',
# Backend 模块(重要!)
'main', # backend/main.py
'backend.main',
'backend.nl_lite_store',
'backend.agents',
'backend.agents.orchestrator',
'backend.agents.schema_linker',
'backend.agents.sql_generator',
'backend.agents.validator',
'backend.config',
'backend.config.prompts',
'backend.config.settings',
'backend.db',
'backend.db.engine',
'backend.db.dbhub_tools',
'backend.db.dbhub_sql_parser',
'backend.db.dbhub_allowed_keywords',
'backend.llm',
'backend.llm.deepseek_client',
'backend.schema',
'backend.schema.manager',
'backend.schema.loader',
'backend.schema.indexer',
'backend.schema.models',
'backend.utils',
'backend.utils.dialog_classifier',
'backend.utils.embedding',
'backend.utils.fewshot_selector',
'backend.utils.question_locale',
'backend.utils.sql_parser',
'backend.utils.validators',
# 数据库相关
'pymssql',
'sqlalchemy',
'sqlalchemy.dialects.mssql.pymssql',
'sqlalchemy.dialects.mysql',
'sqlalchemy.dialects.postgresql',
'sqlglot',
'sqlglot.dialects',
'sqlparse',
# 向量数据库 - 使用 collect_submodules 自动收集所有子模块
# Embedding 相关
'sentencepiece',
'accelerate',
'safetensors',
# LLM API
'openai',
# 数据处理
'numpy',
'jieba',
# Pydantic
'pydantic',
'pydantic_settings',
'dotenv',
# 工具库
'tenacity',
'structlog',
'tqdm',
'camel_ai',
# 标准库中可能被遗漏的
'email.mime.text',
'email.mime.multipart',
] + collect_submodules('chromadb')
# 排除不必要的模块以减小体积
excludes = [
'tkinter',
'matplotlib',
'scipy',
'pandas.tests',
'numpy.testing',
'jupyter',
'notebook',
'IPython',
'pytest',
]
a = Analysis(
['api_server.py'],
pathex=[str(_REPO_DIR)],
binaries=[],
datas=datas,
hiddenimports=hiddenimports,
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=excludes,
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas, # 单文件模式:将数据文件打包进 exe
[],
name='Backman_Text2SQL_Server',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=False, # 禁用 UPX,避免兼容性问题
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)