134 lines
5.1 KiB
Python
134 lines
5.1 KiB
Python
"""config.py —— 生信服务(bioflow-service 8098)环境变量驱动配置(pydantic-settings)。
|
||
|
||
全部键可用环境变量覆盖(前缀 BIOFLOW_;LLM 沿用 docgen 的 DOCGEN_LLM_* 直连模式)。
|
||
零硬编码纪律:不出现任何写死宿主路径;仓库内相对路径按 __file__ 推导 repo 根。
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
from functools import lru_cache
|
||
from pathlib import Path
|
||
|
||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||
|
||
_REPO_ROOT = Path(__file__).resolve().parent.parent.parent # bioflow_service/.. = 仓库根
|
||
|
||
|
||
def _resolve_root(v: str) -> str:
|
||
"""仓库内相对路径按 __file__ 推导 repo 根解析为绝对路径。"""
|
||
p = Path(v).expanduser()
|
||
if not p.is_absolute():
|
||
p = _REPO_ROOT / p
|
||
return str(p)
|
||
|
||
|
||
class Settings(BaseSettings):
|
||
model_config = SettingsConfigDict(
|
||
env_file=".env",
|
||
env_file_encoding="utf-8",
|
||
extra="ignore",
|
||
env_prefix="BIOFLOW_",
|
||
)
|
||
|
||
# ---- 应用 ----
|
||
app_name: str = "plantai-bioflow-service"
|
||
version: str = "0.1.0"
|
||
|
||
# ---- 阶段→镜像映射(A1 交付物,仓库内 bioflow_kernel/stage_image_map.json)----
|
||
image_map_path: str = "bioflow_kernel/stage_image_map.json"
|
||
|
||
# ---- 数据与任务目录 ----
|
||
data_root: str = "bioflow_data" # 生信数据根(hmm/rag_db 等,只读挂载进容器)
|
||
jobs_dir: str = "bioflow_data/jobs" # 任务目录根(input/workspace/events)
|
||
db_path: str = "bioflow_data/bioflow.db" # 独立 SQLite(与 BFF 完全隔离,FR-BIO-028)
|
||
archives_dir: str = "bioflow_data/archives"
|
||
|
||
# ---- 执行 ----
|
||
concurrency: int = 3 # 全局并发信号量上限(FR-BIO-031,可配)
|
||
stage_timeout_sec: float = 36000.0 # 单阶段 docker 硬超时(秒,10h)
|
||
docker_image_tag_suffix: str = "dev" # 镜像 tag 后缀(P0 交付物为 :dev)
|
||
host_uid_env: str = "BIOFLOW_HOST_UID" # 宿主机 uid 环境变量名;未设置时回退服务进程 uid
|
||
container_data_dir: str = "/bioflow-data"
|
||
container_input_dir: str = "/job/input"
|
||
container_workspace_dir: str = "/job/workspace"
|
||
default_cpus: str = "4"
|
||
default_memory: str = "32g"
|
||
log_tail_lines: int = 80 # manifest 接口日志尾行数(≤80)
|
||
max_upload_gb: float = 100.0 # 上传大小上限(GB;超限 413)
|
||
upload_ext_allow: str = ".fasta,.fa,.faa,.fna,.pep,.cds,.gz,.gff,.gff3,.gtf,.txt,.tsv,.csv,.xlsx,.xls,.bed,.vcf,.vcf.gz,.bam,.cram,.sam,.hmm,.nwk,.newick,.treefile,.json,.yaml,.yml,.pdf"
|
||
|
||
# ---- 规划(planner)----
|
||
llm_plan_timeout: float = 60.0 # 规划 LLM 单次调用超时(秒)
|
||
plan_candidates: int = 3 # 候选路径条数(2~3,FR-BIO-002)
|
||
plan_max_rounds: int = 2 # LLM 输出硬校验失败的最多重试轮数
|
||
|
||
# ---- 知识库(vstore/flow.py 本地实例;无 vstore 时自动降级 llm_only)----
|
||
flow_docs_dir: str = "vstore/data/flow_docs"
|
||
|
||
# ---- LLM(沿用 docgen deepseek 直连模式,DOCGEN_LLM_* 键)----
|
||
docgen_llm_base_url: str = "https://api.deepseek.com/v1"
|
||
docgen_llm_api_key: str = ""
|
||
docgen_llm_model: str = "deepseek-chat"
|
||
docgen_llm_timeout: float = 300.0
|
||
|
||
# ---- 报告登记(BFF 反代端点;空=未配置,登记降级为仅落表)----
|
||
service_url: str = "http://127.0.0.1:8098"
|
||
|
||
# ---- 容器环境变量(BBA_*;零硬编码:路径全部从本配置推导)----
|
||
@property
|
||
def bba_env(self) -> dict[str, str]:
|
||
"""docker run 注入的 BBA_* 环境(值全部来自配置推导,无写死宿主路径)。"""
|
||
return {
|
||
"BBA_PFAM_DIR": f"{self.container_data_dir}/hmm",
|
||
"BBA_RAG_MAPPING_FILE": f"{self.container_data_dir}/rag_db/pfam_mapping.json",
|
||
"BBA_DISABLE_RAG_PFAM_LOOKUP": "0",
|
||
}
|
||
|
||
@property
|
||
def host_uid(self) -> int:
|
||
"""--user 注入 uid:BIOFLOW_HOST_UID 环境变量优先,否则服务进程 uid。"""
|
||
import os
|
||
|
||
raw = os.environ.get(self.host_uid_env, "").strip()
|
||
if raw:
|
||
try:
|
||
return int(raw)
|
||
except ValueError:
|
||
pass
|
||
return os.getuid()
|
||
|
||
@property
|
||
def image_map_abs(self) -> str:
|
||
return _resolve_root(self.image_map_path)
|
||
|
||
@property
|
||
def data_root_abs(self) -> str:
|
||
return _resolve_root(self.data_root)
|
||
|
||
@property
|
||
def jobs_dir_abs(self) -> str:
|
||
return _resolve_root(self.jobs_dir)
|
||
|
||
@property
|
||
def db_path_abs(self) -> str:
|
||
return _resolve_root(self.db_path)
|
||
|
||
@property
|
||
def archives_dir_abs(self) -> str:
|
||
return _resolve_root(self.archives_dir)
|
||
|
||
@property
|
||
def flow_docs_dir_abs(self) -> str:
|
||
return _resolve_root(self.flow_docs_dir)
|
||
|
||
@property
|
||
def upload_ext_set(self) -> set[str]:
|
||
return {ext.strip().lower() for ext in self.upload_ext_allow.split(",") if ext.strip()}
|
||
|
||
|
||
@lru_cache(maxsize=1)
|
||
def get_settings() -> Settings:
|
||
return Settings()
|
||
|
||
|
||
settings = get_settings()
|