Files

270 lines
7.7 KiB
RPMSpec
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- mode: python ; coding: utf-8 -*-
import os
from pathlib import Path
from PyInstaller.utils.hooks import collect_data_files, collect_submodules, collect_dynamic_libs
# 获取项目根目录(spec 文件所在目录)
import sys
_REPO_DIR = Path(os.getcwd()).resolve()
def _strip_msvc_runtime_binaries(bin_list):
"""
从 ``collect_dynamic_libs`` 等额外收集的 binaries 中移除 MSVC 运行库 DLL。
chromadb / hnswlib 等 wheel 常自带 ``MSVCP140.dll`` 等;与 PyInstaller 已为
``python.exe`` 解析到的 CRT 一并打入单文件包时,易在运行期混用多份 CRT
导致 ``MSVCP140.dll`` 内 ``0xc0000005``。统一依赖解释器自带的 CRT 集更安全。
"""
skip = frozenset({
'msvcp140.dll',
'msvcp140_1.dll',
'msvcp140_2.dll',
'msvcp140_atomic_wait.dll',
'msvcp140_codecvt_ids.dll',
'vcruntime140.dll',
'vcruntime140_1.dll',
'concrt140.dll',
})
out = []
for item in bin_list:
dest = item[0]
base = Path(dest).name.lower()
if base in skip:
continue
out.append(item)
return out
# 根目录执行:pyinstaller --clean main.spec
#
# ---------------------------------------------------------------------------
# 向量「数据库文件」/ 业务数据 —— 明确不打入 onefile EXE
# ---------------------------------------------------------------------------
# 下列目录为运行时读写或外置分发的数据,禁止加入下方 ``datas``(勿取消注释):
# - Schema 侧:环境变量 VECTOR_DB_PATH(默认如 ./data/embeddings/chroma)
# - Few-shot 侧:FEWSHOT_CHROMA_PATH(默认如 ./data/embeddings/chroma_fewshot)
# 部署:将上述目录放在 exe 同目录旁,或通过 .env 指向可写绝对路径。
# SchemaIndexer 默认使用 PersistentClient(VECTOR_DB_PATH);仅 SCHEMA_INDEXER_EPHEMERAL=true 时为内存索引。
# ---------------------------------------------------------------------------
# 可选:仅当需要把 Schema JSON 等打进包时再取消注释(向量库目录仍不要加)
datas = [
# ('.env', '.'),
# ('data/schemas', 'data/schemas'),
# 勿打包 data/embeddings、data/experiences(向量库与样本数据外置)
]
# Chroma **Python 包**在 Windows 单文件 exe 下如缺少 native 动态库,可能在初始化时直接退出
# 这里收集的是 chromadb / hnswlib 的 **wheel 内 DLL**,不是用户磁盘上的 Chroma 持久化目录
binaries = []
try:
binaries += collect_dynamic_libs("chromadb")
except Exception:
pass
try:
binaries += collect_dynamic_libs("hnswlib")
except Exception:
pass
# chromadb 包自带的非 Python 资源(如 migrations),非业务向量库文件
try:
datas += collect_data_files("chromadb")
except Exception:
pass
binaries = _strip_msvc_runtime_binaries(binaries)
def _dedupe_msvc_runtime_in_analysis_binaries(toc):
"""
对 ``Analysis`` 产出的 ``a.binaries`` 去重:同名 MSVC 运行库 DLL 只保留一份。
PyInstaller 会从 Python 安装目录、numpy、torch、chromadb 等多个依赖各自打入
同名 ``MSVCP140.dll`` 等;单文件解压到 ``_MEI*`` 后若出现多份或加载顺序混乱,
易在 ``MSVCP140.dll`` 内触发 ``0xc0000005``。优先保留 **源路径位于**
``sys.base_prefix`` 下的条目(与解释器主 CRT 一致)。
"""
vc_names = frozenset({
'msvcp140.dll',
'msvcp140_1.dll',
'msvcp140_2.dll',
'msvcp140_atomic_wait.dll',
'msvcp140_codecvt_ids.dll',
'vcruntime140.dll',
'vcruntime140_1.dll',
'concrt140.dll',
})
try:
base_root = str(Path(sys.base_prefix).resolve()).lower()
except OSError:
base_root = str(Path(sys.base_prefix)).lower()
rest = []
vc_groups = {}
for item in toc:
dest = item[0]
bname = Path(dest).name.lower()
if bname not in vc_names:
rest.append(item)
continue
vc_groups.setdefault(bname, []).append(item)
picked = []
for bname, items in vc_groups.items():
if len(items) == 1:
picked.append(items[0])
continue
def _score(it):
try:
sp = str(Path(it[1]).resolve()).lower()
except OSError:
sp = str(it[1]).lower()
under = 0 if sp.startswith(base_root) else 1
return (under, len(sp))
items_sorted = sorted(items, key=_score)
picked.append(items_sorted[0])
return rest + picked
# 收集所有需要的隐藏导入
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.dialog_context',
'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=binaries,
datas=datas,
hiddenimports=hiddenimports,
hookspath=[],
hooksconfig={},
runtime_hooks=[str(_REPO_DIR / 'packaging' / 'pyi_rth_crt_path.py')],
excludes=excludes,
noarchive=False,
optimize=0,
)
# 去掉 Analysis 自动收集到的重复 MSVC 运行库(比仅过滤手动 binaries 更关键)
a.binaries = _dedupe_msvc_runtime_in_analysis_binaries(list(a.binaries))
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas, # 单文件:仅打包 spec 中 datas(chromadb 包资源等),不含业务向量持久化目录
[],
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,
)