from pydantic import Field from pydantic_settings import BaseSettings, SettingsConfigDict class Settings(BaseSettings): model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8", extra="ignore") app_name: str = "AI发糕" openai_api_key: str | None = Field(default=None, alias="OPENAI_API_KEY") openai_base_url: str | None = Field(default=None, alias="OPENAI_BASE_URL") openai_model: str = Field(default="gpt-4.1-mini", alias="OPENAI_MODEL") openai_timeout: float = Field( default=120.0, alias="OPENAI_TIMEOUT", description="HTTP 等待模型单轮响应的最长时间(秒)。通义长文 JSON 建议 120~180", ) openai_max_retries: int = Field( default=0, alias="OPENAI_MAX_RETRIES", description="OpenAI SDK 自动重试次数。为避免单次请求被重试拉长,默认 0。", ) openai_max_output_tokens: int = Field( default=8192, alias="OPENAI_MAX_OUTPUT_TOKENS", description="单次模型输出 token 上限;通义等长文 JSON 需足够大", ) openai_image_model: str = Field( default="gpt-image-1", alias="OPENAI_IMAGE_MODEL", description="用于海报生成的图像模型", ) openai_source_max_chars: int = Field(default=5000, alias="OPENAI_SOURCE_MAX_CHARS") ai_soft_accept: bool = Field( default=True, alias="AI_SOFT_ACCEPT", description="质检未通过时仍输出模型洗稿正文(mode=ai,quality_notes 记录问题),仅模型完全失败时用保底稿", ) wechat_appid: str | None = Field(default=None, alias="WECHAT_APPID") wechat_secret: str | None = Field(default=None, alias="WECHAT_SECRET") wechat_author: str = Field(default="AI 编辑部", alias="WECHAT_AUTHOR") wechat_thumb_media_id: str | None = Field( default=None, alias="WECHAT_THUMB_MEDIA_ID", description="草稿图文封面:永久素材 media_id(素材库或 add_material)。与 WECHAT_THUMB_IMAGE_PATH 二选一即可", ) wechat_thumb_image_path: str | None = Field( default=None, alias="WECHAT_THUMB_IMAGE_PATH", description="本地封面图路径(容器内),将自动上传为永久素材;不配则使用内置灰底图上传", ) poster_image_size: str = Field( default="1024x1536", alias="POSTER_IMAGE_SIZE", description="AI 海报生成尺寸(OpenAI images.generate size)", ) poster_max_images: int = Field( default=6, alias="POSTER_MAX_IMAGES", description="单次自动生成海报上限(首段跳过后生效)", ) poster_upload_max_bytes: int = Field( default=950000, alias="POSTER_UPLOAD_MAX_BYTES", description="上传微信 uploadimg 前压缩目标字节数,预留余量避免超 1MB 限制", ) im_webhook_url: str | None = Field(default=None, alias="IM_WEBHOOK_URL") im_secret: str | None = Field(default=None, alias="IM_SECRET") auth_db_path: str = Field(default="./data/app.db", alias="AUTH_DB_PATH") auth_cookie_name: str = Field(default="x2ws_session", alias="AUTH_COOKIE_NAME") auth_session_ttl_sec: int = Field(default=86400, alias="AUTH_SESSION_TTL_SEC") auth_remember_session_ttl_sec: int = Field(default=604800, alias="AUTH_REMEMBER_SESSION_TTL_SEC") auth_password_reset_key: str | None = Field(default="x2ws-reset-2026", alias="AUTH_PASSWORD_RESET_KEY") vip_trial_tokens: int = Field( default=500, alias="VIP_TRIAL_TOKENS", description="新用户试用赠送 Credits", ) credits_seat_monthly_quota: int = Field( default=1500, alias="CREDITS_SEAT_MONTHLY_QUOTA", description="标准坐席每月 Credits 额度", ) credits_standard_seat_price_cny: float = Field( default=198.0, alias="CREDITS_STANDARD_SEAT_PRICE_CNY", description="标准坐席月费(人民币)", ) vip_rewrite_token_per_1k_chars: int = Field( default=1500, alias="VIP_REWRITE_TOKEN_PER_1K_CHARS", description="兼容字段:改写计费参数(建议使用 Credits 规则字段)", ) vip_image_token_per_image: int = Field( default=1500, alias="VIP_IMAGE_TOKEN_PER_IMAGE", description="兼容字段:生图计费参数(建议使用 Credits 规则字段)", ) credits_per_million_tokens: int = Field( default=1500, alias="CREDITS_PER_MILLION_TOKENS", description="兼容字段:100万 token 对应的 Credits 抵扣(建议使用人民币折算字段)", ) credits_per_120_images: int = Field( default=1500, alias="CREDITS_PER_120_IMAGES", description="兼容字段:120 张图片对应的 Credits 抵扣(建议使用人民币折算字段)", ) credits_token_price_per_million_cny: float = Field( default=7.9, alias="CREDITS_TOKEN_PRICE_PER_MILLION_CNY", description="文本计费:100万 token 的人民币价格", ) credits_image_price_per_image_cny: float = Field( default=0.04, alias="CREDITS_IMAGE_PRICE_PER_IMAGE_CNY", description="兼容字段:生图计费单张价格(建议使用整包折算字段)", ) credits_image_price_package_cny: float = Field( default=0.75, alias="CREDITS_IMAGE_PRICE_PACKAGE_CNY", description="生图计费:图片整包人民币价格", ) credits_image_price_package_images: int = Field( default=160, alias="CREDITS_IMAGE_PRICE_PACKAGE_IMAGES", description="生图计费:整包包含图片张数", ) credits_recharge_package_amount: float = Field( default=19.9, alias="CREDITS_RECHARGE_PACKAGE_AMOUNT", description="标准加油包价格(元)", ) credits_recharge_package_credits: int = Field( default=1500, alias="CREDITS_RECHARGE_PACKAGE_CREDITS", description="标准加油包 Credits 数量", ) platform_openai_api_key: str | None = Field(default=None, alias="PLATFORM_OPENAI_API_KEY") platform_openai_base_url: str | None = Field(default=None, alias="PLATFORM_OPENAI_BASE_URL") platform_openai_model: str = Field(default="qwen-plus", alias="PLATFORM_OPENAI_MODEL") platform_openai_image_model: str = Field(default="wanx2.0-t2i-turbo", alias="PLATFORM_OPENAI_IMAGE_MODEL") platform_openai_text_model_options: str = Field( default="gpt-4.1-mini,gpt-4.1,gpt-4o-mini,qwen-plus,qwen-max", alias="PLATFORM_OPENAI_TEXT_MODEL_OPTIONS", ) platform_openai_image_model_options: str = Field( default="wanx2.0-t2i-turbo,wanx2.1-t2i-plus,wanx2.1-t2i-turbo,gpt-image-1,dall-e-3", alias="PLATFORM_OPENAI_IMAGE_MODEL_OPTIONS", ) platform_openai_timeout: float = Field(default=120.0, alias="PLATFORM_OPENAI_TIMEOUT") platform_openai_max_output_tokens: int = Field(default=8192, alias="PLATFORM_OPENAI_MAX_OUTPUT_TOKENS") platform_openai_max_retries: int = Field(default=0, alias="PLATFORM_OPENAI_MAX_RETRIES") shop_backend_create_order_url: str | None = Field(default=None, alias="SHOP_BACKEND_CREATE_ORDER_URL") shop_backend_callback_token: str = Field(default="", alias="SHOP_BACKEND_CALLBACK_TOKEN") super_admin_token: str = Field(default="", alias="SUPER_ADMIN_TOKEN") settings = Settings()