"""settings.py —— vstore 环境变量驱动配置(dataclass,不引 pydantic)。 环境变量键: - VSTORE_KEY:Fernet key,必填(缺失抛异常拒绝启动) - VSTORE_ROOT:数据根目录(默认 data/vstore) - VSTORE_VAULT_PATH / VSTORE_INDEX_PATH:可覆盖(本地路径或 http(s):// URL) - VSTORE_EMBED_URL:ollama 嵌入服务(默认 http://127.0.0.1:11434) - VSTORE_EMBED_MODEL:嵌入模型(默认 bge-m3:567m) """ from __future__ import annotations import os from dataclasses import dataclass, field from pathlib import Path # ---- 生成参数(2026-09-04 手工测试:上传不再设大小/数量/字符门槛)---- # EMBED_BODY_CAP 仅是嵌入分块预算(向量化截断),不是上传限制; # 原文件全文始终加密入库,不受影响。 MAX_SECTIONS = 12 # 模版大纲裁剪上限(节数) MAX_OUTLINE_CHUNKS = 3 # 大纲嵌入切块上限 EMBED_BODY_CAP = 200_000 # 正文嵌入分块预算(字符) # ---- 检索参数 ---- EMBED_DIM = 1024 # bge-m3:567m 输出维度 DEFAULT_TOP_K = 5 MAX_TOP_K = 20 OUTLINE_EMBED_MAX_CHARS = 7500 # 大纲超过此长度按节切块(≤MAX_OUTLINE_CHUNKS) FALLBACK_LATEST_N = 8 # 三档降级最后一档:最新 N 条 # ---- 嵌入服务 ---- DEFAULT_EMBED_URL = "http://127.0.0.1:11434" DEFAULT_EMBED_MODEL = "bge-m3:567m" EMBED_CONCURRENCY = 8 # embed_batch 并发上限 EMBED_TIMEOUT = 60.0 @dataclass class VstorePaths: """vstore 数据路径(vault/index 可分别指向本地目录或远程 http(s):// 根)。""" root: str = field(default_factory=lambda: os.environ.get("VSTORE_ROOT", "data/vstore")) vault_path: str = field(default_factory=lambda: os.environ.get("VSTORE_VAULT_PATH", "")) index_path: str = field(default_factory=lambda: os.environ.get("VSTORE_INDEX_PATH", "")) embed_url: str = field(default_factory=lambda: os.environ.get("VSTORE_EMBED_URL", DEFAULT_EMBED_URL)) embed_model: str = field(default_factory=lambda: os.environ.get("VSTORE_EMBED_MODEL", DEFAULT_EMBED_MODEL)) def __post_init__(self) -> None: # vault/index 未显式指定时落在 root 下 if not self.vault_path: self.vault_path = str(Path(self.root) / "vault") if not self.index_path: self.index_path = str(Path(self.root) / "index") def get_fernet_key() -> bytes: """VSTORE_KEY(Fernet key,必填)。缺失抛异常(阻止启动)。 生成方式: python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" """ raw = os.environ.get("VSTORE_KEY", "") if not raw: raise RuntimeError( "缺少环境变量 VSTORE_KEY(Fernet key,必填)。生成:" 'python -c "from cryptography.fernet import Fernet; ' 'print(Fernet.generate_key().decode())"' ) return raw.encode("utf-8") _paths: VstorePaths | None = None def get_paths() -> VstorePaths: """取全局路径配置(首次调用构造)。""" global _paths if _paths is None: _paths = VstorePaths() return _paths def set_paths(paths: VstorePaths) -> None: """注入配置(测试/启动用)。""" global _paths _paths = paths