第一批:上传去限制+进度展示、模板HTML预览+按需导出、检索TAB实时更新
第二批:edit护栏移除、导出按钮纯化+file_name落库、编辑失焦还原、
二次预览白底根治(防竞态+渲染兜底)
模板体系:7套公文模板(templates/)+templating渲染器、websearch联网
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
87 lines
3.3 KiB
Python
87 lines
3.3 KiB
Python
"""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
|