154 lines
7.1 KiB
Python
154 lines
7.1 KiB
Python
"""
|
||
配置包:只读环境变量,组装 Settings。
|
||
|
||
本文件职责:集中声明 YTD_ENV、端口、企微与库变量名(对齐 prompt/13、09)。
|
||
禁止:在本包写业务 if/else;禁止把密钥默认值写进源码。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from functools import lru_cache
|
||
from pathlib import Path
|
||
|
||
from pydantic import Field
|
||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||
|
||
|
||
def _find_dotenv() -> str:
|
||
"""自 agent 目录向上找仓库根 .env;找不到则返回空,由环境变量本身提供。"""
|
||
here = Path(__file__).resolve()
|
||
for parent in here.parents:
|
||
candidate = parent / ".env"
|
||
if candidate.is_file():
|
||
return str(candidate)
|
||
return ""
|
||
|
||
|
||
class Settings(BaseSettings):
|
||
"""
|
||
进程级只读配置。
|
||
|
||
调用时机:HTTP / Worker 启动时通过 get_settings() 加载一次。
|
||
企微密钥必须来自环境/.env,源码中不得出现明文 Secret。
|
||
"""
|
||
|
||
model_config = SettingsConfigDict(
|
||
env_file=_find_dotenv() or None,
|
||
env_file_encoding="utf-8",
|
||
extra="ignore",
|
||
)
|
||
|
||
ytd_env: str = Field(default="test", alias="YTD_ENV")
|
||
agent_http_port: int = Field(default=8910, alias="AGENT_HTTP_PORT")
|
||
public_base_url: str = Field(default="", alias="PUBLIC_BASE_URL")
|
||
inquiry_form_public_base_url: str = Field(
|
||
default="",
|
||
alias="INQUIRY_FORM_PUBLIC_BASE_URL",
|
||
)
|
||
biz_api_base_url: str = Field(default="http://127.0.0.1:8180/jeecgboot", alias="BIZ_API_BASE_URL")
|
||
biz_api_token: str = Field(default="", alias="BIZ_API_TOKEN")
|
||
biz_api_username: str = Field(default="", alias="BIZ_API_USERNAME")
|
||
biz_api_password: str = Field(default="", alias="BIZ_API_PASSWORD")
|
||
biz_api_timeout_seconds: float = Field(default=10.0, alias="BIZ_API_TIMEOUT_SECONDS")
|
||
# http=打 8180 真主账/真 TMS;memory=单测假账本
|
||
ledger_backend: str = Field(default="http", alias="LEDGER_BACKEND")
|
||
agent_callback_token: str = Field(default="", alias="AGENT_CALLBACK_TOKEN")
|
||
# 兼容现网变量名
|
||
inquiry_agent_callback_token: str = Field(default="", alias="INQUIRY_AGENT_CALLBACK_TOKEN")
|
||
|
||
# 企微询价应用(与正式同一套应用 ID;密钥只从环境读)
|
||
wecom_corp_id: str = Field(default="", alias="WECOM_CORP_ID")
|
||
wecom_agent_id: str = Field(default="1000010", alias="WECOM_AGENT_ID")
|
||
wecom_secret: str = Field(default="", alias="WECOM_SECRET")
|
||
wecom_callback_token: str = Field(default="", alias="WECOM_CALLBACK_TOKEN")
|
||
wecom_encoding_aes_key: str = Field(default="", alias="WECOM_ENCODING_AES_KEY")
|
||
wecom_callback_reply_text: str = Field(
|
||
default="询价智能体接通成功。已收到:{content}",
|
||
alias="WECOM_CALLBACK_REPLY_TEXT",
|
||
)
|
||
# 本机注入开关:test 默认开;prod 默认关
|
||
dev_inject_enabled: bool = Field(default=False, alias="DEV_INJECT_ENABLED")
|
||
|
||
pg_host: str = Field(default="", alias="PG_HOST")
|
||
pg_port: int = Field(default=5432, alias="PG_PORT")
|
||
pg_database: str = Field(default="", alias="PG_DATABASE")
|
||
pg_user: str = Field(default="", alias="PG_USER")
|
||
pg_password: str = Field(default="", alias="PG_PASSWORD")
|
||
pg_sslmode: str = Field(default="prefer", alias="PG_SSLMODE")
|
||
# 可直接给完整 DSN;为空则由 host/port/user 等拼装
|
||
pg_dsn: str = Field(default="", alias="PG_DSN")
|
||
# postgres | memory;正式必须 postgres;memory 仅本地骨架冒烟
|
||
checkpoint_backend: str = Field(default="postgres", alias="CHECKPOINT_BACKEND")
|
||
|
||
redis_url: str = Field(default="", alias="REDIS_URL")
|
||
redis_key_prefix: str = Field(default="inquiry_robot:", alias="REDIS_KEY_PREFIX")
|
||
# redis | memory;正式必须 redis + URL 指向逻辑库 /1(与现网 /0 隔离)
|
||
redis_backend: str = Field(default="redis", alias="REDIS_BACKEND")
|
||
redis_required: bool = Field(default=False, alias="REDIS_REQUIRED")
|
||
# auto | memory | postgres;auto=有 PG_DSN 则尝试 PG
|
||
message_store_backend: str = Field(default="auto", alias="MESSAGE_STORE_BACKEND")
|
||
wecom_aibot_bridge_token: str = Field(default="", alias="WECOM_AIBOT_BRIDGE_TOKEN")
|
||
wecom_aibot_ingress_url: str = Field(
|
||
default="http://127.0.0.1:8910/internal/aibot/inbound",
|
||
alias="WECOM_AIBOT_INGRESS_URL",
|
||
)
|
||
|
||
deepseek_api_key: str = Field(default="", alias="DEEPSEEK_API_KEY")
|
||
deepseek_base_url: str = Field(default="https://api.deepseek.com", alias="DEEPSEEK_BASE_URL")
|
||
deepseek_model: str = Field(default="", alias="DEEPSEEK_MODEL")
|
||
qwen_api_key: str = Field(default="", alias="QWEN_API_KEY")
|
||
qwen_base_url: str = Field(default="", alias="QWEN_BASE_URL")
|
||
qwen_text_model: str = Field(default="", alias="QWEN_TEXT_MODEL")
|
||
qwen_vl_model: str = Field(default="", alias="QWEN_VL_MODEL")
|
||
# prompt/04:未确认不得打真实模型(出站 HTTP 客户端强制检查)
|
||
llm_data_usage_confirmed: bool = Field(default=False, alias="LLM_DATA_USAGE_CONFIRMED")
|
||
# Worker 槽是否允许对 ordered_actions 走真网(仍受上一开关约束)
|
||
llm_allow_network: bool = Field(default=False, alias="LLM_ALLOW_NETWORK")
|
||
|
||
# LibreOffice:默认 Windows 安装路径;Linux 可改 /usr/bin/libreoffice
|
||
libreoffice_path: str = Field(
|
||
default=r"C:\Program Files\LibreOffice\program\soffice.exe",
|
||
alias="LIBREOFFICE_PATH",
|
||
)
|
||
libreoffice_timeout_seconds: float = Field(
|
||
default=120.0, alias="LIBREOFFICE_TIMEOUT_SECONDS"
|
||
)
|
||
|
||
object_storage_endpoint_url: str = Field(default="", alias="OBJECT_STORAGE_ENDPOINT_URL")
|
||
object_storage_bucket: str = Field(default="inquiry-robot", alias="OBJECT_STORAGE_BUCKET")
|
||
object_storage_access_key: str = Field(default="", alias="OBJECT_STORAGE_ACCESS_KEY")
|
||
object_storage_secret_key: str = Field(default="", alias="OBJECT_STORAGE_SECRET_KEY")
|
||
object_storage_path_style: bool = Field(default=True, alias="OBJECT_STORAGE_PATH_STYLE")
|
||
|
||
libreoffice_slots: int = Field(default=1, alias="LIBREOFFICE_SLOTS")
|
||
http_job_slots: int = Field(default=3, alias="HTTP_JOB_SLOTS")
|
||
|
||
def resolve_pg_dsn(self) -> str:
|
||
"""
|
||
解析 PostgreSQL 连接串(运行账 / Graph checkpoint)。
|
||
|
||
返回值:PG_DSN 或由分量拼出的 URI;缺关键字段时返回空串。
|
||
禁止:连现网 ytd_runtime(由部署/环境保证库名,代码不写死切换)。
|
||
"""
|
||
raw = (self.pg_dsn or "").strip()
|
||
if raw:
|
||
return raw
|
||
if not (self.pg_host and self.pg_database and self.pg_user):
|
||
return ""
|
||
# 密码可为空(本机信任);sslmode 走环境
|
||
from urllib.parse import quote_plus
|
||
|
||
user = quote_plus(self.pg_user)
|
||
password = quote_plus(self.pg_password or "")
|
||
auth = f"{user}:{password}@" if self.pg_password else f"{user}@"
|
||
return (
|
||
f"postgresql://{auth}{self.pg_host}:{self.pg_port}/"
|
||
f"{self.pg_database}?sslmode={self.pg_sslmode or 'prefer'}"
|
||
)
|
||
|
||
|
||
@lru_cache(maxsize=1)
|
||
def get_settings() -> Settings:
|
||
"""返回进程内单例 Settings。副作用:首次可能读 .env。"""
|
||
return Settings()
|