2026-06-07 21:51:01 +08:00
|
|
|
"""应用配置:从 .env / 环境变量读取,集中管理所有开关。"""
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
from functools import lru_cache
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
from pydantic import Field, field_validator
|
|
|
|
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
|
|
|
model_config = SettingsConfigDict(
|
|
|
|
|
env_file=".env",
|
|
|
|
|
env_file_encoding="utf-8",
|
|
|
|
|
case_sensitive=False,
|
|
|
|
|
extra="ignore",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# ===== 通用 =====
|
|
|
|
|
tz: str = "Asia/Hong_Kong"
|
|
|
|
|
log_level: str = "INFO"
|
|
|
|
|
|
|
|
|
|
# ===== 数据库 =====
|
|
|
|
|
postgres_user: str
|
|
|
|
|
postgres_password: str
|
|
|
|
|
postgres_db: str
|
|
|
|
|
postgres_host: str = "postgres"
|
|
|
|
|
postgres_port: int = 5432
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def database_url(self) -> str:
|
|
|
|
|
# asyncpg
|
|
|
|
|
return (
|
|
|
|
|
f"postgresql+asyncpg://{self.postgres_user}:{self.postgres_password}"
|
|
|
|
|
f"@{self.postgres_host}:{self.postgres_port}/{self.postgres_db}"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def sync_database_url(self) -> str:
|
|
|
|
|
# alembic 用的同步 URL
|
|
|
|
|
return (
|
|
|
|
|
f"postgresql+psycopg2://{self.postgres_user}:{self.postgres_password}"
|
|
|
|
|
f"@{self.postgres_host}:{self.postgres_port}/{self.postgres_db}"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# ===== Redis =====
|
|
|
|
|
redis_host: str = "redis"
|
|
|
|
|
redis_port: int = 6379
|
|
|
|
|
redis_password: str
|
|
|
|
|
redis_db: int = 0
|
|
|
|
|
|
|
|
|
|
@property
|
|
|
|
|
def redis_url(self) -> str:
|
|
|
|
|
return (
|
|
|
|
|
f"redis://:{self.redis_password}@{self.redis_host}:{self.redis_port}/{self.redis_db}"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# ===== JWT =====
|
|
|
|
|
jwt_secret: str
|
|
|
|
|
jwt_algorithm: str = "HS256"
|
|
|
|
|
access_token_ttl_min: int = 60
|
|
|
|
|
refresh_token_ttl_day: int = 14
|
|
|
|
|
|
|
|
|
|
# ===== 腾讯云 TMT =====
|
|
|
|
|
tencentcloud_secret_id: str = ""
|
|
|
|
|
tencentcloud_secret_key: str = ""
|
|
|
|
|
tencentcloud_region: str = "ap-hongkong"
|
|
|
|
|
tencent_tmt_endpoint: str = "tmt.tencentcloudapi.com"
|
|
|
|
|
tencent_tmt_quota_month: int = 5_000_000
|
|
|
|
|
tencent_tmt_quota_buffer: float = 0.05
|
|
|
|
|
tencent_tmt_max_chars_per_req: int = 4500
|
|
|
|
|
|
|
|
|
|
@field_validator("tencent_tmt_quota_buffer")
|
|
|
|
|
@classmethod
|
|
|
|
|
def _check_buffer(cls, v: float) -> float:
|
|
|
|
|
if not 0.0 <= v <= 0.5:
|
|
|
|
|
raise ValueError("buffer 必须在 0~0.5")
|
|
|
|
|
return v
|
|
|
|
|
|
|
|
|
|
# ===== 本地翻译 =====
|
|
|
|
|
local_translate_enabled: bool = False
|
|
|
|
|
local_translate_model: str = "nllb-200-distilled-600M"
|
|
|
|
|
local_translate_device: str = "cpu"
|
|
|
|
|
|
2026-06-09 17:33:45 +08:00
|
|
|
# ===== 腾讯 MaaS(OpenAI 兼容翻译备用通道)=====
|
|
|
|
|
# 用法:腾讯云 MaaS 提供的翻译模型,通过 OpenAI 协议调用
|
|
|
|
|
# 留空 api_key = 不启用该 provider
|
|
|
|
|
tencent_maas_api_key: str = ""
|
|
|
|
|
tencent_maas_base_url: str = "https://maas-api.hivoice.cn/v1"
|
|
|
|
|
tencent_maas_model: str = "u2"
|
|
|
|
|
# 每篇调用间隔(秒),与 LLM 客户端解耦
|
|
|
|
|
tencent_maas_interval_sec: float = 1.0
|
|
|
|
|
|
2026-06-07 21:51:01 +08:00
|
|
|
# ===== 抓取 =====
|
|
|
|
|
fetch_global_qps: int = 4
|
|
|
|
|
fetch_timeout: int = 20
|
|
|
|
|
fetch_fail_pause_threshold: int = 3
|
|
|
|
|
fetch_max_retries: int = 2
|
|
|
|
|
|
|
|
|
|
# ===== Caddy / 域名 =====
|
|
|
|
|
domain: str = ""
|
|
|
|
|
acme_email: str = ""
|
|
|
|
|
|
2026-06-08 14:24:23 +08:00
|
|
|
# ===== Agnes LLM(智能增强)=====
|
|
|
|
|
# 留空 = 不启用 LLM 增强(翻译后只走默认排版,提示词也不读)
|
|
|
|
|
agnes_api_key: str = ""
|
|
|
|
|
agnes_base_url: str = "https://apihub.agnes-ai.com/v1"
|
|
|
|
|
agnes_chat_model: str = "agnes-2.0-flash"
|
|
|
|
|
agnes_image_model: str = "agnes-image-2.1-flash"
|
|
|
|
|
# 全局 LLM 调用间隔(秒),避免被限流
|
|
|
|
|
llm_interval_sec: float = 2.0
|
|
|
|
|
|
2026-06-07 21:51:01 +08:00
|
|
|
# ===== 内部路径(部署后可调) =====
|
|
|
|
|
project_root: Path = Path(__file__).resolve().parents[2]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@lru_cache
|
|
|
|
|
def get_settings() -> Settings:
|
|
|
|
|
return Settings() # type: ignore[call-arg]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
settings = get_settings()
|