Files
banban/talkingq-url/config.py
2026-06-22 14:34:20 +08:00

235 lines
11 KiB
Python

from functools import lru_cache
from urllib.parse import quote_plus
from pathlib import Path
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict
ENV_FILE = Path(__file__).resolve().parent / ".env"
class Settings(BaseSettings):
model_config = SettingsConfigDict(
env_file=str(ENV_FILE),
env_file_encoding="utf-8",
extra="ignore",
)
app_name: str = Field(default="banban-server", validation_alias="APP_NAME")
app_version: str = Field(default="0.1.0", validation_alias="APP_VERSION")
app_description: str = Field(
default="Backend service for TalkingQ device",
validation_alias="APP_DESCRIPTION",
)
server_host: str = Field(default="0.0.0.0", validation_alias="SERVER_HOST")
server_port: int = Field(default=8080, validation_alias="SERVER_PORT")
asr_provider: str = Field(default="Aliyun", validation_alias="ASR_PROVIDER")
llm_provider: str = Field(default="Volcano", validation_alias="LLM_PROVIDER")
tts_provider: str = Field(default="MiniMax", validation_alias="TTS_PROVIDER")
volcano_api_key: str = Field(default="", validation_alias="VOLCANO_API_KEY")
volcano_base_url: str = Field(default="", validation_alias="VOLCANO_BASE_URL")
volcano_model_id: str = Field(default="ep-20250226121739-jkd24", validation_alias="VOLCANO_MODEL_ID")
volcano_app_id: str = Field(default="7872932045", validation_alias="VOLCANO_APP_ID")
volcano_access_token: str = Field(default="", validation_alias="VOLCANO_ACCESS_TOKEN")
minimax_api_key: str = Field(default="", validation_alias="MINIMAX_API_KEY")
minimax_group_id: str = Field(default="", validation_alias="MINIMAX_GROUP_ID")
minimax_base_url: str = Field(
default="https://api.minimax.chat/v1/t2a_v2",
validation_alias="MINIMAX_BASE_URL",
)
amap_key: str = Field(default="", validation_alias="Amap_key")
amap_reverse_geocode_url: str = Field(
default="https://restapi.amap.com/v3/geocode/regeo",
validation_alias="AMAP_REVERSE_GEOCODE_URL",
)
amap_http_timeout_seconds: float = Field(default=5.0, validation_alias="AMAP_HTTP_TIMEOUT_SECONDS")
alarm_location_stale_seconds: int = Field(default=600, validation_alias="ALARM_LOCATION_STALE_SECONDS")
alarm_gps_query_timeout_seconds: float = Field(default=5.0, validation_alias="ALARM_GPS_QUERY_TIMEOUT_SECONDS")
aliyun_api_key: str = Field(default="", validation_alias="ALIYUN_API_KEY")
aliyun_vocabulary_id: str = Field(default="", validation_alias="ALIYUN_VOCABULARY_ID")
assets_dir: str = Field(default="assets", validation_alias="ASSETS_DIR")
session_timeout: int = Field(default=600, validation_alias="SESSION_TIMEOUT")
conversation_history_timeout: int = Field(default=1800, validation_alias="CONVERSATION_HISTORY_TIMEOUT")
max_conversation_history: int = Field(default=5, validation_alias="MAX_CONVERSATION_HISTORY")
cleanup_interval: int = Field(default=300, validation_alias="CLEANUP_INTERVAL")
llm_first_token_timeout: int = Field(default=5, validation_alias="LLM_FIRST_TOKEN_TIMEOUT")
tts_request_timeout: int = Field(default=5, validation_alias="TTS_REQUEST_TIMEOUT")
selected_role_key: str = Field(default="", validation_alias="SELECTED_ROLE_KEY")
db_host: str = Field(default="mysql", validation_alias="DB_HOST")
db_port: int = Field(default=3306, validation_alias="DB_PORT")
db_user: str = Field(default="talkingq", validation_alias="DB_USER")
db_password: str = Field(default="", validation_alias="DB_PASSWORD")
db_name: str = Field(default="talkingq", validation_alias="DB_NAME")
db_echo: bool = Field(default=False, validation_alias="DB_ECHO")
talkingq_mqtt_broker: str = Field(default="broker.emqx.io", validation_alias="TALKINGQ_MQTT_BROKER")
talkingq_mqtt_port: int = Field(default=1884, validation_alias="TALKINGQ_MQTT_PORT")
talkingq_mqtt_username: str = Field(default="", validation_alias="TALKINGQ_MQTT_USERNAME")
talkingq_mqtt_password: str = Field(default="", validation_alias="TALKINGQ_MQTT_PASSWORD")
talkingq_mqtt_device_prefix: str = Field(default="TalkingQ", validation_alias="TALKINGQ_MQTT_DEVICE_PREFIX")
talkingq_mqtt_qos: int = Field(default=1, validation_alias="TALKINGQ_MQTT_QOS")
talkingq_mqtt_keepalive: int = Field(default=60, validation_alias="TALKINGQ_MQTT_KEEPALIVE")
talkingq_mqtt_nfc_notice_interval: int = Field(default=600, validation_alias="TALKINGQ_MQTT_NFC_NOTICE_INTERVAL")
ota_sent_timeout_seconds: int = Field(default=300, validation_alias="OTA_SENT_TIMEOUT_SECONDS")
admin_api_key: str = Field(default="", validation_alias="ADMIN_API_KEY")
client_api_key: str = Field(default="", validation_alias="CLIENT_API_KEY")
wechat_app_id: str = Field(default="", validation_alias="WECHAT_APP_ID")
wechat_app_secret: str = Field(default="", validation_alias="WECHAT_APP_SECRET")
wechat_api_base_url: str = Field(
default="https://api.weixin.qq.com",
validation_alias="WECHAT_API_BASE_URL",
)
wechat_http_timeout_seconds: float = Field(
default=5.0,
validation_alias="WECHAT_HTTP_TIMEOUT_SECONDS",
)
wechat_mp_app_id: str = Field(default="", validation_alias="WECHAT_MP_APP_ID")
wechat_mp_app_secret: str = Field(default="", validation_alias="WECHAT_MP_APP_SECRET")
wechat_mp_api_base_url: str = Field(
default="https://api.weixin.qq.com",
validation_alias="WECHAT_MP_API_BASE_URL",
)
wechat_mp_push_enabled: bool = Field(default=False, validation_alias="WECHAT_MP_PUSH_ENABLED")
wechat_mp_http_timeout_seconds: float = Field(
default=5.0,
validation_alias="WECHAT_MP_HTTP_TIMEOUT_SECONDS",
)
wechat_mp_template_id: str = Field(default="", validation_alias="WECHAT_MP_TEMPLATE_ID")
wechat_mp_leave_message_template_id: str = Field(
default="",
validation_alias="WECHAT_MP_LEAVE_MESSAGE_TEMPLATE_ID",
)
wechat_mp_low_battery_template_id: str = Field(
default="",
validation_alias="WECHAT_MP_LOW_BATTERY_TEMPLATE_ID",
)
wechat_mp_sleep_mode_template_id: str = Field(
default="",
validation_alias="WECHAT_MP_SLEEP_MODE_TEMPLATE_ID",
)
wechat_mp_miniprogram_appid: str = Field(default="", validation_alias="WECHAT_MP_MINIPROGRAM_APPID")
wechat_mp_chat_page: str = Field(default="pages/chat/detail/index", validation_alias="WECHAT_MP_CHAT_PAGE")
wechat_mp_device_page: str = Field(default="pages/device/index", validation_alias="WECHAT_MP_DEVICE_PAGE")
wechat_mp_oauth_redirect_uri: str = Field(default="", validation_alias="WECHAT_MP_OAUTH_REDIRECT_URI")
wechat_mp_bind_success_page: str = Field(
default="pages/sleep/index",
validation_alias="WECHAT_MP_BIND_SUCCESS_PAGE",
)
wechat_mp_low_battery_threshold: int = Field(
default=20,
validation_alias="WECHAT_MP_LOW_BATTERY_THRESHOLD",
)
wechat_mp_low_battery_recovery_threshold: int = Field(
default=25,
validation_alias="WECHAT_MP_LOW_BATTERY_RECOVERY_THRESHOLD",
)
wechat_mp_low_battery_dedup_seconds: int = Field(
default=21600,
validation_alias="WECHAT_MP_LOW_BATTERY_DEDUP_SECONDS",
)
wechat_mp_sleep_mode_dedup_seconds: int = Field(
default=300,
validation_alias="WECHAT_MP_SLEEP_MODE_DEDUP_SECONDS",
)
wechat_mp_leave_message_dedup_seconds: int = Field(
default=60,
validation_alias="WECHAT_MP_LEAVE_MESSAGE_DEDUP_SECONDS",
)
sms_enabled: bool = Field(default=False, validation_alias="SMS_ENABLED")
sms_provider: str = Field(default="aliyun", validation_alias="SMS_PROVIDER")
sms_dry_run: bool = Field(default=False, validation_alias="SMS_DRY_RUN")
sms_http_timeout_seconds: float = Field(default=5.0, validation_alias="SMS_HTTP_TIMEOUT_SECONDS")
sms_alarm_dedup_seconds: int = Field(default=300, validation_alias="SMS_ALARM_DEDUP_SECONDS")
sms_leave_message_dedup_seconds: int = Field(
default=60,
validation_alias="SMS_LEAVE_MESSAGE_DEDUP_SECONDS",
)
aliyun_sms_access_key_id: str = Field(default="", validation_alias="ALIYUN_SMS_ACCESS_KEY_ID")
aliyun_sms_access_key_secret: str = Field(default="", validation_alias="ALIYUN_SMS_ACCESS_KEY_SECRET")
aliyun_sms_sign_name: str = Field(default="", validation_alias="ALIYUN_SMS_SIGN_NAME")
aliyun_sms_template_code: str = Field(default="", validation_alias="ALIYUN_SMS_TEMPLATE_CODE")
aliyun_sms_alarm_template_code: str = Field(
default="",
validation_alias="ALIYUN_SMS_ALARM_TEMPLATE_CODE",
)
aliyun_sms_leave_message_template_code: str = Field(
default="",
validation_alias="ALIYUN_SMS_LEAVE_MESSAGE_TEMPLATE_CODE",
)
aliyun_sms_endpoint: str = Field(
default="dysmsapi.aliyuncs.com",
validation_alias="ALIYUN_SMS_ENDPOINT",
)
cos_secret_id: str = Field(default="", validation_alias="COS_SECRET_ID")
cos_secret_key: str = Field(default="", validation_alias="COS_SECRET_KEY")
cos_region: str = Field(default="", validation_alias="COS_REGION")
cos_bucket_message: str = Field(default="", validation_alias="COS_BUCKET_MESSAGE")
cos_bucket_ava: str = Field(default="", validation_alias="COS_BUCKET_AVA")
cos_public_base_url: str = Field(default="", validation_alias="COS_PUBLIC_BASE_URL")
cos_message_prefix: str = Field(default="messages/audio/", validation_alias="COS_MESSAGE_PREFIX")
cos_avatar_prefix: str = Field(default="avatars/", validation_alias="COS_AVATAR_PREFIX")
cos_avatar_url_expire_seconds: int = Field(
default=86400,
validation_alias="COS_AVATAR_URL_EXPIRE_SECONDS",
)
cos_avatar_max_bytes: int = Field(
default=2 * 1024 * 1024,
validation_alias="COS_AVATAR_MAX_BYTES",
)
device_audio_cache_dir: str = Field(
default="runtime/device-audio",
validation_alias="DEVICE_AUDIO_CACHE_DIR",
)
device_audio_public_base_url: str = Field(
default="",
validation_alias="DEVICE_AUDIO_PUBLIC_BASE_URL",
)
device_audio_max_bytes: int = Field(
default=20 * 1024 * 1024,
validation_alias="DEVICE_AUDIO_MAX_BYTES",
)
jwt_secret: str = Field(
default="dev_only_change_jwt_secret",
validation_alias="JWT_SECRET",
)
jwt_algorithm: str = Field(default="HS256", validation_alias="JWT_ALGORITHM")
jwt_access_token_expire_minutes: int = Field(
default=60,
validation_alias="JWT_ACCESS_TOKEN_EXPIRE_MINUTES",
)
log_level: str = Field(default="INFO", validation_alias="LOG_LEVEL")
log_json: bool = Field(default=False, validation_alias="LOG_JSON")
slow_sql_ms: int = Field(default=200, validation_alias="SLOW_SQL_MS")
@property
def mysql_dsn(self) -> str:
user = quote_plus(self.db_user)
password = quote_plus(self.db_password)
auth = f"{user}:{password}" if password else user
return (
f"mysql+pymysql://{auth}@{self.db_host}:{self.db_port}/"
f"{self.db_name}?charset=utf8mb4"
)
@lru_cache(maxsize=1)
def get_settings() -> Settings:
return Settings()
settings = get_settings()