Files
dwarf-fortress-annals/src/dfannals/config.py
T

111 lines
3.9 KiB
Python

"""项目配置:路径、模型、网关、发布目标。
原则:API key 不落本项目文件,运行时从 Pi 的 auth.json 读取。
"""
from __future__ import annotations
import json
import os
from dataclasses import dataclass
from pathlib import Path
HOME = Path.home()
# ---------------------------------------------------------------- 目录
PROJECT_DIR = Path(__file__).resolve().parents[2] # /home/chendy/df-annals
DATA_DIR = PROJECT_DIR / "data" # 原始数据,不入库
EXPORT_DIR = DATA_DIR / "exports" # legends 导出原始文件
OUTPUT_DIR = PROJECT_DIR / "output" # 成品,入库
CHAPTER_DIR = OUTPUT_DIR / "chapters"
PROMPT_DIR = PROJECT_DIR / "prompts"
# ---------------------------------------------------------------- 游戏(Windows 侧)
WINDOWS_USER = "23518"
WIN_DF_ROOT = Path(f"/mnt/c/Users/{WINDOWS_USER}/DwarfFortress") # 下载/安装根目录
WIN_DF_DIR_CANDIDATES = [
WIN_DF_ROOT / "df_53_16_win",
WIN_DF_ROOT / "Dwarf Fortress",
WIN_DF_ROOT,
]
DF_VERSION = "53.16"
DFHACK_VERSION = "53.16-r2"
# ---------------------------------------------------------------- LLM 网关
PI_AUTH_JSON = HOME / ".pi/agent/auth.json"
PI_MODELS_JSON = HOME / ".pi/agent/models.json"
PROVIDER = "workbuddy"
MODEL = "cn:deepseek-v4-pro"
# 该模型是推理模型:思考与正文共用 max_tokens,必须留足
MAX_TOKENS_CHAPTER = 16000
MAX_TOKENS_UTIL = 4000
# ---------------------------------------------------------------- 发布
GITEA_SSH_HOST = "124.222.29.26"
GITEA_SSH_PORT = 2222
GITEA_USER = "gitadmin"
GITEA_REPO = "dwarf-fortress-annals"
GITEA_WEB = "http://124.222.29.26:3000"
GITEA_REMOTE = f"ssh://git@{GITEA_SSH_HOST}:{GITEA_SSH_PORT}/{GITEA_USER}/{GITEA_REPO}.git"
SSH_KEY = HOME / ".ssh/id_ed25519"
# ---------------------------------------------------------------- 世界与章节
WORLD_SIZE = "Medium"
WORLD_HISTORY_YEARS = 250
CHAPTER_MIN_CHARS = 800
CHAPTER_MAX_CHARS = 1600 # 契约上限 1500,留出标点/换行余量
@dataclass
class Gateway:
base_url: str
key: str
model: str = MODEL
provider: str = PROVIDER
def load_gateway(provider: str | None = None) -> Gateway:
"""从 Pi 配置读取网关地址与密钥。密钥只驻留内存,不打印、不落盘。"""
provider = provider or PROVIDER
def _read_json(path: Path, label: str) -> dict:
try:
return json.loads(path.read_text())
except FileNotFoundError:
raise SystemExit(f"找不到 Pi 的{label}文件: {path}") from None
except json.JSONDecodeError as exc:
raise SystemExit(f"Pi 的{label}文件不是合法 JSON: {path} ({exc})") from None
auth = _read_json(PI_AUTH_JSON, "凭据")
models = _read_json(PI_MODELS_JSON, "模型配置")
providers = models.get("providers", models)
if provider not in providers:
raise SystemExit(f"Pi 配置里没有 provider: {provider}")
if provider not in auth:
raise SystemExit(f"Pi 未授权 provider: {provider}")
entry = auth[provider]
key = entry.get("key") if isinstance(entry, dict) else entry
if not key:
raise SystemExit(f"provider {provider} 的凭据为空")
return Gateway(base_url=providers[provider]["baseUrl"].rstrip("/"), key=key, provider=provider)
def find_df_dir() -> Path:
"""定位已安装的 DF 目录(含 Dwarf Fortress.exe)。"""
override = os.environ.get("DF_DIR")
if override:
p = Path(override)
if p.is_dir():
return p
raise SystemExit(f"DF_DIR 不存在: {p}")
for cand in WIN_DF_DIR_CANDIDATES:
if (cand / "Dwarf Fortress.exe").is_file():
return cand
raise SystemExit(
"未找到 Dwarf Fortress.exe。候选路径:" + ", ".join(str(c) for c in WIN_DF_DIR_CANDIDATES)
)
def ensure_dirs() -> None:
for d in (DATA_DIR, EXPORT_DIR, OUTPUT_DIR, CHAPTER_DIR, PROMPT_DIR):
d.mkdir(parents=True, exist_ok=True)