feat: refactor API key configuration and enhance application initialization

- Renamed `check_environment` to `check_api_key_configured` for clarity, simplifying the API key validation logic.
- Removed the blocking behavior of the API key check during application startup, allowing the app to run while providing a prompt for configuration.
- Updated `LocalAgentApp` to accept an `api_configured` parameter, enabling conditional messaging for API key setup.
- Enhanced the `SandboxRunner` to support backup management and improved execution result handling with detailed metrics.
- Integrated data governance strategies into the `HistoryManager`, ensuring compliance and improved data management.
- Added privacy settings and metrics tracking across various components to enhance user experience and application safety.
This commit is contained in:
Mimikko-zeus
2026-02-27 14:32:30 +08:00
parent ab5bbff6f7
commit 8a538bb950
58 changed files with 13457 additions and 350 deletions

150
build.py Normal file
View File

@@ -0,0 +1,150 @@
"""
LocalAgent 打包脚本
使用 PyInstaller 将项目打包成 Windows 可执行文件
使用方法:
python build.py
打包完成后,可执行文件位于 dist/LocalAgent/ 目录下
"""
import os
import sys
import shutil
import subprocess
from pathlib import Path
# 项目根目录
PROJECT_ROOT = Path(__file__).parent
def clean_build():
"""清理之前的构建文件"""
dirs_to_clean = ['build', 'dist']
files_to_clean = ['LocalAgent.spec']
for dir_name in dirs_to_clean:
dir_path = PROJECT_ROOT / dir_name
if dir_path.exists():
print(f"清理目录: {dir_path}")
shutil.rmtree(dir_path)
for file_name in files_to_clean:
file_path = PROJECT_ROOT / file_name
if file_path.exists():
print(f"清理文件: {file_path}")
file_path.unlink()
def build_exe():
"""使用 PyInstaller 打包"""
# PyInstaller 参数
args = [
'pyinstaller',
'--name=LocalAgent', # 程序名称
'--windowed', # 不显示控制台窗口GUI 程序)
# '--console', # 如果需要看到控制台输出,用这个替换上面的
'--onedir', # 打包成目录(比 onefile 启动更快)
# '--onefile', # 如果想打包成单个 exe用这个替换上面的
'--noconfirm', # 覆盖已有文件
'--clean', # 清理临时文件
# 添加数据文件
'--add-data=.env.example;.', # 配置模板
# 隐藏导入PyInstaller 可能检测不到的模块)
'--hidden-import=PIL',
'--hidden-import=PIL.Image',
'--hidden-import=openpyxl',
'--hidden-import=docx',
'--hidden-import=PyPDF2',
'--hidden-import=chardet',
'--hidden-import=dotenv',
'--hidden-import=requests',
'--hidden-import=tkinter',
'--hidden-import=tkinter.ttk',
'--hidden-import=tkinter.scrolledtext',
'--hidden-import=tkinter.messagebox',
'--hidden-import=tkinter.colorchooser',
# 排除不需要的模块(减小体积)
'--exclude-module=matplotlib',
'--exclude-module=numpy',
'--exclude-module=pandas',
'--exclude-module=scipy',
'--exclude-module=torch',
'--exclude-module=tensorflow',
# 入口文件
'main.py'
]
print("=" * 50)
print("开始打包 LocalAgent...")
print("=" * 50)
print(f"命令: {' '.join(args)}")
print()
# 执行打包
result = subprocess.run(args, cwd=PROJECT_ROOT)
if result.returncode == 0:
print()
print("=" * 50)
print("✅ 打包成功!")
print("=" * 50)
print()
print(f"可执行文件位置: {PROJECT_ROOT / 'dist' / 'LocalAgent'}")
print()
print("使用说明:")
print("1. 进入 dist/LocalAgent 目录")
print("2. 复制 .env.example 为 .env 并配置 API Key")
print("3. 运行 LocalAgent.exe")
print()
print("注意: 首次运行会自动创建 workspace 目录")
# 创建 workspace 目录结构
dist_dir = PROJECT_ROOT / 'dist' / 'LocalAgent'
if dist_dir.exists():
workspace = dist_dir / 'workspace'
(workspace / 'input').mkdir(parents=True, exist_ok=True)
(workspace / 'output').mkdir(parents=True, exist_ok=True)
(workspace / 'logs').mkdir(parents=True, exist_ok=True)
(workspace / 'codes').mkdir(parents=True, exist_ok=True)
print("已创建 workspace 目录结构")
# 复制 .env.example
env_example = PROJECT_ROOT / '.env.example'
if env_example.exists():
shutil.copy(env_example, dist_dir / '.env.example')
print("已复制 .env.example")
else:
print()
print("=" * 50)
print("❌ 打包失败!")
print("=" * 50)
print("请检查错误信息")
return result.returncode
def main():
"""主函数"""
# 检查 PyInstaller 是否安装
try:
import PyInstaller
print(f"PyInstaller 版本: {PyInstaller.__version__}")
except ImportError:
print("错误: 未安装 PyInstaller")
print("请运行: pip install pyinstaller")
sys.exit(1)
# 询问是否清理
response = input("是否清理之前的构建文件? (y/n) [y]: ").strip().lower()
if response != 'n':
clean_build()
# 执行打包
return build_exe()
if __name__ == '__main__':
sys.exit(main())